Sfoglia il codice sorgente

配料系统提交20241018

ingredientmanage
gwbvipvip 2 mesi fa
parent
commit
850239f2fd
32 ha cambiato i file con 2870 aggiunte e 787 eliminazioni
  1. +1
    -1
      config/defaultSettings.js
  2. +5
    -5
      config/proxy.js
  3. +84
    -8
      config/routes.js
  4. +67
    -0
      src/pages/batching/batchinginfo/components/CreateForm.jsx
  5. +224
    -0
      src/pages/batching/batchinginfo/index.jsx
  6. +34
    -0
      src/pages/batching/batchinginfo/service.js
  7. +1
    -1
      src/pages/company/account/components/CreateForm.jsx
  8. +0
    -6
      src/pages/company/account/index.jsx
  9. +1
    -1
      src/pages/company/account/service.js
  10. +42
    -126
      src/pages/device/deviceInfo/components/CreateForm.jsx
  11. +180
    -385
      src/pages/device/deviceInfo/index.jsx
  12. +0
    -4
      src/pages/device/deviceInfo/index.less
  13. +43
    -0
      src/pages/device/deviceInfo/service.js
  14. +0
    -247
      src/pages/device/deviceInfo/services.js
  15. +18
    -0
      src/pages/interfacedoc/index/index.jsx
  16. +1
    -1
      src/pages/org/roles/service.js
  17. +203
    -0
      src/pages/recipe/recipeinfo/components/CreateAttrForm.jsx
  18. +56
    -0
      src/pages/recipe/recipeinfo/components/CreateForm.jsx
  19. +393
    -0
      src/pages/recipe/recipeinfo/components/RecipeDetailForm.jsx
  20. +197
    -0
      src/pages/recipe/recipeinfo/components/RecipeInfoForm.jsx
  21. +57
    -0
      src/pages/recipe/recipeinfo/components/UpdateRecipeInfoForm.jsx
  22. +279
    -0
      src/pages/recipe/recipeinfo/index.jsx
  23. +94
    -0
      src/pages/recipe/recipeinfo/service.js
  24. +139
    -0
      src/pages/report/batchingreport/index.jsx
  25. +188
    -0
      src/pages/report/recipereport/index.jsx
  26. +22
    -0
      src/pages/report/service.js
  27. +63
    -0
      src/pages/store/storeinfo/components/CreateForm.jsx
  28. +319
    -0
      src/pages/store/storeinfo/index.jsx
  29. +51
    -0
      src/pages/store/storeinfo/service.js
  30. +96
    -0
      src/pages/sys/devicelog/index.jsx
  31. +10
    -0
      src/pages/sys/devicelog/service.js
  32. +2
    -2
      src/pages/user/login/index.jsx

+ 1
- 1
config/defaultSettings.js Vedi File

@@ -7,7 +7,7 @@ const Settings = {
fixedHeader: false,
fixSiderbar: true,
colorWeak: false,
title: '黑菠萝智慧后厨',
title: '配料系统',
pwa: false,
logo: '/logo.svg',
iconfontUrl: '',


+ 5
- 5
config/proxy.js Vedi File

@@ -9,7 +9,7 @@
export default {
dev: {
'/saasbase/': {
target: 'http://localhost:5006/',
target: 'http://192.168.1.50:5006/',
changeOrigin: true,
secure: false, //关闭证书验证
pathRewrite: {
@@ -17,7 +17,7 @@
},
},
'/kitchbase/': {
target: 'http://localhost:5007/',
target: 'http://192.168.1.50:5007/',
changeOrigin: true,
secure: false, //关闭证书验证
pathRewrite: {
@@ -25,7 +25,7 @@
},
},
'/kitchorder/': {
target: 'http://localhost:5005/',
target: 'http://192.168.1.50:5005/',
changeOrigin: true,
secure: false, //关闭证书验证
pathRewrite: {
@@ -44,7 +44,7 @@
},
test: {
'/api/': {
target: 'http://localhost:5006',
target: 'http://192.168.1.50:5006',
changeOrigin: true,
secure: false,
pathRewrite: {
@@ -54,7 +54,7 @@
},
pre: {
'/api/': {
target: 'http://localhost:5006',
target: 'http://192.168.1.50:5006',
changeOrigin: true,
secure: false,
pathRewrite: {


+ 84
- 8
config/routes.js Vedi File

@@ -40,13 +40,13 @@ export default [
// component: './sys/dictionary/dictdata',
// access: 'k6',
// },
{
name: '字典类型',
icon: 'smile',
path: '/sys/dictionary/dicttype',
component: './sys/dictionary/dicttype',
access: 'k6',
},
// {
// name: '字典类型',
// icon: 'smile',
// path: '/sys/dictionary/dicttype',
// component: './sys/dictionary/dicttype',
// access: 'k6',
// },
// {
// name: '操作日志',
// icon: 'smile',
@@ -61,7 +61,13 @@ export default [
// component: './sys/log',
// access: 'k3',
// },
{
name: '设备日志',
icon: 'smile',
path: '/sys/devicelog',
component: './sys/devicelog',
access: 'k3',
},
],
},
{
@@ -360,6 +366,76 @@ export default [
}
],
},
{
name: '接口文档',
icon: 'smile',
path: '/interfacedoc/index',
component: './interfacedoc/index',
access: 'k12'
},
{
name: '物料管理',
icon: 'BankFilled',
path: '/batching',
routes: [
{
name: '物料信息',
icon: 'smile',
path: '/batching/batchinginfo',
component: './batching/batchinginfo',
access: 'k12',
}
]
},
{
name: '配方管理',
icon: 'BankFilled',
path: '/recipe',
routes: [
{
name: '配方信息',
icon: 'smile',
path: '/recipe/recipeinfo',
component: './recipe/recipeinfo',
access: 'k12',
}
]
},
{
name: '门店管理',
icon: 'BankFilled',
path: '/store',
routes: [
{
name: '门店基础信息',
icon: 'smile',
path: '/store/storeinfo',
component: './store/storeinfo',
access: 'k12',
}
]
},
{
name: '报表管理',
icon: 'BankFilled',
path: '/report',
routes: [
{
name: '配方明细',
icon: 'smile',
path: '/report/recipereport',
component: './report/recipereport',
access: 'k12',
},
{
name: '物料明细',
icon: 'smile',
path: '/report/batchingreport',
component: './report/batchingreport',
access: 'k12',
}
]
},
{
path: '/',
redirect: '/welcome',


+ 67
- 0
src/pages/batching/batchinginfo/components/CreateForm.jsx Vedi File

@@ -0,0 +1,67 @@
import { Modal, Form, TreeSelect, Button, Input } from 'antd';


const CreateForm = (props) => {
return (
<Modal
title={props.values.id ? '编辑物料' : '新建物料'}
width={640}
bodyStyle={{ padding: '32px 40px 1px 48px' }}
visible={props.modalVisible}
onCancel={props.onCancel}
footer={null}
maskClosable={false}
destroyOnClose
>
<Form
layout="horizontal"
preserve={false}
labelCol={{
span: 4,
}}
initialValues={props.values}
onFinish={props.onFinish}
>
<Form.Item name="id" hidden={true}>
<Input />
</Form.Item>
<Form.Item name="name" label="物料名称" rules={[{ required: true }]}>
<Input placeholder='请输入物料名称'/>
</Form.Item>
<Form.Item name="code" label="物料编码">
<Input disabled={true} />
</Form.Item>
<Form.Item name="type" label="物料类型">
<TreeSelect
style={{ width: '100%' }}
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
treeData={props.typeList}
placeholder="请选择物料类型"
treeDefaultExpandAll
allowClear
/>
</Form.Item>
<Form.Item name="stockUint" label="物料单位">
<TreeSelect
style={{ width: '100%' }}
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
treeData={props.uintList}
placeholder="请选择物料单位"
treeDefaultExpandAll
allowClear
/>
</Form.Item>
<Form.Item>
<Button htmlType="button" style={{ float: 'right', left: 10 }} onClick={props.onCancel}>
取消
</Button>
<Button type="primary" htmlType="submit" style={{ float: 'right' }}>
保存
</Button>
</Form.Item>
</Form>
</Modal>
)
}

export default CreateForm;

+ 224
- 0
src/pages/batching/batchinginfo/index.jsx Vedi File

@@ -0,0 +1,224 @@
import { PlusOutlined } from '@ant-design/icons';
import { Button, message, Popconfirm } from 'antd';
import React, { useState, useRef } from 'react';
import { PageContainer } from '@ant-design/pro-layout';
import ProTable from '@ant-design/pro-table';
import CreateForm from './components/CreateForm';
import api from './service';
import { randomString } from '../../comm';

const batchinginfo = () => {
const actionRef = useRef();
const [createModalVisible, handleModalVisible] = useState();
const [stepFormValues, setStepFormValues] = useState({});
const [selectedRowsState, setSelectedRows] = useState();
const uintList = [
{ value: '1', label: 'g' },
{ value: '2', label: 'kg' },
];
const typeList = [
{ value: '1', label: '液体料' },
{ value: '2', label: '主料' },
];
const handleAdd = async (fields) => {
await api.add(fields).then((r) => {
if (r.data) {
message.success('添加成功');
} else {
message.error(r.errors);
}
});
};
const handleUpdate = async (fields) => {
await api.update(fields).then((r) => {
if (r.data) {
message.success('修改成功');
} else {
message.error(r.errors);
}
});
};
const BatchDeletion = () => {
return (
selectedRowsState?.length > 0 &&
(
<Popconfirm
title="确认删除吗?"
onConfirm={onClickdele}
okText="确定"
cancelText="取消"
>
<Button type="primary">批量删除</Button>
</Popconfirm>
))
}
const onClickdele = () => {
handleRemove(selectedRowsState);
setSelectedRows([]);
actionRef.current?.reloadAndRest?.();
}
const handleRemove = async (selectedRows) => {
if (!selectedRows) return true;
await api.delete(selectedRows.map((row) => row.id)).then((r) => {
if (r.data) {
message.success('删除成功');
actionRef.current.reload();
} else {
message.error(r.errors);
}
});
return true;
};
const columns = [
{
title: '主键',
dataIndex: 'id',
tip: 'key',
hideInSearch: true,
hideInTable: true
},
{
title: '物料名称',
dataIndex: 'name',
ellipsis: true,
},
{
title: '物料编码',
dataIndex: 'code',
ellipsis: true,
hideInSearch: true,
},
{
title: '物料类别',
dataIndex: 'type',
valueType: 'select',
fieldProps: {
options: typeList,
},
},
{
title: '物料单位',
dataIndex: 'stockUint',
valueType: 'select',
fieldProps: {
options: uintList,
},
},
{
title: '创建时间',
dataIndex: 'createAt',
valueType: 'date',
hideInForm: true,
hideInSearch: true,
},
{
title: '操作',
dataIndex: 'option',
valueType: 'option',
fixed: 'right',
width: 350,
render: (_, record) => [
<a
onClick={() => {
handleModalVisible(true);
setStepFormValues(record);
}}
>
更新
</a>,
<Popconfirm
type="primary"
key="primary"
title="确认删除吗?"
okText="是"
cancelText="否"
onConfirm={() => {
api.deleteBatching([record.id]).then((r) => {
if (r.succeeded) {
message.success('删除成功');
actionRef.current.reload();
} else {
message.error(r.errors);
}
});
}}
>
<a href="#">删除</a>
</Popconfirm>,
],
}
]

return (
<PageContainer
header={{
title: '',
breadcrumb: {},
}}
>
<ProTable
headerTitle="物料列表"
actionRef={actionRef}
rowKey="id"
pagination={{ defaultPageSize: 10 }}
search={{
labelWidth: 120,
}}
toolBarRender={() => [
<Button
type="primary"
key="primary"
onClick={() => {
var values = { code: randomString(4) }
setStepFormValues(values);
handleModalVisible(true);
}}
>
<PlusOutlined /> 新建
</Button>,
<BatchDeletion />
]}
request={async (params) => {
let batchingData = [];
var total = 0;
await api.getBatchingPage(params).then((r) => {
batchingData = r.data.data;
total = r.data.total;
});
return {
data: batchingData,
total: total,
};
}}
columns={columns}
rowSelection={{
onChange: (_, selectedRows) => {
setSelectedRows(selectedRows);
},
}}
/>
<CreateForm
uintList={uintList}
typeList={typeList}
onFinish={async (value) => {
setStepFormValues({});
if (value.id) {
await handleUpdate(value);
} else {
await handleAdd(value);
}
handleModalVisible(false);
actionRef.current.reload();
}}
onCancel={() => {
handleModalVisible(false);
setStepFormValues({});
}}
modalVisible={createModalVisible}
values={stepFormValues || {}}
/>
</PageContainer>
)
}

export default batchinginfo;

+ 34
- 0
src/pages/batching/batchinginfo/service.js Vedi File

@@ -0,0 +1,34 @@
import { request } from 'umi';
import { getDataBaseUrl } from '@/global_data';
export default {
getBatchingPage(params) {
return request(getDataBaseUrl() + '/api/batching/getBatchingPage', {
method: 'Post',
data: params,
});
},
deleteBatching(data) {
return request(getDataBaseUrl() + `/api/batching/delete`, {
method: 'Post',
data: data,
});
},
add(params) {
return request(getDataBaseUrl() + '/api/batching/add', {
method: 'Post',
data: params,
});
},
update(params) {
return request(getDataBaseUrl() + '/api/batching/update', {
method: 'Post',
data: params,
});
},
delete(data) {
return request(getDataBaseUrl()+`/api/batching/delete`, {
method: 'Post',
data: data,
});
},
}

+ 1
- 1
src/pages/company/account/components/CreateForm.jsx Vedi File

@@ -33,7 +33,7 @@ const CreateForm = (props) => {
<Input placeholder="请输入管理员姓名" />
</Form.Item>
<Form.Item name="email" label="登陆账号(手机号)"
<Form.Item name="phone" label="登陆账号(手机号)"
tooltip="初始密码为:123456"
rules={[{ required: true, max: 50,message:"请输入手机号"},{pattern: /^1[3-9][0-9]{9}$/,
message: '手机号格式不正确', }]}>


+ 0
- 6
src/pages/company/account/index.jsx Vedi File

@@ -160,11 +160,6 @@ const companyManage = () => {
dataIndex: 'adminName',
ellipsis: true,
},
{
title: '邮箱账号',
dataIndex: 'email',
ellipsis: true,
},
{
title: '电话',
dataIndex: 'phone',
@@ -338,7 +333,6 @@ const companyManage = () => {
if (value.id) {
success = await handleUpdate(value);
} else {
value.phone=value.email;
success = await handleAdd(value);
}
if (success) {


+ 1
- 1
src/pages/company/account/service.js Vedi File

@@ -57,7 +57,7 @@ export default {
},
/** 获取角色菜单*/
GetRoleMenu(params) {
return request(getDataBaseUrl()+`/api/roles/getmenurole/?RoleId=${params}`);
return request(getDataBaseUrl()+`/api/roles/getmenurole?RoleId=${params}`);
},
/** 授权菜单功能获取数据*/
menutreepage(params) {


+ 42
- 126
src/pages/device/deviceInfo/components/CreateForm.jsx Vedi File

@@ -1,151 +1,67 @@
import React, { useState, useEffect,useRef } from 'react';
import { PlusOutlined } from '@ant-design/icons';
import { Modal, Form, Input, Button, Select,Radio,Divider,Space,message } from 'antd';
import {
GetDeviceVesion,
AddDeviceType
} from '../services';
import { Modal, Form, TreeSelect, Button, Input } from 'antd';

const CreateForm = (props) => {
const [deviceTypename,setdeviceTypename]=useState("");
const [options,setoptions]=useState([]);
const [stopoptions,setstopoptions]=useState([]);
const [vesionoptions,setvesionoptions]=useState([]);
const inputRef = useRef(null);
useEffect(() => {
if (Object.keys(props.values).length > 0) {
var vesiondata = props.deviceVersions.map((item) => {
return { value: item.id, label: item.vesion }
})
}
setvesionoptions(vesiondata);
var optdata = props.dicData.map((item) => {
return { value: item.id, label: item.label }
})
setoptions(optdata);
var stopData = props.StopData?.map((item) => {
return { value: item.id, label: item.name }
})
setstopoptions(stopData)
}, [props])
const handleChange=(value)=>{
GetDeviceVesion(value).then((res)=>{
var vesiondata = res.data.map((item) => ({
value: item.id, label: item.vesion
}))
setvesionoptions(vesiondata);
})
}
const onNameChange=(event)=>{
setdeviceTypename(event.target.value);
}
const addItem=(e)=>{
e.preventDefault();
if(deviceTypename.length==0){
message.error("请输入名称");
}else{
var parm={name:deviceTypename}
AddDeviceType(parm).then((r)=>{
if(r.succeeded){
message.success('添加成功');
props.initDeviceType()
setdeviceTypename('');
}else{
message.error(r.errors);
}
})
}
}
const { TextArea } = Input;
return (
<Modal
title={props.values.id ? '编辑' : '新建'}
title={props.values.id ? '编辑设备' : '新建设备'}
width={640}
visible={props.createModalVisible}
maskClosable={false}
bodyStyle={{ padding: '32px 40px 48px' }}
bodyStyle={{ padding: '32px 40px 1px 48px' }}
open={props.modalOpen}
onCancel={props.onCancel}
footer={null}
onCancel={() => {
props.onCancel();
}}
destroyOnClose={true}
maskClosable={false}
destroyOnClose
>
<Form
layout="Horizontal"
labelCol={{ span: 4 }}
layout="horizontal"
preserve={false}
labelCol={{
span: 4,
}}
initialValues={props.values}
onFinish={props.onFinish}
>
<Form.Item name="id" hidden={true}>
<Input />
</Form.Item>
<Form.Item label={'选择场景'} name="stopId" rules={[{ required: true }]}>
<Select allowClear showSearch optionFilterProp="children" filterOption={(input, option) => (option?.label ?? '').includes(input)}
filterSort={(optionA, optionB) =>
(optionA?.label ?? '').toLowerCase().localeCompare((optionB?.label ?? '').toLowerCase()) } options={stopoptions}/>
</Form.Item>
<Form.Item label={'产品'} name="productId" rules={[{ required: true }]}>
<Select onChange={handleChange} allowClear showSearch optionFilterProp="children" filterOption={(input, option) => (option?.label ?? '').includes(input)}
filterSort={(optionA, optionB) =>
(optionA?.label ?? '').toLowerCase().localeCompare((optionB?.label ?? '').toLowerCase())
} options={options}/>
<Form.Item name="name" label="设备名称" rules={[{ required: true }]}>
<Input placeholder='请输入设备名称' />
</Form.Item>
<Form.Item label={'产品版本'} name="productVersionId" rules={[{ required: true }]}>
<Select allowClear showSearch optionFilterProp="children" filterOption={(input, option) => (option?.label ?? '').includes(input)}
filterSort={(optionA, optionB) => (optionA?.label ?? '').toLowerCase().localeCompare((optionB?.label ?? '').toLowerCase())} options={vesionoptions}/>
<Form.Item name="storeId" label="门店名称" rules={[{ required: true }]}>
<TreeSelect
style={{ width: '100%' }}
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
treeData={props.storeList}
placeholder="请选择门店名称"
treeDefaultExpandAll
allowClear
/>
</Form.Item>
<Form.Item label={'设备名称'} name="deviceName" rules={[{ required: true, max: 50 }]}>
<Input />
</Form.Item>
<Form.Item label={'设备标签'} name="deviceTypeId" rules={[{ required: false }]}>
<Select
style={{width: '100%'}}
placeholder="请选设备标签"
options={props.storeType}
allowClear showSearch
optionFilterProp="children" filterOption={(input, option) => (option?.label ?? '').includes(input)}
filterSort={(optionA, optionB) =>
(optionA?.label ?? '').toLowerCase().localeCompare((optionB?.label ?? '').toLowerCase())
}
dropdownRender={(menu) => (
<>
{menu}
<Divider style={{margin: '8px 0'}}/>
<Space style={{padding: '0 8px 4px'}}>
<Input
placeholder="请输入设备标签"
ref={inputRef}
value={deviceTypename}
onChange={onNameChange}
onKeyDown={(e) => e.stopPropagation()}
/>
<Button type="text" icon={<PlusOutlined />} onClick={addItem}>
新增
</Button>
</Space>
</>
)}
/>
<Form.Item name="isEnable" label="使用情况" rules={[{ required: true }]}>
<TreeSelect
style={{ width: '100%' }}
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
treeData={props.enableList}
placeholder="请选择使用情况"
treeDefaultExpandAll
allowClear
/>
</Form.Item>
<Form.Item label={'地址'} name="address" rules={[{ max: 50 }]}>
<Input />
<Form.Item name="remark" label="备注">
<TextArea rows={4} rules={[{ max: 500 }]} />
</Form.Item>
{/* <Form.Item label={'支持功能'} name="technologyOrBom" rules={[{ required: true }]}>
<Radio.Group onChange={onChange} value={value}>
<Radio value={0}>工艺</Radio>
<Radio value={1}>配方</Radio>
</Radio.Group>
</Form.Item> */}
<Form.Item>
<Button type="primary" htmlType="submit" style={{float:'right'}} >
<Button htmlType="button" style={{ float: 'right', left: 10 }} onClick={props.onCancel}>
取消
</Button>
<Button type="primary" htmlType="submit" style={{ float: 'right' }}>
保存
</Button>
</Form.Item>
</Form>
</Modal>
);
};
)
}

export default CreateForm;
export default CreateForm;

+ 180
- 385
src/pages/device/deviceInfo/index.jsx Vedi File

@@ -1,164 +1,56 @@
import { PlusOutlined } from '@ant-design/icons';
import { Modal, Button, message, Popconfirm,Transfer } from 'antd';
import { Button, message, Popconfirm, Modal, Transfer, Select } from 'antd';
import React, { useState, useRef, useEffect } from 'react';
import { PageContainer } from '@ant-design/pro-layout';
import ProTable from '@ant-design/pro-table';
import CreateForm from './components/CreateForm';
import {
GetDeviceInfoPage,
GetProductList,
AddDeviceInfo,
UpdateDeviceInfo,
DelDeviceInfo,
GetDeviceType,
GetDeviceVesion,
GetStop,
TechnologyTemplateExport,
GoodsTemplateExport,
Getgoodslist,
getdevicegoods,
editdevicegoodsapi,
GetDeviceVersions
} from './services';
import { gettree } from '../../org/orgamange/service';
import api from './service';

import './index.less';

import { forEach, values } from 'lodash';
const key = 'message';


const GoodsTypeManage = () => {
const [storeType, setStoreType] = useState();

const [storeTypeArray, setStoreTypeArray] = useState([]);
const [OrgList, setOrgList] = useState([]);
const [OrgListArray, setOrgListArray] = useState([]);
const [dicDataAny, setDicDataAny] = useState([]);
const [dicData, setDicData] = useState([]);
const [deviceVersion, setDeviceVersion] = useState([]);
/** 新建/更新窗口的弹窗 */
const [createModalVisible, handleModalVisible] = useState(false);
/** 分布更新窗口的弹窗 */
const deviceinfo = () => {
const actionRef = useRef();
const [currentRow, setCurrentRow] = useState();
const [selectedRowsState, setSelectedRows] = useState([]);
const [StopData, setStopData] = useState([]);
const [selectedRowsState, setSelectedRows] = useState();
const [storeList, setStoreList] = useState([]);
const [createModalOpen, handleModalOpen] = useState();
const [stepFormValues, setStepFormValues] = useState({});
const [isModalOpen, setIsModalOpen] = useState(false);

const [mockData, setMockData] = useState([]);
const [targetKeys, setTargetKeys] = useState([]);

const [googsData,setGoogs]= useState([]);


//初始化数据
const [deviceId, setDeviceId] = useState([]);
const [storeId, setStoreId] = useState([]);
const [isWeight, setIsWeight] = useState('');
const enableList = [
{ value: '1', label: '启用' },
{ value: '0', label: '停用' },
];
useEffect(() => {
GetStop().then((t) => {
setStopData(t.data)
getAllStoreList()
}, []);
function getAllStoreList() {
api.getAllStoreList().then((r) => {
var data = r.data;
const storeList = [];
data.forEach((item) => {
storeList.push({
value: item.id,
label: item.name
})
})
setStoreList(storeList);
});

Getgoodslist().then((item) => {
var data=item.data;
}
function getStoreRecipeList(id, isWeight) {
api.getStoreRecipeList(id, isWeight).then((item) => {
var data = item.data;
const tempMockData = [];
data.forEach((item)=>{
data.forEach((item) => {
tempMockData.push({
key: item.id,
title: item.name,
description: item.name,
});
})
setMockData(tempMockData);
});

function initOrgList() {
gettree().then((r) => {
inittree(r.data);
function inittree(datas) {
for (var i in datas) {
if (datas[i]['type'] == 2 || datas[i]['type'] == 3) {
datas[i]['disabled'] = false;
} else {
datas[i]['disabled'] = true;
}

if (datas[i].children) {
inittree(datas[i].children);
}
}
}
setOrgList(r.data);
let list = {};
formateData(r.data);
function formateData(datas) {
for (var i in datas) {
list[datas[i]['disabled']] = true;
list[datas[i]['value']] = { text: datas[i]['title'] };
if (datas[i].children) {
formateData(datas[i].children);
}
}
}
setOrgListArray(list);
});
}
initOrgList();
initDeviceType();
function intDicData() {
GetProductList().then((r) => {
var arr = r.data;
let data = {};
if (r.succeeded) {
var list = [];
arr.forEach((item) => {
data[item.name] = { text: item.name };
list.push({
id: item.id,
label: item.name,
text: item.name,
});
});
setDicDataAny(data);
setDicData(list);
}
});
}
intDicData();
initDeviceVersionList();
}, []);
const initDeviceType = () => {
GetDeviceType().then((data) => {
let list = {};
let list2 = [];
data.data.forEach((item) => {
list2.push({
label: item.name,
text: item.name,
value: item.id,
});
list[item.id] = { text: item.name };
});
setStoreTypeArray(list2);
setStoreType(list);
});
};
const initDeviceVersionList = async()=>{
const response = await GetDeviceVersions();
if (response.statusCode == 200) {
setDeviceVersion(response.data);
} else {
message.error('获取设备版本失败');
}
}
const initDeviceVersion = async (record) => {
const response = await GetDeviceVesion(record?.productId);
if (response.statusCode == 200) {
setDeviceVersion(response.data);
} else {
message.error('获取设备版本失败');
}
};
const BatchDeletion = () => {
return (
selectedRowsState?.length > 0 &&
@@ -178,283 +70,168 @@ const GoodsTypeManage = () => {
setSelectedRows([]);
actionRef.current?.reloadAndRest?.();
}
/**
* 删除节点
*
* @param selectedRows
*/
const handleRemove = async (selectedRows) => {
if (!selectedRows) return true;
await DelDeviceInfo(selectedRows.map((row) => row.id)).then((r) => {
await api.deleteDevice(selectedRows.map((row) => row.id)).then((r) => {
if (r.data) {
message.success('删除成功');
actionRef.current.reload();
} else {
message.error('删除失败,请重试');
message.error(r.errors);
}
});
return true;
};
/** 国际化配置 */

const editDeviceRecipe = async () => {
const tempMockData = [];
targetKeys.forEach(function (item, index, array) {
tempMockData.push({
"recipeId": item
});
})
var data = {
"deviceId": deviceId,
"recipeIdList": tempMockData
}
api.setDeviceRelation(data).then((r) => {
if (r.succeeded) {
message.success('编辑成功');
} else {
message.error(r.errors)
}
});
setIsModalOpen(false);
actionRef.current.reload();
};
const setrecipeList = async (id) => {
var list = [];
api.getDeviceRelation(id).then((item) => {
item.data.forEach(function (item) {
list.push(item.recipeId);
})
setTargetKeys(list);
});
};
const handleChange = (newTargetKeys) => {
setTargetKeys(newTargetKeys);
};
const handleModeChange = (value) => {
setIsWeight(value);
getStoreRecipeList(storeId, value);
};
const columns = [
{
title: '主键',
dataIndex: 'id',
tip: 'key',
hideInSearch: true,
hideInTable: true,
tip: '规则名称是唯一的 key',
hideInTable: true
},
{
title: '设备名称',
dataIndex: 'deviceName',
valueType: 'textarea',
dataIndex: 'name',
ellipsis: true,
},
{
title: '产品',
dataIndex: 'productName',
valueEnum: dicDataAny,
title: '门店名称',
dataIndex: 'storeId',
valueType: 'select',
hideInSearch: true,
fieldProps: {
options: storeList,
},
},
{
title: '产品版本',
dataIndex: 'productVersionName',
search: false,
},
{
title: '所属场景',
dataIndex: 'stopName',
search: false,

},
{
title: '地址',
dataIndex: 'address',
search: false,
},
// {
// title: '归属门店',
// dataIndex: 'orgId',
// search: false,
// valueEnum: OrgListArray,
// },
{
title: '设备标签',
dataIndex: 'deviceTypeId',
valueEnum: storeType,
title: '使用情况',
dataIndex: 'isEnable',
valueType: 'select',
hideInSearch: true,
fieldProps: {
options: enableList,
},
},
{
title: '设备唯一key',
dataIndex: 'autoKey',
search: false,
ellipsis: true,
hideInSearch: true,
},
{
title: '备注',
dataIndex: 'remark',
ellipsis: true,
hideInSearch: true,
},
// {
// title: '归属门店唯一key',
// dataIndex: 'orgKey',
// search: false,
// },
{
title: '操作',
dataIndex: 'option',
valueType: 'option',
fixed: 'right',
width: 300,
width: 350,
render: (_, record) => [
<a
key="update"
onClick={() => {
initDeviceVersion(record);
handleModalVisible(true);
setCurrentRow(record);
handleModalOpen(true);
setStepFormValues(record);
}}
>
更新
</a>,
<a
key="edit"
onClick={() => {
getStoreRecipeList(record.storeId, isWeight);
setStoreId(record.storeId);
setrecipeList(record.id);
setDeviceId(record.id);
setIsModalOpen(true);
}}
>
设置设备配方
</a>,
<Popconfirm
type="primary"
key="primary"
title="确认删除吗?"
okText="是"
cancelText="否"
onConfirm={async () => {
await DelDeviceInfo([record.id]).then((r) => {
if (r.data) {
onConfirm={() => {
api.deleteDevice([record.id]).then((r) => {
if (r.succeeded) {
message.success('删除成功');
actionRef.current.reload();
} else {
message.error(r.errors);
}

});
}}
onCancel={() => { }}
>
<a href="#">删除</a>
</Popconfirm>,
// <a
// key="copy"
// onClick={() => {
// CopyDevice({ id: record.id }).then((r) => {
// if (r.data) {
// actionRef.current.reload();
// }
// });
// }}
// >
// 复制
// </a>,
// <a
// key="upload-goods"
// onClick={() => {
// setCurrentRow(record);
// setDeviceFoodModal(true);
// }}
// >
// 绑定商品
// </a>,
// <a
// key="add"
// onClick={() => {
// setCurrentRow(record);
// setStockModal(true);
// }}
// >
// 库存原料
// </a>,
<a
key="add"
onClick={async () => {
const response = await TechnologyTemplateExport(record.id);
if (response == '') {
message.error('当前模板不存在!');
return;
}
// const blob = new Blob([response]);
// const url = URL.createObjectURL(blob);
// console.log(blob, url);
// const link = document.createElement('a');
// link.download = `工艺模板_${new Date(Date.now()).toLocaleString()}.xls`;
// link.href = url;
// link.click();
const link = document.createElement('a');
link.href = response;
link.click();
}}
>
导出工艺模版
</a>,

// <a
// key="edit"
// onClick={() => {
// GetdevicegoodsList(record.id);
// setCurrentRow(record);
// setIsModalOpen(true);
// }}
// >
// 设置设备商品
// </a>,

],
},
];

//添加
const handleAdd = async (fields) => {

await AddDeviceInfo(JSON.stringify(fields)).then((r) => {

if (r.succeeded) {
message.success('添加成功');
handleModalVisible(false);
actionRef.current.reload();
} else {
message.error(r.errors)
return false;
}
});

};
//修改
const handleUpdate = async (fields) => {
await UpdateDeviceInfo(JSON.stringify(fields)).then((r) => {
if (r.succeeded) {
message.success('更新成功');
handleModalVisible(false);
actionRef.current.reload();
} else {
message.error(r.errors)
return false;
}
});

};

//修改
const editdevicegoods = async () => {

const tempMockData = [];

targetKeys.forEach(function (item, index, array) {
tempMockData.push({
"goodsId" : item,
"goodsName":""
});
})
var data={
"deviceId": currentRow.id,
"goodsList": tempMockData,
"autoKey":currentRow.autoKey
}
editdevicegoodsapi(data).then((r) => {
if (r.succeeded) {
message.success('编辑成功');
handleModalVisible(false);
actionRef.current.reload();
} else {
message.error(r.errors)
return false;
}
});
setIsModalOpen(false);
};

//修改
const GetdevicegoodsList = async (deviceId ) => {
var list=[];
getdevicegoods(deviceId).then((item) => {
item.data.forEach(function (item, index, array) {
list.push(item.goodsId);
})
setTargetKeys(list);
});
};


const handleChange = (newTargetKeys) => {
setTargetKeys(newTargetKeys);
};

]
return (
<PageContainer host header={{
title: '',
breadcrumb: {},
}}>
<PageContainer
header={{
title: '',
breadcrumb: {},
}}
>
<ProTable
headerTitle="设备信息"
headerTitle="设备列表"
actionRef={actionRef}
rowKey="id"
pagination={{ defaultPageSize: 10 }}
search={{
labelWidth: 120,
}}
scroll={{ x: 2000 }}
toolBarRender={() => [
<Button
type="primary"
key="primary"
onClick={() => {
handleModalVisible(true);
handleModalOpen(true);
}}
>
<PlusOutlined /> 新建
@@ -462,23 +239,14 @@ const GoodsTypeManage = () => {
<BatchDeletion />
]}
request={async (params) => {
var data = [];
let storeData = [];
var total = 0;
await GetDeviceInfoPage(params).then((r) => {
// console.log("StopData", r)
// r.data.data.forEach(x=>{
// var stop=StopData.find(f=>f.id==x.stopId);
// if(stop){
// x.stopName=stop.name
// }

// })
data = r.data.data;
await api.getDevicePage(params).then((r) => {
storeData = r.data.data;
total = r.data.total;
});
return {
data: data,
success: true,
data: storeData,
total: total,
};
}}
@@ -489,45 +257,72 @@ const GoodsTypeManage = () => {
},
}}
/>

<CreateForm
storeType={storeTypeArray}
OrgData={OrgList}
dicData={dicData}
initDeviceType={initDeviceType}
storeList={storeList}
enableList={enableList}
onFinish={async (value) => {
if (value.id) {
handleUpdate(value);
} else {
handleAdd(value);
}
await api.updateDevice(value).then(r => {
if (r.data) {
if (value.id == undefined) {
message.success("添加成功")
}
else {
message.success("修改成功")
}
}
else {
message.error(r.errors)
}
})
handleModalOpen(false);
setStepFormValues({});
actionRef.current.reload();
}}
onCancel={() => {
handleModalVisible(false);
setCurrentRow(undefined);
handleModalOpen(false);
setStepFormValues({});
}}
createModalVisible={createModalVisible}
deviceVersions={deviceVersion}
values={currentRow || {}}
StopData={StopData}
modalOpen={createModalOpen}
values={stepFormValues || {}}
/>


<Modal width={600} title="编辑设备商品" open={isModalOpen} onOk={() => {editdevicegoods() }} onCancel={() => { setIsModalOpen(false); }}>
<Modal
width={800}
title="设置门店配方"
open={isModalOpen}
destroyOnClose
onOk={() => { editDeviceRecipe() }}
onCancel={() => {
setIsModalOpen(false)
setIsWeight('')
}}
>
<Select
defaultValue="全部"
style={{ width: 120, marginBottom: 10 }}
onChange={handleModeChange}
>
<Option value="">全部</Option>
<Option value={false}>标准模式</Option>
<Option value={true}>称重模式</Option>
</Select>
<Transfer
className="tree-transfer"
className="tree-transfer"
listStyle={{
width: 500,
height: 500,
}}
dataSource={mockData}
targetKeys={targetKeys}
onChange={handleChange}
onSearch={()=>{}}
showSearch
pagination
render={(item) => item.title}
/>
</Modal>




</PageContainer>
);
};
)
}

export default GoodsTypeManage;
export default deviceinfo;

+ 0
- 4
src/pages/device/deviceInfo/index.less Vedi File

@@ -1,4 +0,0 @@
.tree-transfer .ant-transfer-list:first-child {
flex: none;
width: 50%;
}

+ 43
- 0
src/pages/device/deviceInfo/service.js Vedi File

@@ -0,0 +1,43 @@
import { request } from 'umi';
import { getDataBaseUrl } from '@/global_data';
export default {
getDevicePage(params) {
return request(getDataBaseUrl() + '/api/store/getDevicePage', {
method: 'Post',
data: params,
});
},
getAllStoreList() {
return request(getDataBaseUrl() + `/api/store/getAllStoreList`, {
method: 'Get',
});
},
updateDevice(data) {
return request(getDataBaseUrl() + '/api/store/updateDevice', {
method: 'Post',
data: data,
});
},
deleteDevice(data) {
return request(getDataBaseUrl() + '/api/store/deleteDevice', {
method: 'Post',
data: data,
});
},
getStoreRecipeList(id, isWeight) {
return request(getDataBaseUrl() + `/api/recipe/getStoreRecipeList?storeId=${id}&isWeight=${isWeight}`, {
method: 'Get',
});
},
getDeviceRelation(id) {
return request(getDataBaseUrl() + `/api/store/getDeviceRelation?deviceId=${id}`, {
method: 'Get',
});
},
setDeviceRelation(data) {
return request(getDataBaseUrl() + '/api/store/setDeviceRelation', {
method: 'Post',
data: data,
});
},
}

+ 0
- 247
src/pages/device/deviceInfo/services.js Vedi File

@@ -1,247 +0,0 @@
import { request } from 'umi';
import { getDataBaseUrl,GetkitchbaseUrl } from '@/global_data';
/** 获取设备列表 */
export async function GetDeviceInfoPage(data) {
return request(getDataBaseUrl()+`/api/device/page`, {
method: 'POST',
data: data,
});
}
/**获取产品 */
export async function GetProductList() {
return request(getDataBaseUrl()+`/api/product/list`, {
method: 'Get',
});
}
/** 添加设备 */
export async function AddDeviceInfo(data) {
return request(getDataBaseUrl()+`/api/device/add`, {
method: 'POST',
data: data,
});
}
export async function PushDeviceGoods(data) {
return request(getDataBaseUrl()+`/api/device/PushDeviceGoods?DeviceId=`+data, {
method: 'Get',
});
}
/** 修改 */
export async function UpdateDeviceInfo(data) {
return request(getDataBaseUrl()+`/api/device/update`, {
method: 'POST',
data: data,
});
}
/** 修改 */
export async function CopyDevice(data) {
return request(getDataBaseUrl()+`/api/device/CopyDevice`, {
method: 'POST',
data: data,
});
}
/** 删除 */
export async function DelDeviceInfo(data) {
return request(getDataBaseUrl()+`​​/api/device/delete`, {
method: 'POST',
data: data,
});
}
/** 获取所有设备标签 */
export async function GetDeviceType() {
return request(getDataBaseUrl()+`​/api/device/getdevicetypelist`, {
method: 'GET',
});
}
/** 添加设备标签 */
export async function AddDeviceType(data) {
return request(getDataBaseUrl()+`​/api/device/adddevicetype`, {
method: 'POST',
data: data,
});
}
/** 获取所有设备版本 */
export async function GetDeviceVersions() {
return request(getDataBaseUrl()+`​/api/devicevesion/getdevicevesionlist`, {
method: 'GET',
});
}

/**
* 获取当前设备绑定的商品
* @param {*} params
* @param {*} options
* @returns
*/
export async function GetDeviceGoods(params, options) {
return request(getDataBaseUrl()+'/api/device/GetDeviceGoods', {
method: 'POST',
data: {
...params,
},
});
}

/**
* 为设备添加商品
* @param {*} params
* @returns
*/
export async function AddDeviceGood(params) {
return request(getDataBaseUrl()+'/api/device/AddDeviceGood', {
method: 'POST',
data: {
...params,
},
});
}

/**
* 获取所有商品
* @param {*} params
* @returns
*/
export async function GetGoods(params) {
return request(getDataBaseUrl()+'/​api/device/GetGoods', {
method: 'POST',
data: {
...params,
},
});
}

/**
* 获取所有商品
* @param {*} params
* @returns
*/
export async function GetByDeviceGoods(params) {
return request(getDataBaseUrl()+'/​api/device/GetByDeviceGoods', {
method: 'POST',
data: {
...params,
},
});
}

/** 获取商品物料库存 */
export async function GetDeviceStorePage(data) {
return request(getDataBaseUrl()+`/api/device/GetDeviceStorePage`, {
method: 'POST',
data: data,
});
}

/**
* 删除库存原料
* @param {*} data
* @returns
*/
export async function removeSingleStockAdjust(data) {
return request(getDataBaseUrl()+`/api/device/DelStockAdjust?Id=${data}`, {
method: 'DELETE',
// data: data,
// ...(options || {}),
});
}

/** 添加商品物料库存 */
export async function AddStoreBatchingStocAsync(data) {
return request(getDataBaseUrl()+`/api/device/AddUpdateDeviceStocAsync`, {
method: 'POST',
data: data,
});
}

/** 修改商品物料库存 */
export async function UpdateStoreBatchingStocAsync(data) {
return request(getDataBaseUrl()+`/api/device/AddUpdateDeviceStocAsync`, {
method: 'put',
data: data,
});
}

/**获取位置配置 */
export async function getloc(data) {
return request(getDataBaseUrl()+`/api/deviceconfig/getlocs?Code=${data}`, {
method: 'GET',
});
}

/** 获取原料 */
export async function GetBatchingAsync(data) {
return request(getDataBaseUrl()+`/api/device/GetProductList`, {
method: 'post',
data: data,
});
}

/**
* 获取菜谱列表
* @param {*} params
* @returns
*/
export async function getFoodMenuList(params) {
return request(getDataBaseUrl()+'/​api/FoodMenu/tree', {
data: {
...params,
},
});
}

/** 同步库存 */
export async function PushStockAsync(data) {
return request(getDataBaseUrl()+`/api/device/PushDevice?DeviceId=${data}`, {
method: 'post',
});
}

/** 获取实时库存 */
export async function CheckStockAdjust(data) {
return request(getDataBaseUrl()+`/api/device/CheckStockAdjust?DeviceId=${data}`, {
method: 'POST',
});
}
export async function GetDeviceVesion(TypeCode) {
return request(getDataBaseUrl()+`/api/DeviceVesion/GetDeviceVesion?productId=${TypeCode}`, {
method: 'Get',
});
}

export async function TechnologyTemplateExport(data) {
return request(getDataBaseUrl()+`/api/goods/TechnologyTemplateExport?deviceId=${data}`, {
method: 'post',
// responseType: 'blob'
});
}
export async function GetStop() {
return request(GetkitchbaseUrl()+`/api/store/getstorelist`, {
method: 'Get',
});
}

//获取全部商品信息
export async function Getgoodslist() {
return request(getDataBaseUrl()+`/api/goods/getgoodslist`, {
method: 'Get',
});
}

//获取设备商品信息
export async function getdevicegoods(deviceId) {
return request(GetkitchbaseUrl()+`/api/devicepushrecode/getdevicegoods?deviceId=`+deviceId, {
method: 'post',
});
}



export async function editdevicegoodsapi(data) {
return request(GetkitchbaseUrl()+`/api/devicepushrecode/editdevicegoods`, {
method: 'post',
data: data,
});
}





+ 18
- 0
src/pages/interfacedoc/index/index.jsx Vedi File

@@ -0,0 +1,18 @@

const interfacedoc = () => {
return (
<div style={{ width: '100%', height: '107%', overflow: 'hidden' ,background: '#ffffff' }}>
<iframe
src="http://localhost:8001/"
title="接口文档"
style={{
width: '100%',
height: '100%',
border: 'none',
}}
/>
</div>
)
};

export default interfacedoc;

+ 1
- 1
src/pages/org/roles/service.js Vedi File

@@ -54,7 +54,7 @@ export async function setMenu(params) {

//获取角色菜单
export async function GetRoleMenu(params) {
return request(getDataBaseUrl()+`/api/roles/getmenurole/?RoleId=${params}`);
return request(getDataBaseUrl()+`/api/roles/getmenurole?RoleId=${params}`);
}

//数据授权


+ 203
- 0
src/pages/recipe/recipeinfo/components/CreateAttrForm.jsx Vedi File

@@ -0,0 +1,203 @@
import React, { useState, useRef, useEffect } from 'react';
import { CloseOutlined } from '@ant-design/icons';
import { Modal, Form, Input, Button, InputNumber, TreeSelect, Card, Space, message } from 'antd';
import { ProCard } from '@ant-design/pro-Card';
import api from '../service';


const CreateAttrForm = (props) => {
const [form] = Form.useForm();
const [expandedCardKeys, setExpandedCardKeys] = useState([]);
const [inputValues, setInputValues] = useState([]);

useEffect(() => {
if (props.values != undefined) {
if (Object.keys(props.values).length > 0) {
var inputs = props.values.map((item, index) => {
return { index: index, value: item.name }
})
setInputValues(inputs)
}
}
else {
setInputValues({ index: 0, value: '' })
}
if (props.modalVisible) {
form.resetFields();
if (props.values) {
form.setFieldsValue({ attrList: props.values });
}
}
}, [props.values]);
const toggleContent = (key) => {
if (expandedCardKeys.includes(key)) {
setExpandedCardKeys(expandedCardKeys.filter(k => k !== key));
} else {
setExpandedCardKeys([...expandedCardKeys, key]);
}
};
const handleInputChange = (e, field) => {
const updatedValues = [...inputValues];
updatedValues[field.key] = { ...updatedValues[field.key], value: e.target.value };
setInputValues(updatedValues);
};

return (
<Modal
key={Date.now}
title={props.values.id ? '编辑配方' : '新建配方'}
width={700}
bodyStyle={{ padding: '32px 40px 1px 48px' }}
visible={props.modalVisible}
onCancel={props.onCancel}
footer={null}
maskClosable={false}
destroyOnClose={true}
>
<Form
labelCol={{
span: 6,
}}
wrapperCol={{
span: 18,
}}
form={form}
name=""
style={{
maxWidth: 700,
}}
autoComplete="off"
onFinish={props.onFinish}
>
<Form.List name="attrList">
{(fields, { add, remove }) => (
<div
style={{
display: 'flex',
rowGap: 16,
flexDirection: 'column',
}}
>
{fields.map((field) => {
const attrId = props.values[field.key]?.id;
return (
<ProCard
size="small"
title={((inputValues[field?.key]?.value == undefined ? '' : inputValues[field?.key]?.value)) + (!expandedCardKeys.includes(field.key) ? '(点击收缩)' : '(点击展开)')}
key={field.key}
type='inner'
headerBordered
bordered
collapsible
onClick={() => toggleContent(field.key)}
extra={
<CloseOutlined
onClick={(e) => {
e.stopPropagation();
if (attrId == undefined) {
remove(field.name)
message.success("删除成功")
} else {
api.deleteRecipeAttribute(attrId, 1).then((r) => {
if (r.data) {
message.success("删除成功")
remove(field.name)
} else {
message.error("删除失败:该属性已被使用!")
}
})
}
}}
/>
}
>
{!expandedCardKeys.includes(field.key) && (
<div onClick={(e) => e.stopPropagation()}>
<Form.Item label="属性名称" name={[field.name, 'name']} rules={[{ required: true, max: 50 }]} style={{ display: 'inline-block', width: '50%' }}>
<Input placeholder="属性名称" onChange={(e) => handleInputChange(e, field)} />
</Form.Item>
<Form.Item label="序号" name={[field.name, 'sort']} rules={[{ required: true }]} style={{ display: 'inline-block', width: '50%' }}>
<InputNumber placeholder="序号" />
</Form.Item>
<Form.Item label="属性值" rules={[{ required: true, max: 50 }]} style={{ display: 'inline-block', width: '100%', marginLeft: '-12.5%' }}>
<Form.List name={[field.name, 'valueList']}>
{(subFields, subOpt) => (
<div
style={{
display: 'flex',
flexDirection: 'column',
rowGap: 16,
}}
>
{subFields.map((subField) => {
const valueId = props.values[field.key]?.valueList[subField.key]?.id;
return (
<Space key={subField.key}>
<Form.Item noStyle name={[subField.name, 'name']}>
<Input placeholder="属性值" />
</Form.Item>
<Form.Item noStyle name={[subField.name, 'sort']}>
<Input placeholder="序号" />
</Form.Item>
<CloseOutlined
onClick={() => {
if (attrId == undefined) {
message.success("删除成功")
subOpt.remove(subField.name)
} else {
api.deleteRecipeAttribute(valueId, 2).then((r) => {
if (r.data) {
message.success("删除成功")
subOpt.remove(subField.name)
} else {
message.error("删除失败:该属性值已被使用!")
}
})
}
}}
/>
</Space>
)
})}
<Button type="dashed" block onClick={() => {
var currentValueList = form.getFieldValue(['attrList', field.name, 'valueList']) || [];
var maxSort = currentValueList.length > 0 ? Math.max(...currentValueList.map(item => item.sort || 0)) : 0;
subOpt.add({ sort: maxSort + 1 });
}}>
添加属性值
</Button>
</div>
)}
</Form.List>
</Form.Item>
</div>
)}
</ProCard>
)
})}
<Button type="dashed" block onClick={() => {
var maxSort = 0;
if (fields.length != 0) {
maxSort = Math.max(...fields.map(field => form.getFieldValue(['attrList', field.name, 'sort']) || 0));
}
add({ sort: maxSort + 1 });
}}>
添加属性
</Button>
</div>
)}
</Form.List>
<br />
<Form.Item style={{ marginLeft: '72%', marginRight: '-7%' }}>
<Button htmlType="button" style={{ float: 'right', left: 10 }} onClick={props.onCancel} >
取消
</Button>
<Button type="primary" htmlType="submit" style={{ float: 'right' }} >
保存
</Button>
</Form.Item>
</Form>
</Modal>
)
}
export default CreateAttrForm;

+ 56
- 0
src/pages/recipe/recipeinfo/components/CreateForm.jsx Vedi File

@@ -0,0 +1,56 @@
import { Modal, Form, Button, Input, InputNumber, TreeSelect } from 'antd';

const CreateForm = (props) => {
return (
<Modal
title={props.values.id ? '编辑分组' : '新建分组'}
width={640}
bodyStyle={{ padding: '32px 40px 1px 48px' }}
visible={props.modalVisible}
onCancel={props.onCancel}
footer={null}
maskClosable={false}
destroyOnClose
>
<Form
layout="horizontal"
preserve={false}
labelCol={{
span: 4,
}}
initialValues={props.values}
onFinish={props.onFinish}
>
<Form.Item name="id" hidden={true}>
<Input />
</Form.Item>
<Form.Item name="name" label="分组名称" rules={[{ required: true }]}>
<Input placeholder='请输入分组名称'/>
</Form.Item>
<Form.Item name="isWeight" label="分组模式" rules={[{ required: true }]}>
<TreeSelect
style={{ width: '100%' }}
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
treeData={props.isWeightList}
placeholder="请选择是否称重"
treeDefaultExpandAll
allowClear
/>
</Form.Item>
<Form.Item name="sort" label="排序">
<InputNumber />
</Form.Item>
<Form.Item>
<Button htmlType="button" style={{ float: 'right', left: 10 }} onClick={props.onCancel}>
取消
</Button>
<Button type="primary" htmlType="submit" style={{ float: 'right' }}>
保存
</Button>
</Form.Item>
</Form>
</Modal>
)
}

export default CreateForm;

+ 393
- 0
src/pages/recipe/recipeinfo/components/RecipeDetailForm.jsx Vedi File

@@ -0,0 +1,393 @@

import { Button, Modal, Radio, Tag, message, Popconfirm, InputNumber } from 'antd';
import { ProCard } from '@ant-design/pro-Card';
import ProTable, { EditableProTable } from '@ant-design/pro-table';
import api from '../service';
import { useEffect, useRef, useState } from 'react';
import CreateAttrForm from './CreateAttrForm';


const RecipeDetailForm = (props) => {
const actionRef = useRef()
const [recipeAttrFormList, setRecipeAttrFormList] = useState([])
const [recipeAttrList, setRecipeAttrList] = useState([])
const [attrModalVisible, handleModalVisible] = useState(false)
const [batchingList, setBatchingList] = useState([])
const [editableKeys, setEditableRowKeys] = useState([])
const [valueIds, setValueIds] = useState('')
const [names, setNames] = useState('')

useEffect(() => {
getRecipeAttrList();
getBaseBatchingList();
}, []);
const getRecipeAttrList = async () => {
if (props.values?.id != undefined) {
var data = await api.getRecipeAttributeList(props.values.id);
setRecipeAttrList(data.data);
}
}
const getBaseBatchingList = async () => {
if (props.values?.id != undefined) {
var data = await api.getBaseBatchingList(props.values.id);
setEditableRowKeys(data.data.map((item) => item.id));
setBatchingList(data.data);
}
}
const getAttrBatchingList = async (valueIds) => {
var data = await api.getAttrBatchingList(props.values.id, valueIds);
setEditableRowKeys(data.data.map((item) => item.id));
setBatchingList(data.data);
}
const setRecipeBatching = async () => {
if (valueIds != '') {
var valueCount = valueIds.split(',').length;
var attrCount = recipeAttrList.length;
if (valueCount != attrCount) {
message.error("属性未勾选完整")
return;
}
}
var data = {};
data.batchingList = batchingList;
data.recipeId = props.values.id;
data.valueIds = valueIds == '' ? null : valueIds;
await api.setRecipeBatching(data).then((r) => {
if (r.data) {
message.success("保存成功");
} else {
message.error(r.errors)
}
actionRef.current.reload()
})
}
const onChangevalue = (e) => {
const { value } = e.target;
var valueIdList = [];
var nameList = [];
var newId = null;
recipeAttrList.forEach(item => {
item.valueList.forEach(x => {
if (x.id == value) {
newId = x.attributeId;
}
})
})
recipeAttrList.forEach(item => {
if (item.id == newId) {
item.valueList.forEach(x => {
if (x.id == value) {
item.defalutvalue = value;
}
}
)
}
})
recipeAttrList.forEach(x => {
if (x.defalutvalue != undefined) {
valueIdList.push(x.defalutvalue);
}
var name = x.valueList.find(t => t.id == x.defalutvalue)?.name;
if (name != undefined) {
nameList.push(name);
}
})
var valueIds = valueIdList.join(',');
setValueIds(valueIds);
var names = nameList.join('-');
setNames(names);
setRecipeAttrList(recipeAttrList);
setBatchingList([]);
getAttrBatchingList(valueIds);
actionRef.current.reload();
}
const cleanAttr = () => {
setBatchingList([]);
setValueIds('');
recipeAttrList.forEach(item => {
item.defalutvalue = null;
})
setRecipeAttrList(recipeAttrList);
actionRef.current.reload();
}
const deleteRecipeAttr = (id) => {
api.deleteRecipeAttr(id).then((r) => {
if (r.data) {
message.success("删除成功")
} else {
message.error(r.errors)
}
if (valueIds != '') {
setBatchingList([]);
}
actionRef.current.reload();
})
}
const editAttrRecipe = (item) => {
recipeAttrList.forEach(t => {
t.valueList.forEach(x => {
if (item.valueIds.includes(x.id)) {
t.defalutvalue = x.id;
}
})
})
setValueIds(item.valueIds);
setNames(item.name);
setRecipeAttrList(recipeAttrList);
setBatchingList(item.batchingList);
setEditableRowKeys(item.batchingList.map((i) => i.id));
}
const setBaseBatchingList = async () => {
var data = await api.getBaseBatchingList(props.values.id);
setEditableRowKeys(data.data.map((item) => item.id));
setBatchingList(data.data);
}
const columns = [
{
title: '主键',
dataIndex: 'id',
key: 'id',
hideInTable: true,
hideInSearch: true,
tip: '规则名称是唯一的 key',
},
{
title: '物料Id',
dataIndex: 'batchingId',
hideInTable: true,
hideInSearch: true
},
{
title: '物料名称',
dataIndex: 'name',
hideInSearch: true,
editable: false
},
{
title: '用量(g)',
dataIndex: 'weight',
valueType: 'digit',
hideInSearch: true,
editable: true
}
]
const listColumns = [
{
title: '主键',
dataIndex: 'id',
key: 'id',
hideInTable: true,
hideInSearch: true,
tip: '规则名称是唯一的 key',
},
{
title: '多属性名称',
dataIndex: 'name',
hideInSearch: true
},
{
title: '操作',
dataIndex: 'option',
valueType: 'option',
fixed: 'right',
render: (_, record) => [
<a
key="primary"
type="primary"
onClick={() => { editAttrRecipe(record) }}
>编辑</a>,
<Popconfirm
type="primary"
key="primary"
title="确认删除吗?"
okText="是"
cancelText="否"
onConfirm={() => { deleteRecipeAttr(record.id) }}
onCancel
>
<a href="#">删除</a>
</Popconfirm>
]
},
]
return (
<Modal
title={props.values.name}
width={1300}
bodyStyle={{ padding: '32px 40px 10px 48px' }}
visible={props.detailModalOpen}
onCancel={props.onCancel}
footer={null}
maskClosable={false}
destroyOnClose
>
<ProCard split="vertical" style={{ height: '700px' }} >
<div style={{ width: '50%', borderRight: '1px solid #f0f0f0' }}>
<div style={{ height: '40%' }}>
<ProCard
title='属性'
type='inner'
colSpan="50%"
height='50%'
>
<Button
type="primary"
key="primary"
onClick={() => {
setRecipeAttrFormList(recipeAttrList)
handleModalVisible(true)
}}
>
编辑属性
</Button>
<Button
type="primary"
key="primary"
style={{ marginLeft: 10 }}
onClick={() => {
getBaseBatchingList()
cleanAttr()
}}
>
查看基础配方
</Button>
<Button
type="dashed"
style={{ float: 'right' }}
onClick={() => { cleanAttr() }}
>
清空选择
</Button>
<div style={{ marginTop: 10, height:150, overflowY: 'auto' }}>
{
recipeAttrList == "" ? (<div>点击“编辑属性”添加属性和属性值!</div>) : (
recipeAttrList.map((item, index) => {
return (
<div style={{ marginTop: 10, width:'max-content', overflowX: 'auto' }}>
<span><Tag color="blue">{item.name}</Tag>:</span>
<span>
<Radio.Group name={index} buttonStyle="solid" onChange={onChangevalue} value={item.defalutvalue}>
{
item.valueList.map((x, i) => {
return (
<Radio.Button key={i} name={i} value={x.id} style={{ marginLeft: 10 }}>{x.name}</Radio.Button>
)
})
}
</Radio.Group>
</span>
</div>
)
})
)
}
</div>
</ProCard>
</div>
<div style={{ height: '60%' }}>
<ProTable
headerTitle="列表"
actionRef={actionRef}
height='50%'
search={false}
pagination={{ defaultPageSize: 5 }}
request={async () => {
var data = [];
await api.getAttrRecipeList(props.values.id, valueIds).then((r) => {
data = r.data;
});
return {
data: data,
success: true
};
}}
columns={listColumns}
/>
</div>
</div>
<ProCard
title={'配方' + (props.isWeight ? "(基准克数:" + (props.values.referenceWeight == null ? '0' : props.values.referenceWeight) + ")" : '')}
type='inner'
colSpan="50%"
>
<div style={{ position: 'relative', height: 580, overflow: 'hidden' }}>
<div style={{ position: 'sticky', height: 50, top: 0, backgroundColor: '#fff', zIndex: 10 }}>
<span style={{ color: '#FA541C', fontSize: 15, marginLeft: 25, height: 50, width: 280, display: 'inline-block', overflowY: 'auto' }}>
{valueIds === '' ? '基础配方' : names}
</span>
<div style={{ float: 'right' }}>
{valueIds === '' ? ("") : (
<Button
type="primary"
key="primary"
onClick={() => { setBaseBatchingList(); }}
style={{ marginRight: '8px' }}
>
填入基础配方数据
</Button>
)}
<Button
type="primary"
key="save"
onClick={() => { setRecipeBatching(); }}
>
保存数据
</Button>
</div>
</div>
<EditableProTable
columns={columns}
rowKey="id"
height='530'
value={batchingList}
onChange={setBatchingList}
recordCreatorProps={false}
style={{ overflowY: 'auto', height: 'calc(100% - 30px)' }}
editable={{
type: 'multiple',
editableKeys,
onValuesChange: (record, recordList) => {
setBatchingList(recordList);
},
}}
/>
</div>
</ProCard>
</ProCard>

<CreateAttrForm
key={Date.now()}
onFinish={async (value) => {
handleModalVisible(false);
setRecipeAttrFormList([]);
value.attrList.forEach((item) => {
item.recipeId = props.values.id
});
await api.updateRecipeAttribute(value.attrList).then(r => {
if (r.data) {
if (value.attrList[0].id == undefined) {
message.success("添加成功")
}
else {
message.success("修改成功")
}
getRecipeAttrList()
}
else {
message.error(r.errors)
}
})
}}
onCancel={() => {
getRecipeAttrList();
handleModalVisible(false);
setRecipeAttrFormList([]);
}}
modalVisible={attrModalVisible}
values={recipeAttrFormList || {}}
/>
</Modal>
)
}

export default RecipeDetailForm;

+ 197
- 0
src/pages/recipe/recipeinfo/components/RecipeInfoForm.jsx Vedi File

@@ -0,0 +1,197 @@
const buttonStyle = {
width: '100%',
height: '150px',
textAlign: 'center',
whiteSpace: 'normal'
}
const operateStyle = {
position: 'absolute',
top: '5%',
right: '3%',
border: 'none',
background: 'transparent',
cursor: 'pointer',
color: '#FA541C',
}
import api from '../service';
import React, { useState, useEffect } from 'react';
import { Button, message, Popconfirm } from 'antd';
import { FormOutlined, CloseOutlined } from '@ant-design/icons';
import UpdateRecipeInfoForm from './UpdateRecipeInfoForm';
import RecipeDetailForm from './RecipeDetailForm';

const RecipeInfoForm = (props) => {
const [modalVisible, handleModalVisible] = useState(false);
const [recipeInfo, setRecipeInfo] = useState([]);
const [filledValues, setFilledValues] = useState([]);
const [detailModalOpen, handleDetailModalOpen] = useState(false);

useEffect(() => {
fillValues(props.values)
}, [props.values]);
function fillValues(list) {
var values = [];
if (list.length == 0) {
values = Array.from({ length: 20 }, (_, index) => {
return {
id: "",
recipeGroupId: "",
name: '+',
sort: index + 1
}
});
}
else {
list.forEach(t => {
if (t.name === "") {
t.name = "+"
}
})
values = list;
}
setFilledValues(values)
}
const getRecipeInfo = async (groupId, current) => {
var data = await api.getRecipeInfoList(groupId, current);
fillValues(data.data);
}
const deleteRecipeInfo = async (id) => {
await api.deleteRecipeInfo(id).then((r) => {
if (r.data) {
message.success('删除成功');
getRecipeInfo(props.groupId, props.current);
} else {
message.error(r.errors);
}
});
}
return (
<div style={{
height: '90%',
display: 'grid',
gridTemplateColumns: 'repeat(5, 19%)',
gridTemplateRows: 'repeat(4, 25%)',
gap: '10px',
justifyContent: 'center',
alignItems: 'center'
}}>
{filledValues.map((item, index) => (
<Button
onClick={() => {
if (props.groupId == undefined) {
message.error("请先选择一个配方分组");
}
else {
setRecipeInfo(item)
if (item.name === '+' || item.name === "") {
handleModalVisible(true)
} else {
handleDetailModalOpen(true)
}
}
}}
key={index}
style={{
...buttonStyle,
border: (item.name === '+' || item.name === "") ? '1px dashed #FA541C' : '1px solid #FA541C',
backgroundColor: (item.name === '+' || item.name === "") ? '#ffffff' : '#fff2e8'
}}
>
{item.name != '+' && (
<span
style={{
...operateStyle
}}
>
<a
onClick={(e) => {
e.stopPropagation()
setRecipeInfo(item)
handleModalVisible(true)
}}
>
<FormOutlined />
</a>
<a
onClick={(e) => {
e.stopPropagation()
}}
style={{ marginLeft: 5 }}
>
<Popconfirm
title="确认删除吗?"
onConfirm={() => deleteRecipeInfo(item.id)}
okText="确定"
cancelText="取消"
>
<CloseOutlined />
</Popconfirm>
</a>
</span>
)}
{item.name}
</Button>
))}

<UpdateRecipeInfoForm
onFinish={async (value) => {
setRecipeInfo([]);
handleModalVisible(false);
value.isWeight = props.isWeight;
value.recipeGroupId = props.groupId;
await api.updateRecipeInfo(value).then(r => {
if (r.data) {
if (value.id == undefined) {
message.success("添加成功")
}
else {
message.success("修改成功")
}
getRecipeInfo(props.groupId, props.current)
}
else {
if (recipeInfo.name === "") {
filledValues.forEach(t => {
if (t.sort == recipeInfo.sort) {
t.name = '+';
}
})
setFilledValues(filledValues);
}
message.error(r.errors)
}
})
}}
onCancel={() => {
handleModalVisible(false);
setRecipeInfo([]);
if (recipeInfo.name === "") {
filledValues.forEach(t => {
if (t.sort == recipeInfo.sort) {
t.name = '+';
}
})
setFilledValues(filledValues);
}
}}
modalVisible={modalVisible}
isWeight={props.isWeight}
values={recipeInfo || {}}
/>

<RecipeDetailForm
key={Date.now()}
onCancel={() => {
getRecipeInfo(props.groupId, props.current);
handleDetailModalOpen(false)
setRecipeInfo([])
}}
values={recipeInfo}
isWeight={props.isWeight}
detailModalOpen={detailModalOpen}
/>
</div>
);
};

export default RecipeInfoForm;

+ 57
- 0
src/pages/recipe/recipeinfo/components/UpdateRecipeInfoForm.jsx Vedi File

@@ -0,0 +1,57 @@
import React, { useEffect } from 'react';
import { Modal, Form, Button, Input, InputNumber } from "antd";

const UpdateRecipeInfoForm = (props) => {

useEffect(() => {
if (props.values.name === '+') {
props.values.name = '';
}
}, [props.values, props.modalVisible]);
return (
<Modal
title={props.values.id ? '编辑配方' : '新建配方'}
width={640}
bodyStyle={{ padding: '32px 40px 1px 48px' }}
visible={props.modalVisible}
onCancel={props.onCancel}
footer={null}
maskClosable={false}
destroyOnClose
>
<Form
layout="horizontal"
preserve={false}
labelCol={{
span: 4,
}}
initialValues={props.values}
onFinish={props.onFinish}
>
<Form.Item name="id" hidden={true}>
<Input />
</Form.Item>
<Form.Item name="name" label="配方名称" rules={[{ required: true }]}>
<Input placeholder='请输入配方名称' />
</Form.Item>
{props.isWeight == 1 ?
<Form.Item name="referenceWeight" label="基准克数">
<InputNumber />
</Form.Item> : ""}
<Form.Item name="sort" label="排序">
<InputNumber disabled/>
</Form.Item>
<Form.Item>
<Button htmlType="button" style={{ float: 'right', left: 10 }} onClick={props.onCancel}>
取消
</Button>
<Button type="primary" htmlType="submit" style={{ float: 'right' }}>
保存
</Button>
</Form.Item>
</Form>
</Modal>
)
}

export default UpdateRecipeInfoForm;

+ 279
- 0
src/pages/recipe/recipeinfo/index.jsx Vedi File

@@ -0,0 +1,279 @@
import React, { useState, useEffect } from 'react';
import { PlusOutlined, FormOutlined, DeleteOutlined, CloseOutlined } from '@ant-design/icons';
import { PageContainer } from '@ant-design/pro-layout';
import { Button, message, Popconfirm, Pagination, Switch } from 'antd';
import { ProCard } from '@ant-design/pro-Card';
import api from './service';
import CreateForm from './components/CreateForm';
import RecipeInfoForm from './components/RecipeInfoForm';

const recipeinfo = () => {
const [isWeight, setIsWeight] = useState(false);
const [groupData, setGroupData] = useState([]);
const [createModalVisible, handleModalVisible] = useState();
const [recipeGroupValues, setRecipeGroupValues] = useState([]);
const [recipeInfoValues, setRecipeInfoValues] = useState([]);
const [groupId, setGroupId] = useState();
const [current, setCurrent] = useState(1);
const [defaultCurrent, setDefaultCurrent] = useState(1);
const [total, setTotal] = useState(20);
const isWeightList = [
{ value: false, label: '标准模式' },
{ value: true, label: '称重模式' },
];

useEffect(() => {
initGroupList(false);
}, []);
const initGroupList = async (isWeight) => {
var data = await api.getRecipeGroupList(isWeight);
setGroupData(data.data);
}
const deleteGroup = async (id) => {
await api.deleteRecipeGroup(id).then((r) => {
if (r.data) {
message.success('删除成功');
initGroupList(isWeight);
setGroupId();
} else {
message.error(r.errors);
}
});
}
const getRecipeCount = async (id) => {
var data = await api.getRecipeCount(id);
var total = data.data;
if (total === 0) {
total = 20
}
setTotal(total);
}
const getRecipeInfo = async (groupId, current) => {
var data = await api.getRecipeInfoList(groupId, current);
setRecipeInfoValues(data.data);
}
const addRecipePage = async () => {
if(groupId === undefined){
message.error("请先选择一个配方分组");
}else{
var data = {};
data.recipeGroupId = groupId;
await api.addRecipePage(data);
var curr = total + 20;
setTotal(curr)
var current = Math.ceil(curr / 20);
setCurrent(current)
onChangepage(current)
}
}
const deleteRecipePage = async () => {
if(groupId === undefined){
message.error("请先选择一个配方分组");
}else{
await api.deleteRecipePage(groupId, current).then((r) => {
if (r.data) {
message.success('删除成功')
changeGroup(groupId)
} else {
message.error(r.errors);
}
})
}
}
const onChangepage = (pageNumber) => {
setCurrent(pageNumber)
setDefaultCurrent(pageNumber)
getRecipeInfo(groupId, pageNumber)
};
const changeGroup = async (id) => {
setCurrent(1)
setDefaultCurrent(1)
setGroupId(id)
getRecipeCount(id)
getRecipeInfo(id, 1)
}
const switchOnChange = (checked) => {
if (checked) {
setIsWeight(true);
} else {
setIsWeight(false);
}
initGroupList(checked);
setGroupId();
setRecipeInfoValues([]);
}

return (
<PageContainer
header={{
title: '',
breadcrumb: {},
}}
>
<ProCard style={{ height: '700px' }} split="vertical">
<ProCard
title='配方分组'
type='inner'
colSpan="25%"
style={{ height: '800px', overflowY: 'auto' }}
headStyle={{ position: 'sticky', top: 0, background: 'white', zIndex: 1 }}
extra={
<Button
type="default"
block
ghost
onClick={() => {
handleModalVisible(true);
}}
style={{
float: 'right',
background: '#FA541C'
}}>
<PlusOutlined />添加分组
</Button>
}
>
<Switch
checkedChildren="称重模式"
unCheckedChildren="标准模式"
defaultunChecked
onChange={switchOnChange}
style={{
marginTop: -10,
marginBottom: 10,
backgroundColor: '#FA541C',
borderColor: '#FA541C',
}}
checkedStyle={{
backgroundColor: '#FA541C',
borderColor: '#FA541C',
}}
unCheckedStyle={{
backgroundColor: '#FA541C',
borderColor: '#FA541C',
}}
/>
<div>
{
groupData.length > 0 ? groupData.map(x => {
const isSelected = x.id === groupId;
return (
<ProCard
boxShadow
hoverable
bordered
key={x.id}
onClick={() => { changeGroup(x.id) }}
bodyStyle={{ padding: 15, display: 'flex', justifyContent: 'center' }}
headStyle={{ padding: '0px 24px' }}
style={{
marginBlockEnd: 8,
boxShadow: isSelected ? '-1px 5px 5px gray' : undefined,
borderColor: isSelected ? 'gray' : undefined,
borderWidth: '1px',
borderStyle: 'solid'
}}
extra={
<div>
<FormOutlined
onClick={() => {
setRecipeGroupValues(x)
handleModalVisible(true)
}}
/>
<Popconfirm
title="确认删除吗?"
onConfirm={() => deleteGroup(x.id)}
okText="确定"
cancelText="取消"
>
<DeleteOutlined style={{ marginLeft: 10 }} />
</Popconfirm>
</div>
}
>
<p>{x.name}</p>
</ProCard>
)
}) : <div>点击添加分组按钮添加配方分组!</div>
}
</div>
</ProCard>
<ProCard
type='inner'
title='配方'
style={{ height: '800px' }}
>
<div style={{ height:'100%', overflow: 'hidden', clear: 'both' }}>
{
<RecipeInfoForm values={recipeInfoValues} current={current} groupId={groupId} isWeight={isWeight} />
}
<div style={{ textAlign: 'center', marginTop: 35 }}>
<Pagination
style={{ display: 'inline-flex' }}
current={current}
defaultPageSize={20}
defaultCurrent={defaultCurrent}
total={total}
onChange={onChangepage}
showSizeChanger={false}
/>
<Button
type="primary"
style={{ marginLeft: 10 }}
onClick={() => addRecipePage()}
>
<PlusOutlined />新增页
</Button>
<Popconfirm
title="删除当前页及其配方?"
onConfirm={() => deleteRecipePage()}
okText="确定"
cancelText="取消"
>
<Button
type="primary"
style={{ marginLeft: 10 }}
>
<CloseOutlined />删除当前页
</Button>
</Popconfirm>
</div>
</div>
</ProCard>
</ProCard>

<CreateForm
onFinish={async (value) => {
setRecipeGroupValues([]);
handleModalVisible(false);
await api.updataRecipeGroup(value).then(r => {
if (r.data) {
if (value.id == undefined) {
message.success("添加成功")
}
else {
message.success("修改成功")
}
initGroupList(isWeight);
}
else {
message.error(r.errors)
}
})
setGroupId();
}}
onCancel={() => {
handleModalVisible(false);
setRecipeGroupValues([]);
setGroupId();
}}
modalVisible={createModalVisible}
values={recipeGroupValues || {}}
isWeightList={isWeightList}
/>
</PageContainer>
)
}

export default recipeinfo;

+ 94
- 0
src/pages/recipe/recipeinfo/service.js Vedi File

@@ -0,0 +1,94 @@
import { request } from 'umi';
import { getDataBaseUrl } from '@/global_data';
export default {
getRecipeGroupList(isWeight) {
return request(getDataBaseUrl() + `/api/recipe/getRecipeGroupList?isWeight=${isWeight}`, {
method: 'Get',
});
},
updataRecipeGroup(data) {
return request(getDataBaseUrl() + `/api/recipe/updataRecipeGroup`, {
method: 'Post',
data: data,
});
},
deleteRecipeGroup(data) {
return request(getDataBaseUrl() + `/api/recipe/deleteRecipeGroup?id=${data}`, {
method: 'Get',
});
},
getRecipeCount(id) {
return request(getDataBaseUrl() + `/api/recipe/GetRecipeCount?recipeGroupId=${id}`, {
method: 'Get',
});
},
getRecipeInfoList(id, page) {
return request(getDataBaseUrl() + `/api/recipe/getRecipeInfoList?recipeGroupId=${id}&page=${page}`, {
method: 'Get',
});
},
updateRecipeInfo(data) {
return request(getDataBaseUrl() + `/api/recipe/updateRecipeInfo`, {
method: 'Post',
data: data,
});
},
deleteRecipeInfo(id) {
return request(getDataBaseUrl() + `/api/recipe/deleteRecipeInfo?id=${id}`, {
method: 'Get',
});
},
getRecipeAttributeList(id) {
return request(getDataBaseUrl() + `/api/recipeAttribute/getRecipeAttributeList?recipeId=${id}`, {
method: 'Get',
});
},
updateRecipeAttribute(data) {
return request(getDataBaseUrl() + `/api/recipeAttribute/updateRecipeAttribute`, {
method: 'Post',
data: data,
});
},
deleteRecipeAttribute(id, type) {
return request(getDataBaseUrl() + `/api/recipeAttribute/deleteRecipeAttribute?id=${id}&type=${type}`, {
method: 'Get',
});
},
getBaseBatchingList(recipeId) {
return request(getDataBaseUrl() + `/api/recipe/getBaseBatchingList?recipeId=${recipeId}`, {
method: 'Get',
});
},
getAttrBatchingList(recipeId, attrIds) {
return request(getDataBaseUrl() + `/api/recipe/getAttrBatchingList?recipeId=${recipeId}&attrIds=${attrIds}`, {
method: 'Get',
});
},
setRecipeBatching(data) {
return request(getDataBaseUrl() + `/api/recipe/setRecipeBatching`, {
method: 'Post',
data: data,
});
},
getAttrRecipeList(recipeId, valueIds) {
return request(getDataBaseUrl() + `/api/recipe/getAttrRecipeList?recipeId=${recipeId}&valueIds=${valueIds}`, {
method: 'Get',
});
},
deleteRecipeAttr(configId) {
return request(getDataBaseUrl() + `/api/recipe/deleteRecipeAttr?configId=${configId}`, {
method: 'Get',
});
},
addRecipePage(data) {
return request(getDataBaseUrl() + `/api/recipe/addRecipePage`, {
method: 'Post',
data: data,
});
},
deleteRecipePage(recipeGroupId, page) {
return request(getDataBaseUrl() + `/api/recipe/deleteRecipePage?recipeGroupId=${recipeGroupId}&page=${page}`, {
method: 'Get',
});
},
}

+ 139
- 0
src/pages/report/batchingreport/index.jsx Vedi File

@@ -0,0 +1,139 @@
import { PageContainer } from "@ant-design/pro-layout";
import { Tabs, Card, Input, DatePicker, Button } from "antd"
import ProTable from '@ant-design/pro-table';
import React, { useState, useEffect } from 'react';
import moment from 'moment';
import api from "../service";
const { RangePicker } = DatePicker;

const batchingreport = () =>{
const [tabIndex, setTabIndex] = useState(1);
const [batchingName, setBatchingName] = useState('');
const [storeName, setStoreName] = useState('');
const [deviceName, setDeviceName] = useState('');
const [dataSource, setDataSource] = useState([]);
const [timeRange, setTimeRange] = useState([
moment(moment(new Date(Date.now())).format('YYYY-MM-DD 00:00:00')),
moment(moment(new Date(Date.now())).format('YYYY-MM-DD 23:59:59')),
]);

useEffect(() => {
initData();
}, [tabIndex]);

const initData = async () => {
var data = [];
if (tabIndex == 1) {
data = await api.getBatchingUseSummary({ "batchingName": batchingName, "startTime": timeRange[0]._i, "endTime": timeRange[1]._i, "storeName": storeName, "deviceName": deviceName });
}
setDataSource(data.data)
}

const resetRequest = async () => {
setBatchingName('')
setStoreName('')
setDeviceName('')
var startDate = moment(new Date(Date.now())).format('YYYY-MM-DD 00:00:00');
var endDate = moment(new Date(Date.now())).format('YYYY-MM-DD 23:59:59');
setTimeRange([moment(startDate),moment(endDate)])
var data = []
if (tabIndex == 1) {
data = await api.getBatchingUseSummary({ "batchingName": "", "startTime": startDate, "endTime": endDate, "storeName": "", "deviceName": "" });
}
setDataSource(data.data)
}
const summaryColumns = [
{
title: '主键',
dataIndex: 'id',
tip: 'key',
hideInSearch: true,
hideInTable: true
},
{
title: '物料名称',
dataIndex: 'batchingName',
hideInSearch: true,
ellipsis: true,
},
{
title: '门店名称',
dataIndex: 'storeName',
hideInSearch: true,
ellipsis: true,
},
{
title: '设备名称',
dataIndex: 'deviceName',
hideInSearch: true,
ellipsis: true,
},
{
title: '使用数量',
dataIndex: 'useWeight',
hideInSearch: true,
ellipsis: true,
},
]
return(
<PageContainer
header={{
title: '',
breadcrumb: {},
}}
>
<Card style={{ marginBottom: 20 }}>
<div style={{ display: 'flex', alignItems: 'center', width: '100%' }}>
<span>物料名称:</span>
<Input placeholder="请输入物料名称" style={{ width: '15%', margin: 10 }} value={batchingName} onChange={(a) => { setBatchingName(a.target.value) }} />
<span>门店名称:</span>
<Input placeholder="请输入门店名称" style={{ width: '15%', margin: 10 }} value={storeName} onChange={(a) => { setStoreName(a.target.value) }} />
<span>设备名称:</span>
<Input placeholder="请输入设备名称" style={{ width: '15%', margin: 10 }} value={deviceName} onChange={(a) => { setDeviceName(a.target.value) }} />
</div>
<div style={{ display: 'flex', alignItems: 'center', width: '100%' }}>
<span>使用时间:</span>
<RangePicker showTime style={{ width: '25%', margin: 10 }} value={timeRange} onChange={(date, dateStrings) => {
let tempDate = [
moment(moment(new Date(dateStrings[0])).format('YYYY-MM-DD HH:mm:ss')),
moment(moment(new Date(dateStrings[1])).format('YYYY-MM-DD HH:mm:ss')),
]
setTimeRange(tempDate);
}} />
<Button onClick={() => { resetRequest() }}>
重置
</Button>
<Button type="primary" style={{ marginLeft: 10 }} onClick={() => { initData() }}>
查询
</Button>
</div>
</Card>
<Card>
<Tabs
tabPosition={'left'}
value={tabIndex}
onChange={a => { setTabIndex(a) }}
items={new Array(1).fill(null).map((_, i) => {
const id = String(i + 1);
return {
label: '物料使用汇总',
key: id,
children:
<ProTable
headerTitle="物料使用汇总"
rowKey="id"
pagination={{ defaultPageSize: 10 }}
search={false}
toolBarRender={false}
columns={summaryColumns}
dataSource={dataSource}
/>
}
})}
/>
</Card>
</PageContainer>
)
}

export default batchingreport;

+ 188
- 0
src/pages/report/recipereport/index.jsx Vedi File

@@ -0,0 +1,188 @@
import { PageContainer } from "@ant-design/pro-layout";
import { Tabs, Card, Input, DatePicker, Button } from "antd"
import ProTable from '@ant-design/pro-table';
import React, { useState, useEffect } from 'react';
import moment from 'moment';
import api from "../service";
const { RangePicker } = DatePicker;


const recipereport = () => {
const [tabIndex, setTabIndex] = useState(1);
const [recipeName, setRecipeName] = useState('');
const [storeName, setStoreName] = useState('');
const [deviceName, setDeviceName] = useState('');
const [dataSource, setDataSource] = useState([]);
const [timeRange, setTimeRange] = useState([
moment(moment(new Date(Date.now())).format('YYYY-MM-DD 00:00:00')),
moment(moment(new Date(Date.now())).format('YYYY-MM-DD 23:59:59')),
]);

useEffect(() => {
initData();
}, [tabIndex]);

const initData = async () => {
var data = [];
if (tabIndex == 1) {
data = await api.getRecipeUseDetail({ "recipeName": recipeName, "startTime": timeRange[0]._i, "endTime": timeRange[1]._i, "storeName": storeName, "deviceName": deviceName });
} else {
data = await api.getRecipeUseSummary({ "recipeName": recipeName, "startTime": timeRange[0]._i, "endTime": timeRange[1]._i, "storeName": storeName, "deviceName": deviceName });
}
setDataSource(data.data)
}

const resetRequest = async () => {
setRecipeName('')
setStoreName('')
setDeviceName('')
var startDate = moment(new Date(Date.now())).format('YYYY-MM-DD 00:00:00');
var endDate = moment(new Date(Date.now())).format('YYYY-MM-DD 23:59:59');
setTimeRange([moment(startDate),moment(endDate)])
var data = []
if (tabIndex == 1) {
data = await api.getRecipeUseDetail({ "recipeName": "", "startTime": startDate, "endTime": endDate, "storeName": "", "deviceName": "" });
} else {
data = await api.getRecipeUseSummary({ "recipeName": "", "startTime": startDate, "endTime": endDate, "storeName": "", "deviceName": "" });
}
setDataSource(data.data)
}

const detailColumns = [
{
title: '主键',
dataIndex: 'id',
tip: 'key',
hideInSearch: true,
hideInTable: true
},
{
title: '配方名称',
dataIndex: 'recipeName',
ellipsis: true,
hideInSearch: true,
},
{
title: '门店名称',
dataIndex: 'storeName',
ellipsis: true,
hideInSearch: true,
},
{
title: '设备名称',
dataIndex: 'deviceName',
ellipsis: true,
hideInSearch: true,
},
{
title: '使用时间',
dataIndex: 'useDate',
ellipsis: true,
hideInSearch: true,
},
]
const summaryColumns = [
{
title: '主键',
dataIndex: 'id',
tip: 'key',
hideInSearch: true,
hideInTable: true
},
{
title: '配方名称',
dataIndex: 'recipeName',
ellipsis: true,
hideInSearch: true,
},
{
title: '门店名称',
dataIndex: 'storeName',
ellipsis: true,
hideInSearch: true,
},
{
title: '设备名称',
dataIndex: 'deviceName',
ellipsis: true,
hideInSearch: true,
},
{
title: '使用数量',
dataIndex: 'useCount',
ellipsis: true,
hideInSearch: true,
},
]
return (
<PageContainer
header={{
title: '',
breadcrumb: {},
}}
>
<Card style={{ marginBottom: 20 }}>
<div style={{ display: 'flex', alignItems: 'center', width: '100%' }}>
<span>配方名称:</span>
<Input placeholder="请输入配方名称" style={{ width: '15%', margin: 10 }} value={recipeName} onChange={(a) => { setRecipeName(a.target.value) }} />
<span>门店名称:</span>
<Input placeholder="请输入门店名称" style={{ width: '15%', margin: 10 }} value={storeName} onChange={(a) => { setStoreName(a.target.value) }} />
<span>设备名称:</span>
<Input placeholder="请输入设备名称" style={{ width: '15%', margin: 10 }} value={deviceName} onChange={(a) => { setDeviceName(a.target.value) }} />
</div>
<div style={{ display: 'flex', alignItems: 'center', width: '100%' }}>
<span>使用时间:</span>
<RangePicker showTime style={{ width: '25%', margin: 10 }} value={timeRange} onChange={(date, dateStrings) => {
let tempDate = [
moment(moment(new Date(dateStrings[0])).format('YYYY-MM-DD HH:mm:ss')),
moment(moment(new Date(dateStrings[1])).format('YYYY-MM-DD HH:mm:ss')),
]
setTimeRange(tempDate);
}} />
<Button onClick={() => { resetRequest() }}>
重置
</Button>
<Button type="primary" style={{ marginLeft: 10 }} onClick={() => { initData() }}>
查询
</Button>
</div>
</Card>
<Card>
<Tabs
tabPosition={'left'}
value={tabIndex}
onChange={a => { setTabIndex(a) }}
items={new Array(2).fill(null).map((_, i) => {
const id = String(i + 1);
return {
label: id == 1 ? '配方使用明细' : '配方使用汇总',
key: id,
children: id == 1 ?
<ProTable
headerTitle="配方使用明细"
rowKey="id"
pagination={{ defaultPageSize: 10 }}
search={false}
toolBarRender={false}
columns={detailColumns}
dataSource={dataSource}
/>
:
<ProTable
headerTitle="配方使用汇总"
rowKey="id"
pagination={{ defaultPageSize: 10 }}
search={false}
toolBarRender={false}
columns={summaryColumns}
dataSource={dataSource}
/>
}
})}
/>
</Card>
</PageContainer>
)
}

export default recipereport;

+ 22
- 0
src/pages/report/service.js Vedi File

@@ -0,0 +1,22 @@
import { request } from 'umi';
import { getDataBaseUrl } from '@/global_data';
export default {
getRecipeUseDetail(params) {
return request(getDataBaseUrl() + '/api/report/getRecipeUseDetail', {
method: 'Post',
data: params,
});
},
getRecipeUseSummary(params) {
return request(getDataBaseUrl() + '/api/report/getRecipeUseSummary', {
method: 'Post',
data: params,
});
},
getBatchingUseSummary(params) {
return request(getDataBaseUrl() + '/api/report/getBatchingUseSummary', {
method: 'Post',
data: params,
});
},
}

+ 63
- 0
src/pages/store/storeinfo/components/CreateForm.jsx Vedi File

@@ -0,0 +1,63 @@
import { Modal, Form, TreeSelect, Button, Input } from 'antd';

const CreateForm = (props) => {
const { TextArea } = Input;
return (
<Modal
title={props.values.id ? '编辑门店' : '新建门店'}
width={640}
bodyStyle={{ padding: '32px 40px 1px 48px' }}
open={props.modalOpen}
onCancel={props.onCancel}
footer={null}
maskClosable={false}
destroyOnClose
>
<Form
layout="horizontal"
preserve={false}
labelCol={{
span: 4,
}}
initialValues={props.values}
onFinish={props.onFinish}
>
<Form.Item name="id" hidden={true}>
<Input />
</Form.Item>
<Form.Item name="name" label="门店名称" rules={[{ required: true }]}>
<Input placeholder='请输入门店名称'/>
</Form.Item>
<Form.Item name="orgId" label="组织名称" rules={[{ required: false }]}>
<TreeSelect
style={{ width: '100%' }}
dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
treeData={props.treeData}
placeholder="请选择组织名称"
treeDefaultExpandAll
allowClear
/>
</Form.Item>
<Form.Item name="header" label="负责人">
<Input placeholder='请输入负责人'/>
</Form.Item>
<Form.Item name="phone" label="手机号">
<Input placeholder='请输入手机号'/>
</Form.Item>
<Form.Item name="description" label="描述">
<TextArea rows={4} rules={[{ max: 500 }]} />
</Form.Item>
<Form.Item>
<Button htmlType="button" style={{ float: 'right', left: 10 }} onClick={props.onCancel}>
取消
</Button>
<Button type="primary" htmlType="submit" style={{ float: 'right' }}>
保存
</Button>
</Form.Item>
</Form>
</Modal>
)
}

export default CreateForm;

+ 319
- 0
src/pages/store/storeinfo/index.jsx Vedi File

@@ -0,0 +1,319 @@
import { PlusOutlined } from '@ant-design/icons';
import { Button, message, Popconfirm, Modal, Transfer, Select } from 'antd';
import React, { useState, useRef, useEffect } from 'react';
import { PageContainer } from '@ant-design/pro-layout';
import ProTable from '@ant-design/pro-table';
import CreateForm from './components/CreateForm';
import api from './service';

const storeinfo = () => {
const actionRef = useRef();
const [createModalOpen, handleModalOpen] = useState();
const [stepFormValues, setStepFormValues] = useState({});
const [selectedRowsState, setSelectedRows] = useState();
const [isModalOpen, setIsModalOpen] = useState(false);
const [treeData, setTreeData] = useState();
const [mockData, setMockData] = useState([]);
const [targetKeys, setTargetKeys] = useState([]);
const [storeId, setStoreId] = useState([]);
const [hasDeviceRelation, setHasDeviceRelation] = useState(false);
const [isWeight, setIsWeight] = useState('');
useEffect(() => {
initOrg()
getAllRecipeList(isWeight)
}, []);
function initOrg() {
api.gettree().then((r) => {
setTreeData(r.data);
});
}
function getAllRecipeList(isWeight) {
api.getAllRecipeList(isWeight).then((item) => {
var data = item.data;
const tempMockData = [];
data.forEach((item) => {
tempMockData.push({
key: item.id,
title: item.name,
});
})
setMockData(tempMockData);
});
}
const getHasDeviceRelation = async (id) => {
api.getHasDeviceRelation(id).then((r) => {
setHasDeviceRelation(r.data)
})
}
const BatchDeletion = () => {
return (
selectedRowsState?.length > 0 &&
(
<Popconfirm
title="确认删除吗?"
onConfirm={onClickdele}
okText="确定"
cancelText="取消"
>
<Button type="primary">批量删除</Button>
</Popconfirm>
))
}
const onClickdele = () => {
handleRemove(selectedRowsState);
setSelectedRows([]);
actionRef.current?.reloadAndRest?.();
}
const handleRemove = async (selectedRows) => {
if (!selectedRows) return true;
await api.deleteStore(selectedRows.map((row) => row.id)).then((r) => {
if (r.data) {
message.success('删除成功');
actionRef.current.reload();
} else {
message.error(r.errors);
}
});
return true;
};
const editStoreRecipe = async () => {
const tempMockData = [];
targetKeys.forEach(function (item, index, array) {
tempMockData.push({
"recipeId": item
});
})
var data = {
"storeId": storeId,
"recipeIdList": tempMockData
}
api.setStoreRelation(data).then((r) => {
if (r.succeeded) {
message.success('编辑成功');
} else {
message.error(r.errors)
}
});
setIsModalOpen(false);
setIsWeight('');
actionRef.current.reload();
};
const setrecipeList = async (id) => {
var list = [];
api.getStoreRelation(id).then((item) => {
item.data.forEach(function (item) {
list.push(item.recipeId);
})
setTargetKeys(list);
});
};
const handleChange = (newTargetKeys) => {
setTargetKeys(newTargetKeys);
};
const handleModeChange = (value) => {
setIsWeight(value);
getAllRecipeList(value);
};
const columns = [
{
title: '主键',
dataIndex: 'id',
tip: 'key',
hideInSearch: true,
hideInTable: true
},
{
title: '门店名称',
dataIndex: 'name',
ellipsis: true,
},
{
title: '组织名称',
dataIndex: 'orgName',
ellipsis: true,
hideInSearch: true,
},
{
title: '负责人',
dataIndex: 'header',
ellipsis: true,
hideInSearch: true,
},
{
title: '手机号',
dataIndex: 'phone',
ellipsis: true,
hideInSearch: true,
},
{
title: '描述',
dataIndex: 'description',
ellipsis: true,
hideInSearch: true,
},
{
title: '操作',
dataIndex: 'option',
valueType: 'option',
fixed: 'right',
width: 350,
render: (_, record) => [
<a
onClick={() => {
handleModalOpen(true);
setStepFormValues(record);
}}
>
更新
</a>,
<a
key="edit"
onClick={() => {
getHasDeviceRelation(record.id);
getAllRecipeList(isWeight)
setrecipeList(record.id);
setStoreId(record.id);
setIsModalOpen(true);
}}
>
设置门店配方
</a>,
<Popconfirm
type="primary"
key="primary"
title="确认删除吗?"
okText="是"
cancelText="否"
onConfirm={() => {
api.deleteStore([record.id]).then((r) => {
if (r.succeeded) {
message.success('删除成功');
actionRef.current.reload();
} else {
message.error(r.errors);
}
});
}}
>
<a href="#">删除</a>
</Popconfirm>,
],
}
]

return (
<PageContainer
header={{
title: '',
breadcrumb: {},
}}
>
<ProTable
headerTitle="门店列表"
actionRef={actionRef}
rowKey="id"
pagination={{ defaultPageSize: 10 }}
search={{
labelWidth: 120,
}}
toolBarRender={() => [
<Button
type="primary"
key="primary"
onClick={() => {
handleModalOpen(true);
}}
>
<PlusOutlined /> 新建
</Button>,
<BatchDeletion />
]}
request={async (params) => {
let storeData = [];
var total = 0;
await api.getStorePage(params).then((r) => {
storeData = r.data.data;
total = r.data.total;
});
return {
data: storeData,
total: total,
};
}}
columns={columns}
rowSelection={{
onChange: (_, selectedRows) => {
setSelectedRows(selectedRows);
},
}}
/>

<CreateForm
treeData={treeData}
onFinish={async (value) => {
await api.updateStore(value).then(r => {
if (r.data) {
if (value.id == undefined) {
message.success("添加成功")
}
else {
message.success("修改成功")
}
}
else {
message.error(r.errors)
}
})
handleModalOpen(false);
setStepFormValues({});
actionRef.current.reload();
}}
onCancel={() => {
handleModalOpen(false);
setStepFormValues({});
}}
modalOpen={createModalOpen}
values={stepFormValues || {}}
/>

<Modal
width={800}
title="设置门店配方"
open={isModalOpen}
destroyOnClose
footer={hasDeviceRelation ? <span style={{ color: '#FA541C' }}>已有设备使用,请取消该门店下所有设备的绑定配方!</span> : undefined}
onOk={() => { editStoreRecipe() }}
onCancel={() => {
setIsModalOpen(false)
setIsWeight('')
}}
>
<Select
defaultValue="全部"
style={{ width: 120, marginBottom: 10 }}
onChange={handleModeChange}
>
<Option value="">全部</Option>
<Option value={false}>标准模式</Option>
<Option value={true}>称重模式</Option>
</Select>
<Transfer
className="tree-transfer"
listStyle={{
width: 500,
height: 500,
}}
dataSource={mockData}
targetKeys={targetKeys}
onChange={handleChange}
showSearch
pagination
render={(item) => item.title}
/>
</Modal>

</PageContainer>
)
}

export default storeinfo;

+ 51
- 0
src/pages/store/storeinfo/service.js Vedi File

@@ -0,0 +1,51 @@
import { request } from 'umi';
import { getDataBaseUrl } from '@/global_data';
export default {
getStorePage(params) {
return request(getDataBaseUrl() + '/api/store/getStorePage', {
method: 'Post',
data: params,
});
},
gettree(params) {
return request(getDataBaseUrl() + '​/api​/organize/tree', {
method: 'GET',
data: {
...params,
},
});
},
updateStore(data) {
return request(getDataBaseUrl() + '/api/store/updateStore', {
method: 'Post',
data: data,
});
},
deleteStore(data) {
return request(getDataBaseUrl() + '/api/store/deleteStore', {
method: 'Post',
data: data,
});
},
getAllRecipeList(isWeight) {
return request(getDataBaseUrl() + `/api/recipe/getAllRecipeList?isWeight=${isWeight}`, {
method: 'Get',
});
},
getHasDeviceRelation(id) {
return request(getDataBaseUrl() + `/api/store/getHasDeviceRelation?storeId=${id}`, {
method: 'Get',
});
},
getStoreRelation(id) {
return request(getDataBaseUrl() + `/api/store/getStoreRelation?storeId=${id}`, {
method: 'Get',
});
},
setStoreRelation(data) {
return request(getDataBaseUrl() + '/api/store/setStoreRelation', {
method: 'Post',
data: data,
});
},
}

+ 96
- 0
src/pages/sys/devicelog/index.jsx Vedi File

@@ -0,0 +1,96 @@
import { PageContainer } from "@ant-design/pro-layout";
import ProTable from "@ant-design/pro-table";
import api from './service';
import moment from 'moment';

const devicelog = () => {

const columns = [
{
title: '主键',
dataIndex: 'id',
tip: 'key',
hideInSearch: true,
hideInTable: true
},
{
title: '文件名称',
dataIndex: 'fileName',
ellipsis: true,
},
{
title: '门店名称',
dataIndex: 'storeName',
ellipsis: true,
},
{
title: '设备名称',
dataIndex: 'deviceName',
ellipsis: true,
},
{
title: '创建时间',
dataIndex: 'createAt',
valueType: 'dateTimeRange',
render: (_, record) => {
return <span>{record.createAt}</span>;
}
},
{
title: '操作',
dataIndex: 'option',
valueType: 'option',
fixed: 'right',
width: 350,
render: (_, record) => [
<a
key="config"
href={record.fileUrl}
download
>
下载
</a>
]
}
]
return (
<PageContainer
header={{
title: '',
breadcrumb: {},
}}
>
<ProTable
headerTitle="日志列表"
rowKey="id"
pagination={{ defaultPageSize: 10 }}
search={{
labelWidth: 'auto',
span: '1',
collapseRender: false,
defaultParams: {
storeName: '99',
// createAt: [moment().startOf('day'), moment().endOf('day')],
},
}}
request={async (params) => {
let logData = [];
var total = 0;
params.startTime = params.createAt != undefined ? params.createAt[0] : null;
params.endTime = params.createAt != undefined ? params.createAt[1] : null;
await api.getDeviceLogPage(params).then((r) => {
logData = r.data.data;
total = r.data.total;
});
return {
data: logData,
total: total
};
}}
columns={columns}
/>
</PageContainer>
)
}

export default devicelog;

+ 10
- 0
src/pages/sys/devicelog/service.js Vedi File

@@ -0,0 +1,10 @@
import { request } from 'umi';
import { getDataBaseUrl } from '@/global_data';
export default {
getDeviceLogPage(params) {
return request(getDataBaseUrl() + '/api/store/getDeviceLogPage', {
method: 'Post',
data: params,
});
},
}

+ 2
- 2
src/pages/user/login/index.jsx Vedi File

@@ -110,13 +110,13 @@ const Login = () => {
<img alt="logo" className={styles.logo} src="/logo.png" />
</div>
</Link>
<div className={styles.desc}>黑菠萝智慧后厨解决方案</div>
<div className={styles.desc}>配料系统</div>
</div>
</div>
</div>

<div className={styles.main}>
<div className={styles.title}>黑菠萝智慧后厨管理</div>
<div className={styles.title}>配料系统</div>
<ProForm
initialValues={{
autoLogin: true,


Caricamento…
Annulla
Salva