Commit e7258cdf9037cffd24b1cd165f21e63d3f9c5e03

Authored by 张志伟
1 parent 96ee8bdc

🐹 feat(*): 会滚后的代码恢复

src/pages/Pay/MerchantList/api.ts
... ... @@ -7,45 +7,56 @@ interface mchQuery {
7 7 groupId?: number;
8 8 status?: number;
9 9 }
10   -export async function queryMchList(params: mchQuery, p2: CommonApi.OptionVO[]): http.PromiseResp<IMerchant.MerchantList[]> {
11   - const resp = await request.get<IMerchant.MerchantList[]>(`${PAYMENT}/mch/data/open/list`, { params: { ...params } });
12   - const mList = resp.data || [];
13   - if ((p2?.length || 0) <= 0) {
14   - resp.data = [];
15   - return resp;
16   - }
17   - resp.data = dataTransfer(p2, mList);
18   - return resp;
19   -}
20 10  
21   -function dataTransfer(dealerList: CommonApi.OptionVO[], merchantList: IMerchant.MerchantList[]): IMerchant.MerchantList[] {
22   - const dealerMap: Record<string, IMerchant.MerchantList[]> = groupBy<IMerchant.MerchantList>(merchantList, 'dealerId');
  11 +function dataTransfer(dealerList: CommonApi.OptionVO[], merchantList: IMerchant.OpenMerchantData[]): IMerchant.OpenMerchantData[] {
  12 + const dealerMap: Record<string, IMerchant.OpenMerchantData[]> = groupBy<IMerchant.OpenMerchantData>(merchantList, 'dealerId');
23 13 return dealerList.map((item) => {
24 14 const dealerId = item.id;
25   - const re: IMerchant.MerchantList = { dealerId, dealerName: item.name };
  15 + const re: IMerchant.OpenMerchantData = { dealerId, dealerName: item.name };
26 16 if (dealerId) {
27 17 const merchant = dealerMap[dealerId]?.[0];
28   - re.id = merchant?.id;
29 18 re.groupId = merchant?.groupId;
30   - re.status = merchant?.status;
31   - re.remark = merchant?.remark;
32   - re.mchBriefName = merchant?.mchBriefName;
33   - re.spMchId = merchant?.spMchId;
34   - re.subMchId = merchant?.subMchId;
  19 + re.openNum = merchant?.openNum;
  20 + re.total = merchant?.total;
  21 + re.itemList = merchant?.itemList || [];
35 22 }
36 23 return re;
37 24 });
38 25 }
  26 +/** 查询商家商户号开通总览 */
  27 +export async function queryMchList(params: mchQuery, p2: CommonApi.OptionVO[]): http.PromiseResp<IMerchant.OpenMerchantData[]> {
  28 + const resp = await request.get<IMerchant.OpenMerchantData[]>(`${PAYMENT}/mch/data/base/list`, { params: { ...params } });
  29 + const mList = resp.data || [];
  30 + if ((p2?.length || 0) <= 0) {
  31 + resp.data = [];
  32 + return resp;
  33 + }
  34 + resp.data = dataTransfer(p2, mList);
  35 + return resp;
  36 +}
39 37  
40   -export async function queryMchDetail(dealerId: number): Promise<IMerchant.MerchantDetail> {
41   - const res = await request.get<IMerchant.MerchantDetail>(`${PAYMENT}/mch/data/view/detail`, { params: { dealerId } });
  38 +/** 查询商家商户基础资料情况 */
  39 +export async function queryMchBaseData(dealerId: number): Promise<IMerchant.BaseMerchantData> {
  40 + const res = await request.get<IMerchant.BaseMerchantData>(`${PAYMENT}/mch/data/view/detail`, { params: { dealerId } });
42 41 return res.data || {};
43 42 }
44 43  
45   -export function saveMchDetail(params: IMerchant.MerchantDetail) {
  44 +/** 保存商家商户基础资料 */
  45 +export function saveMchBaseDetail(params: IMerchant.BaseMerchantData) {
46 46 return request.post(`${PAYMENT}/mch/data/save`, params);
47 47 }
48 48  
  49 +/** 保存子商户 */
  50 +export function saveMchDetail(params: IMerchant.MerchantDetail) {
  51 + return request.post(`${PAYMENT}/mch/data/item/save`, params);
  52 +}
  53 +
  54 +/** 删除子商户 */
  55 +export function delMchDetail(id?: number) {
  56 + return request.get(`${PAYMENT}/mch/data/item/delete`, { params: { id } });
  57 +}
  58 +
  59 +/** 查询授权书模板 */
49 60 export function queryTemplate() {
50   - return request.get<string>(`${PAYMENT}/mch/data/auth/temp`);
  61 + return request.get<string>(`${PAYMENT}/mch/data/item/auth/temp`);
51 62 }
... ...
src/pages/Pay/MerchantList/components/BaseEditModal.tsx 0 → 100644
  1 +import { PlusOutlined } from '@ant-design/icons';
  2 +import type { UploadProps } from 'antd';
  3 +import { Modal, Form, Upload, Input, message, Skeleton } from 'antd';
  4 +import React, { useEffect } from 'react';
  5 +import { useRequest } from 'ahooks';
  6 +import { queryMchBaseData, saveMchBaseDetail } from '../api';
  7 +
  8 +interface BaseMerchantFormData {
  9 + dealerName?: string;
  10 + /** 公司营业执照 */
  11 + subjectFid?: IMerchant.FileInfo[];
  12 + /** 法人身份证照片(正面) */
  13 + legalCertFront?: IMerchant.FileInfo[];
  14 + /** 法人身份证照片(反面) */
  15 + legalCertBack?: IMerchant.FileInfo[];
  16 +}
  17 +
  18 +interface ModalProps {
  19 + visible?: boolean;
  20 + dealerId?: number;
  21 + dealerName?: string;
  22 + onCancel?: () => void;
  23 + onRefresh?: () => void;
  24 +}
  25 +export default function EditModal({ visible, dealerId = -1, dealerName, onCancel, onRefresh }: ModalProps) {
  26 + const [form] = Form.useForm();
  27 +
  28 + const { data, run, loading } = useRequest(queryMchBaseData, {
  29 + manual: true,
  30 + onSuccess: (detail, _params) => loadFromState(detail),
  31 + onError: (e, _params) => {
  32 + message.error(e.message);
  33 + },
  34 + });
  35 +
  36 + const { run: save, loading: confirmLoading } = useRequest(saveMchBaseDetail, {
  37 + manual: true,
  38 + onSuccess: (_, _params) => {
  39 + message.success('保存成功');
  40 + onCancel?.();
  41 + onRefresh?.();
  42 + },
  43 + onError: (e, _) => {
  44 + message.error(e.message);
  45 + },
  46 + });
  47 +
  48 + const uploadProps: UploadProps = {
  49 + action: 'api2/file/upload',
  50 + accept: 'image/*',
  51 + maxCount: 1,
  52 + listType: 'picture-card',
  53 + beforeUpload(file) {
  54 + const isLt2M = file.size / 1024 / 1024 < 2;
  55 + if (!isLt2M) {
  56 + message.error('图片大小不能超过 2MB!');
  57 + return Upload.LIST_IGNORE;
  58 + }
  59 + return true;
  60 + },
  61 + };
  62 +
  63 + useEffect(() => {
  64 + if (visible) {
  65 + run(dealerId);
  66 + }
  67 + }, [visible]);
  68 +
  69 + function loadFromState(detail: IMerchant.BaseMerchantData) {
  70 + const commomFileProps: IMerchant.FileInfo = {
  71 + lastModified: Date.now(),
  72 + lastModifiedDate: new Date(),
  73 + name: '',
  74 + percent: 100,
  75 + status: 'done',
  76 + response: { data: '' },
  77 + uid: '-1',
  78 + thumbUrl: '',
  79 + url: '',
  80 + };
  81 + const fromState: BaseMerchantFormData = {};
  82 + fromState.dealerName = detail?.dealerName ?? dealerName;
  83 + if (detail?.subjectFid) {
  84 + fromState.subjectFid = [
  85 + {
  86 + ...commomFileProps,
  87 + response: { data: detail?.subjectFid },
  88 + uid: detail.subjectFid ?? Date.now().toString(),
  89 + thumbUrl: `api/file/show?fid=${detail?.subjectFid}`,
  90 + url: `api/file/show?fid=${detail?.subjectFid}`,
  91 + },
  92 + ];
  93 + }
  94 + const certFidArr = detail?.legalCertFids?.split(',');
  95 + if (certFidArr?.[0]) {
  96 + fromState.legalCertFront = [
  97 + {
  98 + ...commomFileProps,
  99 + response: { data: certFidArr?.[0] },
  100 + uid: certFidArr?.[0] ?? Date.now().toString(),
  101 + thumbUrl: `api/file/show?fid=${certFidArr?.[0]}`,
  102 + url: `api/file/show?fid=${certFidArr?.[0]}`,
  103 + },
  104 + ];
  105 + }
  106 + if (certFidArr?.[1]) {
  107 + fromState.legalCertBack = [
  108 + {
  109 + ...commomFileProps,
  110 + response: { data: certFidArr?.[1] },
  111 + uid: certFidArr?.[1] ?? Date.now().toString(),
  112 + thumbUrl: `api/file/show?fid=${certFidArr?.[1]}`,
  113 + url: `api/file/show?fid=${certFidArr?.[1]}`,
  114 + },
  115 + ];
  116 + }
  117 + form.setFieldsValue(fromState);
  118 + }
  119 +
  120 + const normFile = (e: any) => {
  121 + if (Array.isArray(e)) {
  122 + return e;
  123 + }
  124 + return e?.fileList;
  125 + };
  126 +
  127 + function handleSave(feildValue: BaseMerchantFormData) {
  128 + let saveData: IMerchant.BaseMerchantData = { dealerId, dealerName };
  129 + if (data?.id) {
  130 + saveData = { ...data };
  131 + }
  132 + saveData.subjectFid = feildValue.subjectFid?.[0]?.response?.data ?? '';
  133 + saveData.legalCertFids = `${feildValue.legalCertFront?.[0]?.response?.data ?? ''},${feildValue.legalCertBack?.[0]?.response?.data ?? ''}`;
  134 + save(saveData);
  135 + }
  136 +
  137 + return (
  138 + <Modal
  139 + title="资料维护"
  140 + width="65%"
  141 + open={visible}
  142 + onCancel={onCancel}
  143 + onOk={form.submit}
  144 + afterClose={() => form.resetFields()}
  145 + confirmLoading={confirmLoading}
  146 + >
  147 + <Skeleton loading={loading} active={loading} paragraph={{ rows: 16 }}>
  148 + <Form form={form} onFinish={handleSave} labelWrap labelCol={{ span: 6 }}>
  149 + <Form.Item label="商家" name="dealerName">
  150 + <Input disabled style={{ maxWidth: 350 }} />
  151 + </Form.Item>
  152 +
  153 + <Form.Item noStyle shouldUpdate={(prevValues, curValues) => prevValues.subjectFid != curValues.subjectFid}>
  154 + {({ getFieldValue }) => {
  155 + const subjectFidLength = getFieldValue('subjectFid')?.length ?? 0;
  156 + return (
  157 + <Form.Item
  158 + label="公司营业执照"
  159 + name="subjectFid"
  160 + valuePropName="fileList"
  161 + getValueFromEvent={normFile}
  162 + rules={[{ required: true, message: '营业执照照片不能为空' }]}
  163 + extra={<p style={{ margin: 0, fontSize: 12 }}>营业执照正面清晰完整照片且不超过2M大小的JPG、BMP、PNG照片</p>}
  164 + >
  165 + <Upload {...uploadProps}>
  166 + {subjectFidLength >= 1 ? null : (
  167 + <button style={{ border: 0, background: 'none' }} type="button">
  168 + <PlusOutlined rev="" />
  169 + <div style={{ marginTop: 8 }}>上传</div>
  170 + </button>
  171 + )}
  172 + </Upload>
  173 + </Form.Item>
  174 + );
  175 + }}
  176 + </Form.Item>
  177 + <Form.Item noStyle shouldUpdate={(prevValues, curValues) => prevValues.legalCertFront != curValues.legalCertFront}>
  178 + {({ getFieldValue }) => {
  179 + const fileLength = getFieldValue('legalCertFront')?.length ?? 0;
  180 + return (
  181 + <Form.Item
  182 + label="法人身份证人像面照片"
  183 + name="legalCertFront"
  184 + valuePropName="fileList"
  185 + getValueFromEvent={normFile}
  186 + rules={[{ required: true, message: '身份证照片不能为空' }]}
  187 + extra={<p style={{ margin: 0, fontSize: 12 }}>身份证人像面清晰完整照片且不超过2M大小的JPG、BMP、PNG格式照片</p>}
  188 + >
  189 + <Upload {...uploadProps}>
  190 + {fileLength >= 1 ? null : (
  191 + <button style={{ border: 0, background: 'none' }} type="button">
  192 + <PlusOutlined rev="" />
  193 + <div style={{ marginTop: 8 }}>上传</div>
  194 + </button>
  195 + )}
  196 + </Upload>
  197 + </Form.Item>
  198 + );
  199 + }}
  200 + </Form.Item>
  201 + <Form.Item noStyle shouldUpdate={(prevValues, curValues) => prevValues.legalCertBack != curValues.legalCertBack}>
  202 + {({ getFieldValue }) => {
  203 + const legalCertBackLength = getFieldValue('legalCertBack')?.length ?? 0;
  204 + return (
  205 + <Form.Item
  206 + label="法人身份证国徽面照片"
  207 + name="legalCertBack"
  208 + valuePropName="fileList"
  209 + getValueFromEvent={normFile}
  210 + rules={[{ required: true, message: '身份证照片不能为空' }]}
  211 + extra={<p style={{ margin: 0, fontSize: 12 }}>身份证国徽面清晰完整照片且不超过2M大小的JPG、BMP、PNG格式照片</p>}
  212 + >
  213 + <Upload {...uploadProps}>
  214 + {legalCertBackLength >= 1 ? null : (
  215 + <button style={{ border: 0, background: 'none' }} type="button">
  216 + <PlusOutlined rev="" />
  217 + <div style={{ marginTop: 8 }}>上传</div>
  218 + </button>
  219 + )}
  220 + </Upload>
  221 + </Form.Item>
  222 + );
  223 + }}
  224 + </Form.Item>
  225 + </Form>
  226 + </Skeleton>
  227 + </Modal>
  228 + );
  229 +}
... ...
src/pages/Pay/MerchantList/components/EditModal.tsx
1 1 import { PlusOutlined } from '@ant-design/icons';
2 2 import type { UploadProps } from 'antd';
3   -import { Modal, Form, Upload, Input, Checkbox, message, Skeleton } from 'antd';
  3 +import { Modal, Form, Upload, Input, Checkbox, message } from 'antd';
4 4 import React, { useEffect } from 'react';
5 5 import { useRequest, useSafeState } from 'ahooks';
6   -import { queryMchDetail, saveMchDetail, queryTemplate } from '../api';
  6 +import { saveMchDetail, queryTemplate } from '../api';
7 7 import { MerchantStatus } from '../entity';
8 8  
9 9 interface ModalProps {
10 10 visible?: boolean;
11   - record?: IMerchant.MerchantList;
  11 + record?: IMerchant.MerchantDetail;
12 12 onCancel?: () => void;
13 13 onRefresh?: () => void;
14 14 }
15 15 export default function EditModal({ visible, record, onCancel, onRefresh }: ModalProps) {
16 16 const [form] = Form.useForm();
17 17 const [template, setTemplate] = useSafeState<string>();
18   - const { data, run, loading } = useRequest(queryMchDetail, {
19   - manual: true,
20   - onSuccess: (detail, _params) => loadFromState(detail),
21   - onError: (e, _params) => {
22   - message.error(e.message);
23   - },
24   - });
25 18  
26 19 const { run: save, loading: confirmLoading } = useRequest(saveMchDetail, {
27 20 manual: true,
... ... @@ -55,11 +48,11 @@ export default function EditModal({ visible, record, onCancel, onRefresh }: Moda
55 48 queryTemplate().then((res) => {
56 49 setTemplate(res.data ?? '');
57 50 });
  51 +
58 52 if (record?.status && record?.dealerId) {
59   - run(record.dealerId);
  53 + loadFromState(record);
60 54 } else {
61 55 const fromState: IMerchant.MerchantFromState = {};
62   - fromState.dealerName = record?.dealerName;
63 56 fromState.mangerSameLegal = true;
64 57 form.setFieldsValue(fromState);
65 58 }
... ... @@ -79,41 +72,6 @@ export default function EditModal({ visible, record, onCancel, onRefresh }: Moda
79 72 url: '',
80 73 };
81 74 const fromState: IMerchant.MerchantFromState = {};
82   - fromState.dealerName = detail?.dealerName;
83   - if (detail?.subjectFid) {
84   - fromState.subjectFid = [
85   - {
86   - ...commomFileProps,
87   - response: { data: detail?.subjectFid },
88   - uid: detail.subjectFid ?? Date.now().toString(),
89   - thumbUrl: `api/file/show?fid=${detail?.subjectFid}`,
90   - url: `api/file/show?fid=${detail?.subjectFid}`,
91   - },
92   - ];
93   - }
94   - const certFidArr = detail?.legalCertFids?.split(',');
95   - if (certFidArr?.[0]) {
96   - fromState.legalCertFront = [
97   - {
98   - ...commomFileProps,
99   - response: { data: certFidArr?.[0] },
100   - uid: certFidArr?.[0] ?? Date.now().toString(),
101   - thumbUrl: `api/file/show?fid=${certFidArr?.[0]}`,
102   - url: `api/file/show?fid=${certFidArr?.[0]}`,
103   - },
104   - ];
105   - }
106   - if (certFidArr?.[1]) {
107   - fromState.legalCertBack = [
108   - {
109   - ...commomFileProps,
110   - response: { data: certFidArr?.[1] },
111   - uid: certFidArr?.[1] ?? Date.now().toString(),
112   - thumbUrl: `api/file/show?fid=${certFidArr?.[1]}`,
113   - url: `api/file/show?fid=${certFidArr?.[1]}`,
114   - },
115   - ];
116   - }
117 75 fromState.publicBankNo = detail?.publicBankNo;
118 76 fromState.publicBankName = detail?.publicBankName;
119 77 fromState.publicBankOrg = detail?.publicBankOrg;
... ... @@ -163,12 +121,10 @@ export default function EditModal({ visible, record, onCancel, onRefresh }: Moda
163 121 };
164 122  
165 123 function handleSave(feildValue: IMerchant.MerchantFromState) {
166   - let saveData: IMerchant.MerchantDetail = { dealerId: record?.dealerId, dealerName: record?.dealerName };
  124 + let saveData: IMerchant.MerchantDetail = { dealerId: record?.dealerId };
167 125 if (record?.id) {
168   - saveData = { ...data };
  126 + saveData = { ...record };
169 127 }
170   - saveData.subjectFid = feildValue.subjectFid?.[0]?.response?.data ?? '';
171   - saveData.legalCertFids = `${feildValue.legalCertFront?.[0]?.response?.data ?? ''},${feildValue.legalCertBack?.[0]?.response?.data ?? ''}`;
172 128 saveData.publicBankNo = feildValue?.publicBankNo;
173 129 saveData.publicBankName = feildValue?.publicBankName;
174 130 saveData.publicBankOrg = feildValue?.publicBankOrg;
... ... @@ -194,225 +150,146 @@ export default function EditModal({ visible, record, onCancel, onRefresh }: Moda
194 150 afterClose={() => form.resetFields()}
195 151 confirmLoading={confirmLoading}
196 152 >
197   - <Skeleton loading={loading} active={loading} paragraph={{ rows: 16 }}>
198   - <Form form={form} onFinish={handleSave} labelWrap labelCol={{ span: 6 }} disabled={MerchantStatus['已开通'] == record?.status}>
199   - <Form.Item label="商家" name="dealerName">
200   - <Input disabled style={{ maxWidth: 350 }} />
201   - </Form.Item>
202   -
203   - <Form.Item noStyle shouldUpdate={(prevValues, curValues) => prevValues.subjectFid != curValues.subjectFid}>
204   - {({ getFieldValue }) => {
205   - const subjectFidLength = getFieldValue('subjectFid')?.length ?? 0;
206   - return (
207   - <Form.Item
208   - label="公司营业执照"
209   - name="subjectFid"
210   - valuePropName="fileList"
211   - getValueFromEvent={normFile}
212   - rules={[{ required: true, message: '营业执照照片不能为空' }]}
213   - extra={<p style={{ margin: 0, fontSize: 12 }}>营业执照正面清晰完整照片且不超过2M大小的JPG、BMP、PNG照片</p>}
214   - >
215   - <Upload {...uploadProps}>
216   - {subjectFidLength >= 1 ? null : (
217   - <button style={{ border: 0, background: 'none' }} type="button">
218   - <PlusOutlined rev="" />
219   - <div style={{ marginTop: 8 }}>上传</div>
220   - </button>
221   - )}
222   - </Upload>
223   - </Form.Item>
224   - );
225   - }}
226   - </Form.Item>
227   - <Form.Item noStyle shouldUpdate={(prevValues, curValues) => prevValues.legalCertFront != curValues.legalCertFront}>
228   - {({ getFieldValue }) => {
229   - const fileLength = getFieldValue('legalCertFront')?.length ?? 0;
230   - return (
231   - <Form.Item
232   - label="法人身份证人像面照片"
233   - name="legalCertFront"
234   - valuePropName="fileList"
235   - getValueFromEvent={normFile}
236   - rules={[{ required: true, message: '身份证照片不能为空' }]}
237   - extra={<p style={{ margin: 0, fontSize: 12 }}>身份证人像面清晰完整照片且不超过2M大小的JPG、BMP、PNG格式照片</p>}
238   - >
239   - <Upload {...uploadProps}>
240   - {fileLength >= 1 ? null : (
241   - <button style={{ border: 0, background: 'none' }} type="button">
242   - <PlusOutlined rev="" />
243   - <div style={{ marginTop: 8 }}>上传</div>
244   - </button>
245   - )}
246   - </Upload>
  153 + <Form form={form} onFinish={handleSave} labelWrap labelCol={{ span: 6 }} disabled={MerchantStatus['已开通'] == record?.status}>
  154 + <Form.Item label="对公账户" name="publicBankNo" rules={[{ required: true, message: '对公账户不能为空' }]}>
  155 + <Input placeholder="请输入" type="number" style={{ maxWidth: 350 }} />
  156 + </Form.Item>
  157 + <Form.Item label="对公账户开户银行" name="publicBankName" rules={[{ required: true, message: '对公账户开户银行不能为空' }]}>
  158 + <Input placeholder="请输入" style={{ maxWidth: 350 }} />
  159 + </Form.Item>
  160 + <Form.Item
  161 + label="对公账户开户支行"
  162 + name="publicBankOrg"
  163 + rules={[{ required: true, message: '对公账户开户支行不能为空' }]}
  164 + extra={<p style={{ margin: 0, marginTop: 10, fontSize: 12 }}>开户支行全称(省份-城市-支行)</p>}
  165 + >
  166 + <Input placeholder="请输入开户支行(省份-城市-支行)" style={{ maxWidth: 350 }} />
  167 + </Form.Item>
  168 + <Form.Item
  169 + label="商户简称"
  170 + name="mchBriefName"
  171 + rules={[{ required: true, min: 2, max: 30, message: '请输入商户简称' }]}
  172 + extra={
  173 + <>
  174 + <p style={{ margin: 0, marginTop: 10, fontSize: 12 }}>1、在支付完成页向买家展示,需与微信经营类目相关。</p>
  175 + <p style={{ margin: 0, fontSize: 12 }}>2、简称要求(2-30字):</p>
  176 + <p style={{ margin: 0, fontSize: 12 }}>
  177 + ① 不支持单纯以人名来命名,若为个体户经营,可用“个体户+经营者名称”或“经营者名称+业务”命名,如“个体户张三”或“张三餐饮店”;
  178 + </p>
  179 + <p style={{ margin: 0, fontSize: 12 }}>② 不支持无实际意义的文案,如“XX特约商户”、“800”、“XX客服电话XXX”;</p>
  180 + </>
  181 + }
  182 + >
  183 + <Input placeholder="请输入" maxLength={30} style={{ maxWidth: 350 }} />
  184 + </Form.Item>
  185 + <Form.Item
  186 + label="客服电话"
  187 + name="mchServicePhone"
  188 + rules={[{ required: true, type: 'string', pattern: /^\d*$/, message: '请输入正确的电话号码' }]}
  189 + extra={<p style={{ margin: 0, marginTop: 10, fontSize: 12 }}>请输入正确的客服电话以便微信回电核实信息。</p>}
  190 + >
  191 + <Input placeholder="请输入" maxLength={32} style={{ maxWidth: 350 }} />
  192 + </Form.Item>
  193 + <Form.Item label="管理者是否法人" name="mangerSameLegal" valuePropName="checked">
  194 + <Checkbox>是</Checkbox>
  195 + </Form.Item>
  196 + <Form.Item noStyle shouldUpdate={(prevValues, curValues) => prevValues.mangerSameLegal != curValues.mangerSameLegal}>
  197 + {({ getFieldValue }) => {
  198 + const _mangerSameLegal = getFieldValue('mangerSameLegal');
  199 + return !_mangerSameLegal ? (
  200 + <>
  201 + <Form.Item noStyle shouldUpdate={(prevValues, curValues) => prevValues.mangerCertFront != curValues.mangerCertFront}>
  202 + {({ getFieldValue }) => {
  203 + const fileLength = getFieldValue('mangerCertFront')?.length ?? 0;
  204 + return (
  205 + <Form.Item
  206 + label="管理者身份证人像面照片"
  207 + name="mangerCertFront"
  208 + valuePropName="fileList"
  209 + getValueFromEvent={normFile}
  210 + rules={[{ required: true, message: '身份证照片不能为空' }]}
  211 + extra={<p style={{ margin: 0, fontSize: 12 }}>身份证人像面清晰完整照片且不超过2M大小的JPG、BMP、PNG格式照片</p>}
  212 + >
  213 + <Upload {...uploadProps}>
  214 + {fileLength >= 1 ? null : (
  215 + <button style={{ border: 0, background: 'none' }} type="button">
  216 + <PlusOutlined rev="" />
  217 + <div style={{ marginTop: 8 }}>上传</div>
  218 + </button>
  219 + )}
  220 + </Upload>
  221 + </Form.Item>
  222 + );
  223 + }}
247 224 </Form.Item>
248   - );
249   - }}
250   - </Form.Item>
251   - <Form.Item noStyle shouldUpdate={(prevValues, curValues) => prevValues.legalCertBack != curValues.legalCertBack}>
252   - {({ getFieldValue }) => {
253   - const legalCertBackLength = getFieldValue('legalCertBack')?.length ?? 0;
254   - return (
255   - <Form.Item
256   - label="法人身份证国徽面照片"
257   - name="legalCertBack"
258   - valuePropName="fileList"
259   - getValueFromEvent={normFile}
260   - rules={[{ required: true, message: '身份证照片不能为空' }]}
261   - extra={<p style={{ margin: 0, fontSize: 12 }}>身份证国徽面清晰完整照片且不超过2M大小的JPG、BMP、PNG格式照片</p>}
262   - >
263   - <Upload {...uploadProps}>
264   - {legalCertBackLength >= 1 ? null : (
265   - <button style={{ border: 0, background: 'none' }} type="button">
266   - <PlusOutlined rev="" />
267   - <div style={{ marginTop: 8 }}>上传</div>
268   - </button>
269   - )}
270   - </Upload>
  225 + <Form.Item noStyle shouldUpdate={(prevValues, curValues) => prevValues.mangerCertBack != curValues.mangerCertBack}>
  226 + {({ getFieldValue }) => {
  227 + const fileLength = getFieldValue('mangerCertBack')?.length ?? 0;
  228 + return (
  229 + <Form.Item
  230 + label="管理者身份证国徽面照片"
  231 + name="mangerCertBack"
  232 + valuePropName="fileList"
  233 + getValueFromEvent={normFile}
  234 + rules={[{ required: true, message: '身份证照片不能为空' }]}
  235 + extra={<p style={{ margin: 0, fontSize: 12 }}>身份证国徽份证人像面清晰完整照片且不超过2M大小的JPG、BMP、PNG格式照片</p>}
  236 + >
  237 + <Upload {...uploadProps}>
  238 + {fileLength >= 1 ? null : (
  239 + <button style={{ border: 0, background: 'none' }} type="button">
  240 + <PlusOutlined rev="" />
  241 + <div style={{ marginTop: 8 }}>上传</div>
  242 + </button>
  243 + )}
  244 + </Upload>
  245 + </Form.Item>
  246 + );
  247 + }}
271 248 </Form.Item>
272   - );
273   - }}
274   - </Form.Item>
275 249  
276   - <Form.Item label="对公账户" name="publicBankNo" rules={[{ required: true, message: '对公账户不能为空' }]}>
277   - <Input placeholder="请输入" type="number" style={{ maxWidth: 350 }} />
278   - </Form.Item>
279   - <Form.Item label="对公账户开户银行" name="publicBankName" rules={[{ required: true, message: '对公账户开户银行不能为空' }]}>
280   - <Input placeholder="请输入" style={{ maxWidth: 350 }} />
281   - </Form.Item>
282   - <Form.Item
283   - label="对公账户开户支行"
284   - name="publicBankOrg"
285   - rules={[{ required: true, message: '对公账户开户支行不能为空' }]}
286   - extra={<p style={{ margin: 0, marginTop: 10, fontSize: 12 }}>开户支行全称(省份-城市-支行)</p>}
287   - >
288   - <Input placeholder="请输入开户支行(省份-城市-支行)" style={{ maxWidth: 350 }} />
289   - </Form.Item>
290   - <Form.Item
291   - label="商户简称"
292   - name="mchBriefName"
293   - rules={[{ required: true, min: 2, max: 30, message: '请输入商户简称' }]}
294   - extra={
295   - <>
296   - <p style={{ margin: 0, marginTop: 10, fontSize: 12 }}>1、在支付完成页向买家展示,需与微信经营类目相关。</p>
297   - <p style={{ margin: 0, fontSize: 12 }}>2、简称要求(2-30字):</p>
298   - <p style={{ margin: 0, fontSize: 12 }}>
299   - ① 不支持单纯以人名来命名,若为个体户经营,可用“个体户+经营者名称”或“经营者名称+业务”命名,如“个体户张三”或“张三餐饮店”;
300   - </p>
301   - <p style={{ margin: 0, fontSize: 12 }}>② 不支持无实际意义的文案,如“XX特约商户”、“800”、“XX客服电话XXX”;</p>
  250 + <Form.Item noStyle shouldUpdate={(prevValues, curValues) => prevValues.mangerAuthFile != curValues.mangerAuthFile}>
  251 + {({ getFieldValue }) => {
  252 + const fileLength = getFieldValue('mangerAuthFile')?.length ?? 0;
  253 + return (
  254 + <Form.Item
  255 + label="管理者业务办理授权书"
  256 + name="mangerAuthFile"
  257 + valuePropName="fileList"
  258 + getValueFromEvent={normFile}
  259 + rules={[{ required: true, message: '授权书不能为空' }]}
  260 + extra={
  261 + <a href={`api/file/show?fid=${template}`} rel="noreferrer" target="_blank">
  262 + 下载授权书模板
  263 + </a>
  264 + }
  265 + >
  266 + <Upload {...uploadProps}>
  267 + {fileLength >= 1 ? null : (
  268 + <button style={{ border: 0, background: 'none' }} type="button">
  269 + <PlusOutlined rev="" />
  270 + <div style={{ marginTop: 8 }}>上传</div>
  271 + </button>
  272 + )}
  273 + </Upload>
  274 + </Form.Item>
  275 + );
  276 + }}
  277 + </Form.Item>
302 278 </>
303   - }
304   - >
305   - <Input placeholder="请输入" maxLength={30} style={{ maxWidth: 350 }} />
306   - </Form.Item>
307   - <Form.Item
308   - label="客服电话"
309   - name="mchServicePhone"
310   - rules={[{ required: true, type: 'string', pattern: /^\d*$/, message: '请输入正确的电话号码' }]}
311   - extra={<p style={{ margin: 0, marginTop: 10, fontSize: 12 }}>请输入正确的客服电话以便微信回电核实信息。</p>}
312   - >
313   - <Input placeholder="请输入" maxLength={32} style={{ maxWidth: 350 }} />
314   - </Form.Item>
315   - <Form.Item label="管理者是否法人" name="mangerSameLegal" valuePropName="checked">
316   - <Checkbox>是</Checkbox>
317   - </Form.Item>
318   - <Form.Item noStyle shouldUpdate={(prevValues, curValues) => prevValues.mangerSameLegal != curValues.mangerSameLegal}>
319   - {({ getFieldValue }) => {
320   - const _mangerSameLegal = getFieldValue('mangerSameLegal');
321   - return !_mangerSameLegal ? (
322   - <>
323   - <Form.Item noStyle shouldUpdate={(prevValues, curValues) => prevValues.mangerCertFront != curValues.mangerCertFront}>
324   - {({ getFieldValue }) => {
325   - const fileLength = getFieldValue('mangerCertFront')?.length ?? 0;
326   - return (
327   - <Form.Item
328   - label="管理者身份证人像面照片"
329   - name="mangerCertFront"
330   - valuePropName="fileList"
331   - getValueFromEvent={normFile}
332   - rules={[{ required: true, message: '身份证照片不能为空' }]}
333   - extra={<p style={{ margin: 0, fontSize: 12 }}>身份证人像面清晰完整照片且不超过2M大小的JPG、BMP、PNG格式照片</p>}
334   - >
335   - <Upload {...uploadProps}>
336   - {fileLength >= 1 ? null : (
337   - <button style={{ border: 0, background: 'none' }} type="button">
338   - <PlusOutlined rev="" />
339   - <div style={{ marginTop: 8 }}>上传</div>
340   - </button>
341   - )}
342   - </Upload>
343   - </Form.Item>
344   - );
345   - }}
346   - </Form.Item>
347   - <Form.Item noStyle shouldUpdate={(prevValues, curValues) => prevValues.mangerCertBack != curValues.mangerCertBack}>
348   - {({ getFieldValue }) => {
349   - const fileLength = getFieldValue('mangerCertBack')?.length ?? 0;
350   - return (
351   - <Form.Item
352   - label="管理者身份证国徽面照片"
353   - name="mangerCertBack"
354   - valuePropName="fileList"
355   - getValueFromEvent={normFile}
356   - rules={[{ required: true, message: '身份证照片不能为空' }]}
357   - extra={<p style={{ margin: 0, fontSize: 12 }}>身份证国徽份证人像面清晰完整照片且不超过2M大小的JPG、BMP、PNG格式照片</p>}
358   - >
359   - <Upload {...uploadProps}>
360   - {fileLength >= 1 ? null : (
361   - <button style={{ border: 0, background: 'none' }} type="button">
362   - <PlusOutlined rev="" />
363   - <div style={{ marginTop: 8 }}>上传</div>
364   - </button>
365   - )}
366   - </Upload>
367   - </Form.Item>
368   - );
369   - }}
370   - </Form.Item>
371   -
372   - <Form.Item noStyle shouldUpdate={(prevValues, curValues) => prevValues.mangerAuthFile != curValues.mangerAuthFile}>
373   - {({ getFieldValue }) => {
374   - const fileLength = getFieldValue('mangerAuthFile')?.length ?? 0;
375   - return (
376   - <Form.Item
377   - label="管理者业务办理授权书"
378   - name="mangerAuthFile"
379   - valuePropName="fileList"
380   - getValueFromEvent={normFile}
381   - rules={[{ required: true, message: '授权书不能为空' }]}
382   - extra={
383   - <a href={`api/file/show?fid=${template}`} rel="noreferrer" target="_blank">
384   - 下载授权书模板
385   - </a>
386   - }
387   - >
388   - <Upload {...uploadProps}>
389   - {fileLength >= 1 ? null : (
390   - <button style={{ border: 0, background: 'none' }} type="button">
391   - <PlusOutlined rev="" />
392   - <div style={{ marginTop: 8 }}>上传</div>
393   - </button>
394   - )}
395   - </Upload>
396   - </Form.Item>
397   - );
398   - }}
399   - </Form.Item>
400   - </>
401   - ) : null;
402   - }}
403   - </Form.Item>
404   - <Form.Item
405   - label="管理者电话"
406   - name="mangerPhone"
407   - rules={[{ required: true, type: 'string', pattern: /^\d*$/, message: '请输入正确的电话号码' }]}
408   - >
409   - <Input placeholder="请输入" maxLength={32} style={{ maxWidth: 350 }} />
410   - </Form.Item>
411   - <Form.Item label="管理者邮箱" name="mangerEmail" required rules={[{ required: true, type: 'email', message: '请输入正确的邮箱' }]}>
412   - <Input placeholder="请输入" type="email" maxLength={128} style={{ maxWidth: 350 }} />
413   - </Form.Item>
414   - </Form>
415   - </Skeleton>
  279 + ) : null;
  280 + }}
  281 + </Form.Item>
  282 + <Form.Item
  283 + label="管理者电话"
  284 + name="mangerPhone"
  285 + rules={[{ required: true, type: 'string', pattern: /^\d*$/, message: '请输入正确的电话号码' }]}
  286 + >
  287 + <Input placeholder="请输入" maxLength={32} style={{ maxWidth: 350 }} />
  288 + </Form.Item>
  289 + <Form.Item label="管理者邮箱" name="mangerEmail" required rules={[{ required: true, type: 'email', message: '请输入正确的邮箱' }]}>
  290 + <Input placeholder="请输入" type="email" maxLength={128} style={{ maxWidth: 350 }} />
  291 + </Form.Item>
  292 + </Form>
416 293 </Modal>
417 294 );
418 295 }
... ...
src/pages/Pay/MerchantList/components/List.tsx
1   -import { Badge, Table } from 'antd';
  1 +import type { TableColumnsType } from 'antd';
  2 +import { Badge, Divider, Popconfirm, Table, message } from 'antd';
2 3 import type { ColumnsType } from 'antd/lib/table';
3 4 import React, { useMemo } from 'react';
4   -import { MerchantStatus } from '../entity';
5 5 import EditModal from './EditModal';
  6 +import BaseEditModal from './BaseEditModal';
6 7 import { useSafeState } from 'ahooks';
  8 +import { MerchantStatus } from '../entity';
  9 +import { delMchDetail } from '../api';
7 10  
8 11 interface Props {
9 12 loading?: boolean;
10   - dataSource?: IMerchant.MerchantList[];
  13 + dataSource?: IMerchant.OpenMerchantData[];
11 14 searchText?: string;
12 15 onRefresh?: () => void;
13 16 }
14 17  
15 18 export default function Index({ loading, dataSource = [], searchText, onRefresh }: Props) {
16 19 const [visible, setVisible] = useSafeState<boolean>();
17   - const [itemRow, setItemRow] = useSafeState<IMerchant.MerchantList>();
  20 + const [itemRow, setItemRow] = useSafeState<IMerchant.OpenMerchantData>();
  21 + const [baseVisible, setBaseVisible] = useSafeState<boolean>();
  22 + const [dealerInfo, setDealerInfo] = useSafeState<IMerchant.OpenMerchantData>();
  23 +
  24 + const badgeArr: ('error' | 'default' | 'success')[] = useMemo(() => ['error', 'default', 'success'], []);
18 25  
19 26 const list = useMemo(() => {
20 27 if (searchText) {
... ... @@ -22,55 +29,94 @@ export default function Index({ loading, dataSource = [], searchText, onRefresh
22 29 }
23 30 return dataSource;
24 31 }, [searchText, dataSource]);
25   - const badgeArr: ('error' | 'default' | 'success')[] = useMemo(() => ['error', 'default', 'success'], []);
  32 +
26 33 //@ts-ignore
27   - const columns: ColumnsType<IMerchant.MerchantList> = useMemo(
  34 + const columns: ColumnsType<IMerchant.OpenMerchantData> = useMemo(
28 35 () => [
29 36 {
30   - title: '编号',
  37 + title: '商家编号',
31 38 dataIndex: 'dealerId',
32 39 key: 'dealerId',
33   - width: 62,
34 40 },
35 41 {
36 42 title: '商家名称',
37 43 dataIndex: 'dealerName',
38 44 key: 'dealerName',
39   - width: '35%',
40 45 },
41 46 {
42   - title: '开通状态',
43   - dataIndex: 'status',
44   - key: 'status',
45   - filters: [
46   - {
47   - text: '草稿',
48   - value: 1,
49   - },
50   - {
51   - text: '已开通',
52   - value: 2,
53   - },
54   - ],
55   - onFilter: (value: number, record) => record.status == value,
56   - render: (text, _record) => {
57   - return text ? <Badge status={badgeArr[Number(text)]} text={MerchantStatus[text]} /> : '-';
58   - },
  47 + title: '已开通数量',
  48 + dataIndex: 'openNum',
  49 + key: 'openNum',
  50 + render: (text, _record) => text ?? '-',
59 51 },
60 52 {
  53 + title: '商户总数',
  54 + dataIndex: 'total',
  55 + key: 'total',
  56 + render: (text, _record) => text ?? '-',
  57 + },
  58 + {
  59 + title: '商家资料',
  60 + dataIndex: '',
  61 + key: 'action',
  62 + render: (_, record) => (
  63 + <>
  64 + <a
  65 + onClick={() => {
  66 + setDealerInfo(record);
  67 + setBaseVisible(true);
  68 + }}
  69 + >
  70 + 资料维护
  71 + </a>
  72 + {(record?.total ?? -1) >= 0 && <Divider type="vertical" />}
  73 + {(record?.total ?? -1) >= 0 && <a onClick={() => showMerchModal({ dealerId: record.dealerId })}>新增子商户</a>}
  74 + </>
  75 + ),
  76 + },
  77 + ],
  78 + [],
  79 + );
  80 +
  81 + function deleteData(id?: number) {
  82 + delMchDetail(id)
  83 + .then(() => onRefresh?.())
  84 + .catch((e) => message.error(e.message));
  85 + }
  86 +
  87 + const expandedRowRender = (merchan: IMerchant.OpenMerchantData, _index: number, _indent: number, _expanded: boolean) => {
  88 + const c_columns: TableColumnsType<IMerchant.MerchantDetail> = [
  89 + // {
  90 + // title: '编号',
  91 + // dataIndex: 'id',
  92 + // key: 'id',
  93 + // width: 50
  94 + // },
  95 + {
61 96 title: '平台商户号',
62 97 dataIndex: 'spMchId',
63 98 key: 'spMchId',
  99 + width: 120
  100 + },
  101 + {
  102 + title: '子商户号',
  103 + dataIndex: 'subMchId',
  104 + key: 'subMchId',
  105 + width: 120
64 106 },
65 107 {
66   - title: '商户简称',
  108 + title: '商户简称',
67 109 dataIndex: 'mchBriefName',
68 110 key: 'mchBriefName',
69 111 },
70 112 {
71   - title: '子商户号',
72   - dataIndex: 'subMchId',
73   - key: 'subMchId',
  113 + title: '状态',
  114 + dataIndex: 'status',
  115 + key: 'status',
  116 + render: (text, _record) => {
  117 + return text ? <Badge status={badgeArr[Number(text)]} text={MerchantStatus[text]} /> : '-';
  118 + },
  119 + width: 120
74 120 },
75 121 {
76 122 title: '备注',
... ... @@ -81,34 +127,57 @@ export default function Index({ loading, dataSource = [], searchText, onRefresh
81 127 title: '操作',
82 128 dataIndex: '',
83 129 key: 'action',
84   - render: (_, record) => (
  130 + render: (_text, record) => (
85 131 <>
86   - {!record?.status && <a onClick={() => showModal(record)}>新增</a>}
87 132 {record?.status == MerchantStatus['已开通'] && (
88   - <a style={{ color: 'hsl(102, 53%, 61%)' }} onClick={() => showModal(record)}>
  133 + <a style={{ color: 'hsl(102, 53%, 61%)' }} onClick={() => showMerchModal(record)}>
89 134 查看
90 135 </a>
91 136 )}
92 137 {record?.status == MerchantStatus['草稿'] && (
93   - <a style={{ color: 'rgb(45, 183, 245)' }} onClick={() => showModal(record)}>
94   - 编辑
95   - </a>
  138 + <>
  139 + <a style={{ color: 'rgb(45, 183, 245)' }} onClick={() => showMerchModal(record)}>
  140 + 编辑
  141 + </a>
  142 + <Divider type="vertical" />
  143 + <Popconfirm title="是否删除此项?" onConfirm={() => deleteData(record.id)} okText="确定" cancelText="取消">
  144 + <a style={{ color: 'red' }}>删除</a>
  145 + </Popconfirm>
  146 + </>
96 147 )}
97 148 </>
98 149 ),
99 150 },
100   - ],
101   - [],
102   - );
  151 + ];
  152 +
  153 + return <Table rowKey="id" size="small" columns={c_columns} dataSource={merchan?.itemList || []} pagination={false} />;
  154 + };
103 155  
104   - function showModal(row: IMerchant.MerchantList) {
  156 + function showMerchModal(row: IMerchant.MerchantDetail) {
105 157 setItemRow(row);
106 158 setVisible(true);
107 159 }
108 160  
109 161 return (
110 162 <>
111   - <Table rowKey="dealerId" columns={columns} scroll={{ x: 'max-content' }} bordered loading={loading} dataSource={list} />
  163 + <Table
  164 + rowKey="dealerId"
  165 + expandable={{
  166 + expandedRowRender,
  167 + rowExpandable: (record) => (record?.total ?? 0) > 0,
  168 + }}
  169 + columns={columns}
  170 + bordered
  171 + loading={loading}
  172 + dataSource={list}
  173 + />
  174 + <BaseEditModal
  175 + visible={baseVisible}
  176 + onCancel={() => setBaseVisible(false)}
  177 + onRefresh={onRefresh}
  178 + dealerName={dealerInfo?.dealerName}
  179 + dealerId={dealerInfo?.dealerId}
  180 + />
112 181 <EditModal visible={visible} onCancel={() => setVisible(false)} record={itemRow} onRefresh={onRefresh} />
113 182 </>
114 183 );
... ...
src/pages/Pay/MerchantList/interface.d.ts
1 1 declare namespace IMerchant {
2   - interface MerchantList {
3   - id?: number;
  2 + interface OpenMerchantData {
4 3 /** 集团id */
5 4 groupId?: number;
6 5 /** 商家id */
7 6 dealerId?: number;
8 7 /** 商家名称 */
9 8 dealerName?: string;
10   - /** 资料状态 1草稿 2开通 @link MerchantStatus */
11   - status?: number;
12   - /** 备注 */
13   - remark?: string;
14   - /** 商户简称 */
15   - mchBriefName?: string;
16   - /** 开通平台商户id */
17   - spMchId?: string;
18   - /** 开通子商户id */
19   - subMchId?: string;
  9 + /** 开通数量 */
  10 + openNum?: number;
  11 + /** 总商户数 */
  12 + total?: number;
  13 + /** 商户信息 */
  14 + itemList?: MerchantDetail[];
20 15 }
21   -
22   - interface MerchantDetail {
  16 + interface BaseMerchantData {
23 17 id?: number;
24 18 /** 集团id */
25 19 groupId?: number;
... ... @@ -31,6 +25,16 @@ declare namespace IMerchant {
31 25 subjectFid?: string;
32 26 /** 法人身份证照片(正反面) */
33 27 legalCertFids?: string;
  28 + }
  29 +
  30 + interface MerchantDetail {
  31 + id?: number;
  32 + /** 集团id */
  33 + groupId?: number;
  34 + /** 商家id */
  35 + dealerId?: number;
  36 + /** 商家名称 */
  37 + dealerName?: string;
34 38 /** 对公账户 */
35 39 publicBankNo?: string;
36 40 /** 对公账户开户行 */
... ... @@ -60,16 +64,9 @@ declare namespace IMerchant {
60 64 /** 开通子商户id */
61 65 subMchId?: string;
62 66 }
63   -
64 67 interface MerchantFromState {
65 68 /** 商家名称 */
66 69 dealerName?: string;
67   - /** 公司营业执照 */
68   - subjectFid?: FileInfo[];
69   - /** 法人身份证照片(正面) */
70   - legalCertFront?: FileInfo[];
71   - /** 法人身份证照片(反面) */
72   - legalCertBack?: FileInfo[];
73 70 /** 对公账户 */
74 71 publicBankNo?: string;
75 72 /** 对公账户开户行 */
... ...
src/pages/Pay/OpenMerchantList/api.ts
1 1 import request from '@/utils/request';
2 2 import { PAYMENT } from '@/utils/host';
3 3  
  4 +
  5 +/** 查询商家的商户列表 */
  6 +export async function queryMchListData(groupId?: number, dealerId?: number): Promise<IMerchant.MerchantDetail[]> {
  7 + const res = await request.get<IMerchant.MerchantDetail[]>(`${PAYMENT}/mch/data/item/list`, { params: { dealerId, groupId } });
  8 + return res.data || [];
  9 +}
  10 +
4 11 export interface Params {
5 12 id?: number;
6 13 open?: boolean;
... ... @@ -10,7 +17,7 @@ export interface Params {
10 17 }
11 18  
12 19 export function openMch(params: Params) {
13   - return request.post(`${PAYMENT}/mch/data/open`, params);
  20 + return request.post(`${PAYMENT}/mch/data/item/open`, params);
14 21 }
15 22  
16 23 interface MchPlfVO {
... ...
src/pages/Pay/OpenMerchantList/components/BasePreViewModal.tsx 0 → 100644
  1 +import { PlusOutlined } from '@ant-design/icons';
  2 +import type { UploadProps } from 'antd';
  3 +import { Modal, Form, Upload, Input, message, Skeleton } from 'antd';
  4 +import React, { useEffect } from 'react';
  5 +import { useRequest } from 'ahooks';
  6 +import { queryMchBaseData } from '@/pages/Pay/MerchantList/api';
  7 +
  8 +interface BaseMerchantFormData {
  9 + dealerName?: string;
  10 + /** 公司营业执照 */
  11 + subjectFid?: IMerchant.FileInfo[];
  12 + /** 法人身份证照片(正面) */
  13 + legalCertFront?: IMerchant.FileInfo[];
  14 + /** 法人身份证照片(反面) */
  15 + legalCertBack?: IMerchant.FileInfo[];
  16 +}
  17 +
  18 +interface ModalProps {
  19 + visible?: boolean;
  20 + dealerId?: number;
  21 + dealerName?: string;
  22 + onCancel?: () => void;
  23 +}
  24 +export default function EditModal({ visible, dealerId = -1, dealerName, onCancel }: ModalProps) {
  25 + const [form] = Form.useForm();
  26 +
  27 + const { run, loading } = useRequest(queryMchBaseData, {
  28 + manual: true,
  29 + onSuccess: (detail, _params) => loadFromState(detail),
  30 + onError: (e, _params) => {
  31 + message.error(e.message);
  32 + },
  33 + });
  34 +
  35 + const uploadProps: UploadProps = {
  36 + action: 'api2/file/upload',
  37 + accept: 'image/*',
  38 + maxCount: 1,
  39 + listType: 'picture-card',
  40 + beforeUpload(file) {
  41 + const isLt2M = file.size / 1024 / 1024 < 2;
  42 + if (!isLt2M) {
  43 + message.error('图片大小不能超过 2MB!');
  44 + return Upload.LIST_IGNORE;
  45 + }
  46 + return true;
  47 + },
  48 + };
  49 +
  50 + useEffect(() => {
  51 + if (visible) {
  52 + run(dealerId);
  53 + }
  54 + }, [visible]);
  55 +
  56 + function loadFromState(detail: IMerchant.BaseMerchantData) {
  57 + const commomFileProps: IMerchant.FileInfo = {
  58 + lastModified: Date.now(),
  59 + lastModifiedDate: new Date(),
  60 + name: '',
  61 + percent: 100,
  62 + status: 'done',
  63 + response: { data: '' },
  64 + uid: '-1',
  65 + thumbUrl: '',
  66 + url: '',
  67 + };
  68 + const fromState: BaseMerchantFormData = {};
  69 + fromState.dealerName = detail?.dealerName ?? dealerName;
  70 + if (detail?.subjectFid) {
  71 + fromState.subjectFid = [
  72 + {
  73 + ...commomFileProps,
  74 + response: { data: detail?.subjectFid },
  75 + uid: detail.subjectFid ?? Date.now().toString(),
  76 + thumbUrl: `api/file/show?fid=${detail?.subjectFid}`,
  77 + url: `api/file/show?fid=${detail?.subjectFid}`,
  78 + },
  79 + ];
  80 + }
  81 + const certFidArr = detail?.legalCertFids?.split(',');
  82 + if (certFidArr?.[0]) {
  83 + fromState.legalCertFront = [
  84 + {
  85 + ...commomFileProps,
  86 + response: { data: certFidArr?.[0] },
  87 + uid: certFidArr?.[0] ?? Date.now().toString(),
  88 + thumbUrl: `api/file/show?fid=${certFidArr?.[0]}`,
  89 + url: `api/file/show?fid=${certFidArr?.[0]}`,
  90 + },
  91 + ];
  92 + }
  93 + if (certFidArr?.[1]) {
  94 + fromState.legalCertBack = [
  95 + {
  96 + ...commomFileProps,
  97 + response: { data: certFidArr?.[1] },
  98 + uid: certFidArr?.[1] ?? Date.now().toString(),
  99 + thumbUrl: `api/file/show?fid=${certFidArr?.[1]}`,
  100 + url: `api/file/show?fid=${certFidArr?.[1]}`,
  101 + },
  102 + ];
  103 + }
  104 + form.setFieldsValue(fromState);
  105 + }
  106 +
  107 + const normFile = (e: any) => {
  108 + if (Array.isArray(e)) {
  109 + return e;
  110 + }
  111 + return e?.fileList;
  112 + };
  113 +
  114 + return (
  115 + <Modal title="资料维护" width="65%" open={visible} onCancel={onCancel} onOk={onCancel} afterClose={() => form.resetFields()}>
  116 + <Skeleton loading={loading} active={loading} paragraph={{ rows: 16 }}>
  117 + <Form form={form} labelWrap labelCol={{ span: 6 }} disabled={true}>
  118 + <Form.Item label="商家" name="dealerName">
  119 + <Input disabled style={{ maxWidth: 350 }} />
  120 + </Form.Item>
  121 +
  122 + <Form.Item noStyle shouldUpdate={(prevValues, curValues) => prevValues.subjectFid != curValues.subjectFid}>
  123 + {({ getFieldValue }) => {
  124 + const subjectFidLength = getFieldValue('subjectFid')?.length ?? 0;
  125 + return (
  126 + <Form.Item
  127 + label="公司营业执照"
  128 + name="subjectFid"
  129 + valuePropName="fileList"
  130 + getValueFromEvent={normFile}
  131 + rules={[{ required: true, message: '营业执照照片不能为空' }]}
  132 + extra={<p style={{ margin: 0, fontSize: 12 }}>营业执照正面清晰完整照片且不超过2M大小的JPG、BMP、PNG照片</p>}
  133 + >
  134 + <Upload {...uploadProps}>
  135 + {subjectFidLength >= 1 ? null : (
  136 + <button style={{ border: 0, background: 'none' }} type="button">
  137 + <PlusOutlined rev="" />
  138 + <div style={{ marginTop: 8 }}>上传</div>
  139 + </button>
  140 + )}
  141 + </Upload>
  142 + </Form.Item>
  143 + );
  144 + }}
  145 + </Form.Item>
  146 + <Form.Item noStyle shouldUpdate={(prevValues, curValues) => prevValues.legalCertFront != curValues.legalCertFront}>
  147 + {({ getFieldValue }) => {
  148 + const fileLength = getFieldValue('legalCertFront')?.length ?? 0;
  149 + return (
  150 + <Form.Item
  151 + label="法人身份证人像面照片"
  152 + name="legalCertFront"
  153 + valuePropName="fileList"
  154 + getValueFromEvent={normFile}
  155 + rules={[{ required: true, message: '身份证照片不能为空' }]}
  156 + extra={<p style={{ margin: 0, fontSize: 12 }}>身份证人像面清晰完整照片且不超过2M大小的JPG、BMP、PNG格式照片</p>}
  157 + >
  158 + <Upload {...uploadProps}>
  159 + {fileLength >= 1 ? null : (
  160 + <button style={{ border: 0, background: 'none' }} type="button">
  161 + <PlusOutlined rev="" />
  162 + <div style={{ marginTop: 8 }}>上传</div>
  163 + </button>
  164 + )}
  165 + </Upload>
  166 + </Form.Item>
  167 + );
  168 + }}
  169 + </Form.Item>
  170 + <Form.Item noStyle shouldUpdate={(prevValues, curValues) => prevValues.legalCertBack != curValues.legalCertBack}>
  171 + {({ getFieldValue }) => {
  172 + const legalCertBackLength = getFieldValue('legalCertBack')?.length ?? 0;
  173 + return (
  174 + <Form.Item
  175 + label="法人身份证国徽面照片"
  176 + name="legalCertBack"
  177 + valuePropName="fileList"
  178 + getValueFromEvent={normFile}
  179 + rules={[{ required: true, message: '身份证照片不能为空' }]}
  180 + extra={<p style={{ margin: 0, fontSize: 12 }}>身份证国徽面清晰完整照片且不超过2M大小的JPG、BMP、PNG格式照片</p>}
  181 + >
  182 + <Upload {...uploadProps}>
  183 + {legalCertBackLength >= 1 ? null : (
  184 + <button style={{ border: 0, background: 'none' }} type="button">
  185 + <PlusOutlined rev="" />
  186 + <div style={{ marginTop: 8 }}>上传</div>
  187 + </button>
  188 + )}
  189 + </Upload>
  190 + </Form.Item>
  191 + );
  192 + }}
  193 + </Form.Item>
  194 + </Form>
  195 + </Skeleton>
  196 + </Modal>
  197 + );
  198 +}
... ...
src/pages/Pay/OpenMerchantList/components/List.tsx
  1 +import type { TableColumnsType } from 'antd';
1 2 import { Badge, Table } from 'antd';
2 3 import type { ColumnsType } from 'antd/lib/table';
3 4 import React, { useMemo } from 'react';
4 5 import { MerchantStatus } from '@/pages/Pay/MerchantList/entity';
5 6 import PreViewModal from './PreViewModal';
  7 +import BasePreViewModal from './BasePreViewModal';
6 8 import { useSafeState } from 'ahooks';
7 9 import OpenModal from './OpenModal';
8 10  
9 11 interface Props {
10 12 loading?: boolean;
11   - dataSource?: IMerchant.MerchantList[];
  13 + dataSource?: IMerchant.OpenMerchantData[];
12 14 searchText?: string;
13 15 onRefresh?: () => void;
14 16 }
... ... @@ -16,7 +18,10 @@ interface Props {
16 18 export default function Index({ loading, dataSource = [], searchText, onRefresh }: Props) {
17 19 const [visible, setVisible] = useSafeState<boolean>();
18 20 const [visibleOpen, setVisibleOpen] = useSafeState<boolean>();
19   - const [itemRow, setItemRow] = useSafeState<IMerchant.MerchantList>();
  21 + const [itemRow, setItemRow] = useSafeState<IMerchant.OpenMerchantData>();
  22 +
  23 + const [baseVisible, setBaseVisible] = useSafeState<boolean>();
  24 + const [dealerInfo, setDealerInfo] = useSafeState<IMerchant.OpenMerchantData>();
20 25  
21 26 const list = useMemo(() => {
22 27 if (searchText) {
... ... @@ -25,54 +30,85 @@ export default function Index({ loading, dataSource = [], searchText, onRefresh
25 30 return dataSource;
26 31 }, [searchText, dataSource]);
27 32 const badgeArr: ('error' | 'default' | 'success')[] = useMemo(() => ['error', 'default', 'success'], []);
28   - //@ts-ignore
29   - const columns: ColumnsType<IMerchant.MerchantList> = useMemo(
  33 +
  34 + const columns: ColumnsType<IMerchant.OpenMerchantData> = useMemo(
30 35 () => [
31 36 {
32   - title: '编号',
  37 + title: '商家编号',
33 38 dataIndex: 'dealerId',
34 39 key: 'dealerId',
35   - width: 62,
36 40 },
37 41 {
38 42 title: '商家名称',
39 43 dataIndex: 'dealerName',
40 44 key: 'dealerName',
41   - width: '35%',
42 45 },
43 46 {
44   - title: '开通状态',
45   - dataIndex: 'status',
46   - key: 'status',
47   - filters: [
48   - {
49   - text: '草稿',
50   - value: 1,
51   - },
52   - {
53   - text: '已开通',
54   - value: 2,
55   - },
56   - ],
57   - onFilter: (value: number, record) => record.status == value,
58   - render: (text, _record) => {
59   - return text ? <Badge status={badgeArr[Number(text)]} text={MerchantStatus[text]} /> : '-';
60   - },
  47 + title: '已开通数量',
  48 + dataIndex: 'openNum',
  49 + key: 'openNum',
  50 + render: (text, _record) => text ?? '-',
  51 + },
  52 + {
  53 + title: '商户总数',
  54 + dataIndex: 'total',
  55 + key: 'total',
  56 + render: (text, _record) => text ?? '-',
  57 + },
  58 + {
  59 + title: '商家资料',
  60 + dataIndex: '',
  61 + key: 'action',
  62 + render: (_, record) => (
  63 + <>
  64 + <a
  65 + onClick={() => {
  66 + setDealerInfo(record);
  67 + setBaseVisible(true);
  68 + }}
  69 + >
  70 + 查看资料
  71 + </a>
  72 + </>
  73 + ),
61 74 },
  75 + ],
  76 + [],
  77 + );
  78 +
  79 + const expandedRowRender = (merchan: IMerchant.OpenMerchantData, _index: number, _indent: number, _expanded: boolean) => {
  80 + const c_columns: TableColumnsType<IMerchant.MerchantDetail> = [
  81 + // {
  82 + // title: '编号',
  83 + // dataIndex: 'id',
  84 + // key: 'id',
  85 + // width: 50,
  86 + // },
62 87 {
63 88 title: '平台商户号',
64 89 dataIndex: 'spMchId',
65 90 key: 'spMchId',
  91 + width: 120,
66 92 },
67 93 {
68   - title: '子商户简称',
  94 + title: '子商户号',
  95 + dataIndex: 'subMchId',
  96 + key: 'subMchId',
  97 + width: 120,
  98 + },
  99 + {
  100 + title: '商户简称',
69 101 dataIndex: 'mchBriefName',
70 102 key: 'mchBriefName',
71 103 },
72 104 {
73   - title: '子商户号',
74   - dataIndex: 'subMchId',
75   - key: 'subMchId',
  105 + title: '状态',
  106 + dataIndex: 'status',
  107 + key: 'status',
  108 + render: (text, _record) => {
  109 + return text ? <Badge status={badgeArr[Number(text)]} text={MerchantStatus[text]} /> : '-';
  110 + },
  111 + width: 120,
76 112 },
77 113 {
78 114 title: '备注',
... ... @@ -83,7 +119,7 @@ export default function Index({ loading, dataSource = [], searchText, onRefresh
83 119 title: '操作',
84 120 dataIndex: '',
85 121 key: 'action',
86   - render: (_, record) => (
  122 + render: (_text, record) => (
87 123 <>
88 124 {(record?.status == MerchantStatus['已开通'] || record?.status == MerchantStatus['草稿']) && (
89 125 <a
... ... @@ -99,7 +135,7 @@ export default function Index({ loading, dataSource = [], searchText, onRefresh
99 135 <a
100 136 style={{ color: 'rgb(45, 183, 245)', marginLeft: 15 }}
101 137 onClick={() => {
102   - setItemRow(record);
  138 + setItemRow({ ...record, dealerName: merchan?.dealerName });
103 139 setVisibleOpen(true);
104 140 }}
105 141 >
... ... @@ -109,13 +145,30 @@ export default function Index({ loading, dataSource = [], searchText, onRefresh
109 145 </>
110 146 ),
111 147 },
112   - ],
113   - [],
114   - );
  148 + ];
  149 +
  150 + return <Table rowKey="id" size="small" columns={c_columns} dataSource={merchan?.itemList || []} pagination={false} />;
  151 + };
115 152  
116 153 return (
117 154 <>
118   - <Table rowKey="dealerId" columns={columns} scroll={{ x: 'max-content' }} bordered loading={loading} dataSource={list} />
  155 + <Table
  156 + rowKey="dealerId"
  157 + expandable={{
  158 + expandedRowRender,
  159 + rowExpandable: (record) => (record?.total ?? 0) > 0,
  160 + }}
  161 + columns={columns}
  162 + bordered
  163 + loading={loading}
  164 + dataSource={list}
  165 + />
  166 + <BasePreViewModal
  167 + visible={baseVisible}
  168 + onCancel={() => setBaseVisible(false)}
  169 + dealerName={dealerInfo?.dealerName}
  170 + dealerId={dealerInfo?.dealerId}
  171 + />
119 172 <PreViewModal visible={visible} onCancel={() => setVisible(false)} record={itemRow} />
120 173 <OpenModal
121 174 visible={visibleOpen}
... ...
src/pages/Pay/OpenMerchantList/components/OpenModal.tsx
... ... @@ -6,7 +6,7 @@ import { openMch, openMchPlf } from &#39;../api&#39;;
6 6  
7 7 interface ModalProps {
8 8 visible?: boolean;
9   - record?: IMerchant.MerchantList;
  9 + record?: IMerchant.MerchantDetail;
10 10 onCancel?: () => void;
11 11 onRefresh?: () => void;
12 12 }
... ... @@ -30,8 +30,8 @@ export default function OpenModal({ visible, record, onCancel, onRefresh }: Moda
30 30 });
31 31  
32 32 useEffect(() => {
33   - if (visible && record?.dealerName) {
34   - form.setFieldsValue({ dealerName: record?.dealerName });
  33 + if (visible) {
  34 + form.setFieldsValue({ dealerName: record?.dealerName, mchBriefName: record?.mchBriefName });
35 35 }
36 36 }, [visible]);
37 37  
... ... @@ -42,7 +42,7 @@ export default function OpenModal({ visible, record, onCancel, onRefresh }: Moda
42 42 }
43 43 saveData.open = !!feildValue?.open;
44 44 saveData.remark = feildValue?.remark;
45   - saveData.spMchId = feildValue?.spMchId;
  45 + saveData.spMchId = feildValue?.spMchId?.trim?.();
46 46 saveData.subMchId = feildValue?.subMchId?.trim?.();
47 47 run(saveData);
48 48 }
... ... @@ -61,6 +61,9 @@ export default function OpenModal({ visible, record, onCancel, onRefresh }: Moda
61 61 <Form.Item label="商家" name="dealerName">
62 62 <Input disabled style={{ maxWidth: 350 }} />
63 63 </Form.Item>
  64 + <Form.Item label="商户简称" name="mchBriefName">
  65 + <Input disabled style={{ maxWidth: 350 }} />
  66 + </Form.Item>
64 67 <Form.Item name="open" label="是否开户" valuePropName="checked">
65 68 <Switch />
66 69 </Form.Item>
... ...
src/pages/Pay/OpenMerchantList/components/PreViewModal.tsx
1 1 import { PlusOutlined } from '@ant-design/icons';
2 2 import type { UploadProps } from 'antd';
3   -import { Modal, Form, Upload, Input, Checkbox, message, Skeleton } from 'antd';
  3 +import { Modal, Form, Upload, Input, Checkbox, message } from 'antd';
4 4 import React, { useEffect } from 'react';
5   -import { useRequest } from 'ahooks';
6   -import { queryMchDetail } from '@/pages/Pay/MerchantList/api';
7 5  
8 6 interface ModalProps {
9 7 visible?: boolean;
10   - record?: IMerchant.MerchantList;
  8 + record?: IMerchant.MerchantDetail;
11 9 onCancel?: () => void;
12 10 onRefresh?: () => void;
13 11 }
14 12 export default function PreViewModal({ visible, record, onCancel }: ModalProps) {
15 13 const [form] = Form.useForm();
16   - const { run, loading } = useRequest(queryMchDetail, {
17   - manual: true,
18   - onSuccess: (detail, _params) => loadFromState(detail),
19   - onError: (e, _params) => {
20   - message.error(e.message);
21   - },
22   - });
23 14  
24 15 const uploadProps: UploadProps = {
25 16 action: 'api2/file/upload',
... ... @@ -39,7 +30,7 @@ export default function PreViewModal({ visible, record, onCancel }: ModalProps)
39 30 useEffect(() => {
40 31 if (visible) {
41 32 if (record?.status && record?.dealerId) {
42   - run(record.dealerId);
  33 + loadFromState(record);
43 34 } else {
44 35 const fromState: IMerchant.MerchantFromState = {};
45 36 fromState.dealerName = record?.dealerName;
... ... @@ -63,40 +54,6 @@ export default function PreViewModal({ visible, record, onCancel }: ModalProps)
63 54 };
64 55 const fromState: IMerchant.MerchantFromState = {};
65 56 fromState.dealerName = detail?.dealerName;
66   - if (detail?.subjectFid) {
67   - fromState.subjectFid = [
68   - {
69   - ...commomFileProps,
70   - response: { data: detail?.subjectFid },
71   - uid: detail.subjectFid ?? Date.now().toString(),
72   - thumbUrl: `api/file/show?fid=${detail?.subjectFid}`,
73   - url: `api/file/show?fid=${detail?.subjectFid}`,
74   - },
75   - ];
76   - }
77   - const certFidArr = detail?.legalCertFids?.split(',');
78   - if (certFidArr?.[0]) {
79   - fromState.legalCertFront = [
80   - {
81   - ...commomFileProps,
82   - response: { data: certFidArr?.[0] },
83   - uid: certFidArr?.[0] ?? Date.now().toString(),
84   - thumbUrl: `api/file/show?fid=${certFidArr?.[0]}`,
85   - url: `api/file/show?fid=${certFidArr?.[0]}`,
86   - },
87   - ];
88   - }
89   - if (certFidArr?.[1]) {
90   - fromState.legalCertBack = [
91   - {
92   - ...commomFileProps,
93   - response: { data: certFidArr?.[1] },
94   - uid: certFidArr?.[1] ?? Date.now().toString(),
95   - thumbUrl: `api/file/show?fid=${certFidArr?.[1]}`,
96   - url: `api/file/show?fid=${certFidArr?.[1]}`,
97   - },
98   - ];
99   - }
100 57 fromState.publicBankNo = detail?.publicBankNo;
101 58 fromState.publicBankName = detail?.publicBankName;
102 59 fromState.publicBankOrg = detail?.publicBankOrg;
... ... @@ -147,82 +104,7 @@ export default function PreViewModal({ visible, record, onCancel }: ModalProps)
147 104  
148 105 return (
149 106 <Modal title={'查看'} width="65%" open={visible} onCancel={onCancel} onOk={form.submit} afterClose={() => form.resetFields()}>
150   - <Skeleton loading={loading} active={loading} paragraph={{ rows: 16 }}>
151 107 <Form form={form} onFinish={() => onCancel?.()} labelWrap labelCol={{ span: 6 }} disabled>
152   - <Form.Item label="商家" name="dealerName">
153   - <Input disabled style={{ maxWidth: 350 }} />
154   - </Form.Item>
155   -
156   - <Form.Item noStyle shouldUpdate={(prevValues, curValues) => prevValues.subjectFid != curValues.subjectFid}>
157   - {({ getFieldValue }) => {
158   - const subjectFidLength = getFieldValue('subjectFid')?.length ?? 0;
159   - return (
160   - <Form.Item
161   - label="公司营业执照"
162   - name="subjectFid"
163   - valuePropName="fileList"
164   - getValueFromEvent={normFile}
165   - rules={[{ required: true, message: '营业执照照片不能为空' }]}
166   - >
167   - <Upload {...uploadProps}>
168   - {subjectFidLength >= 1 ? null : (
169   - <button style={{ border: 0, background: 'none' }} type="button">
170   - <PlusOutlined rev="" />
171   - <div style={{ marginTop: 8 }}>上传</div>
172   - </button>
173   - )}
174   - </Upload>
175   - </Form.Item>
176   - );
177   - }}
178   - </Form.Item>
179   - <Form.Item noStyle shouldUpdate={(prevValues, curValues) => prevValues.legalCertFront != curValues.legalCertFront}>
180   - {({ getFieldValue }) => {
181   - const fileLength = getFieldValue('legalCertFront')?.length ?? 0;
182   - return (
183   - <Form.Item
184   - label="法人身份证人像面照片"
185   - name="legalCertFront"
186   - valuePropName="fileList"
187   - getValueFromEvent={normFile}
188   - rules={[{ required: true, message: '身份证照片不能为空' }]}
189   - >
190   - <Upload {...uploadProps}>
191   - {fileLength >= 1 ? null : (
192   - <button style={{ border: 0, background: 'none' }} type="button">
193   - <PlusOutlined rev="" />
194   - <div style={{ marginTop: 8 }}>上传</div>
195   - </button>
196   - )}
197   - </Upload>
198   - </Form.Item>
199   - );
200   - }}
201   - </Form.Item>
202   - <Form.Item noStyle shouldUpdate={(prevValues, curValues) => prevValues.legalCertBack != curValues.legalCertBack}>
203   - {({ getFieldValue }) => {
204   - const legalCertBackLength = getFieldValue('legalCertBack')?.length ?? 0;
205   - return (
206   - <Form.Item
207   - label="法人身份证国徽面照片"
208   - name="legalCertBack"
209   - valuePropName="fileList"
210   - getValueFromEvent={normFile}
211   - rules={[{ required: true, message: '身份证照片不能为空' }]}
212   - >
213   - <Upload {...uploadProps}>
214   - {legalCertBackLength >= 1 ? null : (
215   - <button style={{ border: 0, background: 'none' }} type="button">
216   - <PlusOutlined rev="" />
217   - <div style={{ marginTop: 8 }}>上传</div>
218   - </button>
219   - )}
220   - </Upload>
221   - </Form.Item>
222   - );
223   - }}
224   - </Form.Item>
225   -
226 108 <Form.Item label="对公账户" name="publicBankNo" rules={[{ required: true, message: '对公账户不能为空' }]}>
227 109 <Input placeholder="请输入" type="number" style={{ maxWidth: 350 }} />
228 110 </Form.Item>
... ... @@ -335,7 +217,6 @@ export default function PreViewModal({ visible, record, onCancel }: ModalProps)
335 217 <Input placeholder="请输入" type="email" maxLength={128} style={{ maxWidth: 350 }} />
336 218 </Form.Item>
337 219 </Form>
338   - </Skeleton>
339 220 </Modal>
340 221 );
341 222 }
... ...