Commit 6c3c8269 authored by guang.wu's avatar guang.wu

fix: 删除供应商\连锁店\系统设置

parent 506c55d2
import slash from 'slash2'; import slash from 'slash2';
import defaultSettings from './defaultSettings'; // https://umijs.org/config/ import defaultSettings from './defaultSettings'; // https://umijs.org/config/
import webpackPlugin from './plugin.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 ; const { pwa, primaryColor } = defaultSettings; // preview.pro.ant.design only do not use in your production ;
// preview.pro.ant.design 专用环境变量,请不要在你的项目中使用它。 // preview.pro.ant.design 专用环境变量,请不要在你的项目中使用它。
...@@ -124,18 +123,6 @@ export default { ...@@ -124,18 +123,6 @@ export default {
name: 'afterSaleAddress', name: 'afterSaleAddress',
component: './afterSaleAddress', component: './afterSaleAddress',
}, },
{
title: '商户管理后台',
path: '/reconciliation',
name: 'reconciliation',
component: './reconciliation',
},
{
title: '商户管理后台',
path: '/reconciliation/detail',
name: 'reconciliationDetail',
component: './reconciliation/detail/index',
},
{ {
title: '商户管理后台', title: '商户管理后台',
path: '/settleManage', path: '/settleManage',
...@@ -202,92 +189,18 @@ export default { ...@@ -202,92 +189,18 @@ export default {
icon: 'smile', icon: 'smile',
component: './distributionArea', component: './distributionArea',
}, },
{
title: '商户管理后台',
path: '/reconciliationQuery',
name: 'reconciliationQuery',
icon: 'smile',
component: './ReconciliationQuery',
},
{
title: '商户管理后台',
path: '/settlementSheet',
name: 'settlementSheet',
icon: 'smile',
component: './SettlementSheet',
},
{
title: '商户管理后台',
path: '/paymentMange',
name: 'paymentMange',
icon: 'smile',
component: './PaymentMange',
},
{ {
path: '/password', path: '/password',
name: 'password', name: 'password',
icon: 'smile', icon: 'smile',
component: './password', component: './password',
}, },
{
path: '/chainStoreManage',
name: 'chainStoreManage',
icon: 'smile',
component: './chainStoreManage',
},
// {
// path: '/GoodsManage-new',
// name: 'GoodsManageNew',
// component: './GoodsManage-new',
// },
{ {
title: '服务商品改造-商品模块', title: '服务商品改造-商品模块',
path: '/ServiceGoods/:id', path: '/ServiceGoods/:id',
name: 'ServiceGoods', name: 'ServiceGoods',
component: './ServiceGoods/index', component: './ServiceGoods/index',
}, },
{
title: '商户管理后台-账号管理',
path: '/systemManage/account',
name: 'systemManageAccount',
component: './systemManage/Account',
},
{
title: '商户管理后台-账号管理-用户信息',
path: '/systemManage/account/userInfo',
name: 'accountUserInfo',
component: './systemManage/Account/UserInfo',
},
{
title: '商户管理后台-角色管理',
path: '/systemManage/role',
name: 'systemManageRole',
component: './systemManage/Role',
},
{
title: '商户管理后台-角色管理-角色信息',
path: '/systemManage/role/roleInfo',
name: 'roleInfo',
component: './systemManage/Role/RoleInfo',
},
{
title: '商户管理后台-日志管理',
path: '/systemManage/log',
name: 'systemManageLog',
component: './systemManage/Log',
},
{
title: '商户管理后台-合同查看',
path: '/contractView',
name: 'contractView',
component: './contractView',
},
{
title: '商户管理后台-商家资料',
path: '/businessInfo',
name: 'businessInfo',
component: './businessManage/info',
},
{ {
title: '商户管理后台-品牌管理', title: '商户管理后台-品牌管理',
path: '/brandManage', path: '/brandManage',
...@@ -300,7 +213,6 @@ export default { ...@@ -300,7 +213,6 @@ export default {
name: 'supplyPriceUpdate', name: 'supplyPriceUpdate',
component: './GoodsManage/SupplyPriceUpdate', component: './GoodsManage/SupplyPriceUpdate',
}, },
...groupMealRoute,
{ {
component: './404', component: './404',
}, },
......
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',
},
];
import React from 'react';
import { Button } from 'antd';
export function renderModal() {
return [
<Button type="primary" key="cancel" onClick={() => this.handleCancel()}>
取消
</Button>,
<Button type="primary" onClick={() => this.handleSubmit()} key="save">
提交
</Button>,
];
}
export function detailsModal(filePath) {
return [
<Button type="primary" key="cancel" onClick={() => this.handleCancel()}>
取消
</Button>,
<Button type="primary" href={filePath} target="_blank" key="down">
下载
</Button>,
];
}
export const formItemLayout = {
labelCol: { span: 6 },
wrapperCol: { span: 18 },
};
import React, { Component } from 'react';
import { Modal, Input, Button, Icon, notification } from 'antd';
import { Form } from '@ant-design/compatible';
import styles from '../style.less';
import { renderModal, detailsModal, formItemLayout } from './data';
import Upload from './upload';
import { uploadBill } from '../service';
const { Item: FormItem } = Form;
class toExamine extends Component {
uploadRef = React.createRef();
handleCancel = () => {
const { resetFields, getFieldValue } = this.props.form;
const { clearFileList } = this.uploadRef.current;
clearFileList();
resetFields();
this.props.close();
};
normFile = fileList => fileList;
handleSubmit = async () => {
const {
form: { validateFields },
toExamineData: { id },
} = this.props;
validateFields(async (err, values) => {
if (err) {
return;
}
if (!values?.fileList[0]?.url) {
notification.error({ message: '请上传发票错误,请重新上传' });
return;
}
const params = {
filePath: values?.fileList[0].url,
fileName: values?.fileList[0]?.name || '',
id,
};
const data = await uploadBill(params);
if (data.businessCode === '0000') {
this.handleCancel();
notification.success({ message: '上传成功' });
}
});
};
render() {
const {
form: { getFieldDecorator },
visible,
status,
toExamineData: { filePath, fileName, refuseReason },
} = this.props;
const listFile = [{ url: filePath, name: fileName }];
return (
<Modal
width={400}
title={status === 1 ? '上传发票' : '查看发票'}
visible={visible}
onCancel={() => this.handleCancel()}
footer={status === 1 ? renderModal.call(this) : detailsModal.call(this, filePath)}
className={styles.textArea}
>
<Form className="login-form" {...formItemLayout}>
<FormItem label="上传发票">
{getFieldDecorator('fileList', {
rules: [{ required: true, message: '请上传发票' }],
valuePropName: 'fileList',
getValueFromEvent: this.normFile,
initialValue: status === 1 ? [] : listFile,
})(<Upload max={1} ref={this.uploadRef} status={status} />)}
</FormItem>
{status === 2 ? (
<FormItem label="备注">
{getFieldDecorator('remark', {
initialValue: refuseReason,
})(<Input.TextArea rows={5} disabled />)}
</FormItem>
) : (
<div className="tipMessage">支持格式:pdf/doc/docx/png/jpeg/.zip/.rar</div>
)}
</Form>
</Modal>
);
}
}
export default Form.create()(toExamine);
import { Upload, Icon, Modal, message, Button, notification } from 'antd';
import React, { useState, useEffect } from 'react';
import config from '../../../../config/env.config';
import styles from '../style.less';
import UUID from '../../../utils/uuid';
import { qiniuToken } from '@/services/qiniu';
const qiniu = require('@/utils/qiniu.min.js');
const { qiniuHost } = config;
let token = null;
class PicturesWall extends React.Component {
state = {
fileList: [],
};
async componentDidMount() {
this.initFileList(this.props.fileList || []);
if (this.props.status === 1) {
token = await qiniuToken();
}
}
componentWillReceiveProps(nextProps) {
if (!nextProps?.fileList?.fileList) {
this.initFileList(nextProps.fileList || []);
}
}
initFileList = fileList => {
const fileLists =
fileList.map((item, index) => ({
url: item.url,
name: item.name ? item.name : '发票',
uid: index,
status: 'done',
})) || [];
this.setState({ fileList: fileLists });
};
clearFileList = () => {
this.setState({
fileList: [],
});
};
customRequest = ({ file, onError, onSuccess }) => {
let filename = '';
let suffix = '';
if (file.name) {
const index = file.name.lastIndexOf('.');
filename = file.name.substr(0, index);
suffix = file.name.substr(index + 1, file.name.length - 1);
const types = ['pdf', 'doc', 'docx', 'zip', 'rar', 'png', 'jpeg'];
if (!types.includes(suffix)) {
message.error('文件格式错误!');
return;
}
}
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() {
// ...
},
error(t) {
if (t.code === 614) {
notification.error({
message: '文件名重复,请更换文件名。',
});
}
onError(file);
// ...
},
complete(res) {
const comFile = file;
const url = `${qiniuHost}/${res.key}`;
comFile.url = url;
const list = vm.state.fileList;
list.push({
url: file.url,
name: file.name,
uid: list.length,
status: 'done',
});
vm.setState({ fileList: [...list] });
vm.props.onChange(list);
onSuccess(comFile);
// ...
},
};
observable.subscribe(observer); // 上传开始
};
clearFileList = () => {
this.setState({
fileList: [],
});
};
render() {
const { fileList } = this.state;
const { status } = this.props;
const uploadButton = (
<div className={styles.uploadButton}>
<Icon type="upload" />
<span style={{ paddingLeft: '10px' }}>点击上传发票</span>
</div>
);
const { max } = this.props;
return (
<div className="clearfix">
<Upload
{...this.props}
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>
</div>
);
}
}
export default PicturesWall;
import React from 'react';
import { Button } from 'antd';
import styles from './style.less';
export const payStateEnum = {
1: { text: '待申请' },
2: { text: '待审批' },
3: { text: '已通过' },
4: { text: '待打款' },
5: { text: '已打款' },
};
export const orderStateNum = {
1: { text: '待上传' },
2: { text: '待审核' },
3: { text: '未通过' },
4: { text: '已通过' },
};
export const redInvoiceState = {
1: { text: '', actionValue: 2 },
2: { text: '', actionValue: 1 },
};
export function columns(res, pages) {
const { current, pageSize } = pages;
return [
{
title: '序号',
dataIndex: 'index',
hideInSearch: true,
width: '50px',
align: 'center',
render: (text, record, index) => (current - 1) * pageSize + index + 1,
},
{
title: '供应商名称',
dataIndex: 'supplierName',
width: '150px',
hideInSearch: true,
},
{
title: '付款单批次号',
dataIndex: 'payBatchNo',
width: '160px',
align: 'center',
},
{
title: '付款单生成日期',
key: 'dateTimeRange',
dataIndex: 'createdAtRange',
valueType: 'dateRange',
width: '120px',
hideInTable: true,
initialValue: [],
align: 'center',
},
{
title: '打款日期',
key: 'payTimeRange',
dataIndex: 'payAtRange',
valueType: 'dateRange',
width: '120px',
hideInTable: true,
initialValue: [],
align: 'center',
},
{
title: '帐单确认周期',
dataIndex: 'createdAtRange',
key: 'created',
hideInSearch: true,
align: 'center',
width: '160px',
render: (text, record) => `${record.billPeriodStart}-${record.billPeriodEnd}`,
},
{
title: '付款单生成日期',
dataIndex: 'createdAt',
key: 'createdAt',
hideInSearch: true,
align: 'center',
width: '160px',
},
{
title: '销售总额',
dataIndex: 'saleAmount',
width: '100px',
hideInSearch: true,
align: 'center',
},
{
title: '退款总额',
dataIndex: 'refundAmount',
width: '100px',
hideInSearch: true,
align: 'center',
},
{
title: '红字金额',
dataIndex: 'pastRefundAmount',
width: '100px',
hideInSearch: true,
align: 'center',
},
{
title: () => (
<div style={{ textAlign: 'center' }}>
<div>蓝字金额</div>
<div style={{ color: '#f00' }}>(开票金额)</div>
</div>
),
dataIndex: 'currentRefundAmount',
key: 'currentRefundAmount',
width: '100px',
hideInSearch: true,
align: 'center',
},
{
title: '应付金额',
dataIndex: 'payableAmount',
width: '140px',
hideInSearch: true,
align: 'center',
render: (value, row) => (
<div>
<div>{row.payableAmount}</div>
<div style={{ color: '#f00' }}>赔付 ({row.claimAmount})</div>
<div>(实付 {row.needPayAmount})</div>
</div>
),
},
{
title: '是否缺少红字发票',
dataIndex: 'redInvoiceState',
valueEnum: redInvoiceState,
align: 'center',
width: 100,
},
{
title: '付款状态',
dataIndex: 'payState',
valueEnum: payStateEnum,
width: '110px',
align: 'center',
},
{
title: '发票状态',
dataIndex: 'blueInvoiceState',
valueEnum: orderStateNum,
width: '110px',
align: 'center',
},
{
title: '操作',
dataIndex: 'option',
// valueType: 'option',
key: 'option',
align: 'center',
fixed: 'right',
render: (_, row) => (
<div>
{res.canEditable ? (
<>
<Button
type="primary"
disabled={row.payState !== 1}
className={styles.button}
onClick={() => res.edit(0, row)}
>
申请结算
</Button>
<Button
type="primary"
disabled={
!(
(row.blueInvoiceState === 1 || row.blueInvoiceState === 3) &&
row.payState === 3
)
}
className={styles.button}
onClick={() => res.edit(1, row)}
>
上传发票
</Button>
</>
) : (
''
)}
<Button
type="primary"
disabled={row.blueInvoiceState === 1}
className={styles.button}
onClick={() => res.edit(2, row)}
>
查看发票
</Button>
<Button type="primary" className={styles.button} onClick={() => res.edit(3, row)}>
下载账单
</Button>
</div>
),
},
];
}
export const toolBarRender = onExport => () => [];
/* eslint-disable no-shadow */
import { Button, notification, Modal, Spin } from 'antd';
import React, { useRef, useState } from 'react';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import ProTable from '@ant-design/pro-table';
import { saveAs } from 'file-saver';
import { format } from 'date-fns';
import moment from 'moment';
import { connect } from 'dva';
import { PAYMENT_MANAGE } from '@/../config/permission.config';
import ToExamineModal from './ToExamine';
import { columns, toolBarRender } from './data';
import {
query,
selfPaymentExport,
selfPaymentexportDetail,
settlement,
merchantView,
} from './service';
const { confirm } = Modal;
const PaymentMange = props => {
const { permissions } = props;
const canEditable = permissions[PAYMENT_MANAGE.EDITABLE];
const actionRef = useRef();
const formRef = useRef();
const [toExamineData, settoExamineData] = useState({});
const [loading, setloading] = useState(false);
const [status, setstatus] = useState(1);
const [pages, setpages] = useState({});
const [toExamineModalVisibel, setToExamineModalVisibel] = useState(false);
const reload = () => {
if (actionRef.current) {
actionRef.current.reload();
}
};
// 下载账单付款单明细
const exportDetail = async params => {
setloading(true);
const data = await selfPaymentexportDetail(params);
if (data) {
const blob = new Blob([data]);
saveAs(blob, `账单付款单明细-${format(new Date(), 'yyyyMMddHHmmss')}.xlsx`);
} else {
notification.error({ message: '下载失败' });
}
setloading(false);
};
// 申请结算
const applySettlement = async params => {
setloading(true);
const data = await settlement(params);
if (data.businessCode === '0000') {
reload();
notification.success({ message: '申请成功' });
}
setloading(false);
};
const showConfirm = params => {
confirm({
title: '申请结算',
content: '现在结算?',
onOk() {
applySettlement(params);
},
});
};
// 查看发票
const invoiceDetail = async params => {
const data = await merchantView(params);
if (data.businessCode === '0000') {
settoExamineData({ ...data.data });
setToExamineModalVisibel(true);
}
};
// 0申请结算 1上传发票 2查看发票 3下载账单付款单明细
const edit = async (status, { id }) => {
if (status === 0) {
showConfirm(id);
} else if (status === 1) {
setstatus(status);
settoExamineData({ id });
setToExamineModalVisibel(true);
} else if (status === 2) {
setstatus(status);
invoiceDetail(id);
} else if (status === 3) {
exportDetail(id);
}
};
const onColse = () => {
if (status === 1) {
reload();
}
setToExamineModalVisibel(false);
};
const checkedTime = ({ dateTimeRange }) => {
const startTimeStr = moment(dateTimeRange?.[0]).format('YYYY-MM-DD');
const endTimeStr = moment(dateTimeRange?.[1]).format('YYYY-MM-DD');
const diffTime = moment(endTimeStr).valueOf() - moment(startTimeStr).valueOf();
const maxTime = 62 * 3600 * 24 * 1000;
if (!dateTimeRange?.[0]) {
notification.error({ message: '付款单开始时间不能为空' });
return false;
}
if (diffTime > maxTime) {
notification.error({ message: '只允许下载2个月的数据' });
return false;
}
return true;
};
const onExport = async () => {
const params = formRef.current.getFieldsValue();
setloading(true);
if (checkedTime(params)) {
const data = await selfPaymentExport(params);
const blob = new Blob([data]);
saveAs(blob, `付款单导出数据-${format(new Date(), 'yyyyMMddHHmmss')}.xlsx`);
}
setloading(false);
};
const onToolBarRender = toolBarRender();
const res = {
edit,
canEditable,
};
const searchRender = ({ searchText, resetText }, { form }) => [
<Button
key="search"
type="primary"
style={{ marginRight: '10px' }}
onClick={() => {
// eslint-disable-next-line no-unused-expressions
form?.submit();
}}
>
{searchText}
</Button>,
<Button
key="rest"
style={{ marginRight: '10px' }}
onClick={() => {
// eslint-disable-next-line no-unused-expressions
form?.resetFields();
// eslint-disable-next-line no-unused-expressions
form?.submit();
}}
>
{resetText}
</Button>,
<Button
key="export"
type="primary"
style={{ marginRight: '10px' }}
onClick={() => {
// eslint-disable-next-line no-unused-expressions
onExport();
}}
>
导出
</Button>,
];
return (
<Spin tip="处理中..." spinning={loading} delay={100}>
<PageHeaderWrapper>
<ProTable
actionRef={actionRef}
formRef={formRef}
request={params => {
const { current, pageSize } = params;
setpages({ current, pageSize });
return query({ ...params });
}}
columns={columns(res, pages)}
rowKey={r => r.id}
search={{
collapsed: false,
optionRender: searchRender,
}}
bordered
toolBarRender={onToolBarRender}
scroll={{ x: '100%', y: 500 }}
/>
<ToExamineModal
visible={toExamineModalVisibel}
toExamineData={toExamineData}
status={status}
close={() => {
onColse();
}}
/>
</PageHeaderWrapper>
</Spin>
);
};
export default connect(({ menu }) => ({
permissions: menu.permissions,
}))(PaymentMange);
import request from '@/utils/request';
import config from '../../../config/env.config';
import moment from 'moment';
import _ from 'lodash';
// 分页查询所有数据
const { querysApi } = config;
export async function query(params) {
const queryParams = {
pageSize: params.pageSize || 10,
pageNo: params.current || 1,
payBatchNo: params?.payBatchNo,
payBeginDate: params?.dateTimeRange?.[0],
payEndDate: params?.dateTimeRange?.[1],
paidBeginDate: params?.payTimeRange?.[0],
paidEndDate: params?.payTimeRange?.[1],
redInvoiceState: params?.redInvoiceState,
blueInvoiceState: params?.blueInvoiceState,
payState: params?.payState,
};
const { data } = await request.post('/selfPaymentBill/merchant/page', {
prefix: querysApi,
data: _.omitBy(queryParams, v => !v),
});
return {
data: data?.records || [],
total: data.total || 1,
};
}
export async function qiniuToken() {
// const data = await request.get('/upload/getToken', {
// prefix: config.opapiHost,
// });
const data = await request.get('/api/kdsp/common/upload/token', {
prefix: config.opapiHost,
});
return data?.data?.token;
}
// 付款单导出
export async function selfPaymentExport(params) {
console.log(params?.dateTimeRange?.[0]);
const queryParams = {
payBatchNo: params?.payBatchNo,
payBeginDate: params?.dateTimeRange?.[0]
? moment(params?.dateTimeRange?.[0]).format('yyyy-MM-DD')
: '',
payEndDate: params?.dateTimeRange?.[1]
? moment(params?.dateTimeRange?.[1]).format('yyyy-MM-DD')
: '',
paidBeginDate: params?.payTimeRange?.[0]
? moment(params?.payTimeRange?.[1]).format('yyyy-MM-DD')
: '',
paidEndDate: params?.payTimeRange?.[1]
? moment(params?.payTimeRange?.[1]).format('yyyy-MM-DD')
: '',
redInvoiceState: params?.redInvoiceState,
blueInvoiceState: params?.blueInvoiceState,
payState: params?.payState,
};
return request.post('/selfPaymentBill/merchant/export', {
prefix: querysApi,
data: _.omitBy(queryParams, v => !v),
responseType: 'arrayBuffer',
});
}
// 付款单导出
export async function selfPaymentexportDetail(params) {
return request.post('/selfPaymentBill/merchant/exportDetail', {
prefix: querysApi,
data: params,
responseType: 'arrayBuffer',
headers: {
'Content-Type': 'application/json;charset=UTF-8',
},
});
}
// 申请结算单
export async function settlement(params) {
return request.get(`/selfPaymentBill/merchant/apply/settlement/${params}`, {
prefix: querysApi,
});
}
// 上传发票
export async function uploadBill(params) {
return request.post('/selfPaymentBill/merchant/upload/bill', {
prefix: querysApi,
data: params,
});
}
// 查看发票
export async function merchantView(params) {
return request.get(`/selfPaymentBill/merchant/view/${params}`, {
prefix: querysApi,
});
}
.button {
display: block;
margin: 0 auto 10px;
}
.uploadButton {
padding: 5px 10px;
border: 1px solid #cecece;
cursor: pointer;
}
.textArea {
:global {
.ant-modal-footer {
text-align: center;
}
.ant-btn-primary {
margin-left: 20px;
}
.tipMessage {
margin-top: -20px;
padding-left: 5px;
color: red;
}
}
}
import { Button } from 'antd';
import React from 'react';
export const activeStateEnum = {
10: { text: '销售' },
20: { text: '售后' },
};
export const orderStateNum = {
1: { text: '待确认' },
2: { text: '已确认' },
3: { text: '已生成结算单' },
};
export function columns(pages) {
const { current, pageSize } = pages;
return [
{
title: '序号',
dataIndex: 'index',
hideInSearch: true,
width: '50px',
align: 'center',
render: (text, record, index) => (current - 1) * pageSize + index + 1,
},
{
title: '供应商名称',
dataIndex: 'supplierName',
width: '150px',
align: 'center',
hideInSearch: true,
},
{
title: '结算单批次号',
dataIndex: 'settleBatchNo',
width: '160px',
align: 'center',
},
{
title: '账单日期',
key: 'dateTimeRange',
dataIndex: 'createdAtRange',
valueType: 'dateRange',
width: '120px',
hideInTable: true,
initialValue: [],
align: 'center',
},
{
title: '账单日期',
dataIndex: 'billDateStr',
key: 'billDateStr',
hideInSearch: true,
align: 'center',
width: '160px',
},
{
title: '账单类型',
dataIndex: 'billType',
valueEnum: activeStateEnum,
width: '120px',
filters: false,
align: 'center',
},
{
title: '金额',
dataIndex: 'amount',
width: '100px',
hideInSearch: true,
align: 'center',
render: (value, row) => {
const msg = row.billType === 20 ? <div>包含赔付金额</div> : null;
return (
<div>
<div>{value}</div>
{msg}
</div>
);
},
},
{
title: '笔数',
dataIndex: 'quantity',
width: '100px',
hideInSearch: true,
align: 'center',
render: (value, row) => {
const msg = row.billType === 20 ? <div>包含赔付笔数</div> : null;
return (
<div>
<div>{value}</div>
{msg}
</div>
);
},
},
{
title: '状态',
dataIndex: 'status',
valueEnum: orderStateNum,
width: '100px',
filters: false,
align: 'center',
},
];
}
export const toolBarRender = gather => {
const { onDownload, confirmAction, canEditable } = gather;
return () => [
<Button type="primary" style={{ marginRight: '20px' }} onClick={onDownload}>
下载
</Button>,
canEditable ? (
<Button type="primary" onClick={confirmAction}>
确认
</Button>
) : (
''
),
];
};
/* eslint-disable no-shadow */
import { Button, Upload, notification, Spin } from 'antd';
import React, { useRef, useEffect, useState } from 'react';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import ProTable from '@ant-design/pro-table';
import { saveAs } from 'file-saver';
import { format } from 'date-fns';
import { connect } from 'dva';
import { RECONCILIATION_QUERY } from '@/../config/permission.config';
import { columns, toolBarRender } from './data';
import { query, confirmSelfBill, downloadSelfBillDetail } from './service';
const ReconciliationQuery = props => {
const { permissions } = props;
const canEditable = permissions[RECONCILIATION_QUERY.EDITABLE];
const actionRef = useRef();
const [selectedRowKeys, setselectedRowKeys] = useState([]);
const [selectedRows, setselectedRows] = useState([]);
const [loading, setloading] = useState(false);
const [pages, setpages] = useState({});
const [billName, setbillName] = useState('');
const reload = () => {
if (actionRef.current) {
actionRef.current.reload();
}
};
// 检查是否是同类型的
const checkedBillType = data => {
const billType = data[0]?.billType;
const sameData = data.filter(item => item.billType === billType);
if (data.length === sameData.length && sameData.length < 32) {
const billName = billType === 10 ? '销售' : '售后';
setbillName(billName);
return true;
}
if (sameData.length > 31) {
notification.error({ message: '一次最多选择31个账单批次' });
return false;
}
notification.error({ message: '不同账单类型不可批量操作' });
return false;
};
// 检查是否是确认账单
const checkedStatus = data => {
const sameData = data.filter(item => item.status === 1);
if (sameData.length > 0 && data.length === sameData.length) {
return true;
}
if (!sameData.length && !data.length) {
notification.error({ message: '请选择1~31条账单批次' });
return false;
}
notification.error({ message: '只有待确认账单可以确认' });
return false;
};
const onSelectChange = (selectedRowKeys, selectedRows) => {
if (checkedBillType(selectedRows)) {
setselectedRowKeys(selectedRowKeys);
setselectedRows(selectedRows);
}
};
const queryData = async params => {
const { current, pageSize } = params;
setpages({ current, pageSize });
setselectedRowKeys([]);
setselectedRows([]);
const data = await query({ ...params });
return data;
};
const confirmAction = async () => {
setloading(true);
if (checkedStatus(selectedRows)) {
const data = await confirmSelfBill(selectedRowKeys);
if (data.businessCode === '0000') {
setselectedRowKeys([]);
setselectedRows([]);
actionRef.current.reloadAndRest();
// reload();
// queryData({ current: 1, pageSize: 20 })
notification.success({ message: '确认成功' });
}
setloading(false);
} else {
setloading(false);
}
};
const onDownload = async () => {
if (selectedRowKeys.length && selectedRowKeys.length < 32) {
setloading(true);
const data = await downloadSelfBillDetail(selectedRowKeys);
const blob = new Blob([data]);
saveAs(blob, `${billName}-${format(new Date(), 'yyyyMMddHHmmss')}.xlsx`);
setloading(false);
setselectedRowKeys([]);
setselectedRows([]);
reload();
} else {
notification.error({ message: '请选择1~31条账单批次' });
setloading(false);
}
};
const gather = {
onDownload,
confirmAction,
canEditable,
};
const onToolBarRender = toolBarRender(gather);
const rowSelection = {
selectedRowKeys,
onChange: onSelectChange,
};
const searchRender = ({ searchText, resetText }, { form }) => [
<Button
key="search"
type="primary"
style={{ marginRight: '10px' }}
onClick={() => {
// eslint-disable-next-line no-unused-expressions
form?.submit();
}}
>
{searchText}
</Button>,
<Button
key="rest"
style={{ marginRight: '10px' }}
onClick={() => {
// eslint-disable-next-line no-unused-expressions
form?.resetFields();
// eslint-disable-next-line no-unused-expressions
form?.submit();
}}
>
{resetText}
</Button>,
];
return (
<Spin tip="处理中..." spinning={loading} delay={100}>
<PageHeaderWrapper>
<ProTable
actionRef={actionRef}
request={params => queryData(params)}
columns={columns(pages)}
rowKey={r => r.id}
search={{
collapsed: false,
optionRender: searchRender,
}}
rowSelection={rowSelection}
bordered
toolBarRender={onToolBarRender}
scroll={{ x: '100%', y: 400 }}
/>
</PageHeaderWrapper>
</Spin>
);
};
export default connect(({ menu }) => ({
permissions: menu.permissions,
}))(ReconciliationQuery);
import request from '@/utils/request';
import config from '../../../config/env.config';
import _ from 'lodash';
// 分页查询所有数据
const { querysApi } = config;
export async function query(params) {
const queryParams = {
pageSize: params.pageSize || 10,
pageNo: params.current || 1,
beginBillDate: params?.dateTimeRange?.[0],
endBillDate: params?.dateTimeRange?.[1],
settleBatchNo: params?.settleBatchNo,
billType: params?.billType,
status: params?.status,
};
const { data } = await request.post('/selfBill/merchant/querySelfBill', {
prefix: querysApi,
data: _.omitBy(queryParams, v => !v),
});
return {
data: data?.records || [],
total: data?.total || 1,
};
}
// 对账单确认
export async function confirmSelfBill(params) {
return request.post('/selfBill/merchant/confirmSelfBill', {
prefix: querysApi,
data: params,
});
}
// 对账下载
export async function downloadSelfBillDetail(params) {
return request.post('/selfBill/merchant/downloadSelfBillDetail', {
prefix: querysApi,
data: params,
responseType: 'arrayBuffer',
});
}
/* eslint-disable consistent-return */
/* eslint-disable react/no-array-index-key */
/* eslint-disable no-shadow */
import React, {
useContext,
useState,
useEffect,
forwardRef,
useImperativeHandle,
useRef,
} from 'react';
import {
Form,
Input,
Select,
Button,
Checkbox,
Radio,
Space,
Switch,
Row,
Col,
InputNumber,
Cascader,
Divider,
DatePicker,
message,
} from 'antd';
import moment from 'moment';
import { MinusCircleOutlined, PlusOutlined } from '@ant-design/icons';
import { Title } from './CommonTemplate';
import { formItemLayout, ENUM_REPERTORY, ENUM_SET_REPERTORY, ENUM_WEEK } from '../config';
import { ServiceContext } from '../context';
import { debounce } from '@/utils/utils';
import { isCheckPriceTwoDecimal, isIntegerNotZero } from '@/utils/validator';
import UploadImage from './UploadImage';
import styles from '../common.less';
import AddRepertoryModal from './AddRepertoryModal';
import { apiTagList, apiUnits } from '../service';
import { localAutoSaveKey, calcDescartes } from '../utils';
import localStorage from '@/utils/localStorage';
const createInitValues = () => ({
description: '', // 商品描述
detailImageList: [], // 商品图片
stock: '1', // 库存类型
saleTimeType: 0, // 售卖时间
singleDelivery: 1, // 单点不送
list: 1, // 列出商品
label: [],
minPurchaseNum: 1,
});
const format = 'HH:mm';
const { RangePicker } = DatePicker;
const TakeawayGoodsInfo = forwardRef((props, ref) => {
const { editData, infoMation } = props;
const [form] = Form.useForm();
const [initValue, setInitValue] = useState(createInitValues());
const customer = useContext(ServiceContext);
const [detailImageList, setDetailImageList] = useState([]);
const addSellTimeRef = useRef(null);
const AddRepertoryRef = useRef(null);
const AddMultiSpecRef = useRef(null);
const [saleDates, setSaleDates] = useState({
saleDates: [],
saleTimes: [],
}); // 可售日期
const [timeType, setTimeType] = useState(0);
const [repertoryType, setRepertoryType] = useState('1');
const [tagList, setTagList] = useState([]);
const [unitsList, setUnitsList] = useState([]);
const [takeawayData, setTakeawayData] = useState({});
// eslint-disable-next-line prefer-const
let [multiSpu, setMultiSpu] = useState([]);
const [singularSpu, setSingularSpu] = useState([]);
const [intactData, setIntactData] = useState({});
const [repertoryState, setRepertoryState] = useState('');
const [repertoryModel, setRepertoryModel] = useState({});
const [tempMultiSpu, setTempMultiSpu] = useState([]);
const [tempWeight, setTempWeight] = useState([]);
const [tempSpecs, setTempSpecs] = useState([]);
const [weightUnits, setWeightUnits] = useState([]);
const [peopleUnits, setPeopleUnits] = useState([]);
const [isRequired, setIsRequired] = useState(true);
const [initMultiSpu, setInitMultiSpu] = useState([]); // 初始化sku 作对比
// 自定义加入菜单
const showModal = () => {
addSellTimeRef.current.setOpen(true);
};
const onCheck = async () => {
try {
const values = await form.validateFields();
return {
...values,
temp: 'takeawayItem',
intactData,
};
} catch (errorInfo) {
return null;
}
};
// 过滤单位
const filterUnit = unit =>
(Array.isArray(unit) && unit?.length && unit?.slice(1).toString()) || unit;
// 商品基本信息编辑商品名称
const takeawayCalc = takeawayData => {
const { infoMation: name, infoMation, takeawayItem } = takeawayData;
// weight 份量 specs规格 生成sku规则 weight * specs
const {
specs = [],
weight = [],
description,
detailImageList,
list,
minPurchaseNum,
saleDates,
maxStock = 0,
saleTimes = [],
label,
quantity,
productStock = 0,
salePrice,
singleDelivery,
saleTimeType,
autoStock = 0,
skuList,
id,
categoryId,
} = takeawayItem;
let { unit } = takeawayItem;
unit = (unit && filterUnit(unit)) || unit;
const singularSpecList = [{ specGroupName: '份量', generateSku: 1, specs: [] }]; // 单规格
const multiSpecList = [{ specGroupName: '份量', generateSku: 1, specs: [] }]; // 多规格
let singularSpuData = []; // 单库存spu
const saleTimesTemp = [];
if (saleTimes.length) {
saleTimes.forEach(item => {
const startTime = item ? moment(item[0]).format(format) : '';
const endTime = item ? moment(item[1]).format(format) : '';
saleTimesTemp.push({ startTime, endTime });
});
}
const temp = {
salePrice: salePrice || 0,
productStock,
list: 1,
id: skuList?.length === 1 ? skuList[0].id : '', // 单规格默认取第一个
serviceItem: {
description, // 商品描述
maxStock, // 最大库存
productStock,
minPurchaseNum, // 最少购买
saleTimeType, // 售卖时间
singleDelivery, // 单点不送
list, // 是否列出 1 是 0 否(外卖商品必填)
label, // 商品标签id
saleDates, // 可售日期 1-8
saleTimes: saleTimesTemp, // 可售日期 时间段
autoStock, // 弹框设置---自动补足
},
};
// 单规格
if (+repertoryType === 1) {
const specs = {
salePrice,
maxStock,
quantity,
// unit: (Array.isArray(unit) && unit?.length && unit?.splice(unit?.length - 1)[0]) || unit,
unit,
productStock,
specGroupName: '份量',
};
if (peopleUnits.includes(specs.unit)) {
delete specs.quantity;
}
singularSpecList[0].specs = [specs];
singularSpuData = [{ ...temp, specs: [specs] }];
setSingularSpu(singularSpuData);
}
// 多规格
if (+repertoryType === 2) {
// if (name || editData.name) {
if (weight.length) {
let tempSku = [];
multiSpecList[0].specs = weight; // 处理specList
if (specs.length) {
specs.forEach((specsItem, specsIndex) => {
if (specsItem && !specsItem.generateSku) {
specsItem.generateSku = 1;
}
if (specsItem?.specGroupName) {
specsItem.specs.forEach(itm => {
itm.specGroupName = specsItem?.specGroupName;
});
}
multiSpecList.push(specsItem);
});
}
const sepcx = specs.map(item => item.specs);
sepcx.map(item => {
item.unit = '';
return item;
});
// 生成 specs 规格sku
tempSku = [[...weight], ...sepcx].reduce(
(x, y) => {
const arr = [];
x.forEach(x => y.forEach(y => arr.push(x.concat([y]))));
return arr;
},
[[]],
);
multiSpu = tempSku.map((item, index) => {
const obj = {};
obj.salePrice = salePrice;
obj.productStock = productStock;
obj.list = list;
obj.id = multiSpu?.[index]?.id || '';
obj.serviceItem = {
...temp.serviceItem,
productStock: multiSpu?.[index]?.serviceItem?.productStock || productStock,
maxStock: multiSpu?.[index]?.serviceItem?.maxStock || maxStock,
autoStock: multiSpu?.[index]?.serviceItem?.autoStock || autoStock,
};
obj.unique = '';
multiSpecList.forEach((itm, idx) => {
if (item[idx] && !item[idx]?.specGroupName) {
if (multiSpecList[idx].specGroupName) {
item[idx].specGroupName = multiSpecList[idx].specGroupName;
}
}
});
obj.specs = [...item];
return obj;
});
}
multiSpu.forEach(item => {
item.salePrice = 0;
item.unique = '';
item.unique = item?.specs
.map(itm => {
if (itm?.unit) {
itm.unit =
itm?.unit &&
(Array.isArray(itm?.unit) ? itm?.unit.slice(itm?.unit.length - 1)[0] : itm?.unit);
}
if (itm?.unit && peopleUnits.includes(itm.unit)) {
delete itm.quantity;
}
if (itm?.specGroupName && itm?.specName) {
return `${itm?.specGroupName + itm.specName}`;
}
return itm;
})
.toString();
});
// 编辑数据
if (customer.isEdit) {
const uniqueArr = tempMultiSpu.map(item => item.unique);
const multiSpuUnique = multiSpu.map(item => item.unique);
const newArrLen = Array.from(new Set(multiSpuUnique)).length;
if (newArrLen < multiSpuUnique.length) {
message.error('份量名称不能重复!');
}
// 新增对比数据
multiSpu.forEach((item, index) => {
if (!uniqueArr.includes(item.unique)) {
tempMultiSpu.push({
...item,
serviceItem: { ...temp.serviceItem, maxStock: 0, autoStock: 0, productStock: 0 },
id: '',
});
}
});
// // 删除对比数据
while (multiSpu.length !== tempMultiSpu.length) {
tempMultiSpu.forEach((item, index) => {
if (!multiSpuUnique.includes(item.unique)) {
tempMultiSpu.splice(index, 1);
}
});
}
// 同步库存设置
multiSpu.map(item => {
tempMultiSpu.forEach(itm => {
if (item.unique === itm.unique) {
// 修改商品售卖信息参数
const params = {
saleDates: temp.serviceItem.saleDates,
saleTimes: temp.serviceItem.saleTimes,
saleTimeType: temp.serviceItem.saleTimeType,
minPurchaseNum: temp.serviceItem.minPurchaseNum,
};
item.serviceItem = { ...item.serviceItem, ...itm.serviceItem, ...params };
itm.specs = [...item.specs];
}
});
return item;
});
setTempMultiSpu(tempMultiSpu);
setMultiSpu(JSON.parse(JSON.stringify(multiSpu)));
}
// 新增
if (!customer.isEdit) {
setTempMultiSpu(multiSpu);
setMultiSpu(multiSpu);
}
}
// +repertoryType === 1 单规格 2多规格
const intactDataTemp = {
...takeawayItem,
id,
type: 5, // 外卖类型
...infoMation,
label: label && label.toString(),
list,
description,
detailImageList,
repertoryType,
singleDelivery,
specList: +repertoryType === 1 ? singularSpecList : multiSpecList, // 单库存和多库存specList
items: +repertoryType === 1 ? singularSpuData : JSON.parse(JSON.stringify(multiSpu)),
initMultiSpu,
};
setIntactData(intactDataTemp);
return intactData;
};
const onChange = () => {};
const getFormValues = debounce(() => {
const values = form.getFieldsValue();
props.onValuesChange({
takeawayItem: JSON.parse(JSON.stringify(Object.assign(values, { multiSpu }))), // { multiSpu }草稿箱回显使用
});
const takeawayData = customer.isEdit
? { takeawayItem: Object.assign({}, editData, values) }
: localStorage.get(localAutoSaveKey) || {};
// : { takeawayItem: values };
setTakeawayData(takeawayData);
takeawayCalc(takeawayData);
}, 400);
// 设置库存
const modifiedInventory = (type, idx, values) => {
const { productStock, maxStock, autoStock } = values;
if (type === 'multi') {
tempMultiSpu.map(item => {
if (item.unique === idx) {
item.serviceItem.productStock = productStock;
item.serviceItem.autoStock = autoStock;
item.serviceItem.maxStock = maxStock;
}
return item;
});
}
if (type === 'all') {
tempMultiSpu.map(item => {
item.serviceItem.productStock = productStock;
item.serviceItem.autoStock = autoStock;
item.serviceItem.maxStock = maxStock;
return item;
});
}
setTempMultiSpu(tempMultiSpu);
getFormValues();
return false;
};
const filterCategoryOptions = (inputValue, path) =>
path.some(option => option.name.toLowerCase().indexOf(inputValue.toLowerCase()) > -1);
useImperativeHandle(ref, () => ({
onCheck,
reset: () => {
setInitValue(createInitValues());
form.resetFields();
},
}));
const startTime = moment(moment().format(format));
const endTime = moment(moment().format(format));
// 上传图片
const onCardSuccessImageList = imgList => {
setDetailImageList(imgList);
form.setFieldsValue({
detailImageList: imgList,
});
};
// 自定义菜单下拉
const dropdownRender = menus => (
<div>
{menus}
<Divider
style={{
margin: 0,
}}
/>
<div
style={{
padding: 8,
background: '#1890ff',
color: '#fff',
textAlign: 'center',
}}
onClick={showModal}
>
添加自定售卖时间
</div>
</div>
);
// 切换库存
const onChangeRepertory = e => {
const { value } = e.target;
setRepertoryType(`${value}`);
if (+value === 1) {
form.setFieldsValue({ weight: [] });
form.setFieldsValue({ specs: [] });
} else {
const params = {
quantity: '',
unit: [],
salePrice: '',
productStock: '',
maxStock: '',
autoStock: false,
};
form.setFieldsValue(params);
}
};
// 勾选库存设置
const onChangeSetRepertory = e => {
setRepertoryState(`${e.target.value}`);
if (+e.target.value === 0) {
form.setFieldsValue({
productStock: 0,
});
} else {
const { maxStock } = form.getFieldsValue(['maxStock']);
form.setFieldsValue({
productStock: maxStock,
});
}
getFormValues();
};
// 最大库存设置
const onChangeMaxStock = e => {
// 已经勾选最大库存 自动更新剩余库存
if (+repertoryState === 1) {
form.setFieldsValue({
productStock: e,
});
}
};
// 切换时间
const onChangeTime = e => {
setTimeType(e.target.value);
};
// 自动补全
const onChangeAutoStock = e => {
form.setFieldsValue({
autoStock: e ? 1 : 0,
});
setIsRequired(!e);
};
// 显示加入库存弹框
const showAddRepertoryModal = (type, idx, item) => {
AddRepertoryRef.current.setOpenRepertory(true);
setRepertoryModel({ type, idx, item });
};
// 拼接sku 名称
const calcLabelName = item => {
let firstName = '';
let lastName = '';
if ((item?.unique || item?.specs?.length >= 0) && item?.unique !== '[object Object]') {
const tempQuantity = `(${
item?.specs[0]?.unit
? (weightUnits.includes(filterUnit(item.specs[0]?.unit)) && '') || ''
: ''
} ${
peopleUnits.includes(filterUnit(item.specs[0]?.unit)) ? '' : item.specs[0]?.quantity || ''
}`;
const tempUnit = `${item.specs[0]?.unit || ''} ${item.specs[0]?.unit ? ')' : ''}`;
const uniqueName = item?.unique?.split(',') || [];
if (uniqueName && uniqueName?.length) {
firstName = uniqueName[0] || '';
lastName = uniqueName?.slice(1).join(' ') || '';
}
return `${firstName} ${tempQuantity} ${tempUnit} ${lastName.replace('[object Object]', '')}`;
}
};
const init = async () => {
if (!tagList.length) {
const res = await apiTagList();
setTagList(res.data || []);
}
if (!unitsList?.length) {
const res = await apiUnits();
setUnitsList(res.data || []);
const tempWeight = res.data.filter(item => item.name === '准确重量单位');
const tempPeople = res.data.filter(item => item.name === '适用人数');
if (tempWeight?.length && tempWeight[0]?.children) {
const tempWeightName = tempWeight[0]?.children.map(item => item.name);
sessionStorage.setItem('weightUnits', JSON.stringify(tempWeightName || []));
setWeightUnits(tempWeightName);
}
if (tempPeople?.length && tempPeople[0]?.children) {
const tempPeopleName = tempPeople[0]?.children.map(item => item.name);
sessionStorage.setItem('peopleUnits', JSON.stringify(tempPeopleName || []));
setPeopleUnits(tempPeopleName);
}
}
// setTempMultiSpu([]);// 回显时候库存无法展示
return false;
};
useEffect(() => {
if (customer.isEdit && !customer.isUseCache) {
if (!editData) return;
const {
label,
firstCategoryId,
secondCategoryId,
thirdCategoryId,
id,
saleTimes,
saleTimeType,
} = editData;
editData.label = label && label.split(',')?.map(item => +item);
const specList = editData?.specList;
const weight = specList?.filter(item => item.specGroupName === '份量');
const specs = specList?.filter(item => item.specGroupName !== '份量');
const tempWeightName = JSON.parse(sessionStorage.getItem('weightUnits'));
if (weight?.length) {
weight.forEach(item => {
if (item?.specs?.length) {
item.specs.forEach(itm => {
if (itm?.unit && tempWeightName.includes(itm?.unit)) {
itm.quantity =
itm?.quantity.indexOf('约') > -1 ? itm?.quantity.slice(1) : itm?.quantity || '';
}
});
}
});
}
editData.saleTimes = saleTimes?.length
? saleTimes.map(item => [moment(item?.startTime, format), moment(item?.endTime, format)])
: [];
if (editData?.skuList?.length) {
// 单规格
if (editData?.skuList?.length === 1) {
const {
productStock,
shopId,
specs,
serviceItem: { maxStock, id, autoStock },
} = editData?.skuList[0];
const { quantity, unit, salePrice } = specs[0];
editData.productStock = productStock;
editData.quantity = `${quantity}`.indexOf('约') > -1 ? quantity.slice(1) : quantity || '';
editData.unit = unit;
editData.salePrice = salePrice;
editData.maxStock = maxStock;
editData.categoryId = [firstCategoryId, secondCategoryId, thirdCategoryId];
editData.autoStock = +autoStock === 1;
editData.productRefShopId = editData.shopId;
editData.skuId = id;
form.setFieldsValue(editData);
} else {
// 多规格
setRepertoryType('2');
const specList = editData?.specList;
const weight = specList.filter(item => item?.specGroupName === '份量');
const tempWeightName = JSON.parse(sessionStorage.getItem('weightUnits'));
if (weight?.length) {
weight.forEach(item => {
if (item?.specs?.length) {
item.specs.forEach(itm => {
if (itm?.unit && tempWeightName?.includes(itm?.unit)) {
itm.quantity =
itm?.quantity?.indexOf('约') > -1
? itm?.quantity.slice(1)
: itm?.quantity || '';
}
});
}
});
}
editData.categoryId = [firstCategoryId, secondCategoryId, thirdCategoryId];
editData.productRefShopId = editData.shopId;
// setTempWeight(weight)
// setTempSpecs(specs)
form.setFieldsValue({ weight: weight[0].specs });
form.setFieldsValue(editData);
form.setFieldsValue({ specs });
const tempMultiSpu = editData?.skuList.map(item => {
if (item) {
item.serviceItem.productStock = item.productStock;
const weightIndex = item.specs.findIndex(itm => itm.specGroupName === '份量');
const tempQuantity = item.specs[weightIndex].quantity;
const tempUnit = item.specs[weightIndex].unit;
const specsNameList = item.specs.map(itm => itm.specName);
const lastName = specsNameList.slice(1)?.join(' ');
// item.unique = item?.specs.map(itm => itm.specName).toString();
// item.uniqueAll = item?.specs.map(itm => `${itm.specGroupName + itm.specName}`).toString();
item.unique = item?.specs
.map(itm => `${itm.specGroupName + itm.specName}`)
.toString();
}
return item;
});
setTempMultiSpu(tempMultiSpu || []);
setInitMultiSpu(JSON.parse(JSON.stringify(tempMultiSpu)) || []);
setMultiSpu(JSON.parse(JSON.stringify(tempMultiSpu)) || []);
}
setTimeType(saleTimeType);
form.setFieldsValue({ productRefShopId: editData.shopId });
onCardSuccessImageList(editData?.detailImageList);
getFormValues();
}
}
if (customer.isUseCache) {
props.onValuesChange(editData, !0);
let tempEdit = JSON.parse(JSON.stringify(editData));
tempEdit = Object.assign({}, editData, { ...tempEdit.takeawayItem, ...tempEdit.infoMation });
if (Object.keys(tempEdit).length) {
if (tempEdit?.saleTimes) {
tempEdit.saleTimes = tempEdit?.saleTimes?.length
? tempEdit?.saleTimes?.map(item => [moment(item[0]), moment(item[1])])
: [];
}
if (tempEdit?.stock === '1') {
setRepertoryType('1');
}
if (tempEdit?.stock === '2') {
setRepertoryType('2');
form.setFieldsValue({ weight: tempEdit?.weight });
form.setFieldsValue({ specs: tempEdit?.specs });
setTempMultiSpu(JSON.parse(JSON.stringify(tempEdit?.multiSpu)));
}
if (tempEdit?.detailImageList) {
onCardSuccessImageList(tempEdit?.detailImageList);
}
setTimeType(tempEdit?.saleTimeType);
form.setFieldsValue(tempEdit);
getFormValues();
}
}
}, [customer.isEdit, editData]);
useEffect(() => {
setIntactData(intactData);
}, [intactData]);
useEffect(() => {
init();
}, []);
useEffect(() => {
getFormValues();
}, [repertoryType]);
useEffect(() => {}, [tempMultiSpu]);
return (
<>
<Form
{...formItemLayout}
form={form}
name="takeaway"
initialValues={initValue}
scrollToFirstError
onValuesChange={getFormValues}
>
<Title title="商品详细信息" />
<Form.Item name="description" label="商品描述">
<Input.TextArea
showCount
maxLength={200}
style={{ width: 400 }}
placeholder="请输入商品描述"
/>
</Form.Item>
<Form.Item
name="detailImageList"
label="商品图片"
extra="支持.jpg/png格式图片,建议单张图片宽750像素,大小200kb左右,您可以拖拽图片调整顺序,最多上传5张。"
>
<UploadImage
name="detailImageList"
limit={5}
disabled={customer.isDisabled}
pictures={detailImageList}
limitPicHeight="true"
setPictureList={list => onCardSuccessImageList(list)}
/>
</Form.Item>
<Title title="商品售卖信息" />
<Form.Item
name="minPurchaseNum"
label="最少购买"
rules={[
{ required: true, message: '请输入最少购买数' },
{
validator: isIntegerNotZero,
type: 'number',
message: '请输入大于0的整数',
},
]}
>
<InputNumber min={1} max={999999999} style={{ width: 200 }} placeholder="请输入购买量" />
</Form.Item>
<Form.Item
name="saleTimeType"
label="售卖时间"
// rules={[{ type: 'array', required: true, message: '请输入售卖时间!' }]}
>
<Radio.Group onChange={onChangeTime}>
<Radio value={0}>全时段</Radio>
<Radio value={1}>自定义售卖时间</Radio>
</Radio.Group>
</Form.Item>
{+timeType === 1 && (
<>
<Form.Item name="saleDates" label="售卖时期(可多选)">
<Checkbox.Group>
<Row>
<Col className={styles.colRow} span={8}>
{ENUM_WEEK.map((item, index) => (
<Checkbox value={item.value}>{item.label}</Checkbox>
))}
</Col>
</Row>
</Checkbox.Group>
</Form.Item>
<Form.Item label="售卖时段">
<Form.List name="saleTimes" initialValue={[[]]}>
{(fields, { add, remove }) => (
<>
{fields.map((field, index) => (
<Form.Item required={false} key={field.key} className={styles.deal}>
<Form.Item
className={styles.deal}
{...field}
rules={[
{
required: true,
message: '请输入售卖时间',
},
]}
>
<RangePicker
picker="time"
defaultPickerValue={[startTime, endTime]}
format={format}
onChange={onChange}
showTime={{
format: 'HH:mm',
// defaultValue: [moment('00:00', 'HH:mm'), moment('00:00', 'HH:mm')],
hideDisabledOptions: true,
}}
disabledTime={() => ({
disabledMinutes: () => {
const result = [];
for (let i = 1; i < 60; i++) {
if (i !== 30) {
result.push(i);
}
}
return result;
},
})}
/>
</Form.Item>
{fields.length > 1 ? (
<MinusCircleOutlined
className="dynamic-delete-button"
onClick={() => remove(field.name)}
/>
) : null}
</Form.Item>
))}
<Form.Item>
{fields.length < 5 ? (
<Button
type="dashed"
onClick={() => add()}
style={{ width: '60%' }}
icon={<PlusOutlined />}
>
新增售卖时段
</Button>
) : null}
</Form.Item>
</>
)}
</Form.List>
</Form.Item>
</>
)}
<Form.Item name="label" label="商品标签">
<Select
// mode="multiple"
allowClear
placeholder="请选择商品标签"
style={{ width: 400 }}
disabled={customer.isEdit && customer.isNormal}
// showSearch={{ filter: filterCategoryOptions }}
fieldNames={{ label: 'tagName', value: 'tagId' }}
// onChange={props.onCategoryChange}
options={tagList}
/>
</Form.Item>
<Form.Item
name="singleDelivery"
label="是否单点不送"
extra="选择单点不送后顾客点该商品不可单独下单"
>
<Radio.Group>
<Radio value={1}>单点送</Radio>
<Radio value={0}>单点不送</Radio>
</Radio.Group>
</Form.Item>
<Form.Item name="list" label="列出商品" extra="开启后平台展示商品">
<Radio.Group>
<Radio value={1}>是</Radio>
<Radio value={0}>否</Radio>
</Radio.Group>
</Form.Item>
<Title title="规格信息" />
{!customer.isEdit && (
<Form.Item label="库存" name="stock">
<Radio.Group
options={ENUM_REPERTORY}
onChange={onChangeRepertory}
value="1"
buttonStyle="solid"
optionType="button"
/>
</Form.Item>
)}
{/* 单规格 */}
{repertoryType === '1' && (
<>
<Form.Item label="份量" className={styles.required}>
{form.getFieldValue(['unit']) &&
weightUnits.includes(filterUnit(form.getFieldValue(['unit']))) && (
<span className="ant-form-text"> 约</span>
)}
<Form.Item
// noStyle
shouldUpdate={(prevValues, curValues) => false}
>
{form => {
let unit = form.getFieldValue('unit') || '';
unit = filterUnit(unit);
if (!peopleUnits.includes(unit)) {
return (
<Form.Item
name="quantity"
style={{
display: 'inline-block',
}}
rules={[
{ required: true, message: '请输入分量!' },
{
validator: isCheckPriceTwoDecimal,
type: 'number',
message: '保留两位小数',
},
]}
>
<InputNumber
min={1}
max={999999999}
style={{ width: 200 }}
placeholder="请输入数字"
/>
</Form.Item>
);
}
}}
</Form.Item>
<Form.Item
name="unit"
rules={[{ required: true, message: '请选择单位' }]}
// rules={[{ type: 'array', required: true, message: '请选择单位' }]}
style={{
display: 'inline-block',
margin: '0 8px',
}}
>
<Cascader
placeholder="请选择单位"
disabled={customer.isEdit && customer.isNormal}
showSearch={{ filter: filterCategoryOptions }}
fieldNames={{ label: 'name', value: 'name', children: 'children' }}
onChange={() => {
const unit = form.getFieldValue('unit') || [];
if (unit && peopleUnits.includes(unit[1])) {
form.setFieldsValue({ quantity: '' });
}
}}
options={unitsList}
/>
</Form.Item>
</Form.Item>
<Form.Item
name="salePrice"
label="销售价格"
rules={[{ required: true, message: '请输入销售价格' }]}
>
<InputNumber
min={0}
max={999999999}
precision={2}
style={{ width: 200 }}
placeholder="元"
/>
</Form.Item>
<Form.Item className={styles.itemInline}>
<Form.Item
name="productStock"
label="剩余库存"
style={{
display: 'flex',
marginRight: '8px',
}}
rules={[
{ required: true, message: '请填写剩余库存' },
{
validator: isIntegerNotZero,
type: 'number',
message: '请输入大于0的整数',
},
]}
>
<InputNumber
min={0}
max={999999999}
style={{ width: 200, display: 'inline-block' }}
placeholder="请输入"
/>
</Form.Item>
<Form.Item style={{ width: 200, display: 'inline-block' }}>
<Radio.Group value={repertoryState} onChange={onChangeSetRepertory}>
<Radio.Button value="0">清空</Radio.Button>
<Radio.Button value="1">最大</Radio.Button>
</Radio.Group>
</Form.Item>
</Form.Item>
<Form.Item
name="maxStock"
label="最大库存"
rules={[
{ required: !isRequired, message: '请填写最大库存' },
{
validator: isIntegerNotZero,
type: 'number',
message: '请输入大于0的整数',
},
]}
>
<InputNumber
max={999999999}
min={0}
style={{ width: 200 }}
placeholder="请输入"
onChange={onChangeMaxStock}
/>
</Form.Item>
<Form.Item name="autoStock" label="自动补足" valuePropName="checked">
<Switch
checkedChildren="开启"
unCheckedChildren="关闭"
// defaultUnChecked
onChange={onChangeAutoStock}
/>
</Form.Item>
<div className={styles.rowWarp}>
{singularSpu.length > 0 &&
singularSpu[0]?.specs[0]?.unit &&
singularSpu.map((item, index) => (
<div className={styles.specsSingularBetween}>
<Form.Item label={calcLabelName(item)}>
<div className={styles.specsSingularBetween}>
<span className={styles.repertoryLimit}>
{item?.specs[0]?.productStock}/{item?.specs[0]?.maxStock}
</span>
{/* <div
className={styles.specRepertory}
onClick={() => {
showAddRepertoryModal('singular', index); // 单规格库存
}}
>
设置库存
</div> */}
</div>
</Form.Item>
</div>
))}
</div>
</>
)}
{/* 多规格 */}
{repertoryType === '2' && (
<>
<Form.Item>
<div>份量(如大小/小份、微辣/特辣等)</div>
<Form.List name="weight" initialValue={[{}]}>
{(weightFields, { add: weightAdd, remove: weightRemove }) => (
<>
{weightFields.map((weightField, index) => (
<Space key={weightField.key} align="baseline" className={styles.conBg}>
<Form.Item
{...weightField}
name={[weightField.name, 'specName']}
rules={[
{
required: true,
message: '请输入名称',
},
]}
>
<Input style={{ width: '200px' }} placeholder="名称" />
</Form.Item>
{weightUnits.includes(
filterUnit(form.getFieldValue(['weight'])?.[index]?.unit),
) && <span className="ant-form-text"> 约</span>}
<Form.Item
// noStyle
shouldUpdate={(prevValues, curValues) => false}
>
{form => {
let unit = form.getFieldValue(['weight'])?.[index]?.unit || '';
unit = filterUnit(unit);
if (!peopleUnits.includes(unit)) {
return (
<Form.Item
style={{
display:
peopleUnits.includes(
form.getFieldValue(['weight'])?.[index]?.unit,
) && 'none',
}}
{...weightField}
name={[weightField.name, 'quantity']}
rules={[
{
required: !peopleUnits.includes(
form.getFieldValue(['weight'])?.[index]?.unit,
),
message: '份量',
},
{
validator: isIntegerNotZero,
type: 'number',
message: '请输入大于0的整数',
},
]}
>
<InputNumber
style={{ width: '120px' }}
max={999999999}
min={0}
placeholder="约 份量(数字)"
/>
</Form.Item>
);
}
}}
</Form.Item>
<Form.Item
{...weightField}
name={[weightField.name, 'unit']}
rules={[
{
// type: 'array',
required: true,
message: '请选择单位',
},
]}
>
<Cascader
placeholder="请选择单位"
disabled={customer.isEdit && customer.isNormal}
showSearch={{ filter: filterCategoryOptions }}
fieldNames={{
label: 'name',
value: 'name',
children: 'children',
}}
// onChange={props.onCategoryChange}
options={unitsList}
/>
</Form.Item>
<Form.Item
{...weightField}
name={[weightField.name, 'salePrice']}
rules={[
{
required: true,
message: '销售价',
},
]}
>
<InputNumber
max={999999999}
min={0}
precision={2}
style={{ width: '120px' }}
placeholder="销售价(元)"
/>
</Form.Item>
{/* <Form.Item
{...weightField}
name={[weightField.name, 'activityPrice']}
rules={[
{
required: false,
message: '活动价',
},
]}
>
<InputNumber
max={999999999}
min={0}
precision={2}
style={{ width: '120px' }}
placeholder="活动价(元)"
/>
</Form.Item> */}
{weightFields.length > 1 ? (
<MinusCircleOutlined onClick={() => weightRemove(weightField.name)} />
) : null}
</Space>
))}
<Form.Item>
<Button
type="primary"
onClick={() => weightAdd()}
block
style={{ width: '400px' }}
icon={<PlusOutlined />}
>
新增份量
</Button>
</Form.Item>
</>
)}
</Form.List>
</Form.Item>
{
<>
<Form.Item>
<div>添加规格(如加料、甜度、辣度等)</div>
<Form.List name="specs" initialValue={[]}>
{(specsFields, { add, remove }) => (
<>
{specsFields.map((specsField, index) => (
<Form.Item key={specsField.key} className={styles.conBg}>
<Form.Item
{...specsField}
name={[specsField.name, 'specGroupName']}
rules={[
{
required: true,
whitespace: true,
message: '请输入规格名称',
},
]}
noStyle
>
<Input placeholder="规格名称" className={styles.nameWidth} />
</Form.Item>
<MinusCircleOutlined
className="dynamic-delete-button"
onClick={() => remove(specsField.name)}
/>
<Form.List
{...specsField}
name={[specsField.name, 'specs']}
initialValue={[{}]}
>
{(specsInfoFields, { add: specsAdd, remove: specsRemove }) => (
<>
{specsInfoFields.map(specsInfofield => (
<Space key={specsInfofield.key} align="baseline">
<Form.Item
{...specsInfofield}
name={[specsInfofield.name, 'specName']}
rules={[
{
required: true,
message: '请输入加价名称',
},
]}
>
<Input style={{ width: '200px' }} placeholder="加价名称" />
</Form.Item>
<Form.Item
{...specsInfofield}
name={[specsInfofield.name, 'salePrice']}
rules={[
{
required: true,
message: '请输入加价金额(元)',
},
]}
>
<InputNumber
max={999999999}
min={0}
precision={2}
style={{ width: '200px' }}
placeholder="加价名称金额(元)"
/>
</Form.Item>
{specsInfoFields.length > 1 ? (
<MinusCircleOutlined
onClick={() => specsRemove(specsInfofield.name)}
/>
) : null}
</Space>
))}
<Form.Item>
<Button
type="dashed"
onClick={() => specsAdd()}
block
style={{ width: '400px' }}
icon={<PlusOutlined />}
>
新增加价
</Button>
</Form.Item>
</>
)}
</Form.List>
</Form.Item>
))}
<Form.Item>
<Button
type="primary"
onClick={() => add()}
style={{ width: '400px' }}
icon={<PlusOutlined />}
>
新增规格
</Button>
</Form.Item>
</>
)}
</Form.List>
</Form.Item>
<div className={styles.textStyle}>修改成功后,原库存将被替换,请谨慎操作!</div>
<Form.Item
name="receptionVolume"
label="多规格库存"
className={styles.multiSpecification}
>
<Button
size="small"
danger
style={{ marginBottom: '10px' }}
onClick={() => {
showAddRepertoryModal('all'); // 统一库存
}}
shape="round"
>
统一设置库存
</Button>
<div className={styles.rowWarp}>
{tempMultiSpu.length > 0 &&
tempMultiSpu?.map((item, idx) => (
<>
{item.unique && (
<>
<div key={idx} className={styles.specsBetween}>
<Form.Item>
<div className={styles.specsBetween}>
<div>{calcLabelName(item)} :</div>
<span className={styles.repertoryLimit}>
{item?.serviceItem?.productStock}/
{item?.serviceItem?.maxStock}
</span>
<div
className={styles.specRepertory}
onClick={() => {
showAddRepertoryModal('multi', `${item.unique}`, item); // 多个库存
}}
>
去设置
</div>
</div>
</Form.Item>
</div>
</>
)}
</>
))}
</div>
</Form.Item>
</>
}
</>
)}
</Form>
{/* 加入库存 */}
<AddRepertoryModal
ref={AddRepertoryRef}
modifiedInventory={modifiedInventory}
intactData={intactData}
repertoryModel={repertoryModel}
/>
</>
);
});
export default TakeawayGoodsInfo;
...@@ -33,95 +33,6 @@ export const TaskList = (canAddService, canAddNormal, canTakeawayService) => [ ...@@ -33,95 +33,6 @@ export const TaskList = (canAddService, canAddNormal, canTakeawayService) => [
}, },
}, },
}, },
// {
// name: '虚拟商品',
// type: 2,
// desc: '无需物流',
// hide: !canAddNormal,
// imgConfig: {
// commonImageList: {
// title: '公共滑动图',
// rule: false,
// limit: null,
// renderExtra: () => '(图片最大上传2M)',
// },
// imageList: {
// rule: false,
// limit: null,
// renderExtra: () => '(图片最大上传2M)',
// },
// detailImageList: {
// title: '详情图',
// rule: true,
// limit: null,
// renderExtra: () => '(图片最大上传2M)',
// },
// },
// },
{
name: '服务类商品',
type: 4,
desc: '无需物流',
hide: !canAddService,
imgConfig: {
commonImageList: {
title: '封面图片',
rule: true,
limit: 1,
renderExtra(leng) {
return `建议尺寸: ##宽##高 (${leng} / 1) 封面图第一张 `;
},
},
cardImageList: {
title: '商品图片',
rule: true,
limit: 11,
renderExtra(leng) {
return `建议尺寸: ##宽##高,sku商品轮播图(${leng} / 11)`;
},
},
detailImageList: {
title: '商品详情图',
// rule: true,
limit: 30,
renderExtra() {
return '最多上传30张';
},
},
},
},
{
name: '外卖商品',
type: 5,
desc: '无需物流',
hide: !canTakeawayService,
imgConfig: {
commonImageList: {
title: '封面图片',
rule: true,
limit: 1,
renderExtra(leng) {
return `建议尺寸: ##宽##高 (${leng} / 1) 封面图第一张 `;
},
},
cardImageList: {
title: '商品图片',
rule: true,
limit: 11,
renderExtra(leng) {
return `建议尺寸: ##宽##高,sku商品轮播图(${leng} / 11)`;
},
},
detailImageList: {
title: '商品详情图',
// rule: true,
limit: 30,
renderExtra() {
return '最多上传30张';
},
},
},
},
]; ];
export const WeeksList = [ export const WeeksList = [
......
...@@ -10,7 +10,6 @@ import FormRuleSetting from './components/FormRuleSetting'; ...@@ -10,7 +10,6 @@ import FormRuleSetting from './components/FormRuleSetting';
import FormRuleVPictures from './components/FormRuleVPictures'; import FormRuleVPictures from './components/FormRuleVPictures';
import FormSettlementOthers from './components/FormSettlementOthers'; import FormSettlementOthers from './components/FormSettlementOthers';
import FormAttr from './components/FormAttr'; import FormAttr from './components/FormAttr';
import FormTakeaway from './components/FormTakeaway';
import localStorage from '@/utils/localStorage'; import localStorage from '@/utils/localStorage';
import { import {
merchantBrandList, merchantBrandList,
...@@ -625,18 +624,6 @@ const ServiceGoods = options => { ...@@ -625,18 +624,6 @@ const ServiceGoods = options => {
onValuesChange={onValuesChange} onValuesChange={onValuesChange}
/> />
)} )}
{productType === 5 && (
<>
<FormTakeaway
ref={takeawayRef}
takeAway={takeAway}
editData={takeawayEditData}
takeawayInfoMation={takeawayInfoMation}
supplierIdList={supplierIdList}
onValuesChange={onValuesChange}
/>
</>
)}
</ServiceContext.Provider> </ServiceContext.Provider>
</WrapperContainer> </WrapperContainer>
</Spin> </Spin>
......
import React from 'react';
import { Button } from 'antd';
export const dateStateEnum = {
1: { text: '一周', maxlength: 8 },
2: { text: '半个月', maxlength: 4 },
3: { text: ' 一个月', maxlength: 2 },
};
export const orderStateNum = {
1: { text: '待结算' },
2: { text: '已结算' },
3: { text: '无需结算' },
};
export const paymentStateEnum = {
true: { text: '' },
false: { text: '' },
};
export function columns(pages) {
const { current, pageSize } = pages;
return [
{
title: '序号',
dataIndex: 'index',
hideInSearch: true,
width: '50px',
align: 'center',
render: (text, record, index) => (current - 1) * pageSize + index + 1,
},
{
title: '供应商名称',
dataIndex: 'supplierName',
width: '150px',
hideInSearch: true,
align: 'center',
},
{
title: '付款单批次号',
dataIndex: 'payBatchNo',
width: '160px',
align: 'center',
},
{
title: '结算单批次号',
dataIndex: 'settleBatchNo',
width: '150px',
align: 'center',
},
{
title: '结算单生成日期',
key: 'dateTimeRange',
dataIndex: 'createdAtRange',
valueType: 'dateRange',
width: '120px',
hideInTable: true,
initialValue: [],
align: 'center',
},
{
title: '帐单确认周期',
dataIndex: 'createdAtRange',
key: 'createdAtRange',
hideInSearch: true,
align: 'center',
width: '160px',
render: (text, record) => `${record.billPeriodStart}-${record.billPeriodEnd}`,
},
{
title: '账期',
dataIndex: 'billPeriodType',
valueEnum: dateStateEnum,
width: '120px',
hideInSearch: true,
filters: false,
align: 'center',
},
{
title: '结算单生成日期',
dataIndex: 'createdAt',
key: 'createdAt',
hideInSearch: true,
align: 'center',
width: '160px',
},
{
title: '销售金额',
dataIndex: 'saleAmount',
width: '100px',
hideInSearch: true,
align: 'center',
},
{
title: '退款金额',
dataIndex: 'refundAmount',
width: '100px',
hideInSearch: true,
align: 'center',
render: (value, row) => (
<div>
<div>{value}</div>
<div style={{ color: '#f00' }}>(赔付{row.claimAmount})</div>
</div>
),
},
{
title: '结算金额',
dataIndex: 'payableAmount',
width: '130px',
hideInSearch: true,
align: 'center',
},
{
title: '结算状态',
dataIndex: 'settleState',
valueEnum: orderStateNum,
width: '100px',
initialValue: 0,
filters: false,
align: 'center',
},
{
title: '是否生成付款单',
dataIndex: 'isCreatePaymentBill',
valueEnum: paymentStateEnum,
hideInTable: true,
align: 'center',
},
];
}
export const toolBarRender = ({ paymentOrder, canEditable }) => () => [
canEditable ? (
<Button type="primary" onClick={paymentOrder}>
生成付款单
</Button>
) : (
<></>
),
];
/* eslint-disable no-undef */
/* eslint-disable no-shadow */
/* eslint-disable no-return-assign */
import { Button, Upload, notification, Spin } from 'antd';
import React, { useRef, useEffect, useState } from 'react';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import ProTable from '@ant-design/pro-table';
import { connect } from 'dva';
import { columns, toolBarRender, dateStateEnum } from './data';
import { query, selfPaymentBill } from './service';
import { SETTLEMENT_SHEET } from '@/../config/permission.config';
const SettlementSheet = props => {
const { permissions } = props;
const canEditable = permissions[SETTLEMENT_SHEET.EDITABLE];
const actionRef = useRef();
const [selectedRowKeys, setselectedRowKeys] = useState([]);
const [selectedRowsList, setselectedRowsList] = useState([]);
const [loading, setloading] = useState(false);
const [pages, setpages] = useState({});
const reload = () => {
if (actionRef.current) {
actionRef.current.reload();
}
};
// 计算当前结算金额为0或负
const checkedAmount = data => {
const totalPrice = data.reduce(
(totalPrice, item) => (totalPrice += Number(item.payableAmount)),
0,
);
if (totalPrice <= 0) {
notification.warning({ message: '当前结算金额为0或负' });
}
};
// 检查帐期类型的最多下载条数
const checkedBillType = data => {
if (!data.length) {
notification.error({ message: '请选择数据' });
return false;
}
const billPeriodType = data[0]?.billPeriodType;
const billPeriodData = dateStateEnum[billPeriodType];
if (data.length <= billPeriodData.maxlength) {
return true;
}
notification.error({
message: `${billPeriodData.text}帐期最多可选${billPeriodData.maxlength}条`,
});
return false;
};
// 检查帐期周期是否连续
const checkedTime = data => {
const minTime = data[0]?.billPeriodStart;
const maxTime = data[0]?.billPeriodEnd;
let minTimeNum = new Date(minTime).getTime();
let maxTimeNum = new Date(maxTime).getTime();
if (data.length < 2) {
return true;
}
for (let i = 1; i < data.length; i += 1) {
const startTime = data[i]?.billPeriodStart;
const endTime = data[i]?.billPeriodEnd;
const startTimeNum = new Date(startTime).getTime();
const endTimeNum = new Date(endTime).getTime();
if (startTimeNum > maxTimeNum && maxTimeNum + 24 * 3600 * 1000 === startTimeNum) {
maxTimeNum = endTimeNum;
} else if (minTimeNum > endTimeNum && endTimeNum + 24 * 3600 * 1000 === minTimeNum) {
minTimeNum = startTimeNum;
}
}
const endRulstTime = data[data.length - 1]?.billPeriodEnd;
const endRulstTimeNum = new Date(endRulstTime).getTime();
if (endRulstTimeNum <= maxTimeNum && minTimeNum <= endRulstTimeNum) {
return true;
}
notification.error({ message: '账单周期不连续' });
return false;
};
const onSelectChange = (selectedRowKeys, selectedRows) => {
if (selectedRowsList.length > selectedRows.length) {
setselectedRowKeys(selectedRowKeys);
setselectedRowsList(selectedRows);
} else if (checkedTime(selectedRows)) {
setselectedRowKeys(selectedRowKeys);
setselectedRowsList(selectedRows);
}
};
const paymentOrder = async () => {
if (checkedBillType(selectedRowsList)) {
setloading(true);
checkedAmount(selectedRowsList);
const data = await selfPaymentBill(selectedRowKeys);
if (data.businessCode === '0000') {
setselectedRowKeys([]);
setselectedRowsList([]);
reload();
notification.success({ message: '生成付款单成功' });
}
setloading(false);
}
};
const queryData = async params => {
const { current, pageSize } = params;
setpages({ current, pageSize });
setselectedRowKeys([]);
setselectedRowsList([]);
const data = await query({ ...params });
return data;
};
const onToolBarRender = toolBarRender({ paymentOrder, canEditable });
const rowSelection = {
selectedRowKeys,
onChange: onSelectChange,
getCheckboxProps: record => ({
disabled: !(record.settleState === 1 && !record.payBatchNo), // Column configuration not to be checked
}),
};
const searchRender = ({ searchText, resetText }, { form }) => [
<Button
key="search"
type="primary"
style={{ marginRight: '10px' }}
onClick={() => {
// eslint-disable-next-line no-unused-expressions
form?.submit();
}}
>
{searchText}
</Button>,
<Button
key="rest"
style={{ marginRight: '10px' }}
onClick={() => {
// eslint-disable-next-line no-unused-expressions
form?.resetFields();
// eslint-disable-next-line no-unused-expressions
form?.submit();
}}
>
{resetText}
</Button>,
];
return (
<Spin tip="处理中..." spinning={loading} delay={100}>
<PageHeaderWrapper>
<ProTable
actionRef={actionRef}
request={params => queryData(params)}
columns={columns(pages)}
rowKey={r => r.settleBatchNo}
search={{
collapsed: false,
optionRender: searchRender,
}}
rowSelection={rowSelection}
bordered
toolBarRender={onToolBarRender}
scroll={{ x: '100%', y: 400 }}
/>
</PageHeaderWrapper>
</Spin>
);
};
export default connect(({ menu }) => ({
permissions: menu.permissions,
}))(SettlementSheet);
import request from '@/utils/request';
import config from '../../../config/env.config';
import _ from 'lodash';
// 分页查询所有数据
const { querysApi } = config;
export async function query(params) {
const queryParams = {
pageSize: params.pageSize || 10,
pageNo: params.current || 1,
createStart: params?.dateTimeRange?.[0],
createEnd: params?.dateTimeRange?.[1],
settleState: params?.settleState,
settleBatchNo: params?.settleBatchNo,
isCreatePaymentBill: params?.isCreatePaymentBill,
payBatchNo: params?.payBatchNo,
};
const { data } = await request.post('/selfSettleBill/merchant/pageQuery', {
prefix: querysApi,
data: _.omitBy(queryParams, v => !v),
});
return {
data: data?.records || [],
total: data.total || 1,
};
}
// 生成付款单
export async function selfPaymentBill(params) {
return request.post('/selfPaymentBill/merchant/generate/paymentBill', {
prefix: querysApi,
data: params,
});
}
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;
/**
* 获取企业客户列表
* /yapi/project/389/interface/api/65324
*/
export async function apiEnterpriseList(data) {
return request.post(`${apiPrefix}/enterprise/pageList`, {
data,
prefix: roleApi,
});
}
/**
* 获取企业客户详细
* /yapi/project/389/interface/api/65339
*/
export async function apiEnterpriseInfo(id) {
return request.get(`${apiPrefix}/enterprise/info?id=${id}`, {
prefix: roleApi,
});
}
/**
* 添加企业客户
* /yapi/project/389/interface/api/65329
*/
export async function apiNewEnterprise(data) {
return request.post(`${apiPrefix}/enterprise/save`, {
data,
prefix: roleApi,
});
}
/**
* 编辑企业客户
* /yapi/project/389/interface/api/65334
*/
export async function apiEditEnterprise(data) {
return request.post(`${apiPrefix}/enterprise/update`, {
data,
prefix: roleApi,
});
}
/**
* 查询自提点列表
* /yapi/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;
/**
* 获取企业外卖商品列表
* /yapi/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;
}
/**
* 获取企业虚拟商品列表
* /yapi/project/389/interface/api/64794
*/
export async function apiVirtualList(param) {
return request.post(`${apiPrefix}/product/enterprise/virtual/pageList`, {
data: param,
prefix: roleApi,
});
}
/**
* 企业客户列表
* /yapi/project/389/interface/api/65324
*/
export async function apiEnterpriseList(param) {
return request.post(`${apiPrefix}/enterprise/pageList`, {
data: param,
prefix: roleApi,
});
}
/**
* 模糊查询店铺列表
* /yapi/project/389/interface/api/65289
*/
export async function apiShopList(param) {
return request.post(`${apiPrefix}/shops/getListByName`, {
data: param,
prefix: roleApi,
});
}
/**
* 企业团餐-查询餐段配置
* /yapi/project/389/interface/api/65444
*/
export async function apiMealTimePeriod(param) {
return request.post(`${apiPrefix}/product/enterprise/getMealTimePeriod`, {
data: param,
prefix: roleApi,
});
}
/**
* 企业团餐->信息修改
* /yapi/project/389/interface/api/65099
*/
export async function apiMealInfoUpdate(param) {
return request.post(`${apiPrefix}/product/enterprise/main/update`, {
data: param,
prefix: roleApi,
});
}
/**
* 企业团餐->删除外卖商品
* /yapi/project/389/interface/api/65109
*/
export async function apiTakeawayGoodsDel(param) {
return request.post(`${apiPrefix}/product/enterprise/main/deleteById`, {
data: param,
prefix: roleApi,
});
}
/**
* 企业团餐->删除虚拟商品
* /yapi/project/389/interface/api/65109
*/
export async function apiVirtualGoodsDel(param) {
return request.post(`${apiPrefix}/product/enterprise/virtual/deleteById`, {
data: param,
prefix: roleApi,
});
}
/**
* 企业团餐->查询自提点列表
* /yapi/project/389/interface/api/65494
*/
export async function apiPickSelfList() {
return request.get(`${apiPrefix}/selfPickUpLocation/list`, {
prefix: roleApi,
});
}
/**
* 企业团餐->根据企业ID查询已选择自提点
* /yapi/project/389/interface/api/65449
*/
export async function apiSelPickSelfList(param) {
return request.post(`${apiPrefix}/product/enterprise/queryByEnterpriseId`, {
data: param,
prefix: roleApi,
});
}
/**
* 企业团餐->查询外卖商品 - 选择商品弹窗
* /yapi/project/389/interface/api/65479
*/
export async function apiSelGoodsList(param) {
return request.post(`${apiPrefix}/product/enterprise/sku/page`, {
data: param,
prefix: roleApi,
});
}
/**
* 企业团餐->查询商品 - 选择虚拟商品弹窗
* /yapi/project/389/interface/api/65479
*/
export async function apiSelVirtualGoodsList(param) {
return request.post(`${apiPrefix}/product/enterprise/virtual/sku/page`, {
data: param,
prefix: roleApi,
});
}
/**
* 企业团餐->添加外卖商品保存
* /yapi/project/389/interface/api/65094
*/
export async function apiSaveGoodsList(param) {
return request.post(`${apiPrefix}/product/enterprise/add`, {
data: param,
prefix: roleApi,
});
}
/**
* 企业团餐->添加虚拟商品保存
* /yapi/project/389/interface/api/65484
*/
export async function apiSaveVirtualGoodsList(param) {
return request.post(`${apiPrefix}/product/enterprise/virtual/add`, {
data: param,
prefix: roleApi,
});
}
/**
* 企业团餐->虚拟品->根据企业ID查询店铺列表
* /yapi/project/389/interface/api/65504
*/
export async function apiShopListByEnterpriseID(param) {
return request.post(`${apiPrefix}/product/enterprise/virtual/shops`, {
data: param,
prefix: roleApi,
});
}
/**
* 企业团餐->根据企业ID+自提点列表查询店铺列表
* /yapi/project/389/interface/api/65539
*/
export async function apiShopListByPickSelfID(param) {
return request.post(`${apiPrefix}/product/enterprise/queryByEnterpriseIdAndPickSelfId`, {
data: param,
prefix: roleApi,
});
}
/**
* 企业团餐->根据企业ID 获取企业客户详细
* /yapi/project/389/interface/api/65339
*/
export async function apiEnterpriseInfo(id) {
return request.get(`${apiPrefix}/enterprise/info?id=${id}`, {
prefix: roleApi,
});
}
/**
* 企业团餐->虚拟商品 校验是否可修改餐段和可售日期
* /yapi/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;
import React from 'react';
import moment from 'moment';
import { PlusOutlined } from '@ant-design/icons';
import { notification, Tag, Tooltip, Input } from 'antd';
import {
uploadFile,
apiCategoryList,
apiAddrArea,
apiServiceFacility,
apiBusinessDetail,
apiBankList,
} from '../service';
export const wrapperCol = {
xs: { span: 24 },
sm: { span: 12 },
};
export const formItemLayout = {
labelCol: {
xs: { span: 24 },
sm: { span: 8 },
},
wrapperCol: {
xs: { span: 24 },
sm: { span: 16 },
},
};
export const businessTypeDesc = {
1: '到家业务',
2: '实物业务',
3: '到店业务',
};
// 业务模式
export const businessModel = [
{ label: '到家外卖业务(外卖配送业务)', value: 1 },
{ label: '实物商品业务员', value: 2 },
{ label: '到店业务(服务类业务)', value: 3 },
];
export const signDateTypeList = [{ label: '自商品售卖起默认一年', value: 1 }];
export const legalPersonList = [{ label: '长期', value: '长期' }];
// 获取申请信息
export async function getInfo() {
const data = await apiBusinessDetail();
let settlementType = 1;
const res = data.data;
if (res) {
if (res.facilities) {
res.customList = res.facilities.customList || [];
res.selfList = res.facilities.selfList || [];
}
res.signDateType = res.signDateType ? [res.signDateType] : [];
res.accountOpenPermitImage = res.accountOpenPermitImage
? [{ uid: 0, url: res.accountOpenPermitImage }]
: [];
res.businessLicenseImage = res.businessLicenseImage
? [{ uid: 0, url: res.businessLicenseImage }]
: [];
res.idCardEmblemImage = res.idCardEmblemImage ? [{ uid: 0, url: res.idCardEmblemImage }] : [];
res.idCardPortraitImage = res.idCardPortraitImage
? [{ uid: 0, url: res.idCardPortraitImage }]
: [];
res.primaryImage = res.primaryImage ? [{ uid: 0, url: res.primaryImage }] : [];
res.sealImage = res.sealImage ? [{ uid: 0, url: res.sealImage }] : [];
res.brandCertificate = res.brandCertificate ? [{ uid: 0, url: res.brandCertificate }] : [];
// 非同名结算授权文件
res.differentNameAuthorizationImage = res.differentNameAuthorizationImage
? [{ uid: 0, url: res.brandCertificate }]
: [];
// 户口本本人页
res.householdRegisterImage = res.householdRegisterImage
? [{ uid: 0, url: res.householdRegisterImage }]
: [];
// 工商局
res.icbProofImage = res.icbProofImage ? [{ uid: 0, url: res.icbProofImage }] : [];
if (res.categoryQualificateImage && res.categoryQualificateImage.length) {
res.categoryQualificateImage = res.categoryQualificateImage.map((item, index) => ({
uid: index,
url: item,
}));
}
if (res.otherImage && res.otherImage.length) {
res.otherImage = res.otherImage.map((item, index) => ({
uid: index,
url: item,
}));
}
res.addr = [res.provinceId, res.cityId, res.countyId];
if (res.townId) {
res.addr.push(res.townId);
}
if (settlementType !== null) {
settlementType = +res.settlementType;
}
}
// 身份证有效期处理
let checkboxDisabled = false;
if (res.legalPersonPeriod) {
const date = res.legalPersonPeriod.split('-') || [];
res.legalPersonStart = date[0] && moment(date[0]);
res.legalPersonEnd = date[1] && date[1] !== '长期' ? moment(date[1]) : '';
res.checked = date[1] && date[1] === '长期' ? ['长期'] : [];
checkboxDisabled = date[1] && date[1] === '长期';
}
// 营业执照
const businessDate =
res.businessLicensePeriod.indexOf('-') !== -1 ? res.businessLicensePeriod?.split('-') : [];
res.businessLicensePeriodBegin = businessDate[0] ? moment(businessDate[0]) : '';
res.businessLicensePeriodEnd = businessDate[1] ? moment(businessDate[1]) : '';
const companyNamedis = !!res.companyName;
// 获取主营类目
let categoryList = [];
if (res.productBusiness?.length) {
const dataList = await apiCategoryList(res.productBusiness);
categoryList = dataList.data;
}
this.setState({
settlementType,
businessInfo: res,
checkboxDisabled,
companyNamedis,
categoryList,
mainCategoryId: res.mainCategoryId,
companyType: res.companyType,
});
}
// 删除图片
function delImg(keyName, e) {
this.setState(state => {
if (keyName === 'categoryQualificateImage') {
state.businessInfo.categoryQualificateImage.forEach((item, index) => {
if (item.uid === e.uid) {
state.businessInfo.categoryQualificateImage.splice(index, 1);
}
});
} else {
state.businessInfo[keyName].splice(e.uid, 1);
}
return {
businessInfo: state.businessInfo,
};
});
}
// 上传图片
export function uploadPropsFn(
{ keyName, limit = 1, maxSize = 1024 * 1024 * 5, maxSizeMsg = '文件大小不能超过5M!', type = 0 },
successCallback = () => {},
) {
const that = this;
let timer = null;
return {
maxSize,
async customRequest(info) {
const { file } = info;
if (info.file.size > maxSize) {
notification.error({ message: maxSizeMsg });
return;
}
if (that.state.businessInfo[keyName] && that.state.businessInfo[keyName].length >= limit) {
notification.error({ message: `最多只能上传${limit}个文件!` });
return;
}
const res = await uploadFile([file], type);
if (res.businessCode !== '0000') {
return;
}
const url = res.data?.[0];
that.setState(state => {
const imgList = state.businessInfo[keyName] || [];
const attachment = {
uid: imgList.length || 0,
name: file?.name,
status: 'done',
url,
};
imgList.push(attachment);
if (imgList.length <= limit) {
state.businessInfo[keyName] = imgList;
} else {
notification.error({ message: `最多只能上传${limit}个文件!` });
}
return {
businessInfo: state.businessInfo,
};
});
successCallback(keyName, url);
// 需要把上传的数据存储下来
},
disabled: this.state.disabled,
multiple: limit > 1,
listType: 'picture-card',
showUploadList: true,
accept: '.png,.bmp,.gif,.jpeg,.jpg',
onRemove: e => delImg.call(this, keyName, e),
beforeUpload: (e, flist) => {
const imgList = that.state.businessInfo[keyName] || [];
if (flist.length + imgList.length > limit) {
clearTimeout(timer);
timer = setTimeout(() => {
notification.error({ message: `最多只能上传${limit}个文件!` });
}, 100);
return false;
}
return true;
},
};
}
/** **** 自定义服务设施 start ***************************** */
// 关闭标签输入框
function handleClose(removedTag) {
this.setState(state => {
state.businessInfo.customList = state.businessInfo.customList.filter(tag => tag !== removedTag);
return {
businessInfo: state.businessInfo,
};
});
}
// 显示标签输入框
function showInput() {
this.setState({ tagVisible: true }, () => this.input.focus());
}
// 标签内容改变
function handleInputChange(e) {
if (e && e.target) {
this.setState({ tagValue: e.target.value });
}
}
// 保存标签
function handleInputConfirm() {
const { tagValue } = this.state;
if (tagValue) {
let { customList } = this.state.businessInfo;
if (tagValue && customList.indexOf(tagValue) === -1) {
customList = [...customList, tagValue];
}
this.setState(state => {
state.businessInfo.customList = customList;
return {
businessInfo: state.businessInfo,
tagVisible: false,
tagValue: '',
};
});
}
}
// 渲染标签
export function renderServiceTags() {
const that = this;
return (
<div>
{that.state.businessInfo.customList &&
that.state.businessInfo.customList.map(tag => {
const isLongTag = tag.length > 20;
const tagElem = (
// 二期放开
<Tag key={tag} closable onClose={() => handleClose.call(that, tag)}>
{isLongTag ? `${tag.slice(0, 20)}...` : tag}
</Tag>
);
return isLongTag ? (
<Tooltip title={tag} key={tag}>
{tagElem}
</Tooltip>
) : (
tagElem
);
})}
{that.state.tagVisible && (
<Input
// eslint-disable-next-line react/no-this-in-sfc
ref={this.saveInputRef}
type="text"
size="small"
style={{ width: 78 }}
value={that.state.tagValue}
onChange={e => handleInputChange.call(that, e)}
onBlur={e => handleInputConfirm.call(that, e)}
onPressEnter={e => handleInputConfirm.call(that, e)}
/>
)}
{/* 二期放开 */}
{!that.state.tagVisible && (
<Tag
onClick={() => showInput.call(that)}
style={{ background: '#fff', borderStyle: 'dashed' }}
>
<PlusOutlined /> 新增
</Tag>
)}
</div>
);
}
/** **** 自定义服务设施 end ***************************** */
// 获取服务设施
export async function getServiceFacility() {
const res = await apiServiceFacility();
if (res.data) {
this.setState({
serviceFacilitys: res.data,
});
}
}
// 开户行
export async function getBankList() {
const res = await apiBankList();
if (res.data) {
this.setState({
bankList: res.data,
});
}
}
// 获取地址省
export async function getAreaAddr() {
const data = await apiAddrArea();
const areaAddr = data.data || null;
if (areaAddr) {
const res = areaAddr.map(item => ({
isLeaf: false,
loading: false,
label: item.addrName,
value: item.addrId,
}));
// 编辑时 回显 市、区、街道
if (this.state.businessInfo.provinceId) {
const pros = res.filter(item => +item.value === +this.state.businessInfo.provinceId);
await loadData.call(this, pros);
const citys = pros[0].children.filter(
item => +item.value === +this.state.businessInfo.cityId,
);
await loadData.call(this, citys);
if (this.state.businessInfo.townId) {
const countys = citys[0].children.filter(
item => +item.value === +this.state.businessInfo.countyId,
);
await loadData.call(this, countys);
}
}
this.setState({
areaAddr: res,
});
}
}
// 获取市区街道
export async function loadData(selectedOptions) {
const targetOption = selectedOptions[selectedOptions.length - 1];
targetOption.loading = true;
const data = await apiAddrArea({
parentId: targetOption.value,
});
const areaAddr = data.data || null;
if (areaAddr) {
const children = areaAddr.map(item => ({
isLeaf: +item.addrLevel === 4,
loading: false,
label: item.addrName,
value: item.addrId,
}));
targetOption.loading = false;
if (children.length > 0) {
targetOption.children = children;
} else {
targetOption.isLeaf = true;
}
this.setState({
// eslint-disable-next-line react/no-access-state-in-setstate
areaAddr: [...this.state.areaAddr],
});
}
}
import React, { Component } from 'react';
import { history } from 'umi';
import moment from 'moment';
import { ArrowRightOutlined, UploadOutlined } from '@ant-design/icons';
import { Form } from '@ant-design/compatible';
import '@ant-design/compatible/assets/index.css';
import {
Select,
Input,
Radio,
Checkbox,
DatePicker,
Upload,
Button,
Card,
Row,
Col,
Cascader,
notification,
Spin,
Tabs,
} from 'antd';
import styles from './style.less';
import {
wrapperCol,
uploadPropsFn,
formItemLayout,
renderServiceTags,
loadData,
getAreaAddr,
getServiceFacility,
businessModel,
getInfo,
getBankList,
businessTypeDesc,
signDateTypeList,
legalPersonList,
} from './data';
import { validatePhone, validateEMail, validNumber } from '@/utils/validator';
import { getErrorMessage } from '@/utils/utils';
import MapModal from '@/components/BaiduMap';
import { apiCategoryList, apiEditStoreInfo, apiRecognize } from '../service';
const { Option } = Select;
const { Item: FormItem } = Form;
const { RangePicker } = DatePicker;
const { TabPane } = Tabs;
const carID = 301008; // 汽车类商家
const fileterBrandOptions = (input, options) => options.children.includes(input);
class BusinessInfo extends Component {
getFieldDecorator = this.props?.form?.getFieldDecorator;
setFieldsValue = this.props?.form?.setFieldsValue;
state = {
id: null,
businessInfo: {
customList: [],
settlementType: 1,
companyType: '',
},
areaAddr: [], // 四级地址列表
categoryList: [], // 主要类目
serviceFacilitys: [], // 服务设施
bankList: [], // 开户行
settlementType: 1,
// loading: false,
visibleLoading: false,
checkboxDisabled: false,
mainCategoryId: null,
companyNamedis: false, // 公司名称
companyType: null, // 企业类型
disabled: false, // 二期放开
};
async componentDidMount() {
await getInfo.call(this);
getAreaAddr.call(this);
getServiceFacility.call(this);
getBankList.call(this);
}
// 主营类目
onMainCategory(e) {
this.setState({
mainCategoryId: e,
});
}
// 长期
oncheckedChange = e => {
const str = e.toString();
this.setState({
checkboxDisabled: str === '长期',
});
this.props.form.setFieldsValue({
legalPersonEnd: '',
});
};
// 计费类型改变
onChangeCalculateType = (e, i) => {
this.setState(state => {
const { calculateType } = state;
calculateType[i] = +e;
return {
calculateType,
};
});
};
static getDerivedStateFromProps() {
return null;
}
// 帐号类型切换
onChangeSettlType(e) {
this.setState({
settlementType: e,
});
}
// 切换企业类型
onChangeCompanyType = e => {
const { value } = e.target;
this.setState({
companyType: value,
});
let type = null;
if ([1, 3].includes(value)) {
type = 1;
} else {
type = 2;
}
this.onChangeSettlType(type);
this.props.form.setFieldsValue({
settlementType: type,
});
};
// 获取主营类目
async getCategoryList(e) {
const data = await apiCategoryList(e);
if (!data.data) return;
this.setState({ categoryList: data.data });
}
// 返回
onCancel = () => {
getInfo.call(this);
};
// 提交
handleSubmit = e => {
e.preventDefault();
this.props.form.validateFields(async (err, values) => {
const obj = Object.assign({}, values); // , this.state.businessInfo
if (!err) {
const imgs = this.state.businessInfo;
// 营业执照
// obj.businessLicensePeriod = `${moment(obj.businessLicensePeriodBegin).format(
// 'YYYY-MM-DD',
// )}-${moment(obj.businessLicensePeriodEnd).format('YYYY-MM-DD')}`;
// 身份证有效期
const legalPersonStart = moment(obj.legalPersonStart).format('YYYY/MM/DD');
const legalPersonEnd = this.state.checkboxDisabled
? '长期'
: moment(obj.legalPersonEnd).format('YYYY/MM/DD');
obj.legalPersonPeriod = `${legalPersonStart}-${legalPersonEnd}`;
obj.facilities = {
customList: this.state.businessInfo.customList,
selfList: obj.selfList,
};
const areaArr = ['provinceId', 'cityId', 'countyId', 'townId'];
obj.addr.forEach((item, i) => {
obj[areaArr[i]] = item;
});
obj.accountOpenPermitImage =
(imgs?.accountOpenPermitImage?.length && imgs.accountOpenPermitImage[0].url) || '';
if (obj.supplierRateDTOList && obj.supplierRateDTOList.length) {
obj.supplierRateDTOList.forEach(item => {
if (item.ratedate && item.ratedate.length) {
item.startDate = moment(item.ratedate[0]).format('YYYY-MM-DD');
item.endDate = moment(item.ratedate[1]).format('YYYY-MM-DD');
delete item.ratedate;
}
});
}
obj.businessLicenseImage =
(imgs?.businessLicenseImage?.length && imgs?.businessLicenseImage[0].url) || '';
obj.idCardEmblemImage =
(imgs?.idCardEmblemImage?.length && imgs?.idCardEmblemImage[0].url) || '';
obj.idCardPortraitImage =
(imgs?.idCardPortraitImage?.length && imgs?.idCardPortraitImage[0].url) || '';
obj.primaryImage = (imgs?.primaryImage?.length && imgs?.primaryImage[0].url) || '';
obj.sealImage = (imgs?.sealImage?.length && imgs?.sealImage[0].url) || '';
// 新增
obj.brandCertificate =
(imgs?.brandCertificate?.length && imgs?.brandCertificate[0]?.url) || '';
obj.differentNameAuthorizationImage =
(imgs?.differentNameAuthorizationImage?.length &&
imgs?.differentNameAuthorizationImage[0]?.url) ||
'';
obj.householdRegisterImage =
(imgs?.householdRegisterImage?.length && imgs?.householdRegisterImage[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;
if (imgs.otherImage && imgs.otherImage.length) {
obj.otherImage = imgs.otherImage.map(item => item.url);
} else {
delete obj.otherImage;
}
if (imgs.categoryQualificateImage && imgs.categoryQualificateImage.length) {
const list = [];
imgs.categoryQualificateImage.forEach(item => {
list.push(item.url);
});
obj.categoryQualificateImage = list;
} else {
delete obj.categoryQualificateImage;
}
obj.legalPersonIdCard = obj.legalPersonIdCard?.toLocaleUpperCase() || '';
obj.bankAccountLicenseNum = obj.bankAccountLicenseNum?.toLocaleUpperCase() || '';
obj.socialCode = obj.socialCode?.toLocaleUpperCase() || '';
obj.id = this.state.id;
obj.headImage = obj.primaryImage;
// this.setState({
// loading: true,
// });
const data = await apiEditStoreInfo(obj);
if (data.businessCode === '0000') {
notification.success({ message: `${data.msg}!~` });
}
// this.setState({
// loading: false,
// });
} else {
const message = getErrorMessage(err);
notification.warning({ message });
}
});
};
onSetPoint = e => {
this.setFieldsValue({
latitude: e.lat,
longitude: e.lng,
});
};
// 修改公司名称
setCompanyName = () => {
const val = this.state.companyNamedis;
this.setState({
companyNamedis: !val,
});
if (!val) {
this.props.form.setFieldsValue({
companyName: this.state.businessInfo.companyName,
icbProofImage: '',
householdRegisterImage: '',
});
}
};
onChangeBusinessModel = e => {
this.getCategoryList(e);
this.props.form.setFieldsValue({
mainCategoryId: '',
});
};
/* eslint-disable no-return-assign */
saveInputRef = input => (this.input = input);
// 处理图片
dealImgInfo = async (type, url) => {
const imgType = {
businessLicenseImage: 2,
idCardPortraitImage: 5,
idCardEmblemImage: 4,
};
if (!url) {
return;
}
this.setState({
visibleLoading: true,
});
const data = await apiRecognize({
imageUrl: url,
imageType: imgType[type],
});
if (data.businessCode === '0000') {
const res = data.data;
const info = {};
this.setState(state => {
if (type === 'businessLicenseImage') {
info.socialCode = res.socialCode || '';
info.companyName = res.companyName || '';
info.businessLicensePeriod = res.businessLicensePeriodBegin;
if (res.businessLicensePeriodEnd) {
if (info.businessLicensePeriod) {
info.businessLicensePeriod += '-';
}
info.businessLicensePeriod += res.businessLicensePeriodEnd;
}
// info.businessLicensePeriodBegin = res.businessLicensePeriodBegin
// ? moment(res.businessLicensePeriodBegin)
// : '';
// info.businessLicensePeriodEnd = res.businessLicensePeriodEnd
// ? moment(res.businessLicensePeriodEnd)
// : '';
} else if (type === 'idCardPortraitImage') {
info.legalPersonName = res.legalPersonName || '';
info.legalPersonIdCard = res.legalPersonIdCard || '';
} else if (type === 'idCardEmblemImage' && res.legalPersonPeriod) {
// 身份证有效期处理
const date = res.legalPersonPeriod.split('-') || [];
info.legalPersonStart = date[0] && moment(date[0]);
info.legalPersonEnd = date[1] && date[1] !== '长期' ? moment(date[1]) : '';
info.checked = date[1] && date[1] === '长期' ? ['长期'] : [];
const checkboxDisabled = date[1] && date[1] === '长期';
this.setState({
checkboxDisabled,
});
// info.legalPersonPeriod = res.legalPersonPeriod
// .replace(/\./g, '/')
// .split('-')
// .map(o => moment(o));
}
return {
businessInfo: Object.assign(state.businessInfo, info),
};
});
this.props.form.setFieldsValue({
...info,
});
} else {
notification.error({ message: data.msg });
}
this.setState({
visibleLoading: false,
});
};
render() {
const {
form: { getFieldDecorator },
} = this.props;
const {
businessInfo,
type,
checkboxDisabled,
mainCategoryId,
companyNamedis,
companyType,
disabled,
} = this.state;
return (
<div className={styles.infoBox}>
<Form className="login-form" onSubmit={this.handleSubmit} {...formItemLayout}>
<Card title="店铺基本信息">
<Row gutter={24}>
<Col span={24}>
<FormItem label="业务模式" labelCol={{ span: 4 }}>
{getFieldDecorator('productBusiness', {
rules: [{ required: true, message: '请选择业务模式!' }],
initialValue: businessInfo.productBusiness,
})(
<Checkbox.Group
options={businessModel}
onChange={e => this.onChangeBusinessModel(e)}
disabled={+mainCategoryId === carID || disabled}
/>,
)}
</FormItem>
</Col>
<Col span={24}>
<FormItem label="主营类目" labelCol={{ span: 4 }}>
{getFieldDecorator('mainCategoryId', {
rules: [{ required: true, message: '请选择主营类目!' }],
initialValue: businessInfo.mainCategoryId,
})(
<Select
onChange={e => this.onMainCategory(e)}
showSearch
filterOption={(input, option) =>
option.props.children.toLowerCase().indexOf(input.toLowerCase()) >= 0
}
disabled={+businessInfo.mainCategoryId === 301008 || disabled}
>
{this.state.categoryList.map(item => (
<Option value={item.id} key={item.id}>
{item.name}
</Option>
))}
</Select>,
)}
</FormItem>
</Col>
<Col span={24}>
<FormItem label="商户名称" labelCol={{ span: 4 }}>
{getFieldDecorator('name', {
rules: [{ required: true, message: '请输入商户名称!' }],
initialValue: businessInfo.name,
})(<Input maxLength={32} disabled={disabled} />)}
</FormItem>
</Col>
<Col span={24}>
<FormItem label="店铺区域" labelCol={{ span: 4 }}>
{getFieldDecorator('addr', {
rules: [{ required: true, type: 'array', message: '请选择店铺区域!' }],
initialValue: businessInfo.addr,
})(
<Cascader
options={this.state.areaAddr}
loadData={e => loadData.call(this, e)}
changeOnSelect
disabled={disabled}
/>,
)}
</FormItem>
</Col>
<Col span={24}>
<FormItem label="经营地址" labelCol={{ span: 4 }}>
{getFieldDecorator('detailAddress', {
rules: [{ required: true, message: '请输入经营地址!' }],
initialValue: businessInfo.detailAddress,
})(<Input maxLength={500} disabled={disabled} />)}
</FormItem>
</Col>
<Col span={12}>
<FormItem label="联系人姓名" labelCol={{ span: 8 }}>
{getFieldDecorator('contactName', {
rules: [
{ required: true, message: '请输入联系人姓名!' },
{ pattern: /^[A-Za-z\u4e00-\u9fa5]+$/, message: '请输入汉字或字母!' },
],
initialValue: businessInfo.contactName,
})(<Input disabled={disabled} />)}
</FormItem>
</Col>
<Col span={12}>
<FormItem label="联系人手机号" labelCol={{ span: 5 }}>
{getFieldDecorator('contactPhone', {
rules: [
{ required: true, message: '请输入联系人手机号!' },
{ validator: validatePhone, message: '请输入正确的手机号!' },
],
initialValue: businessInfo.contactPhone,
})(<Input maxLength={11} disabled={disabled} />)}
</FormItem>
</Col>
<Col span={12}>
<FormItem label="联系人邮箱" labelCol={{ span: 8 }}>
{getFieldDecorator('contactEmail', {
rules: [
{ required: true, message: '请输入联系人邮箱!' },
{ validator: validateEMail, message: '请输入正确的邮箱!' },
],
initialValue: businessInfo.contactEmail,
})(<Input disabled={disabled} />)}
</FormItem>
</Col>
{+mainCategoryId !== carID && (
<>
<Col span={12}>
<FormItem label="客服电话" labelCol={{ span: 5 }}>
{getFieldDecorator('servicePhone', {
rules: [{ required: true, message: '请输入客服电话!' }],
initialValue: businessInfo.servicePhone,
})(<Input maxLength={11} disabled={disabled} />)}
</FormItem>
</Col>
<Col span={24}>
<FormItem label="签约日期" labelCol={{ span: 4 }}>
{getFieldDecorator('signDateType', {
rules: [{ required: true, message: '请选择签约日期' }],
initialValue: businessInfo.signDateType,
})(<Checkbox.Group options={signDateTypeList} disabled={disabled} />)}
</FormItem>
</Col>
</>
)}
</Row>
</Card>
<Card title="店铺营业信息">
<Col span={12} className={styles.imgList}>
<FormItem label="商户头图" labelCol={{ span: 8 }} wrapperCol={wrapperCol}>
{getFieldDecorator('primaryImage', {
rules: [{ required: true, message: '请上传商户头图!' }],
initialValue: businessInfo.primaryImage,
})(
<Upload
{...uploadPropsFn.call(this, { keyName: 'primaryImage', type: 1 })}
fileList={businessInfo.primaryImage}
disabled={disabled}
>
<UploadOutlined /> 上传文件
</Upload>,
)}
</FormItem>
</Col>
{+mainCategoryId !== carID && (
<>
<Col span={24}>
<FormItem label="服务设施" labelCol={{ span: 4 }}>
{getFieldDecorator('selfList', {
initialValue: businessInfo.selfList,
})(
<Select
mode="multiple"
filterOption={fileterBrandOptions}
disabled={disabled}
>
{this.state.serviceFacilitys.map(item => (
<Option value={item.code} key={item.code}>
{item.desc}
</Option>
))}
</Select>,
)}
</FormItem>
</Col>
<Col span={24}>
<FormItem label="自定义服务设施" labelCol={{ span: 4 }}>
{getFieldDecorator('customList', {
initialValue: businessInfo.customList,
})(renderServiceTags.call(this))}
</FormItem>
</Col>
</>
)}
</Card>
<Card title="证照信息录入区">
<Row gutter={24}>
{+mainCategoryId !== carID && (
<Col span={24}>
<FormItem label="企业类型" labelCol={{ span: 4 }}>
{getFieldDecorator('companyType', {
rules: [{ required: true, message: '请选择企业类型!' }],
initialValue: businessInfo.companyType,
})(
<Radio.Group
onChange={this.onChangeCompanyType}
disabled={businessInfo.companyType || disabled}
>
<Radio value={1}>一般纳税人</Radio>
<Radio value={2}>小规模</Radio>
<Radio value={3}>个体工商</Radio>
</Radio.Group>,
)}
</FormItem>
</Col>
)}
<Col span={24} className={styles.imgList}>
<FormItem label="营业执照" labelCol={{ span: 4 }} wrapperCol={wrapperCol}>
{getFieldDecorator('businessLicenseImage', {
rules: [{ required: true, message: '请上传营业执照!' }],
initialValue: businessInfo.businessLicenseImage,
})(
<Upload
{...uploadPropsFn.call(
this,
{ keyName: 'businessLicenseImage', type: 2 },
this.dealImgInfo,
)}
fileList={businessInfo.businessLicenseImage}
disabled={disabled}
>
<UploadOutlined /> 上传文件
</Upload>,
)}
</FormItem>
</Col>
{+mainCategoryId !== carID && (
<>
<Col span={12}>
<Row>
<Col span={4} style={{ marginTop: '4px' }}>
{/* 二期放开 */}
{businessInfo.companyName && (
<Button onClick={this.setCompanyName}>
{this.state.companyNamedis ? '修改' : '取消'}
</Button>
)}
</Col>
<Col span={12}>
<FormItem label="公司名称" labelCol={{ span: 8 }}>
{getFieldDecorator('companyName', {
rules: [{ required: true, message: '请输入公司名称!' }],
initialValue: businessInfo.companyName,
})(<Input maxLength={50} disabled={companyNamedis || disabled} />)}
</FormItem>
</Col>
</Row>
</Col>
<Col span={12}>
<FormItem label="统一社会信用代码" labelCol={{ span: 8 }}>
{getFieldDecorator('socialCode', {
rules: [
{ required: true, message: '请输入统一社会信用代码!' },
{ pattern: /^[A-Za-z0-9]*$/, message: '仅支持输入数字和字母' },
],
initialValue: businessInfo.socialCode,
})(<Input maxLength={18} disabled={disabled} />)}
</FormItem>
</Col>
{(!companyNamedis &&
businessInfo.companyType &&
businessInfo.companyType !== 2) ||
(!companyNamedis && businessInfo?.icbProofImage?.length) ? (
<Col span={12}>
<FormItem
label="工商局变更证明"
labelCol={{ span: 8 }}
wrapperCol={wrapperCol}
>
{getFieldDecorator('icbProofImage', {
rules: [{ required: true, message: '请上传工商局变更证明!' }],
initialValue: businessInfo.icbProofImage,
})(
<Upload
{...uploadPropsFn.call(this, {
keyName: 'icbProofImage',
limit: 1,
})}
fileList={businessInfo.icbProofImage}
disabled={disabled}
>
<UploadOutlined /> 上传文件
</Upload>,
)}
</FormItem>
</Col>
) : (
''
)}
{(!companyNamedis &&
businessInfo.companyType &&
businessInfo.companyType === 2) ||
(!companyNamedis && businessInfo?.householdRegisterImage?.length) ? (
<Col span={12}>
<FormItem label="户口本本人页" labelCol={{ span: 8 }} wrapperCol={wrapperCol}>
{getFieldDecorator('householdRegisterImage', {
rules: [{ required: true, message: '请上传户口本本人页!' }],
initialValue: businessInfo.householdRegisterImage,
})(
<Upload
{...uploadPropsFn.call(this, {
keyName: 'householdRegisterImage',
limit: 1,
})}
fileList={businessInfo.householdRegisterImage}
disabled={disabled}
>
<UploadOutlined /> 上传文件
</Upload>,
)}
</FormItem>
</Col>
) : (
''
)}
<Col span={24}>
<FormItem label="营业执照有效期" labelCol={{ span: 4 }} wrapperCol={wrapperCol}>
{getFieldDecorator('businessLicensePeriod', {
rules: [{ required: true, message: '请输入营业执照有效期!' }],
initialValue: businessInfo.businessLicensePeriod,
})(
<Input
style={{ width: '200px' }}
placeholder="请输入营业执照有效期"
disabled={disabled}
/>,
)}
</FormItem>
</Col>
{/* <Col span={12}>
<FormItem
label="营业执照执照结束日期"
labelCol={{ span: 12 }}
wrapperCol={{ span: 12 }}
>
{getFieldDecorator('businessLicensePeriodEnd', {
rules: [{ required: true, message: '请选择营业执照结束日期!' }],
initialValue: businessInfo.businessLicensePeriodEnd,
})(
<DatePicker
style={{ width: '200px' }}
placeholder="请选择营业执照结束日期"
disabled={disabled}
/>,
)}
</FormItem>
</Col> */}
<Col span={24}>
<FormItem label="类目所需资质" labelCol={{ span: 4 }} wrapperCol={wrapperCol}>
{getFieldDecorator('categoryQualificateImage', {
initialValue: businessInfo.categoryQualificateImage,
})(
<Upload
{...uploadPropsFn.call(this, {
keyName: 'categoryQualificateImage',
type: 6,
limit: 9,
})}
fileList={businessInfo.categoryQualificateImage}
disabled={disabled}
>
<UploadOutlined /> 上传文件
</Upload>,
)}
</FormItem>
</Col>
<Col span={24}>
<FormItem label="电子签章" labelCol={{ span: 4 }} wrapperCol={wrapperCol}>
{getFieldDecorator('sealImage', {
rules: [{ required: true, message: '请上传电子签章!' }],
initialValue: businessInfo.sealImage,
})(
<Upload
{...uploadPropsFn.call(this, {
keyName: 'sealImage',
type: 3,
maxSize: 1024 * 200,
maxSizeMsg: '文件大小不能超过200K',
limit: 1,
})}
fileList={businessInfo.sealImage}
disabled={disabled}
>
<UploadOutlined /> 上传文件
</Upload>,
)}
</FormItem>
</Col>
</>
)}
{+mainCategoryId === carID && (
<>
<Col span={24}>
<FormItem label="授权品牌" labelCol={{ span: 4 }}>
{getFieldDecorator('brand', {
rules: [{ required: true, message: '请输入授权品牌!' }],
initialValue: businessInfo.brand,
})(<Input maxLength={32} disabled={disabled} />)}
</FormItem>
</Col>
<Col span={24}>
<FormItem
label="品牌经销商授权书"
labelCol={{ span: 4 }}
wrapperCol={wrapperCol}
>
{getFieldDecorator('brandCertificate', {
rules: [{ required: true, message: '请上传品牌经销商授权书!' }],
initialValue: businessInfo.brandCertificate,
})(
<Upload
{...uploadPropsFn.call(this, {
keyName: 'brandCertificate',
limit: 1,
})}
fileList={businessInfo.brandCertificate}
disabled={disabled}
>
<UploadOutlined /> 上传文件
</Upload>,
)}
</FormItem>
</Col>
</>
)}
</Row>
</Card>
<Card title="法人信息录入区">
<Row gutter={24}>
{+mainCategoryId !== carID && (
<>
<Col span={12} className={styles.imgList}>
<FormItem
label="法人身份证人像面"
labelCol={{ span: 8 }}
wrapperCol={wrapperCol}
>
{getFieldDecorator('idCardPortraitImage', {
rules: [{ required: true, message: '请上传身份证人像面!' }],
initialValue: businessInfo.idCardPortraitImage,
})(
<Upload
{...uploadPropsFn.call(
this,
{ keyName: 'idCardPortraitImage', type: 5 },
this.dealImgInfo,
)}
fileList={businessInfo.idCardPortraitImage}
disabled={disabled}
>
<UploadOutlined />
<div>身份证人像面</div>
</Upload>,
)}
</FormItem>
</Col>
<Col span={12} className={styles.imgList}>
<FormItem
label="法人身份证国徽面"
labelCol={{ span: 8 }}
wrapperCol={wrapperCol}
>
{getFieldDecorator('idCardEmblemImage', {
rules: [{ required: true, message: '请上传身份证国徽面!' }],
initialValue: businessInfo.idCardEmblemImage,
})(
<Upload
{...uploadPropsFn.call(
this,
{ keyName: 'idCardEmblemImage', type: 4 },
this.dealImgInfo,
)}
fileList={businessInfo.idCardEmblemImage}
disabled={disabled}
>
<UploadOutlined />
<div>身份证国徽面</div>
</Upload>,
)}
</FormItem>
</Col>
<Col span={12}>
<FormItem label="证件姓名" labelCol={{ span: 8 }}>
{getFieldDecorator('legalPersonName', {
rules: [{ required: true, message: '请输入证件姓名!' }],
initialValue: businessInfo.legalPersonName,
})(<Input maxLength={8} disabled={disabled} />)}
</FormItem>
</Col>
<Col span={12}>
<FormItem label="身份证号码" labelCol={{ span: 8 }}>
{getFieldDecorator('legalPersonIdCard', {
rules: [
{ required: true, message: '请输入身份证号码!' },
{ pattern: /^[A-Za-z0-9]*$/, message: '请输入数字或字母' },
],
initialValue: businessInfo.legalPersonIdCard,
})(<Input maxLength={18} disabled={disabled} />)}
</FormItem>
</Col>
<Col span={12}>
<FormItem label="身份证起始有效期" labelCol={{ span: 8 }}>
{getFieldDecorator('legalPersonStart', {
rules: [{ required: true, message: '身份证起始有效期!' }],
initialValue: businessInfo.legalPersonStart,
})(<DatePicker style={{ width: '200px' }} disabled={disabled} />)}
</FormItem>
</Col>
<Col span={12}>
<Row gutter={24}>
<Col span={19}>
<FormItem
label="身份证结束有效期"
labelCol={{ span: 12 }}
wrapperCol={{ span: 12 }}
>
{getFieldDecorator('legalPersonEnd', {
rules: [{ required: !checkboxDisabled, message: '身份证结束有效期!' }],
initialValue: businessInfo.legalPersonEnd,
})(
<DatePicker
style={{ width: '200px' }}
disabled={checkboxDisabled || disabled}
/>,
)}
</FormItem>
</Col>
<Col span={5}>
<FormItem>
{getFieldDecorator('checked', {
initialValue: businessInfo.checked,
})(
<Checkbox.Group
options={legalPersonList}
className={styles.radio}
onChange={e => this.oncheckedChange(e)}
disabled={disabled}
/>,
)}
</FormItem>
</Col>
</Row>
</Col>
</>
)}
<Col span={12}>
<FormItem label="手机号" labelCol={{ span: 8 }}>
{getFieldDecorator('legalPersonPhone', {
rules: [
{ required: true, message: '请输入手机号!' },
{ validator: validatePhone, message: '请输入正确的手机号!' },
],
initialValue: businessInfo.legalPersonPhone,
})(<Input maxLength={11} disabled={disabled} />)}
</FormItem>
</Col>
{+mainCategoryId !== carID && (
<Col span={24}>
<FormItem label="其它证照" labelCol={{ span: 4 }} wrapperCol={wrapperCol}>
{getFieldDecorator('otherImage', {
initialValue: businessInfo.otherImage,
})(
<Upload
{...uploadPropsFn.call(this, { keyName: 'otherImage', limit: 9, type: 7 })}
fileList={businessInfo.otherImage}
disabled={disabled}
>
<UploadOutlined /> 上传文件
</Upload>,
)}
</FormItem>
</Col>
)}
</Row>
</Card>
<Card title="结算信息录入区">
{+mainCategoryId !== carID && (
<>
<Row gutter={24}>
<Col span={24}>
<FormItem label="账户类型" labelCol={{ span: 4 }} wrapperCol={wrapperCol}>
{getFieldDecorator('settlementType', {
rules: [{ required: true, message: '请选择账户类型!' }],
initialValue: businessInfo.settlementType,
})(
<Radio.Group
onChange={e => this.onChangeSettlType(e?.target?.value)}
disabled={disabled}
>
<Radio value={1} disabled={[2].includes(companyType)}>
对公
</Radio>
<Radio value={2} disabled={[1, 3].includes(companyType)}>
对私(小微商户)
</Radio>
</Radio.Group>,
)}
</FormItem>
</Col>
</Row>
{this.state.settlementType === 1 && (
<Row gutter={24}>
<Col span={12}>
<FormItem label="开户许可证" labelCol={{ span: 8 }} wrapperCol={wrapperCol}>
{getFieldDecorator('accountOpenPermitImage', {
rules: [{ required: true, message: '请上传开户许可证!' }],
initialValue: businessInfo.accountOpenPermitImage,
})(
<Upload
{...uploadPropsFn.call(this, {
keyName: 'accountOpenPermitImage',
type: 8,
})}
fileList={businessInfo.accountOpenPermitImage}
disabled={disabled}
>
<UploadOutlined /> 上传文件
</Upload>,
)}
</FormItem>
</Col>
<Col span={12}>
<FormItem label="开户许可证编号" labelCol={{ span: 8 }}>
{getFieldDecorator('bankAccountLicenseNum', {
rules: [
{ required: true, message: '请输入开户许可证编号!' },
{ pattern: /^[A-Za-z0-9]*$/, message: '请输入数字或字母' },
],
initialValue: businessInfo.bankAccountLicenseNum,
})(<Input disabled={disabled} />)}
</FormItem>
</Col>
<Col span={12}>
<FormItem label="开户名称" labelCol={{ span: 8 }}>
{getFieldDecorator('bankAccountName', {
rules: [{ required: true, message: '请输入开户名称!' }],
initialValue: businessInfo.bankAccountName,
})(<Input maxLength={32} disabled={disabled} />)}
</FormItem>
</Col>
<Col span={12}>
<FormItem label="开户行" labelCol={{ span: 8 }}>
{getFieldDecorator('accountBankName', {
rules: [{ required: true, message: '请选择开户行!' }],
initialValue: businessInfo.accountBankName,
})(
<Select
showSearch
filterOption={(input, option) =>
(option?.value ?? '').toLowerCase().includes(input.toLowerCase())
}
disabled={disabled}
>
{this.state.bankList.map(item => (
<Option value={item.bankName} key={item.bankName}>
{item.bankName}
</Option>
))}
</Select>,
)}
</FormItem>
</Col>
<Col span={12}>
<FormItem label="银行帐号" labelCol={{ span: 8 }}>
{getFieldDecorator('bankAccount', {
rules: [
{ required: true, message: '请输入银行帐号!' },
{ pattern: /^[0-9]*$/, message: '请输入数字' },
],
initialValue: businessInfo.bankAccount,
})(<Input disabled={disabled} />)}
</FormItem>
</Col>
<Col span={12}>
<FormItem label="联行号" labelCol={{ span: 8 }}>
{getFieldDecorator('interbankNum', {
rules: [{ pattern: /^[0-9]*$/, message: '请输入数字' }],
initialValue: businessInfo.interbankNum,
})(<Input maxLength={50} disabled={disabled} />)}
</FormItem>
</Col>
<Col span={24}>
<FormItem
label="非同名结算授权文件"
labelCol={{ span: 4 }}
wrapperCol={wrapperCol}
>
{getFieldDecorator('differentNameAuthorizationImage', {
initialValue: businessInfo.differentNameAuthorizationImage,
})(
<Upload
{...uploadPropsFn.call(this, {
keyName: 'differentNameAuthorizationImage',
limit: 1,
})}
fileList={businessInfo.differentNameAuthorizationImage}
disabled={disabled}
>
<UploadOutlined /> 上传文件
</Upload>,
)}
</FormItem>
</Col>
</Row>
)}
{this.state.settlementType === 2 && (
<Row gutter={24}>
<Col span={12}>
<FormItem label="银行卡类型" labelCol={{ span: 8 }}>
{getFieldDecorator('bankAccountType', {
rules: [{ required: true, message: '请选择银行卡类型!' }],
initialValue: businessInfo.bankAccountType,
})(
<Select disabled={disabled}>
<Option value={1} key={1}>
借记卡
</Option>
</Select>,
)}
</FormItem>
</Col>
<Col span={12}>
<FormItem label="开户名称" labelCol={{ span: 8 }}>
{getFieldDecorator('bankAccountName', {
rules: [{ required: true, message: '请输入开户名称!' }],
initialValue: businessInfo.bankAccountName,
})(<Input maxLength={32} disabled={disabled} />)}
</FormItem>
</Col>
<Col span={12}>
<FormItem label="开户行" labelCol={{ span: 8 }}>
{getFieldDecorator('accountBankName', {
rules: [{ required: true, message: '请选择开户行!' }],
initialValue: businessInfo.accountBankName,
})(
<Select
showSearch
filterOption={(input, option) =>
(option?.value ?? '').toLowerCase().includes(input.toLowerCase())
}
disabled={disabled}
>
{this.state.bankList.map(item => (
<Option value={item.bankName} key={item.bankName}>
{item.bankName}
</Option>
))}
</Select>,
)}
</FormItem>
</Col>
<Col span={12}>
<FormItem label="银行帐号" labelCol={{ span: 8 }}>
{getFieldDecorator('bankAccount', {
rules: [
{ required: true, message: '请输入银行帐号!' },
{ pattern: /^[0-9]*$/, message: '请输入数字' },
],
initialValue: businessInfo.bankAccount,
})(<Input disabled={disabled} />)}
</FormItem>
</Col>
</Row>
)}
</>
)}
{+mainCategoryId === carID && (
<Row gutter={24}>
<Col span={12}>
<FormItem label="开户许可证" labelCol={{ span: 8 }} wrapperCol={wrapperCol}>
{getFieldDecorator('accountOpenPermitImage', {
rules: [{ required: true, message: '请上传开户许可证!' }],
initialValue: businessInfo.accountOpenPermitImage,
})(
<Upload
{...uploadPropsFn.call(this, {
keyName: 'accountOpenPermitImage',
type: 8,
})}
fileList={businessInfo.accountOpenPermitImage}
disabled={disabled}
>
<UploadOutlined /> 上传文件
</Upload>,
)}
</FormItem>
</Col>
<Col span={12}>
<FormItem label="开户名称" labelCol={{ span: 8 }}>
{getFieldDecorator('bankAccountName', {
rules: [{ required: true, message: '请输入开户名称!' }],
initialValue: businessInfo.bankAccountName,
})(<Input maxLength={32} disabled={disabled} />)}
</FormItem>
</Col>
<Col span={12}>
<FormItem label="开户行" labelCol={{ span: 8 }}>
{getFieldDecorator('accountBankName', {
rules: [{ required: true, message: '请选择开户行!' }],
initialValue: businessInfo.accountBankName,
})(
<Select
showSearch
filterOption={(input, option) =>
(option?.value ?? '').toLowerCase().includes(input.toLowerCase())
}
disabled={disabled}
>
{this.state.bankList.map(item => (
<Option value={item.bankName} key={item.bankName}>
{item.bankName}
</Option>
))}
</Select>,
)}
</FormItem>
</Col>
<Col span={12}>
<FormItem label="银行帐号" labelCol={{ span: 8 }}>
{getFieldDecorator('bankAccount', {
rules: [
{ required: true, message: '请输入银行帐号!' },
{ pattern: /^[0-9]*$/, message: '请输入数字' },
],
initialValue: businessInfo.bankAccount,
})(<Input disabled={disabled} />)}
</FormItem>
</Col>
</Row>
)}
</Card>
{+mainCategoryId === carID && (
<Card title="其他信息">
<Row gutter={24}>
<Col span={24}>
<FormItem label="礼包内容" labelCol={{ span: 4 }}>
{getFieldDecorator('giftPackageContent', {
rules: [{ required: true, message: '请输入礼包内容!' }],
initialValue: businessInfo.giftPackageContent,
})(<Input style={{ width: '50%' }} disabled={disabled} />)}
</FormItem>
</Col>
</Row>
</Card>
)}
{/* 临时注释二期放开 */}
<div className={styles.formBtns}>
<Button type="primary" size="large" htmlType="submit" loading={this.state.loading}>
修改并提交
</Button>
<Button size="large" onClick={this.onCancel}>
取消
</Button>
</div>
</Form>
{this.state.visibleLoading && (
<div className={styles.spinBox}>
<div className={styles.spinBoxWrapper}>
<Spin tip="Loading..." />
</div>
</div>
)}
</div>
);
}
}
export default Form.create()(BusinessInfo);
.textArea {
:global {
.ant-form-item-label {
width: 16%;
}
.ant-form-item-control-wrapper {
width: 80%;
}
}
}
.imgList {
:global {
.ant-upload-list-picture-card-container {
width: 200px;
}
.ant-upload-list-item {
width: 200px;
}
// .ant-upload.ant-upload-select-picture-card {
// width: 200px;
// }
}
}
.infoBox {
position: relative;
}
.spinBox {
position: absolute;
top: 0;
left: 0;
display: flex;
align-items: center;
justify-content: center;
width: 100%;
height: 100%;
background-color: rgba(255, 255, 255, 0.5);
}
.spinBoxWrapper {
position: fixed;
top: 50%;
left: 50%;
display: flex;
align-items: center;
justify-content: center;
width: 200px;
height: 80px;
text-align: center;
transform: translate(-100px, -80px);
}
.addLink {
display: block;
margin: 20px 50px;
}
.formBtns {
display: flex;
align-items: center;
justify-content: space-evenly;
width: 100%;
padding: 20px 0;
background-color: #fff;
}
const Model = {
namespace: 'BusinessManage',
state: {},
effects: {},
reducers: {},
};
export default Model;
import request from '@/utils/request';
import config from '../../../config/env.config';
import qs from 'qs';
import { da } from 'date-fns/locale';
const { kdspApi, goodsApi } = config;
// 获取地址
export const apiAddrArea = params =>
request.get(`/api/merchants/addresses/list?${qs.stringify(params)}`, {
prefix: kdspApi,
});
// 主营类目
export async function apiCategoryList(param) {
return request.post('/api/merchants/suppliers/main-category/list', {
data: param,
prefix: goodsApi,
});
}
// 服务设施
export const apiServiceFacility = () =>
request.get('/api/merchants/suppliers/facilities/list', { prefix: kdspApi });
// 银行
export const apiBankList = () =>
request.get('/api/merchants/suppliers/banks/list', { prefix: kdspApi });
/**
* 上传文件
* imageType: 1-主头图,2-营业执照,3-电子签章,4-法人身份证国徽面,5-法人身份证人像面,6-类目所需资质,7-其它证件,8-开户许可证
* 0: 默认值 不校验
* */
export async function uploadFile(files, imageType = 0) {
const params = new FormData();
files.map(file => params.append('file', file));
params.append('imageType', imageType);
// image/upload
const data = await request.post('/api/merchants/images/upload', {
prefix: kdspApi,
data: params,
canRepeat: true,
});
return data;
}
// 图片内容识别
export const apiRecognize = params =>
request.get(`/api/merchants/images/recognize?${qs.stringify(params)}`, { prefix: kdspApi });
// 编辑商户信息
export const apiEditStoreInfo = params =>
request.post('/api/merchants/suppliers/info/edit', { prefix: kdspApi, data: params });
// 查询商户详情
export const apiBusinessDetail = businessId =>
request.get('/api/merchants/suppliers/pops/detail', { prefix: kdspApi });
.table {
:global {
.ant-table-row-expand-icon-cell {
visibility: hidden;
}
.ant-table-expanded-row {
.ant-pro-table {
margin-left: -17px;
}
}
}
}
.logBtn {
display: inherit;
margin: 20px auto;
}
.pageHeader {
background-color: #fff;
border: 1px solid rgb(235, 237, 240);
}
.listHeader {
display: flex;
&--item {
position: relative;
display: flex;
align-items: center;
justify-content: center;
box-sizing: border-box;
width: 40px;
height: 40px;
margin-right: 20px;
font-size: 14px;
line-height: 14px;
text-align: center;
border: 1px solid #333;
border-radius: 50%;
&::after {
position: absolute;
right: -21px;
width: 20px;
height: 1px;
background-color: #333;
content: '';
}
&:last-child::after {
width: 0;
height: 0;
}
}
}
import React, { useState, useRef } from 'react';
import ProTable from '@ant-design/pro-table';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import { notification, Button, Modal, Image, message } from 'antd';
import { query } from './services';
import style from './style.less';
const ContractView = () => {
const [visible, setVisible] = useState(false);
const [url, setUrl] = useState();
// 查看
const onLook = ({ contractPdfUrl }) => {
if (contractPdfUrl) {
setUrl(contractPdfUrl);
setVisible(true);
} else {
message.error('暂无合同');
}
};
// 下载
const ondown = ({ contractPdfUrl }) => {
if (contractPdfUrl) {
window.open(contractPdfUrl);
} else {
message.error('暂无合同');
}
};
const columns = [
{
title: '合同名称',
dataIndex: 'contractName',
key: 'contractName',
align: 'center',
},
{
title: '签约时间',
dataIndex: 'signDate',
key: 'signDate',
align: 'center',
hideInSearch: true,
render: (val, data) => {
if (data.signDate && typeof data.signDate === 'string') {
return data.signDate;
}
if (data.signDate && data?.signDate.length) {
let date = '';
data.signDate.forEach((item, index) => {
if (item && item.toString().length === 1) {
item = `0${item}`;
}
if ([0, 1].includes(index)) {
date += `${item}-`;
} else if ([3, 4].includes(index)) {
date += `${item}:`;
} else {
date += `${item} `;
}
});
return date;
}
return '-';
},
},
{
title: '合同状态',
dataIndex: 'state',
key: 'state',
align: 'center',
filters: false,
valueEnum: {
0: '失效',
1: '有效',
},
},
{
title: '操作',
dataIndex: 'action',
width: 200,
align: 'center',
hideInSearch: true,
render: (text, record) => (
<>
<Button
key="edit"
type="primary"
onClick={() => onLook(record)}
style={{ marginRight: '10px' }}
>
查看
</Button>
<Button key="del" type="primary" onClick={() => ondown(record)}>
下载
</Button>
</>
),
},
];
return (
<PageHeaderWrapper>
{!visible && (
<ProTable
search={{
collapsed: false,
collapseRender: () => null,
}}
columns={columns}
request={params => query({ ...params })}
rowKey={r => r.appealNo}
expandIconColumnIndex={10}
bordered
toolBarRender={false}
scroll={{ x: '100%', y: 400 }}
pagination={false}
/>
)}
{visible && (
<div className={style.contract}>
<iframe src={url} height="600" width="100%" title="合同"></iframe>
<Button type="primary" onClick={() => setVisible(false)}>
取消
</Button>
</div>
)}
</PageHeaderWrapper>
);
};
export default ContractView;
import { stringify } from 'querystring';
import request from '@/utils/request';
import _ from 'lodash';
import config from '../../../config/env.config';
const { goodsApi } = config;
// 分页查询所有数据
export async function query(params) {
const param = {
...params,
pageIndex: params.current,
pageSize: params.pageSize || 20,
};
const data = await request.post('/api/merchants/suppliers/contract/list', {
prefix: goodsApi,
data: param,
headers: {
'Content-Type': 'application/json;charset=UTF-8',
},
});
if (data.data) {
return {
data: data.data,
};
}
return {
total: 0,
data: [],
};
}
.contract {
text-align: center;
}
import { Button } from 'antd';
import React, { useRef, useState } from 'react';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import ProTable from '@ant-design/pro-table';
import styles from '../index.less';
import ActionModal from './modal';
import { detailQuery } from '../services';
const TableList = props => {
const [visibleModal, setVisibleModal] = useState(false);
const [modalValue, setModalValue] = useState({});
const { batchNo } = props.location.query;
const actionRef = useRef();
const reload = () => {
actionRef.current.reload();
};
const onClose = type => {
if (type) {
reload();
}
setVisibleModal(false);
};
const openAction = row => {
setModalValue(() => row);
setVisibleModal(true);
};
const columns = [
{
title: '订单号',
dataIndex: 'orderNo',
key: 'orderNo',
hideInSearch: true,
},
{
title: '京东订单号',
dataIndex: 'jdOrderId',
key: 'jdOrderId',
hideInSearch: true,
},
{
title: '售后单号',
dataIndex: 'afterServiceNo',
key: 'afterServiceNo',
hideInSearch: true,
},
{
title: '我方交易金额',
dataIndex: 'ownTradeAmount',
key: 'ownTradeAmount',
hideInSearch: true,
},
{
title: '三方交易金额',
dataIndex: 'thirdTradeAmount',
key: 'thirdTradeAmount',
hideInSearch: true,
},
{
title: '交易类型',
dataIndex: 'tradeType',
key: 'tradeType',
hideInSearch: true,
valueEnum: {
1: '订单支付',
2: '取消订单退款',
3: '售后退款',
},
},
{
title: '交易日期',
dataIndex: 'tradeDate',
key: 'tradeDate',
hideInSearch: true,
},
{
title: '状态',
dataIndex: 'status',
key: 'status',
valueEnum: {
1: '',
2: '不平',
},
},
{
title: '备注信息',
dataIndex: 'remarks',
key: 'remarks',
hideInSearch: true,
},
{
title: '操作',
dataIndex: 'townName',
key: 'townName',
hideInSearch: true,
render: (_, row) => [
<Button
key="edit"
type="primary"
onClick={() => {
openAction(row);
}}
>
补齐
</Button>,
],
},
];
return (
<PageHeaderWrapper>
<ProTable
className={styles.protable}
actionRef={actionRef}
columns={columns}
params={{ batchNo }}
request={res => detailQuery(res)}
rowKey="id"
bordered
scroll={{ x: 1500 }}
search={{
collapsed: false,
}}
/>
<ActionModal visible={visibleModal} formData={modalValue} onClose={onClose}></ActionModal>
</PageHeaderWrapper>
);
};
export default TableList;
import React from 'react';
import { Form } from '@ant-design/compatible';
import '@ant-design/compatible/assets/index.css';
import { Modal, Select, Input, notification } from 'antd';
import { updateStatus } from '../services';
const FormItem = Form.Item;
const { Option } = Select;
const layout = {
labelCol: { span: 6 },
wrapperCol: { span: 16 },
};
const valueEnum = [
{
status: 1,
text: '',
},
{
status: 2,
text: '不平',
},
];
const StatusForm = props => {
const { visible, formData } = props;
const { getFieldDecorator } = props.form;
const resetForm = () => {
props.form.resetFields();
};
const onCancel = () => {
props.onClose();
resetForm();
};
const handleSubmit = () => {
props.form.validateFields(async (err, fieldsValue) => {
if (err) return;
const params = {
...fieldsValue,
id: formData.id,
};
const businessCode = await updateStatus(params);
if (businessCode === '0000') {
notification.success({ message: '操作成功' });
resetForm();
props.onClose('success');
}
});
};
return (
<Modal title="对账手动补齐" visible={visible} onCancel={() => onCancel()} onOk={handleSubmit}>
<Form {...layout} name="formData">
<FormItem label="状态">
{getFieldDecorator('status', {
initialValue: formData.status,
rules: [
{
required: true,
message: '请选择状态!',
},
],
})(
<Select showSearch placeholder="">
{valueEnum.map(item => (
<Option value={item.status} key={item.status}>
{item.text}
</Option>
))}
</Select>,
)}
</FormItem>
<FormItem label="备注">
{getFieldDecorator('remarks', {
initialValue: formData.remarks,
})(<Input />)}
</FormItem>
</Form>
</Modal>
);
};
export default Form.create()(StatusForm);
import React, { useState, useEffect } from 'react';
import { Form } from '@ant-design/compatible';
import '@ant-design/compatible/assets/index.css';
import { Modal, Select, Input, notification } from 'antd';
import _ from 'lodash';
import { apiSave, apiAdd } from './services';
import { addrQuery } from '../afterSaleAddress/services';
const FormItem = Form.Item;
const { Option } = Select;
const layout = {
labelCol: { span: 6 },
wrapperCol: { span: 16 },
};
const AddressForm = props => {
const { visible, supplierList } = props;
const [formData, setFormData] = useState(() => _.cloneDeep(props.value));
const [provinceList, setProvinceList] = useState([]);
const [cityList, setCityList] = useState([]);
const [countyList, setCountyList] = useState([]);
const [townList, setTownList] = useState([]);
const { getFieldDecorator } = props.form;
const areaData = [
{
name: '',
key: 'province',
child: 'city',
data: provinceList,
required: true,
},
{
name: '',
key: 'city',
child: 'county',
data: cityList,
required: true,
},
{
name: '',
key: 'county',
child: 'town',
data: countyList,
},
{
name: '',
key: 'town',
child: '',
data: townList,
},
];
const resetForm = () => {
props.form.resetFields();
setFormData({});
};
const onCancel = () => {
props.onClose();
resetForm();
};
const getAddr = async (val, type, edit) => {
const data = await addrQuery({ parentId: val?.key || val });
switch (type) {
case 'city':
setCityList(data);
if (!edit) {
setCountyList([]);
setTownList([]);
props.form.setFieldsValue({
city: { key: '', label: '' },
county: { key: '', label: '' },
town: { key: '', label: '' },
});
}
break;
case 'county':
setCountyList(data);
if (!edit) {
setTownList([]);
props.form.setFieldsValue({
county: { key: '', label: '' },
town: { key: '', label: '' },
});
}
break;
case 'town':
setTownList(data);
if (!edit) {
props.form.setFieldsValue({ town: { key: '', label: '' } });
}
break;
default:
break;
}
};
const getFormData = () => {
const data = _.cloneDeep(props.value);
if (data.id) {
getAddr(data.provinceId, 'city', 'edit');
getAddr(data.cityId, 'county', 'edit');
getAddr(data.countyId, 'town', 'edit');
return {
...data,
province: {
key: data.provinceId ?? null,
label: data.provinceName ?? null,
},
city: {
key: data.cityId ?? null,
label: data.cityName ?? null,
},
county: {
key: data.countyId ?? null,
lable: data.countyName ?? null,
},
town: {
key: data.townId ?? null,
label: data.townName ?? null,
},
};
}
return data;
};
useEffect(() => {
async function getProvince() {
const data = await addrQuery();
setProvinceList(data);
}
getProvince();
setFormData(() => _.cloneDeep(getFormData()));
}, [props.value]);
const handleSubmit = () => {
props.form.validateFields(async (err, fieldsValue) => {
if (err) return;
const { province = {}, city = {}, county = {}, town = {} } = fieldsValue;
const params = {
...fieldsValue,
provinceId: province.key,
provinceName: province.label,
cityId: city.key,
cityName: city.label,
countyId: county.key,
countyName: county.label,
townId: town.key,
townName: town.label,
};
delete params.province;
delete params.city;
delete params.county;
delete params.town;
let api = apiAdd;
if (formData.id) {
params.id = formData.id;
api = apiSave;
}
const businessCode = await api(params);
if (businessCode === '0000') {
notification.success({ message: '操作成功' });
resetForm();
props.onClose('success');
}
});
};
return (
<Modal
title={`${formData.id ? '修改' : '增加'}售后地址`}
visible={visible}
onCancel={() => onCancel()}
onOk={handleSubmit}
>
<Form {...layout} name="formData">
<FormItem label="供应商名称">
{getFieldDecorator('shopId', {
initialValue: formData.shopId,
rules: [
{
required: true,
message: '请选择供应商名称!',
},
],
})(
<Select showSearch placeholder="请选择供应商名称" allowClear>
{supplierList.map(item => (
<Option value={item.id} key={item.id}>
{item.name}
</Option>
))}
</Select>,
)}
</FormItem>
{areaData.map(area => (
<FormItem label={area.name} name={area.key} key={area.key}>
{getFieldDecorator(area.key, {
initialValue: formData[area.key],
rules: [
{
required: area.required,
message: `请选择${area.name}!`,
},
],
})(
<Select
showSearch
placeholder={`请选择${area.name}`}
onChange={val => getAddr(val, area.child)}
labelInValue
allowClear
>
{area.data.map(item => (
<Option value={item.addrId} key={item.addrId}>
{item.addrName}
</Option>
))}
</Select>,
)}
</FormItem>
))}
<FormItem label="详情地址">
{getFieldDecorator('detail', {
initialValue: formData.detail,
rules: [
{
required: true,
message: '请输入详情地址!',
},
],
})(<Input />)}
</FormItem>
<FormItem label="联系人姓名">
{getFieldDecorator('receiverName', {
initialValue: formData.receiverName,
rules: [
{
required: true,
message: '请输入联系人姓名!',
},
],
})(<Input />)}
</FormItem>
<FormItem label="联系人电话">
{getFieldDecorator('receiverTel', {
initialValue: formData.receiverTel,
rules: [
{
required: true,
message: '请输入联系人电话!',
},
],
})(<Input />)}
</FormItem>
<FormItem label="备注">
{getFieldDecorator('remark', {
initialValue: formData.remark,
rules: [
{
required: true,
message: '请输入备注!',
},
],
})(<Input />)}
</FormItem>
</Form>
</Modal>
);
};
export default Form.create()(AddressForm);
import { Button, Select } from 'antd';
import React, { useRef, useEffect, useState } from 'react';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import ProTable from '@ant-design/pro-table';
import { Link } from 'umi';
import styles from './index.less';
import { query, supplier, downDetail } from './services';
const { Option } = Select;
const TableList = () => {
const [supplierList, setSupplierList] = useState([]);
const [shopId, setShopId] = useState(null);
const actionRef = useRef();
useEffect(() => {
async function fetchData() {
const data = await supplier();
setSupplierList(data);
}
fetchData();
}, []);
const changeSupplier = res => {
setShopId(res);
};
const reload = type => {
if (type) {
setShopId(null);
}
actionRef.current.reload();
};
const down = ({ batchNo }) => {
downDetail({ batchNo });
};
const columns = [
{
title: '对账批次号',
dataIndex: 'batchNo',
key: 'batchNo',
hideInSearch: true,
},
{
title: '交易日期',
dataIndex: 'tradeDate',
key: 'tradeDate',
hideInSearch: true,
},
{
title: '交易日期',
dataIndex: 'dateRange',
key: 'dateRange',
valueType: 'dateRange',
hideInTable: true,
},
{
title: '供应商名称',
dataIndex: 'shopId',
key: 'shopId',
hideInTable: true,
valueType: 'select',
renderFormItem: () => (
<Select onChange={changeSupplier} value={shopId} allowClear>
{supplierList.map(item => (
<Option value={item.id} key={item.id}>
{item.name}
</Option>
))}
</Select>
),
},
{
title: '供应商名称',
dataIndex: 'supplierName',
key: 'supplierName',
hideInSearch: true,
},
{
title: '我方对账条数',
dataIndex: 'ownCount',
key: 'ownCount',
hideInSearch: true,
},
{
title: '三方对账条数',
dataIndex: 'thirdCount',
key: 'thirdCount',
hideInSearch: true,
},
{
title: '对账成功条数',
dataIndex: 'succCount',
key: 'succCount',
hideInSearch: true,
},
{
title: '对账状态',
dataIndex: 'status',
key: 'status',
valueEnum: {
1: '',
2: '不平',
},
},
{
title: '操作',
dataIndex: 'option',
key: 'option',
valueType: 'option',
width: 120,
render: (_, row) => [
<Link
to={{
pathname: '/reconciliation/detail',
query: {
batchNo: row.batchNo,
},
}}
>
<Button key="edit" type="primary">
查看详情
</Button>
</Link>,
],
},
{
title: '下载',
dataIndex: 'townName',
key: 'townName',
width: 100,
hideInSearch: true,
render: (_, row) => [
<Button
key="edit"
type="primary"
onClick={() => {
down(row);
}}
>
下载
</Button>,
],
},
];
return (
<PageHeaderWrapper>
<ProTable
className={styles.protable}
actionRef={actionRef}
columns={columns}
params={{ shopId }}
request={res => query(res)}
rowKey={r => r.batchNo}
bordered
scroll={{ x: 1500 }}
search={{
collapsed: false,
}}
onReset={() => reload('reset')}
/>
</PageHeaderWrapper>
);
};
export default TableList;
.protable {
table {
margin: 20px;
}
}
const Model = {
namespace: 'reconciliation',
state: {
tableData: [],
typeList: [],
organizationList: [],
},
effects: {},
reducers: {},
};
export default Model;
import { stringify } from 'querystring';
import _ from 'lodash';
import request from '@/utils/request';
import { saveAs } from 'file-saver';
import { format } from 'date-fns';
import config from '../../../config/env.config';
// 查询
export async function query(params) {
const tempParams = {
...params,
startDate: params.dateRange?.[0],
endDate: params.dateRange?.[1],
pageNo: params.current,
};
delete tempParams.dateRange;
delete tempParams.current;
try {
const {
data: { current, records, total, size },
} = await request.post('/api/kdsp/account/account-verify-record-page', {
prefix: config.kdspApi,
data: stringify(_.omitBy(tempParams, v => !v)),
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
});
return {
current,
data: records.map(v => ({ ...v, logisticsStatus: `_${v.logisticsStatus}` })),
total,
pageSize: size,
};
} catch (error) {
return {};
}
}
export async function supplier() {
const { data } = await request.get('/api/merchants/suppliers/querylist', {
prefix: config.kdspApi,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
});
return data;
}
export async function downDetail(params) {
const data = await request.get('/api/kdsp/account/account-verify-detail-downLoad', {
params,
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
prefix: config.kdspApi,
responseType: 'arrayBuffer',
});
const blob = new Blob([data]);
saveAs(blob, `对账明细-${format(new Date(), 'yyyyMMddHHmmss')}.xlsx`);
}
export async function detailQuery(params) {
const tempParams = {
...params,
pageNo: params.current,
};
delete tempParams.current;
try {
const {
data: { current, records, total, size },
} = await request.post('/api/kdsp/account/account-verify-detail-page', {
prefix: config.kdspApi,
data: stringify(_.omitBy(tempParams, v => !v)),
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
});
return {
current,
data: records.map(v => ({ ...v, logisticsStatus: `_${v.logisticsStatus}` })),
total,
pageSize: size,
};
} catch (error) {
return {};
}
}
export async function updateStatus(params) {
const { businessCode } = await request.post('/api/kdsp/account/account-verify-detail-update', {
prefix: config.kdspApi,
data: stringify(params),
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
});
return businessCode;
}
export async function apiSave(params) {
const { businessCode } = await request.post('/api/merchants/aftersales/addresses/edit', {
prefix: config.kdspApi,
data: stringify(params),
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
});
return businessCode;
}
export async function apiAdd(params) {
const { businessCode } = await request.post('/api/merchants/aftersales/addresses/add', {
prefix: config.kdspApi,
data: stringify(params),
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
});
return businessCode;
}
import React, { useRef, useState, useEffect } from 'react';
import { Input, Checkbox, Radio, Button, notification, Spin } from 'antd';
import { Form } from '@ant-design/compatible';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import RolePermissionsModal from '../components/RolePermissionsModal';
import { getRoleList, addUser, getUserDetail, updateUser } from '../../service';
import { shopGetBySupplierId } from '@/pages/ServiceGoods/service';
import UUID from '@/utils/uuid';
import style from './index.less';
const AccountInfo = props => {
const { getFieldDecorator, validateFields, getFieldValue, setFieldsValue } = props.form;
const { id } = props.location.query;
const [title, setTitle] = useState('新增用户');
const [inputType, setInputType] = useState('text');
const [confirmInputType, setConfirmInputType] = useState('text');
const [roleList, setRoleList] = useState([]);
const [supplierList, setSupplierList] = useState([]);
const [detailInfo, setDetailInfo] = useState({});
const [loading, setLoading] = useState(false);
const userInfo = JSON.parse(localStorage.getItem('user') || '{}');
const rolePermissionsModalRef = useRef();
const popReg = /^pop_/g;
const isPop = popReg.test(userInfo.supplierCode);
// 表单布局
const formLayout = {
labelCol: { span: 4 },
wrapperCol: { span: 12 },
};
const btnLayout = {
wrapperCol: { offset: 4, span: 12 },
};
const handleConfirmPassword = (rule, value, callback) => {
if (value && value !== getFieldValue('password')) {
callback('两次输入不一致!');
}
callback();
};
// 表单验证
const formRules = {
account: [
{ required: true, message: '请设置登录用户名' },
{ pattern: /^[a-zA-Z0-9@.]{1,32}$/g, message: '用户名格式错误' },
],
name: [{ required: true, message: '请输入姓名' }],
password: [
{ required: true, message: '请设置密码' },
{
pattern: /^(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*[\W_]).{13,18}$/g,
message: '密码格式错误',
},
],
confirmPassword: [
{ required: true, message: '请确认登录密码' },
{ validator: handleConfirmPassword, message: '两次密码不一致' },
],
roleId: [{ required: true, type: 'array', message: '请选择用户角色' }],
};
// 角色权限 modal
const openRolePermissionsModal = record => {
rolePermissionsModalRef.current.open(record);
};
// 获取所有门店列表
const getAllSupplier = res => {
if (res.code !== '0000') {
notification.warning({
message: res.detail,
});
return;
}
setSupplierList(res.data);
};
// 获取所有角色列表
const getAllRoleList = res => {
if (res.code !== 2000) {
notification.warning({ message: res.detail });
return;
}
console.log(res);
const { content } = res.data;
setRoleList(content);
};
// 获取当前选中的角色id
const getSelectedRoleIds = () => {
const cacheIds = window.sessionStorage.getItem('_selectedRoleIds');
return cacheIds ? JSON.parse(cacheIds) : [];
};
// 获取账户详情
const getAccountDetail = res => {
setLoading(false);
if (res.code !== 2000) {
notification.warning({ message: res.detail });
return;
}
console.log(res);
setDetailInfo(res.data);
const { account, name, organization } = res.data;
const roleId = getSelectedRoleIds();
console.log(roleId);
setFieldsValue({
account,
name,
organizationId: organization.id,
roleId,
});
};
// 用户角色多选框
const userRoleCheckbox = () =>
roleList.map(item => (
<Checkbox value={item.id}>
<a
onClick={e => {
e.preventDefault();
openRolePermissionsModal(item);
}}
>
{item.name}
</a>
</Checkbox>
));
// 选择门店多选框
const selectSupplierCheckbox = () => {
let list = supplierList;
const filterData = supplierList.filter(item => item.orgId === userInfo.orgId);
if (filterData.length) {
list = filterData;
}
return list.map(item => <Radio value={item.orgId}> {item.name}</Radio>);
};
// 表单验证辅助函数
const rulesHandler = (field, initialValue = '') => ({
rules: formRules[field] || [],
initialValue,
});
// 提交
const onSubmit = () => {
validateFields(async (err, fieldsValue) => {
console.log(err, fieldsValue);
if (err) {
return;
}
// 提交
console.log(fieldsValue);
const { organizationId, email } = fieldsValue;
const { orgId } = userInfo;
const code = UUID.createUUID();
const params = {
...fieldsValue,
code,
email: email || `${code}@stms.oppo.cn`,
source: 1,
tenantId: 560761,
organization: {
id: organizationId || orgId,
},
};
delete params.organizationId;
let api = addUser;
if (id) {
params.id = id;
api = updateUser;
}
const res = await api(params);
if (res.code !== 2000) {
notification.warning({
message: res.detail || res.msg,
});
return;
}
notification.success({
message: '保存成功',
});
setTimeout(() => {
window.history.back();
}, 1000);
});
};
// 处理初始化密码自动填充函数
const handlePasswordChange = (e, fn) => {
if (e.target.value) {
fn('password');
} else {
fn('text');
}
};
const initData = async () => {
const roleListParams = {
page: 1,
size: 1000,
};
setLoading(true);
const roleListRes = await getRoleList(roleListParams);
const supplierListRes = await shopGetBySupplierId();
let accountDetailRes = null;
if (id) {
accountDetailRes = await getUserDetail({ id });
getAccountDetail(accountDetailRes);
}
setLoading(false);
getAllRoleList(roleListRes);
getAllSupplier(supplierListRes);
};
useEffect(() => {
initData();
if (id) {
setTitle('编辑用户');
}
}, []);
return (
<PageHeaderWrapper title={title}>
<div className={style['user-info']}>
<Spin spinning={loading}>
<Form className={style['user-info--form']} {...formLayout}>
<Form.Item label="用户名" extra="限32字符以内,限英文/数字/“@”“ .”">
{getFieldDecorator('account', rulesHandler('account'))(
<Input disabled={!!id} placeholder="请输入用户名" maxLength={32} />,
)}
</Form.Item>
<Form.Item label="姓名" extra="限10字符以内">
{getFieldDecorator('name', rulesHandler('name'))(
<Input maxLength={10} placeholder="请输入用户姓名" />,
)}
</Form.Item>
{id ? (
''
) : (
<>
<Form.Item label="初始密码" extra="限13-18字符,包含大小写字母、数字和特殊字符">
{getFieldDecorator('password', rulesHandler('password'))(
<Input
type={inputType}
onChange={e => handlePasswordChange(e, setInputType)}
placeholder="请设置登录密码"
/>,
)}
</Form.Item>
<Form.Item label="确认密码">
{getFieldDecorator('confirmPassword', rulesHandler('confirmPassword'))(
<Input
type={confirmInputType}
onChange={e => handlePasswordChange(e, setConfirmInputType)}
placeholder="请确认登录密码"
/>,
)}
</Form.Item>
</>
)}
<Form.Item label="选择门店" extra="仅限POP商家设置">
{getFieldDecorator('organizationId', rulesHandler('organizationId'))(
isPop && supplierList.length ? (
<Radio.Group disabled={!!id} style={{ width: '100%' }}>
{selectSupplierCheckbox()}
</Radio.Group>
) : (
<div>暂无门店信息</div>
),
)}
</Form.Item>
<Form.Item label="用户角色">
{getFieldDecorator('roleId', rulesHandler('roleId'))(
roleList.length ? (
<Checkbox.Group style={{ width: '100%' }}>{userRoleCheckbox()}</Checkbox.Group>
) : (
<div>暂无角色信息</div>
),
)}
</Form.Item>
<Form.Item {...btnLayout}>
<Button type="default" onClick={() => window.history.back()}>
返回
</Button>
<Button className={style['submit-button']} type="primary" onClick={onSubmit}>
提交
</Button>
</Form.Item>
</Form>
</Spin>
</div>
<RolePermissionsModal ref={rolePermissionsModalRef} />
</PageHeaderWrapper>
);
};
export default Form.create({})(AccountInfo);
.user-info {
padding: 30px 0;
background: #fff;
&--form {
width: 1200px;
}
.submit-button {
margin-left: 20px;
}
}
import React, { forwardRef, useImperativeHandle, useState, useRef, useEffect } from 'react';
import { Modal, Input, notification } from 'antd';
import { Form } from '@ant-design/compatible';
import { resetPassword } from '../../../service';
const FormComponent = props => {
const { record = {}, form } = props;
const { getFieldDecorator, setFieldsValue, getFieldValue } = form;
const [inputType, setInputType] = useState('text');
const [confirmInputType, setConfirmInputType] = useState('text');
const handlepassword = (rule, value, callback) => {
if (value && value !== getFieldValue('newPassword')) {
callback('两次输入不一致!');
}
// Note: 必须总是返回一个 callback,否则 validateFieldsAndScroll 无法响应
callback();
};
// 处理初始化密码自动填充函数
const handlePasswordChange = (e, fn) => {
if (e.target.value) {
fn('password');
} else {
fn('text');
}
};
const formLayout = {
labelCol: { span: 4 },
wrapperCol: { span: 20 },
};
const formRules = {
newPassword: [
{ required: true, message: '请设置新密码' },
{
pattern: /^(?=.*[A-Z])(?=.*[a-z])(?=.*[0-9])(?=.*[\W_]).{13,18}$/g,
message: '密码格式错误',
},
],
password: [
{ required: true, message: '请确认新密码' },
{ validator: handlepassword, message: '两次密码不一致' },
],
};
const rulesHandler = name => ({
rules: formRules[name],
});
useEffect(() => {
setFieldsValue(record);
}, [record]);
return (
<Form {...formLayout}>
<Form.Item label="用户名">{record.name}</Form.Item>
<Form.Item label="新密码" extra="限13-18字符,包含大小写字母、数字和特殊字符">
{getFieldDecorator('newPassword', rulesHandler('newPassword'))(
<Input
type={inputType}
onChange={e => handlePasswordChange(e, setInputType)}
placeholder="请设置新密码"
/>,
)}
</Form.Item>
<Form.Item label="确认密码">
{getFieldDecorator('password', rulesHandler('password'))(
<Input
type={confirmInputType}
onChange={e => handlePasswordChange(e, setConfirmInputType)}
placeholder="请确认新密码"
/>,
)}
</Form.Item>
</Form>
);
};
const FormWarpper = Form.create({})(forwardRef(FormComponent));
const ResetPasswordModal = (props, ref) => {
const formRef = useRef();
const [visible, setVisible] = useState(false);
const [confirmLoading, setConfirmLoading] = useState(false);
const [record, setRecord] = useState({
name: '',
id: '',
});
const open = item => {
setVisible(true);
const { name, id } = item;
// 初始化数据
setRecord({
name,
id,
});
};
const onCancel = () => {
setVisible(false);
setConfirmLoading(false);
formRef.current.resetFields();
};
const onOk = () => {
formRef.current.validateFields(async (err, fieldsValue) => {
console.log('fieldsValue :>>', fieldsValue);
if (err) {
return;
}
setConfirmLoading(true);
const { password } = fieldsValue;
const params = {
id: record.id,
password,
};
const res = await resetPassword(params);
setConfirmLoading(false);
if (res.code !== 2000) {
notification.warning({
message: res.detail,
});
return;
}
notification.success({
message: '重置成功',
});
setConfirmLoading(false);
onCancel();
});
};
useImperativeHandle(ref, () => ({
open,
}));
const modalProps = {
title: '重置密码',
visible,
confirmLoading,
onCancel,
onOk,
};
return (
<Modal {...modalProps}>
<FormWarpper ref={formRef} record={record} />
</Modal>
);
};
export default forwardRef(ResetPasswordModal);
import React, { forwardRef, useImperativeHandle, useState } from 'react';
import { Modal } from 'antd';
import { RoleInfoComponent } from '../../../Role/RoleInfo';
const RolePermissionsModal = (props, ref) => {
const [visible, setVisible] = useState(false);
const [roleInfo, setRoleInfo] = useState({});
const open = record => {
setVisible(true);
console.log('record', record);
setRoleInfo(record);
};
const onCancel = () => {
setVisible(false);
};
useImperativeHandle(ref, () => ({
open,
}));
const modalProps = {
width: 600,
title: '查看角色',
visible,
footer: null,
onCancel,
};
const roleProps = {
id: roleInfo.id,
submittable: false,
readyonly: true,
height: '500px',
wrapperCol: {
span: 18,
},
};
console.log(roleProps);
return (
<>
{visible ? (
<Modal {...modalProps}>
<RoleInfoComponent {...roleProps} />
</Modal>
) : null}
</>
);
};
export default forwardRef(RolePermissionsModal);
import React, { forwardRef, useImperativeHandle, useState, useRef, useEffect } from 'react';
import { Modal, Select, notification } from 'antd';
import { Form } from '@ant-design/compatible';
import { userEnabled, userDisabled, userDelete } from '@/pages/systemManage/service';
const FormComponent = props => {
const { record = {}, form } = props;
const { getFieldDecorator, setFieldsValue } = form;
const formLayout = {
labelCol: { span: 4 },
wrapperCol: { span: 20 },
};
const formRules = {
status: [{ required: true, message: '' }],
};
useEffect(() => {
setFieldsValue(record);
}, [record]);
const rulesHandler = name => ({
rules: formRules[name],
});
return (
<Form {...formLayout}>
<Form.Item label="用户名">{record.name}</Form.Item>
<Form.Item label="状态">
{getFieldDecorator('status', rulesHandler('status'))(
<Select>
<Select.Option value={2}>使用中</Select.Option>
<Select.Option value={3}>已禁用</Select.Option>
{record.currentStatus === 2 ? '' : <Select.Option value={1}>已删除</Select.Option>}
</Select>,
)}
</Form.Item>
</Form>
);
};
const FormWarpper = Form.create({})(forwardRef(FormComponent));
const UpdateStatusModal = (props, ref) => {
const { reload } = props;
const formRef = useRef();
const [visible, setVisible] = useState(false);
const [confirmLoading, setConfirmLoading] = useState(false);
const [record, setRecord] = useState({
name: '',
status: '',
id: '',
});
const open = item => {
setVisible(true);
const { name, status, id } = item;
// 初始化数据,currentStatus 用来判断状态选项
setRecord({
name,
status: status.value,
id,
currentStatus: status.value,
});
};
const onCancel = () => {
setVisible(false);
setConfirmLoading(false);
formRef.current.resetFields();
};
const onOk = () => {
formRef.current.validateFields(async (err, fieldsValue) => {
console.log('fieldsValue :>>', fieldsValue);
if (err) {
return;
}
const apiName = {
1: userDelete,
2: userEnabled,
3: userDisabled,
};
const params = {
id: record.id,
};
setConfirmLoading(true);
const res = await apiName[fieldsValue.status](params);
if (res.code !== 2000) {
notification.warning({
message: res.detail,
});
setConfirmLoading(false);
return;
}
setConfirmLoading(false);
notification.success({
message: '保存成功',
});
reload();
onCancel();
});
};
useImperativeHandle(ref, () => ({
open,
}));
const modalProps = {
title: '修改状态',
visible,
confirmLoading,
onCancel,
onOk,
};
return (
<Modal {...modalProps}>
<FormWarpper ref={formRef} record={record} />
</Modal>
);
};
export default forwardRef(UpdateStatusModal);
import React from 'react';
import { Divider } from 'antd';
export const getColumns = props => {
const {
toUserInfo,
openRolePermissionsModal,
openUpdateStatusModal,
openResetPasswordModal,
canEditable,
} = props;
return [
{
title: '用户名',
dataIndex: 'account',
},
{
title: '姓名',
dataIndex: 'name',
},
{
title: '创建时间',
dataIndex: 'createTime',
hideInSearch: true,
},
{
title: '最后一次登录时间',
dataIndex: 'lastLogin',
hideInSearch: true,
},
{
title: '角色',
dataIndex: 'creator',
render: (value, record) => {
const { roles } = record;
const roleElement = roles.map((role, index) => (
<>
<a onClick={() => openRolePermissionsModal(role)}>{role.name}</a>
{roles.length - 1 !== index ? '' : ''}
</>
));
return roleElement;
},
},
{
title: '状态',
dataIndex: 'status',
width: 150,
initialValue: '3',
hideInFilter: true,
valueEnum: {
all: { text: '全部', status: '' },
close: { text: '使用中', status: 2 },
running: { text: '已禁用', status: 3 },
},
render: value => {
const textMapping = {
2: '已使用',
3: '已禁用',
};
return textMapping[value.value] || '-';
},
},
{
title: '操作',
key: 'action',
width: 220,
hideInSearch: true,
render: (value, record) => {
if (!canEditable) {
return <>-</>;
}
return (
<>
<a
onClick={() => {
toUserInfo(record);
}}
>
编辑
</a>
<Divider type="vertical" />
<a
onClick={() => {
openUpdateStatusModal(record);
}}
>
修改状态
</a>
<Divider type="vertical" />
<a
onClick={() => {
openResetPasswordModal(record);
}}
>
重置密码
</a>
</>
);
},
},
];
};
import React, { useRef, useState, useEffect } from 'react';
import { useHistory } from 'react-router-dom';
import { Button, Divider } from 'antd';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import ProTable from '@ant-design/pro-table';
import { connect } from 'umi';
// data
import { ACCOUNT_MANAGE } from '@/../config/permission.config';
import { getColumns } from './data';
// compoents
import RolePermissionsModal from './components/RolePermissionsModal';
import UpdateStatusModal from './components/UpdateStatusModal';
import ResetPasswordModal from './components/ResetPasswordModal';
// mock
import { getUserList } from '../service';
const Account = props => {
const { permissions } = props;
const history = useHistory();
const actionRef = useRef();
const formRef = useRef();
const rolePermissionsModalRef = useRef();
const updateStatusModalRef = useRef();
const resetPasswordModal = useRef();
// 权限控制-新增/修改
const canEditable = permissions[ACCOUNT_MANAGE.EDITABLE];
// 获取当前用户组织信息
const userInfo = JSON.parse(localStorage.getItem('user') || '{}');
// 临时缓存当前账户的角色id
const setSelectedRoleIds = roleIds => {
window.sessionStorage.setItem('_selectedRoleIds', JSON.stringify(roleIds));
};
// 清空临时缓存的角色id
const clearnSelectedRoleIds = () => {
window.sessionStorage.removeItem('_selectedRoleIds');
};
// 新增编辑
const toUserInfo = ({ id, roles = [] } = {}) => {
const query = id ? { id } : {};
if (id) {
const roleIds = roles.map(role => role.id);
setSelectedRoleIds(roleIds);
}
history.push({
pathname: '/systemManage/account/userInfo',
query,
});
};
// 角色权限 modal
const openRolePermissionsModal = record => {
rolePermissionsModalRef.current.open(record);
};
// 修改状态 modal
const openUpdateStatusModal = record => {
updateStatusModalRef.current.open(record);
};
// 重置密码 modal
const openResetPasswordModal = record => {
resetPasswordModal.current.open(record);
};
const reload = () => {
actionRef.current.reload();
};
// 获取列表配置
const columns = getColumns({
toUserInfo,
openRolePermissionsModal,
openUpdateStatusModal,
openResetPasswordModal,
canEditable,
});
// 表格属性
const tableProps = {
columns,
rowKey: 'id',
request: async params => {
console.log('搜索', params);
const { current: page, pageSize: size } = params;
const res = await getUserList({
page,
size,
id: userInfo,
});
const { content = [], totalElements } = res.data;
return {
data: content,
success: true,
total: totalElements,
};
},
toolBarRender: () => {
// TODO: 权限控制
if (canEditable) {
return [
<Button type="primary" onClick={() => toUserInfo()}>
新增用户
</Button>,
];
}
return [];
},
};
// 搜索属性
const searchProps = {
collapsed: false,
collapseRender: () => null,
};
useEffect(() => {
clearnSelectedRoleIds();
}, []);
return (
<PageHeaderWrapper>
<ProTable actionRef={actionRef} formRef={formRef} {...tableProps} search={false}></ProTable>
<RolePermissionsModal ref={rolePermissionsModalRef} />
<UpdateStatusModal reload={reload} ref={updateStatusModalRef} />
<ResetPasswordModal reload={reload} ref={resetPasswordModal} />
</PageHeaderWrapper>
);
};
export default connect(({ menu }) => ({
permissions: menu.permissions,
}))(Account);
import React, { useRef } from 'react';
import { useHistory } from 'react-router-dom';
import { Button, Divider } from 'antd';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import ProTable from '@ant-design/pro-table';
// mock
import { logList } from '../mock/log';
const columns = [
{
title: '操作人',
dataIndex: 'name',
},
{
title: '一级菜单',
dataIndex: 'menu',
},
{
title: '二级菜单',
dataIndex: 'subMenu',
},
{
title: '操作点',
dataIndex: 'status',
hideInSearch: true,
},
{
title: '操作时间',
hideInSearch: true,
dataIndex: 'actionTime',
},
{
title: '操作时间2',
hideInTable: true,
dataIndex: 'actionTime1',
valueType: 'dateRange',
},
{
title: '操作',
key: 'action',
hideInSearch: true,
render: () => (
<>
<a>查看</a>
</>
),
},
];
const SystemLog = props => {
const history = useHistory();
const actionRef = useRef();
const formRef = useRef();
const toUserInfo = (record = {}) => {
const { id } = record;
history.push({
pathname: '/SystemManage/Account/UserInfo',
query: {
id: 1,
},
});
};
// 表格属性
const tableProps = {
columns,
rowKey: 'id',
request: params => {
console.log('搜索', params);
return new Promise(resolve => {
setTimeout(() => {
resolve({
data: logList,
success: true,
});
}, 2000);
});
},
search: {
collapsed: false,
collapseRender: () => null,
},
};
// 搜索属性
const searchProps = {};
return (
<PageHeaderWrapper>
<ProTable actionRef={actionRef} formRef={formRef} {...tableProps}></ProTable>
</PageHeaderWrapper>
);
};
export default SystemLog;
import React, { forwardRef, useImperativeHandle, useState, useEffect } from 'react';
import { Input, Checkbox, Button, Tree, notification, Spin } from 'antd';
import { Form } from '@ant-design/compatible';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import { getUserPermissions } from '@/services/user';
import { addResource, getRoleDetail, modifyRole } from '../../service';
import UUID from '@/utils/uuid';
import style from './index.less';
import { APP_CODE, TENANT_ID } from '@/utils/constants';
// mock
const { TextArea } = Input;
const { TreeNode } = Tree;
/**
* 权限表单/权限展示
* @param {*} props
* @param {boolean} props.submittable 是否可提交(即:是否展示操作)
* @param {number} props.id 判断是否是新增
* @param {boolean} props.readyonly 是否全部为只读(查看操作)
*/
const RoleInfo = props => {
const {
id,
submittable = true,
readyonly = false,
defaultExpandAll = true,
height,
labelCol = { span: 4 },
wrapperCol = { span: 12 },
} = props;
const { getFieldDecorator, getFieldValue, validateFields, setFieldsValue } = props.form;
// 可选的权限数据
const [permissionsData, setPermissionsData] = useState([]);
// 当前选中的权限-仅提交时使用
const [selectedPermissions, setSelectedPermissions] = useState([]);
const [detailInfo, setDetailInfo] = useState({});
const [loading, setLoading] = useState(false);
// 表单布局
const formLayout = {
labelCol,
wrapperCol,
};
const btnLayout = {
wrapperCol: { offset: 4, span: 12 },
};
// 表单验证
const formRules = {
name: [{ required: true, message: '请设置角色名称' }],
description: [{ required: true, message: '请填写角色描述' }],
selected: [{ type: 'array', required: true, message: '请选择权限' }],
};
const treeProps = {
defaultExpandAll,
};
// 表单验证辅助函数
const rulesHandler = field => ({
rules: formRules[field] || [],
});
// 提交数据
const onSubmit = () => {
validateFields(async (err, fieldsValue) => {
console.log('fieldsValue :>>', fieldsValue);
if (err) {
return;
}
// 提交
const params = {
tenantId: TENANT_ID,
code: UUID.createUUID(),
app: {
appCode: APP_CODE,
selected: selectedPermissions,
},
...fieldsValue,
};
if (id) {
params.id = id;
}
// 删除提交时无用的字段
delete params.selected;
console.log('params :>>', params);
const res = await addResource(params);
console.log(res);
if (res.code !== 2000) {
notification.warning({
message: `${res.msg}:${res.detail}`,
});
return;
}
notification.success({
message: '提交成功',
});
setTimeout(() => {
window.history.back();
}, 1000);
});
};
// 选择权限回调
const onPermissionsCheck = checkedKeys => {
if (readyonly) {
return;
}
setSelectedPermissions(checkedKeys);
setFieldsValue({
selected: checkedKeys,
});
};
// 初始化权限数据
// TODO:分离接口
const initPermissions = (res, roleId) => {
if (res.code !== 2000) {
notification.warning({
message: res.msg,
});
}
let resource = [];
let selected = [];
if (roleId) {
const { resource: resourceData, selected: selectedData } = res.data;
resource = resourceData;
selected = selectedData;
} else {
resource = res.data;
}
setPermissionsData(resource);
setSelectedPermissions(selected);
setFieldsValue({
selected,
});
};
// 初始化当前角色数据
const initRoleData = res => {
if (res.code !== 2000) {
notification.warning({ message: res.detail });
return;
}
console.log(res.data);
const { name, description } = res.data;
setFieldsValue({
name,
description,
});
setDetailInfo({
name,
description,
});
};
const initData = async () => {
const roleId = id || null;
setLoading(true);
const permissionsRes = await getUserPermissions(roleId);
if (id) {
const roleDetailRes = await getRoleDetail({ id });
initRoleData(roleDetailRes);
}
setLoading(false);
initPermissions(permissionsRes, roleId);
};
useEffect(() => {
initData();
}, []);
const renderTreeNodes = data =>
data.map(item => {
if (item.childNodes) {
return (
<TreeNode title={item.node.name} key={item.node.id} dataRef={item}>
{renderTreeNodes(item.childNodes)}
</TreeNode>
);
}
return <TreeNode key={item.node.id} {...item} />;
});
return (
<div className={style['info-box']}>
<Spin spinning={loading}>
<Form className={style['info-box--form']} {...formLayout}>
<Form.Item label="角色名称">
{readyonly
? detailInfo.name
: getFieldDecorator('name', rulesHandler('name'))(
<Input maxLength={10} placeholder="10个字以内" />,
)}
</Form.Item>
<Form.Item label="角色描述">
{readyonly
? detailInfo.description || '-'
: getFieldDecorator('description', rulesHandler('description'))(
<TextArea maxLength={30} placeholder="30个字以内"></TextArea>,
)}
</Form.Item>
<Form.Item label="权限">
{getFieldDecorator('selected', rulesHandler('selected'))(
<div className={style.tree} style={{ height }}>
{permissionsData.length ? (
<Tree
checkable
selectable={false}
defaultExpandAll
onCheck={onPermissionsCheck}
checkedKeys={selectedPermissions}
>
{renderTreeNodes(permissionsData)}
</Tree>
) : (
''
)}
</div>,
)}
</Form.Item>
{submittable ? (
<Form.Item {...btnLayout}>
<Button type="default" onClick={() => window.history.back()}>
返回
</Button>
<Button className={style['submit-button']} type="primary" onClick={onSubmit}>
提交
</Button>
</Form.Item>
) : (
''
)}
</Form>
</Spin>
</div>
);
};
export const RoleInfoComponent = Form.create({})(RoleInfo);
const PageWapperComponent = props => {
const { id } = props.location?.query;
const [title, setTitle] = useState('新增角色');
useEffect(() => {
if (id) {
setTitle('编辑角色');
}
}, []);
const roleInfo = {
id,
};
return (
<PageHeaderWrapper title={title}>
<RoleInfoComponent {...roleInfo} />
</PageHeaderWrapper>
);
};
export default PageWapperComponent;
.info-box {
padding: 30px 0;
background: #fff;
&--form {
max-width: 1200px;
}
.submit-button {
margin-left: 20px;
}
}
.tree {
min-height: 500px;
padding: 20px 30px;
overflow: auto;
border: 1px solid #efefef;
}
import React from 'react';
import { Divider, Popconfirm } from 'antd';
export const getColumns = props => {
const { onDeleteRole, toRoleInfo, canEditable } = props;
return [
{
title: '角色ID',
dataIndex: 'id',
hideInTable: true,
},
{
title: '角色名称',
dataIndex: 'name',
},
{
title: '角色描述',
dataIndex: 'description',
hideInSearch: true,
},
{
title: '状态',
dataIndex: 'status',
hideInSearch: true,
render: value => value.name,
},
{
title: '关联账号',
dataIndex: 'userCount',
hideInSearch: true,
},
{
title: '创建人',
dataIndex: 'creator',
hideInSearch: true,
},
{
title: '创建日期',
hideInSearch: true,
dataIndex: 'createTime',
},
{
title: '操作',
key: 'action',
hideInSearch: true,
render: (value, record) => {
if (!canEditable) {
return '-';
}
return (
<>
<a onClick={() => toRoleInfo(record)}>编辑</a>
{record.userCount === 0 ? (
<>
<Divider type="vertical" />
<Popconfirm
placement="top"
title="确定删除该角色吗?"
onConfirm={() => onDeleteRole(record)}
okText="确定"
cancelText="取消"
>
<a>删除</a>
</Popconfirm>
</>
) : (
''
)}
</>
);
},
},
];
};
import React, { useRef } from 'react';
import { useHistory } from 'react-router-dom';
import { Button, notification } from 'antd';
import { PageHeaderWrapper } from '@ant-design/pro-layout';
import ProTable from '@ant-design/pro-table';
import { connect } from 'umi';
import { ROLE_MANAGE } from '@/../config/permission.config';
import { deleteRole, getRoleList } from '../service';
// data
import { getColumns } from './data';
const Role = props => {
const { permissions } = props;
const actionRef = useRef();
const formRef = useRef();
const history = useHistory();
// 权限控制
const canEditable = permissions[ROLE_MANAGE.EDITABLE];
const toRoleInfo = ({ id = null } = {}) => {
const query = id ? { id } : {};
history.push({
pathname: '/systemManage/role/roleInfo',
query,
});
};
// 删除
const onDeleteRole = async ({ id }) => {
const res = await deleteRole({ id });
if (res.code !== 2000) {
notification.warning({
message: res.detail,
});
return;
}
notification.success({
message: '删除成功',
});
};
const columns = getColumns({
onDeleteRole,
toRoleInfo,
canEditable,
});
// 表格属性
const tableProps = {
columns,
rowKey: 'id',
request: async params => {
console.log('搜索:', params);
const { current: page, pageSize: size } = params;
const res = await getRoleList({ page, size, ...params });
const { content, totalElements } = res.data;
return {
success: true,
data: content,
total: totalElements,
};
},
toolBarRender: () => [
canEditable ? (
<Button type="primary" onClick={() => toRoleInfo()}>
新增角色
</Button>
) : (
<></>
),
],
};
// 搜索属性
const searchProps = {
collapsed: false,
collapseRender: () => null,
};
return (
<PageHeaderWrapper>
<ProTable actionRef={actionRef} formRef={formRef} {...tableProps} search={false}></ProTable>
</PageHeaderWrapper>
);
};
export default connect(({ menu }) => ({
permissions: menu.permissions,
}))(Role);
export const logList = [
{
id: 1,
name: 'liteng',
actionTime: '2017-01-01',
menu: '系统管理',
subMenu: '账号管理',
status: '编辑',
},
];
export const roleList = [
{
id: 1,
username: 'liteng',
name: '李腾',
createAt: '2017-01-01',
lastAt: '2020-01-01',
role: '超级管理员',
status: '启用',
},
];
export const treeData = [
{
title: '0-0',
key: '0-0',
children: [
{
title: '0-0-0',
key: '0-0-0',
children: [
{ title: '0-0-0-0', key: '0-0-0-0' },
{ title: '0-0-0-1', key: '0-0-0-1' },
{ title: '0-0-0-2', key: '0-0-0-2' },
],
},
{
title: '0-0-1',
key: '0-0-1',
children: [
{ title: '0-0-1-0', key: '0-0-1-0' },
{ title: '0-0-1-1', key: '0-0-1-1' },
{ title: '0-0-1-2', key: '0-0-1-2' },
],
},
{
title: '0-0-2',
key: '0-0-2',
},
],
},
{
title: '0-1',
key: '0-1',
children: [
{ title: '0-1-0-0', key: '0-1-0-0' },
{ title: '0-1-0-1', key: '0-1-0-1' },
{ title: '0-1-0-2', key: '0-1-0-2' },
],
},
{
title: '0-2',
key: '0-2',
},
];
import _ from 'lodash';
import request from '@/utils/request';
import { APP_CODE } from '@/utils/constants';
const PAGE = 1;
const SIZE = 20;
/**
* @name 子账号列表
* @param {Object} params
* @see /yapi/project/509/interface/api/42832
*/
export function getUserList({ page = PAGE, size = SIZE }) {
return request.post(`/v2/user/list/${page}/${size}`, {
data: {},
});
}
/**
* @name 子账号详情
* @param {Object} params
* @param {number} params.id 子账号id
* @see /yapi/project/509/interface/api/42856
*/
export function getUserDetail({ id }) {
return request.get(`/v2/user/info/${id}`);
}
/**
* @name 新增/修改子账号
* @see /yapi/project/509/interface/api/42896
*/
export function addUser(params) {
return request.post('/v2/user/addnew', {
data: params,
});
}
/**
* @name 修改子账号
* @see /yapi/project/509/interface/api/43344
*/
export function updateUser(params) {
return request.post('/v2/user/update', {
data: params,
});
}
/**
* @name 用户禁用
* @param {Object} params
* @param {number} params.id
* @see /yapi/project/509/interface/api/42864
*/
export function userDisabled({ id }) {
return request.post(`/v2/user/disabled/${id}`);
}
/**
* @name 用户启用
* @param {Object} params
* @param {number} params.id
* @see /yapi/project/509/interface/api/42880
*/
export function userEnabled({ id }) {
return request.post(`/v2/user/enabled/${id}`);
}
/**
* @name 删除用户
* @param {Object} params
* @param {number} params.id
* @see /yapi/project/509/interface/api/43120
*/
export function userDelete({ id }) {
return request.post(`/v2/user/delete/${id}`);
}
/**
* @name 重置/修改密码
* @param {Object} params
* @param {string} params.password 确认密码
* @see /yapi/project/509/interface/api/43128
*/
export function resetPassword(params) {
return request.post('/v2/user/password/resetpassword', {
data: params,
});
}
/**
* @name 角色列表-分页
* @param {Object} params
* @see /yapi/project/509/interface/api/41816
*/
export function getRoleList({ page, size }) {
return request.post(`/v2/role/list/${page}/${size}?appCode=${APP_CODE}`, {
data: {},
});
}
/**
* @name 角色详情
* @param {Object} params
* @param {number} params.id 角色id
* @see /yapi/project/509/interface/api/41848
*
*/
export async function getRoleDetail({ id }) {
return request.get(`/v2/role/info/${id}`);
}
/**
* @name 新增角色
* @param {Object} params
* @see /yapi/project/509/interface/api/42920
*/
export async function addResource(params) {
return request.post('/v2/role/addResource', {
data: params,
});
}
/**
* @name 修改角色
* @param {Object} params
* @see /yapi/project/509/interface/api/42928
*/
export async function modifyRole(params) {
return request.post('/v2/role/updateResource', params);
}
/**
* @name 删除角色
* @param {Object} params
* @param {number} params.id 角色id
* @see /yapi/project/509/interface/api/41832
*/
export async function deleteRole({ id }) {
return request.post(`/v2/role/delete/${id}`);
}
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