Commit 4465b4f3909b6d0aa92a419afb935530ba062194

Authored by PurelzMgnead
2 parents 169b564b 0f671707

feat: update订单查询

.umirc.ts
... ... @@ -86,12 +86,13 @@ export default defineConfig({
86 86 icon: 'BookOutlined',
87 87 access: 'canReadAdmin',
88 88 },
89   - /*{
  89 + {
90 90 name: '客户管理',
91 91 path: '/client',
92 92 component: './Client',
93 93 icon: 'BookOutlined',
94   - },*/
  94 + access: 'canReadAdmin',
  95 + },
95 96 {
96 97 name: '打印',
97 98 path: '/print',
... ...
src/pages/Client/Components/ClientDrawer.tsx 0 → 100644
  1 +import { RESPONSE_CODE } from '@/constants/enum';
  2 +import {
  3 + postAdminClientAddAdminClient,
  4 + postAdminClientModifyClientInfo,
  5 + postDistrictSelectByLevel,
  6 + postDistrictSelectByNameAndLevel,
  7 + postDistrictSelOrderProvince,
  8 + postServiceConstClientLevels,
  9 + postServiceConstTradeStatus,
  10 +} from '@/services';
  11 +import { enumToSelect } from '@/utils';
  12 +import {
  13 + DrawerForm,
  14 + ProFormDateTimePicker,
  15 + ProFormSelect,
  16 + ProFormText,
  17 +} from '@ant-design/pro-components';
  18 +import { Button, Form, message } from 'antd';
  19 +import { useState } from 'react';
  20 +
  21 +export default ({ optType, record }) => {
  22 + const [form] = Form.useForm<{ name: string; company: string }>();
  23 + //省市区
  24 + const [province, setProvince] = useState('');
  25 + const [city, setCity] = useState('');
  26 + const [setDistrict] = useState('');
  27 + const optTypeEnum = {
  28 + add: {
  29 + text: '新增',
  30 + button: <Button type="primary">新增</Button>,
  31 + readonly: false,
  32 + onFinish: async (values) => {
  33 + const res = await postAdminClientAddAdminClient({
  34 + data: values,
  35 + });
  36 + if (res.result === RESPONSE_CODE.SUCCESS) {
  37 + message.success('新增成功');
  38 + // 不返回不会关闭弹框
  39 + return true;
  40 + }
  41 + },
  42 + },
  43 + edit: {
  44 + text: '编辑',
  45 + button: <a type="primary">编辑</a>,
  46 + readonly: false,
  47 + onFinish: async (values) => {
  48 + const res = await postAdminClientModifyClientInfo({
  49 + data: values,
  50 + });
  51 + if (res.result === RESPONSE_CODE.SUCCESS) {
  52 + message.success('编辑成功');
  53 + // 不返回不会关闭弹框
  54 + return true;
  55 + }
  56 + },
  57 + },
  58 + detail: {
  59 + text: '详情',
  60 + button: <a type="primary">详情</a>,
  61 + readonly: true,
  62 + },
  63 + };
  64 +
  65 + return (
  66 + <DrawerForm
  67 + title={optTypeEnum[optType].text}
  68 + resize={{
  69 + onResize() {
  70 + console.log('resize!');
  71 + },
  72 + maxWidth: window.innerWidth * 0.8,
  73 + minWidth: 300,
  74 + }}
  75 + initialValues={record}
  76 + form={form}
  77 + trigger={optTypeEnum[optType].button}
  78 + autoFocusFirstInput
  79 + drawerProps={{
  80 + destroyOnClose: true,
  81 + }}
  82 + submitTimeout={2000}
  83 + width={500}
  84 + readonly={optTypeEnum[optType].readonly}
  85 + onFinish={optTypeEnum[optType].onFinish}
  86 + >
  87 + <ProFormText name="id" label="id" hidden={true} />
  88 +
  89 + <ProFormText
  90 + name="name"
  91 + label="客户名称"
  92 + placeholder="请输入名称"
  93 + rules={[
  94 + {
  95 + required: true,
  96 + message: '请输入客户名称',
  97 + },
  98 + ]}
  99 + />
  100 + <ProFormText
  101 + name="companyName"
  102 + label="单位名称"
  103 + placeholder="请输入单位名称"
  104 + rules={[
  105 + {
  106 + required: true,
  107 + message: '请输入单位名称',
  108 + },
  109 + ]}
  110 + />
  111 + <div
  112 + style={{
  113 + display: 'flex',
  114 + justifyContent: 'space-between',
  115 + width: 340,
  116 + }}
  117 + >
  118 + <ProFormSelect
  119 + name="province"
  120 + key="province"
  121 + width={100}
  122 + label="省"
  123 + allowClear={false}
  124 + onChange={(value) => {
  125 + console.log(value);
  126 +
  127 + if (value !== undefined || value !== null) {
  128 + console.log('setProvince');
  129 +
  130 + setProvince(value);
  131 + }
  132 + }}
  133 + placeholder="请选择"
  134 + rules={[
  135 + {
  136 + required: true,
  137 + message: '请选择!',
  138 + },
  139 + ]}
  140 + request={async () => {
  141 + let province = [];
  142 + let res = await postDistrictSelectByLevel({ data: 1 });
  143 + if (res) {
  144 + res.data.forEach((item) => {
  145 + province.push({ value: item.district, label: item.district });
  146 + });
  147 + }
  148 + return province;
  149 + }}
  150 + />
  151 + <ProFormSelect
  152 + key={province}
  153 + name="city"
  154 + width={100}
  155 + label="市"
  156 + allowClear={false}
  157 + disabled={province === ''}
  158 + placeholder="请选择"
  159 + onChange={(value) => {
  160 + if (value !== undefined || value !== null) {
  161 + setCity(value);
  162 + }
  163 + }}
  164 + rules={[
  165 + {
  166 + required: true,
  167 + message: '请选择!',
  168 + },
  169 + ]}
  170 + request={async () => {
  171 + let cityOptions = [];
  172 + console.log(form.getFieldValue('id'));
  173 + if (form.getFieldValue('id')) {
  174 + const resp = await postDistrictSelOrderProvince({
  175 + data: form.getFieldValue('id'),
  176 + });
  177 + if (
  178 + resp.data.province !== null &&
  179 + resp.data.province !== undefined
  180 + ) {
  181 + console.log('province is ok');
  182 + let res = await postDistrictSelectByNameAndLevel({
  183 + data: { district: resp.data.province, level: 1 },
  184 + });
  185 + if (res && res.data) {
  186 + cityOptions = res.data.map((item) => ({
  187 + value: item.district,
  188 + label: item.district,
  189 + }));
  190 + }
  191 + }
  192 + }
  193 + if (province !== '') {
  194 + console.log(province);
  195 + console.log('province is okk');
  196 + let res = await postDistrictSelectByNameAndLevel({
  197 + data: { district: province, level: 1 },
  198 + });
  199 + if (res && res.data) {
  200 + cityOptions = res.data.map((item) => ({
  201 + value: item.district,
  202 + label: item.district,
  203 + }));
  204 + }
  205 + }
  206 + return cityOptions;
  207 + }}
  208 + />
  209 + <ProFormSelect
  210 + key={city ? city.toString() : 'district'}
  211 + name="district"
  212 + width={100}
  213 + label="区"
  214 + allowClear={false}
  215 + onChange={(value) => {
  216 + if (value !== undefined || value !== null) {
  217 + setDistrict(value);
  218 + }
  219 + }}
  220 + disabled={city === ''}
  221 + placeholder="请选择"
  222 + rules={[
  223 + {
  224 + required: true,
  225 + message: '请选择!',
  226 + },
  227 + ]}
  228 + request={async () => {
  229 + let districtOptions = [];
  230 + if (form.getFieldValue('id')) {
  231 + const resp = await postDistrictSelOrderProvince({
  232 + data: form.getFieldValue('id'),
  233 + });
  234 + if (resp.data.city !== null && resp.data.city !== undefined) {
  235 + let res = await postDistrictSelectByNameAndLevel({
  236 + data: { district: resp.data.city, level: 2 },
  237 + });
  238 + if (res && res.data) {
  239 + districtOptions = res.data.map((item) => ({
  240 + value: item.district,
  241 + label: item.district,
  242 + }));
  243 + }
  244 + }
  245 + }
  246 + if (city !== '') {
  247 + let res = await postDistrictSelectByNameAndLevel({
  248 + data: { district: city, level: 2 },
  249 + });
  250 + if (res && res.data) {
  251 + districtOptions = res.data.map((item) => ({
  252 + value: item.district,
  253 + label: item.district,
  254 + }));
  255 + }
  256 + }
  257 + return districtOptions;
  258 + }}
  259 + />
  260 + </div>
  261 + <ProFormText
  262 + name="companyAddress"
  263 + label="详细地址"
  264 + placeholder="请输入单位地址"
  265 + rules={[
  266 + {
  267 + required: true,
  268 + message: '请输入单位地址',
  269 + },
  270 + ]}
  271 + />
  272 + <ProFormText
  273 + name="phoneNumber"
  274 + label="联系电话"
  275 + placeholder="请输入联系电话"
  276 + rules={[
  277 + {
  278 + required: true,
  279 + message: '请输入联系电话',
  280 + },
  281 + ]}
  282 + />
  283 + <ProFormText
  284 + name="source"
  285 + label="客户来源"
  286 + placeholder="请输入客户来源"
  287 + />
  288 + <ProFormText
  289 + name="requirements"
  290 + label="客户需求"
  291 + placeholder="请输入客户需求"
  292 + rules={[
  293 + {
  294 + required: true,
  295 + message: '请输入客户需求',
  296 + },
  297 + ]}
  298 + />
  299 + <ProFormText name="referrers" label="推荐人" placeholder="请输入推荐人" />
  300 + <ProFormSelect
  301 + name="hasScheme"
  302 + label="是否已报方案"
  303 + placeholder="请选择是否已报方案"
  304 + options={[
  305 + {
  306 + label: '是',
  307 + value: true,
  308 + },
  309 + {
  310 + label: '否',
  311 + value: false,
  312 + },
  313 + ]}
  314 + rules={[
  315 + {
  316 + required: true,
  317 + message: '请选择是否已报方案',
  318 + },
  319 + ]}
  320 + />
  321 + <ProFormDateTimePicker
  322 + name="quoteDatetime"
  323 + label="报价时间"
  324 + placeholder="请输入报价时间"
  325 + />
  326 + <ProFormSelect
  327 + name="level"
  328 + label="客户等级"
  329 + placeholder="请输入客户等级"
  330 + rules={[
  331 + {
  332 + required: true,
  333 + message: '请输入客户等级',
  334 + },
  335 + ]}
  336 + request={async () => {
  337 + const res = await postServiceConstClientLevels();
  338 + return enumToSelect(res.data);
  339 + }}
  340 + />
  341 + <ProFormSelect
  342 + name="tradeStatus"
  343 + label="跟进状态"
  344 + placeholder="请输入跟进状态"
  345 + rules={[
  346 + {
  347 + required: true,
  348 + message: '请输入跟进状态',
  349 + },
  350 + ]}
  351 + request={async () => {
  352 + const res = await postServiceConstTradeStatus();
  353 + return enumToSelect(res.data);
  354 + }}
  355 + />
  356 + <ProFormText name="notes" label="备注" placeholder="请输入备注" />
  357 + </DrawerForm>
  358 + );
  359 +};
... ...
src/pages/Client/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/Components/CommunicationHistoryModal.tsx 0 → 100644
  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/index.tsx
1   -import { EllipsisOutlined, PlusOutlined } from '@ant-design/icons';
2   -import type { ActionType, ProColumns } from '@ant-design/pro-components';
3   -import { ProTable, TableDropdown } from '@ant-design/pro-components';
4   -import { request } from '@umijs/max';
5   -import { Button, Dropdown, Space, Tag } from 'antd';
  1 +import ClientDrawer from '@/pages/Client/Components/ClientDrawer';
  2 +import ClientImportModal from '@/pages/Client/Components/ClientImportModal';
  3 +import CommunicationHistoryModal from '@/pages/Client/Components/CommunicationHistoryModal';
  4 +import {
  5 + postAdminClientQueryClientPage,
  6 + postServiceConstClientLevels,
  7 + postServiceConstTradeStatus,
  8 +} from '@/services';
  9 +import { orderExport } from '@/services/order';
  10 +import { enumToSelect } from '@/utils';
  11 +import type { ActionType } from '@ant-design/pro-components';
  12 +import { ProTable } from '@ant-design/pro-components';
  13 +import { Button, message } from 'antd';
6 14 import { useRef } from 'react';
7 15  
8   -export const waitTimePromise = async (time: number = 100) => {
9   - return new Promise((resolve) => {
10   - setTimeout(() => {
11   - resolve(true);
12   - }, time);
13   - });
14   -};
15   -
16   -export const waitTime = async (time: number = 100) => {
17   - await waitTimePromise(time);
18   -};
19   -
20   -type GithubIssueItem = {
21   - url: string;
22   - id: number;
23   - number: number;
24   - title: string;
25   - labels: {
26   - name: string;
27   - color: string;
28   - }[];
29   - state: string;
30   - comments: number;
31   - created_at: string;
32   - updated_at: string;
33   - closed_at?: string;
34   -};
35   -
36   -const columns: ProColumns<GithubIssueItem>[] = [
  16 +const columns = [
37 17 {
38 18 dataIndex: 'index',
39 19 valueType: 'indexBorder',
40 20 width: 48,
41 21 },
42 22 {
43   - title: '标题',
44   - dataIndex: 'title',
45   - copyable: true,
46   - ellipsis: true,
47   - tooltip: '标题过长会自动收缩',
48   - formItemProps: {
49   - rules: [
50   - {
51   - required: true,
52   - message: '此项为必填项',
53   - },
54   - ],
55   - },
  23 + title: '客户名称',
  24 + dataIndex: 'name',
  25 + width: 100,
  26 + hideInSearch: true,
  27 + },
  28 + {
  29 + title: '单位名称',
  30 + width: 150,
  31 + dataIndex: 'companyName',
  32 + hideInSearch: true,
  33 + },
  34 + {
  35 + title: '单位地址',
  36 + width: 250,
  37 + dataIndex: 'companyName',
  38 + hideInSearch: true,
  39 + },
  40 + {
  41 + title: '联系电话',
  42 + width: 150,
  43 + dataIndex: 'phoneNumber',
  44 + hideInSearch: true,
  45 + },
  46 + {
  47 + title: '客户来源',
  48 + width: 150,
  49 + dataIndex: 'source',
  50 + hideInSearch: true,
  51 + },
  52 + {
  53 + title: '推荐人',
  54 + dataIndex: 'referrers',
  55 + width: 150,
  56 + hideInSearch: true,
  57 + },
  58 + {
  59 + title: '客户需求',
  60 + dataIndex: 'requirements',
  61 + width: 150,
  62 + hideInSearch: true,
  63 + },
  64 + {
  65 + title: '是否已报方案',
  66 + width: 150,
  67 + dataIndex: 'hasSchemeText',
  68 + hideInSearch: true,
  69 + },
  70 + {
  71 + title: '报价时间',
  72 + key: 'since',
  73 + width: 150,
  74 + dataIndex: 'quoteDatetime',
  75 + valueType: 'dateTime',
  76 + hideInSearch: true,
  77 + },
  78 + {
  79 + title: '跟进状态',
  80 + width: 150,
  81 + dataIndex: 'tradeStatusText',
  82 + hideInSearch: true,
  83 + },
  84 + {
  85 + title: '客户等级',
  86 + width: 150,
  87 + dataIndex: 'levelText',
  88 + hideInSearch: true,
56 89 },
57 90 {
58   - disable: true,
59   - title: '状态',
60   - dataIndex: 'state',
61   - filters: true,
62   - onFilter: true,
63   - ellipsis: true,
  91 + title: '创建时间',
  92 + key: 'since',
  93 + width: 150,
  94 + dataIndex: 'createTime',
  95 + valueType: 'dateTime',
  96 + hideInSearch: true,
  97 + },
  98 + {
  99 + title: '最新跟进时间',
  100 + key: 'since',
  101 + width: 150,
  102 + dataIndex: 'latestCommunicationTime',
  103 + valueType: 'dateTime',
  104 + hideInSearch: true,
  105 + },
  106 + {
  107 + title: '客户名称',
  108 + dataIndex: 'nameLike',
  109 + valueType: 'Text',
  110 + hideInTable: true,
  111 + },
  112 + {
  113 + title: '单位名称',
  114 + dataIndex: 'companyNameLike',
  115 + valueType: 'Text',
  116 + hideInTable: true,
  117 + },
  118 + {
  119 + title: '联系电话',
  120 + dataIndex: 'phoneNumberLike',
  121 + valueType: 'Text',
  122 + hideInTable: true,
  123 + },
  124 + {
  125 + title: '是否已报方案',
  126 + dataIndex: 'hasScheme',
64 127 valueType: 'select',
65 128 valueEnum: {
66   - all: { text: '超长'.repeat(50) },
67   - open: {
68   - text: '未解决',
69   - status: 'Error',
70   - },
71   - closed: {
72   - text: '已解决',
73   - status: 'Success',
74   - disabled: true,
  129 + true: {
  130 + text: '是',
  131 + value: true,
75 132 },
76   - processing: {
77   - text: '解决中',
78   - status: 'Processing',
  133 + false: {
  134 + text: '否',
  135 + value: false,
79 136 },
80 137 },
  138 + hideInTable: true,
81 139 },
82 140 {
83   - disable: true,
84   - title: '标签',
85   - dataIndex: 'labels',
86   - search: false,
87   - renderFormItem: (_, { defaultRender }) => {
88   - return defaultRender(_);
  141 + title: '客户等级',
  142 + dataIndex: 'level',
  143 + valueType: 'select',
  144 + hideInTable: true,
  145 + request: async () => {
  146 + const res = await postServiceConstClientLevels();
  147 + return enumToSelect(res.data);
89 148 },
90   - render: (_, record) => (
91   - <Space>
92   - {record.labels.map(({ name, color }) => (
93   - <Tag color={color} key={name}>
94   - {name}
95   - </Tag>
96   - ))}
97   - </Space>
98   - ),
99 149 },
100 150 {
101   - title: '创建时间',
102   - key: 'showTime',
103   - dataIndex: 'created_at',
104   - valueType: 'date',
105   - sorter: true,
106   - hideInSearch: true,
  151 + title: '跟进状态',
  152 + dataIndex: 'tradeStatus',
  153 + valueType: 'select',
  154 + hideInTable: true,
  155 + request: async () => {
  156 + const res = await postServiceConstTradeStatus();
  157 + return enumToSelect(res.data);
  158 + },
107 159 },
108 160 {
109 161 title: '创建时间',
110   - dataIndex: 'created_at',
111 162 valueType: 'dateRange',
112 163 hideInTable: true,
113 164 search: {
114 165 transform: (value) => {
115   - return {
116   - startTime: value[0],
117   - endTime: value[1],
118   - };
  166 + if (value) {
  167 + return {
  168 + createTimeGe: value[0],
  169 + createTimeLe: value[1],
  170 + };
  171 + }
119 172 },
120 173 },
121 174 },
... ... @@ -123,121 +176,121 @@ const columns: ProColumns&lt;GithubIssueItem&gt;[] = [
123 176 title: '操作',
124 177 valueType: 'option',
125 178 key: 'option',
126   - render: (text, record, _, action) => [
127   - <a
128   - key="editable"
129   - onClick={() => {
130   - action?.startEditable?.(record.id);
131   - }}
132   - >
133   - 编辑
134   - </a>,
135   - <a href={record.url} target="_blank" rel="noopener noreferrer" key="view">
136   - 查看
137   - </a>,
138   - <TableDropdown
139   - key="actionGroup"
140   - onSelect={() => action?.reload()}
141   - menus={[
142   - { key: 'copy', name: '复制' },
143   - { key: 'delete', name: '删除' },
144   - ]}
145   - />,
146   - ],
  179 + width: 150,
  180 + render: (text, record) => {
  181 + console.log(JSON.stringify(record));
  182 + return [
  183 + <CommunicationHistoryModal
  184 + key={'communicationHistory'}
  185 + clientId={record.id}
  186 + />,
  187 + <ClientDrawer
  188 + key={'detail'}
  189 + record={record}
  190 + optType={'detail'}
  191 + ></ClientDrawer>,
  192 + <ClientDrawer
  193 + key={'edit'}
  194 + record={record}
  195 + optType={'edit'}
  196 + ></ClientDrawer>,
  197 + ];
  198 + },
147 199 },
148 200 ];
149 201  
150 202 export default () => {
  203 + const [messageApi, contextHolder] = message.useMessage();
151 204 const actionRef = useRef<ActionType>();
152 205 return (
153   - <ProTable<GithubIssueItem>
154   - columns={columns}
155   - actionRef={actionRef}
156   - cardBordered
157   - request={async (params, sort, filter) => {
158   - console.log(sort, filter);
159   - await waitTime(2000);
160   - return request<{
161   - data: GithubIssueItem[];
162   - }>('https://proapi.azurewebsites.net/github/issues', {
163   - params,
164   - });
165   - }}
166   - editable={{
167   - type: 'multiple',
168   - }}
169   - columnsState={{
170   - persistenceKey: 'pro-table-singe-demos',
171   - persistenceType: 'localStorage',
172   - defaultValue: {
173   - option: { fixed: 'right', disable: true },
174   - },
175   - onChange(value) {
176   - console.log('value: ', value);
177   - },
178   - }}
179   - rowKey="id"
180   - search={{
181   - labelWidth: 'auto',
182   - }}
183   - options={{
184   - setting: {
185   - listsHeight: 400,
186   - },
187   - }}
188   - form={{
189   - // 由于配置了 transform,提交的参与与定义的不同这里需要转化一下
190   - syncToUrl: (values, type) => {
191   - if (type === 'get') {
192   - return {
193   - ...values,
194   - created_at: [values.startTime, values.endTime],
195   - };
196   - }
197   - return values;
198   - },
199   - }}
200   - pagination={{
201   - pageSize: 5,
202   - onChange: (page) => console.log(page),
203   - }}
204   - dateFormatter="string"
205   - headerTitle="高级表格"
206   - toolBarRender={() => [
207   - <Button
208   - key="button"
209   - icon={<PlusOutlined />}
210   - onClick={() => {
211   - actionRef.current?.reload();
212   - }}
213   - type="primary"
214   - >
215   - 新建
216   - </Button>,
217   - <Dropdown
218   - key="menu"
219   - menu={{
220   - items: [
221   - {
222   - label: '1st item',
223   - key: '1',
224   - },
225   - {
226   - label: '2nd item',
227   - key: '1',
228   - },
229   - {
230   - label: '3rd item',
231   - key: '1',
232   - },
233   - ],
234   - }}
235   - >
236   - <Button>
237   - <EllipsisOutlined />
238   - </Button>
239   - </Dropdown>,
240   - ]}
241   - />
  206 + <>
  207 + <ProTable
  208 + columns={columns}
  209 + actionRef={actionRef}
  210 + cardBordered
  211 + request={async (params) => {
  212 + const res = await postAdminClientQueryClientPage({
  213 + data: {
  214 + ...params,
  215 + },
  216 + });
  217 + const data = res.data;
  218 + return data;
  219 + }}
  220 + search={{
  221 + defaultCollapsed: false,
  222 + optionRender: (searchConfig, formProps, dom) => [
  223 + ...dom.reverse(),
  224 + <Button
  225 + key="out"
  226 + onClick={() => {
  227 + const values = searchConfig?.form?.getFieldsValue();
  228 + messageApi.open({
  229 + type: 'loading',
  230 + content: '导出中...',
  231 + duration: 0,
  232 + });
  233 + orderExport(
  234 + '/api/admin/client/exportClients',
  235 + '客户信息.xlsx',
  236 + 'POST',
  237 + values,
  238 + () => {
  239 + messageApi.destroy();
  240 + },
  241 + );
  242 + }}
  243 + >
  244 + 导出
  245 + </Button>,
  246 + ],
  247 + }}
  248 + scroll={{
  249 + x: 1400,
  250 + }}
  251 + editable={{
  252 + type: 'multiple',
  253 + }}
  254 + columnsState={{
  255 + persistenceKey: 'pro-table-singe-demos',
  256 + persistenceType: 'localStorage',
  257 + defaultValue: {
  258 + option: { fixed: 'right', disable: true },
  259 + },
  260 + onChange(value) {
  261 + console.log('value: ', value);
  262 + },
  263 + }}
  264 + rowKey="id"
  265 + options={{
  266 + setting: {
  267 + listsHeight: 400,
  268 + },
  269 + }}
  270 + form={{
  271 + // 由于配置了 transform,提交的参与与定义的不同这里需要转化一下
  272 + syncToUrl: (values, type) => {
  273 + if (type === 'get') {
  274 + return {
  275 + ...values,
  276 + created_at: [values.startTime, values.endTime],
  277 + };
  278 + }
  279 + return values;
  280 + },
  281 + }}
  282 + pagination={{
  283 + pageSize: 5,
  284 + onChange: (page) => console.log(page),
  285 + }}
  286 + dateFormatter="string"
  287 + headerTitle="高级表格"
  288 + toolBarRender={() => [
  289 + <ClientDrawer optType={'add'} key="button"></ClientDrawer>,
  290 + <ClientImportModal key="import" />,
  291 + ]}
  292 + />
  293 + {contextHolder}
  294 + </>
242 295 );
243 296 };
... ...
src/services/request.ts
... ... @@ -56,6 +56,7 @@ import type {
56 56 MaterialUnitListRes,
57 57 MeasureUnitListRes,
58 58 MessageQueryDTO,
  59 + ModelAndView,
59 60 OrderAddVO,
60 61 OrderAuditLogQueryVO,
61 62 OrderBaseInfoQueryVO,
... ... @@ -1910,78 +1911,6 @@ export const postDistrictAddOrderAndProvince = /* #__PURE__ */ (() =&gt; {
1910 1911 return request;
1911 1912 })();
1912 1913  
1913   -/** @description request parameter type for postDistrictFindProvinceAndCityAndDistrict */
1914   -export interface PostDistrictFindProvinceAndCityAndDistrictOption {
1915   - /**
1916   - * @description
1917   - * str
1918   - */
1919   - body: {
1920   - /**
1921   - @description
1922   - str */
1923   - str: string;
1924   - };
1925   -}
1926   -
1927   -/** @description response type for postDistrictFindProvinceAndCityAndDistrict */
1928   -export interface PostDistrictFindProvinceAndCityAndDistrictResponse {
1929   - /**
1930   - * @description
1931   - * OK
1932   - */
1933   - 200: ServerResult;
1934   - /**
1935   - * @description
1936   - * Created
1937   - */
1938   - 201: any;
1939   - /**
1940   - * @description
1941   - * Unauthorized
1942   - */
1943   - 401: any;
1944   - /**
1945   - * @description
1946   - * Forbidden
1947   - */
1948   - 403: any;
1949   - /**
1950   - * @description
1951   - * Not Found
1952   - */
1953   - 404: any;
1954   -}
1955   -
1956   -export type PostDistrictFindProvinceAndCityAndDistrictResponseSuccess =
1957   - PostDistrictFindProvinceAndCityAndDistrictResponse[200];
1958   -/**
1959   - * @description
1960   - * 读取字符串中的数据
1961   - * @tags order-district-controller
1962   - * @produces *
1963   - * @consumes application/json
1964   - */
1965   -export const postDistrictFindProvinceAndCityAndDistrict =
1966   - /* #__PURE__ */ (() => {
1967   - const method = 'post';
1968   - const url = '/district/findProvinceAndCityAndDistrict';
1969   - function request(
1970   - option: PostDistrictFindProvinceAndCityAndDistrictOption,
1971   - ): Promise<PostDistrictFindProvinceAndCityAndDistrictResponseSuccess> {
1972   - return requester(request.url, {
1973   - method: request.method,
1974   - ...option,
1975   - }) as unknown as Promise<PostDistrictFindProvinceAndCityAndDistrictResponseSuccess>;
1976   - }
1977   -
1978   - /** http method */
1979   - request.method = method;
1980   - /** request url */
1981   - request.url = url;
1982   - return request;
1983   - })();
1984   -
1985 1914 /** @description request parameter type for postDistrictSelOrderProvince */
1986 1915 export interface PostDistrictSelOrderProvinceOption {
1987 1916 /**
... ... @@ -2462,9 +2391,7 @@ export interface GetErrorResponse {
2462 2391 * @description
2463 2392 * OK
2464 2393 */
2465   - 200: {
2466   - [propertyName: string]: any;
2467   - };
  2394 + 200: ModelAndView;
2468 2395 /**
2469 2396 * @description
2470 2397 * Unauthorized
... ... @@ -2485,9 +2412,9 @@ export interface GetErrorResponse {
2485 2412 export type GetErrorResponseSuccess = GetErrorResponse[200];
2486 2413 /**
2487 2414 * @description
2488   - * error
  2415 + * errorHtml
2489 2416 * @tags basic-error-controller
2490   - * @produces *
  2417 + * @produces text/html
2491 2418 */
2492 2419 export const getError = /* #__PURE__ */ (() => {
2493 2420 const method = 'get';
... ... @@ -2511,9 +2438,7 @@ export interface PutErrorResponse {
2511 2438 * @description
2512 2439 * OK
2513 2440 */
2514   - 200: {
2515   - [propertyName: string]: any;
2516   - };
  2441 + 200: ModelAndView;
2517 2442 /**
2518 2443 * @description
2519 2444 * Created
... ... @@ -2539,9 +2464,9 @@ export interface PutErrorResponse {
2539 2464 export type PutErrorResponseSuccess = PutErrorResponse[200];
2540 2465 /**
2541 2466 * @description
2542   - * error
  2467 + * errorHtml
2543 2468 * @tags basic-error-controller
2544   - * @produces *
  2469 + * @produces text/html
2545 2470 * @consumes application/json
2546 2471 */
2547 2472 export const putError = /* #__PURE__ */ (() => {
... ... @@ -2566,9 +2491,7 @@ export interface PostErrorResponse {
2566 2491 * @description
2567 2492 * OK
2568 2493 */
2569   - 200: {
2570   - [propertyName: string]: any;
2571   - };
  2494 + 200: ModelAndView;
2572 2495 /**
2573 2496 * @description
2574 2497 * Created
... ... @@ -2594,9 +2517,9 @@ export interface PostErrorResponse {
2594 2517 export type PostErrorResponseSuccess = PostErrorResponse[200];
2595 2518 /**
2596 2519 * @description
2597   - * error
  2520 + * errorHtml
2598 2521 * @tags basic-error-controller
2599   - * @produces *
  2522 + * @produces text/html
2600 2523 * @consumes application/json
2601 2524 */
2602 2525 export const postError = /* #__PURE__ */ (() => {
... ... @@ -2621,9 +2544,7 @@ export interface DeleteErrorResponse {
2621 2544 * @description
2622 2545 * OK
2623 2546 */
2624   - 200: {
2625   - [propertyName: string]: any;
2626   - };
  2547 + 200: ModelAndView;
2627 2548 /**
2628 2549 * @description
2629 2550 * No Content
... ... @@ -2644,9 +2565,9 @@ export interface DeleteErrorResponse {
2644 2565 export type DeleteErrorResponseSuccess = DeleteErrorResponse[200];
2645 2566 /**
2646 2567 * @description
2647   - * error
  2568 + * errorHtml
2648 2569 * @tags basic-error-controller
2649   - * @produces *
  2570 + * @produces text/html
2650 2571 */
2651 2572 export const deleteError = /* #__PURE__ */ (() => {
2652 2573 const method = 'delete';
... ... @@ -2670,9 +2591,7 @@ export interface OptionsErrorResponse {
2670 2591 * @description
2671 2592 * OK
2672 2593 */
2673   - 200: {
2674   - [propertyName: string]: any;
2675   - };
  2594 + 200: ModelAndView;
2676 2595 /**
2677 2596 * @description
2678 2597 * No Content
... ... @@ -2693,9 +2612,9 @@ export interface OptionsErrorResponse {
2693 2612 export type OptionsErrorResponseSuccess = OptionsErrorResponse[200];
2694 2613 /**
2695 2614 * @description
2696   - * error
  2615 + * errorHtml
2697 2616 * @tags basic-error-controller
2698   - * @produces *
  2617 + * @produces text/html
2699 2618 * @consumes application/json
2700 2619 */
2701 2620 export const optionsError = /* #__PURE__ */ (() => {
... ... @@ -2720,9 +2639,7 @@ export interface HeadErrorResponse {
2720 2639 * @description
2721 2640 * OK
2722 2641 */
2723   - 200: {
2724   - [propertyName: string]: any;
2725   - };
  2642 + 200: ModelAndView;
2726 2643 /**
2727 2644 * @description
2728 2645 * No Content
... ... @@ -2743,9 +2660,9 @@ export interface HeadErrorResponse {
2743 2660 export type HeadErrorResponseSuccess = HeadErrorResponse[200];
2744 2661 /**
2745 2662 * @description
2746   - * error
  2663 + * errorHtml
2747 2664 * @tags basic-error-controller
2748   - * @produces *
  2665 + * @produces text/html
2749 2666 * @consumes application/json
2750 2667 */
2751 2668 export const headError = /* #__PURE__ */ (() => {
... ... @@ -2770,9 +2687,7 @@ export interface PatchErrorResponse {
2770 2687 * @description
2771 2688 * OK
2772 2689 */
2773   - 200: {
2774   - [propertyName: string]: any;
2775   - };
  2690 + 200: ModelAndView;
2776 2691 /**
2777 2692 * @description
2778 2693 * No Content
... ... @@ -2793,9 +2708,9 @@ export interface PatchErrorResponse {
2793 2708 export type PatchErrorResponseSuccess = PatchErrorResponse[200];
2794 2709 /**
2795 2710 * @description
2796   - * error
  2711 + * errorHtml
2797 2712 * @tags basic-error-controller
2798   - * @produces *
  2713 + * @produces text/html
2799 2714 * @consumes application/json
2800 2715 */
2801 2716 export const patchError = /* #__PURE__ */ (() => {
... ... @@ -8025,78 +7940,6 @@ export const postOrderErpOrderZoNingSelectSaleUserByProvince =
8025 7940 return request;
8026 7941 })();
8027 7942  
8028   -/** @description request parameter type for postOrderErpOrderZoNingSelectSaleUserHasZoning */
8029   -export interface PostOrderErpOrderZoNingSelectSaleUserHasZoningOption {
8030   - /**
8031   - * @description
8032   - * id
8033   - */
8034   - body: {
8035   - /**
8036   - @description
8037   - id */
8038   - id: number;
8039   - };
8040   -}
8041   -
8042   -/** @description response type for postOrderErpOrderZoNingSelectSaleUserHasZoning */
8043   -export interface PostOrderErpOrderZoNingSelectSaleUserHasZoningResponse {
8044   - /**
8045   - * @description
8046   - * OK
8047   - */
8048   - 200: ServerResult;
8049   - /**
8050   - * @description
8051   - * Created
8052   - */
8053   - 201: any;
8054   - /**
8055   - * @description
8056   - * Unauthorized
8057   - */
8058   - 401: any;
8059   - /**
8060   - * @description
8061   - * Forbidden
8062   - */
8063   - 403: any;
8064   - /**
8065   - * @description
8066   - * Not Found
8067   - */
8068   - 404: any;
8069   -}
8070   -
8071   -export type PostOrderErpOrderZoNingSelectSaleUserHasZoningResponseSuccess =
8072   - PostOrderErpOrderZoNingSelectSaleUserHasZoningResponse[200];
8073   -/**
8074   - * @description
8075   - * 查询出该销售所在的大区
8076   - * @tags order-zo-ning-controller
8077   - * @produces *
8078   - * @consumes application/json
8079   - */
8080   -export const postOrderErpOrderZoNingSelectSaleUserHasZoning =
8081   - /* #__PURE__ */ (() => {
8082   - const method = 'post';
8083   - const url = '/order/erp/orderZoNing/selectSaleUserHasZoning';
8084   - function request(
8085   - option: PostOrderErpOrderZoNingSelectSaleUserHasZoningOption,
8086   - ): Promise<PostOrderErpOrderZoNingSelectSaleUserHasZoningResponseSuccess> {
8087   - return requester(request.url, {
8088   - method: request.method,
8089   - ...option,
8090   - }) as unknown as Promise<PostOrderErpOrderZoNingSelectSaleUserHasZoningResponseSuccess>;
8091   - }
8092   -
8093   - /** http method */
8094   - request.method = method;
8095   - /** request url */
8096   - request.url = url;
8097   - return request;
8098   - })();
8099   -
8100 7943 /** @description response type for getOrderErpOrderZoNingSelectUserAll */
8101 7944 export interface GetOrderErpOrderZoNingSelectUserAllResponse {
8102 7945 /**
... ...