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 diff is collapsed.
......@@ -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>,
],
},
];
This diff is collapsed.
.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;
};
This diff is collapsed.
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,
});
}
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
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