Commit 2144d84dda897f640f653bf91def4304e75ae2f5

Authored by boyang
1 parent 2159dc50

feat: 客户列表与跟进记录

.umirc.ts
@@ -124,10 +124,21 @@ export default defineConfig({ @@ -124,10 +124,21 @@ export default defineConfig({
124 }, 124 },
125 { 125 {
126 name: '客户管理', 126 name: '客户管理',
127 - path: '/client',  
128 - component: './Client', 127 + path: '/Client',
129 icon: 'BookOutlined', 128 icon: 'BookOutlined',
130 access: 'canReadAdminAndSales', 129 access: 'canReadAdminAndSales',
  130 + routes: [
  131 + {
  132 + name: '客户列表',
  133 + path: 'clint',
  134 + component: './Client/Client',
  135 + },
  136 + {
  137 + name: '跟进记录',
  138 + path: 'FollowRecord',
  139 + component: './Client/FollowRecord',
  140 + },
  141 + ],
131 }, 142 },
132 { 143 {
133 name: '打印', 144 name: '打印',
src/pages/Client/Components/ClientDrawer.tsx renamed to src/pages/Client/Client/Components/ClientDrawer.tsx
@@ -6,8 +6,11 @@ import { @@ -6,8 +6,11 @@ import {
6 postDistrictSelectByNameAndLevel, 6 postDistrictSelectByNameAndLevel,
7 postDistrictSelOrderProvince, 7 postDistrictSelOrderProvince,
8 postServiceConstClientLevels, 8 postServiceConstClientLevels,
  9 + postServiceConstClientRequirements,
  10 + postServiceConstClientSource,
9 postServiceConstTradeStatus, 11 postServiceConstTradeStatus,
10 } from '@/services'; 12 } from '@/services';
  13 +
11 import { enumToSelect } from '@/utils'; 14 import { enumToSelect } from '@/utils';
12 import { 15 import {
13 DrawerForm, 16 DrawerForm,
@@ -42,7 +45,7 @@ export default ({ optType, record, onFinish }) => { @@ -42,7 +45,7 @@ export default ({ optType, record, onFinish }) => {
42 }, 45 },
43 edit: { 46 edit: {
44 text: '编辑', 47 text: '编辑',
45 - button: <a type="primary">编辑</a>, 48 + button: <a type="link">编辑</a>,
46 readonly: false, 49 readonly: false,
47 onFinish: async (values) => { 50 onFinish: async (values) => {
48 const res = await postAdminClientModifyClientInfo({ 51 const res = await postAdminClientModifyClientInfo({
@@ -58,7 +61,7 @@ export default ({ optType, record, onFinish }) =&gt; { @@ -58,7 +61,7 @@ export default ({ optType, record, onFinish }) =&gt; {
58 }, 61 },
59 detail: { 62 detail: {
60 text: '详情', 63 text: '详情',
61 - button: <a type="primary">详情</a>, 64 + // button: <a type="primary">详情</a>,
62 readonly: true, 65 readonly: true,
63 }, 66 },
64 }; 67 };
@@ -98,7 +101,7 @@ export default ({ optType, record, onFinish }) =&gt; { @@ -98,7 +101,7 @@ export default ({ optType, record, onFinish }) =&gt; {
98 }, 101 },
99 ]} 102 ]}
100 /> 103 />
101 - <ProFormText 104 + {/* <ProFormText
102 name="companyName" 105 name="companyName"
103 label="单位名称" 106 label="单位名称"
104 placeholder="请输入单位名称" 107 placeholder="请输入单位名称"
@@ -108,7 +111,7 @@ export default ({ optType, record, onFinish }) =&gt; { @@ -108,7 +111,7 @@ export default ({ optType, record, onFinish }) =&gt; {
108 message: '请输入单位名称', 111 message: '请输入单位名称',
109 }, 112 },
110 ]} 113 ]}
111 - /> 114 + /> */}
112 <div 115 <div
113 style={{ 116 style={{
114 display: 'flex', 117 display: 'flex',
@@ -260,7 +263,7 @@ export default ({ optType, record, onFinish }) =&gt; { @@ -260,7 +263,7 @@ export default ({ optType, record, onFinish }) =&gt; {
260 /> 263 />
261 </div> 264 </div>
262 <ProFormText 265 <ProFormText
263 - name="companyAddress" 266 + name="detailAddress"
264 label="详细地址" 267 label="详细地址"
265 placeholder="请输入单位地址" 268 placeholder="请输入单位地址"
266 rules={[ 269 rules={[
@@ -271,6 +274,17 @@ export default ({ optType, record, onFinish }) =&gt; { @@ -271,6 +274,17 @@ export default ({ optType, record, onFinish }) =&gt; {
271 ]} 274 ]}
272 /> 275 />
273 <ProFormText 276 <ProFormText
  277 + name="contacts"
  278 + label="联系人"
  279 + placeholder="请输入联系人"
  280 + rules={[
  281 + {
  282 + required: true,
  283 + message: '请输入联系人',
  284 + },
  285 + ]}
  286 + />
  287 + <ProFormText
274 name="phoneNumber" 288 name="phoneNumber"
275 label="联系电话" 289 label="联系电话"
276 placeholder="请输入联系电话" 290 placeholder="请输入联系电话"
@@ -281,12 +295,22 @@ export default ({ optType, record, onFinish }) =&gt; { @@ -281,12 +295,22 @@ export default ({ optType, record, onFinish }) =&gt; {
281 }, 295 },
282 ]} 296 ]}
283 /> 297 />
284 - <ProFormText 298 + <ProFormSelect
285 name="source" 299 name="source"
286 label="客户来源" 300 label="客户来源"
287 - placeholder="请输入客户来源" 301 + placeholder="请选择客户来源"
  302 + request={async () => {
  303 + const res = await postServiceConstClientSource();
  304 + return enumToSelect(res.data);
  305 + }}
  306 + rules={[
  307 + {
  308 + required: true,
  309 + message: '请选择客户来源',
  310 + },
  311 + ]}
288 /> 312 />
289 - <ProFormText 313 + <ProFormSelect
290 name="requirements" 314 name="requirements"
291 label="客户需求" 315 label="客户需求"
292 placeholder="请输入客户需求" 316 placeholder="请输入客户需求"
@@ -296,6 +320,10 @@ export default ({ optType, record, onFinish }) =&gt; { @@ -296,6 +320,10 @@ export default ({ optType, record, onFinish }) =&gt; {
296 message: '请输入客户需求', 320 message: '请输入客户需求',
297 }, 321 },
298 ]} 322 ]}
  323 + request={async () => {
  324 + const res = await postServiceConstClientRequirements();
  325 + return enumToSelect(res.data);
  326 + }}
299 /> 327 />
300 <ProFormText name="referrers" label="推荐人" placeholder="请输入推荐人" /> 328 <ProFormText name="referrers" label="推荐人" placeholder="请输入推荐人" />
301 <ProFormSelect 329 <ProFormSelect
src/pages/Client/Components/ClientImportModal.tsx renamed to src/pages/Client/Client/Components/ClientImportModal.tsx
src/pages/Client/Client/Components/ClientModal.tsx 0 → 100644
  1 +import { RESPONSE_CODE } from '@/constants/enum';
  2 +import {
  3 + postAdminClientAddOrModifyClientComunicationInfo,
  4 + postAdminClientQueryClientPage,
  5 + postOrderErpOrderStagesUpload,
  6 + postServiceConstClientWay,
  7 +} from '@/services';
  8 +import { enumToSelect } from '@/utils';
  9 +import {
  10 + ModalForm,
  11 + ProFormDateTimePicker,
  12 + ProFormSelect,
  13 + ProFormText,
  14 + ProFormTextArea,
  15 + ProFormUploadDragger,
  16 +} from '@ant-design/pro-components';
  17 +import { Button, Form, message } from 'antd';
  18 +import { RcFile } from 'antd/es/upload';
  19 +export default ({ data, type, reloadTable }) => {
  20 + const [form] = Form.useForm();
  21 + const onfinish = async (values) => {
  22 + console.log(data, '5656datatest');
  23 + console.log(values, '5656...values');
  24 + const res = await postAdminClientAddOrModifyClientComunicationInfo({
  25 + data: {
  26 + ...values,
  27 + clientId: data.id,
  28 + },
  29 + });
  30 + if (res.result === RESPONSE_CODE.SUCCESS) {
  31 + message.success('新增成功');
  32 + reloadTable();
  33 + return true;
  34 + }
  35 + // 不返回不会关闭弹框
  36 + };
  37 + const optType = {
  38 + add: {
  39 + readOnly: false,
  40 + title: '新增跟进记录',
  41 + button: (
  42 + <Button size={'small'} type="link">
  43 + 新增跟进
  44 + </Button>
  45 + ),
  46 + onFinish: onfinish,
  47 + },
  48 + modify: {
  49 + readOnly: false,
  50 + title: '修改跟进记录',
  51 + button: (
  52 + <Button size={'small'} type="link">
  53 + 编辑
  54 + </Button>
  55 + ),
  56 + onFinish: onfinish,
  57 + },
  58 + detail: {
  59 + readOnly: true,
  60 + title: '查看跟进记录',
  61 + button: (
  62 + <Button size={'small'} type="link">
  63 + 查看
  64 + </Button>
  65 + ),
  66 + onFinish: () => {},
  67 + },
  68 + };
  69 + return (
  70 + <ModalForm
  71 + title={optType[type].title}
  72 + resize={{
  73 + onResize() {
  74 + console.log('resize!');
  75 + },
  76 + maxWidth: window.innerWidth * 0.8,
  77 + minWidth: 400,
  78 + }}
  79 + form={form}
  80 + trigger={optType[type].button}
  81 + autoFocusFirstInput
  82 + drawerProps={{
  83 + destroyOnClose: true,
  84 + }}
  85 + submitTimeout={2000}
  86 + onFinish={optType[type].onFinish}
  87 + >
  88 + <ProFormSelect
  89 + name="sendStoreCode"
  90 + readonly={optType[type].readOnly}
  91 + fieldProps={{
  92 + labelInValue: false,
  93 + }}
  94 + initialValue={data ? data?.name + '' : null}
  95 + label="客户"
  96 + width="sm"
  97 + request={async () => {
  98 + const res = await postAdminClientQueryClientPage({
  99 + data: {
  100 + groupFilter: 'all',
  101 + },
  102 + });
  103 + console.log(data?.datetime, '5656data?.datetime');
  104 + const namesArray = res.data.data.map((item) => item.name);
  105 + return enumToSelect(namesArray);
  106 + }}
  107 + rules={[
  108 + {
  109 + required: true,
  110 + message: '请选择客户',
  111 + },
  112 + ]}
  113 + disabled
  114 + ></ProFormSelect>
  115 + <ProFormDateTimePicker
  116 + name="datetime"
  117 + label="日期"
  118 + initialValue={data.datetime ? data?.datetime + '' : null}
  119 + placeholder="请选择跟进时间"
  120 + width="sm"
  121 + rules={[
  122 + {
  123 + required: true,
  124 + message: '请选择日期',
  125 + },
  126 + ]}
  127 + />
  128 + <ProFormSelect
  129 + name="way"
  130 + width="sm"
  131 + readonly={optType[type].readOnly}
  132 + fieldProps={{
  133 + labelInValue: false,
  134 + }}
  135 + initialValue={data?.way ? data?.way + '' : null}
  136 + label="类型"
  137 + request={async () => {
  138 + const res = await postServiceConstClientWay();
  139 + return enumToSelect(res.data);
  140 + }}
  141 + rules={[
  142 + {
  143 + required: true,
  144 + message: '请选择跟进类型',
  145 + },
  146 + ]}
  147 + ></ProFormSelect>
  148 + <ProFormTextArea
  149 + name="content"
  150 + label="详情"
  151 + placeholder="请输入详情"
  152 + initialValue={data?.content}
  153 + readonly={optType[type].readOnly}
  154 + rules={[
  155 + {
  156 + required: true,
  157 + message: '请输入详情',
  158 + },
  159 + ]}
  160 + ></ProFormTextArea>
  161 + <ProFormUploadDragger
  162 + label="附件"
  163 + name="attachment"
  164 + action="upload.do"
  165 + hidden={optType[type].readOnly}
  166 + onChange={(info) => {
  167 + const uploadFile = async ({ fileList: newFileList }) => {
  168 + if (newFileList.length > 0) {
  169 + const formData = new FormData();
  170 + formData.append('file', newFileList[0].originFileObj as RcFile);
  171 + const res = await postOrderErpOrderStagesUpload({
  172 + data: formData,
  173 + headers: {
  174 + 'Content-Type':
  175 + 'multipart/form-data; boundary=----WebKitFormBoundarynl6gT1BKdPWIejNq',
  176 + },
  177 + });
  178 + const url = res.data;
  179 + console.log('attachments' + JSON.stringify(url));
  180 + form.setFieldValue('attachments', url);
  181 + } else {
  182 + form.setFieldValue('attachments', null);
  183 + }
  184 + };
  185 + uploadFile(info);
  186 + }}
  187 + max={1}
  188 + />
  189 + <a hidden={!optType[type].readOnly} href={data?.attachments} download>
  190 + 下载附件
  191 + </a>
  192 + <ProFormText
  193 + initialValue={data?.attachments}
  194 + name="attachments"
  195 + hidden
  196 + ></ProFormText>
  197 + <ProFormText initialValue={data?.id} name="id" hidden></ProFormText>
  198 + </ModalForm>
  199 + );
  200 +};
src/pages/Client/Client/Components/ClientModalBak.tsx 0 → 100644
  1 +import { RESPONSE_CODE } from '@/constants/enum';
  2 +import {
  3 + postOrderErpOrderStagesUpload,
  4 + postProcureReturnBillAddOrModify,
  5 + postServiceConstStores,
  6 +} from '@/services';
  7 +import { enumToSelect } from '@/utils';
  8 +import {
  9 + ModalForm,
  10 + ProFormSelect,
  11 + ProFormText,
  12 + ProFormTextArea,
  13 + ProFormUploadDragger,
  14 +} from '@ant-design/pro-components';
  15 +import { Button, Form, message } from 'antd';
  16 +import { RcFile } from 'antd/es/upload';
  17 +export default ({ data, type, reloadTable }) => {
  18 + const [form] = Form.useForm();
  19 + const onfinish = async (values) => {
  20 + const res = await postProcureReturnBillAddOrModify({
  21 + data: values,
  22 + });
  23 + if (res.result === RESPONSE_CODE.SUCCESS) {
  24 + message.success('新增成功');
  25 + reloadTable();
  26 + return true;
  27 + }
  28 + // 不返回不会关闭弹框
  29 + };
  30 + const optType = {
  31 + add: {
  32 + readOnly: false,
  33 + title: '新增采购退货单',
  34 + button: (
  35 + <Button size={'small'} type="link">
  36 + 新增记录
  37 + </Button>
  38 + ),
  39 + onFinish: onfinish,
  40 + },
  41 + modify: {
  42 + readOnly: false,
  43 + title: '修改采购退货单',
  44 + button: (
  45 + <Button size={'small'} type="link">
  46 + 编辑
  47 + </Button>
  48 + ),
  49 + onFinish: onfinish,
  50 + },
  51 + detail: {
  52 + readOnly: true,
  53 + title: '查看采购退货单',
  54 + button: (
  55 + <Button size={'small'} type="link">
  56 + 查看
  57 + </Button>
  58 + ),
  59 + onFinish: () => {},
  60 + },
  61 + };
  62 + return (
  63 + <ModalForm
  64 + title={optType[type].title}
  65 + resize={{
  66 + onResize() {
  67 + console.log('resize!');
  68 + },
  69 + maxWidth: window.innerWidth * 0.8,
  70 + minWidth: 400,
  71 + }}
  72 + form={form}
  73 + trigger={optType[type].button}
  74 + autoFocusFirstInput
  75 + drawerProps={{
  76 + destroyOnClose: true,
  77 + }}
  78 + submitTimeout={2000}
  79 + onFinish={optType[type].onFinish}
  80 + >
  81 + <ProFormText
  82 + name="consignee"
  83 + label="收货人"
  84 + placeholder="请输入收货人"
  85 + readonly={optType[type].readOnly}
  86 + initialValue={data?.consignee}
  87 + rules={[
  88 + {
  89 + required: true,
  90 + message: '请输入收货人',
  91 + },
  92 + ]}
  93 + />
  94 + <ProFormText
  95 + name="phoneNumber"
  96 + label="联系电话"
  97 + placeholder="请输入联系电话"
  98 + initialValue={data?.phoneNumber}
  99 + readonly={optType[type].readOnly}
  100 + rules={[
  101 + {
  102 + required: true,
  103 + message: '请输入联系电话',
  104 + },
  105 + ]}
  106 + />
  107 + <ProFormText
  108 + name="address"
  109 + label="收货地址"
  110 + placeholder="请输入收货地址"
  111 + initialValue={data?.address}
  112 + readonly={optType[type].readOnly}
  113 + rules={[
  114 + {
  115 + required: true,
  116 + message: '请输入联系电话',
  117 + },
  118 + ]}
  119 + />
  120 + <ProFormTextArea
  121 + name="productDetail"
  122 + label="产品明细"
  123 + placeholder="请输入产品明细"
  124 + initialValue={data?.productDetail}
  125 + readonly={optType[type].readOnly}
  126 + rules={[
  127 + {
  128 + required: true,
  129 + message: '请输入联系电话',
  130 + },
  131 + ]}
  132 + ></ProFormTextArea>
  133 + <ProFormSelect
  134 + name="sendStoreCode"
  135 + readonly={optType[type].readOnly}
  136 + fieldProps={{
  137 + labelInValue: false,
  138 + }}
  139 + initialValue={data ? data?.sendStoreCode + '' : null}
  140 + label="发货仓库"
  141 + request={async () => {
  142 + const res = await postServiceConstStores();
  143 + return enumToSelect(res.data);
  144 + }}
  145 + rules={[
  146 + {
  147 + required: true,
  148 + message: '请输入联系电话',
  149 + },
  150 + ]}
  151 + ></ProFormSelect>
  152 + <ProFormTextArea
  153 + name="notes"
  154 + label="备注"
  155 + initialValue={data?.notes}
  156 + readonly={optType[type].readOnly}
  157 + placeholder="请输入备注"
  158 + ></ProFormTextArea>
  159 + <ProFormUploadDragger
  160 + label="附件"
  161 + name="attachmentsFile"
  162 + action="upload.do"
  163 + hidden={optType[type].readOnly}
  164 + onChange={(info) => {
  165 + const uploadFile = async ({ fileList: newFileList }) => {
  166 + if (newFileList.length > 0) {
  167 + const formData = new FormData();
  168 + formData.append('file', newFileList[0].originFileObj as RcFile);
  169 + const res = await postOrderErpOrderStagesUpload({
  170 + data: formData,
  171 + headers: {
  172 + 'Content-Type':
  173 + 'multipart/form-data; boundary=----WebKitFormBoundarynl6gT1BKdPWIejNq',
  174 + },
  175 + });
  176 + const url = res.data;
  177 + console.log('attachments' + JSON.stringify(url));
  178 + form.setFieldValue('attachments', url);
  179 + } else {
  180 + form.setFieldValue('attachments', null);
  181 + }
  182 + };
  183 + uploadFile(info);
  184 + }}
  185 + max={1}
  186 + />
  187 + <a hidden={!optType[type].readOnly} href={data?.attachments} download>
  188 + 下载附件
  189 + </a>
  190 + <ProFormText
  191 + initialValue={data?.attachments}
  192 + name="attachments"
  193 + hidden
  194 + ></ProFormText>
  195 + <ProFormText initialValue={data?.id} name="id" hidden></ProFormText>
  196 + </ModalForm>
  197 + );
  198 +};
src/pages/Client/Client/Components/CommunicationHistoryModal.tsx 0 → 100644
  1 +import { RESPONSE_CODE } from '@/constants/enum';
  2 +import {
  3 + postAdminClientAddOrModifyClientComunicationInfo,
  4 + postAdminClientQueryClientComunicationInfo,
  5 + postAdminClientRemoveClientComunicationInfo,
  6 + postServiceConstClientWay,
  7 +} from '@/services';
  8 +import { enumToSelect } from '@/utils';
  9 +import {
  10 + ActionType,
  11 + EditableProTable,
  12 + ModalForm,
  13 + ProFormInstance,
  14 +} from '@ant-design/pro-components';
  15 +import { Descriptions, Popconfirm, message } from 'antd';
  16 +import { useEffect, useRef, useState } from 'react';
  17 +export default ({ record }) => {
  18 + const [editableKeys, setEditableRowKeys] = useState<React.Key[]>([]);
  19 + const [dataSource, setDataSource] = useState();
  20 + const ref = useRef<ProFormInstance>();
  21 + const actionRef = useRef<ActionType>();
  22 + const columns = [
  23 + {
  24 + title: '跟进时间',
  25 + dataIndex: 'datetime',
  26 + width: 50,
  27 + valueType: 'dateTime',
  28 + rules: [{ required: true, message: '请选择时间' }],
  29 + },
  30 + {
  31 + title: '跟进人',
  32 + width: 50,
  33 + rules: [{ required: true, message: '请输入跟进人' }],
  34 + dataIndex: 'createByName',
  35 + },
  36 + // {
  37 + // title: '跟进类型',
  38 + // width: 150,
  39 + // ellipsis: true,
  40 + // dataIndex: 'wayText',
  41 + // hideInSearch: true,
  42 + // },
  43 + {
  44 + title: '跟进类型',
  45 + width: 50,
  46 + dataIndex: 'way',
  47 + rules: [{ required: true, message: '请选择方式' }],
  48 + request: async () => {
  49 + const res = await postServiceConstClientWay();
  50 + return enumToSelect(res.data);
  51 + },
  52 + },
  53 + {
  54 + title: '内容',
  55 + width: 100,
  56 + valueType: 'textarea',
  57 + rules: [{ required: true, message: '请输入内容' }],
  58 + dataIndex: 'content',
  59 + },
  60 + {
  61 + title: '操作',
  62 + valueType: 'option',
  63 + width: 50,
  64 + render: (text, record, _, action) => [
  65 + <a
  66 + key="editable"
  67 + onClick={() => {
  68 + action?.startEditable?.(record.tid);
  69 + }}
  70 + >
  71 + 编辑
  72 + </a>,
  73 + <Popconfirm
  74 + key={'delete'}
  75 + title="删除记录"
  76 + description="确认删除记录?"
  77 + onConfirm={async () => {
  78 + setDataSource(dataSource.filter((item) => item.tid !== record.tid));
  79 + const res = await postAdminClientRemoveClientComunicationInfo({
  80 + query: {
  81 + id: record.id,
  82 + },
  83 + });
  84 + if (res.result === RESPONSE_CODE.SUCCESS) {
  85 + message.success(res.message);
  86 + action?.reload();
  87 + } else {
  88 + message.error('删除失败');
  89 + }
  90 + }}
  91 + okText="是"
  92 + cancelText="否"
  93 + >
  94 + <a type={'danger'}>删除</a>
  95 + </Popconfirm>,
  96 + ],
  97 + },
  98 + ];
  99 + const [name, setName] = useState(''); // 客户名称
  100 + const [contacts, setContacts] = useState(''); // 联系人
  101 + const [sourceText, setSourceText] = useState(''); // 来源文本
  102 + const [phoneNumber, setPhoneNumber] = useState(''); // 联系电话
  103 + const [hasSchemeText, setHasSchemeText] = useState(''); // 报方案状态文本
  104 + const [quoteDatetime, setQuoteDatetime] = useState(null); // 报价时间
  105 + const [referrers, setReferrers] = useState(''); // 推荐人
  106 + const [requirementsText, setRequirementsText] = useState(''); // 需求文本
  107 + const [tradeStatusText, setTradeStatusText] = useState(''); // 跟进状态文本
  108 + const [levelText, setLevelText] = useState(''); // 客户等级文本
  109 + const [createTime, setCreateTime] = useState(null); // 最新沟通时间
  110 + const [address, setAddress] = useState(''); // 客户地址
  111 + const [notes, setNotes] = useState(''); // 备注信息
  112 + const [createByName, setCreateByName] = useState(''); // 创建人
  113 + const [latestObject, setLatestObject] = useState(); // 最新跟进时间
  114 + useEffect(() => {
  115 + const request = async () => {
  116 + const resShow = await postAdminClientQueryClientComunicationInfo({
  117 + data: {
  118 + clientId: record.id,
  119 + },
  120 + });
  121 + if (resShow?.data?.data !== null) {
  122 + const data = resShow?.data?.data;
  123 + // const latest = data.reduce((latest: any, current: any) => {
  124 + // return new Date(current.updateTime) > new Date(latest.updateTime) ? current : latest;
  125 + // });
  126 + // const data = [];
  127 + const latestObject2 =
  128 + data?.length > 0
  129 + ? data.reduce((latest, current) =>
  130 + new Date(current.datetime) > new Date(latest.datetime)
  131 + ? current
  132 + : latest,
  133 + )
  134 + : null; // 或返回其他默认值
  135 + setLatestObject(latestObject2?.datetime);
  136 + }
  137 +
  138 + setName(record.name);
  139 + setContacts(record.contacts);
  140 + setSourceText(record.sourceText);
  141 + setPhoneNumber(record.phoneNumber);
  142 + setHasSchemeText(record.hasSchemeText);
  143 + setQuoteDatetime(record.quoteDatetime);
  144 + setReferrers(record.referrers);
  145 + setRequirementsText(record.requirementsText);
  146 + setTradeStatusText(record.tradeStatusText);
  147 + setLevelText(record.levelText);
  148 + setCreateTime(record.createTime);
  149 + setAddress(record.address);
  150 + setNotes(record.notes);
  151 + setCreateByName(record.createByName);
  152 + };
  153 + request();
  154 + }, []);
  155 + const items = [
  156 + {
  157 + key: '1',
  158 + label: '客户名称',
  159 + children: name, // 客户名称
  160 + },
  161 + {
  162 + key: '2',
  163 + label: '联系人',
  164 + children: contacts, // 联系人
  165 + },
  166 + {
  167 + key: '3',
  168 + label: '来源',
  169 + children: sourceText, // 来源文本
  170 + },
  171 + {
  172 + key: '4',
  173 + label: '联系电话',
  174 + children: phoneNumber, // 联系电话
  175 + },
  176 + {
  177 + key: '5',
  178 + label: '是否已报方案',
  179 + children: hasSchemeText, // 报方案状态文本
  180 + },
  181 + {
  182 + key: '6',
  183 + label: '报价时间',
  184 + children: quoteDatetime, // 报价时间
  185 + },
  186 + {
  187 + key: '7',
  188 + label: '推荐人',
  189 + children: referrers, // 推荐人
  190 + },
  191 + {
  192 + key: '8',
  193 + label: '需求',
  194 + children: requirementsText, // 需求文本
  195 + },
  196 + {
  197 + key: '9',
  198 + label: '跟进状态',
  199 + children: tradeStatusText, // 跟进状态文本
  200 + },
  201 + {
  202 + key: '10',
  203 + label: '客户等级',
  204 + children: levelText, // 客户等级文本
  205 + },
  206 + {
  207 + key: '11',
  208 + label: '创建时间',
  209 + children: createTime, // 最新沟通时间
  210 + },
  211 + {
  212 + key: '12',
  213 + label: '客户地址',
  214 + children: address, // 客户地址
  215 + },
  216 + {
  217 + key: '13',
  218 + label: '备注',
  219 + children: notes, // 备注信息
  220 + },
  221 + {
  222 + key: '14',
  223 + label: '创建人',
  224 + children: createByName, // 创建人
  225 + },
  226 + {
  227 + key: '15',
  228 + label: '最新跟进时间',
  229 + children: latestObject, // 最新跟进时间
  230 + },
  231 + ];
  232 + return (
  233 + <ModalForm
  234 + title="客户详情"
  235 + trigger={<a type="primary">查看</a>}
  236 + modalProps={{
  237 + destroyOnClose: true,
  238 + }}
  239 + onFinish={async () => {
  240 + return true;
  241 + }}
  242 + >
  243 + <Descriptions items={items} column={2} />
  244 + <EditableProTable
  245 + rowKey="tid"
  246 + formRef={ref}
  247 + actionRef={actionRef}
  248 + recordCreatorProps={{
  249 + record: () => ({ tid: (Math.random() * 1000000).toFixed(0) }),
  250 + }}
  251 + loading={false}
  252 + columns={columns}
  253 + request={async () => {
  254 + const res = await postAdminClientQueryClientComunicationInfo({
  255 + data: {
  256 + clientId: record.id,
  257 + },
  258 + });
  259 + if (res.result === RESPONSE_CODE.SUCCESS) {
  260 + console.log(JSON.stringify(res.data));
  261 + return {
  262 + ...res.data,
  263 + data: res.data.data.map((item) => {
  264 + return {
  265 + ...item,
  266 + tid: (Math.random() * 1000000).toFixed(0),
  267 + };
  268 + }),
  269 + };
  270 + } else {
  271 + message.error('获取失败');
  272 + }
  273 + }}
  274 + value={dataSource}
  275 + onChange={setDataSource}
  276 + editable={{
  277 + type: 'multiple',
  278 + editableKeys,
  279 + onSave: async (rowKey, data, row) => {
  280 + console.log(rowKey, data, row);
  281 + if (data?.way === '拜访') {
  282 + data.way = 'VISIT';
  283 + } else if (data?.way === '电话') {
  284 + data.way = 'PHONE';
  285 + } else if (data?.way === '微信') {
  286 + data.way = 'WECHAT';
  287 + } else if (data?.way === '邮件') {
  288 + data.way = 'EMAIL';
  289 + } else if (data?.way === '其他') {
  290 + data.way = 'OTHER';
  291 + }
  292 + const res = await postAdminClientAddOrModifyClientComunicationInfo({
  293 + data: {
  294 + ...data,
  295 + clientId: record.id,
  296 + },
  297 + });
  298 + if (res.result === RESPONSE_CODE.SUCCESS) {
  299 + message.success(res.message);
  300 + } else {
  301 + message.error('修改失败');
  302 + }
  303 + actionRef.current?.reload();
  304 + },
  305 + onChange: setEditableRowKeys,
  306 + }}
  307 + />
  308 + {/* <Descriptions title="User Info" items={items} column={2} /> */}
  309 + {/*<ProCard title="表格数据" headerBordered collapsible defaultCollapsed>
  310 + <ProFormField
  311 + ignoreFormItem
  312 + fieldProps={{
  313 + style: {
  314 + width: '100%',
  315 + },
  316 + }}
  317 + mode="read"
  318 + valueType="jsonCode"
  319 + text={JSON.stringify(dataSource)}
  320 + />
  321 + </ProCard>*/}
  322 + </ModalForm>
  323 + );
  324 +};
src/pages/Client/index.tsx renamed to src/pages/Client/Client/index.tsx
1 -import ClientDrawer from '@/pages/Client/Components/ClientDrawer';  
2 -import ClientImportModal from '@/pages/Client/Components/ClientImportModal';  
3 -import ClientStatistic from '@/pages/Client/Components/ClientStatistic';  
4 -import CommunicationHistoryModal from '@/pages/Client/Components/CommunicationHistoryModal'; 1 +import ClientDrawer from '@/pages/Client/Client/Components/ClientDrawer';
  2 +import ClientImportModal from '@/pages/Client/Client/Components/ClientImportModal';
  3 +import ClientModal from '@/pages/Client/Client/Components/ClientModal';
  4 +import CommunicationHistoryModal from '@/pages/Client/Client/Components/CommunicationHistoryModal';
5 import { 5 import {
  6 + postAdminClientDeleteAdminClient,
  7 + postAdminClientGetStatisticalData,
6 postAdminClientQueryClientPage, 8 postAdminClientQueryClientPage,
7 - postServiceConstClientGroupFilters,  
8 postServiceConstClientLevels, 9 postServiceConstClientLevels,
  10 + postServiceConstClientRequirements,
  11 + postServiceConstClientSource,
9 postServiceConstTradeStatus, 12 postServiceConstTradeStatus,
10 } from '@/services'; 13 } from '@/services';
11 import { orderExport } from '@/services/order'; 14 import { orderExport } from '@/services/order';
12 import { enumToSelect } from '@/utils'; 15 import { enumToSelect } from '@/utils';
13 import type { ActionType } from '@ant-design/pro-components'; 16 import type { ActionType } from '@ant-design/pro-components';
14 import { ProTable } from '@ant-design/pro-components'; 17 import { ProTable } from '@ant-design/pro-components';
15 -import { Button, Radio, Space, message } from 'antd'; 18 +import { Badge, Button, Radio, Space, message } from 'antd';
16 import { useEffect, useRef, useState } from 'react'; 19 import { useEffect, useRef, useState } from 'react';
17 20
18 const columns = [ 21 const columns = [
@@ -25,23 +28,30 @@ const columns = [ @@ -25,23 +28,30 @@ const columns = [
25 { 28 {
26 title: '客户名称', 29 title: '客户名称',
27 dataIndex: 'name', 30 dataIndex: 'name',
28 - width: 100, 31 + width: 150,
29 ellipsis: true, 32 ellipsis: true,
30 hideInSearch: true, 33 hideInSearch: true,
31 }, 34 },
  35 + // {
  36 + // title: '单位名称',
  37 + // width: 150,
  38 + // ellipsis: true,
  39 + // dataIndex: 'companyName',
  40 + // hideInSearch: true,
  41 + // },
32 { 42 {
33 - title: '单位名称',  
34 - width: 150, 43 + title: '客户地址',
  44 + width: 250,
35 ellipsis: true, 45 ellipsis: true,
36 - dataIndex: 'companyName',  
37 - hideInSearch: true, 46 + dataIndex: 'address',
  47 + hideInSearch: false,
38 }, 48 },
39 { 49 {
40 - title: '单位地址',  
41 - width: 250, 50 + title: '联系人',
  51 + width: 150,
42 ellipsis: true, 52 ellipsis: true,
43 - dataIndex: 'companyAddressText',  
44 - hideInSearch: true, 53 + dataIndex: 'contacts',
  54 + hideInSearch: false,
45 }, 55 },
46 { 56 {
47 title: '联系电话', 57 title: '联系电话',
@@ -54,10 +64,20 @@ const columns = [ @@ -54,10 +64,20 @@ const columns = [
54 title: '客户来源', 64 title: '客户来源',
55 width: 150, 65 width: 150,
56 ellipsis: true, 66 ellipsis: true,
57 - dataIndex: 'source', 67 + dataIndex: 'sourceText',
58 hideInSearch: true, 68 hideInSearch: true,
59 }, 69 },
60 { 70 {
  71 + title: '客户来源',
  72 + valueType: 'select',
  73 + hideInTable: true,
  74 + dataIndex: 'source',
  75 + request: async () => {
  76 + const res = await postServiceConstClientSource();
  77 + return enumToSelect(res.data);
  78 + },
  79 + },
  80 + {
61 title: '推荐人', 81 title: '推荐人',
62 dataIndex: 'referrers', 82 dataIndex: 'referrers',
63 width: 150, 83 width: 150,
@@ -66,12 +86,22 @@ const columns = [ @@ -66,12 +86,22 @@ const columns = [
66 }, 86 },
67 { 87 {
68 title: '客户需求', 88 title: '客户需求',
69 - dataIndex: 'requirements',  
70 width: 150, 89 width: 150,
71 ellipsis: true, 90 ellipsis: true,
  91 + dataIndex: 'requirementsText',
72 hideInSearch: true, 92 hideInSearch: true,
73 }, 93 },
74 { 94 {
  95 + title: '客户需求',
  96 + dataIndex: 'requirements',
  97 + valueType: 'select',
  98 + hideInTable: true,
  99 + request: async () => {
  100 + const res = await postServiceConstClientRequirements();
  101 + return enumToSelect(res.data);
  102 + },
  103 + },
  104 + {
75 title: '是否已报方案', 105 title: '是否已报方案',
76 width: 150, 106 width: 150,
77 ellipsis: true, 107 ellipsis: true,
@@ -111,26 +141,35 @@ const columns = [ @@ -111,26 +141,35 @@ const columns = [
111 hideInSearch: true, 141 hideInSearch: true,
112 }, 142 },
113 { 143 {
114 - title: '最新进时间', 144 + title: '最新进时间',
115 key: 'since', 145 key: 'since',
116 width: 150, 146 width: 150,
117 ellipsis: true, 147 ellipsis: true,
118 - dataIndex: 'latestCommunicationTime', 148 + dataIndex: 'updateTime',
119 valueType: 'dateTime', 149 valueType: 'dateTime',
120 hideInSearch: true, 150 hideInSearch: true,
121 }, 151 },
  152 + // {
  153 + // title: '最新跟进时间',
  154 + // key: 'since',
  155 + // width: 150,
  156 + // ellipsis: true,
  157 + // dataIndex: 'latestCommunicationTime',
  158 + // valueType: 'dateTime',
  159 + // hideInSearch: true,
  160 + // },
122 { 161 {
123 title: '客户名称', 162 title: '客户名称',
124 dataIndex: 'nameLike', 163 dataIndex: 'nameLike',
125 valueType: 'Text', 164 valueType: 'Text',
126 hideInTable: true, 165 hideInTable: true,
127 }, 166 },
128 - {  
129 - title: '单位名称',  
130 - dataIndex: 'companyNameLike',  
131 - valueType: 'Text',  
132 - hideInTable: true,  
133 - }, 167 + // {
  168 + // title: '单位名称',
  169 + // dataIndex: 'companyNameLike',
  170 + // valueType: 'Text',
  171 + // hideInTable: true,
  172 + // },
134 { 173 {
135 title: '联系电话', 174 title: '联系电话',
136 dataIndex: 'phoneNumberLike', 175 dataIndex: 'phoneNumberLike',
@@ -194,11 +233,23 @@ const columns = [ @@ -194,11 +233,23 @@ const columns = [
194 key: 'option', 233 key: 'option',
195 width: 150, 234 width: 150,
196 render: (text, record, index, action) => { 235 render: (text, record, index, action) => {
  236 + const handleDelete = async () => {
  237 + console.log(JSON.stringify(record), '5656record');
  238 + // 调用删除接口
  239 + const success = await postAdminClientDeleteAdminClient({
  240 + query: {
  241 + id: record.id,
  242 + },
  243 + });
  244 + if (success) {
  245 + action.reload(); // 刷新表格
  246 + }
  247 + };
197 console.log(JSON.stringify(record)); 248 console.log(JSON.stringify(record));
198 return [ 249 return [
199 <CommunicationHistoryModal 250 <CommunicationHistoryModal
200 key={'communicationHistory'} 251 key={'communicationHistory'}
201 - clientId={record.id} 252 + record={record}
202 />, 253 />,
203 <ClientDrawer 254 <ClientDrawer
204 key={'detail'} 255 key={'detail'}
@@ -208,6 +259,14 @@ const columns = [ @@ -208,6 +259,14 @@ const columns = [
208 action.reload(); 259 action.reload();
209 }} 260 }}
210 ></ClientDrawer>, 261 ></ClientDrawer>,
  262 + <ClientModal
  263 + key={'add'}
  264 + data={record}
  265 + reloadTable={() => {
  266 + action?.reload();
  267 + }}
  268 + type={'add'}
  269 + />,
211 <ClientDrawer 270 <ClientDrawer
212 key={'edit'} 271 key={'edit'}
213 record={record} 272 record={record}
@@ -216,6 +275,9 @@ const columns = [ @@ -216,6 +275,9 @@ const columns = [
216 action.reload(); 275 action.reload();
217 }} 276 }}
218 ></ClientDrawer>, 277 ></ClientDrawer>,
  278 + <a key={'delete'} onClick={handleDelete} style={{ color: 'red' }}>
  279 + 删除
  280 + </a>,
219 ]; 281 ];
220 }, 282 },
221 }, 283 },
@@ -224,23 +286,79 @@ const columns = [ @@ -224,23 +286,79 @@ const columns = [
224 export default () => { 286 export default () => {
225 const [messageApi, contextHolder] = message.useMessage(); 287 const [messageApi, contextHolder] = message.useMessage();
226 const [groupFilter, setGroupFilter] = useState('All'); 288 const [groupFilter, setGroupFilter] = useState('All');
227 - const [groupFilterOptions, setGroupFilterDataOptions] = useState([]); 289 + // const [groupFilterOptions, setGroupFilterDataOptions] = useState([]);
228 const actionRef = useRef<ActionType>(); 290 const actionRef = useRef<ActionType>();
  291 + //获得预警/全部数量
  292 + const [clientStatistic, setClientStatistic] = useState([]);
  293 + const [allClientStatistic, setAllClientStatistic] = useState([]);
  294 + const [warningClientStatistic, setWarningClientStatistic] = useState([]);
  295 + // const reloadTable = () => {
  296 + // actionRef.current?.reload();
  297 + // };
229 useEffect(() => { 298 useEffect(() => {
230 - const pullGroupFilterDataOptions = async () => {  
231 - const res = await postServiceConstClientGroupFilters();  
232 - console.log('setGroupFilterDataOptions' + JSON.stringify(res.data));  
233 - setGroupFilterDataOptions(enumToSelect(res.data)); 299 + const pullStatistic = async () => {
  300 + let statisticalData = await postAdminClientGetStatisticalData();
  301 + console.log('stati' + JSON.stringify(statisticalData.data));
  302 + setClientStatistic(statisticalData.data);
  303 + setWarningClientStatistic(statisticalData.data[1].value);
  304 + setAllClientStatistic(statisticalData.data[0].value);
  305 + setTimeout(() => {
  306 + console.log(clientStatistic, '5656groupFilterOptions1');
  307 + // groupFilterOptions[0].label = groupFilterOptions[0].label + '(' + clientStatistic + ')';
  308 + }, 100);
  309 + actionRef.current?.reload(); // 可能需要在这里刷新
234 }; 310 };
235 - pullGroupFilterDataOptions(); 311 + pullStatistic();
236 }, []); 312 }, []);
  313 + const groupFilterOptions = [
  314 + {
  315 + value: 'All',
  316 + label: (
  317 + <span>
  318 + 全部
  319 + <Badge count={allClientStatistic} style={{ marginLeft: 8 }} />
  320 + </span>
  321 + ),
  322 + },
  323 + {
  324 + value: 'WARNING_CLIENT',
  325 + label: (
  326 + <span>
  327 + 预警客户
  328 + <Badge count={warningClientStatistic} style={{ marginLeft: 8 }} />
  329 + </span>
  330 + ),
  331 + },
  332 + ];
  333 + // useEffect(() => {
  334 + // const pullGroupFilterDataOptions = async () => {
  335 + // const res = await postServiceConstClientGroupFilters();
  336 + // // console.log('setGroupFilterDataOptions' + JSON.stringify(res.data));
  337 + // const select = enumToSelect(res.data);
  338 + // console.log(select, '5656selet');
  339 + // setGroupFilterDataOptions(select);
  340 + // };
  341 + // pullGroupFilterDataOptions();
  342 + // }, []);
237 useEffect(() => { 343 useEffect(() => {
  344 + // console.log(groupFilterOptions, '5656groupFilterOptions2');
  345 + console.log(clientStatistic, '5656clientStatistic');
  346 + console.log(warningClientStatistic, '5656warningClientStatistic');
238 actionRef.current?.reload(); 347 actionRef.current?.reload();
239 }, [groupFilter]); 348 }, [groupFilter]);
240 return ( 349 return (
241 <> 350 <>
242 <Space direction="vertical" size="middle" style={{ display: 'flex' }}> 351 <Space direction="vertical" size="middle" style={{ display: 'flex' }}>
243 - <ClientStatistic></ClientStatistic> 352 + <div key={'groupFilter'}>
  353 + <Radio.Group
  354 + options={groupFilterOptions}
  355 + onChange={(e) => {
  356 + setGroupFilter(e.target.value);
  357 + }}
  358 + value={groupFilter}
  359 + optionType="button"
  360 + />
  361 + </div>
244 <ProTable 362 <ProTable
245 columns={columns} 363 columns={columns}
246 actionRef={actionRef} 364 actionRef={actionRef}
@@ -273,6 +391,7 @@ export default () =&gt; { @@ -273,6 +391,7 @@ export default () =&gt; {
273 'POST', 391 'POST',
274 values, 392 values,
275 () => { 393 () => {
  394 + console.log(searchConfig, '5656searchConfig');
276 messageApi.destroy(); 395 messageApi.destroy();
277 }, 396 },
278 ); 397 );
@@ -323,16 +442,6 @@ export default () =&gt; { @@ -323,16 +442,6 @@ export default () =&gt; {
323 dateFormatter="string" 442 dateFormatter="string"
324 headerTitle="高级表格" 443 headerTitle="高级表格"
325 toolBarRender={() => [ 444 toolBarRender={() => [
326 - <div key={'groupFilter'}>  
327 - <Radio.Group  
328 - options={groupFilterOptions}  
329 - onChange={(e) => {  
330 - setGroupFilter(e.target.value);  
331 - }}  
332 - value={groupFilter}  
333 - optionType="button"  
334 - />  
335 - </div>,  
336 <ClientDrawer 445 <ClientDrawer
337 optType={'add'} 446 optType={'add'}
338 key="button" 447 key="button"
src/pages/Client/Components/ClientStatistic.tsx deleted 100644 → 0
1 -import { postAdminClientGetStatisticalData } from '@/services';  
2 -import { StatisticCard } from '@ant-design/pro-components';  
3 -import { useEffect, useState } from 'react';  
4 -  
5 -export default () => {  
6 - const [clientStatistic, setClientStatistic] = useState([]);  
7 - useEffect(() => {  
8 - const pullStatistic = async () => {  
9 - let statisticalData = await postAdminClientGetStatisticalData();  
10 - console.log('stati' + JSON.stringify(statisticalData.data));  
11 - setClientStatistic(statisticalData.data);  
12 - };  
13 - pullStatistic();  
14 - }, []);  
15 - return (  
16 - <StatisticCard.Group>  
17 - {clientStatistic.map((stat, index) => (  
18 - <StatisticCard  
19 - key={index}  
20 - statistic={{  
21 - title: stat.title,  
22 - tip: stat.tip || '', // 如果tip不存在,则使用空字符串  
23 - value: stat.value,  
24 - status: stat.status || 'default', // 如果status不存在,则使用'default'  
25 - }}  
26 - />  
27 - ))}  
28 - </StatisticCard.Group>  
29 - );  
30 -};  
src/pages/Client/Components/CommunicationHistoryModal.tsx deleted 100644 → 0
1 -import { RESPONSE_CODE } from '@/constants/enum';  
2 -import {  
3 - postAdminClientAddOrModifyClientComunicationInfo,  
4 - postAdminClientQueryClientComunicationInfo,  
5 - postAdminClientRemoveClientComunicationInfo,  
6 -} from '@/services';  
7 -import {  
8 - ActionType,  
9 - EditableProTable,  
10 - ModalForm,  
11 - ProFormInstance,  
12 -} from '@ant-design/pro-components';  
13 -import { Popconfirm, message } from 'antd';  
14 -import { useEffect, useRef, useState } from 'react';  
15 -export default ({ clientId }) => {  
16 - const [editableKeys, setEditableRowKeys] = useState<React.Key[]>([]);  
17 - const [dataSource, setDataSource] = useState();  
18 - const ref = useRef<ProFormInstance>();  
19 - const actionRef = useRef<ActionType>();  
20 - const columns = [  
21 - {  
22 - title: '跟进时间',  
23 - dataIndex: 'datetime',  
24 - width: 50,  
25 - valueType: 'dateTime',  
26 - rules: [{ required: true, message: '请选择时间' }],  
27 - },  
28 - {  
29 - title: '方式',  
30 - width: 50,  
31 - dataIndex: 'way',  
32 - rules: [{ required: true, message: '请选择方式' }],  
33 - },  
34 - {  
35 - title: '内容',  
36 - width: 100,  
37 - valueType: 'textarea',  
38 - rules: [{ required: true, message: '请输入内容' }],  
39 - dataIndex: 'content',  
40 - },  
41 - {  
42 - title: '操作',  
43 - valueType: 'option',  
44 - width: 50,  
45 - render: (text, record, _, action) => [  
46 - <a  
47 - key="editable"  
48 - onClick={() => {  
49 - action?.startEditable?.(record.tid);  
50 - }}  
51 - >  
52 - 编辑  
53 - </a>,  
54 - <Popconfirm  
55 - key={'delete'}  
56 - title="删除记录"  
57 - description="确认删除记录?"  
58 - onConfirm={async () => {  
59 - setDataSource(dataSource.filter((item) => item.tid !== record.tid));  
60 - const res = await postAdminClientRemoveClientComunicationInfo({  
61 - query: {  
62 - id: record.id,  
63 - },  
64 - });  
65 - if (res.result === RESPONSE_CODE.SUCCESS) {  
66 - message.success(res.message);  
67 - action?.reload();  
68 - } else {  
69 - message.error('删除失败');  
70 - }  
71 - }}  
72 - okText="是"  
73 - cancelText="否"  
74 - >  
75 - <a type={'danger'}>删除</a>  
76 - </Popconfirm>,  
77 - ],  
78 - },  
79 - ];  
80 -  
81 - useEffect(() => {  
82 - console.log('clientId', clientId);  
83 - }, []);  
84 - return (  
85 - <ModalForm  
86 - title="跟进记录"  
87 - trigger={<a type="primary">跟进记录</a>}  
88 - modalProps={{  
89 - destroyOnClose: true,  
90 - }}  
91 - >  
92 - <EditableProTable  
93 - rowKey="tid"  
94 - formRef={ref}  
95 - actionRef={actionRef}  
96 - recordCreatorProps={{  
97 - record: () => ({ tid: (Math.random() * 1000000).toFixed(0) }),  
98 - }}  
99 - loading={false}  
100 - columns={columns}  
101 - request={async () => {  
102 - const res = await postAdminClientQueryClientComunicationInfo({  
103 - data: {  
104 - clientId: clientId,  
105 - },  
106 - });  
107 - if (res.result === RESPONSE_CODE.SUCCESS) {  
108 - console.log(JSON.stringify(res.data));  
109 - return {  
110 - ...res.data,  
111 - data: res.data.data.map((item) => {  
112 - return {  
113 - ...item,  
114 - tid: (Math.random() * 1000000).toFixed(0),  
115 - };  
116 - }),  
117 - };  
118 - } else {  
119 - message.error('获取失败');  
120 - }  
121 - }}  
122 - value={dataSource}  
123 - onChange={setDataSource}  
124 - editable={{  
125 - type: 'multiple',  
126 - editableKeys,  
127 - onSave: async (rowKey, data, row) => {  
128 - console.log(rowKey, data, row);  
129 - const res = await postAdminClientAddOrModifyClientComunicationInfo({  
130 - data: {  
131 - ...data,  
132 - clientId: clientId,  
133 - },  
134 - });  
135 - if (res.result === RESPONSE_CODE.SUCCESS) {  
136 - message.success(res.message);  
137 - } else {  
138 - message.error('修改失败');  
139 - }  
140 - actionRef.current?.reload();  
141 - },  
142 - onChange: setEditableRowKeys,  
143 - }}  
144 - />  
145 - {/*<ProCard title="表格数据" headerBordered collapsible defaultCollapsed>  
146 - <ProFormField  
147 - ignoreFormItem  
148 - fieldProps={{  
149 - style: {  
150 - width: '100%',  
151 - },  
152 - }}  
153 - mode="read"  
154 - valueType="jsonCode"  
155 - text={JSON.stringify(dataSource)}  
156 - />  
157 - </ProCard>*/}  
158 - </ModalForm>  
159 - );  
160 -};  
src/pages/Client/FollowRecord/Components/ClientImportModal.tsx 0 → 100644
  1 +import { RESPONSE_CODE } from '@/constants/enum';
  2 +import { orderExport } from '@/services/order';
  3 +import { blobToJson } from '@/utils';
  4 +import { ModalForm, ProFormUploadDragger } from '@ant-design/pro-components';
  5 +import { Button, Form, message } from 'antd';
  6 +import { RcFile } from 'antd/es/upload';
  7 +import axios from 'axios';
  8 +
  9 +export default () => {
  10 + const [form] = Form.useForm();
  11 + const [messageApi, contextHolder] = message.useMessage();
  12 + const downloadImportTemplate = () => {
  13 + messageApi.open({
  14 + type: 'loading',
  15 + content: '正在导入...',
  16 + duration: 0,
  17 + });
  18 + orderExport(
  19 + '/api/admin/client/downloadImportTemplate',
  20 + '客户导入模板.xlsx',
  21 + 'post',
  22 + {},
  23 + () => {
  24 + messageApi.destroy();
  25 + },
  26 + );
  27 + };
  28 +
  29 + return (
  30 + <ModalForm
  31 + title="导入客户信息"
  32 + trigger={<Button type="primary">导入客户信息</Button>}
  33 + form={form}
  34 + autoFocusFirstInput
  35 + modalProps={{
  36 + destroyOnClose: true,
  37 + onCancel: () => console.log('run'),
  38 + }}
  39 + submitTimeout={2000}
  40 + onFinish={async (values) => {
  41 + const formData = new FormData();
  42 + values.file.forEach((file) => {
  43 + formData.append('file', file.originFileObj as RcFile);
  44 + });
  45 + axios({
  46 + url: '/api/admin/client/importClient',
  47 + method: 'post',
  48 + responseType: 'blob',
  49 + headers: {
  50 + Authorization: localStorage.getItem('token'),
  51 + 'Content-Type':
  52 + 'multipart/form-data; boundary=----WebKitFormBoundarynl6gT1BKdPWIejNq',
  53 + },
  54 + data: formData,
  55 + })
  56 + .then((response) => {
  57 + let data = response.data;
  58 + if (data.type === 'application/json') {
  59 + blobToJson(data).then((dataJson) => {
  60 + if (dataJson?.result === RESPONSE_CODE.SUCCESS) {
  61 + message.success(dataJson?.message);
  62 + } else {
  63 + message.error(dataJson?.message);
  64 + }
  65 + });
  66 + } else {
  67 + message.error('上传失败,已下载错误信息表格');
  68 + // 创建一个新的 Blob 对象,它包含了服务器响应的数据(即你的 Excel 文件)
  69 + const blob = new Blob([response.data]); // Excel 的 MIME 类型
  70 + const downloadUrl = window.URL.createObjectURL(blob);
  71 + const a = document.createElement('a');
  72 + a.href = downloadUrl;
  73 + a.download = '银行流水导入模板.xlsx'; // 你可以为文件命名
  74 + document.body.appendChild(a);
  75 + a.click(); // 模拟点击操作来下载文件
  76 + URL.revokeObjectURL(downloadUrl); // 释放掉 blob 对象所占用的内存
  77 + document.body.removeChild(a);
  78 + }
  79 + })
  80 + .catch((error) => {
  81 + // 处理错误
  82 + message.error('系统出现异常了,请联系管理员', error);
  83 + })
  84 + .finally(() => {});
  85 + return true;
  86 + }}
  87 + >
  88 + <ProFormUploadDragger max={1} label="上传" name="file" />
  89 + <Button type="link" onClick={downloadImportTemplate}>
  90 + 下载导入模板
  91 + </Button>
  92 + {contextHolder}
  93 + </ModalForm>
  94 + );
  95 +};
src/pages/Client/FollowRecord/Components/ClientModal.tsx 0 → 100644
  1 +import { RESPONSE_CODE } from '@/constants/enum';
  2 +import {
  3 + postAdminClientAddOrModifyClientComunicationInfo,
  4 + postAdminClientQueryClientPage,
  5 + postOrderErpOrderStagesUpload,
  6 + postServiceConstClientWay,
  7 +} from '@/services';
  8 +import { enumToSelect } from '@/utils';
  9 +import {
  10 + ModalForm,
  11 + ProFormDateTimePicker,
  12 + ProFormSelect,
  13 + ProFormText,
  14 + ProFormTextArea,
  15 + ProFormUploadDragger,
  16 +} from '@ant-design/pro-components';
  17 +import { Button, Form, message } from 'antd';
  18 +import { RcFile } from 'antd/es/upload';
  19 +export default ({ data, type, reloadTable }) => {
  20 + const [form] = Form.useForm();
  21 + const onfinish = async (values) => {
  22 + const resSearchId = await postAdminClientQueryClientPage({
  23 + data: {
  24 + groupFilter: 'all',
  25 + },
  26 + });
  27 + const matchingItem = resSearchId.data.data.find(
  28 + (item) => item.name === values.name,
  29 + );
  30 + let matchedId;
  31 + if (matchingItem) {
  32 + matchedId = matchingItem.id; // 匹配成功,取出 id
  33 + } else {
  34 + matchedId = null; // 如果没有匹配项,可以设置为 null 或其他值
  35 + }
  36 + const res = await postAdminClientAddOrModifyClientComunicationInfo({
  37 + data: {
  38 + ...values,
  39 + clientId: matchedId,
  40 + },
  41 + });
  42 + if (res.result === RESPONSE_CODE.SUCCESS) {
  43 + message.success('新增成功');
  44 + reloadTable();
  45 + return true;
  46 + }
  47 + // 不返回不会关闭弹框
  48 + };
  49 + const optType = {
  50 + add: {
  51 + readOnly: false,
  52 + title: '新增跟进记录',
  53 + button: (
  54 + <Button size={'middle'} type="primary">
  55 + 新增
  56 + </Button>
  57 + ),
  58 + onFinish: onfinish,
  59 + },
  60 + modify: {
  61 + readOnly: false,
  62 + title: '修改跟进记录',
  63 + button: (
  64 + <Button size={'middle'} type="primary">
  65 + 编辑
  66 + </Button>
  67 + ),
  68 + onFinish: onfinish,
  69 + },
  70 + detail: {
  71 + readOnly: true,
  72 + title: '查看跟进记录',
  73 + button: (
  74 + <Button size={'middle'} type="primary" color="red">
  75 + 查看
  76 + </Button>
  77 + ),
  78 + onFinish: () => {},
  79 + },
  80 + };
  81 + return (
  82 + <ModalForm
  83 + title={optType[type].title}
  84 + resize={{
  85 + onResize() {
  86 + console.log('resize!');
  87 + },
  88 + maxWidth: window.innerWidth * 0.8,
  89 + minWidth: 400,
  90 + }}
  91 + form={form}
  92 + trigger={optType[type].button}
  93 + autoFocusFirstInput
  94 + drawerProps={{
  95 + destroyOnClose: true,
  96 + }}
  97 + submitTimeout={2000}
  98 + onFinish={optType[type].onFinish}
  99 + >
  100 + <ProFormSelect
  101 + name="name"
  102 + readonly={optType[type].readOnly}
  103 + fieldProps={{
  104 + labelInValue: false,
  105 + disabled: type === 'modify',
  106 + }}
  107 + initialValue={data ? data?.clientName + '' : null}
  108 + label="客户"
  109 + width="sm"
  110 + request={async () => {
  111 + const res = await postAdminClientQueryClientPage({
  112 + data: {
  113 + groupFilter: 'all',
  114 + },
  115 + });
  116 + console.log(data, '5656data?.nameedit');
  117 + const namesArray = res.data.data.map((item) => item.name);
  118 + const formattedObject = namesArray.reduce((acc, name) => {
  119 + acc[name] = name; // 将名称作为键和值
  120 + return acc;
  121 + }, {});
  122 + console.log(namesArray, '5656namesArray');
  123 + return enumToSelect(formattedObject);
  124 + }}
  125 + rules={[
  126 + {
  127 + required: true,
  128 + message: '请选择客户',
  129 + },
  130 + ]}
  131 + ></ProFormSelect>
  132 + <ProFormDateTimePicker
  133 + name="datetime"
  134 + label="日期"
  135 + initialValue={data ? data?.datetime + '' : null}
  136 + placeholder="请选择跟进时间"
  137 + width="sm"
  138 + rules={[
  139 + {
  140 + required: true,
  141 + message: '请选择日期',
  142 + },
  143 + ]}
  144 + />
  145 + <ProFormSelect
  146 + name="way"
  147 + width="sm"
  148 + readonly={optType[type].readOnly}
  149 + fieldProps={{
  150 + labelInValue: false,
  151 + }}
  152 + initialValue={data?.way ? data?.way + '' : null}
  153 + label="类型"
  154 + request={async () => {
  155 + const res = await postServiceConstClientWay();
  156 + return enumToSelect(res.data);
  157 + }}
  158 + rules={[
  159 + {
  160 + required: true,
  161 + message: '请选择跟进类型',
  162 + },
  163 + ]}
  164 + ></ProFormSelect>
  165 + <ProFormTextArea
  166 + name="content"
  167 + label="详情"
  168 + placeholder="请输入详情"
  169 + initialValue={data?.content}
  170 + readonly={optType[type].readOnly}
  171 + rules={[
  172 + {
  173 + required: true,
  174 + message: '请输入详情',
  175 + },
  176 + ]}
  177 + ></ProFormTextArea>
  178 + <ProFormUploadDragger
  179 + label="附件"
  180 + name="attachment"
  181 + action="upload.do"
  182 + hidden={optType[type].readOnly}
  183 + onChange={(info) => {
  184 + const uploadFile = async ({ fileList: newFileList }) => {
  185 + if (newFileList.length > 0) {
  186 + const formData = new FormData();
  187 + formData.append('file', newFileList[0].originFileObj as RcFile);
  188 + const res = await postOrderErpOrderStagesUpload({
  189 + data: formData,
  190 + headers: {
  191 + 'Content-Type':
  192 + 'multipart/form-data; boundary=----WebKitFormBoundarynl6gT1BKdPWIejNq',
  193 + },
  194 + });
  195 + const url = res.data;
  196 + console.log('attachments' + JSON.stringify(url));
  197 + form.setFieldValue('attachments', url);
  198 + } else {
  199 + form.setFieldValue('attachments', null);
  200 + }
  201 + };
  202 + uploadFile(info);
  203 + }}
  204 + max={1}
  205 + />
  206 + <a hidden={!optType[type].readOnly} href={data?.attachments} download>
  207 + 下载附件
  208 + </a>
  209 + <ProFormText
  210 + initialValue={data?.attachments}
  211 + name="attachments"
  212 + hidden
  213 + ></ProFormText>
  214 + <ProFormText initialValue={data?.id} name="id" hidden></ProFormText>
  215 + </ModalForm>
  216 + );
  217 +};
src/pages/Client/FollowRecord/Components/CommunicationHistoryModal.tsx 0 → 100644
  1 +import ClientModal from '@/pages/Client/FollowRecord/Components/ClientModal';
  2 +import {
  3 + postAdminClientQueryClientComunicationInfo,
  4 + postAdminClientRemoveClientComunicationInfo,
  5 +} from '@/services/request';
  6 +import { ModalForm } from '@ant-design/pro-components';
  7 +import { Button, Descriptions, Space } from 'antd';
  8 +import { useEffect, useRef, useState } from 'react';
  9 +
  10 +export default ({ data, reloadTable }) => {
  11 + // const [isModalVisible, setIsModalVisible] = useState(false); // 控制 ClientModal 的显示
  12 + const actionRef = useRef(); // 引用 actionRef,方便调用 reload 方法
  13 +
  14 + const [datetime, setDatetime] = useState(); // 跟进日期
  15 + const [createByName, setCreateByName] = useState(''); // 跟进人员
  16 + const [clientName, setClientName] = useState(''); // 客户名称
  17 + // const [clientAddress, setClientAddress] = useState(''); // 客户地址
  18 + const [way, setWay] = useState(); // 类型
  19 + // const [clientNameLike, setClientNameLike] = useState(''); // 客户名称模糊查询
  20 + // const [clientAddressLike, setClientAddressLike] = useState(''); // 客户地址模糊查询
  21 + // const [tradeStatus, setTradeStatus] = useState(''); // 客户状态
  22 + // const [tradeStatusLike, setTradeStatusLike] = useState(''); // 客户状态模糊查询
  23 + const [content, setContent] = useState(''); // 跟进详情
  24 + const [createTime, setCreateTime] = useState(null); // 创建时间
  25 + const [attachments, setAttachments] = useState(); // 创建时间
  26 +
  27 + useEffect(() => {
  28 + const request = async () => {
  29 + console.log(data, '5656datasearch');
  30 + const res = await postAdminClientQueryClientComunicationInfo({
  31 + data: {
  32 + id: data.id,
  33 + },
  34 + });
  35 + console.log(res, '5656res');
  36 + const dataSearch = res.data.data[0];
  37 + if (dataSearch) {
  38 + setDatetime(dataSearch.datetime); // 设置跟进日期
  39 + // setDateRange(data.dateRange || []); // 设置跟进时间范围
  40 + setCreateByName(dataSearch.createByName); // 设置跟进人员
  41 + setClientName(dataSearch.clientName); // 设置客户名称
  42 + // setClientAddress(data.clientAddress || ''); // 设置客户地址
  43 + // setClientNameLike(data.clientNameLike || ''); // 设置客户名称模糊查询
  44 + // setClientAddressLike(data.clientAddressLike || ''); // 设置客户地址模糊查询
  45 + // setTradeStatus(data.tradeStatus || ''); // 设置客户状态
  46 + // setTradeStatusLike(data.tradeStatusLike || ''); // 设置客户状态模糊查询
  47 + setContent(dataSearch.content); // 设置跟进详情
  48 + setCreateTime(dataSearch.createTime); // 设置创建时间
  49 + setWay(dataSearch.wayText);
  50 + setAttachments(dataSearch.attachments);
  51 + }
  52 + console.log(attachments, '5656attachments');
  53 + };
  54 + request();
  55 + }, []);
  56 + const items = [
  57 + {
  58 + key: '1',
  59 + label: '跟进日期',
  60 + children: datetime, // 跟进日期
  61 + },
  62 + {
  63 + key: '2',
  64 + label: '跟进人员',
  65 + children: createByName, // 跟进人员
  66 + },
  67 + {
  68 + key: '3',
  69 + label: '客户名称',
  70 + children: clientName, // 客户名称
  71 + },
  72 + {
  73 + key: '4',
  74 + label: '跟进详情',
  75 + children: content, // 跟进详情
  76 + },
  77 + {
  78 + key: '5',
  79 + label: '创建时间',
  80 + children: createTime, // 创建时间
  81 + },
  82 + {
  83 + key: '6',
  84 + label: '跟进类型',
  85 + children: way, // 跟进类型
  86 + },
  87 + ];
  88 + const handleDelete = async () => {
  89 + console.log(JSON.stringify(data), '5656record');
  90 + // 调用删除接口
  91 + const success = await postAdminClientRemoveClientComunicationInfo({
  92 + query: {
  93 + id: data.id,
  94 + },
  95 + });
  96 + // setIsModalVisible(false);
  97 + if (success) {
  98 + actionRef?.current?.reload(); // 重新加载表格数据
  99 + }
  100 + };
  101 + return (
  102 + <Space>
  103 + <ModalForm
  104 + title="跟进记录"
  105 + trigger={<Button type="link">查看</Button>}
  106 + submitter={{
  107 + resetButtonProps: {
  108 + style: {
  109 + display: 'none',
  110 + },
  111 + },
  112 + submitButtonProps: {
  113 + style: {
  114 + display: 'none',
  115 + },
  116 + },
  117 + render: (props, defaultDoms) => {
  118 + return [
  119 + ...defaultDoms,
  120 + <>
  121 + <ClientModal
  122 + key={'modify'}
  123 + data={data} // 将表单数据传递给 ClientModal
  124 + reloadTable={() => {
  125 + actionRef?.current?.reload(); // 重新加载表格数据
  126 + props.submit();
  127 + reloadTable();
  128 + console.log('5656close');
  129 + }}
  130 + type={'modify'}
  131 + onFinish={() => {
  132 + // setIsModalVisible(false);
  133 + }} // 关闭 Modal
  134 + style={{ marginRight: '10px' }}
  135 + />
  136 + <Button
  137 + key={'delete'}
  138 + onClick={() => {
  139 + handleDelete();
  140 + props.submit();
  141 + reloadTable();
  142 + }}
  143 + type="primary"
  144 + size="middle"
  145 + danger // 使用 danger 属性来将按钮颜色设置为红色
  146 + style={{ marginLeft: '10px' }}
  147 + onFinish={() => {
  148 + actionRef.current.reload();
  149 + }}
  150 + >
  151 + 删除
  152 + </Button>
  153 + </>,
  154 + ];
  155 + },
  156 + }}
  157 + onFinish={async () => {
  158 + // 提交成功后,显示 ClientDrawer
  159 + // setIsModalVisible(true);
  160 + return true;
  161 + }}
  162 + >
  163 + <Descriptions items={items} column={1} />
  164 + <a href={attachments} download>
  165 + 下载附件
  166 + </a>
  167 + </ModalForm>
  168 + </Space>
  169 + );
  170 +};
src/pages/Client/FollowRecord/index.tsx 0 → 100644
  1 +import { RESPONSE_CODE } from '@/constants/enum';
  2 +import ClientModal from '@/pages/Client/FollowRecord/Components/ClientModal';
  3 +import CommunicationHistoryModal from '@/pages/Client/FollowRecord/Components/CommunicationHistoryModal';
  4 +import {
  5 + postAdminClientQueryClientComunicationInfo,
  6 + postAdminClientRemoveClientComunicationInfo,
  7 + // postServiceConstClientGroupFilters,
  8 + postServiceConstClientWay,
  9 + postServiceConstTradeStatus,
  10 +} from '@/services';
  11 +import { enumToSelect } from '@/utils';
  12 +import type { ActionType } from '@ant-design/pro-components';
  13 +import { ProTable } from '@ant-design/pro-components';
  14 +import { Button, Space, message } from 'antd';
  15 +import { useRef, useState } from 'react';
  16 +
  17 +export default () => {
  18 + const actionRef = useRef<ActionType>();
  19 + const [refreshKey, setRefreshKey] = useState(0); // 用于强制刷新的键
  20 +
  21 + const reload = () => {
  22 + actionRef.current.reload(); // 重新加载数据
  23 + console.log('5656flush');
  24 +
  25 + // 更新 refreshKey,强制刷新 CommunicationHistoryModal
  26 + setRefreshKey((prevKey) => prevKey + 1);
  27 + };
  28 + //biaoji
  29 + const columns = [
  30 + {
  31 + title: '跟进日期',
  32 + key: 'datetime',
  33 + width: 150,
  34 + ellipsis: true,
  35 + dataIndex: 'datetime',
  36 + valueType: 'dateTime',
  37 + hideInSearch: true,
  38 + search: {
  39 + transform: (value) => {
  40 + if (value) {
  41 + return {
  42 + createTimeGe: value[0],
  43 + createTimeLe: value[1],
  44 + };
  45 + }
  46 + },
  47 + },
  48 + },
  49 + {
  50 + title: '跟进时间',
  51 + dataIndex: 'dateRange',
  52 + valueType: 'dateRange',
  53 + hideInTable: true,
  54 + search: {
  55 + transform: (value) => {
  56 + if (value) {
  57 + return {
  58 + createTimeGe: value[0],
  59 + createTimeLe: value[1],
  60 + };
  61 + }
  62 + },
  63 + },
  64 + },
  65 + {
  66 + title: '跟进人员',
  67 + dataIndex: 'createByName',
  68 + width: 100,
  69 + ellipsis: true,
  70 + hideInSearch: false,
  71 + },
  72 + {
  73 + title: '客户名称',
  74 + dataIndex: 'clientName',
  75 + width: 150,
  76 + ellipsis: true,
  77 + hideInSearch: true,
  78 + },
  79 + {
  80 + title: '客户地址',
  81 + dataIndex: 'clientAddress',
  82 + width: 250,
  83 + ellipsis: true,
  84 + hideInSearch: true,
  85 + },
  86 + {
  87 + title: '类型',
  88 + dataIndex: 'wayText',
  89 + width: 100,
  90 + ellipsis: true,
  91 + hideInSearch: true,
  92 + },
  93 + {
  94 + title: '类型',
  95 + dataIndex: 'way',
  96 + width: 100,
  97 + ellipsis: true,
  98 + hideInSearch: false,
  99 + hideInTable: true,
  100 + request: async () => {
  101 + const res = await postServiceConstClientWay();
  102 + return enumToSelect(res.data);
  103 + },
  104 + },
  105 + {
  106 + title: '客户名称',
  107 + dataIndex: 'clientNameLike',
  108 + width: 150,
  109 + ellipsis: true,
  110 + hideInSearch: false,
  111 + hideInTable: true,
  112 + },
  113 + {
  114 + title: '客户地址',
  115 + dataIndex: 'clientAddressLike',
  116 + width: 250,
  117 + ellipsis: true,
  118 + hideInSearch: false,
  119 + hideInTable: true,
  120 + },
  121 + {
  122 + title: '客户状态',
  123 + dataIndex: 'tradeStatus',
  124 + width: 100,
  125 + ellipsis: true,
  126 + hideInSearch: true,
  127 + },
  128 + {
  129 + title: '客户状态',
  130 + dataIndex: 'tradeStatusLike',
  131 + width: 100,
  132 + ellipsis: true,
  133 + hideInSearch: false,
  134 + hideInTable: true,
  135 + request: async () => {
  136 + const res = await postServiceConstTradeStatus();
  137 + return enumToSelect(res.data);
  138 + },
  139 + },
  140 + {
  141 + title: '跟进详情',
  142 + dataIndex: 'content',
  143 + width: 250,
  144 + ellipsis: true,
  145 + hideInSearch: false,
  146 + },
  147 + {
  148 + title: '创建时间',
  149 + dataIndex: 'createTime',
  150 + width: 150,
  151 + ellipsis: true,
  152 + hideInSearch: true,
  153 + },
  154 + {
  155 + title: '附件',
  156 + dataIndex: 'attachments',
  157 + width: 150,
  158 + ellipsis: true,
  159 + hideInSearch: true,
  160 + hideInTable: true,
  161 + },
  162 + {
  163 + title: '操作',
  164 + valueType: 'option',
  165 + key: 'option',
  166 + width: 150,
  167 + render: (text, record, index, action) => {
  168 + const handleDelete = async () => {
  169 + // console.log(JSON.stringify(record), '5656record');
  170 + // 调用删除接口
  171 + const success = await postAdminClientRemoveClientComunicationInfo({
  172 + query: {
  173 + id: record.id,
  174 + },
  175 + });
  176 + if (success) {
  177 + action.reload(); // 刷新表格
  178 + }
  179 + };
  180 + return [
  181 + <CommunicationHistoryModal
  182 + // key={'communicationHistory'}
  183 + key={`communicationHistory-${refreshKey}`} // 使用 refreshKey 来强制更新组件
  184 + data={record}
  185 + // reloadTable={() => {
  186 + // actionRef.current.reload();
  187 + // console.log('5656flush');
  188 + // }}
  189 + reloadTable={reload}
  190 + />,
  191 + <>
  192 + <Button
  193 + key={'delete'}
  194 + onClick={() => {
  195 + handleDelete();
  196 + actionRef.current.reload();
  197 + }}
  198 + // reloadTable={() => {
  199 + // actionRef.current.reload();
  200 + // }}
  201 + type="link"
  202 + size="middle"
  203 + danger // 使用 danger 属性来将按钮颜色设置为红色
  204 + // onFinish={() => {
  205 + // actionRef.current.reload();
  206 + // }}
  207 + >
  208 + 删除
  209 + </Button>
  210 + </>,
  211 + ];
  212 + },
  213 + },
  214 + ];
  215 +
  216 + // useEffect(() => {
  217 + // const pullGroupFilterDataOptions = async () => {
  218 + // const res = await postServiceConstClientGroupFilters();
  219 + // console.log('setGroupFilterDataOptions' + JSON.stringify(res.data));
  220 + // setGroupFilterDataOptions(enumToSelect(res.data));
  221 + // };
  222 + // pullGroupFilterDataOptions();
  223 + // }, []);
  224 + // useEffect(() => {
  225 + // actionRef.current?.reload();
  226 + // }, [groupFilter]);
  227 + return (
  228 + <>
  229 + <Space direction="vertical" size="middle" style={{ display: 'flex' }}>
  230 + <ProTable
  231 + columns={columns}
  232 + actionRef={actionRef}
  233 + cardBordered
  234 + request={async (params) => {
  235 + const res = await postAdminClientQueryClientComunicationInfo({
  236 + data: {
  237 + ...params,
  238 + },
  239 + });
  240 + console.log(params, '5656566params');
  241 + if (res.result === RESPONSE_CODE.SUCCESS) {
  242 + console.log(JSON.stringify(res.data));
  243 + return {
  244 + ...res.data,
  245 + data: res.data.data.map((item) => {
  246 + return {
  247 + ...item,
  248 + tid: (Math.random() * 1000000).toFixed(0),
  249 + };
  250 + }),
  251 + };
  252 + } else {
  253 + message.error('获取失败');
  254 + }
  255 + }}
  256 + search={{
  257 + optionRender: (searchConfig, formProps, dom) => [...dom.reverse()],
  258 + }}
  259 + scroll={{
  260 + x: 1400,
  261 + }}
  262 + editable={{
  263 + type: 'multiple',
  264 + }}
  265 + columnsState={{
  266 + persistenceKey: 'pro-table-singe-demos',
  267 + persistenceType: 'localStorage',
  268 + defaultValue: {
  269 + option: { fixed: 'right', disable: true },
  270 + },
  271 + onChange(value) {
  272 + console.log('value: ', value);
  273 + },
  274 + }}
  275 + rowKey="id"
  276 + options={{
  277 + setting: {
  278 + listsHeight: 400,
  279 + },
  280 + }}
  281 + form={{
  282 + // 由于配置了 transform,提交的参与与定义的不同这里需要转化一下
  283 + syncToUrl: (values, type) => {
  284 + if (type === 'get') {
  285 + return {
  286 + ...values,
  287 + created_at: [values.startTime, values.endTime],
  288 + };
  289 + }
  290 + return values;
  291 + },
  292 + }}
  293 + pagination={{
  294 + pageSize: 5,
  295 + onChange: (page) => console.log(page),
  296 + }}
  297 + dateFormatter="string"
  298 + headerTitle="高级表格"
  299 + toolBarRender={() => [
  300 + <ClientModal
  301 + key={'add'}
  302 + reloadTable={() => {
  303 + actionRef.current.reload();
  304 + }}
  305 + type={'add'}
  306 + />,
  307 + ]}
  308 + />
  309 + </Space>
  310 + {/* {contextHolder} */}
  311 + </>
  312 + );
  313 +};
src/services/definition.ts
@@ -94,6 +94,11 @@ export interface AdminAuthUserVO { @@ -94,6 +94,11 @@ export interface AdminAuthUserVO {
94 export interface AdminClientDto { 94 export interface AdminClientDto {
95 /** 95 /**
96 * @description 96 * @description
  97 + * 客户地址
  98 + */
  99 + address?: string;
  100 + /**
  101 + * @description
97 * 市 102 * 市
98 */ 103 */
99 city?: string; 104 city?: string;
@@ -108,17 +113,17 @@ export interface AdminClientDto { @@ -108,17 +113,17 @@ export interface AdminClientDto {
108 */ 113 */
109 companyAddressText?: string; 114 companyAddressText?: string;
110 companyId?: string; 115 companyId?: string;
111 - /**  
112 - * @description  
113 - * 单位名称  
114 - */  
115 - companyName?: string;  
116 contacts?: string; 116 contacts?: string;
117 createBy?: string; 117 createBy?: string;
118 /** @format date-time */ 118 /** @format date-time */
119 createTime?: string; 119 createTime?: string;
120 /** 120 /**
121 * @description 121 * @description
  122 + * 详细地址
  123 + */
  124 + detailAddress?: string;
  125 + /**
  126 + * @description
122 * 区 127 * 区
123 */ 128 */
124 district?: string; 129 district?: string;
@@ -2791,6 +2796,7 @@ export interface QueryBankStatementDto { @@ -2791,6 +2796,7 @@ export interface QueryBankStatementDto {
2791 } 2796 }
2792 2797
2793 export interface QueryClientDto { 2798 export interface QueryClientDto {
  2799 + address?: string;
2794 companyAddressLike?: string; 2800 companyAddressLike?: string;
2795 companyIds?: Array<number>; 2801 companyIds?: Array<number>;
2796 companyNameLike?: string; 2802 companyNameLike?: string;
@@ -2802,6 +2808,7 @@ export interface QueryClientDto { @@ -2802,6 +2808,7 @@ export interface QueryClientDto {
2802 createTimeLe?: string; 2808 createTimeLe?: string;
2803 /** @format int32 */ 2809 /** @format int32 */
2804 current?: number; 2810 current?: number;
  2811 + detailAddress?: string;
2805 /** @format int32 */ 2812 /** @format int32 */
2806 end?: number; 2813 end?: number;
2807 groupFilter?: string; 2814 groupFilter?: string;
@@ -2811,6 +2818,8 @@ export interface QueryClientDto { @@ -2811,6 +2818,8 @@ export interface QueryClientDto {
2811 /** @format int32 */ 2818 /** @format int32 */
2812 pageSize?: number; 2819 pageSize?: number;
2813 phoneNumber?: string; 2820 phoneNumber?: string;
  2821 + requirements?: string;
  2822 + source?: string;
2814 /** @format int32 */ 2823 /** @format int32 */
2815 start?: number; 2824 start?: number;
2816 /** @format int32 */ 2825 /** @format int32 */
src/services/request.ts
@@ -64,6 +64,7 @@ import type { @@ -64,6 +64,7 @@ import type {
64 MaterialUnitListRes, 64 MaterialUnitListRes,
65 MeasureUnitListRes, 65 MeasureUnitListRes,
66 MessageQueryDTO, 66 MessageQueryDTO,
  67 + ModelAndView,
67 OrderAddVO, 68 OrderAddVO,
68 OrderAuditLogQueryVO, 69 OrderAuditLogQueryVO,
69 OrderBaseInfoQueryVO, 70 OrderBaseInfoQueryVO,
@@ -360,6 +361,79 @@ export const postAdminClientAddOrModifyClientComunicationInfo = @@ -360,6 +361,79 @@ export const postAdminClientAddOrModifyClientComunicationInfo =
360 return request; 361 return request;
361 })(); 362 })();
362 363
  364 +/** @description request parameter type for postAdminClientDeleteAdminClient */
  365 +export interface PostAdminClientDeleteAdminClientOption {
  366 + /**
  367 + * @description
  368 + * id
  369 + * @format int64
  370 + */
  371 + query?: {
  372 + /**
  373 + @description
  374 + id
  375 + @format int64 */
  376 + id?: number;
  377 + };
  378 +}
  379 +
  380 +/** @description response type for postAdminClientDeleteAdminClient */
  381 +export interface PostAdminClientDeleteAdminClientResponse {
  382 + /**
  383 + * @description
  384 + * OK
  385 + */
  386 + 200: ServerResult;
  387 + /**
  388 + * @description
  389 + * Created
  390 + */
  391 + 201: any;
  392 + /**
  393 + * @description
  394 + * Unauthorized
  395 + */
  396 + 401: any;
  397 + /**
  398 + * @description
  399 + * Forbidden
  400 + */
  401 + 403: any;
  402 + /**
  403 + * @description
  404 + * Not Found
  405 + */
  406 + 404: any;
  407 +}
  408 +
  409 +export type PostAdminClientDeleteAdminClientResponseSuccess =
  410 + PostAdminClientDeleteAdminClientResponse[200];
  411 +/**
  412 + * @description
  413 + * 删除客户
  414 + * @tags 客户管理
  415 + * @produces *
  416 + * @consumes application/json
  417 + */
  418 +export const postAdminClientDeleteAdminClient = /* #__PURE__ */ (() => {
  419 + const method = 'post';
  420 + const url = '/admin/client/deleteAdminClient';
  421 + function request(
  422 + option?: PostAdminClientDeleteAdminClientOption,
  423 + ): Promise<PostAdminClientDeleteAdminClientResponseSuccess> {
  424 + return requester(request.url, {
  425 + method: request.method,
  426 + ...option,
  427 + }) as unknown as Promise<PostAdminClientDeleteAdminClientResponseSuccess>;
  428 + }
  429 +
  430 + /** http method */
  431 + request.method = method;
  432 + /** request url */
  433 + request.url = url;
  434 + return request;
  435 +})();
  436 +
363 /** @description response type for postAdminClientDownloadImportTemplate */ 437 /** @description response type for postAdminClientDownloadImportTemplate */
364 export interface PostAdminClientDownloadImportTemplateResponse { 438 export interface PostAdminClientDownloadImportTemplateResponse {
365 /** 439 /**
@@ -3237,9 +3311,7 @@ export interface GetErrorResponse { @@ -3237,9 +3311,7 @@ export interface GetErrorResponse {
3237 * @description 3311 * @description
3238 * OK 3312 * OK
3239 */ 3313 */
3240 - 200: {  
3241 - [propertyName: string]: any;  
3242 - }; 3314 + 200: ModelAndView;
3243 /** 3315 /**
3244 * @description 3316 * @description
3245 * Unauthorized 3317 * Unauthorized
@@ -3260,9 +3332,9 @@ export interface GetErrorResponse { @@ -3260,9 +3332,9 @@ export interface GetErrorResponse {
3260 export type GetErrorResponseSuccess = GetErrorResponse[200]; 3332 export type GetErrorResponseSuccess = GetErrorResponse[200];
3261 /** 3333 /**
3262 * @description 3334 * @description
3263 - * error 3335 + * errorHtml
3264 * @tags basic-error-controller 3336 * @tags basic-error-controller
3265 - * @produces * 3337 + * @produces text/html
3266 */ 3338 */
3267 export const getError = /* #__PURE__ */ (() => { 3339 export const getError = /* #__PURE__ */ (() => {
3268 const method = 'get'; 3340 const method = 'get';
@@ -3286,9 +3358,7 @@ export interface PutErrorResponse { @@ -3286,9 +3358,7 @@ export interface PutErrorResponse {
3286 * @description 3358 * @description
3287 * OK 3359 * OK
3288 */ 3360 */
3289 - 200: {  
3290 - [propertyName: string]: any;  
3291 - }; 3361 + 200: ModelAndView;
3292 /** 3362 /**
3293 * @description 3363 * @description
3294 * Created 3364 * Created
@@ -3314,9 +3384,9 @@ export interface PutErrorResponse { @@ -3314,9 +3384,9 @@ export interface PutErrorResponse {
3314 export type PutErrorResponseSuccess = PutErrorResponse[200]; 3384 export type PutErrorResponseSuccess = PutErrorResponse[200];
3315 /** 3385 /**
3316 * @description 3386 * @description
3317 - * error 3387 + * errorHtml
3318 * @tags basic-error-controller 3388 * @tags basic-error-controller
3319 - * @produces * 3389 + * @produces text/html
3320 * @consumes application/json 3390 * @consumes application/json
3321 */ 3391 */
3322 export const putError = /* #__PURE__ */ (() => { 3392 export const putError = /* #__PURE__ */ (() => {
@@ -3341,9 +3411,7 @@ export interface PostErrorResponse { @@ -3341,9 +3411,7 @@ export interface PostErrorResponse {
3341 * @description 3411 * @description
3342 * OK 3412 * OK
3343 */ 3413 */
3344 - 200: {  
3345 - [propertyName: string]: any;  
3346 - }; 3414 + 200: ModelAndView;
3347 /** 3415 /**
3348 * @description 3416 * @description
3349 * Created 3417 * Created
@@ -3369,9 +3437,9 @@ export interface PostErrorResponse { @@ -3369,9 +3437,9 @@ export interface PostErrorResponse {
3369 export type PostErrorResponseSuccess = PostErrorResponse[200]; 3437 export type PostErrorResponseSuccess = PostErrorResponse[200];
3370 /** 3438 /**
3371 * @description 3439 * @description
3372 - * error 3440 + * errorHtml
3373 * @tags basic-error-controller 3441 * @tags basic-error-controller
3374 - * @produces * 3442 + * @produces text/html
3375 * @consumes application/json 3443 * @consumes application/json
3376 */ 3444 */
3377 export const postError = /* #__PURE__ */ (() => { 3445 export const postError = /* #__PURE__ */ (() => {
@@ -3396,9 +3464,7 @@ export interface DeleteErrorResponse { @@ -3396,9 +3464,7 @@ export interface DeleteErrorResponse {
3396 * @description 3464 * @description
3397 * OK 3465 * OK
3398 */ 3466 */
3399 - 200: {  
3400 - [propertyName: string]: any;  
3401 - }; 3467 + 200: ModelAndView;
3402 /** 3468 /**
3403 * @description 3469 * @description
3404 * No Content 3470 * No Content
@@ -3419,9 +3485,9 @@ export interface DeleteErrorResponse { @@ -3419,9 +3485,9 @@ export interface DeleteErrorResponse {
3419 export type DeleteErrorResponseSuccess = DeleteErrorResponse[200]; 3485 export type DeleteErrorResponseSuccess = DeleteErrorResponse[200];
3420 /** 3486 /**
3421 * @description 3487 * @description
3422 - * error 3488 + * errorHtml
3423 * @tags basic-error-controller 3489 * @tags basic-error-controller
3424 - * @produces * 3490 + * @produces text/html
3425 */ 3491 */
3426 export const deleteError = /* #__PURE__ */ (() => { 3492 export const deleteError = /* #__PURE__ */ (() => {
3427 const method = 'delete'; 3493 const method = 'delete';
@@ -3445,9 +3511,7 @@ export interface OptionsErrorResponse { @@ -3445,9 +3511,7 @@ export interface OptionsErrorResponse {
3445 * @description 3511 * @description
3446 * OK 3512 * OK
3447 */ 3513 */
3448 - 200: {  
3449 - [propertyName: string]: any;  
3450 - }; 3514 + 200: ModelAndView;
3451 /** 3515 /**
3452 * @description 3516 * @description
3453 * No Content 3517 * No Content
@@ -3468,9 +3532,9 @@ export interface OptionsErrorResponse { @@ -3468,9 +3532,9 @@ export interface OptionsErrorResponse {
3468 export type OptionsErrorResponseSuccess = OptionsErrorResponse[200]; 3532 export type OptionsErrorResponseSuccess = OptionsErrorResponse[200];
3469 /** 3533 /**
3470 * @description 3534 * @description
3471 - * error 3535 + * errorHtml
3472 * @tags basic-error-controller 3536 * @tags basic-error-controller
3473 - * @produces * 3537 + * @produces text/html
3474 * @consumes application/json 3538 * @consumes application/json
3475 */ 3539 */
3476 export const optionsError = /* #__PURE__ */ (() => { 3540 export const optionsError = /* #__PURE__ */ (() => {
@@ -3495,9 +3559,7 @@ export interface HeadErrorResponse { @@ -3495,9 +3559,7 @@ export interface HeadErrorResponse {
3495 * @description 3559 * @description
3496 * OK 3560 * OK
3497 */ 3561 */
3498 - 200: {  
3499 - [propertyName: string]: any;  
3500 - }; 3562 + 200: ModelAndView;
3501 /** 3563 /**
3502 * @description 3564 * @description
3503 * No Content 3565 * No Content
@@ -3518,9 +3580,9 @@ export interface HeadErrorResponse { @@ -3518,9 +3580,9 @@ export interface HeadErrorResponse {
3518 export type HeadErrorResponseSuccess = HeadErrorResponse[200]; 3580 export type HeadErrorResponseSuccess = HeadErrorResponse[200];
3519 /** 3581 /**
3520 * @description 3582 * @description
3521 - * error 3583 + * errorHtml
3522 * @tags basic-error-controller 3584 * @tags basic-error-controller
3523 - * @produces * 3585 + * @produces text/html
3524 * @consumes application/json 3586 * @consumes application/json
3525 */ 3587 */
3526 export const headError = /* #__PURE__ */ (() => { 3588 export const headError = /* #__PURE__ */ (() => {
@@ -3545,9 +3607,7 @@ export interface PatchErrorResponse { @@ -3545,9 +3607,7 @@ export interface PatchErrorResponse {
3545 * @description 3607 * @description
3546 * OK 3608 * OK
3547 */ 3609 */
3548 - 200: {  
3549 - [propertyName: string]: any;  
3550 - }; 3610 + 200: ModelAndView;
3551 /** 3611 /**
3552 * @description 3612 * @description
3553 * No Content 3613 * No Content
@@ -3568,9 +3628,9 @@ export interface PatchErrorResponse { @@ -3568,9 +3628,9 @@ export interface PatchErrorResponse {
3568 export type PatchErrorResponseSuccess = PatchErrorResponse[200]; 3628 export type PatchErrorResponseSuccess = PatchErrorResponse[200];
3569 /** 3629 /**
3570 * @description 3630 * @description
3571 - * error 3631 + * errorHtml
3572 * @tags basic-error-controller 3632 * @tags basic-error-controller
3573 - * @produces * 3633 + * @produces text/html
3574 * @consumes application/json 3634 * @consumes application/json
3575 */ 3635 */
3576 export const patchError = /* #__PURE__ */ (() => { 3636 export const patchError = /* #__PURE__ */ (() => {
@@ -13049,6 +13109,168 @@ export const postServiceConstClientLevels = /* #__PURE__ */ (() =&gt; { @@ -13049,6 +13109,168 @@ export const postServiceConstClientLevels = /* #__PURE__ */ (() =&gt; {
13049 return request; 13109 return request;
13050 })(); 13110 })();
13051 13111
  13112 +/** @description response type for postServiceConstClientRequirements */
  13113 +export interface PostServiceConstClientRequirementsResponse {
  13114 + /**
  13115 + * @description
  13116 + * OK
  13117 + */
  13118 + 200: ServerResult;
  13119 + /**
  13120 + * @description
  13121 + * Created
  13122 + */
  13123 + 201: any;
  13124 + /**
  13125 + * @description
  13126 + * Unauthorized
  13127 + */
  13128 + 401: any;
  13129 + /**
  13130 + * @description
  13131 + * Forbidden
  13132 + */
  13133 + 403: any;
  13134 + /**
  13135 + * @description
  13136 + * Not Found
  13137 + */
  13138 + 404: any;
  13139 +}
  13140 +
  13141 +export type PostServiceConstClientRequirementsResponseSuccess =
  13142 + PostServiceConstClientRequirementsResponse[200];
  13143 +/**
  13144 + * @description
  13145 + * 客户需求
  13146 + * @tags front-const-controller
  13147 + * @produces *
  13148 + * @consumes application/json
  13149 + */
  13150 +export const postServiceConstClientRequirements = /* #__PURE__ */ (() => {
  13151 + const method = 'post';
  13152 + const url = '/service/const/clientRequirements';
  13153 + function request(): Promise<PostServiceConstClientRequirementsResponseSuccess> {
  13154 + return requester(request.url, {
  13155 + method: request.method,
  13156 + }) as unknown as Promise<PostServiceConstClientRequirementsResponseSuccess>;
  13157 + }
  13158 +
  13159 + /** http method */
  13160 + request.method = method;
  13161 + /** request url */
  13162 + request.url = url;
  13163 + return request;
  13164 +})();
  13165 +
  13166 +/** @description response type for postServiceConstClientSource */
  13167 +export interface PostServiceConstClientSourceResponse {
  13168 + /**
  13169 + * @description
  13170 + * OK
  13171 + */
  13172 + 200: ServerResult;
  13173 + /**
  13174 + * @description
  13175 + * Created
  13176 + */
  13177 + 201: any;
  13178 + /**
  13179 + * @description
  13180 + * Unauthorized
  13181 + */
  13182 + 401: any;
  13183 + /**
  13184 + * @description
  13185 + * Forbidden
  13186 + */
  13187 + 403: any;
  13188 + /**
  13189 + * @description
  13190 + * Not Found
  13191 + */
  13192 + 404: any;
  13193 +}
  13194 +
  13195 +export type PostServiceConstClientSourceResponseSuccess =
  13196 + PostServiceConstClientSourceResponse[200];
  13197 +/**
  13198 + * @description
  13199 + * 客户来源
  13200 + * @tags front-const-controller
  13201 + * @produces *
  13202 + * @consumes application/json
  13203 + */
  13204 +export const postServiceConstClientSource = /* #__PURE__ */ (() => {
  13205 + const method = 'post';
  13206 + const url = '/service/const/clientSource';
  13207 + function request(): Promise<PostServiceConstClientSourceResponseSuccess> {
  13208 + return requester(request.url, {
  13209 + method: request.method,
  13210 + }) as unknown as Promise<PostServiceConstClientSourceResponseSuccess>;
  13211 + }
  13212 +
  13213 + /** http method */
  13214 + request.method = method;
  13215 + /** request url */
  13216 + request.url = url;
  13217 + return request;
  13218 +})();
  13219 +
  13220 +/** @description response type for postServiceConstClientWay */
  13221 +export interface PostServiceConstClientWayResponse {
  13222 + /**
  13223 + * @description
  13224 + * OK
  13225 + */
  13226 + 200: ServerResult;
  13227 + /**
  13228 + * @description
  13229 + * Created
  13230 + */
  13231 + 201: any;
  13232 + /**
  13233 + * @description
  13234 + * Unauthorized
  13235 + */
  13236 + 401: any;
  13237 + /**
  13238 + * @description
  13239 + * Forbidden
  13240 + */
  13241 + 403: any;
  13242 + /**
  13243 + * @description
  13244 + * Not Found
  13245 + */
  13246 + 404: any;
  13247 +}
  13248 +
  13249 +export type PostServiceConstClientWayResponseSuccess =
  13250 + PostServiceConstClientWayResponse[200];
  13251 +/**
  13252 + * @description
  13253 + * 类型
  13254 + * @tags front-const-controller
  13255 + * @produces *
  13256 + * @consumes application/json
  13257 + */
  13258 +export const postServiceConstClientWay = /* #__PURE__ */ (() => {
  13259 + const method = 'post';
  13260 + const url = '/service/const/clientWay';
  13261 + function request(): Promise<PostServiceConstClientWayResponseSuccess> {
  13262 + return requester(request.url, {
  13263 + method: request.method,
  13264 + }) as unknown as Promise<PostServiceConstClientWayResponseSuccess>;
  13265 + }
  13266 +
  13267 + /** http method */
  13268 + request.method = method;
  13269 + /** request url */
  13270 + request.url = url;
  13271 + return request;
  13272 +})();
  13273 +
13052 /** @description response type for postServiceConstGetPayeeEnum */ 13274 /** @description response type for postServiceConstGetPayeeEnum */
13053 export interface PostServiceConstGetPayeeEnumResponse { 13275 export interface PostServiceConstGetPayeeEnumResponse {
13054 /** 13276 /**