Commit ec7357f1 authored by 武广's avatar 武广

Merge branch 'feature/merchant-info-alter' into 'master'

Feature/merchant info alter

See merge request !98
parents d1c26369 46250595
......@@ -14,7 +14,7 @@ module.exports = {
'@typescript-eslint/no-unused-vars': ['off'],
'import/no-unresolved': 0,
'import/extensions': 0,
'no-unused-expressions': ['error', { allowShortCircuit: true }],
'no-unused-expressions': ['off'],
'template-curly-spacing': 'off',
},
};
import slash from 'slash2';
import defaultSettings from './defaultSettings'; // https://umijs.org/config/
import webpackPlugin from './plugin.config';
import groupMealRoute from './groupMealRoute';
const { pwa, primaryColor } = defaultSettings; // preview.pro.ant.design only do not use in your production ;
// preview.pro.ant.design 专用环境变量,请不要在你的项目中使用它。
......@@ -48,7 +48,6 @@ const plugins = [
},
],
];
export default {
antd: {},
dva: {
......@@ -62,8 +61,11 @@ export default {
targets: {
ie: 11,
},
// devtool: process.env.SENTRY_ENV === 'test' ? false : 'hidden-source-map',
devtool: false,
// devtool: process.env.SENTRY_ENV === 'prod' ? 'hidden-source-map' : false,
// devtool: 'source-map',
// uglifyJSOptions: {
// sourceMap: true
// },
// umi routes: https://umijs.org/zh/guide/router.html
routes: [
{
......@@ -286,6 +288,7 @@ export default {
name: 'businessInfo',
component: './businessManage/info',
},
...groupMealRoute,
{
component: './404',
},
......@@ -340,7 +343,11 @@ export default {
},
},
},
chunks: ['antdesigns', 'vendors', 'mapvgl', 'umi'],
chainWebpack: webpackPlugin,
nodeModulesTransform: {
type: 'none',
},
/*
proxy: {
'/server/api/': {
......
import RoleType, { isPlatForm } from './role.config';
const isProduction = process.env.NODE_ENV === 'production';
const isPre = process.env.PRE_ENV === 'pre';
const environment = 'yxm2';
const envAPi = {
api: `https://security-${environment}.liangkebang.net`, //'https://security-xyqb.liangkebang.net',
kdspOpApi: `https://sc-merchant-api-${environment}.liangkebang.net`,
......@@ -12,6 +14,9 @@ const envAPi = {
opapiHost: `https://gw.yxmie.com`,
wsApi: `wss://push-${environment}.liangkebang.net`,
msgApi: `https://msgapi-${environment}.liangkebang.net`,
roleApi: isPlatForm
? `https://sc-op-api-${environment}.liangkebang.net`
: `https://sc-merchant-api-${environment}.liangkebang.net`,
};
const prodApi = {
......@@ -29,6 +34,7 @@ const prodApi = {
querysApi: 'https://sc-merchant-api.q-gp.com/admin/merchant/sc-settlement',
wsApi: 'wss://push.q-gp.com',
msgApi: 'https://msgapi.q-gp.com',
roleApi: isPlatForm ? 'https://sc-op-api.q-gp.com' : 'https://sc-merchant-api.q-gp.com',
};
const preProdApi = {
......@@ -42,16 +48,19 @@ const preProdApi = {
querysApi: 'https://sc-settlement-api.q-gp.com',
wsApi: 'wss://push.q-gp.com',
msgApi: 'https://msgapi.q-gp.com',
roleApi: isPlatForm ? 'https://sc-op-api-pre.q-gp.com' : 'https://sc-merchant-api-pre.q-gp.com',
};
let exportApi = envAPi;
let json = envAPi;
if (isPre) {
exportApi = preProdApi;
json = preProdApi;
} else if (isProduction) {
exportApi = prodApi;
json = prodApi;
}
module.exports = exportApi;
const exportApi = Object.assign({}, RoleType, json);
// module.exports = exportApi;
// let exportApi;
// isProduction ? (exportApi = prodApi) : (exportApi = envAPi);
// export default exportApi;
export default exportApi;
export default [
{
title: '商户管理后台-企业团餐-企业客户',
path: '/businessCustomer',
name: 'BusinessCustomer',
component: './businessCustomer/index',
},
{
title: '商户管理后台-企业团餐-外卖商品',
path: '/takeawayGoods',
name: 'TakeawayGoods',
component: './businessGoods/takeawayGoods',
},
{
title: '商户管理后台-企业团餐-外卖商品-添加商品',
path: '/takeawayGoodsInfo',
name: 'TakeawayGoodsInfo',
component: './businessGoods/takeawayGoodsInfo',
},
{
title: '商户管理后台-企业团餐-虚拟商品',
path: '/virtualGoods',
name: 'VirtualGoods',
component: './businessGoods/virtualGoods',
},
{
title: '商户管理后台-企业团餐-虚拟商品-添加商品',
path: '/virtualGoodsInfo',
name: 'VirtualGoodsInfo',
component: './businessGoods/virtualGoodsInfo',
},
{
title: '商户管理后台-企业团餐-企业店铺管理',
path: '/StoreManagement',
name: 'StoreManagement',
component: './StoreManagement',
},
{
title: '商户管理后台-企业团餐-企业员工管理',
path: '/EmployeeManagement',
name: 'EmployeeManagement',
component: './EmployeeManagement',
},
];
......@@ -4,28 +4,9 @@
/* eslint-disable import/no-extraneous-dependencies */
import ThemeColorReplacer from 'webpack-theme-color-replacer';
import generate from '@ant-design/colors/lib/generate';
import path from 'path';
import webpack from 'webpack';
const SentryPlugin = require('@qg/sentry-webpack-plugin');
const SentryConfig = require('../.sentryclirc');
function getModulePackageName(module) {
if (!module.context) return null;
const nodeModulesPath = path.join(__dirname, '../node_modules/');
if (module.context.substring(0, nodeModulesPath.length) !== nodeModulesPath) {
return null;
}
const moduleRelativePath = module.context.substring(nodeModulesPath.length);
const [moduleDirName] = moduleRelativePath.split(path.sep);
let packageName = moduleDirName; // handle tree shaking
if (packageName && packageName.match('^_')) {
// eslint-disable-next-line prefer-destructuring
packageName = packageName.match(/^_(@?[^@]+)/)[1];
}
return packageName;
}
export default config => {
if (process.env.NODE_ENV === 'production') {
......@@ -61,44 +42,44 @@ export default config => {
},
]);
} // optimize chunks
if (process.env.SENTRY_ENV === 'prod') {
config.plugin('source-map-dev-tool-plugin').use(webpack.SourceMapDevToolPlugin, [
{
exclude: /(antdesigns|vendors|mapvgl).*/, // 以下文件排除不生成
filename: '[file].map[query]', // 指定map文件名称
},
]);
}
config.optimization // share the same chunks across different modules
.runtimeChunk(false)
.splitChunks({
chunks: 'async',
name: 'vendors',
maxInitialRequests: Infinity,
minSize: 0,
chunks: 'all', //async异步代码分割 initial同步代码分割 all同步异步分割都开启
automaticNameDelimiter: '.',
name: true,
minSize: 30000, // 引入的文件大于30kb才进行分割
minChunks: 1, // 模块至少使用次数
cacheGroups: {
// 分离antd系代码
antdesigns: {
name: 'antdesigns',
chunks: 'all',
test: /[\\/]node_modules[\\/](@antv|antd|@ant-design|rc-*|[\\@]ctrl[\\/]tinycolor|tinycolor2)/,
priority: 10,
},
// 分离公共类库
vendors: {
test: module => {
const packageName = getModulePackageName(module) || '';
if (packageName) {
return [
'bizcharts',
'gg-editor',
'g6',
'@antv',
'gg-editor-core',
'bizcharts-plugin-slider',
].includes(packageName);
}
return false;
},
name(module) {
const packageName = getModulePackageName(module);
if (packageName) {
if (['bizcharts', '@antv_data-set'].indexOf(packageName) >= 0) {
return 'viz'; // visualization package
}
}
return 'misc';
},
name: 'vendors',
chunks: 'all',
test: /([\\/]node_modules[\\/](lodash|moment|react|dva|postcss|mapbox-gl|date-fns|sentry|react-sortablejs|sortablejs))|([\\/]src[\\/]utils[\\/]qiniu\.min\.js)/,
priority: 10,
enforce: true,
},
// 分离geo库
mapvgl: {
name: 'mapvgl',
chunks: 'all',
test: /[\\/]node_modules[\\/](mapvgl|react-bmapgl|react-amap)/,
priority: 11,
},
},
});
......
/**
* 从别的平台进入角色判断
*/
// 从tob进入的判断接口前缀
const getUrlParams = name => {
const regArg = RegExp(`(^|&)${name}=([^&]*)(&|$)`);
const r = regArg.exec(window.location.search.substring(1));
if (r != null) return decodeURIComponent(r[2]);
return null;
};
export const apiType = {
platform: '/api/consoles',
enterprise: '/api/enterprises',
pop: '/api/consoles',
};
// role 类型 (platform:平台, enterprise:企业, pop:pop商)
export const RolePlatform = 'platform'; // 平台
export const RoleEnterprise = 'enterprise'; // 企业
export const RolePop = 'pop'; // pop商
export const UserRole = getUrlParams('role') || '';
export const isPlatForm = UserRole === RolePlatform;
export const apiPrefix = apiType[UserRole];
const RoleType = {
RolePlatform,
RoleEnterprise,
RolePop,
apiPrefix,
isPlatForm,
UserRole,
};
export default RoleType;
This source diff could not be displayed because it is too large. You can view the blob instead.
......@@ -2,7 +2,7 @@
import * as Sentry from '@sentry/react';
import localStorage from '@/utils/localStorage';
if (process.env.NODE_ENV === 'production') {
if (process.env.SENTRY_ENV !== 'prod' && process.env.NODE_ENV === 'production') {
try {
Sentry.init({
dsn: 'https://b3f60c62e1234e26a5b851b9f26fba07@sentry.q-gp.com/34',
......
......@@ -24,7 +24,7 @@ const CustomTree = forwardRef(props => {
const findArr = childrens => {
childrens.forEach(node => {
if (node.children && node.children.length) {
node.visibleChildren && arr.push(initChildrenStatus(node.children, node.checked));
node.visibleChildren && arr.push(initChildrenStatus(node.children));
findArr(node.children);
}
});
......@@ -69,7 +69,7 @@ const CustomTree = forwardRef(props => {
}
});
};
props.value && props.value.length && checkfn(props.value, json);
props.value && props.value.length && checkfn(props.value);
};
// 格式化数据
const filterData = (arr, parentChecked) => {
......@@ -247,7 +247,7 @@ const CustomTree = forwardRef(props => {
await getSyncLoadChildrens(datas, ckey, isChecked);
}
changeChecked(datas, ckey, isChecked);
const values = getCheckTreeValue(datas, isChecked);
const values = getCheckTreeValue(datas);
if (level === 1) {
onVisibleChildren('', false);
}
......@@ -269,7 +269,7 @@ const CustomTree = forwardRef(props => {
// 隐藏所有子树
const handleMouseUp = e => {
const isCur = e.path.some(
const isCur = e?.path?.some(
item =>
item.className &&
typeof item.className === 'string' &&
......
......@@ -69,3 +69,10 @@ ol {
}
}
}
.ant-pro-card-body {
padding: 0 !important;
}
.ant-pro-table-list-toolbar-container {
box-sizing: border-box;
padding: 10px 30px !important;
}
......@@ -13,7 +13,7 @@ import { Result, Button, Layout, Menu } from 'antd';
import Authorized from '@/utils/Authorized';
import RightContent from '@/components/GlobalHeader/RightContent';
import MessageReminder from '@/components/MessageReminder';
import { getAuthorityFromRouter } from '@/utils/utils';
import { getAuthorityFromRouter, getUrlSearchParams, getToken } from '@/utils/utils';
import { getSocketUrl } from '@/services/messageReminder';
import logo from '../assets/logo.png';
import style from './BasicLayout.less';
......@@ -54,11 +54,12 @@ const BasicLayout = props => {
const [siderCollapsed, setSiderCollapsed] = useState(false);
const messageReminderComplexRef = useRef();
// const audioRef = useRef()
useEffect(() => {
try {
const token = window.localStorage.getItem('token');
const token = getToken();
const socket = new Socket({
url: getSocketUrl({ token, channelId: CHANNEL_ID }),
});
......@@ -131,7 +132,8 @@ const BasicLayout = props => {
),
);
return window.__POWERED_BY_QIANKUN__ ? (
// 乾坤嵌套进入系统
const qianKunLayout = () => (
<div id="micro">
<Layout className={style.layout}>
<Sider
......@@ -148,7 +150,10 @@ const BasicLayout = props => {
</Layout>
</Layout>
</div>
) : (
);
// 商家端直接进入系统
const merchantLayout = () => (
<ProLayout
logo={logo}
onCollapse={handleMenuCollapse}
......@@ -206,6 +211,27 @@ const BasicLayout = props => {
{/* <Button ref={audioRef} onClick={() => { socket.play() }}>声音</Button> */}
</ProLayout>
);
// tob后管嵌套进入系统
const tobLayout = () => (
<Layout className={style.tobLayout}>
<Authorized noMatch={noMatch}>{children}</Authorized>
</Layout>
);
// 是否从消费地图后管跳转过来
const searchPrams = getUrlSearchParams();
const isTob = searchPrams.source === 'tob';
if (window.__POWERED_BY_QIANKUN__) {
return qianKunLayout();
}
if (isTob && searchPrams.token) {
console.log('window.parent :>> ', window.parent);
window.wp = window.parent;
return tobLayout();
}
return merchantLayout();
};
export default connect(({ global, settings, menu }) => ({
collapsed: global.collapsed,
......
......@@ -44,3 +44,11 @@
}
}
}
.tobLayout {
:global {
.ant-pro-page-header-wrap-page-header-warp {
display: none !important;
}
}
}
......@@ -3,6 +3,8 @@ import { connect } from 'dva';
import { Redirect } from 'umi';
import PageLoading from '@/components/PageLoading';
import localStorage from '@/utils/localStorage';
import sessionStorage from '@/utils/sessionStorage';
import { getUrlSearchParams, getToken } from '@/utils/utils';
class SecurityLayout extends React.Component {
state = {
......@@ -24,13 +26,24 @@ class SecurityLayout extends React.Component {
/* eslint-disable no-underscore-dangle */
render() {
// 判断是否从tob进入
const searchPrams = getUrlSearchParams();
const isTob = searchPrams.source === 'tob';
if (isTob) {
localStorage.set('tobToken', searchPrams.token || '');
// role 类型 (platform:平台, enterprise:企业, pop:pop商)
sessionStorage.set('role', searchPrams.role);
}
const { isReady } = this.state;
let isLogin = true;
// 你可以把它替换成你自己的登录认证规则(比如判断 token 是否存在)
const { children, loading } = this.props;
if (!localStorage.get('token')) {
if (!getToken()) {
isLogin = false;
}
console.log('getToken() :>> ', getToken());
console.log('isLogin :>> ', isLogin);
// 切换子应用布局
if (window.__POWERED_BY_QIANKUN__) {
......
import React, { useState, useEffect } from 'react';
import { Row, Col } from 'antd';
import { Link } from 'umi';
// import { PageHeaderWrapper } from '@ant-design/pro-layout';
// eslint-disable-next-line import/no-extraneous-dependencies
import { FileTextOutlined } from '@ant-design/icons';
import style from './styles.less';
import { getPendingNum } from './service';
import { connect } from 'dva';
import {
AFTER_SALE_ORDER,
CANCEL_BILL_MANAGE,
PENDING_DELIVERY_ORDER,
} from '@/../config/permission.config';
import { AFTER_SALE_ORDER, PENDING_DELIVERY_ORDER } from '@/../config/permission.config';
const Admin = props => {
const [pendingNum, setpendingNum] = useState({});
const [pendingNum, setPendingNum] = useState({});
const showAfterSaleList = props.permissions[AFTER_SALE_ORDER.LIST];
const showCancelBillList = props.permissions[CANCEL_BILL_MANAGE.LIST];
const showPendingDeliveryOrderList = props.permissions[PENDING_DELIVERY_ORDER.LIST];
const query = async () => {
const res = await getPendingNum();
setPendingNum(res?.data || {});
};
useEffect(() => {
const qurey = async () => {
const { data } = await getPendingNum();
setpendingNum(data);
};
qurey();
query();
}, []);
return (
......
import React from 'react';
import { Modal, Table } from 'antd';
const RechargeDetailsModal = ({ visible, onClose, list }) => {
const columns = [
{
title: '员工ID',
dataIndex: 'staffNo',
key: 'staffNo',
align: 'center',
},
{
title: '员工姓名',
dataIndex: 'staffName',
key: 'staffName',
align: 'center',
},
{
title: '充值余额',
dataIndex: 'rechargeAmount',
key: 'rechargeAmount',
align: 'center',
},
{
title: '充值时间',
dataIndex: 'generateDate',
key: 'generateDate',
align: 'center',
},
];
return (
<Modal
width="800px"
visible={visible}
title="充值明细"
onCancel={() => onClose(false, 'rechargeDetails')}
footer={null}
>
<Table
dataSource={list}
columns={columns}
rowKey={record => record.id}
pagination={false}
bordered
/>
</Modal>
);
};
export default RechargeDetailsModal;
import React from 'react';
import { Modal, Form, Input, Checkbox, message } from 'antd';
import styles from '../index.less';
import { apiStaffBlack } from '../service';
const { Item } = Form;
const BlacklistModal = ({ visible, onClose, list, enterpriseId, selectedRows }) => {
const [form] = Form.useForm();
const handleSave = async () => {
const values = await form.validateFields();
const params = {
enterpriseId,
ids: list,
isBlack: 1,
balanceBackFlag: 1,
};
const res = await apiStaffBlack(params);
if (res?.businessCode === '0000') {
message.success('设置成功');
onClose(true, 'blacklist');
}
};
return (
<Modal
visible={visible}
title="设置员工黑名单"
onCancel={() => onClose(false, 'blacklist')}
onOk={handleSave}
>
<Form form={form} layout="vertical">
<Item name="ids" label="您确定要把员工ID:">
{(selectedRows.length &&
selectedRows.map(item => <span key={item.staffNo}>{item.staffNo},</span>)) ||
''}
</Item>
<Item name="reason" label="加入黑名单吗?">
<div className={styles.blackList}>
<Checkbox defaultChecked disabled></Checkbox>
<div className={styles.left}>
<span>请确定要回收员工账户内的剩余余额&餐券吗? </span>
<br />
<span>(勾选则回收,不勾选则不回收。)</span>
</div>
</div>
</Item>
</Form>
</Modal>
);
};
export default BlacklistModal;
import React, { useState } from 'react';
import { Modal, Form, Radio, Input, Button, Upload, message, Select } from 'antd';
import { UploadOutlined } from '@ant-design/icons';
import { apiDepartmentSave, apiDepartmentExcel } from '../service';
import styles from '../index.less';
const { Dragger } = Upload;
const { Item } = Form;
const layout = {
labelCol: { span: 6 },
wrapperCol: { span: 16 },
};
const DepartmentModal = ({ visible, onClose, enterpriseList }) => {
const [form] = Form.useForm();
const [importMode, setImportMode] = useState(false);
const handleCancel = val => {
form.resetFields();
setImportMode(false);
onClose(val, 'department');
};
const handleImportChange = info => {
if (info.file.status === 'done') {
message.success('文件上传成功');
} else if (info.file.status === 'error') {
message.error('文件上传失败');
}
};
const getDepartmentSave = async values => {
const params = {
name: values.name,
enterpriseId: values.enterpriseId,
};
const res = await apiDepartmentSave(params);
if (res.businessCode === '0000') {
message.success('保存成功');
handleCancel(true);
}
};
const getDepartmentExcel = async values => {
const params = {
enterpriseId: values.enterpriseId,
file: values.file,
};
const res = await apiDepartmentExcel(params);
if (res?.businessCode === '0000') {
message.success('保存成功');
handleCancel(true);
}
};
const handleSave = async () => {
const values = await form.validateFields();
if (importMode) {
getDepartmentExcel(values);
return;
}
getDepartmentSave(values);
};
return (
<Modal
title="创建部门"
visible={visible}
onCancel={() => handleCancel(false)}
footer={[
<Button key="cancel" onClick={() => handleCancel(false)}>
取消
</Button>,
<Button key="save" type="primary" onClick={() => handleSave()}>
保存
</Button>,
]}
initialValue={{ configMode: 0 }}
>
<Form form={form} {...layout}>
<Item
label="选择企业"
name="enterpriseId"
rules={[{ required: true, message: '请选择企业' }]}
>
<Select
placeholder="请选择企业"
allowClear
showSearch
filterOption={(input, option) =>
(option?.label ?? '').toLowerCase().includes(input.toLowerCase())
}
options={enterpriseList}
/>
</Item>
<Item
label="配置方式"
name="configMode"
rules={[{ required: true, message: '请选择配置方式' }]}
initialValue={0}
>
<Radio.Group>
<Radio value={0} onChange={() => setImportMode(false)}>
单个添加
</Radio>
<Radio value={1} onChange={() => setImportMode(true)}>
批量导入
</Radio>
</Radio.Group>
</Item>
{importMode ? (
<>
<Item
label="上传文件"
name="file"
rules={[
{
// eslint-disable-next-line no-confusing-arrow
validator: (_, value) =>
value && value.fileList.length > 0
? Promise.resolve()
: // eslint-disable-next-line prefer-promise-reject-errors
Promise.reject('请上传文件'),
},
]}
>
<Dragger
beforeUpload={() => false}
maxCount={1}
accept=".xls,.xlsx"
onChange={handleImportChange}
>
<UploadOutlined />
<p>
将文件拖到此处,或<a href="#">点击上传</a>
</p>
</Dragger>
</Item>
<div className={styles.employees}>
<a href="https://kdspstatic.q-gp.com/%E6%96%B0%E5%A2%9E%E9%83%A8%E9%97%A8.xlsx">
部门导入模版.xlsx
</a>
</div>
</>
) : (
<Item
label="部门名称"
name="name"
rules={[{ required: true, message: '请输入部门名称' }]}
>
<Input maxLength={20} />
</Item>
)}
</Form>
</Modal>
);
};
export default DepartmentModal;
import React, { useState } from 'react';
import { Modal, Form, Input, Button, Select, message, Upload, Radio } from 'antd';
import { UploadOutlined } from '@ant-design/icons';
import { apiStaffSave, apiStaffExcel, apiDepartmentList } from '../service';
import styles from '../index.less';
const { Dragger } = Upload;
const { Option } = Select;
const layout = {
labelCol: { span: 6 },
wrapperCol: { span: 16 },
};
const NewEmployeeModal = props => {
const { visible, onClose, enterpriseList } = props;
const [form] = Form.useForm();
const [importMode, setImportMode] = useState(false);
const [departmentList, setDepartmentList] = useState([]);
const handleCancel = val => {
form.resetFields();
setImportMode(false);
onClose(val, 'newEmployee');
};
const handleSave = async () => {
const values = await form.validateFields();
if (importMode) {
const res = await apiStaffExcel(values);
if (res?.businessCode === '0000') {
message.success('上传成功');
handleCancel(true);
}
return;
}
const res = await apiStaffSave(values);
if (res?.businessCode === '0000') {
message.success('保存成功');
handleCancel(true);
}
};
const validatePhone = (_, value) => {
const phoneRegex = /^1[3456789]\d{9}$/;
if (!value || phoneRegex.test(value)) {
return Promise.resolve();
}
// eslint-disable-next-line prefer-promise-reject-errors
return Promise.reject('请输入有效的手机号码');
};
// 部门查询
const getDepartmentList = async id => {
const res = await apiDepartmentList({ data: { enterpriseId: id }, page: 1, size: 10000 });
if (res?.businessCode === '0000' && res.data?.records?.length) {
const list = res.data.records;
const optionData = list.map(item => ({
value: item.id,
label: item.name,
}));
setDepartmentList(optionData);
return;
}
setDepartmentList([]);
};
const onChange = value => {
form.setFieldsValue({ departmentId: undefined });
getDepartmentList(value);
};
return (
<Modal
visible={visible}
title="添加新员工"
onCancel={() => handleCancel(false)}
footer={[
<Button key="cancel" onClick={() => handleCancel(false)}>
取消
</Button>,
<Button key="save" type="primary" onClick={handleSave}>
保存
</Button>,
]}
>
<Form form={form} {...layout}>
<Form.Item
name="enterpriseId"
label="企业"
rules={[
{
required: true,
message: '请选择企业',
},
]}
>
<Select
onChange={onChange}
placeholder="请选择企业"
allowClear
showSearch
filterOption={(input, option) =>
(option?.label ?? '').toLowerCase().includes(input.toLowerCase())
}
options={enterpriseList}
/>
</Form.Item>
<Form.Item
label="配置方式"
name="configMode"
rules={[{ required: true, message: '请选择配置方式' }]}
initialValue={0}
>
<Radio.Group>
<Radio value={0} onChange={() => setImportMode(false)}>
单个添加
</Radio>
<Radio value={1} onChange={() => setImportMode(true)}>
批量导入
</Radio>
</Radio.Group>
</Form.Item>
{importMode ? (
<>
<Form.Item
label="上传文件"
name="file"
rules={[
{
// eslint-disable-next-line no-confusing-arrow
validator: (_, value) =>
value && value.fileList.length > 0
? Promise.resolve()
: // eslint-disable-next-line prefer-promise-reject-errors
Promise.reject('请上传文件'),
},
]}
>
<Dragger beforeUpload={() => false} maxCount={1} accept=".xls,.xlsx">
<UploadOutlined />
<p>
将文件拖到此处,或<a href="#">点击上传</a>
</p>
</Dragger>
</Form.Item>
<div className={styles.employees}>
<a href="https://kdspstatic.q-gp.com/%E6%96%B0%E5%A2%9E%E5%91%98%E5%B7%A5.xlsx">
员工导入模版.xlsx
</a>
</div>
</>
) : (
<>
<Form.Item
name="departmentId"
label="部门"
rules={[
{
required: true,
message: '请选择部门',
},
]}
>
<Select
placeholder="请选择部门"
allowClear
showSearch
filterOption={(input, option) =>
(option?.label ?? '').toLowerCase().includes(input.toLowerCase())
}
options={departmentList}
/>
</Form.Item>
<Form.Item
name="staffNo"
label="员工ID"
rules={[
{
required: true,
message: '请输入员工ID',
},
{
pattern: /^[a-zA-Z0-9]+$/,
message: '员工ID由数字与英文大小写字母组成',
},
]}
>
<Input maxLength={15} />
</Form.Item>
<Form.Item
name="staffName"
label="员工姓名"
rules={[
{
required: true,
message: '请输入员工姓名',
},
]}
>
<Input />
</Form.Item>
<Form.Item
name="mobile"
label="员工手机号"
rules={[
{
required: true,
message: '请输入员工手机号',
},
{
validator: validatePhone,
},
]}
>
<Input maxLength={11} />
</Form.Item>
</>
)}
</Form>
</Modal>
);
};
export default NewEmployeeModal;
import React, { useEffect, useState } from 'react';
import { Modal, Form, Input, Button, Table, Space, Row, Col, Select, message } from 'antd';
import { apiDepartmentList, apiDepartmentUpdate } from '../service';
const { Item } = Form;
const ViewDepartmentModal = ({ visible, onClose, enterpriseList }) => {
const [form] = Form.useForm();
const [refForm] = Form.useForm();
const [dataSource, setDataSource] = useState([]);
const [nameVisible, setNameVisible] = useState(false);
const [enterpriseId, setEnterpriseId] = useState('');
const [name, setName] = useState('');
const [id, setId] = useState('');
const [total, setTotal] = useState(0);
const [pageInfo, setPageInfo] = useState({
page: 1,
size: 10,
});
const handleCancel = () => {
form.resetFields();
setDataSource([]);
setPageInfo({ page: 1, size: 10 });
setTotal(0);
onClose(false, 'viewDepartment');
};
const editName = row => {
setName(row.name);
refForm.setFieldsValue({ name: row.name });
setId(row.id);
setNameVisible(true);
};
const getDepartmentList = async params => {
const res = await apiDepartmentList(params);
if (res.businessCode === '0000') {
const list = res.data.records;
setTotal(res.data.total);
setDataSource(list);
}
};
const handleSearch = async values => {
setEnterpriseId(values.enterpriseId);
getDepartmentList({ data: { enterpriseId: values.enterpriseId }, ...pageInfo });
};
const handleSave = () => {
refForm.validateFields().then(async values => {
const params = {
enterpriseId,
id,
name: values.name,
};
const res = await apiDepartmentUpdate(params);
if (res?.businessCode === '0000') {
message.success('修改成功');
setNameVisible(false);
getDepartmentList({ data: { enterpriseId }, ...pageInfo });
}
});
};
const columns = [
{
title: 'ID',
dataIndex: 'id',
key: 'id',
},
{
title: '部门',
dataIndex: 'name',
key: 'name',
},
{
title: '创建时间',
dataIndex: 'createdAt',
key: 'createdAt',
},
{
title: '创建人',
dataIndex: 'createdBy',
key: 'createdBy',
},
{
title: '操作',
dataIndex: 'operation',
key: 'operation',
render: (_, record) => (
<Space>
<Button type="link" onClick={() => editName(record)}>
修改部门名称
</Button>
</Space>
),
},
];
const onCancel = () => {
refForm.resetFields();
setName('');
setNameVisible(false);
};
const onChange = (page, size) => {
const json = {
page,
size,
};
setPageInfo(json);
getDepartmentList({ data: { enterpriseId }, ...json });
};
const pagination = {
...pageInfo,
total,
showTotal: t => `共 ${t} 条`,
onChange,
onShowSizeChange: onChange,
};
return (
<Modal visible={visible} onCancel={handleCancel} width={800} footer={null} title="查看部门">
<Form form={form} onFinish={handleSearch}>
<Row gutter={16}>
<Col span={10}>
<Item name="enterpriseId" style={{ width: '300px' }}>
<Select
placeholder="请选择企业"
allowClear
showSearch
filterOption={(input, option) =>
(option?.label ?? '').toLowerCase().includes(input.toLowerCase())
}
options={enterpriseList}
/>
</Item>
</Col>
<Col>
<Item>
<Button type="primary" htmlType="submit">
搜索
</Button>
</Item>
</Col>
</Row>
</Form>
<Table
dataSource={dataSource}
columns={columns}
bordered
rowKey={r => r.id}
pagination={pagination}
/>
<Modal
title="修改部门名称"
visible={nameVisible}
footer={[
<Button key="cancel" onClick={onCancel}>
取消
</Button>,
<Button key="save" type="primary" onClick={() => handleSave()}>
保存
</Button>,
]}
onCancel={onCancel}
>
<Form form={refForm}>
<Form.Item
label="部门名称"
name="name"
initialValue={name}
rules={[{ required: true, message: '请填写部门名称' }]}
>
<Input maxLength={20} />
</Form.Item>
</Form>
</Modal>
</Modal>
);
};
export default ViewDepartmentModal;
import React from 'react';
import { Image as ImageComponent, Button, Switch } from 'antd';
import { FormOutlined } from '@ant-design/icons';
export const repastTypeList = [
{
value: 0,
label: '拉黑',
},
{
value: 1,
label: '正常',
},
];
export const columns = props => [
{
title: '部门名称',
key: 'departmentName',
dataIndex: 'departmentName',
align: 'center',
},
{
title: '员工姓名',
key: 'staffName',
dataIndex: 'staffName',
align: 'center',
},
{
title: '员工ID',
key: 'staffNo',
dataIndex: 'staffNo',
align: 'center',
},
{
title: '员工手机号',
key: 'mobile',
dataIndex: 'mobile',
align: 'center',
},
{
title: '员工状态',
key: 'isBlack',
dataIndex: 'isBlack',
align: 'center',
render: _ => (_ ? '拉黑' : '正常'),
},
{
title: '余额/券状态',
key: 'balanceBackFlag',
dataIndex: 'balanceBackFlag',
align: 'center',
render: _ => (_ ? '已收回' : '正常'),
},
{
title: '是否消费限额',
key: 'isLimit',
dataIndex: 'isLimit',
align: 'center',
width: '150px',
render: (_, row) => (
<>
<Switch defaultChecked={row?.isLimit} onChange={val => props.handleLimitChange(val, row)} />
&nbsp;<span>{row?.isLimit ? '已开启' : '已关闭'}</span>
</>
),
},
{
title: '员工企业余额',
key: 'balance',
dataIndex: 'balance',
align: 'center',
},
{
title: '有效餐券张数',
key: 'couponCount',
dataIndex: 'couponCount',
align: 'center',
},
{
title: '操作',
dataIndex: 'option',
valueType: 'option',
align: 'center',
key: 'option',
render: (_, row) => [
<Button type="link" key="goDetails" onClick={() => props.goDetails(row)}>
余额充值明细
</Button>,
<Button
type="link"
key="delEmployee"
disabled={!row?.isBlack}
onClick={() => props.delEmployee(row)}
>
删除
</Button>,
],
},
];
import React, { useState, useRef, forwardRef, useEffect } from 'react';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import {
Modal,
Form,
Select,
Table,
Card,
Row,
Col,
Input,
Button,
message,
Upload,
notification,
} from 'antd';
import { columns, repastTypeList } from './data';
import styles from './index.less';
import {
apiStaffList,
apiEnterpriseList,
apiDepartmentList,
apiStaffExcel,
apiStaffDelete,
apiGenerateLogList,
apiStaffLimit,
} from './service.js';
import NewEmployeeModal from './components/newEmployeeModal';
import DepartmentModal from './components/departmentModal';
import ViewDepartmentModal from './components/viewDepartmentModal';
import BlacklistModal from './components/blacklistModal';
import RechargeDetailsModal from './components/RechargeDetailsModal';
import { stringOrObjectTrim } from '@/utils/utils';
const layout = {
labelCol: { span: 6 },
wrapperCol: { span: 8 },
};
const { confirm } = Modal;
const StoreManagement = () => {
const [modalVisible, setModalVisible] = useState(false);
const [departmentVisible, setDepartmentVisible] = useState(false);
const [viewDepartmentVisible, setViewDepartmentVisible] = useState(false);
const [blacklistVisible, setBlacklistVisible] = useState(false);
const [rechargeDetailsVisible, setRechargeDetailsVisible] = useState(false);
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
const [selectedRows, setSelectedRows] = useState([]);
const [staffList, setStaffList] = useState([]);
const [enterpriseList, setEnterpriseList] = useState([]);
const [firstEnterprise, setFirstEnterprise] = useState();
const [departmentList, setDepartmentList] = useState([]);
const [generateLog, setGenerateLog] = useState([]);
const [loading, setLoading] = useState(false);
const [page, setPage] = useState({
current: 1,
pageSize: 10,
});
const [total, setTotal] = useState(0);
const [searchForm, setSearchForm] = useState({});
const formRef = useRef(null);
const [editVisible, setEditVisible] = useState(false);
const [addVisible, setAddVisible] = useState(false);
const [repastType, setRepastType] = useState([]);
const [repastId, setRepastId] = useState(null);
const shopList = async params => {
// 去掉前后空格
const data = stringOrObjectTrim(params);
setLoading(true);
const res = await apiStaffList(data);
if (res?.businessCode === '0000') {
const list = res.data?.records;
setTotal(res.data.total);
setStaffList(list);
setLoading(false);
return;
}
setTimeout(() => {
setLoading(false);
setStaffList([]);
setTotal(0);
}, 5000);
};
const rowSelection = {
selectedRowKeys,
onChange: (select, selectList) => {
setSelectedRowKeys(select);
setSelectedRows(selectList);
},
getCheckboxProps: record => ({
disabled: record.isBlack === 1,
}),
};
// 部门查询
const getDepartmentList = async id => {
const res = await apiDepartmentList({ data: { enterpriseId: id }, page: 1, size: 10000 });
if (res?.businessCode === '0000' && res.data?.records?.length) {
const list = res.data.records;
const optionData = list.map(item => ({
value: item.id,
label: item.name,
}));
setDepartmentList(optionData);
return;
}
setDepartmentList([]);
};
// 关闭弹框
const handleCloseModal = (status, val) => {
switch (val) {
case 'newEmployee':
setModalVisible(false);
break;
case 'viewDepartment':
setViewDepartmentVisible(false);
getDepartmentList(searchForm.enterpriseId);
shopList({ ...page, data: searchForm });
break;
case 'department':
setDepartmentVisible(false);
getDepartmentList(searchForm.enterpriseId);
break;
case 'blacklist':
setBlacklistVisible(false);
setSelectedRowKeys([]);
setSelectedRows([]);
break;
case 'rechargeDetails':
setGenerateLog([]);
setRechargeDetailsVisible(false);
break;
default:
break;
}
if (status) {
shopList({ ...page, data: searchForm });
}
};
// 企业查询
const getEnterpriseList = async () => {
const res = await apiEnterpriseList();
if (res?.businessCode === '0000' && res.data?.records?.length) {
const list = res.data.records;
const firstOption = list[0].id;
const optionData = list.map(item => ({
value: item.id,
label: item.name,
}));
setEnterpriseList(optionData);
setFirstEnterprise(firstOption);
getDepartmentList(firstOption);
setSearchForm({ enterpriseId: firstOption });
shopList({ ...page, data: { enterpriseId: firstOption } });
}
};
// 设置黑名单
const getBlacklist = () => {
if (!selectedRowKeys.length) {
notification.error({ message: '请选择员工' });
return;
}
setBlacklistVisible(true);
};
useEffect(() => {
getEnterpriseList();
}, []);
// 删除
const deleteEmployee = async id => {
const res = await apiStaffDelete({ id, enterpriseId: searchForm.enterpriseId, state: 0 });
if (res?.businessCode === '0000') {
message.success('删除成功!');
shopList({ ...page, data: searchForm });
}
};
// 删除
const delEmployee = ({ staffName, id }) => {
confirm({
title: `确认删除企业员工#${staffName || ''}#?`,
onOk() {
deleteEmployee(id);
},
onCancel() {
console.log('Cancel');
},
});
};
// 添加商户
const addShop = () => {
setAddVisible(true);
};
// 搜索
const onFinish = values => {
setSearchForm(values);
const pageInfo = {
current: 1,
pageSize: 10,
};
setPage(pageInfo);
shopList({ ...pageInfo, data: values });
};
// 重置
const onReset = () => {
formRef.current.resetFields();
setSearchForm({ enterpriseId: firstEnterprise });
shopList({ ...page, data: { enterpriseId: firstEnterprise } });
};
// 分页
const onChange = (current, pageSize) => {
const pageInfo = {
current,
pageSize,
};
setPage(pageInfo);
shopList({ data: searchForm, ...pageInfo });
};
const goDetails = async ({ staffNo }) => {
setRechargeDetailsVisible(true);
const res = await apiGenerateLogList({ staffNo, enterpriseId: searchForm.enterpriseId });
if (res?.businessCode === '0000') {
const list = res.data;
setGenerateLog(list);
}
};
const handleLimitChange = async (checked, row) => {
const res = await apiStaffLimit({
id: row.id,
isLimit: checked ? 1 : 0,
enterpriseId: searchForm.enterpriseId,
});
if (res?.businessCode === '0000') {
message.success('设置成功');
shopList({ ...page, data: searchForm });
}
};
const enterpriseOnChange = val => {
setFirstEnterprise(val);
getDepartmentList(val);
const pageInfo = {
current: 1,
pageSize: 10,
};
const values = formRef.current.getFieldsValue();
setPage(pageInfo);
setSearchForm(values);
shopList({ ...pageInfo, data: values });
};
const res = {
delEmployee,
goDetails,
handleLimitChange,
};
const pagination = {
...page,
total,
showTotal: t => `共 ${t} 条`,
onChange,
onShowSizeChange: onChange,
};
return (
<PageHeaderWrapper>
<Card className={styles.card}>
{firstEnterprise && (
<Form ref={formRef} onFinish={onFinish} {...layout}>
<Row gutter={24}>
<Col span={8}>
<Form.Item
label="企业名称"
name="enterpriseId"
wrapperCol={{ span: 16 }}
rules={[{ required: true }]}
initialValue={firstEnterprise}
>
<Select
showSearch
filterOption={(input, option) =>
(option?.label ?? '').toLowerCase().includes(input.toLowerCase())
}
options={enterpriseList}
onChange={enterpriseOnChange}
/>
</Form.Item>
</Col>
<Col span={8}>
<Form.Item label="部门查询" name="departmentIds" wrapperCol={{ span: 16 }}>
<Select
mode="multiple"
allowClear
showSearch
filterOption={(input, option) =>
(option?.label ?? '').toLowerCase().includes(input.toLowerCase())
}
options={departmentList}
/>
</Form.Item>
</Col>
<Col span={8}>
<Form.Item label="员工手机号" name="mobile" wrapperCol={{ span: 16 }}>
<Input maxLength="11" allowClear />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item label="员工id" name="staffNo" wrapperCol={{ span: 16 }}>
<Input allowClear />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item label="员工状态" name="isBlack" wrapperCol={{ span: 16 }}>
<Select
allowClear
showSearch
filterOption={(input, option) =>
(option?.label ?? '').toLowerCase().includes(input.toLowerCase())
}
options={[
{
value: 0,
label: '正常',
},
{
value: 1,
label: '拉黑',
},
]}
placeholder="全部"
/>
</Form.Item>
</Col>
<Col span={8}>
<Form.Item label="员工姓名" name="staffName" wrapperCol={{ span: 16 }}>
<Input maxLength="8" allowClear />
</Form.Item>
</Col>
<Col span={24}>
<Form.Item>
<Button type="primary" htmlType="submit">
搜索
</Button>
<Button htmlType="button" onClick={onReset} className={styles.left}>
重置
</Button>
</Form.Item>
</Col>
</Row>
</Form>
)}
<div className={styles.addBtn}>
<Button type="primary" className={styles.left} onClick={getBlacklist}>
设置员工黑名单
</Button>
<Button
type="primary"
className={styles.left}
onClick={() => {
setModalVisible(true);
}}
>
添加新员工
</Button>
<Button
type="primary"
className={styles.left}
onClick={() => {
setDepartmentVisible(true);
}}
>
创建部门
</Button>
<Button
type="primary"
className={styles.left}
onClick={() => {
setViewDepartmentVisible(true);
}}
>
查看部门
</Button>
</div>
</Card>
<Card>
<Table
columns={columns(res)}
dataSource={staffList}
rowKey={r => r.id}
bordered
rowSelection={rowSelection}
pagination={pagination}
loading={loading}
/>
</Card>
<NewEmployeeModal
visible={modalVisible}
enterpriseList={enterpriseList}
onClose={handleCloseModal}
getDepartmentList={getDepartmentList}
/>
<DepartmentModal
visible={departmentVisible}
enterpriseList={enterpriseList}
onClose={handleCloseModal}
/>
<ViewDepartmentModal
visible={viewDepartmentVisible}
enterpriseList={enterpriseList}
onClose={handleCloseModal}
/>
<BlacklistModal
list={selectedRowKeys}
visible={blacklistVisible}
onClose={handleCloseModal}
enterpriseId={searchForm.enterpriseId}
selectedRows={selectedRows}
/>
<RechargeDetailsModal
visible={rechargeDetailsVisible}
list={generateLog}
onClose={handleCloseModal}
/>
</PageHeaderWrapper>
);
};
export default StoreManagement;
.tip {
padding-top: 5px;
color: #ff8c00;
font-size: 12px;
}
.card {
margin-bottom: 16px;
}
.btn {
position: absolute;
right: 0;
bottom: 0;
display: flex;
}
.left {
margin-left: 20px;
}
.editShop {
margin-bottom: 10px;
}
.addBtn {
display: flex;
align-items: center;
justify-content: flex-end;
}
.employees {
text-align: center;
}
.blackList {
display: flex;
align-items: center;
color: #8e8e8e;
span {
&:nth-child(1) {
font-size: 12px;
}
&:nth-child(3) {
font-size: 10px;
}
}
}
import request from '@/utils/request';
import config from '../../../config/env.config';
import { stringify } from 'qs';
import _ from 'lodash';
const { goodsApi, apiPrefix, roleApi } = config;
// [企业员工]-列表查询
// http://yapi.quantgroups.com/project/389/interface/api/65359
export const apiStaffList = async params => {
params.page = params.current;
params.size = params.pageSize;
delete params.current;
delete params.pageSize;
const data = await request.post(`${apiPrefix}/enterprise/staff/pageList`, {
prefix: roleApi,
data: params,
});
return data;
};
// [企业客户]-列表查询
// http://yapi.quantgroups.com/project/389/interface/api/65324
export const apiEnterpriseList = async () => {
const data = await request.post(`${apiPrefix}/enterprise/pageList`, {
prefix: roleApi,
data: {
page: 1,
size: 10000,
},
});
return data;
};
// [企业部门]-列表查询
// http://yapi.quantgroups.com/project/389/interface/api/65344
export const apiDepartmentList = async params => {
const data = await request.post(`${apiPrefix}/enterprise/department/pageList`, {
prefix: roleApi,
data: params,
});
return data;
};
// [企业员工]-添加员工
// http://yapi.quantgroups.com/project/389/interface/api/65364
export const apiStaffSave = async params => {
const data = await request.post(`${apiPrefix}/enterprise/staff/save`, {
prefix: roleApi,
data: params,
});
return data;
};
// [企业员工]-导入员工
// http://yapi.quantgroups.com/project/389/interface/api/65384
export const apiStaffExcel = async file => {
console.log(file);
const params = new FormData();
params.append('file', file.file.file);
params.append('enterpriseId', file.enterpriseId);
const data = await request.post(`${apiPrefix}/enterprise/staff/excel`, {
prefix: roleApi,
data: params,
});
return data;
};
// [企业部门]-新增企业部门
// http://yapi.quantgroups.com/project/389/interface/api/65349
export const apiDepartmentSave = async params => {
const data = await request.post(`${apiPrefix}/enterprise/department/save`, {
prefix: roleApi,
data: params,
});
return data;
};
// [企业部门]-导入企业部门
// http://yapi.quantgroups.com/project/389/interface/api/65354
export const apiDepartmentExcel = async file => {
const params = new FormData();
params.append('file', file.file.file);
params.append('enterpriseId', file.enterpriseId);
const data = await request.post(`${apiPrefix}/enterprise/department/excel`, {
prefix: roleApi,
data: params,
});
return data;
};
// [企业员工]-删除员工
// http://yapi.quantgroups.com/project/389/interface/api/65374
export const apiStaffDelete = async params => {
const data = await request.post(`${apiPrefix}/enterprise/staff/delete`, {
prefix: roleApi,
data: params,
});
return data;
};
// [企业部门]-更新部门
// http://yapi.quantgroups.com/project/389/interface/api/65474
export const apiDepartmentUpdate = async params => {
const data = await request.post(`${apiPrefix}/enterprise/department/update`, {
prefix: roleApi,
data: params,
});
return data;
};
// [企业员工]-充值明细查询
// http://yapi.quantgroups.com/project/389/interface/api/65489
export const apiGenerateLogList = async params => {
const data = await request.get(`${apiPrefix}/enterprise/staff/generateLog/list`, {
prefix: roleApi,
params,
});
return data;
};
// [企业员工]-员工限额
// http://yapi.quantgroups.com/project/389/interface/api/65379
export const apiStaffLimit = async params => {
const data = await request.post(`${apiPrefix}/enterprise/staff/limit`, {
prefix: roleApi,
data: params,
});
return data;
};
// [企业员工]-拉黑员工
// http://yapi.quantgroups.com/project/389/interface/api/65369
export const apiStaffBlack = async params => {
const data = await request.post(`${apiPrefix}/enterprise/staff/black`, {
prefix: roleApi,
data: params,
});
return data;
};
......@@ -5,8 +5,6 @@ import React, { Component } from 'react';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import { connect } from 'dva';
import styles from './style.less';
import LocalStroage from '@/utils/localStorage';
import configApi from '../../../config/env.config';
import UpdateStock from './UpdateStock';
import {
......@@ -30,6 +28,8 @@ import DraftModal from './DraftModal';
import Takeaway from './Takeaway';
import { GOOD_MANAGE } from '@/../config/permission.config';
import LocalStroage from '@/utils/localStorage';
import configApi from '@/../config/env.config';
@connect(({ goodsManage, menu }) => ({
goodsManage,
......@@ -125,6 +125,15 @@ class goodsManage extends Component {
this.handleSearch(page);
};
audit = skuId => {
this.setState({
previewVisible: true,
src: `${configApi.prologueDomain}/goods/${skuId}?h=0&token=${LocalStroage.get(
'token',
)}&hideReport=1&time=${Date.now()}`,
});
};
onPageSizeChange = (current, size) => {
this.setState(
{
......@@ -160,15 +169,6 @@ class goodsManage extends Component {
});
};
audit = skuId => {
this.setState({
previewVisible: true,
src: `${configApi.prologueDomain}/goods/${skuId}?h=0&token=${LocalStroage.get(
'token',
)}&hideReport=1&time=${Date.now()}`,
});
};
setArea = async (isAll, type) => {
// distribution配送区域 after售后地址
if (!this.state.selectedRowKeys.length && !isAll) {
......
......@@ -17,12 +17,12 @@ const Model = {
const productCategoryId = payload?.productCategoryId || [];
params.productCategoryId =
(productCategoryId.length && productCategoryId[productCategoryId.length - 1]) || '';
const { data } = yield call(api.searchList, params);
if (!data) return;
const res = yield call(api.searchList, params);
if (res && !res.data) return;
yield put({
type: 'saveData',
payload: {
tableData: data,
tableData: res.data,
},
});
},
......
......@@ -30,9 +30,13 @@ class toExamine extends Component {
if (err) {
return;
}
if (!values?.fileList[0]?.url) {
notification.error({ message: '请上传发票错误,请重新上传' });
return;
}
const params = {
filePath: values?.fileList[0].url,
fileName: values?.fileList[0].name,
fileName: values?.fileList[0]?.name || '',
id,
};
const data = await uploadBill(params);
......
......@@ -62,6 +62,10 @@ class PicturesWall extends React.Component {
const vm = this;
// eslint-disable-next-line new-cap
const data = `${UUID.createUUID()}.${suffix}`;
if (!token) {
message.error('上传失败,请刷新页面重试!');
return;
}
const observable = qiniu.upload(file, data, token);
const observer = {
next() {
......@@ -121,7 +125,9 @@ class PicturesWall extends React.Component {
customRequest={this.customRequest}
listType="text"
fileList={fileList}
disabled={status !== 1}
onRemove={status === 1 ? this.clearFileList : ''}
accept=".pdf,.doc,.docx,.zip,.rar,.png,.jpeg"
>
{max && fileList.length >= max ? null : uploadButton}
</Upload>
......
......@@ -92,7 +92,7 @@
}
}
.pullImage {
position: absolute;
position: absolute !important;
top: 30px;
right: 40px;
}
......
......@@ -147,7 +147,7 @@ const FormAttr = forwardRef((props, ref) => {
if (typeof values[item] === 'string') {
obj.productAttributeApplyValueList = JSON.parse(values[item]);
} else {
obj.productAttributeApplyValueList = values[item].map(v => JSON.parse(v));
obj.productAttributeApplyValueList = values[item]?.map(v => JSON.parse(v)) || [];
}
return obj;
});
......
......@@ -989,7 +989,7 @@ const TakeawayGoodsInfo = forwardRef((props, ref) => {
singularSpu[0]?.specs[0]?.unit &&
singularSpu.map((item, index) => (
<div className={styles.specsSingularBetween}>
<Form.Item label={calcLabelName(item, 'singular')}>
<Form.Item label={calcLabelName(item)}>
<div className={styles.specsSingularBetween}>
<span className={styles.repertoryLimit}>
{item?.specs[0]?.productStock}/{item?.specs[0]?.maxStock}
......
......@@ -355,7 +355,7 @@ const ServiceGoods = options => {
setIsUseCache(true);
setVisibleCacheEdit(false);
onSetData(SourceData);
onValuesChange(SourceData, !0);
onValuesChange(SourceData);
// 外卖类型---
if (SourceData && SourceData?.type === 5) {
setTakeawayEditData(SourceData);
......
import React, { useState, useEffect, forwardRef, useRef, useImperativeHandle } from 'react';
import { Button, Modal, Select, Form, notification } from 'antd';
import styles from '../index.less';
import { apiSelectList, apiShopAdd } from '../service.js';
const AddModal = props => {
const [form] = Form.useForm();
const { addVisible, enterpriseId, name, onCancel } = props;
const [selectList, setSelectList] = useState([]);
const [options, setOptions] = useState([]);
const handleCancel = status => {
form.resetFields();
onCancel(status, 'add');
};
const handleChange = value => {
setSelectList(value);
};
const onOk = async () => {
if (!selectList.length) {
notification.error({
message: '请添加微店',
});
return;
}
const res = await apiShopAdd({ enterpriseId, shopIds: selectList });
if (res.businessCode === '0000') {
notification.success({
message: '添加成功',
});
handleCancel(true);
}
};
const getSelectList = async () => {
const res = await apiSelectList({ enterpriseId });
if (res.businessCode === '0000') {
const optionData = res.data.map(item => ({
value: item.shopId,
label: item.shopName,
}));
setOptions(optionData);
}
};
useEffect(() => {
if (addVisible) {
getSelectList();
}
}, [addVisible]);
return (
<>
<Modal title="添加企业店铺" onOk={onOk} visible={addVisible} onCancel={handleCancel}>
<Form form={form}>
<Form.Item label="企业名称">
<span>{name}</span>
</Form.Item>
<Form.Item label="添加微店" name="shopIds">
<Select
mode="multiple"
allowClear
style={{ width: '100%' }}
placeholder="请选择微店"
onChange={handleChange}
options={options}
filterOption={(input, option) =>
(option?.label ?? '').toLowerCase().includes(input.toLowerCase())
}
/>
</Form.Item>
<Form.Item label="餐品类型">
<span>到店</span>
</Form.Item>
</Form>
</Modal>
</>
);
};
export default AddModal;
import React from 'react';
import { Image as ImageComponent, Button } from 'antd';
import { FormOutlined } from '@ant-design/icons';
export const repastTypeList = [
{
value: 1,
label: '外卖',
},
{
value: 2,
label: '自助餐',
},
{
value: 4,
label: '到店',
},
];
const repastType = list => {
if (list.includes(4) && list.length > 1) {
return list
.filter(item => item !== 4)
.map(item => {
const name = repastTypeList.find(i => i.value === item)?.label;
return name ? `${name}/到店 ` : null;
});
}
return list.map(item => {
const name = repastTypeList.find(i => i.value === item)?.label;
return name ? `${name} ` : null;
});
};
export const columns = props => [
{
title: '微店ID',
key: 'shopId',
dataIndex: 'shopId',
align: 'center',
},
{
title: '微店名称',
key: 'shopName',
dataIndex: 'shopName',
align: 'center',
},
{
title: '餐品类型',
key: 'mealType',
dataIndex: 'mealType',
align: 'center',
render: (_, row) => {
const status = !row?.pickNameList?.length;
const isArray = Array.isArray(row?.mealType);
if (isArray) {
return (
<Button type="text" disabled={status} onClick={() => props.editRepastType(row)}>
{repastType(row?.mealType)}
<FormOutlined />
</Button>
);
}
return '/';
},
},
{
title: '所属企业取餐点',
key: 'pickNameList',
dataIndex: 'pickNameList',
align: 'center',
render: (text, record) => {
if (record.pickNameList?.length) {
return text.map((item, index) => (
<span key={item}>
{item}
{index < record.pickNameList.length - 1 && ','}
</span>
));
}
return '/';
},
},
{
title: '修改人',
key: 'updatedBy',
dataIndex: 'updatedBy',
align: 'center',
},
{
title: '修改时间',
key: 'updatedAt',
dataIndex: 'updatedAt',
align: 'center',
},
{
title: '操作',
dataIndex: 'option',
valueType: 'option',
align: 'center',
key: 'option',
render: (_, row) => (
<Button type="link" disabled={row?.pickNameList?.length} onClick={() => props.delShop(row)}>
删除
</Button>
),
},
];
import React, { useState, useEffect } from 'react';
import { Modal, Radio, Form, Space, message, Checkbox } from 'antd';
import styles from '../index.less';
import { apiShopUpdate } from '../service.js';
const EditRepastModal = props => {
const [form] = Form.useForm();
const { editVisible, repastType, id, enterpriseId, isStore, selectMealType } = props;
const handleCancel = status => {
form.resetFields();
props.onCancel(status, 'edit');
};
useEffect(() => {
if (selectMealType.length) {
let mealTypeList = selectMealType.filter(item => item !== 4);
mealTypeList = (mealTypeList.length && Number(mealTypeList.join(','))) || '';
const store = selectMealType.includes(4) ? [4] : [];
form.setFieldsValue({ mealTypeList, store });
}
}, [selectMealType]);
const onOk = () => {
form.validateFields().then(async values => {
const store = values?.store || '';
let mealTypeList = [];
if (values?.mealTypeList) {
mealTypeList = [values?.mealTypeList, ...store];
} else {
mealTypeList = [...store];
}
const params = {
id,
mealTypeList,
};
const res = await apiShopUpdate(params);
if (res.businessCode === '0000') {
message.success('修改成功');
handleCancel(true);
}
});
};
return (
<>
<Modal
title="餐品类型"
onOk={onOk}
visible={editVisible}
onCancel={() => handleCancel(false)}
>
<Form layout="vertical" form={form}>
<Form.Item
label="取餐点下商户餐品类型"
rules={[{ required: true, message: '请选择商户餐品类型' }]}
name="mealTypeList"
>
<Radio.Group>
<Space direction="vertical">
{(repastType.length &&
repastType.map(item => (
<Radio value={item.value} key={item.value}>
{item.label}
</Radio>
))) ||
''}
</Space>
<p className={styles.tip}>切换餐品类型后,请及时维护商品</p>
</Radio.Group>
</Form.Item>
{isStore && (
<Form.Item label="是否开启餐品类型" name="store">
<Checkbox.Group>
<Space direction="vertical">
<Checkbox value={4}>到店</Checkbox>
</Space>
<p className={styles.tip}>关闭【到店】餐类时,关联到店企业商品将一并删除</p>
</Checkbox.Group>
</Form.Item>
)}
</Form>
</Modal>
</>
);
};
export default EditRepastModal;
import React, { useState, useRef, forwardRef, useEffect } from 'react';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import { Modal, Form, Select, Table, Card, Row, Col, Input, Button, message } from 'antd';
import { set } from 'lodash';
import { da } from 'date-fns/locale';
import { columns, repastTypeList } from './data';
import EditRepastModal from './editRepastModal';
import AddModal from './addModal';
import styles from './index.less';
import { stringOrObjectTrim } from '@/utils/utils';
import {
setShopList,
setShopDelete,
mealTypeList,
apiEnterpriseList,
businessTypeCheck,
} from './service.js';
const { confirm } = Modal;
const StoreManagement = () => {
const [page, setPage] = useState({
current: 1,
pageSize: 10,
});
const [total, setTotal] = useState(0);
const [searchForm, setSearchForm] = useState({});
const formRef = useRef(null);
const [editVisible, setEditVisible] = useState(false);
const [addVisible, setAddVisible] = useState(false);
const [repastType, setRepastType] = useState([]);
const [repastId, setRepastId] = useState(null);
const [enterpriseList, setEnterpriseList] = useState();
const [firstEnterprise, setFirstEnterprise] = useState('');
const [enterprise, setEnterprise] = useState({});
const [dataList, setDataList] = useState([]);
const [name, setName] = useState('');
const [isStore, setIsStore] = useState(false);
const [loading, setLoading] = useState(false);
const [selectMealType, setSelectMealType] = useState([]);
const shopList = async params => {
// 去掉前后空格
const data = stringOrObjectTrim(params);
setLoading(true);
const res = await setShopList(data);
if (res?.businessCode === '0000') {
setLoading(false);
setDataList(res.data?.records);
setTotal(res.data?.total);
return;
}
setTimeout(() => {
setLoading(false);
setDataList([]);
setTotal(0);
}, 5000);
};
// 企业查询
const getEnterpriseList = async () => {
const res = await apiEnterpriseList();
if (res.businessCode === '0000' && res.data?.records?.length) {
const list = res.data.records;
const firstOption = list[0].id;
const optionData = list.map(item => ({
value: item.id,
label: item.name,
}));
setEnterprise({ value: list[0].id, label: list[0].name });
setFirstEnterprise(firstOption);
setEnterpriseList(optionData);
setSearchForm({ enterpriseId: firstOption });
shopList({ data: { enterpriseId: firstOption }, ...page });
}
};
useEffect(() => {
getEnterpriseList();
}, []);
// 关闭弹框
const closeModal = (status, val) => {
if (status) {
const pageInfo = {
current: 1,
pageSize: 10,
};
setPage(pageInfo);
shopList({ data: searchForm, ...pageInfo });
}
if (val === 'edit') {
setIsStore(false);
setEditVisible(false);
setRepastType([]);
setSelectMealType([]);
return;
}
setName(false);
setAddVisible(false);
};
const getMealTypeList = async id => {
const res = await mealTypeList({ id });
if (res.businessCode === '0000') {
const resData = res.data;
const data = [];
repastTypeList.forEach(item => {
if (resData.includes(item.value) && item.value !== 4) {
data.push(item);
}
});
if (resData.includes(4)) {
setIsStore(true);
}
setRepastType(data);
setEditVisible(true);
}
};
// 修改餐饮类型
const editRepastType = ({ id, mealType }) => {
setSelectMealType(mealType);
setRepastId(id);
getMealTypeList(id);
};
// 删除
const editShop = async id => {
const res = await setShopDelete({ id });
if (res.businessCode === '0000') {
message.success('删除成功!');
shopList({ ...page, data: searchForm });
}
};
// 删除
const delShop = ({ shopName, id }) => {
confirm({
title: `确认删除企业商户#${shopName || ''}#?`,
content: '注:删除微店后,其商户下企业商品一并删除',
onOk() {
editShop(id);
},
onCancel() {
console.log('Cancel');
},
});
};
const onChange = (value, option) => {
setFirstEnterprise(value);
const values = formRef.current.getFieldsValue();
const pageInfo = {
current: 1,
pageSize: 10,
};
setPage(pageInfo);
setSearchForm(values);
shopList({ ...pageInfo, data: values });
setEnterprise(option);
};
// 添加商户
const addShop = async () => {
const res = await businessTypeCheck({ enterpriseId: searchForm.enterpriseId });
if (res.businessCode === '0000') {
setName(enterprise.label);
setAddVisible(true);
}
};
// 搜索
const onFinish = async values => {
setSearchForm(values);
const pageInfo = {
current: 1,
pageSize: 10,
};
setPage(pageInfo);
shopList({ data: values, ...pageInfo });
};
// 重置
const onReset = () => {
formRef.current.resetFields();
const pageInfo = {
current: 1,
pageSize: 10,
};
setPage(pageInfo);
setSearchForm({ enterpriseId: firstEnterprise });
shopList({ data: { enterpriseId: firstEnterprise }, ...pageInfo });
};
// 分页
const handleTableChange = (current, pageSize) => {
const pageInfo = {
current,
pageSize,
};
setPage(pageInfo);
shopList({ data: searchForm, ...pageInfo });
};
const pagination = {
...page,
total,
showTotal: t => `共 ${t} 条`,
onChange: handleTableChange,
onShowSizeChange: handleTableChange,
};
const res = {
editRepastType,
delShop,
};
return (
<PageHeaderWrapper>
<Card className={styles.card}>
{firstEnterprise && (
<Form ref={formRef} onFinish={onFinish}>
<Row gutter={24}>
<Col span={8}>
<Form.Item
label="企业名称"
name="enterpriseId"
wrapperCol={{ span: 16 }}
rules={[{ required: true }]}
initialValue={firstEnterprise}
>
<Select
filterOption={(input, option) =>
(option?.label ?? '').toLowerCase().includes(input.toLowerCase())
}
onChange={onChange}
options={enterpriseList}
/>
</Form.Item>
</Col>
<Col span={8}>
<Form.Item label="微店名称" name="shopName" wrapperCol={{ span: 16 }}>
<Input maxLength="20" allowClear />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item label="微店ID" name="shopId" wrapperCol={{ span: 16 }}>
<Input maxLength="20" allowClear />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item label="餐品类型" name="mealType" wrapperCol={{ span: 16 }}>
<Select
allowClear
showSearch
filterOption={(input, option) =>
(option?.label ?? '').toLowerCase().includes(input.toLowerCase())
}
options={repastTypeList}
placeholder="全部"
/>
</Form.Item>
</Col>
<Col className={styles.btn}>
<Form.Item>
<Button type="primary" htmlType="submit">
搜索
</Button>
<Button htmlType="button" onClick={onReset} className={styles.left}>
重置
</Button>
<Button type="primary" onClick={addShop} className={styles.left}>
添加商户
</Button>
</Form.Item>
</Col>
</Row>
</Form>
)}
</Card>
<Card>
<Table
columns={columns(res)}
dataSource={dataList}
rowKey={r => r.id}
bordered
pagination={pagination}
loading={loading}
/>
</Card>
<EditRepastModal
editVisible={editVisible}
repastType={repastType}
id={repastId}
onCancel={closeModal}
enterpriseId={searchForm.enterpriseId}
isStore={isStore}
selectMealType={selectMealType}
/>
<AddModal
addVisible={addVisible}
enterpriseId={searchForm.enterpriseId}
name={name}
onCancel={closeModal}
/>
</PageHeaderWrapper>
);
};
export default StoreManagement;
.tip {
padding-top: 5px;
color: #ff8c00;
font-size: 12px;
}
.card {
margin-bottom: 16px;
}
.btn {
position: absolute;
right: 0;
bottom: 0;
display: flex;
}
.left {
margin-left: 20px;
}
.editShop {
margin-bottom: 10px;
}
import request from '@/utils/request';
import config from '../../../config/env.config';
import { stringify } from 'qs';
import _ from 'lodash';
import { de } from 'date-fns/locale';
const { goodsApi, apiPrefix, roleApi } = config;
console.log(apiPrefix, 'apiPrefix');
// [企业店铺]-列表查询
// http://yapi.quantgroups.com/project/389/interface/api/65284
export const setShopList = async params => {
params.page = params.current;
params.size = params.pageSize;
delete params.current;
delete params.pageSize;
const data = await request.post(`${apiPrefix}/enterprise/shop/list`, {
prefix: roleApi,
data: params,
});
return data;
};
// [企业店铺]-删除企业下面的店铺
// http://yapi.quantgroups.com/project/389/interface/api/65319
export const setShopDelete = async params => {
const data = await request.post(`${apiPrefix}/enterprise/shop/delete`, {
prefix: roleApi,
data: params,
});
return data;
};
// [企业店铺]-查询餐品类型
// http://yapi.quantgroups.com/project/389/interface/api/65314
export const mealTypeList = async params => {
const data = await request.post(`${apiPrefix}/enterprise/shop/mealType/list`, {
prefix: roleApi,
data: params,
});
return data;
};
// [企业店铺]-添加企业店铺
// http://yapi.quantgroups.com/project/389/interface/api/65304
export const shopAdd = async params => {
const data = await request.post(`${apiPrefix}/enterprise/shop/add`, {
prefix: roleApi,
data: params,
});
return data;
};
// 企业店铺]-修改餐品类型
// http://yapi.quantgroups.com/project/389/interface/api/65309
export const apiShopUpdate = async params => {
const data = await request.post(`${apiPrefix}/enterprise/shop/update`, {
prefix: roleApi,
data: params,
});
return data;
};
// [企业客户]-列表查询
// http://yapi.quantgroups.com/project/389/interface/api/65324
export const apiEnterpriseList = async () => {
const data = await request.post(`${apiPrefix}/enterprise/pageList`, {
prefix: roleApi,
data: {
page: 1,
size: 10000,
},
});
return data;
};
// [企业店铺]-可选择店铺列表
// http://yapi.quantgroups.com/project/389/interface/api/65524
export const apiSelectList = async params => {
const data = await request.post(`${apiPrefix}/enterprise/shop/select/list`, {
prefix: roleApi,
data: params,
});
return data;
};
// [企业店铺]-添加企业店铺校验是否是到店类型
// http://yapi.quantgroups.com/project/389/interface/api/65304
export const businessTypeCheck = async params => {
const data = await request.post(`${apiPrefix}/enterprise/shop/busineesType/check`, {
prefix: roleApi,
data: params,
});
return data;
};
// [企业店铺]-添加企业店铺
// http://yapi.quantgroups.com/project/389/interface/api/65304
export const apiShopAdd = async params => {
const data = await request.post(`${apiPrefix}/enterprise/shop/add`, {
prefix: roleApi,
data: params,
});
return data;
};
import React, { useEffect, useState } from 'react';
import {
Button,
Divider,
Form,
Input,
InputNumber,
Checkbox,
Radio,
Col,
Row,
Modal,
Select,
message,
notification,
} from 'antd';
import { ExclamationCircleOutlined } from '@ant-design/icons';
import { jsonToArray } from '@/utils/utils';
import moment from 'moment';
import { layout, mealType, boolOptions, hideOptions, mealSections } from '../staticData/index';
import {
apiEnterpriseInfo,
apiNewEnterprise,
apiEditEnterprise,
apiEnterprisePickSelf,
} from '../service';
import MealSection from './MealSection';
import MealLimit from './MealLimit';
const CheckboxGroup = Checkbox.Group;
const RadioGroup = Radio.Group;
const { confirm } = Modal;
const CustomerInfo = props => {
const [form] = Form.useForm();
const [meals, setMeals] = useState({});
const [mealTypes, setMealTypes] = useState([]);
const [pickSelfList, setPickSelfList] = useState([]);
// 关闭分组信息弹窗
const handleCancel = () => {
props.reFresh();
props.handleClose(false);
};
// 校验时间
const checkTime = (arr, curren, curName) => {
let valid = false;
arr.forEach(item => {
if (curren < item.endTime) {
valid = true;
const name = meals[item.mealPeriodType];
notification.error({ message: `${curName}起始时间不能早于${name}截止时间` });
}
});
return valid;
};
// 提交
const handleConfirm = async () => {
const res = await form.validateFields();
console.log('res :>> ', res);
// const res = await form.getFieldsValue();
if (res.mealTimePeriod.length < 1) {
message.warn('请选择餐段');
return;
}
const params = Object.assign(
{
hideImage: 0,
hidePrice: 0,
},
res,
);
const arr = [];
let validTime = false;
res.mealTimePeriod.forEach(item => {
if (item && meals[item.mealPeriodType]) {
const obj = Object.assign({}, item);
obj.beginTime = moment(obj.time[0]).format('HH:mm');
obj.endTime = moment(obj.time[1]).format('HH:mm');
delete obj.time;
if (checkTime(arr, obj.beginTime, meals[item.mealPeriodType])) {
validTime = true;
}
arr.push(obj);
}
});
if (validTime) {
// notification.error({ message: '时间段不能交叉!' });
return;
}
params.mealTimePeriod = arr;
if (res.hideInfo && res.hideInfo.length) {
params.hidePrice = res.hideInfo.includes('hidePrice') ? 1 : 0;
params.hideImage = res.hideInfo.includes('hideImage') ? 1 : 0;
delete params.hideInfo;
}
const limits = [];
if (!res.mealLimit) {
notification.error({ message: '请输入限额!' });
return;
}
// 处理限额
Object.keys(res.mealLimit).forEach(item => {
const mealPeriodType = item.replace('limit', '');
if (meals[mealPeriodType]) {
const json = {
mealPeriodType,
limit: [],
};
Object.keys(res.mealLimit[item]).forEach(t => {
if (mealTypes.includes(t)) {
json.limit.push({
mealType: t,
limit: res.mealLimit[item][t],
});
}
});
limits.push(json);
}
});
if (limits.length < 1) {
notification.error({ message: '请选择餐段配置!' });
return;
}
params.mealLimit = limits;
console.log('params :>> ', params);
let api = apiNewEnterprise;
if (props.id) {
params.id = props.id;
api = apiEditEnterprise;
}
const resp = await api(params);
if (resp && resp.data) {
handleCancel();
props.reFresh();
notification.success({ message: '保存成功!' });
}
};
// 风险提示
const checkConfirm = () => {
const mt = '';
return new Promise((resolve, reject) => {
confirm({
title: '风险提示',
icon: <ExclamationCircleOutlined />,
content: `确定关闭${mt}餐品类型?此餐品类型下关联的商户及商品将一并删除,不可逆请谨慎操作!`,
onOk() {
resolve(1);
},
onCancel() {
reject(new Error());
},
});
});
};
// 改变餐品类型 (选自助餐必选外卖)
const onChangeMealType = async ms => {
try {
if (props.id && ms.length < mealTypes.length) {
await checkConfirm();
}
// 取消外卖,必须取消自助餐
if (mealTypes.includes('1') && !ms.includes('1')) {
ms = ms.filter(item => item !== '2');
}
// 选择自助餐,必须选择外卖
if (!mealTypes.includes('1') && ms.includes('2')) {
ms.push('1');
}
form.setFieldsValue({
mealType: ms,
});
setMealTypes(ms);
} catch {
form.setFieldsValue({
mealType: mealTypes,
});
}
};
// 改变餐段
const onChangeMealSection = e => {
const { id, checked, label } = e.target;
const values = Object.assign({}, meals);
if (checked) {
values[id] = label;
} else {
delete values[id];
}
setMeals(values);
};
// 获取企业客户信息
const getInfo = async () => {
const res = await apiEnterpriseInfo(props.id);
if (res && res.data) {
const obj = Object.assign({}, res.data);
if (res.data.mealTimePeriod && res.data.mealTimePeriod.length) {
const m = moment().format('YYYY-MM-DD');
const arr = Object.keys(mealSections);
obj.mealTimePeriod = Object.keys(mealSections).map(() => ({}));
res.data.mealTimePeriod.forEach((item, i) => {
if (item) {
const index = arr.indexOf(`${item.mealPeriodType}`);
if (index > -1) {
obj.mealTimePeriod[index] = {
mealPeriodType: `${item.mealPeriodType}`,
time: [moment(`${m} ${item.beginTime}`), moment(`${m} ${item.endTime}`)],
};
}
}
});
} else {
obj.mealTimePeriod = [];
}
obj.mealLimit = {};
if (res.data.mealLimit && res.data.mealLimit.length) {
res.data.mealLimit.forEach(item => {
obj.mealLimit[`limit${item.mealPeriodType}`] = {};
item.limit.forEach(limit => {
obj.mealLimit[`limit${item.mealPeriodType}`][limit.mealType] = limit.limit;
});
});
}
obj.hideInfo = [];
if (+res.data.hidePrice) {
obj.hideInfo.push('hidePrice');
}
if (+res.data.hideImage) {
obj.hideInfo.push('hideImage');
}
if (res.data.mealType) {
obj.mealType = res.data.mealType.map(item => `${item}`);
} else {
obj.mealType = [];
}
setMealTypes(obj.mealType);
const json = {};
if (res.data.mealTimePeriod) {
res.data.mealTimePeriod.forEach(item => {
json[item.mealPeriodType] = mealSections[item.mealPeriodType];
});
}
setMeals(json);
form.setFieldsValue(obj);
}
};
// 获取自提点列表
const getPickSelf = async () => {
const res = await apiEnterprisePickSelf({});
if (res && res.data && res.data.records) {
const data = res.data.records;
setPickSelfList(
data.map(item => ({
value: item.id,
label: item.pickselfName,
})),
);
}
};
useEffect(() => {
if (props.visible) {
if (props.id) {
getInfo();
} else {
setMealTypes([]);
setMeals({});
form.resetFields();
getPickSelf();
}
} else {
setMealTypes([]);
setMeals({});
form.setFieldsValue({});
}
}, [props.visible]);
return (
<Modal
title="企业客户信息"
open={props.visible}
destroyOnClose
maskClosable={false}
width="900px"
onOk={handleConfirm}
onCancel={handleCancel}
>
<Form
name="basicInfo"
initialValues={{ mealTimePeriod: [{}, {}, {}] }}
{...layout}
form={form}
>
<Form.Item
label="公司名称"
name="name"
rules={[{ required: true, message: '请输入公司名称!' }]}
>
<Input />
</Form.Item>
{!props.id && (
<Form.Item
label="企业取餐点"
name="pickselfIds"
rules={[{ required: true, type: 'array', message: '请选择企业取餐点!' }]}
>
<Select
options={pickSelfList}
mode="multiple"
showSearch
filterOption={(v, option) =>
(option?.label ?? '').toLowerCase().includes(v.toLowerCase())
}
/>
</Form.Item>
)}
<Form.Item
label="企业截单时间"
name="endOrderTime"
wrapperCol={{ span: 20 }}
rules={[{ required: true, message: '请输入企业截单时间!' }]}
extra={<span>企业员工下单的截至时间,仅支持正整数,单位为分钟。</span>}
>
<InputNumber min={0} max={600} addonAfter="分钟" />
</Form.Item>
<Form.Item
label="餐品类型"
name="mealType"
rules={[{ required: true, message: '请选择餐品类型!' }]}
>
<CheckboxGroup options={jsonToArray(mealType)} onChange={onChangeMealType} />
</Form.Item>
<Form.Item label="餐段配置" required wrapperCol={{ span: 12 }}>
<MealSection meals={meals} onChange={onChangeMealSection} />
</Form.Item>
<Divider orientation="left" plain>
企业单笔消费限额
</Divider>
<Form.List name="mealLimit">
{fields => (
<>
{Object.keys(meals).map(meal => (
<Form.Item label={`${mealSections[meal]}订单`} required wrapperCol={{ span: 20 }}>
<Form.List
name={`limit${meal}`}
key={`${meal}limit`}
required
wrapperCol={{ span: 20 }}
>
{fs => (
<Row key={`row${meal}`}>
{mealTypes.map((t, i) => (
<Col span={7} offset={i ? 1 : 0} key={t}>
<MealLimit value={t} label={mealType[t]} name={`${t}`} />
</Col>
))}
</Row>
)}
</Form.List>
</Form.Item>
))}
</>
)}
</Form.List>
<Form.Item label="商品隐藏信息" name="hideInfo">
<CheckboxGroup options={hideOptions} />
</Form.Item>
<Form.Item
label="是否周预览"
name="weekPreview"
rules={[{ required: true, message: '请选择是否周预览!' }]}
>
<RadioGroup options={boolOptions} />
</Form.Item>
</Form>
</Modal>
);
};
export default CustomerInfo;
import React from 'react';
import { Checkbox } from 'antd';
import { mealSections } from '../staticData/index';
const MealCheckbox = props => {
const onChange = e => {
props.onChange(props.field);
props.changeType(e);
};
return (
<Checkbox
onChange={onChange}
checked={props.meals[props.field]}
id={props.field}
label={mealSections[props.field]}
>
{mealSections[props.field]}
</Checkbox>
);
};
export default MealCheckbox;
import React from 'react';
import { Form, InputNumber, Row, Col } from 'antd';
import style from '../style/info.less';
import { validateRequired, isCheckPriceTwoDecimal } from '@/utils/validator';
const MealLimit = props => (
<Form.Item
label={`${props.label}限额`}
name={props.name}
rules={[
{ validator: validateRequired, message: `请输入${props.label}限额` },
{ validator: isCheckPriceTwoDecimal, message: '请输入正确的价格' },
]}
>
<InputNumber addonAfter="元" max={999.99} />
</Form.Item>
);
export default MealLimit;
import React from 'react';
import { Form, Space, TimePicker } from 'antd';
import { mealSections } from '../staticData/index';
import MealCheckbox from './MealCheckbox';
const MealSection = props => (
<Form.List name="mealTimePeriod">
{fields => (
<>
{Object.keys(mealSections).map((field, i) => (
<Space key={field} align="baseline">
<Form.Item label="" name={[i, 'mealPeriodType']}>
<MealCheckbox changeType={props.onChange} meals={props.meals} field={field} />
</Form.Item>
<Form.Item
name={[i, 'time']}
rules={
props.meals[field] ? [{ type: 'array', required: true, message: '请选择!' }] : []
}
>
<TimePicker.RangePicker format="HH:mm" minuteStep={30} />
</Form.Item>
</Space>
))}
</>
)}
</Form.List>
);
export default MealSection;
import React, { useState, useRef } from 'react';
import ProTable from '@ant-design/pro-table';
import { Button } from 'antd';
import { PlusOutlined } from '@ant-design/icons';
import { customerColumn } from './staticData/index';
import CustomerInfo from './components/CustomerInfo';
import utilStyle from '@/utils/utils.less';
import { stringOrObjectTrim } from '@/utils/utils';
import { apiEnterpriseList } from './service';
const BusinessCustomer = () => {
const refTable = useRef();
const [visible, setVisible] = useState(false);
const [id, setId] = useState('');
const query = async params => {
const data = {
page: params.current,
size: params.pageSize,
data: stringOrObjectTrim(params),
};
const res = await apiEnterpriseList(data);
return {
data: res.data.records,
total: res.data.total,
};
};
const onEdit = v => {
setId(v);
setVisible(true);
};
return (
<div className={utilStyle.formPageBox}>
<ProTable
actionRef={refTable}
search={{
collapsed: false,
collapseRender: () => null,
}}
columns={customerColumn({ onEdit })}
request={params => query({ ...params })}
rowKey={r => r.id}
expandIconColumnIndex={10}
bordered
options={false}
toolBarRender={() => [
<Button
key="3"
icon={<PlusOutlined />}
type="primary"
onClick={() => {
setId('');
setVisible(!0);
}}
>
添加企业客户
</Button>,
]}
/>
<CustomerInfo
visible={visible}
id={id}
reFresh={() => refTable.current.reload()}
handleClose={setVisible}
/>
</div>
);
};
export default BusinessCustomer;
import request from '@/utils/request';
import config from '@/../config/env.config';
// import qs from 'qs';
const { roleApi, apiPrefix } = config;
/**
* 获取企业客户列表
* http://yapi.quantgroups.com/project/389/interface/api/65324
*/
export async function apiEnterpriseList(data) {
return request.post(`${apiPrefix}/enterprise/pageList`, {
data,
prefix: roleApi,
});
}
/**
* 获取企业客户详细
* http://yapi.quantgroups.com/project/389/interface/api/65339
*/
export async function apiEnterpriseInfo(id) {
return request.get(`${apiPrefix}/enterprise/info?id=${id}`, {
prefix: roleApi,
});
}
/**
* 添加企业客户
* http://yapi.quantgroups.com/project/389/interface/api/65329
*/
export async function apiNewEnterprise(data) {
return request.post(`${apiPrefix}/enterprise/save`, {
data,
prefix: roleApi,
});
}
/**
* 编辑企业客户
* http://yapi.quantgroups.com/project/389/interface/api/65334
*/
export async function apiEditEnterprise(data) {
return request.post(`${apiPrefix}/enterprise/update`, {
data,
prefix: roleApi,
});
}
/**
* 查询自提点列表
* http://yapi.quantgroups.com/project/389/interface/api/65494
*/
export async function apiEnterprisePickSelf() {
return request.get(`${apiPrefix}/selfPickUpLocation/list`, {
prefix: roleApi,
});
}
import React from 'react';
import { Button } from 'antd';
export const layout = {
labelCol: { span: 4 },
wrapperCol: { span: 18 },
};
// 餐品类型:(1外卖 2 自助餐 4到店)
export const mealType = {
1: '外卖',
2: '自助餐',
4: '到店',
};
export const infoOptions = [
{ label: '商品价格及图片', value: 1 },
{ label: '仅商品价格', value: 2 },
{ label: '仅商品图片', value: 3 },
{ label: '均不展示', value: 4 },
];
export const boolOptions = [{ label: '', value: 1 }, { label: '', value: 0 }];
export const hideOptions = [
{ label: '隐藏商品价格', value: 'hidePrice' },
{ label: '隐藏商品图片', value: 'hideImage' },
];
export const mealSections = {
1: '早餐',
2: '午餐',
4: '晚餐',
};
// 企业列表字段
export const customerColumn = options => {
const { onEdit } = options;
return [
{
title: 'ID',
dataIndex: 'enterpriseId',
hideInTable: true,
},
{
title: 'ID',
dataIndex: 'id',
width: 120,
align: 'center',
hideInSearch: true,
},
{
title: '公司名称',
dataIndex: 'name',
width: 120,
align: 'center',
},
{
title: '截单时间(分钟)',
dataIndex: 'endOrderTime',
width: 120,
align: 'center',
hideInSearch: true,
},
{
title: '餐品类型',
dataIndex: 'mealType',
width: 120,
align: 'center',
hideInSearch: true,
render(types) {
if (types && types.length && typeof types === 'object') {
const arr = types.map(meal => mealType[meal]);
return arr.join('/');
}
return '-';
},
},
{
title: '创建人',
dataIndex: 'createdBy',
width: 120,
align: 'center',
hideInSearch: true,
},
{
title: '创建时间',
dataIndex: 'createdAt',
width: 120,
align: 'center',
hideInSearch: true,
},
{
title: '操作',
hideInSearch: true,
dataIndex: 'action',
width: 200,
align: 'center',
fixed: 'right',
render: (val, r) => (
<Button key="edit" onClick={() => onEdit(r.id)}>
编辑
</Button>
),
},
];
};
.tip {
height: 32px;
padding-left: 5px;
line-height: 32px;
}
import React, { useEffect } from 'react';
import { Form, InputNumber, Modal, notification } from 'antd';
import { isCheckPriceTwoDecimal } from '@/utils/validator';
import { apiMealInfoUpdate } from '../service';
const SaleDateModal = props => {
const [form] = Form.useForm();
const layout = {
labelCol: { span: 8 },
wrapperCol: { span: 14 },
};
// 关闭弹窗
const handleCancel = () => {
props.handleClose(false);
};
// 提交
const handleConfirm = async () => {
const res = await form.validateFields();
if (props.id) {
const params = {
id: props.id,
enterprisePrice: res.price,
};
await apiMealInfoUpdate(params);
notification.success({ message: '保存成功' });
}
handleCancel();
props.handleRefresh(res.price);
};
useEffect(() => {
if (props.visible) {
const price = props.item?.enterprisePrice || props.item?.activityPrice || null;
form.setFieldsValue({ price });
}
}, [props.visible]);
return (
<Modal
title="修改企业商品价格"
open={props.visible}
destroyOnClose
maskClosable={false}
width="400px"
okText="保存"
onOk={handleConfirm}
onCancel={handleCancel}
>
<Form name="basicInfo" {...layout} form={form}>
<Form.Item
label="企业商品价格"
name="price"
rules={[
{ required: true, message: '请输入企业商品价格!' },
{ validator: isCheckPriceTwoDecimal, message: '请输入正确的价格' },
]}
>
<InputNumber addonAfter="元" max={99999.99} />
</Form.Item>
</Form>
</Modal>
);
};
export default SaleDateModal;
import React, { useEffect } from 'react';
import { Form, InputNumber, Modal, notification } from 'antd';
import { apiMealInfoUpdate } from '../service';
const SaleDateModal = props => {
const [form] = Form.useForm();
const layout = {
labelCol: { span: 8 },
wrapperCol: { span: 14 },
};
// 关闭弹窗
const handleCancel = () => {
props.handleClose(false);
};
// 提交
const handleConfirm = async () => {
const res = await form.validateFields();
const params = {
id: props.id,
sort: res.sort,
};
await apiMealInfoUpdate(params);
notification.success({ message: '保存成功' });
handleCancel();
props.handleRefresh();
};
useEffect(() => {
if (props.visible) {
const sort = props.item?.sort || 1000;
form.setFieldsValue({ sort });
}
}, [props.visible]);
return (
<Modal
title="修改商品排序"
open={props.visible}
destroyOnClose
maskClosable={false}
width="300px"
okText="保存"
onOk={handleConfirm}
onCancel={handleCancel}
>
<Form name="basicInfo" {...layout} form={form}>
<Form.Item label="排序" name="sort" rules={[{ required: true, message: '请输入排序!' }]}>
<InputNumber max={999999} min={0} />
</Form.Item>
</Form>
</Modal>
);
};
export default SaleDateModal;
import React, { useState, useEffect } from 'react';
import { Checkbox, Space, message, Modal, notification, Button } from 'antd';
import { weekOptions } from '../staticData/goods';
import { apiMealInfoUpdate, apiCheckInfo } from '../service';
import style from '../style/index.less';
const SaleDateModal = props => {
const [value, setValue] = useState([]);
const [loading, setLoading] = useState(false);
const [checkAll, setCheckAll] = useState(false);
// 关闭弹窗
const handleCancel = () => {
props.handleClose(false);
};
const onChangeWeek = e => {
setValue(e);
setCheckAll(e?.length === 7);
};
// 提交
const handleConfirm = async () => {
if (!value || value.length < 1) {
message.error('请选择可售日期');
return;
}
if (props.productType === 4) {
const data = [];
props.dataSource.forEach(item => {
const obj = {
saleDateList: item.saleDate,
tabCateList: item.tabCate?.map(v => ({ tabId: v })),
};
if (props.item.skuId === item.skuId && item.tabCate) {
obj.saleDateList = value;
data.push(obj);
} else if (item.saleDate?.length && item.tabCate?.length) {
data.push(obj);
}
});
setLoading(true);
const res = await apiCheckInfo(data);
setLoading(false);
if (!res || !res.success) {
return;
}
}
if (props.id) {
const params = {
id: props.id,
saleDateList: value,
};
setLoading(true);
await apiMealInfoUpdate(params);
setLoading(false);
notification.success({ message: '保存成功' });
}
handleCancel();
props.handleRefresh(value);
};
useEffect(() => {
if (props.visible) {
let v = [];
let dateList = [];
if (props.item) {
if (props.type) {
dateList = props.item[props.type];
}
dateList = dateList || props.item.saleDateList || props.item.saleDate;
}
if (dateList && dateList.length) {
v = dateList.map(item => {
if (typeof item === 'object') {
return `${item.code}`;
}
return `${item}`;
});
}
setCheckAll(v.length === 7);
setValue(v);
}
}, [props.visible]);
// 全选事件
const onCheckAll = e => {
if (e.target.checked) {
setValue(Object.keys(weekOptions).map(w => `${w}`));
} else {
setValue([]);
}
setCheckAll(e.target.checked);
};
// 弹窗底部
const footerComponent = [
<div key="footer" className={style.modalFooters}>
<Checkbox checked={checkAll} onChange={onCheckAll}>
全选
</Checkbox>
<div>
<Button onClick={handleCancel}> 取消 </Button>
<Button type="primary" loading={loading} onClick={handleConfirm}>
{' '}
保存{' '}
</Button>
</div>
</div>,
];
return (
<Modal
title={props.title || '设置可售日期'}
open={props.visible}
destroyOnClose
maskClosable={false}
width="300px"
footer={footerComponent}
>
<Checkbox.Group onChange={onChangeWeek} value={value}>
<Space direction="vertical">
{Object.keys(weekOptions).map(key => (
<Checkbox key={key} value={key}>
{weekOptions[key]}
</Checkbox>
))}
</Space>
</Checkbox.Group>
</Modal>
);
};
export default SaleDateModal;
import React, { useState, useEffect } from 'react';
import { Checkbox, Space, Modal, notification, message } from 'antd';
import { mealColumn } from '../staticData/goods';
import { apiMealInfoUpdate, apiEnterpriseInfo, apiCheckInfo } from '../service';
const SaleDateModal = props => {
const [value, setValue] = useState([]);
const [loading, setLoading] = useState(false);
const [tabCateList, setTabCateList] = useState(Object.keys(mealColumn));
// 关闭弹窗
const handleCancel = () => {
props.handleClose(false);
};
const onChangeMeal = e => {
setValue(e);
};
// 提交
const handleConfirm = async () => {
if (!value || value.length < 1) {
message.error('请选择餐段');
return;
}
const arr = value.sort((x, y) => x - y);
if (props.productType === 4) {
const data = [];
props.dataSource.forEach(item => {
const obj = {
saleDateList: item.saleDate,
tabCateList: item.tabCate?.map(v => ({ tabId: v })),
};
if (props.item.skuId === item.skuId && item.saleDate) {
obj.tabCateList = arr.map(v => ({ tabId: v }));
data.push(obj);
} else if (item.saleDate?.length && item.tabCate?.length) {
data.push(obj);
}
});
const res = await apiCheckInfo(data);
if (!res || !res.success) {
return;
}
}
if (props.id) {
const params = {
id: props.id,
tabIds: arr,
};
await apiMealInfoUpdate(params);
notification.success({ message: '保存成功' });
}
handleCancel();
props.handleRefresh(arr);
};
// 获取店铺餐段通过企业ID
const getEnterpriseMealColumn = async valueList => {
setLoading(true);
const res = await apiEnterpriseInfo(props.enterpriseID);
setLoading(false);
if (res && res.data && res.data.mealTimePeriod && res.data.mealTimePeriod.length) {
const arr = res.data.mealTimePeriod.map(item => `${item.mealPeriodType}`);
setTabCateList(arr);
// 餐段的值需在企业配置的可选餐段之内
const v = [];
valueList.forEach(t => {
arr.includes(t) && v.push(t);
});
setValue(v);
}
};
useEffect(() => {
if (props.visible) {
let v = [];
let tabCate = [];
if (props.item) {
if (props.type) {
tabCate = props.item[props.type];
}
tabCate = tabCate || props.item.tabCateList || props.item.tabCate;
}
if (tabCate && tabCate.length) {
v = tabCate.map(item => {
if (typeof item === 'object') {
return `${item.tabId}`;
}
return `${item}`;
});
}
setValue(v);
if (props.enterpriseID) {
getEnterpriseMealColumn(v);
}
}
}, [props.visible]);
return (
<Modal
title={props.title || '设置可售餐段'}
open={props.visible}
destroyOnClose
maskClosable={false}
width="200px"
okText="保存"
confirmLoading={loading}
onOk={handleConfirm}
onCancel={handleCancel}
>
<Checkbox.Group onChange={onChangeMeal} value={value}>
<Space direction="vertical">
{tabCateList.map(key => (
<Checkbox key={key} value={key}>
{mealColumn[key]}
</Checkbox>
))}
</Space>
</Checkbox.Group>
</Modal>
);
};
export default SaleDateModal;
import React, { useState, useEffect } from 'react';
import { Select, Modal, Table, Input, Button, Pagination, notification } from 'antd';
import { SearchOutlined } from '@ant-design/icons';
import { apiSelGoodsList, apiSelVirtualGoodsList, apiShopListByPickSelfID } from '../service';
import { SelectGoodsColumn, productType, weekOptions } from '../staticData/goods';
import style from '../style/index.less';
import { jsonToArray, deepClone } from '@/utils/utils';
const { Option } = Select;
const SaleDateModal = props => {
const [searchType, setSearchType] = useState('1');
const [searchKeyword, setSearchKeyword] = useState('');
const [shopId, setShopId] = useState(props.shopID || null);
const [shopName, setShopName] = useState('');
const [searchName, setSearchName] = useState('');
const [loading, setLoading] = useState(false);
const [page, setPage] = useState(1);
const [pageSize, setPageSize] = useState(10);
const [total, setTotal] = useState(0);
const [dataSource, setDataSource] = useState([]);
const [shopList, setShopList] = useState([]);
const [selectedRowKeys, setSelectedRowKeys] = useState(props.selectedRowKeys || []);
const [selectedRows, setSelectedRows] = useState(props.selectedRows || []);
const searchList = async params => {
const { enterpriseId } = props;
const searchObj = {};
if (searchKeyword && searchType && !params.search) {
if (searchType === '1') {
searchObj.name = searchKeyword;
} else {
searchObj.skuId = searchKeyword;
}
}
const data = {
page: params.current || page,
size: params.pageSize || pageSize,
data: Object.assign(
{
shopId,
productType: props.productType,
enterpriseId,
},
searchObj,
params,
),
};
setLoading(true);
let api = apiSelGoodsList;
if (props.type === 'virtual') {
api = apiSelVirtualGoodsList;
}
const res = await api(data);
const arr =
res?.data?.records?.map(item => {
item.enterprisePrice = item.salePrice;
return item;
}) || [];
setDataSource(arr);
setTotal(res?.data?.total || 0);
setLoading(false);
};
// 点击搜索
const onSearch = () => {
setSearchKeyword(searchName);
setPage(1);
searchList({ [searchType === '1' ? 'name' : 'skuId']: searchName, current: 1 });
};
// 切换店铺
const onChangeShop = v => {
setShopId(v);
setPage(1);
searchList({
shopId: v,
current: 1,
});
setSelectedRowKeys([]);
setSelectedRows([]);
};
// 关闭弹窗
const handleCancel = () => {
props.handleClose(false);
};
// 提交
const handleConfirm = async () => {
if (!selectedRows || selectedRows.length < 1) {
notification.error({ message: '请选择要添加的商品' });
return;
}
let sName = '';
if (shopList && shopList.length) {
shopList.forEach(item => {
if (+item.value === +shopId) {
sName = item.label;
}
});
} else {
sName = shopName;
}
const arr = deepClone(selectedRows).map(item => ({
...item,
shopId,
saleDate:
!item.saleDate || item.saleDate.length < 1 ? Object.keys(weekOptions) : item.saleDate,
shopName: sName,
}));
props.onSelectChange(arr);
if (!props.shopID) {
props.onChangeShop(shopId);
}
if (props.onSelectedRowKeys) {
props.onSelectedRowKeys(selectedRowKeys);
}
handleCancel();
};
// 商品单选
const onSelectChange = (record, selected) => {
const { skuId } = record;
if (selected) {
const keys = [...selectedRowKeys, skuId];
const arr = [...selectedRows, record];
setSelectedRowKeys(keys);
setSelectedRows(arr);
} else {
const rows = [];
const keys = [];
selectedRows.forEach(item => {
if (item.skuId !== skuId) {
rows.push(item);
keys.push(item.skuId);
}
});
setSelectedRowKeys(keys);
setSelectedRows(rows);
}
};
// 商品全选
const onSelectAllChange = (selected, rows) => {
const keys = [...selectedRowKeys];
const arr = [...selectedRows];
if (selected) {
rows.forEach(item => {
if (item && !keys.includes(item.skuId)) {
keys.push(item.skuId);
arr.push(item);
}
});
setSelectedRowKeys(keys);
setSelectedRows(arr);
} else {
dataSource.forEach(item => {
const index = keys.findIndex(k => k === item.skuId);
if (index > -1) {
keys.splice(index, 1);
arr.splice(index, 1);
}
});
setSelectedRowKeys(keys);
setSelectedRows(arr);
}
};
// 切换页码
const onPageChange = (current, size) => {
setPage(current);
setPageSize(size);
searchList({
current,
pageSize: size,
});
};
// 获取店铺列表
const getShopList = async () => {
const res = await apiShopListByPickSelfID({
enterpriseId: props.enterpriseId,
pickSelfIdList: props.pickSelfIdList,
});
if (res && res.data) {
setShopList(
res.data.map(item => ({
label: item.name,
value: item.id,
})),
);
}
};
const rowSelection = {
selectedRowKeys,
onSelect: onSelectChange,
onSelectAll: onSelectAllChange,
getCheckboxProps: record => ({
disabled: !!record.selected,
}),
};
const onChangeSearchType = v => {
setSearchType(v);
setSearchName('');
};
const selectBefore = (
<Select defaultValue="1" onChange={onChangeSearchType}>
<Option value="1" key={1}>
名称
</Option>
<Option value="2" key={2}>
SKU
</Option>
</Select>
);
const selectAfter = <SearchOutlined onClick={onSearch} />;
const footers = () => [
<div className={style.footers} key="footer">
<Pagination defaultCurrent={1} total={total} showQuickJumper onChange={onPageChange} />
<div className={style['footers-btn']}>
<div className={style['footers-desc']}>
已选商品(<span className={style['footers-num']}>{selectedRowKeys.length}</span>)
</div>
<Button key="back" onClick={handleCancel}>
取消
</Button>
<Button key="submit" type="primary" loading={loading} onClick={handleConfirm}>
确定
</Button>
</div>
</div>,
];
useEffect(() => {
if (+props.productType === 5) {
setShopId(null);
getShopList();
} else {
setShopId(props.shopID);
setShopName(props.shopName);
searchList({});
}
}, []);
return (
<Modal
title="选择商品"
open={props.visible}
destroyOnClose
maskClosable={false}
width="1000px"
onOk={handleConfirm}
onCancel={handleCancel}
footer={footers()}
>
<div className={style['select-goods-box']}>
{props.productType === 5 ? (
<Select
placeholder="请选择店铺"
options={shopList}
value={shopId}
onChange={onChangeShop}
className={style['select-goods-box--select']}
/>
) : (
<Input disabled value={shopName} className={style['select-goods-box--txt']} />
)}
<Select
placeholder="请选择商品类型"
disabled
value={`${props.productType}`}
options={jsonToArray(productType)}
className={style['select-goods-box--select']}
/>
<Input
addonBefore={selectBefore}
addonAfter={selectAfter}
value={searchName}
allowClear
type={+searchType === 2 ? 'number' : 'text'}
onChange={e => setSearchName(e.target.value)}
onPressEnter={onSearch}
className={style['select-goods-box--txt']}
/>
</div>
<Table
rowSelection={rowSelection}
rowKey="skuId"
columns={SelectGoodsColumn}
pagination={false}
dataSource={dataSource}
/>
</Modal>
);
};
export default SaleDateModal;
import { apiEnterpriseList, apiShopList, apiShopListByPickSelfID } from './index';
// 获取企业列表
export const getEnterpriseList = async (param = {}) => {
const res = await apiEnterpriseList({
page: 1,
size: 10000,
data: param,
});
if (res?.data?.records?.length) {
const data = res.data.records;
const arr = data.map(item => ({
label: item.name,
value: item.id,
key: item.id,
}));
return {
id: data[0].id,
list: arr,
};
}
return {
id: '',
list: [],
};
};
// 获取店铺列表
export const getShopList = async e => {
if (e.keyWords) {
const res = await apiShopList({ name: e.keyWords });
if (res && res.data) {
const { data } = res;
return data.map(item => ({
value: item.id,
label: item.name,
}));
}
}
return [];
};
// 获取店铺列表通过自提点ID
export const getShopListByPickSelf = async e => {
const { name } = e;
const res = await apiShopListByPickSelfID({ name });
if (res && res.data && res.data.records) {
const data = res.data.records;
const json = {};
data.forEach(item => {
json[item.id] = { text: item.name };
});
return {
id: res[res.length - 1].id,
list: json,
};
}
return {
id: '',
list: {},
};
};
// 获取店铺名称
export const getEnterpriseName = (arr, id) => {
let name = '';
if (arr && arr.length) {
arr.forEach(item => {
if (+item.value === +id) {
name = item.label;
}
});
}
return name;
};
import request from '@/utils/request';
import config from '@/../config/env.config';
const { roleApi, apiPrefix } = config;
/**
* 获取企业外卖商品列表
* http://yapi.quantgroups.com/project/389/interface/api/64794
*/
export async function apiTakeawayList(param) {
const res = await request.post(`${apiPrefix}/product/enterprise/main/pageList`, {
data: param,
prefix: roleApi,
});
return res.data;
}
/**
* 获取企业虚拟商品列表
* http://yapi.quantgroups.com/project/389/interface/api/64794
*/
export async function apiVirtualList(param) {
return request.post(`${apiPrefix}/product/enterprise/virtual/pageList`, {
data: param,
prefix: roleApi,
});
}
/**
* 企业客户列表
* http://yapi.quantgroups.com/project/389/interface/api/65324
*/
export async function apiEnterpriseList(param) {
return request.post(`${apiPrefix}/enterprise/pageList`, {
data: param,
prefix: roleApi,
});
}
/**
* 模糊查询店铺列表
* http://yapi.quantgroups.com/project/389/interface/api/65289
*/
export async function apiShopList(param) {
return request.post(`${apiPrefix}/shops/getListByName`, {
data: param,
prefix: roleApi,
});
}
/**
* 企业团餐-查询餐段配置
* http://yapi.quantgroups.com/project/389/interface/api/65444
*/
export async function apiMealTimePeriod(param) {
return request.post(`${apiPrefix}/product/enterprise/getMealTimePeriod`, {
data: param,
prefix: roleApi,
});
}
/**
* 企业团餐->信息修改
* http://yapi.quantgroups.com/project/389/interface/api/65099
*/
export async function apiMealInfoUpdate(param) {
return request.post(`${apiPrefix}/product/enterprise/main/update`, {
data: param,
prefix: roleApi,
});
}
/**
* 企业团餐->删除外卖商品
* http://yapi.quantgroups.com/project/389/interface/api/65109
*/
export async function apiTakeawayGoodsDel(param) {
return request.post(`${apiPrefix}/product/enterprise/main/deleteById`, {
data: param,
prefix: roleApi,
});
}
/**
* 企业团餐->删除虚拟商品
* http://yapi.quantgroups.com/project/389/interface/api/65109
*/
export async function apiVirtualGoodsDel(param) {
return request.post(`${apiPrefix}/product/enterprise/virtual/deleteById`, {
data: param,
prefix: roleApi,
});
}
/**
* 企业团餐->查询自提点列表
* http://yapi.quantgroups.com/project/389/interface/api/65494
*/
export async function apiPickSelfList() {
return request.get(`${apiPrefix}/selfPickUpLocation/list`, {
prefix: roleApi,
});
}
/**
* 企业团餐->根据企业ID查询已选择自提点
* http://yapi.quantgroups.com/project/389/interface/api/65449
*/
export async function apiSelPickSelfList(param) {
return request.post(`${apiPrefix}/product/enterprise/queryByEnterpriseId`, {
data: param,
prefix: roleApi,
});
}
/**
* 企业团餐->查询外卖商品 - 选择商品弹窗
* http://yapi.quantgroups.com/project/389/interface/api/65479
*/
export async function apiSelGoodsList(param) {
return request.post(`${apiPrefix}/product/enterprise/sku/page`, {
data: param,
prefix: roleApi,
});
}
/**
* 企业团餐->查询商品 - 选择虚拟商品弹窗
* http://yapi.quantgroups.com/project/389/interface/api/65479
*/
export async function apiSelVirtualGoodsList(param) {
return request.post(`${apiPrefix}/product/enterprise/virtual/sku/page`, {
data: param,
prefix: roleApi,
});
}
/**
* 企业团餐->添加外卖商品保存
* http://yapi.quantgroups.com/project/389/interface/api/65094
*/
export async function apiSaveGoodsList(param) {
return request.post(`${apiPrefix}/product/enterprise/add`, {
data: param,
prefix: roleApi,
});
}
/**
* 企业团餐->添加虚拟商品保存
* http://yapi.quantgroups.com/project/389/interface/api/65484
*/
export async function apiSaveVirtualGoodsList(param) {
return request.post(`${apiPrefix}/product/enterprise/virtual/add`, {
data: param,
prefix: roleApi,
});
}
/**
* 企业团餐->虚拟品->根据企业ID查询店铺列表
* http://yapi.quantgroups.com/project/389/interface/api/65504
*/
export async function apiShopListByEnterpriseID(param) {
return request.post(`${apiPrefix}/product/enterprise/virtual/shops`, {
data: param,
prefix: roleApi,
});
}
/**
* 企业团餐->根据企业ID+自提点列表查询店铺列表
* http://yapi.quantgroups.com/project/389/interface/api/65539
*/
export async function apiShopListByPickSelfID(param) {
return request.post(`${apiPrefix}/product/enterprise/queryByEnterpriseIdAndPickSelfId`, {
data: param,
prefix: roleApi,
});
}
/**
* 企业团餐->根据企业ID 获取企业客户详细
* http://yapi.quantgroups.com/project/389/interface/api/65339
*/
export async function apiEnterpriseInfo(id) {
return request.get(`${apiPrefix}/enterprise/info?id=${id}`, {
prefix: roleApi,
});
}
/**
* 企业团餐->虚拟商品 校验是否可修改餐段和可售日期
* http://yapi.quantgroups.com/project/389/interface/api/65674
*/
export async function apiCheckInfo(data) {
return request.post(`${apiPrefix}/product/enterprise/virtual/addParamCheck`, {
data,
prefix: roleApi,
});
}
import React from 'react';
import { Button, Space, Switch } from 'antd';
import { FormOutlined } from '@ant-design/icons';
import { getShopList } from '../service/bll';
import style from '../style/index.less';
export const layout = {
labelCol: { span: 4 },
wrapperCol: { span: 18 },
};
// 餐段 0-推荐 1-早餐 2-午餐 3-下午茶 4-晚餐
export const mealColumn = {
// 0: '推荐',
1: '早餐',
2: '午餐',
// 3: '晚餐',
4: '晚餐',
};
// 商品类型
export const productType = {
1: '实物商品',
2: '虚拟商品',
// 3: '电子卡券'(废弃)
4: '服务类商品',
5: '外卖商品',
};
// 餐品类型:(1外卖 2 自助餐 4到店)
export const mealType = {
1: '外卖',
2: '自助餐',
4: '到店',
};
export const weekOptions = {
1: '周一',
2: '周二',
3: '周三',
4: '周四',
5: '周五',
6: '周六',
7: '周日',
};
// 外卖商品列表字段
export const takeawayGoodsColumn = options => {
const {
onDel,
onChangeFlag,
setVisibleSaleDate,
enterprises,
enterpriseId,
setRecordID,
setEditItem,
setVisiblePrice,
setVisibleSaleSection,
setVisibleSort,
onChangeEnterprise,
} = options;
return [
{
title: '企业名称',
dataIndex: 'enterpriseId',
hideInTable: true,
valueType: 'select',
fieldProps: {
value: enterpriseId ? +enterpriseId : null,
showSearch: true,
filterOption: (v, option) => (option?.label ?? '').toLowerCase().includes(v.toLowerCase()),
onChange: onChangeEnterprise,
options: enterprises,
allowClear: false,
},
},
{
title: '微店名称',
dataIndex: 'shopId',
hideInTable: true,
fieldProps: {
showSearch: true,
filterOption: false,
// onSearch: getShopList,
},
request: getShopList,
valueEnum: {},
},
{
title: '微店名称',
dataIndex: 'shopName',
hideInSearch: true,
align: 'center',
},
{
title: 'SKU编码',
dataIndex: 'skuId',
width: 170,
align: 'center',
},
{
title: '商品名称',
dataIndex: 'skuName',
align: 'center',
},
{
title: '商品图片',
dataIndex: 'primaryImage',
width: 80,
align: 'center',
hideInSearch: true,
valueType: 'image',
},
{
title: '可售日期',
dataIndex: 'saleDateList',
align: 'center',
hideInSearch: true,
render: (_, record) => (
<Space>
{_ && _.length ? (
<div>
{_.length === 7 ? '不限制' : <span>{_.map(item => item.name).join('/')}</span>}
</div>
) : (
<span>-</span>
)}
<span
className={style.columnBtnEdit}
onClick={() => {
setRecordID(record.id);
setEditItem(record);
setVisibleSaleDate(true);
}}
>
<FormOutlined />
</span>
</Space>
),
},
{
title: '可售餐段',
dataIndex: 'tabCateList',
align: 'center',
hideInSearch: true,
render: (_, record) => (
<Space>
{_ && _.length ? (
<span>
{_.sort((x, y) => x.tabId - y.tabId)
.map(item => item.tabName)
.join('/')}
</span>
) : (
<span>-</span>
)}
<span
className={style.columnBtnEdit}
onClick={() => {
setRecordID(record.id);
setEditItem(record);
setVisibleSaleSection(true);
}}
>
<FormOutlined />
</span>
</Space>
),
},
{
title: '企业价格',
dataIndex: 'activityPrice',
width: 120,
align: 'center',
hideInSearch: true,
render: (_, record) => (
<Space>
<span>{_}</span>
<span
className={style.columnBtnEdit}
onClick={() => {
setRecordID(record.id);
setEditItem(record);
setVisiblePrice(true);
}}
>
<FormOutlined />
</span>
</Space>
),
},
{
title: '商品销售价',
dataIndex: 'price',
width: 100,
align: 'center',
hideInSearch: true,
},
{
title: '状态',
dataIndex: 'status',
width: 80,
align: 'center',
hideInSearch: true,
valueEnum: {
1: '已上架',
2: '已下架',
3: '已售罄',
},
},
{
title: '排序',
dataIndex: 'sort',
width: 90,
align: 'center',
hideInSearch: true,
render: (_, record) => (
<Space>
<span>{_}</span>
<span
className={style.columnBtnEdit}
onClick={() => {
setRecordID(record.id);
setEditItem(record);
setVisibleSort(true);
}}
>
<FormOutlined />
</span>
</Space>
),
},
{
title: '列出商品',
dataIndex: 'showFlag',
align: 'center',
width: 100,
hideInSearch: true,
render: (_, record) => (
<Space>
<span>
<Switch checked={+_ === 1} onChange={e => onChangeFlag(record.id, e)} />
</span>
</Space>
),
},
{
title: '餐品类型',
dataIndex: 'mealTypeList',
align: 'center',
hideInSearch: true,
render(arr) {
if (arr && arr.length) {
return arr.map(item => item.name).join('/');
}
return '-';
},
},
{
title: '添加时间',
dataIndex: 'createDate',
valueType: 'dateRange',
align: 'center',
render(v, record) {
return record.createDate;
},
},
{
title: '可售餐段',
dataIndex: 'tabId',
align: 'center',
valueEnum: mealColumn,
hideInTable: true,
},
{
title: '操作',
hideInSearch: true,
dataIndex: 'action',
width: '100px',
align: 'center',
fixed: 'right',
render: (val, r) => (
<Button key="del" onClick={() => onDel(r.id)}>
删除
</Button>
),
},
];
};
// 添加商品列表字段
export const GoodsInfoColumn = options => {
const {
onDel,
setVisibleSaleDate,
setVisibleSaleSection,
setEditItem,
setVisiblePrice,
} = options;
return [
{
title: '微店名称',
dataIndex: 'shopName',
},
{
title: '商品名称',
dataIndex: 'skuName',
align: 'center',
},
{
title: '商品售价',
dataIndex: 'salePrice',
width: 120,
align: 'center',
},
{
title: '企业价格',
dataIndex: 'enterprisePrice',
width: 120,
align: 'center',
render: (_, record) => (
<Space>
<span key="1">{_}</span>
<span
key="2"
className={style.columnBtnEdit}
onClick={() => {
setEditItem(record);
setVisiblePrice(true);
}}
>
<FormOutlined />
</span>
</Space>
),
},
{
title: '库存',
dataIndex: 'stock',
width: 120,
align: 'center',
render(v, record) {
const arr = record.supplierProductItemList;
if (arr && arr.length) {
return arr[0].stock;
}
if (![null, undefined].includes(v)) {
return v;
}
return '-';
},
},
{
title: '可售日期',
dataIndex: 'saleDate',
align: 'center',
hideInSearch: true,
render: (_, record) => (
<Space>
{_ && _.length ? (
<div key="1">
{_.length === 7 ? (
'不限制'
) : (
<span key="3">{_.map(item => weekOptions[item]).join('/')}</span>
)}
</div>
) : (
<span key="2">-</span>
)}
<span
key="4"
className={style.columnBtnEdit}
onClick={() => {
setEditItem(record);
setVisibleSaleDate(true);
}}
>
<FormOutlined />
</span>
</Space>
),
},
{
title: '可售餐段',
dataIndex: 'tabCate',
align: 'center',
hideInSearch: true,
render: (_, record) => (
<Space>
{_ && _.length ? (
<span key="1">
{_.sort((x, y) => x - y)
.map(item => mealColumn[item])
.join('/')}
</span>
) : (
<span key="2" className={style.columnTip}>
请配置
</span>
)}
<span
key="3"
className={style.columnBtnEdit}
onClick={() => {
setEditItem(record);
setVisibleSaleSection(true);
}}
>
<FormOutlined />
</span>
</Space>
),
},
{
title: '餐品类型',
dataIndex: 'mealTypeList',
width: 120,
align: 'center',
hideInSearch: true,
render(arr) {
if (arr && arr.length) {
return arr.map(item => item.name).join('/');
}
return '-';
},
},
{
title: '操作',
hideInSearch: true,
dataIndex: 'action',
width: '100px',
align: 'center',
fixed: 'right',
render: (val, r, i) => (
<Button key="del" onClick={() => onDel(i)}>
删除
</Button>
),
},
];
};
// 选择商品弹窗字段
export const SelectGoodsColumn = [
{
title: '商品ID',
width: 120,
align: 'center',
dataIndex: 'skuId',
},
{
title: '商品名称',
dataIndex: 'skuName',
},
// {
// title: '商品编号',
// dataIndex: 'skuNo',
// width: 120,
// align: 'center',
// },
{
title: '销售价',
dataIndex: 'salePrice',
width: 120,
align: 'center',
},
{
title: '剩余库存',
dataIndex: 'stock',
width: 100,
align: 'center',
render(v, record) {
const arr = record.supplierProductItemList;
if (arr && arr.length) {
return arr[0].stock;
}
return '-';
},
},
// {
// title: '已参与活动',
// dataIndex: 'activity',
// align: 'center',
// width: 110,
// hideInSearch: true,
// },
];
// 企业虚拟商品列表字段
export const virtualGoodsColumn = options => {
const { onDel, enterprises, enterpriseId, onChangeEnterprise } = options;
return [
{
title: '企业名称',
dataIndex: 'enterpriseId',
hideInTable: true,
valueType: 'select',
fieldProps: {
value: enterpriseId ? +enterpriseId : null,
showSearch: true,
filterOption: (v, option) => (option?.label ?? '').toLowerCase().includes(v.toLowerCase()),
onChange: onChangeEnterprise,
options: enterprises,
allowClear: false,
},
},
{
title: '微店名称',
dataIndex: 'shopId',
hideInTable: true,
fieldProps: {
showSearch: true,
filterOption: false,
// onSearch: getShopList,
},
request: getShopList,
valueEnum: {}, // shopEnum,
},
{
title: '微店名称',
dataIndex: 'shopName',
hideInSearch: true,
align: 'center',
},
{
title: 'SKU编码',
dataIndex: 'skuId',
width: 160,
align: 'center',
},
{
title: '商品名称',
dataIndex: 'skuName',
align: 'center',
},
{
title: '商品图片',
dataIndex: 'primaryImage',
width: 80,
align: 'center',
hideInSearch: true,
valueType: 'image',
},
{
title: '可售日期',
dataIndex: 'saleDateList',
width: 120,
align: 'center',
hideInSearch: true,
render: (_, record) => (
<Space>
{_ && _.length && (
<div>
{_.length === 7 ? '不限制' : <span>{_.map(item => item.name).join('/')}</span>}
</div>
)}
</Space>
),
},
{
title: '可售餐段',
dataIndex: 'tabCateList',
width: 120,
align: 'center',
hideInSearch: true,
render: (_, record) => (
<Space>
{_ && _.length && (
<span>
{_.sort((x, y) => x.tabId - y.tabId)
.map(item => item.tabName)
.join('/')}
</span>
)}
</Space>
),
},
{
title: '企业价格',
dataIndex: 'activityPrice',
width: 100,
align: 'center',
hideInSearch: true,
render: (_, record) => (
<Space>
<span>{_}</span>
</Space>
),
},
{
title: '商品销售价',
dataIndex: 'price',
width: 100,
align: 'center',
hideInSearch: true,
},
{
title: '状态',
dataIndex: 'status',
width: 80,
align: 'center',
hideInSearch: true,
valueEnum: {
1: '已上架',
2: '已下架',
3: '已售罄',
},
},
{
title: '餐品类型',
dataIndex: 'mealTypeList',
width: 120,
align: 'center',
hideInSearch: true,
render(arr) {
if (arr && arr.length) {
return arr.map(item => item.name).join('/');
}
return '-';
},
},
{
title: '添加时间',
dataIndex: 'createDate',
width: 120,
valueType: 'dateRange',
align: 'center',
render(v, record) {
return record.createDate;
},
},
{
title: '可售餐段',
dataIndex: 'tabId',
align: 'center',
valueEnum: mealColumn,
hideInTable: true,
},
{
title: '操作',
hideInSearch: true,
dataIndex: 'action',
width: '100px',
align: 'center',
fixed: 'right',
render: (val, r) => (
<Button key="del" onClick={() => onDel(r.id)}>
删除
</Button>
),
},
];
};
.search {
padding: 0;
:global {
.ant-form-item {
display: block !important;
}
}
}
.info-box {
min-height: 100%;
padding: 20px 40px;
background-color: #fff;
&--line {
min-height: 32px;
margin-bottom: 15px;
line-height: 32px;
}
&--label {
text-align: right;
}
&--select {
width: 100%;
min-width: 200px;
}
&--btns {
margin-top: 20px;
&__confirm {
margin-right: 15px;
}
}
&--batch-btn {
text-align: right;
}
}
.select-goods-box {
display: flex;
padding: 5px 0;
.select-goods-box--select {
width: 200px;
}
.select-goods-box--txt {
width: 300px;
:global {
.ant-input::-webkit-inner-spin-button,
.ant-input::-webkit-outer-spin-button {
margin: 0;
-webkit-appearance: none;
}
.ant-input {
-webkit-appearance: textfield;
}
}
}
}
.footers {
display: flex;
align-items: center;
justify-content: space-between;
&-btn {
display: flex;
align-items: center;
justify-content: flex-end;
text-align: right;
}
&-desc {
margin-right: 10px;
}
&-num {
color: #1890ff;
}
}
.columnTip {
color: #999;
}
.columnBtnEdit {
cursor: pointer;
}
.modalFooters {
display: flex;
align-items: center;
justify-content: space-between;
}
import React, { useState, useEffect, useRef } from 'react';
import { useHistory } from 'react-router-dom';
import { ProTable } from '@ant-design/pro-components';
import { Button, notification } from 'antd';
import { PlusOutlined } from '@ant-design/icons';
import { takeawayGoodsColumn } from './staticData/goods';
import utilStyle from '@/utils/utils.less';
import {
apiTakeawayList,
apiSelPickSelfList,
apiTakeawayGoodsDel,
apiMealInfoUpdate,
} from './service';
import { getEnterpriseList, getEnterpriseName } from './service/bll';
import SaleDateModal from './components/SaleDateModal';
import SaleSectionModal from './components/SaleSectionModal';
import GoodPriceModal from './components/GoodPriceModal';
import GoodSortModal from './components/GoodSortModal';
import { getToUrlQuery } from '@/utils/utils';
// 企业外卖商品
const TakeawayGoods = () => {
const history = useHistory();
const refTable = useRef();
const [pageLoaded, setPageLoaded] = useState(false); // 可售日期弹窗
const [visibleSaleDate, setVisibleSaleDate] = useState(false); // 可售日期弹窗
const [visibleSaleSection, setVisibleSaleSection] = useState(false); // 可售餐段弹窗
const [visiblePrice, setVisiblePrice] = useState(false); // 修改企业商品价格弹窗
const [visibleSort, setVisibleSort] = useState(false); // 商品排序弹窗
const [enterprises, setEnterprises] = useState(null); // 企业列表
const [activeKey, setActiveKey] = useState(''); // 自提点ID
const [enterpriseId, setEnterpriseId] = useState(''); // 企业ID
const [pickSelfList, setPickSelfList] = useState([]); // 取餐点列表
const [recordID, setRecordID] = useState(''); // 编辑的记录ID
const [editItem, setEditItem] = useState({}); // 编辑的记录信息
// 刷新列表
const onRefresh = isReset => {
if (!pageLoaded) {
return;
}
if (isReset) {
refTable?.current?.reloadAndRest();
} else {
refTable?.current?.reload();
}
};
// 搜索商品列表
const searchList = async params => {
if (params.createDate && params.createDate.length) {
const [startDateTime, endDateTime] = params.createDate;
params.startDateTime = startDateTime;
params.endDateTime = endDateTime;
}
const data = {
page: params.current || 1,
size: params.pageSize || 10,
data: Object.assign({}, params, {
enterpriseId,
pickSelfId: activeKey,
}),
};
const res = await apiTakeawayList(data);
if (res && res.records) {
return {
data: res.records || [],
total: res.total || 0,
};
}
return {
data: [],
total: 0,
};
};
// 删除商品
const onDel = async id => {
await apiTakeawayGoodsDel({
id,
});
refTable.current.reload();
notification.success({
message: '删除成功',
});
};
// 跳转添加商品
const toAdd = async () => {
const params = getToUrlQuery();
const query = Object.assign(
{
id: enterpriseId,
name: getEnterpriseName(enterprises, enterpriseId),
},
params,
);
history.push({
pathname: '/takeawayGoodsInfo',
query,
});
};
// 修改商品是否列出
const onChangeFlag = async (id, checked) => {
const params = {
id,
showFlag: checked ? 1 : 0,
};
await apiMealInfoUpdate(params);
notification.success({ message: '保存成功' });
onRefresh();
};
// 根据企业ID获取取餐点
const getPickSelf = async id => {
const res = await apiSelPickSelfList({ enterpriseId: id });
const arr =
res?.data?.map(item => ({
key: `${item.pickSelfId}`,
label: <span>{item.pickSelfName}</span>,
})) || [];
setPickSelfList(arr);
const [obj] = res?.data || [];
const pid = `${obj?.pickSelfId || ''}`;
setActiveKey(`${pid}`);
if (pageLoaded) {
onRefresh(true);
}
};
// 改变企业
const onChangeEnterprise = v => {
setEnterpriseId(v);
getPickSelf(v);
};
// 获取企业列表
const getList = async () => {
const obj = await getEnterpriseList({
type: 1,
});
if (obj.list && Object.keys(obj.list).length) {
setEnterprises(obj.list);
setEnterpriseId(`${obj.id}`);
await getPickSelf(obj.id);
} else if (!enterpriseId) {
notification.error({ message: '未找到企业' });
}
setPageLoaded(true);
};
useEffect(() => {
getList();
}, []);
const options = {
setVisibleSaleDate,
setVisibleSaleSection,
setVisiblePrice,
setVisibleSort,
onDel,
setRecordID,
setEditItem,
enterpriseId,
enterprises,
onChangeEnterprise,
onChangeFlag,
onRefresh,
};
let request = () => ({
data: [],
total: 0,
});
if (enterpriseId) {
request = params => searchList({ ...params });
}
return (
<div className={utilStyle.formPageBox}>
{pageLoaded && (
<ProTable
search={{
span: 6,
className: utilStyle.formSearch,
collapsed: false,
collapseRender: () => null,
}}
actionRef={refTable}
tableClassName={utilStyle.formTable}
columns={takeawayGoodsColumn(options)}
request={request}
rowKey={r => r.id}
bordered
options={false}
form={{ initialValues: { enterpriseId } }}
scroll={{ x: 1300 }}
toolbar={{
menu: {
type: 'tab',
activeKey,
items: pickSelfList,
onChange: key => {
setActiveKey(`${key}`);
onRefresh(true);
},
},
actions: [
<Button key="1" icon={<PlusOutlined />} type="primary" onClick={toAdd}>
添加外卖商品
</Button>,
],
}}
/>
)}
{/* 可售日期弹窗 */}
{visibleSaleDate && (
<SaleDateModal
visible={visibleSaleDate}
id={recordID}
item={editItem}
type="saleDateList"
handleRefresh={() => onRefresh()}
handleClose={() => setVisibleSaleDate(false)}
/>
)}
{/* 可售餐段弹窗 */}
{visibleSaleSection && (
<SaleSectionModal
visible={visibleSaleSection}
id={recordID}
enterpriseID={enterpriseId}
type="tabCateList"
item={editItem}
handleRefresh={() => onRefresh()}
handleClose={() => setVisibleSaleSection(false)}
/>
)}
{/* 修改企业商品价格弹窗 */}
{visiblePrice && (
<GoodPriceModal
visible={visiblePrice}
id={recordID}
item={editItem}
handleRefresh={() => onRefresh()}
handleClose={() => setVisiblePrice(false)}
/>
)}
{/* 商品排序弹窗 */}
{visibleSort && (
<GoodSortModal
visible={visibleSort}
id={recordID}
item={editItem}
handleRefresh={() => onRefresh()}
handleClose={() => setVisibleSort(false)}
/>
)}
</div>
);
};
export default TakeawayGoods;
import React, { useState, useEffect } from 'react';
import { Button, notification, Spin, Select, Row, Col, Table, message } from 'antd';
import { useHistory } from 'react-router-dom';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import { GoodsInfoColumn } from './staticData/goods';
import SaleDateModal from './components/SaleDateModal';
import GoodPriceModal from './components/GoodPriceModal';
import SaleSectionModal from './components/SaleSectionModal';
import SelectGoodsModal from './components/SelectGoodsModal';
import { apiSelPickSelfList, apiPickSelfList, apiSaveGoodsList } from './service';
import style from './style/index.less';
const TakeawayGoodsInfo = props => {
const { id, name } = props.location.query;
const history = useHistory();
const [loading, setLoading] = useState(false);
const [visibleSaleDate, setVisibleSaleDate] = useState(false); // 可售日期弹窗
const [visibleSaleSection, setVisibleSaleSection] = useState(false); // 可售餐段弹窗
const [visibleSelectGoods, setVisibleSelectGoods] = useState(false); // 选择商品弹窗
const [visiblePrice, setVisiblePrice] = useState(false); // 修改企业商品价格弹窗
const [dataSource, setDataSource] = useState([]);
const [pickSelfList, setPickSelfList] = useState([]); // 取餐点列表
const [slePickSelf, setSelPickSelf] = useState([]); // 选中的取餐点列表
const [editItem, setEditItem] = useState({}); // 编辑信息
const [shopId, setShopId] = useState(''); // 店铺ID
// 取消
const onCancel = () => {
history.goBack();
};
// 提交
const onSubmit = async () => {
if (!dataSource || dataSource.length < 1) {
message.error('请添加商品');
return;
}
const skuInfoList = [];
try {
dataSource.forEach(item => {
if (!item.mealTypeList || item.mealTypeList.length < 1) {
throw Error('请添加餐品类型!');
}
if (!item.enterprisePrice || `${item.enterprisePrice}`.length < 1) {
throw Error('请添加企业价格!');
}
if (!item.saleDate || item.saleDate.length < 1) {
throw Error('请添加可售日期!');
}
if (!item.tabCate || item.tabCate.length < 1) {
throw Error('请添加可售餐段!');
}
const json = {
skuId: item.skuId,
enterprisePrice: item.enterprisePrice,
mealTypeList: item.mealTypeList.map(m => m.code),
saleDateList: item.saleDate,
tabCateList: item.tabCate.map(t => ({ tabId: t })),
};
skuInfoList.push(json);
});
} catch (e) {
message.error(e.message);
return;
}
const params = {
shopId,
enterpriseId: id,
pickSelfIdList: slePickSelf,
skuInfoList,
};
console.log('params :>> ', params);
const res = await apiSaveGoodsList(params);
if (res && res.success) {
notification.success({ message: '添加成功' });
onCancel();
}
};
// 删除
const onDel = i => {
const arr = [...dataSource];
arr.splice(i, 1);
setDataSource(arr);
};
// 根据企业ID获取取餐点
const getPickSelf = async () => {
const res = await apiPickSelfList();
if (res && res.data && res.data.records) {
setPickSelfList(
res.data.records.map(item => ({
value: item.id,
label: item.pickselfName,
})),
);
}
};
// 根据企业ID获取已选取餐点
const getSelPickSelf = async () => {
const res = await apiSelPickSelfList({
enterpriseId: id,
});
if (res && res.data) {
setSelPickSelf(res.data.map(item => item.pickSelfId));
}
};
// 刷新列表数据
const refreshList = (type, v) => {
const arr = dataSource.map(item => {
if (editItem.skuId) {
if (`${item.skuId}` === `${editItem.skuId}`) {
item[type] = v;
}
} else {
item[type] = v;
}
return item;
});
setDataSource(arr);
};
// 显示选择商品弹窗
const onShowSelect = () => {
if (!slePickSelf || slePickSelf.length < 1) {
message.error('请选择取餐点!');
return;
}
setVisibleSelectGoods(true);
};
useEffect(() => {
getPickSelf();
getSelPickSelf();
}, []);
const options = {
setVisibleSaleDate,
setVisibleSaleSection,
setEditItem,
onDel,
setVisiblePrice,
};
return (
<PageHeaderWrapper title="添加企业严选商品">
<div className={style['info-box']}>
<Spin spinning={loading}>
<Row className={style['info-box--line']}>
<Col span={3} className={style['info-box--label']}>
企业名称:
</Col>
<Col span={20}>{name}</Col>
</Row>
<Row className={style['info-box--line']}>
<Col span={3} className={style['info-box--label']}>
选择取餐点:
</Col>
<Col span={20}>
<Select
mode="multiple"
showSearch
options={pickSelfList}
className={style['info-box--select']}
placeholder="清选择"
value={slePickSelf}
onChange={setSelPickSelf}
filterOption={(input, option) =>
(option?.label ?? '').toLowerCase().includes(input.toLowerCase())
}
/>
</Col>
</Row>
<Row className={style['info-box--line']}>
<Col span={3} className={style['info-box--label']}>
添加商品:
</Col>
<Col span={20}>
<Button type="primary" onClick={() => onShowSelect()}>
选择商品
</Button>
</Col>
</Row>
<Row className={style['info-box--line']}>
<Col span={24}>
{dataSource && dataSource.length ? (
<div className={style['info-box--batch-btn']}>
<Button
onClick={() => {
setEditItem({});
setVisibleSaleDate(true);
}}
>
批量配置日期
</Button>
<Button
onClick={() => {
setEditItem({});
setVisibleSaleSection(true);
}}
>
批量配置餐段
</Button>
</div>
) : (
''
)}
<Table
columns={GoodsInfoColumn(options)}
pagination={false}
dataSource={dataSource}
/>
</Col>
</Row>
<Row className={style['info-box--btns']}>
<Col span={4} />
<Col span={20}>
<Button
type="primary"
onClick={onSubmit}
className={style['info-box--btns__confirm']}
>
确定
</Button>
<Button onClick={onCancel}>返回</Button>
</Col>
</Row>
</Spin>
{/* 可售日期弹窗 */}
{visibleSaleDate && (
<SaleDateModal
visible={visibleSaleDate}
item={editItem}
type="saleDate"
handleRefresh={v => refreshList('saleDate', v)}
handleClose={() => setVisibleSaleDate(false)}
/>
)}
{/* 可售餐段弹窗 */}
{visibleSaleSection && (
<SaleSectionModal
visible={visibleSaleSection}
enterpriseID={id}
item={editItem}
type="tabCate"
handleRefresh={v => refreshList('tabCate', v)}
handleClose={() => setVisibleSaleSection(false)}
/>
)}
{/* 修改企业商品价格弹窗 */}
{visiblePrice && (
<GoodPriceModal
visible={visiblePrice}
item={editItem}
handleRefresh={v => refreshList('enterprisePrice', v)}
handleClose={() => setVisiblePrice(false)}
/>
)}
{/* 选择商品弹窗 */}
{visibleSelectGoods && (
<SelectGoodsModal
visible={visibleSelectGoods}
enterpriseId={id}
productType={5}
pickSelfIdList={slePickSelf}
shopID={shopId}
item={editItem}
onChangeShop={setShopId}
onSelectChange={setDataSource}
handleClose={() => setVisibleSelectGoods(false)}
/>
)}
</div>
</PageHeaderWrapper>
);
};
export default TakeawayGoodsInfo;
import React, { useState, useEffect, useRef } from 'react';
import { useHistory } from 'react-router-dom';
import { ProTable } from '@ant-design/pro-components';
import { Button, notification } from 'antd';
import { PlusOutlined } from '@ant-design/icons';
import { virtualGoodsColumn } from './staticData/goods';
import utilStyle from '@/utils/utils.less';
import { apiVirtualList, apiVirtualGoodsDel } from './service';
import { getEnterpriseList, getEnterpriseName } from './service/bll';
import { getToUrlQuery } from '@/utils/utils';
// 企业虚拟商品
const VirtualGoods = () => {
const history = useHistory();
const refTable = useRef();
const [pageLoaded, setPageLoaded] = useState(false);
const [enterprises, setEnterprises] = useState(null); // 企业列表
const [enterpriseId, setEnterpriseId] = useState(''); // 选中企业ID
// 刷新列表
const onRefresh = () => {
if (pageLoaded) {
refTable?.current?.reloadAndRest();
}
};
// 搜索商品列表
const searchList = async params => {
if (params.createDate && params.createDate.length) {
const [startDateTime, endDateTime] = params.createDate;
params.startDateTime = startDateTime;
params.endDateTime = endDateTime;
}
const data = {
page: params.current || 1,
size: params.pageSize || 10,
data: Object.assign({}, params, {
enterpriseId,
}),
};
const res = await apiVirtualList(data);
if (res && res.data && res.data.records && res.data.records.length) {
return {
data: res.data.records || [],
total: res.data.total || 0,
};
}
return {
data: [],
total: 0,
};
};
// 删除商品
const onDel = async id => {
await apiVirtualGoodsDel({
id,
});
refTable.current.reload();
notification.success({
message: '删除成功',
});
};
// 跳转添加商品
const toAdd = async () => {
const params = getToUrlQuery();
const query = Object.assign(
{
id: enterpriseId,
name: getEnterpriseName(enterprises, enterpriseId),
},
params,
);
history.push({
pathname: '/virtualGoodsInfo',
query,
});
};
// 改变企业
const onChangeEnterprise = v => {
setEnterpriseId(v);
onRefresh();
};
// 获取企业列表
const getList = async () => {
const obj = await getEnterpriseList({
type: 2,
});
if (obj.list && Object.keys(obj.list).length) {
setEnterprises(obj.list);
setEnterpriseId(`${obj.id}`);
} else if (!enterpriseId) {
notification.error({ message: '未找到企业' });
}
setPageLoaded(true);
};
useEffect(() => {
getList();
}, []);
const options = {
onDel,
enterprises,
enterpriseId,
onChangeEnterprise,
};
let request = () => ({
data: [],
total: 0,
});
if (enterpriseId) {
request = params => searchList({ ...params });
}
return (
<div className={utilStyle.formPageBox}>
{pageLoaded && (
<ProTable
search={{
span: 6,
className: utilStyle.formSearch,
collapsed: false,
collapseRender: () => null,
}}
actionRef={refTable}
tableClassName={utilStyle.formTable}
columns={virtualGoodsColumn(options)}
request={request}
rowKey={r => r.id}
bordered
options={false}
scroll={{ x: 1300 }}
toolbar={{
actions: [
<Button key="1" icon={<PlusOutlined />} type="primary" onClick={toAdd}>
添加虚拟商品
</Button>,
],
}}
/>
)}
</div>
);
};
export default VirtualGoods;
import React, { useRef, useState, useEffect } from 'react';
import { Button, notification, Spin, Select, Row, Col, Table, message } from 'antd';
import { useHistory } from 'react-router-dom';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import { GoodsInfoColumn } from './staticData/goods';
import { jsonToArray } from '@/utils/utils';
import SaleDateModal from './components/SaleDateModal';
import GoodPriceModal from './components/GoodPriceModal';
import SaleSectionModal from './components/SaleSectionModal';
import SelectGoodsModal from './components/SelectGoodsModal';
import { apiSaveVirtualGoodsList, apiShopListByEnterpriseID, apiVirtualList } from './service';
import style from './style/index.less';
const VirtualGoodsInfo = props => {
const { id, name } = props.location.query;
const history = useHistory();
const [loading, setLoading] = useState(false);
const [visibleSaleDate, setVisibleSaleDate] = useState(false); // 可售日期弹窗
const [visibleSaleSection, setVisibleSaleSection] = useState(false); // 可售餐段弹窗
const [visibleSelectGoods, setVisibleSelectGoods] = useState(false); // 选择商品弹窗
const [visiblePrice, setVisiblePrice] = useState(false); // 修改企业商品价格弹窗
const [dataSource, setDataSource] = useState([]);
const [shopList, setShopList] = useState({}); // 店铺列表
const [sleShopID, setSelShopID] = useState(null); // 选中的店铺ID
const [sleShopName, setSelShopName] = useState(''); // 选中的店铺名称
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
const [editItem, setEditItem] = useState({}); // 编辑信息
// 取消
const onCancel = () => {
history.goBack();
};
// 打开选择商品弹窗
const openSelectModal = () => {
if (!sleShopID) {
message.error('请选择店铺');
return;
}
setVisibleSelectGoods(true);
};
// 提交
const onSubmit = async () => {
if (!dataSource || dataSource.length < 1) {
message.error('请添加商品');
return;
}
const skuInfoList = [];
try {
dataSource.forEach(item => {
if (!item.mealTypeList || item.mealTypeList.length < 1) {
throw Error('请添加餐品类型!');
}
if (!item.enterprisePrice || `${item.enterprisePrice}`.length < 1) {
throw Error('请添加企业价格!');
}
if (!item.saleDate || item.saleDate.length < 1) {
throw Error('请添加可售日期!');
}
if (!item.tabCate || item.tabCate.length < 1) {
throw Error('请添加可售餐段!');
}
const json = {
skuId: item.skuId,
enterprisePrice: item.enterprisePrice,
mealTypeList: item.mealTypeList.map(m => m.code),
saleDateList: item.saleDate,
tabCateList: item.tabCate.map(t => ({ tabId: t })),
};
skuInfoList.push(json);
});
} catch (e) {
message.error(e.message);
return;
}
const params = {
enterpriseId: id,
shopId: sleShopID,
skuInfoList,
};
const res = await apiSaveVirtualGoodsList(params);
if (res && res.success) {
notification.success({ message: '添加成功' });
onCancel();
}
};
// 删除
const onDel = i => {
const arr = [...dataSource];
const keys = [...selectedRowKeys];
arr.splice(i, 1);
keys.splice(i, 1);
setDataSource(arr);
setSelectedRowKeys(keys);
};
// 根据企业ID获取店铺列表
const getShopList = async () => {
const res = await apiShopListByEnterpriseID({
enterpriseId: id,
});
if (res && res.data) {
const json = {};
res.data.forEach(item => {
json[item.id] = item.name;
});
setShopList(json);
}
};
// 查询已选商品列表
const getGoodsList = async shopId => {
const data = {
page: 1,
size: 1000,
data: {
enterpriseId: id,
shopId,
},
};
const res = await apiVirtualList(data);
if (res && res.data && res.data.records && res.data.records.length) {
const keys = [];
setDataSource(
res.data.records.map(item => {
item.saleDate = item.saleDateList.map(d => d.code);
item.tabCate = item.tabCateList.map(c => c.tabId);
item.enterprisePrice = item.activityPrice;
item.salePrice = item.price;
keys.push(item.skuId);
return item;
}),
);
setSelectedRowKeys(keys);
} else {
setDataSource([]);
setSelectedRowKeys([]);
}
};
// 选中店铺
const onSelectShop = sid => {
setSelShopID(sid);
setSelShopName(shopList[sid]);
getGoodsList(sid);
};
// 刷新列表数据
const refreshList = (type, v) => {
const arr = dataSource.map(item => {
if (editItem.skuId) {
if (`${item.skuId}` === `${editItem.skuId}`) {
item[type] = v;
}
} else {
item[type] = v;
}
return item;
});
setDataSource(arr);
};
useEffect(() => {
getShopList();
}, []);
const options = {
setVisibleSaleDate,
setVisibleSaleSection,
setEditItem,
setVisiblePrice,
onDel,
};
return (
<PageHeaderWrapper title="添加企业严选商品">
<div className={style['info-box']}>
<Spin spinning={loading}>
<Row className={style['info-box--line']}>
<Col span={3} className={style['info-box--label']}>
企业名称:
</Col>
<Col span={20}>{name}</Col>
</Row>
<Row className={style['info-box--line']}>
<Col span={3} className={style['info-box--label']}>
选择店铺:
</Col>
<Col span={20}>
<Select
showSearch
options={jsonToArray(shopList)}
className={style['info-box--select']}
placeholder="清选择"
value={sleShopID}
onChange={e => onSelectShop(e)}
filterOption={(input, option) =>
(option?.label ?? '').toLowerCase().includes(input.toLowerCase())
}
/>
</Col>
</Row>
<Row className={style['info-box--line']}>
<Col span={3} className={style['info-box--label']}>
添加商品:
</Col>
<Col span={20}>
<Button type="primary" onClick={openSelectModal}>
选择商品
</Button>
</Col>
</Row>
<Row className={style['info-box--line']}>
<Col span={24}>
<Table rowKey="skuId" columns={GoodsInfoColumn(options)} dataSource={dataSource} />
</Col>
</Row>
<Row className={style['info-box--btns']}>
<Col span={4} />
<Col span={20}>
<Button
type="primary"
onClick={onSubmit}
className={style['info-box--btns__confirm']}
>
确定
</Button>
<Button onClick={onCancel}>返回</Button>
</Col>
</Row>
</Spin>
{/* 可售日期弹窗 */}
{visibleSaleDate && (
<SaleDateModal
visible={visibleSaleDate}
item={editItem}
type="saleDate"
productType={4}
dataSource={dataSource}
handleRefresh={v => refreshList('saleDate', v)}
handleClose={() => setVisibleSaleDate(false)}
/>
)}
{/* 可售餐段弹窗 */}
{visibleSaleSection && (
<SaleSectionModal
visible={visibleSaleSection}
item={editItem}
type="tabCate"
enterpriseID={id}
productType={4}
dataSource={dataSource}
handleRefresh={v => refreshList('tabCate', v)}
handleClose={() => setVisibleSaleSection(false)}
/>
)}
{/* 修改企业商品价格弹窗 */}
{visiblePrice && (
<GoodPriceModal
visible={visiblePrice}
item={editItem}
handleRefresh={v => refreshList('enterprisePrice', v)}
handleClose={() => setVisiblePrice(false)}
/>
)}
{/* 选择商品弹窗 */}
{visibleSelectGoods && (
<SelectGoodsModal
visible={visibleSelectGoods}
type="virtual"
enterpriseId={id}
productType={4}
shopID={sleShopID}
shopName={sleShopName}
selectedRowKeys={selectedRowKeys}
selectedRows={dataSource}
onSelectChange={setDataSource}
onSelectedRowKeys={setSelectedRowKeys}
handleClose={() => setVisibleSelectGoods(false)}
/>
)}
</div>
</PageHeaderWrapper>
);
};
export default VirtualGoodsInfo;
......@@ -266,8 +266,8 @@ export function renderServiceTags() {
that.state.businessInfo.customList.map(tag => {
const isLongTag = tag.length > 20;
const tagElem = (
// closable 二期放开
<Tag key={tag} onClose={() => handleClose.call(that, tag)}>
// 二期放开
<Tag key={tag} closable onClose={() => handleClose.call(that, tag)}>
{isLongTag ? `${tag.slice(0, 20)}...` : tag}
</Tag>
);
......@@ -293,14 +293,14 @@ export function renderServiceTags() {
/>
)}
{/* 二期放开 */}
{/* {!that.state.tagVisible && (
{!that.state.tagVisible && (
<Tag
onClick={() => showInput.call(that)}
style={{ background: '#fff', borderStyle: 'dashed' }}
>
<PlusOutlined /> 新增
</Tag>
)} */}
)}
</div>
);
}
......
......@@ -70,7 +70,7 @@ class BusinessInfo extends Component {
mainCategoryId: null,
companyNamedis: false, // 公司名称
companyType: null, // 企业类型
disabled: true, // 二期放开
disabled: false, // 二期放开
};
async componentDidMount() {
......@@ -203,7 +203,11 @@ class BusinessInfo extends Component {
'';
obj.householdRegisterImage =
(imgs?.householdRegisterImage?.length && imgs?.householdRegisterImage[0]?.url) || '';
obj.icbProofImage = (imgs?.icbProofImage?.length && imgs?.icbProofImage[0]?.url) || '';
if (!this.state.companyNamedis) {
obj.icbProofImage = (imgs?.icbProofImage?.length && imgs?.icbProofImage[0]?.url) || '';
} else {
delete obj.icbProofImage;
}
obj.applySource = 1;
obj.signDateType = 1;
......@@ -582,9 +586,11 @@ class BusinessInfo extends Component {
<Row>
<Col span={4} style={{ marginTop: '4px' }}>
{/* 二期放开 */}
{/* {businessInfo.companyName && (
<Button onClick={this.setCompanyName}>修改</Button>
)} */}
{businessInfo.companyName && (
<Button onClick={this.setCompanyName}>
{this.state.companyNamedis ? '修改' : '取消'}
</Button>
)}
</Col>
<Col span={12}>
<FormItem label="公司名称" labelCol={{ span: 8 }}>
......@@ -1194,14 +1200,14 @@ class BusinessInfo extends Component {
</Card>
)}
{/* 临时注释二期放开 */}
{/* <div className={styles.formBtns}>
<div className={styles.formBtns}>
<Button type="primary" size="large" htmlType="submit" loading={this.state.loading}>
修改并提交
</Button>
<Button size="large" onClick={this.onCancel}>
取消
</Button>
</div> */}
</div>
</Form>
{this.state.visibleLoading && (
<div className={styles.spinBox}>
......
......@@ -50,7 +50,7 @@ export const apiRecognize = params =>
// 编辑商户信息
export const apiEditStoreInfo = params =>
request.post('/api/merchants/suppliers/edit', { prefix: kdspApi, data: params });
request.post('/api/merchants/suppliers/info/edit', { prefix: kdspApi, data: params });
// 查询商户详情
export const apiBusinessDetail = businessId =>
......
......@@ -10,7 +10,7 @@ const FormItem = Form.Item;
const { Option } = Select;
const LogisticsForm = props => {
const { modalVisible, onCancel, companys = [], skuList, onSubmit } = props;
const { modalVisible, onCancel, company = [], skuList, onSubmit } = props;
const { getFieldDecorator } = props.form;
const [result, setResult] = useState(() => props.value);
const formData = async (formDataList, fieldsValue, suffixes) => {
......@@ -127,14 +127,14 @@ const LogisticsForm = props => {
],
})(
<Select showSearch placeholder="请选择物流公司">
{companys.map(item => (
{company?.map(item => (
<Option
value={`${item.expressCompanyCode}-${item.expressCompanyName}`}
key={item.expressCompanyCode}
>
{item.expressCompanyName}
</Option>
))}
)) || []}
</Select>,
)}
</FormItem>
......
......@@ -248,51 +248,54 @@ const UpdateStatusModal = (props, ref) => {
const onOk = async () => {
const submitApi = oldPackageList.length === 0 ? apiDeliveriesAdd : apiDeliveriesEdit;
formRef.current.form.validateFields().then(async values => {
const packageList = onFilterParams(values.packageList);
formRef.current.form
.validateFields()
.then(async values => {
const packageList = onFilterParams(values.packageList);
// 对比修改差异
oldPackageList.forEach((item, index) => {
if (!packageList[index]) return;
const updateAttr = () => {
packageList[index].preExpressCompanyCode = item.expressCompanyCode;
packageList[index].preExpressCompanyName = item.expressCompanyName;
packageList[index].preExpressNo = item.expressNo;
};
// eslint-disable-next-line no-restricted-syntax
// for (const key in item) {
// if (typeof item[key] !== 'object' && item[key] !== packageList[index][key]) {
// console.log('修改了物流信息');
// updateAttr();
// break;
// } else if (
// typeof item[key] === 'object' &&
// JSON.stringify(item[key]) !== JSON.stringify(packageList[index][key])
// ) {
// console.log('修改了选择的商品');
// updateAttr();
// break;
// }
// }
updateAttr();
});
// 对比修改差异
oldPackageList.forEach((item, index) => {
if (!packageList[index]) return;
const updateAttr = () => {
packageList[index].preExpressCompanyCode = item.expressCompanyCode;
packageList[index].preExpressCompanyName = item.expressCompanyName;
packageList[index].preExpressNo = item.expressNo;
};
const params = {
orderNo: record.orderNo,
packageList,
};
setConfirmLoading(true);
const res = await submitApi(params);
setConfirmLoading(false);
if (res.code === '0000' && res.businessCode === '0000') {
notification.success({
message: '提交成功',
// eslint-disable-next-line no-restricted-syntax
// for (const key in item) {
// if (typeof item[key] !== 'object' && item[key] !== packageList[index][key]) {
// console.log('修改了物流信息');
// updateAttr();
// break;
// } else if (
// typeof item[key] === 'object' &&
// JSON.stringify(item[key]) !== JSON.stringify(packageList[index][key])
// ) {
// console.log('修改了选择的商品');
// updateAttr();
// break;
// }
// }
updateAttr();
});
onCancel();
actionRef.current.reload();
}
});
const params = {
orderNo: record.orderNo,
packageList,
};
setConfirmLoading(true);
const res = await submitApi(params);
setConfirmLoading(false);
if (res?.code === '0000' && res?.businessCode === '0000') {
notification.success({
message: '提交成功',
});
onCancel();
actionRef.current.reload();
}
})
.catch(err => {});
};
useImperativeHandle(ref, () => ({
......
......@@ -451,7 +451,7 @@ const TableList = props => {
multiLogisticsModalRef.current.open(
{
...record,
packageList: res.data.packageList || [],
packageList: res?.data?.packageList || [],
},
actionRef,
);
......@@ -591,7 +591,7 @@ const TableList = props => {
<LogisticsForm
onSubmit={reload}
skuList={skuList}
companys={companys}
company={companys}
onCancel={() => handleModalVisible(false)}
modalVisible={LogisticsModalVisible}
value={LogisticsData}
......
......@@ -48,13 +48,13 @@
}
}
.btnWarning {
background-color: rgb(247, 143, 74);
border-color: rgb(247, 143, 74);
background-color: rgb(247, 143, 74) !important;
border-color: rgb(247, 143, 74) !important;
}
.btnWarning:hover,
.btnWarning:focus {
background-color: rgb(253, 168, 111);
border-color: rgb(253, 168, 111);
background-color: rgb(253, 168, 111) !important;
border-color: rgb(253, 168, 111) !important;
}
.tableList {
......
......@@ -386,7 +386,7 @@ const OrderList = props => {
skuNo: item.skuId?.toString(),
orderNo: record.orderNoStr,
})),
packageList: res.data.packageList || [],
packageList: res?.data?.packageList || [],
},
actionRef,
);
......
/* eslint-disable class-methods-use-this */
class IframeBridge {
constructor() {
console.info('use iframeBridge');
this.mountEventBus();
}
run(data, callbackName) {
return new Promise(resolve => {
if (!data.event) {
resolve(false);
return;
}
console.log('childIframe', data);
window.parent.postMessage(data, '*');
!callbackName && resolve(true);
});
}
mountEventBus() {
window.addEventListener('message', info => {
// 来源页面地址
console.log('addEventListenerCallBack', info);
});
}
// async closeBrowser() {
// return await this.run({ event: 'closeBrowser' });
// }
// async getToken() {
// return await this.run({ event: 'getToken' }, 'getTokenSuccess');
// }
// async openNewUrl(data: RunData = {}) {
// if (!data.event) {
// data.event = 'openNewUrl';
// }
// return await this.run(data);
// }
}
export default IframeBridge;
......@@ -6,8 +6,10 @@
/* eslint-disable no-underscore-dangle */
import { extend } from 'umi-request';
import { notification } from 'antd';
import { getUrlSearchParams, getToken } from './utils';
import localStorage from './localStorage';
import config from '../../config/env.config';
import IframeBridge from './iframeBridge';
let isRefreshing = true;
let subscriber = [];
......@@ -70,13 +72,17 @@ const refreshRequest = async (url, options) => {
}
return promise;
};
// 判断是否从tob进入
const searchPrams = getUrlSearchParams();
const isTob = searchPrams.source === 'tob';
/**
* 异常处理程序
*/
// request拦截器, 改变url 或 options.
request.interceptors.request.use(async (url, options) => {
const token = localStorage.get('token');
const token = getToken();
if (!token && !url.includes('/v2/oauth/login') && !url.includes('/v2/sms/send')) {
window.location.href = `${window.origin}/user/login`;
}
......@@ -120,7 +126,12 @@ request.interceptors.response.use(async (response, options) => {
notification.warning({
message: data.msg,
});
window.location.href = loginPath;
if (!isTob) {
window.location.href = loginPath;
} else {
const iframeBridgeModel = new IframeBridge();
iframeBridgeModel.run({ event: 'toLogin' });
}
}
if (data.businessCode && data.businessCode !== '0000' && !options.notTip) {
notification.warning({
......
......@@ -2,6 +2,7 @@
import { parse } from 'querystring';
import pathRegexp from 'path-to-regexp';
import moment from 'moment';
import localStorage from './localStorage';
/* eslint no-useless-escape:0 import/prefer-default-export:0 */
const reg = /(((^https?:(?:\/\/)?)(?:[-;:&=\+\$,\w]+@)?[A-Za-z0-9.-]+(?::\d+)?|(?:www.|[-;:&=\+\$,\w]+@)[A-Za-z0-9.-]+)((?:\/[\+~%\/.\w-_]*)?\??(?:[-\+=&;%@.\w_]*)#?(?:[\w]*))?)$/;
......@@ -148,6 +149,13 @@ export const stringOrObjectTrim = params => {
return params;
};
// json转数组用于[Radio/Select/Checkbox]的options
export const jsonToArray = (obj, valueType) =>
Object.keys(obj).map(key => ({
label: obj[key],
value: valueType === 'number' ? +key : key,
}));
export const getObjectType = v => Object.prototype.toString.call(v).replace(/\[object |]/g, '');
// 获取长表单错误提示
......@@ -171,3 +179,47 @@ export const getErrorMessage = err => {
})(err);
return message;
};
// 获取地址栏参数,name:参数名称
export const getUrlParams = name => {
const regArg = RegExp(`(^|&)${name}=([^&]*)(&|$)`);
const r = regArg.exec(window.location.search.substring(1));
if (r != null) return decodeURIComponent(r[2]);
return null;
};
// 获取地址栏所有参数
export const getUrlSearchParams = () => {
const newObj = {};
const url = window.location.search; // 获取url中"?"符后的字符串
if (url.indexOf('?') !== -1) {
const strs = url.replace('?', '').split('&');
for (let i = 0; i < strs.length; i++) {
newObj[strs[i].split('=')[0]] = strs[i].split('=')[1] || '';
}
}
return newObj;
};
// 获取token
export const getToken = () => {
// 从消费地图后管过来
if (getUrlParams('source') === 'tob' && localStorage.get('tobToken')) {
return localStorage.get('tobToken');
}
return localStorage.get('token');
};
// 获取跳转参数
export const getToUrlQuery = () => {
const params = getUrlSearchParams();
// 从消费地图后管过来
if (params.source) {
return {
source: params.source,
token: params.token,
role: params.role,
};
}
return {};
};
......@@ -48,3 +48,26 @@
visibility: hidden;
}
}
.formSearch {
padding: 0;
:global {
.ant-form-item {
display: block !important;
}
}
}
.formTable {
padding: 0 0 15px 0;
}
.formPageBox {
box-sizing: border-box;
padding: 20px;
:global {
.ant-pro-query-filter-actions {
.ant-form-item-label {
width: 0 !important;
min-width: 0 !important;
}
}
}
}
......@@ -452,3 +452,15 @@ export const getDefaultRule = data => {
data,
);
};
// 输入空格校验
export const validateSpaces = (rule, value, callback) => {
// 输入不能包括空格
if (value?.includes(' ')) {
// eslint-disable-next-line prefer-promise-reject-errors
return Promise.reject('输入不能为空格');
}
// 去掉空格后进行进一步校验
// ...
return Promise.resolve();
};
......@@ -12,7 +12,6 @@ class Socket extends EventEmitter {
this.taskRemindInterval = null;
this.connected = false;
this.waitingSendData = [];
this.reconnectCount = 0;
this.heartBeatTimer = null;
return this;
}
......@@ -117,7 +116,9 @@ class Socket extends EventEmitter {
return;
}
const sendValue = typeof value === 'string' ? value : JSON.stringify(value);
this.socket.send(sendValue);
if (this.socket.readyState === this.socket.OPEN) {
this.socket.send(sendValue);
}
}
};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment