Browse Source

(*) 网关界面配置

master
yinweiwen 2 years ago
parent
commit
b3f2d67e0a
  1. 28
      code/web/client/assets/color.less
  2. 17
      code/web/client/src/sections/gateway/actions/gateway.js
  3. 123
      code/web/client/src/sections/gateway/containers/ConfigModal.js
  4. 24
      code/web/client/src/sections/gateway/containers/Gateway.js
  5. 4
      code/web/client/src/sections/gateway/containers/models.js
  6. 7
      code/web/client/src/utils/webapi.js
  7. 2
      code/web/package.json

28
code/web/client/assets/color.less

@ -174,7 +174,7 @@ button::-moz-focus-inner,
[type='submit']::-moz-focus-inner {border-style: none;} [type='submit']::-moz-focus-inner {border-style: none;}
fieldset {border: 0;} fieldset {border: 0;}
legend {color: inherit;} legend {color: inherit;}
mark {background-color: #feffe6;} mark {background-color: color(~`colorPalette("@{table-header-sort-active-bg}", 2)`);}
::selection {color: #fff;background: @primary-color;} ::selection {color: #fff;background: @primary-color;}
.anticon {color: inherit;} .anticon {color: inherit;}
.ant-fade-enter, .ant-fade-appear {animation-fill-mode: both;} .ant-fade-enter, .ant-fade-appear {animation-fill-mode: both;}
@ -1147,10 +1147,10 @@ tr > .ant-picker-cell-in-view.ant-picker-cell-range-hover-start:last-child::afte
.ant-mentions-dropdown-menu-item-active {background-color: @item-hover-bg;} .ant-mentions-dropdown-menu-item-active {background-color: @item-hover-bg;}
.ant-menu-item-danger.ant-menu-item {color: #ff4d4f;} .ant-menu-item-danger.ant-menu-item {color: #ff4d4f;}
.ant-menu-item-danger.ant-menu-item:hover, .ant-menu-item-danger.ant-menu-item-active {color: #ff4d4f;} .ant-menu-item-danger.ant-menu-item:hover, .ant-menu-item-danger.ant-menu-item-active {color: #ff4d4f;}
.ant-menu-item-danger.ant-menu-item:active {background: #fff1f0;} .ant-menu-item-danger.ant-menu-item:active {background: color(~`colorPalette("@{alert-info-border-color}", 4)`);}
.ant-menu-item-danger.ant-menu-item-selected {color: #ff4d4f;} .ant-menu-item-danger.ant-menu-item-selected {color: #ff4d4f;}
.ant-menu-item-danger.ant-menu-item-selected > a, .ant-menu-item-danger.ant-menu-item-selected > a:hover {color: #ff4d4f;} .ant-menu-item-danger.ant-menu-item-selected > a, .ant-menu-item-danger.ant-menu-item-selected > a:hover {color: #ff4d4f;}
.ant-menu:not(.ant-menu-horizontal) .ant-menu-item-danger.ant-menu-item-selected {background-color: #fff1f0;} .ant-menu:not(.ant-menu-horizontal) .ant-menu-item-danger.ant-menu-item-selected {background-color: color(~`colorPalette("@{alert-info-border-color}", 4)`);}
.ant-menu-inline .ant-menu-item-danger.ant-menu-item::after {border-right-color: #ff4d4f;} .ant-menu-inline .ant-menu-item-danger.ant-menu-item::after {border-right-color: #ff4d4f;}
.ant-menu-dark .ant-menu-item-danger.ant-menu-item, .ant-menu-dark .ant-menu-item-danger.ant-menu-item:hover, .ant-menu-dark .ant-menu-item-danger.ant-menu-item > a {color: #ff4d4f;} .ant-menu-dark .ant-menu-item-danger.ant-menu-item, .ant-menu-dark .ant-menu-item-danger.ant-menu-item:hover, .ant-menu-dark .ant-menu-item-danger.ant-menu-item > a {color: #ff4d4f;}
.ant-menu-dark.ant-menu-dark:not(.ant-menu-horizontal) .ant-menu-item-danger.ant-menu-item-selected {color: #fff;background-color: #ff4d4f;} .ant-menu-dark.ant-menu-dark:not(.ant-menu-horizontal) .ant-menu-item-danger.ant-menu-item-selected {color: #fff;background-color: #ff4d4f;}
@ -1727,31 +1727,31 @@ tr.ant-table-expanded-row:hover > td {background: @table-expanded-row-bg;}
.ant-tag-checkable:active, .ant-tag-checkable-checked {color: #fff;} .ant-tag-checkable:active, .ant-tag-checkable-checked {color: #fff;}
.ant-tag-checkable-checked {background-color: @primary-color;} .ant-tag-checkable-checked {background-color: @primary-color;}
.ant-tag-checkable:active {background-color: color(~`colorPalette("@{primary-color}", 7)`);} .ant-tag-checkable:active {background-color: color(~`colorPalette("@{primary-color}", 7)`);}
.ant-tag-pink {color: #c41d7f;background: color(~`colorPalette("@{alert-warning-border-color}", 1)`);border-color: #ffadd2;} .ant-tag-pink {color: #c41d7f;background: #fff0f6;border-color: #ffadd2;}
.ant-tag-pink-inverse {color: #fff;background: #eb2f96;border-color: #eb2f96;} .ant-tag-pink-inverse {color: #fff;background: #eb2f96;border-color: #eb2f96;}
.ant-tag-magenta {color: #c41d7f;background: color(~`colorPalette("@{alert-warning-border-color}", 1)`);border-color: #ffadd2;} .ant-tag-magenta {color: #c41d7f;background: #fff0f6;border-color: #ffadd2;}
.ant-tag-magenta-inverse {color: #fff;background: #eb2f96;border-color: #eb2f96;} .ant-tag-magenta-inverse {color: #fff;background: #eb2f96;border-color: #eb2f96;}
.ant-tag-red {color: #cf1322;background: #fff1f0;border-color: #ffa39e;} .ant-tag-red {color: #cf1322;background: color(~`colorPalette("@{alert-info-border-color}", 4)`);border-color: #ffa39e;}
.ant-tag-red-inverse {color: #fff;background: #f5222d;border-color: #f5222d;} .ant-tag-red-inverse {color: #fff;background: #f5222d;border-color: #f5222d;}
.ant-tag-volcano {color: #d4380d;background: #fff2e8;border-color: #ffbb96;} .ant-tag-volcano {color: #d4380d;background: #fff2e8;border-color: #ffbb96;}
.ant-tag-volcano-inverse {color: #fff;background: #fa541c;border-color: #fa541c;} .ant-tag-volcano-inverse {color: #fff;background: #fa541c;border-color: #fa541c;}
.ant-tag-orange {color: #d46b08;background: color(~`colorPalette("@{shadow-color}", 1)`);border-color: #ffd591;} .ant-tag-orange {color: #d46b08;background: #fff7e6;border-color: #ffd591;}
.ant-tag-orange-inverse {color: #fff;background: #fa8c16;border-color: #fa8c16;} .ant-tag-orange-inverse {color: #fff;background: #fa8c16;border-color: #fa8c16;}
.ant-tag-yellow {color: #d4b106;background: #feffe6;border-color: #fffb8f;} .ant-tag-yellow {color: #d4b106;background: color(~`colorPalette("@{table-header-sort-active-bg}", 2)`);border-color: #fffb8f;}
.ant-tag-yellow-inverse {color: #fff;background: #fadb14;border-color: #fadb14;} .ant-tag-yellow-inverse {color: #fff;background: #fadb14;border-color: #fadb14;}
.ant-tag-gold {color: #d48806;background: color(~`colorPalette("@{btn-shadow}", 1)`);border-color: #ffe58f;} .ant-tag-gold {color: #d48806;background: #fffbe6;border-color: #ffe58f;}
.ant-tag-gold-inverse {color: #fff;background: #faad14;border-color: #faad14;} .ant-tag-gold-inverse {color: #fff;background: #faad14;border-color: #faad14;}
.ant-tag-cyan {color: #08979c;background: #e6fffb;border-color: #87e8de;} .ant-tag-cyan {color: #08979c;background: #e6fffb;border-color: #87e8de;}
.ant-tag-cyan-inverse {color: #fff;background: #13c2c2;border-color: #13c2c2;} .ant-tag-cyan-inverse {color: #fff;background: #13c2c2;border-color: #13c2c2;}
.ant-tag-lime {color: #7cb305;background: color(~`colorPalette("@{dropdown-menu-submenu-disabled-bg}", 1)`);border-color: #eaff8f;} .ant-tag-lime {color: #7cb305;background: #fcffe6;border-color: #eaff8f;}
.ant-tag-lime-inverse {color: #fff;background: #a0d911;border-color: #a0d911;} .ant-tag-lime-inverse {color: #fff;background: #a0d911;border-color: #a0d911;}
.ant-tag-green {color: #389e0d;background: #f6ffed;border-color: #b7eb8f;} .ant-tag-green {color: #389e0d;background: #f6ffed;border-color: #b7eb8f;}
.ant-tag-green-inverse {color: #fff;background: #52c41a;border-color: #52c41a;} .ant-tag-green-inverse {color: #fff;background: #52c41a;border-color: #52c41a;}
.ant-tag-blue {color: #096dd9;background: color(~`colorPalette("@{transfer-item-hover-bg}", 1)`);border-color: #91d5ff;} .ant-tag-blue {color: #096dd9;background: #e6f7ff;border-color: #91d5ff;}
.ant-tag-blue-inverse {color: #fff;background: #1890ff;border-color: #1890ff;} .ant-tag-blue-inverse {color: #fff;background: #1890ff;border-color: #1890ff;}
.ant-tag-geekblue {color: #1d39c4;background: color(~`colorPalette("@{badge-text-color}", 1)`);border-color: #adc6ff;} .ant-tag-geekblue {color: #1d39c4;background: color(~`colorPalette("@{slider-rail-background-color-hover}", 1)`);border-color: #adc6ff;}
.ant-tag-geekblue-inverse {color: #fff;background: #2f54eb;border-color: #2f54eb;} .ant-tag-geekblue-inverse {color: #fff;background: #2f54eb;border-color: #2f54eb;}
.ant-tag-purple {color: #531dab;background: color(~`colorPalette("@{table-header-sort-bg}", 2)`);border-color: #d3adf7;} .ant-tag-purple {color: #531dab;background: color(~`colorPalette("@{shadow-color-inverse}", 1)`);border-color: #d3adf7;}
.ant-tag-purple-inverse {color: #fff;background: #722ed1;border-color: #722ed1;} .ant-tag-purple-inverse {color: #fff;background: #722ed1;border-color: #722ed1;}
.ant-tag-success {color: #52c41a;background: @success-color-deprecated-bg;border-color: @success-color-deprecated-border;} .ant-tag-success {color: #52c41a;background: @success-color-deprecated-bg;border-color: @success-color-deprecated-border;}
.ant-tag-processing {color: @primary-color;background: @info-color-deprecated-bg;border-color: @info-color-deprecated-border;} .ant-tag-processing {color: @primary-color;background: @info-color-deprecated-bg;border-color: @info-color-deprecated-border;}
@ -1988,7 +1988,7 @@ a.ant-typography.ant-typography-disabled:hover, .ant-typography a.ant-typography
.ant-upload-list-picture .ant-upload-list-item-error, .ant-upload-list-picture-card .ant-upload-list-item-error {border-color: #ff4d4f;} .ant-upload-list-picture .ant-upload-list-item-error, .ant-upload-list-picture-card .ant-upload-list-item-error {border-color: #ff4d4f;}
.ant-upload-list-picture .ant-upload-list-item:hover .ant-upload-list-item-info, .ant-upload-list-picture-card .ant-upload-list-item:hover .ant-upload-list-item-info {background: transparent;} .ant-upload-list-picture .ant-upload-list-item:hover .ant-upload-list-item-info, .ant-upload-list-picture-card .ant-upload-list-item:hover .ant-upload-list-item-info {background: transparent;}
.ant-upload-list-picture .ant-upload-list-item-uploading, .ant-upload-list-picture-card .ant-upload-list-item-uploading {border-style: dashed;} .ant-upload-list-picture .ant-upload-list-item-uploading, .ant-upload-list-picture-card .ant-upload-list-item-uploading {border-style: dashed;}
.ant-upload-list-picture .ant-upload-list-item-error .ant-upload-list-item-thumbnail .anticon svg path[fill='color(~`colorPalette("@{transfer-item-hover-bg}", 1)`)'], .ant-upload-list-picture-card .ant-upload-list-item-error .ant-upload-list-item-thumbnail .anticon svg path[fill='color(~`colorPalette("@{transfer-item-hover-bg}", 1)`)'] {fill: @error-color-deprecated-bg;} .ant-upload-list-picture .ant-upload-list-item-error .ant-upload-list-item-thumbnail .anticon svg path[fill='#e6f7ff'], .ant-upload-list-picture-card .ant-upload-list-item-error .ant-upload-list-item-thumbnail .anticon svg path[fill='#e6f7ff'] {fill: @error-color-deprecated-bg;}
.ant-upload-list-picture .ant-upload-list-item-error .ant-upload-list-item-thumbnail .anticon svg path[fill='#1890ff'], .ant-upload-list-picture-card .ant-upload-list-item-error .ant-upload-list-item-thumbnail .anticon svg path[fill='#1890ff'] {fill: #ff4d4f;} .ant-upload-list-picture .ant-upload-list-item-error .ant-upload-list-item-thumbnail .anticon svg path[fill='#1890ff'], .ant-upload-list-picture-card .ant-upload-list-item-error .ant-upload-list-item-thumbnail .anticon svg path[fill='#1890ff'] {fill: #ff4d4f;}
.ant-upload-list-picture-card .ant-upload-list-item-info::before {background-color: rgba(0, 0, 0, 0.5);} .ant-upload-list-picture-card .ant-upload-list-item-info::before {background-color: rgba(0, 0, 0, 0.5);}
.ant-upload-list-picture-card .ant-upload-list-item-actions .anticon-eye, .ant-upload-list-picture-card .ant-upload-list-item-actions .anticon-download, .ant-upload-list-picture-card .ant-upload-list-item-actions .anticon-delete {color: rgba(255, 255, 255, 0.85);} .ant-upload-list-picture-card .ant-upload-list-item-actions .anticon-eye, .ant-upload-list-picture-card .ant-upload-list-item-actions .anticon-download, .ant-upload-list-picture-card .ant-upload-list-item-actions .anticon-delete {color: rgba(255, 255, 255, 0.85);}

17
code/web/client/src/sections/gateway/actions/gateway.js

@ -15,6 +15,17 @@ export function list(query) {
}) })
} }
export function getProtocols() {
return dispatch => basicAction({
type: 'get',
dispatch: dispatch,
actionType: 'GET_GATEWAY_PROTOCOLS',
url: ApiTable.getGatewayProtocols,
msg: { error: '获取网关协议类型失败' },
reducer: { name: 'gatewayProtocols' }
})
}
export function add(data) { export function add(data) {
return dispatch => basicAction({ return dispatch => basicAction({
type: 'post', type: 'post',
@ -26,13 +37,13 @@ export function add(data) {
}) })
} }
export function edit(data, gatewayId) { export function edit(data) {
return dispatch => basicAction({ return dispatch => basicAction({
type: 'put', type: 'post',
dispatch: dispatch, dispatch: dispatch,
data: data, data: data,
actionType: 'EDIT_GATEWAY_CONFIG', actionType: 'EDIT_GATEWAY_CONFIG',
url: `${ApiTable.editGatewayConfig.replace('{gatewayId}', gatewayId)}`, url: `${ApiTable.editGatewayConfig}`,
msg: { option: '编辑网关配置' } msg: { option: '编辑网关配置' }
}) })
} }

123
code/web/client/src/sections/gateway/containers/ConfigModal.js

@ -1,13 +1,45 @@
import React, { useEffect, useRef } from 'react'; import React, { useEffect, useRef } from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { Spin, Button, Modal, Form, Switch } from 'antd'; import { Spin, Select, Divider, Space, Button, Modal, Input, Form, Switch, AutoComplete } from 'antd';
import ProForm, { ProFormText, ProFormSelect, ProFormDateTimePicker } from '@ant-design/pro-form'; import ProForm, { ProFormText, ProFormSelect, ProFormTextArea } from '@ant-design/pro-form';
import { useState } from 'react'; import { useState } from 'react';
import { InMode, OutMode, ProtocolTypes } from './models';
import { PlusOutlined } from '@ant-design/icons';
const { Option } = Select;
const ConfigModal = (props) => { const ConfigModal = (props) => {
const { dispatch, actions, visible, close, editData, types, catalogs } = props const { dispatch, actions, visible, close, editData } = props
const formRef = useRef() const formRef = useRef()
const inputRef = useRef()
const { gateway } = actions const { gateway } = actions
const [protocols, setProtocols] = useState([])
const [newProtocol, setNewProtocol] = useState('')
useEffect(() => {
setProtocols([]);
dispatch(gateway.getProtocols()).then(res => {
if (res.success) {
setProtocols(res.payload.data.sort())
}
})
}, [])
const onNewProtocolChange = (e) => {
setNewProtocol(e.target.value)
}
const addNewProtocol = (e) => {
e.preventDefault();
setProtocols([...protocols, newProtocol])
setNewProtocol('');
setTimeout(()=>{
inputRef.current?.focus();
})
}
const batchAdd=(v)=>{
}
return <Modal return <Modal
title={`${editData ? '编辑' : '新增'}配置`} title={`${editData ? '编辑' : '新增'}配置`}
@ -15,7 +47,14 @@ const ConfigModal = (props) => {
onOk={() => { onOk={() => {
formRef.current.validateFields() formRef.current.validateFields()
.then(v => { .then(v => {
dispatch(editData ? gateway.edit(v, editData.id) : gateway.add(v)) v.id = editData?.id;
if (!v.in_config) {
v.in_config = '{}'
}
if (!v.out_config) {
v.out_config = '{}'
}
dispatch(editData ? gateway.edit([v]) : gateway.add([v]))
.then(res => { .then(res => {
if (res.success) { if (res.success) {
dispatch(gateway.list()) dispatch(gateway.list())
@ -31,55 +70,75 @@ const ConfigModal = (props) => {
<ProForm <ProForm
formRef={formRef} formRef={formRef}
autoFocusFirstInput autoFocusFirstInput
labelCol={{ span: 4 }} Layout={'vertical'}
wrapperCol={{ span: 18 }}
initialValues={ initialValues={
editData ? editData ?
editData : editData :
{ {
excuteTime: '00:00', in_mode: InMode[0],
isEnable: true out_mode: OutMode[0],
protocol: ProtocolTypes[0],
} }
} }
submitter={false} submitter={false}
formKey='config-form' formKey='config-form'
grid={true}
> >
<ProFormText <ProFormText name="name" label="名称" tooltip="最长为 24 位" placeholder="请输入名称" rules={[{ required: true, message: '请输入名称' }]} />
label="任务名称"
name={'name'}
placeholder="请输入名称"
required
rules={[{ required: true, message: '请输入名称' }]}
/>
<ProFormSelect <ProFormSelect
options={catalogs} colProps={{ span: 12 }}
cacheForSwr label="输入模式"
name="catalog" name="in_mode"
label="分类" valueEnum={InMode}
required
rules={[{ required: true, message: '请选择任务分类' }]}
/> />
<ProFormSelect <ProFormSelect
options={types} colProps={{ span: 12 }}
cacheForSwr label="输出模式"
name="type" name="out_mode"
label="类型" valueEnum={OutMode}
required
rules={[{ required: true, message: '请选择任务类型' }]}
/> />
<ProFormDateTimePicker <ProFormTextArea colProps={{ span: 24 }} name="in_config" label="输入配置" />
label="截止日期" <ProFormTextArea colProps={{ span: 24 }} name="out_config" label="输出配置" />
name="deadlineAt" <Form.Item name="protocol" label="协议" rules={[{ required: true }]}>
<Select
style={{ width: 300 }}
placeholder="选择协议"
dropdownRender={menu => (
<>
{menu}
<Divider style={{ margin: '8px 0' }} />
<Space style={{ padding: '0 8px 4px' }}>
<Input
placeholder="输入新增协议名"
ref={inputRef}
value={newProtocol}
onChange={onNewProtocolChange}
/>
<Button type="text" icon={<PlusOutlined />} onClick={addNewProtocol}>
添加项
</Button>
</Space>
</>
)}
>
{protocols.map(item => (
<Option key={item}>{item}</Option>
))}
</Select>
</Form.Item>
<ProFormTextArea colProps={{ span: 24 }}
name="protocol_info" label="协议信息"
/> />
</ProForm> </ProForm>
</Modal> </Modal>
} }
function mapStateToProps(state) { function mapStateToProps(state) {
const { auth, global, } = state; const { auth, global } = state;
return { return {
user: auth.user, user: auth.user,
actions: global.actions actions: global.actions,
}; };
} }

24
code/web/client/src/sections/gateway/containers/Gateway.js

@ -1,25 +1,19 @@
import React, { useState,useEffect,useRef } from 'react'; import React, { useState, useEffect, useRef } from 'react';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { Spin,Button, Card, Input } from 'antd'; import { Spin, Button, Card, Input } from 'antd';
import '../style.less'; import '../style.less';
import { push } from 'react-router-redux' import { push } from 'react-router-redux'
import ProTable, { TableDropdown } from '@ant-design/pro-table'; import ProTable, { TableDropdown } from '@ant-design/pro-table';
import { InMode, OutMode, ProtocolTypes } from './models';
const GatewayMode = ["tcp", "udp", "mqtt", "http", "dtu"] import ConfigModal from './ConfigModal';
const InMode = GatewayMode
const OutMode = GatewayMode
const ProtocolTypes = ["DeviceA", "DeviceB", "DeviceC", "DeviceD"]
const Gateway = (props) => { const Gateway = (props) => {
const { dispatch, actions, user, loading, gateways } = props const { dispatch, actions, user, loading, gateways, total } = props
const { gateway } = actions; const { gateway } = actions;
const [configModalVis, setConfigModalVis] = useState(false) const [configModalVis, setConfigModalVis] = useState(false)
const [editData, setEditData] = useState(null) const [editData, setEditData] = useState(null)
useEffect(() => { useEffect(() => {
// dispatch(task.getTaskList()) // dispatch(task.getTaskList())
}, []) }, [])
@ -97,7 +91,7 @@ const Gateway = (props) => {
const res = await dispatch(gateway.list(query)); const res = await dispatch(gateway.list(query));
return { return {
...res, ...res,
total: res.payload.data ? res.payload.data.count : 0 total: res.payload.data ? res.payload.data.total : 0
} }
}} }}
options={false} options={false}
@ -125,10 +119,12 @@ const Gateway = (props) => {
} }
function mapStateToProps(state) { function mapStateToProps(state) {
const { auth, global } = state; const { auth, global, gateways } = state;
return { return {
user: auth.user, user: auth.user,
actions: global.actions actions: global.actions,
loading: gateways.isRequesting,
gateways: gateways.data?.data ?? [],
}; };
} }

4
code/web/client/src/sections/gateway/containers/models.js

@ -0,0 +1,4 @@
const GatewayMode = ["tcp", "udp", "mqtt", "http", "dtu"]
export const InMode = GatewayMode
export const OutMode = GatewayMode
export const ProtocolTypes = ["DeviceA", "DeviceB", "DeviceC", "DeviceD"]

7
code/web/client/src/utils/webapi.js

@ -10,11 +10,12 @@ export const ApiTable = {
search: 'v1/search/{wd}', search: 'v1/search/{wd}',
//网关配置 //网关配置
getGateways: 'gateway/list', getGateways: 'v1/deviceProxy/query',
addGatewayConfig: 'gateway', addGatewayConfig: 'v1/deviceProxy/add',
editGatewayConfig: 'v1/deviceProxy/update',
getGatewayConfig: 'gateway/{gatewayId}', getGatewayConfig: 'gateway/{gatewayId}',
editGatewayConfig: 'gateway/{gatewayId}',
delGatewayConfig: 'gateway/{gatewayId}', delGatewayConfig: 'gateway/{gatewayId}',
getGatewayProtocols: 'v1/deviceProxy/protocols',
getEnterprisesMembers: 'enterprises/{enterpriseId}/members', getEnterprisesMembers: 'enterprises/{enterpriseId}/members',
}; };

2
code/web/package.json

@ -6,7 +6,7 @@
"scripts": { "scripts": {
"test": "mocha", "test": "mocha",
"start": "cross-env NODE_ENV=development npm run start-params", "start": "cross-env NODE_ENV=development npm run start-params",
"start-params": "npm run color && node server -p 5000 -u http://127.0.0.1:4100", "start-params": "npm run color && node server -p 5000 -u http://127.0.0.1:4101",
"deploy": "export NODE_ENV=production&&npm run color && npm run build && node server", "deploy": "export NODE_ENV=production&&npm run color && npm run build && node server",
"build-dev": "export NODE_ENV=development&&webpack --config webpack.config.js", "build-dev": "export NODE_ENV=development&&webpack --config webpack.config.js",
"build": "export NODE_ENV=production&&webpack --config webpack.config.prod.js", "build": "export NODE_ENV=production&&webpack --config webpack.config.prod.js",

Loading…
Cancel
Save