Commit 2a14661064c5dc14290fa1846708c33a1a1e2b52

Authored by 杜志良
2 parents 7ac9515f 32033702

Merge branch 'master' into d-cas

Showing 77 changed files with 3011 additions and 486 deletions
config/routers/order3.ts
... ... @@ -316,6 +316,21 @@ export default [
316 316 component: './order3/BuyCarLoanCost/subpages/AddConfig',
317 317 },
318 318 {
  319 + // 车贷银行返利和提成配置
  320 + path: '/order3/rebateCommission',
  321 + component: './order3/RebateCommission',
  322 + },
  323 + {
  324 + // 车贷银行返利和提成分期条件列表
  325 + path: '/order3/rebateCommission/loanCondition',
  326 + component: './order3/RebateCommission/subpages/LoanCondition',
  327 + },
  328 + {
  329 + // 车贷银行返利和提成分期条件新增
  330 + path: '/order3/rebateCommission/loanCondition/edit',
  331 + component: './order3/RebateCommission/subpages/LoanCondition/subpages/LoanConditionEdit',
  332 + },
  333 + {
319 334 // 直营策车厂家促销设置
320 335 path: '/order3/promotionSettings/directCarPromotion',
321 336 component: './order3/DirectCarPromotion',
... ...
config/routers/performance.ts
... ... @@ -99,6 +99,11 @@ export default [
99 99 path: '/morax/evaDataImport',
100 100 component: './performance/EvaDataImport',
101 101 },
  102 + /** 数据导入 */
  103 + {
  104 + path: '/morax/DataImport',
  105 + component: './performance/DataImport',
  106 + },
102 107 /** 考评数据导入==> 查看数据清单 */
103 108 {
104 109 path: '/morax/evaDataImport/edit/:id?/:dimensionType?',
... ...
src/components/ApprovalProgress/index.tsx
... ... @@ -43,41 +43,6 @@ export default function ApprovalProgress({ orderNo }: Props) {
43 43 setProgress({
44 44 loading: false,
45 45 data: res.data || [],
46   - // data: [
47   - // {
48   - // // 审批中倒计时
49   - // approveTime: 1709741004000,
50   - // approverName: 'Shinner',
51   - // approverId: 2144,
52   - // status: 1,
53   - // opinion: '测试理由',
54   - // efficientTime: 1709781004000,
55   - // createTime: 1709721004000,
56   - // roleNames: ['销售一级管理', '销售二级管理', '销售三级管理'],
57   - // },
58   - // {
59   - // // 审批中超时
60   - // approveTime: 1799722004000,
61   - // approverName: 'Shinner',
62   - // approverId: 2145,
63   - // status: 1,
64   - // opinion: '测试理由',
65   - // efficientTime: 1709721004000,
66   - // createTime: 1709721004000,
67   - // roleNames: ['销售一级管理', '销售二级管理', '销售三级管理'],
68   - // },
69   - // {
70   - // // 审批结束超时
71   - // approveTime: 1799722004000,
72   - // approverName: 'Shinner',
73   - // approverId: 2146,
74   - // status: 2,
75   - // opinion: '测试理由',
76   - // efficientTime: 1709781004000,
77   - // createTime: 1709721004000,
78   - // roleNames: ['销售一级管理', '销售二级管理', '销售三级管理'],
79   - // },
80   - // ],
81 46 });
82 47 })
83 48 .catch((error) => {
... ...
src/components/ShopSelectNew/components/ShopModal.tsx
... ... @@ -38,6 +38,7 @@ interface Props {
38 38 showBrand?: boolean; //表格中是否展示授权品牌,默认展示
39 39 showAfshop?: boolean; //表格中是否展示对应售后门店,默认展示
40 40 destroyOnClose?: boolean;
  41 + diyOptions?: Option;
41 42 }
42 43  
43 44 export default function ShopModal({
... ... @@ -59,6 +60,7 @@ export default function ShopModal({
59 60 showBrand = true,
60 61 showAfshop = true,
61 62 destroyOnClose = false,
  63 + diyOptions,
62 64 }: Props) {
63 65 const optionsStatus = useMemo(() => {
64 66 return {
... ... @@ -191,12 +193,12 @@ export default function ShopModal({
191 193 setSelected(
192 194 checked
193 195 ? listInitial.data
194   - .map((item) => ({
195   - ...item,
196   - value: item.shopId ?? -1,
197   - label: item.shopShortName,
198   - }))
199   - .filter((item) => (disabledShopIds.includes(item.shopId!) ? selected.find((s) => s.shopId === item.shopId) : true))
  196 + .map((item) => ({
  197 + ...item,
  198 + value: item.shopId ?? -1,
  199 + label: item.shopShortName,
  200 + }))
  201 + .filter((item) => (disabledShopIds.includes(item.shopId!) ? selected.find((s) => s.shopId === item.shopId) : true))
200 202 : selected.filter((s) => disabledShopIds.includes(s.shopId!)),
201 203 );
202 204 };
... ... @@ -260,7 +262,7 @@ export default function ShopModal({
260 262 <div style={{ width: 10 }} />
261 263 <FeeweeFilterOption title="业态">
262 264 <Select
263   - allowClear
  265 + allowClear={!diyOptions?.bizList?.length}
264 266 placeholder="请选择业态"
265 267 mode="multiple"
266 268 disabled={disabledOptions?.includes('bizTypes')}
... ... @@ -310,8 +312,8 @@ export default function ShopModal({
310 312 </>
311 313 )}
312 314 >
313   - {optionInitial.data.bizList?.map((biz) => (
314   - <Select.Option key={biz.type} value={'' + biz.type!} disabled={defaultOptions.bizTypes?.includes('' + biz.type)}>
  315 + {(diyOptions?.bizList ?? optionInitial.data.bizList)?.map((biz) => (
  316 + <Select.Option key={biz.type} value={'' + biz.type!} disabled={!diyOptions?.bizList?.length && defaultOptions.bizTypes?.includes('' + biz.type)}>
315 317 {biz.name}
316 318 </Select.Option>
317 319 ))}
... ... @@ -319,7 +321,7 @@ export default function ShopModal({
319 321 </FeeweeFilterOption>
320 322 <FeeweeFilterOption title="区域">
321 323 <Select
322   - allowClear
  324 + allowClear={!diyOptions?.regionList?.length}
323 325 placeholder="请选择区域"
324 326 mode="multiple"
325 327 disabled={disabledOptions?.includes('regions')}
... ... @@ -369,8 +371,8 @@ export default function ShopModal({
369 371 </>
370 372 )}
371 373 >
372   - {optionInitial.data.regionList?.map((region) => (
373   - <Select.Option key={region.bh} value={region.bh!} disabled={defaultOptions.regions?.includes('' + region.bh)}>
  374 + {(diyOptions?.regionList ?? optionInitial.data.regionList)?.map((region) => (
  375 + <Select.Option key={region.bh} value={region.bh!} disabled={!diyOptions?.regionList?.length && defaultOptions.regions?.includes('' + region.bh)}>
374 376 {region.fullName}
375 377 </Select.Option>
376 378 ))}
... ... @@ -378,7 +380,7 @@ export default function ShopModal({
378 380 </FeeweeFilterOption>
379 381 <FeeweeFilterOption title="品牌">
380 382 <Select
381   - allowClear
  383 + allowClear={!diyOptions?.brandList?.length}
382 384 placeholder="请选择品牌"
383 385 mode="multiple"
384 386 disabled={disabledOptions?.includes('brands')}
... ... @@ -428,8 +430,8 @@ export default function ShopModal({
428 430 </>
429 431 )}
430 432 >
431   - {optionInitial.data.brandList?.map((brand) => (
432   - <Select.Option key={brand.id} value={'' + brand.id!} disabled={defaultOptions.brands?.includes('' + brand.id)}>
  433 + {(diyOptions?.brandList ?? optionInitial.data.brandList)?.map((brand) => (
  434 + <Select.Option key={brand.id} value={'' + brand.id!} disabled={!diyOptions?.brandList?.length && defaultOptions.brands?.includes('' + brand.id)}>
433 435 {brand.name}
434 436 </Select.Option>
435 437 ))}
... ... @@ -437,7 +439,7 @@ export default function ShopModal({
437 439 </FeeweeFilterOption>
438 440 <FeeweeFilterOption title="商家">
439 441 <Select
440   - allowClear
  442 + allowClear={!diyOptions?.dealerList?.length}
441 443 placeholder="请选择商家"
442 444 mode="multiple"
443 445 disabled={disabledOptions?.includes('dealers')}
... ... @@ -487,8 +489,8 @@ export default function ShopModal({
487 489 </>
488 490 )}
489 491 >
490   - {optionInitial.data.dealerList?.map((dealer) => (
491   - <Select.Option key={dealer.id} value={'' + dealer.id!} disabled={defaultOptions.dealers?.includes('' + dealer.id)}>
  492 + {(diyOptions?.dealerList ?? optionInitial.data.dealerList)?.map((dealer) => (
  493 + <Select.Option key={dealer.id} value={'' + dealer.id!} disabled={!diyOptions?.dealerList?.length && defaultOptions.dealers?.includes('' + dealer.id)}>
492 494 {dealer.name}
493 495 </Select.Option>
494 496 ))}
... ...
src/components/ShopSelectNew/index.tsx
... ... @@ -9,7 +9,7 @@ import { isObjectChanged } from &#39;@/utils/validate&#39;;
9 9 import { Tag } from 'antd';
10 10 import type { Ref } from 'react';
11 11 import React, { forwardRef, memo, useEffect, useImperativeHandle, useState } from 'react';
12   -import type { QueryParams, ShopSelectNewOptions, Value } from './api';
  12 +import type { QueryParams, ShopSelectNewOptions, Value, Option } from './api';
13 13 import { getShopListApi, getShopListChooseOptionsApi } from './api';
14 14 import Close from './components/Close';
15 15 import ShopModal from './components/ShopModal';
... ... @@ -41,6 +41,7 @@ interface Props {
41 41 showBrand?: boolean;
42 42 showAfshop?: boolean;
43 43 destroyOnClose?: boolean;
  44 + diyOptions?: Option; //自定义筛选项
44 45 }
45 46  
46 47 export interface ShopSelectNewRef {
... ... @@ -66,6 +67,7 @@ function ShopSelectNew(
66 67 showBrand,
67 68 showAfshop,
68 69 destroyOnClose,
  70 + diyOptions,
69 71 }: Props,
70 72 ref: Ref<ShopSelectNewRef>,
71 73 ) {
... ... @@ -223,6 +225,7 @@ function ShopSelectNew(
223 225 showBrand={showBrand}
224 226 showAfshop={showAfshop}
225 227 destroyOnClose={destroyOnClose}
  228 + diyOptions={diyOptions}
226 229 />
227 230 )}
228 231 </div>
... ...
src/pages/Login/index.tsx
... ... @@ -29,7 +29,7 @@ let tiktakTimer: any = null;
29 29 // 轮询开始时间
30 30 let loopstartTime: any = null;
31 31 // 二维码超时时间
32   -const overDueTimeLimit: number = 1000 * 10;
  32 +const overDueTimeLimit: number = 1000 * 20;
33 33  
34 34 const LoginPage = () => {
35 35 const [qrStatusCode, setQrStatusCode] = useState<number>(-1);
... ...
src/pages/Pay/wxPayConfig/components/EditModal.tsx
... ... @@ -25,13 +25,13 @@ function EditModal({ visible, onCancel, handleOk, itemData, onRefresh, configLis
25 25  
26 26 useEffect(() => {
27 27 queryShop({ dealerId: itemData.dealerId });
28   - if (form) {
  28 + if (form && visible) {
29 29 const defaultList = itemData.shopInfos || [];
30 30 form.setFieldsValue({
31 31 shops: defaultList
32 32 });
33 33 }
34   - }, [itemData]);
  34 + }, [itemData, visible]);
35 35  
36 36 // 筛选出已经有关联的门店
37 37 const shopList = useMemo(() => {
... ...
src/pages/attendance/FieldService/subpages/WorkAddress/api.tsx
1   -import { http } from "@/typing/http";
2   -import request from "@/utils/request";
3   -import { ATTENDANCE_HOST,FVM_HOST } from "@/utils/host";
  1 +import type { http } from '@/typing/http';
  2 +import request from '@/utils/request';
  3 +import { ATTENDANCE_HOST, FVM_HOST } from '@/utils/host';
4 4  
5 5 type PromiseResp<T> = http.PromiseResp<T>;
6 6 type PromisePageResp<T> = http.PromisePageResp<T>;
... ... @@ -74,7 +74,7 @@ export interface AddParams {
74 74 addressType?: number; //地址类型
75 75 }
76 76  
77   -export interface Reason {
  77 +export interface Reason extends AddParams {
78 78 id?: number;
79 79 reason?: string; //理由
80 80 addressType?: number;
... ... @@ -103,7 +103,7 @@ export function reasonSaveOrUpdateApi(params: AddParams) {
103 103 * 删除理由
104 104 * /reason/setting/del
105 105 */
106   -export function reasonDeleteApi(params: { reasonId?: number,[key:string]:any }) {
  106 +export function reasonDeleteApi(params: { reasonId?: number; [key: string]: any }) {
107 107 return request.del(`${ATTENDANCE_HOST}/reason/setting/del`, { params });
108 108 }
109 109  
... ... @@ -132,7 +132,7 @@ export function GetAddressApi(params: AddressParams): PromisePageResp&lt;AddressIte
132 132  
133 133 export interface StorageParams {
134 134 brandId?: number;
135   - type?:number;
  135 + type?: number;
136 136 }
137 137 export interface StorageItems {
138 138 id?: number; //库房id
... ... @@ -165,10 +165,10 @@ export function reasonAddressDeleteApi(params: { addressId?: number; [key: strin
165 165 return request.del(`${ATTENDANCE_HOST}/reason/address/del`, { params });
166 166 }
167 167  
168   -interface OutsideTypeItem{
169   - outsideType?:number;//外勤类型值
170   - outsideTypeName?:string;//外勤类型名称
171   - tags?:string;
  168 +interface OutsideTypeItem {
  169 + outsideType?: number; //外勤类型值
  170 + outsideTypeName?: string; //外勤类型名称
  171 + tags?: string;
172 172 }
173 173 /**
174 174 * 获取系统配置的外勤类型列表
... ... @@ -177,4 +177,4 @@ interface OutsideTypeItem{
177 177  
178 178 export function getOutsideTypeListApi(): PromiseResp<OutsideTypeItem[]> {
179 179 return request.get(`${ATTENDANCE_HOST}/reason/setting/getOutsideTypeList`);
180   -}
181 180 \ No newline at end of file
  181 +}
... ...
src/pages/attendance/FieldService/subpages/WorkAddress/components/TripReasonModal.tsx
1   -import React, { useEffect, useState } from "react";
2   -import { Modal, Form, message, Select, Input } from "antd";
3   -import * as API from "../api";
4   -import { addrData } from "@/pages/attendance/TravelSetting/TravelAddress/entity";
5   -import { Reason, getOutsideTypeListApi } from "../api";
6   -import useInitial from "@/hooks/useInitail";
  1 +import React, { useEffect, useState } from 'react';
  2 +import { Modal, Form, message, Select, Input } from 'antd';
  3 +import * as API from '../api';
  4 +import { addrData } from '@/pages/attendance/TravelSetting/TravelAddress/entity';
  5 +import type { Reason} from '../api';
  6 +import { getOutsideTypeListApi } from '../api';
  7 +import useInitial from '@/hooks/useInitail';
7 8  
8 9 const FormItem = Form.Item;
9 10  
10 11 interface Props {
11 12 tripReasonModal: { visible: boolean; current?: Reason };
12   - onClose: (fresh?:boolean)=>void;
  13 + onClose: (fresh?: boolean, newData?: API.Reason) => void;
13 14 }
14 15  
15 16 export default function ModalIndex(props: Props) {
... ... @@ -29,14 +30,14 @@ export default function ModalIndex(props: Props) {
29 30 function submit(params: any) {
30 31 const { fieldType = {}, addressType } = params;
31 32 const res = data.find((item) => item.outsideType === fieldType.value);
32   - const _param = { ...res, type: 1, id: current.id, reason: res!.outsideTypeName, addressType };
  33 + const _param: API.AddParams = { ...res, type: 1, id: current.id, reason: res!.outsideTypeName, addressType };
33 34 setLoading(true);
34 35 API.reasonSaveOrUpdateApi(_param)
35 36 .then(() => {
36 37 setLoading(false);
37 38 form.resetFields();
38   - message.success("保存成功");
39   - onClose && onClose(true);
  39 + message.success('保存成功');
  40 + onClose && onClose(true, _param);
40 41 })
41 42 .catch((e) => {
42 43 setLoading(false);
... ... @@ -46,7 +47,7 @@ export default function ModalIndex(props: Props) {
46 47  
47 48 return (
48 49 <Modal
49   - title={`${current.id ? "编辑" : "新增"}外勤类型`}
  50 + title={`${current.id ? '编辑' : '新增'}外勤类型`}
50 51 open={visible}
51 52 onOk={form.submit}
52 53 onCancel={() => {
... ... @@ -59,14 +60,8 @@ export default function ModalIndex(props: Props) {
59 60 confirmLoading={loading}
60 61 >
61 62 <Form form={form} onFinish={submit} labelCol={{ span: 6 }} wrapperCol={{ span: 20 }}>
62   - <FormItem name="fieldType" label="外勤类型" rules={[{ required: true, message: "请输入外勤类型" }]}>
63   - <Select
64   - placeholder="请选择外勤类型"
65   - labelInValue
66   - showSearch
67   - optionFilterProp="children"
68   - disabled={!!current.id}
69   - >
  63 + <FormItem name="fieldType" label="外勤类型" rules={[{ required: true, message: '请输入外勤类型' }]}>
  64 + <Select placeholder="请选择外勤类型" labelInValue showSearch optionFilterProp="children" disabled={!!current.id}>
70 65 {data.map((item: any) => (
71 66 <Select.Option value={item.outsideType} key={item.outsideType}>
72 67 {item.outsideTypeName}
... ... @@ -74,7 +69,7 @@ export default function ModalIndex(props: Props) {
74 69 ))}
75 70 </Select>
76 71 </FormItem>
77   - <FormItem name="addressType" label="外勤地址类型" rules={[{ required: true, message: "请选择外勤地址类型" }]}>
  72 + <FormItem name="addressType" label="外勤地址类型" rules={[{ required: true, message: '请选择外勤地址类型' }]}>
78 73 <Select placeholder="请选择外勤地址类型">
79 74 {addrData.map((item: any) => (
80 75 <Select.Option value={item.id} key={item.id}>
... ...
src/pages/attendance/FieldService/subpages/WorkAddress/index.tsx
... ... @@ -95,10 +95,14 @@ function WorkKnowledgeBase() {
95 95 setReasonParams({ reason: e }, true);
96 96 }
97 97  
98   - function onClose(fresh?: boolean) {
  98 + function onClose(fresh?: boolean, newData?: API.Reason) {
99 99 setTripReasonModal({ visible: false, current: {} });
100 100 if (fresh) {
  101 + if (newData) {
  102 + setSelectedRows(newData);
  103 + }
101 104 setReasonParams({ ...reasonParams }, true);
  105 + setParams({}, true);
102 106 }
103 107 }
104 108  
... ...
src/pages/capital/ReceiveRules/component/AddAuthItems.tsx
... ... @@ -84,7 +84,20 @@ function CreateModal(props: Props) {
84 84 )}
85 85 {!postDimension ? (
86 86 <FormItem name="shopIds" label="适用门店" rules={[{ required: true }]}>
87   - <ShopSelectNew onChange={(v) => v && setShopIds(v.map((i) => Number(i.value)))} multiple={multiple} disabled={disabled} />
  87 + <ShopSelectNew
  88 + defaultOptions={{ bizTypes: '1,2,5' }}
  89 + diyOptions={{
  90 + bizList: [
  91 + { type: 1, name: '新车销售' },
  92 + { type: 2, name: '售后' },
  93 + { type: 5, name: '交付中心' },
  94 + ]
  95 + }}
  96 + onDefaultOptionsChangeFetch
  97 + onChange={(v) => v && setShopIds(v.map((i) => Number(i.value)))}
  98 + multiple={multiple}
  99 + disabled={disabled}
  100 + />
88 101 </FormItem>
89 102 ) : null}
90 103 {!postDimension ? (
... ...
src/pages/capital/ReceiveRules/subPages/GoodsDimension/components/PageAuthEdit/index.tsx
... ... @@ -169,6 +169,14 @@ export default function SpecConfig(props: Props) {
169 169 <Row justify="space-between" style={{ marginBottom: 10 }}>
170 170 <Row>
171 171 <ShopSelectNew
  172 + defaultOptions={{ bizTypes: '1,2,5' }}
  173 + diyOptions={{
  174 + bizList: [
  175 + { type: 1, name: '新车销售' },
  176 + { type: 2, name: '售后' },
  177 + { type: 5, name: '交付中心' },
  178 + ]
  179 + }}
172 180 value={innerParams.shopId ? [{ value: innerParams.shopId, label: innerParams.shopName }] : undefined}
173 181 style={{ width: 200, marginRight: 15 }}
174 182 onChange={(v: Value) => onFilter({ shopId: v && v[0] ? v[0].value : undefined, shopName: v && v[0] ? v[0].label : undefined })}
... ...
src/pages/capital/ReceiveRules/subPages/ShopsDimension/components/Filter.tsx
... ... @@ -23,7 +23,17 @@ const AdvancedSearchForm = ({ onFilter }: Props) =&gt; {
23 23 },
24 24 ]}
25 25 >
26   - <ShopSelectNew onChange={form.submit} />
  26 + <ShopSelectNew
  27 + defaultOptions={{ bizTypes: '1,2,5' }}
  28 + diyOptions={{
  29 + bizList: [
  30 + { type: 1, name: '新车销售' },
  31 + { type: 2, name: '售后' },
  32 + { type: 5, name: '交付中心' },
  33 + ],
  34 + }}
  35 + onChange={form.submit}
  36 + />
27 37 </Form.Item>
28 38 </Col>
29 39 <Col span={8}>
... ... @@ -41,10 +51,7 @@ const AdvancedSearchForm = ({ onFilter }: Props) =&gt; {
41 51 </Form.Item>
42 52 </Col>
43 53 <Col span={6}>
44   - <Form.Item
45   - name="keywords"
46   - label="搜索"
47   - >
  54 + <Form.Item name="keywords" label="搜索">
48 55 <Input allowClear placeholder="物品名称/规格型号" />
49 56 </Form.Item>
50 57 </Col>
... ... @@ -63,13 +70,10 @@ const AdvancedSearchForm = ({ onFilter }: Props) =&gt; {
63 70 };
64 71  
65 72 return (
66   - <Form
67   - form={form}
68   - name="advanced_search"
69   - className="ant-advanced-search-form"
70   - onFinish={onFinish}
71   - >
72   - <Row style={{ height: 45 }} gutter={24}>{getFields()}</Row>
  73 + <Form form={form} name="advanced_search" className="ant-advanced-search-form" onFinish={onFinish}>
  74 + <Row style={{ height: 45 }} gutter={24}>
  75 + {getFields()}
  76 + </Row>
73 77 {/* <Row>
74 78 <Col span={24} style={{ textAlign: 'right' }}>
75 79 <Button type="primary" htmlType="submit">
... ... @@ -87,4 +91,4 @@ const Filter: React.FC&lt;Props&gt; = (props) =&gt; (
87 91 </div>
88 92 );
89 93  
90   -export default Filter;
91 94 \ No newline at end of file
  95 +export default Filter;
... ...
src/pages/carinsur/insureSaleconfig/components/CreateModal.tsx
1 1 import React, { useState, useEffect } from 'react';
2 2 import '@ant-design/compatible/assets/index.css';
3   -import { Select, Modal, Form, message, InputNumber, Switch, Input } from 'antd';
  3 +import { Select, Modal, Form, message, InputNumber, Input, Radio } from 'antd';
4 4 import { getCompanyList, saveData } from '../api';
5 5  
6 6 const FormItem = Form.Item;
7 7 const Option = Select.Option;
  8 +const RadioGroup = Radio.Group;
8 9  
9 10 interface Props {
10 11 record: InsurRepair.listItem;
... ... @@ -108,13 +109,13 @@ export default function CreateModal({ onCancel, record, onRrfreshing, visible }:
108 109 <Input placeholder="出保占比" />
109 110 </Form.Item>
110 111 </Form.Item>
111   - <Form.Item
112   - name="controlMonthlySaleAmount"
113   - valuePropName="checked"
114   - rules={[{ required: true, message: '该选项为必填项' }]}
115   - label="是否控制月出保金额"
116   - >
117   - <Switch />
  112 + <Form.Item name="controlMonthlySaleAmount" rules={[{ required: true, message: '该选项为必填项' }]} label="是否控制月出保金额">
  113 + <RadioGroup
  114 + options={[
  115 + { value: true, label: '是' },
  116 + { value: false, label: '否' },
  117 + ]}
  118 + />
118 119 </Form.Item>
119 120 <FormItem
120 121 label="月出保金额浮动比例≤"
... ...
src/pages/cas/ClaimConfirmation/components/SpecialFeeConfirm.tsx
... ... @@ -227,6 +227,9 @@ export default function SpecialFeeDetail({ current, visible, setVisible, setLoad
227 227 <DescriptionItem label="故障描述" span={3}>
228 228 {(detail && detail.consultantDesc) || '--'}
229 229 </DescriptionItem>
  230 + <DescriptionItem label="特情原因" span={3}>
  231 + {(detail && detail.remark) || '--'}
  232 + </DescriptionItem>
230 233 <DescriptionItem label="附件" span={3}>
231 234 <ImageModal title="查看附件" fids={detail?.attachment ? detail?.attachment.split(',') : []} />
232 235 </DescriptionItem>
... ...
src/pages/cas/ClaimFiling/components/CustomerCareModal.tsx
... ... @@ -93,7 +93,7 @@ export default function CustomerCareModal({ current, visible, setVisible, setLoa
93 93 function submit(item: any) {
94 94 const params: any = {
95 95 claimId: detail?.claimId,
96   - claimNo: item.claimNo,
  96 + claimNo: item.claimNo?.trim(),
97 97 submitDealerId: item.submitDealer.value,
98 98 tradeCompId: item.tradeComp.value,
99 99 tradeCompName: item.tradeComp.label,
... ...
src/pages/cas/ClaimFiling/components/DetailModal.tsx
... ... @@ -241,7 +241,7 @@ export default function DetailMOdal({ current, visible, setVisible, setLoading:
241 241 function submit(item: any) {
242 242 const params = {
243 243 claimId: detail?.claimId,
244   - claimNo: item.claimNo,
  244 + claimNo: item.claimNo?.trim(),
245 245 submitDealerId: item.submitDealer.value,
246 246 tradeCompId: item.tradeComp.value,
247 247 tradeCompName: item.tradeComp.label,
... ...
src/pages/cas/ClaimFiling/components/SpecialFeeSubmit.tsx
... ... @@ -225,7 +225,7 @@ export default function DetailMOdal({ current, visible, setVisible, setLoading:
225 225  
226 226 function submit(item: any) {
227 227 const params = {
228   - claimNo: item.claimNo,
  228 + claimNo: item.claimNo?.trim(),
229 229 claimId: detail?.claimOrderId,
230 230 submitDealerId: item.submitDealer.value,
231 231 tradeCompId: item.tradeComp.value,
... ...
src/pages/cas/ClaimFiling/subpages/OldPart/components/EditModal.tsx
... ... @@ -112,7 +112,7 @@ export default function EditModal({ current, visible, onClose, onSuccess }: Prop
112 112 submitDealerName: values.submitDealer.label,
113 113 settlementUnit: values.settlementUnit.value,
114 114 settlementUnitName: values.settlementUnit.label,
115   - claimNo: values.claimNo,
  115 + claimNo: values.claimNo?.trim(),
116 116 appendix: fids.join(','),
117 117 };
118 118  
... ...
src/pages/coupon/CouponConfig/components/FullReduce.tsx
... ... @@ -43,6 +43,7 @@ export default function FullReduce({ info, readonly, form, getCouponType, confNo
43 43 form.setFieldsValue({
44 44 aliasName: currentItems.type == 1 ? currentItems.name : undefined,
45 45 settlementType: 1,
  46 + superimposed: false,
46 47 settlementCalType: v.value === 'xjdhq' ? 1 : undefined,
47 48 });
48 49 getCouponType && getCouponType(currentItems.type);
... ... @@ -153,8 +154,11 @@ export default function FullReduce({ info, readonly, form, getCouponType, confNo
153 154 </Form.Item>
154 155 )}
155 156 {/* 全部默认不叠加false,立减券允许修改类型 */}
156   - <Form.Item label="可与其他同类型优惠券叠加使用" name="superimposed" valuePropName="checked">
157   - <Switch checkedChildren="是" unCheckedChildren="否" disabled={readonly || info.discountsType != 1} />
  157 + <Form.Item label="可与其他同类型优惠券叠加使用" name="superimposed" >
  158 + <Radio.Group disabled={readonly || !!confNo || info.discountsType != 1}>
  159 + <Radio key={1} value={true}>可叠加</Radio>
  160 + <Radio key={0} value={false}>不可叠加</Radio>
  161 + </Radio.Group>
158 162 </Form.Item>
159 163 <Form.Item label="优惠券券面金额" name="amount" rules={[{ required: true, message: '请输入优惠券销售价格' }]}>
160 164 <InputNumber disabled={readonly || !!confNo} style={{ width: '100%' }} placeholder="请输入" />
... ...
src/pages/ehr/GroupMobileRecord/api.ts
... ... @@ -3,14 +3,12 @@
3 3 * @LastEditors: wangqiang@feewee.cn
4 4 * @LastEditTime: 2023-02-24 09:17:40
5 5 */
6   -import request from "@/utils/request";
7   -import { ARCHIVES_HOST, EHR_HOST, FINANCE2_HOST } from "@/utils/host";
8   -import { http } from "@/typing/http";
  6 +import request from '@/utils/request';
  7 +import { ARCHIVES_HOST, EHR_HOST, FINANCE2_HOST } from '@/utils/host';
  8 +import type { http } from '@/typing/http';
9 9  
10 10 /** 获取手机号列表 */
11   -export function getMobileListApi(
12   - params: GroupMobileRecord.QueryParams
13   -): http.PromisePageResp<GroupMobileRecord.List> {
  11 +export function getMobileListApi(params: GroupMobileRecord.QueryParams): http.PromisePageResp<GroupMobileRecord.List> {
14 12 return request.get(`${ARCHIVES_HOST}/mobile/list`, { params });
15 13 }
16 14  
... ... @@ -78,3 +76,12 @@ export function enableGroupMobileApi(id: number): http.PromiseResp&lt;string&gt; {
78 76 params: { id },
79 77 });
80 78 }
  79 +
  80 +/**
  81 + * @description: 发放集团手机号
  82 + * @param {GroupMobileRecord.ProvideParams} params
  83 + * @return {http.PromiseResp<string>}
  84 + */
  85 +export function provideGroupMobileApi(params: GroupMobileRecord.ProvideParams): http.PromiseResp<string> {
  86 + return request.get(`${ARCHIVES_HOST}/mobile/provide`, { params });
  87 +}
... ...
src/pages/ehr/GroupMobileRecord/components/GroupMobileProvideModal.tsx 0 → 100644
  1 +import React, { useEffect, useState } from 'react';
  2 +import { useStore } from '../index';
  3 +import { Form, Modal, message } from 'antd';
  4 +import MemberSelectNew from '@/components/MemberSelectNew';
  5 +import { provideGroupMobileApi } from '@/pages/ehr/GroupMobileRecord/api';
  6 +
  7 +export default function GroupMobileProvideModal() {
  8 + const { currentItem, setCurrentItem, provideMobileOpen, setProvideMobileOpen, pagination } = useStore();
  9 + const [form] = Form.useForm();
  10 + const [confirmLoading, setConfirmLoading] = useState(false);
  11 +
  12 + useEffect(() => {
  13 + if (!provideMobileOpen) {
  14 + form.resetFields();
  15 + setCurrentItem(undefined);
  16 + }
  17 + }, [provideMobileOpen]);
  18 +
  19 + function onOk(val: any) {
  20 + const params = {
  21 + id: currentItem?.id,
  22 + staffId: val.staff?.[0]?.value,
  23 + };
  24 + console.log(params);
  25 + setConfirmLoading(true);
  26 + const hide = message.loading('保存发放中...', 0);
  27 + provideGroupMobileApi(params)
  28 + .then((res) => {
  29 + hide();
  30 + setConfirmLoading(false);
  31 + message.success(res.result);
  32 + pagination.setLoading(true);
  33 + setProvideMobileOpen(false);
  34 + })
  35 + .catch((error) => {
  36 + hide();
  37 + setConfirmLoading(false);
  38 + message.error(error.message ?? '保存发放失败');
  39 + });
  40 + }
  41 +
  42 + return (
  43 + <Modal
  44 + title="发放集团手机号"
  45 + open={provideMobileOpen}
  46 + maskClosable={false}
  47 + onOk={form.submit}
  48 + onCancel={() => setProvideMobileOpen(false)}
  49 + confirmLoading={confirmLoading}
  50 + >
  51 + <Form form={form} onFinish={onOk}>
  52 + <Form.Item label="集团手机号">
  53 + <span>{currentItem?.mobile ?? '-'}</span>
  54 + </Form.Item>
  55 + <Form.Item name="staff" label="发放人员" rules={[{ required: true, message: '请选择发放集团手机号人员' }]}>
  56 + <MemberSelectNew />
  57 + </Form.Item>
  58 + </Form>
  59 + </Modal>
  60 + );
  61 +}
... ...
src/pages/ehr/GroupMobileRecord/components/GroupMobileRecordList.tsx
... ... @@ -3,20 +3,11 @@
3 3 * @LastEditors: wangqiang@feewee.cn
4 4 * @LastEditTime: 2023-05-11 14:06:10
5 5 */
6   -import React, { useState } from "react";
7   -import {
8   - Table,
9   - Divider,
10   - Popconfirm,
11   - message,
12   - Popover,
13   - Badge,
14   - Modal,
15   - Button,
16   -} from "antd";
17   -import { CheckCircleFilled, CloseCircleFilled } from "@ant-design/icons";
18   -import { useStore } from "../index";
19   -import { deleteMobileApi } from "../api";
  6 +import React, { useState } from 'react';
  7 +import { Table, Divider, Popconfirm, message, Popover, Badge, Modal, Button } from 'antd';
  8 +import { CheckCircleFilled, CloseCircleFilled } from '@ant-design/icons';
  9 +import { useStore } from '../index';
  10 +import { deleteMobileApi } from '../api';
20 11 import { formatNumber } from '@/utils/utils';
21 12  
22 13 const { Column } = Table;
... ... @@ -32,28 +23,22 @@ export default function GroupMobileRecordList() {
32 23 setEditSubject,
33 24 disableGroupMobile,
34 25 enableGroupMobile,
  26 + setProvideMobileOpen,
35 27 } = useStore();
36   - const {
37   - list,
38   - loading,
39   - setLoading,
40   - innerParams,
41   - setParams,
42   - paginationConfig,
43   - } = pagination;
  28 + const { list, loading, setLoading, innerParams, setParams, paginationConfig } = pagination;
44 29 const [modal, setModal] = useState<{
45 30 visible: boolean;
46   - type?: "post" | "shop";
  31 + type?: 'post' | 'shop';
47 32 }>({
48 33 visible: false,
49 34 });
50 35  
51 36 const deleteMobile = (id: number) => {
52   - const hide = message.loading("删除中,请稍后...", 0);
  37 + const hide = message.loading('删除中,请稍后...', 0);
53 38 deleteMobileApi(id)
54 39 .then((res) => {
55 40 hide();
56   - message.success("删除成功");
  41 + message.success('删除成功');
57 42 if (list.length === 1 && (innerParams.current || 1) > 1) {
58 43 setParams({ current: innerParams.current! - 1 }, true);
59 44 } else {
... ... @@ -66,15 +51,14 @@ export default function GroupMobileRecordList() {
66 51 });
67 52 };
68 53  
  54 + function provideMobile(record: GroupMobileRecord.List) {
  55 + setCurrentItem(record);
  56 + setProvideMobileOpen(true);
  57 + }
  58 +
69 59 return (
70 60 <>
71   - <Table
72   - dataSource={list}
73   - rowKey="id"
74   - loading={loading}
75   - pagination={paginationConfig}
76   - className="FWStickyTableHeader"
77   - >
  61 + <Table dataSource={list} rowKey="id" loading={loading} pagination={paginationConfig} className="FWStickyTableHeader">
78 62 <Column
79 63 title="手机号信息"
80 64 align="left"
... ... @@ -85,178 +69,142 @@ export default function GroupMobileRecordList() {
85 69 content={
86 70 <span>
87 71 <span>
88   - <span style={{ color: "#999" }}>手机号:</span>
89   - <span style={{ color: "#333" }}>
90   - {record.mobile || "-"}
91   - </span>
  72 + <span style={{ color: '#999' }}>手机号:</span>
  73 + <span style={{ color: '#333' }}>{record.mobile || '-'}</span>
92 74 <Divider type="vertical" />
93   - <span style={{ color: "#999" }}>
  75 + <span style={{ color: '#999' }}>
94 76 录音:
95   - {record.recording ? (
96   - <CheckCircleFilled style={{ color: "#52c41a" }} />
97   - ) : (
98   - <CloseCircleFilled style={{ color: "#ff4d4f" }} />
99   - )}
  77 + {record.recording ? <CheckCircleFilled style={{ color: '#52c41a' }} /> : <CloseCircleFilled style={{ color: '#ff4d4f' }} />}
100 78 </span>
101 79 <Divider type="vertical" />
102   - <span style={{ color: "#999" }}>
  80 + <span style={{ color: '#999' }}>
103 81 云转写:
104 82 <span>
105 83 {record.recordingTranslate ? (
106   - <CheckCircleFilled style={{ color: "#52c41a" }} />
  84 + <CheckCircleFilled style={{ color: '#52c41a' }} />
107 85 ) : (
108   - <CloseCircleFilled style={{ color: "#ff4d4f" }} />
  86 + <CloseCircleFilled style={{ color: '#ff4d4f' }} />
109 87 )}
110 88 </span>
111 89 </span>
112 90 <Divider type="vertical" />
113   - <span style={{ color: "#999" }}>
  91 + <span style={{ color: '#999' }}>
114 92 云名片:
115 93 <span>
116 94 {record.businessCard ? (
117   - <CheckCircleFilled style={{ color: "#52c41a" }} />
  95 + <CheckCircleFilled style={{ color: '#52c41a' }} />
118 96 ) : (
119   - <CloseCircleFilled style={{ color: "#ff4d4f" }} />
  97 + <CloseCircleFilled style={{ color: '#ff4d4f' }} />
120 98 )}
121 99 </span>
122 100 </span>
123 101 <Divider type="vertical" />
124   - <span style={{ color: "#999" }}>
  102 + <span style={{ color: '#999' }}>
125 103 最低月租:
126 104 <span>
127   - {record.monthRent ? (
128   - <CheckCircleFilled style={{ color: "#52c41a" }} />
129   - ) : (
130   - <CloseCircleFilled style={{ color: "#ff4d4f" }} />
131   - )}
  105 + {record.monthRent ? <CheckCircleFilled style={{ color: '#52c41a' }} /> : <CloseCircleFilled style={{ color: '#ff4d4f' }} />}
132 106 </span>
133 107 </span>
134 108 </span>
135 109 <br />
136 110 <span>
137   - <span style={{ color: "#999" }}>当前保管门店:</span>
138   - <span style={{ color: "#333" }}>
139   - {record.custodyShopName || "-"}
140   - </span>
  111 + <span style={{ color: '#999' }}>当前保管门店:</span>
  112 + <span style={{ color: '#333' }}>{record.custodyShopName || '-'}</span>
141 113 </span>
142 114 <br />
143 115 <span>
144   - <span style={{ color: "#999" }}>往来单位:</span>
145   - <span style={{ color: "#333" }}>
146   - {record.subjectName || "-"}
147   - </span>
  116 + <span style={{ color: '#999' }}>往来单位:</span>
  117 + <span style={{ color: '#333' }}>{record.subjectName || '-'}</span>
148 118 </span>
149 119 </span>
150 120 }
151 121 >
152 122 <span>
153 123 <span className="span_limit_1">
154   - <span style={{ color: "#999" }}>手机号:</span>
155   - <span style={{ color: "#333" }}>{record.mobile || "-"}</span>
  124 + <span style={{ color: '#999' }}>手机号:</span>
  125 + <span style={{ color: '#333' }}>{record.mobile || '-'}</span>
156 126 <Divider type="vertical" />
157   - <span style={{ color: "#999" }}>
  127 + <span style={{ color: '#999' }}>
158 128 录音:
159   - {record.recording ? (
160   - <CheckCircleFilled style={{ color: "#52c41a" }} />
161   - ) : (
162   - <CloseCircleFilled style={{ color: "#ff4d4f" }} />
163   - )}
  129 + {record.recording ? <CheckCircleFilled style={{ color: '#52c41a' }} /> : <CloseCircleFilled style={{ color: '#ff4d4f' }} />}
164 130 </span>
165 131 <Divider type="vertical" />
166   - <span style={{ color: "#999" }}>
  132 + <span style={{ color: '#999' }}>
167 133 云转写:
168 134 <span>
169 135 {record.recordingTranslate ? (
170   - <CheckCircleFilled style={{ color: "#52c41a" }} />
  136 + <CheckCircleFilled style={{ color: '#52c41a' }} />
171 137 ) : (
172   - <CloseCircleFilled style={{ color: "#ff4d4f" }} />
  138 + <CloseCircleFilled style={{ color: '#ff4d4f' }} />
173 139 )}
174 140 </span>
175 141 </span>
176 142 <Divider type="vertical" />
177   - <span style={{ color: "#999" }}>
  143 + <span style={{ color: '#999' }}>
178 144 云名片:
179 145 <span>
180   - {record.businessCard ? (
181   - <CheckCircleFilled style={{ color: "#52c41a" }} />
182   - ) : (
183   - <CloseCircleFilled style={{ color: "#ff4d4f" }} />
184   - )}
  146 + {record.businessCard ? <CheckCircleFilled style={{ color: '#52c41a' }} /> : <CloseCircleFilled style={{ color: '#ff4d4f' }} />}
185 147 </span>
186 148 </span>
187 149 <Divider type="vertical" />
188   - <span style={{ color: "#999" }}>
  150 + <span style={{ color: '#999' }}>
189 151 最低月租:
190 152 <span>
191   - {record.monthRent ? (
192   - <CheckCircleFilled style={{ color: "#52c41a" }} />
193   - ) : (
194   - <CloseCircleFilled style={{ color: "#ff4d4f" }} />
195   - )}
  153 + {record.monthRent ? <CheckCircleFilled style={{ color: '#52c41a' }} /> : <CloseCircleFilled style={{ color: '#ff4d4f' }} />}
196 154 </span>
197 155 </span>
198 156 </span>
199 157 <span className="span_limit_1">
200   - <span style={{ color: "#999" }}>运营商:</span>
201   - <span style={{ color: "#333" }}>
202   - {record.operatorName || "-"}
203   - </span>
  158 + <span style={{ color: '#999' }}>运营商:</span>
  159 + <span style={{ color: '#333' }}>{record.operatorName || '-'}</span>
204 160 <Divider type="vertical" />
205   - <span style={{ color: "#999" }}>归属地:</span>
206   - <span style={{ color: "#333" }}>{record.address || "-"}</span>
  161 + <span style={{ color: '#999' }}>归属地:</span>
  162 + <span style={{ color: '#333' }}>{record.address || '-'}</span>
207 163 <Divider type="vertical" />
208   - <span style={{ color: "#999" }}>预充金额:</span>
209   - <span style={{ color: "#333" }}>
210   - {record.preChargeAmount
211   - ? formatNumber(record.preChargeAmount)
212   - : "-"}
213   - </span>
  164 + <span style={{ color: '#999' }}>预充金额:</span>
  165 + <span style={{ color: '#333' }}>{record.preChargeAmount ? formatNumber(record.preChargeAmount) : '-'}</span>
214 166 </span>
215 167 <span className="span_limit_1">
216   - <span style={{ color: "#999" }}>归属门店:</span>
217   - <span style={{ color: "#333" }}>
  168 + <span style={{ color: '#999' }}>归属门店:</span>
  169 + <span style={{ color: '#333' }}>
218 170 {(record?.shopList || []).length > 0 ? (
219 171 <a
220 172 onClick={() => {
221 173 setCurrentItem(record);
222   - setModal({ visible: true, type: "shop" });
  174 + setModal({ visible: true, type: 'shop' });
223 175 }}
224 176 >
225 177 查看
226 178 </a>
227 179 ) : (
228   - "-"
  180 + '-'
229 181 )}
230 182 </span>
231 183 <Divider type="vertical" />
232   - <span style={{ color: "#999" }}>适用岗位:</span>
233   - <span style={{ color: "#333" }}>
  184 + <span style={{ color: '#999' }}>适用岗位:</span>
  185 + <span style={{ color: '#333' }}>
234 186 {(record?.postList || []).length > 0 ? (
235 187 <a
236 188 onClick={() => {
237 189 setCurrentItem(record);
238   - setModal({ visible: true, type: "post" });
  190 + setModal({ visible: true, type: 'post' });
239 191 }}
240 192 >
241 193 查看
242 194 </a>
243 195 ) : (
244   - "-"
  196 + '-'
245 197 )}
246 198 </span>
247 199 </span>
248 200 <span className="span_limit_1">
249   - <span style={{ color: "#999" }}>当前保管门店:</span>
250   - <span style={{ color: "#333" }}>
251   - {record.custodyShopName || "-"}
252   - </span>
  201 + <span style={{ color: '#999' }}>当前保管门店:</span>
  202 + <span style={{ color: '#333' }}>{record.custodyShopName || '-'}</span>
253 203 </span>
254 204 <span className="span_limit_1">
255   - <span style={{ color: "#999" }}>往来单位:</span>
256   - <span style={{ color: "#333" }}>
257   - {record.subjectName || "-"}
258   - </span>
259   - {record.status === StatusText["空闲"] ? null : (
  205 + <span style={{ color: '#999' }}>往来单位:</span>
  206 + <span style={{ color: '#333' }}>{record.subjectName || '-'}</span>
  207 + {record.status === StatusText['空闲'] ? null : (
260 208 <>
261 209 &nbsp;&nbsp;
262 210 <a
... ... @@ -279,60 +227,50 @@ export default function GroupMobileRecordList() {
279 227 title="人员信息"
280 228 align="left"
281 229 width="30%"
282   - render={(record: GroupMobileRecord.List) => (record.staffName ? (
283   - <Popover
284   - placement="topLeft"
285   - content={
286   - <>
287   - <span>
  230 + render={(record: GroupMobileRecord.List) =>
  231 + record.staffName ? (
  232 + <Popover
  233 + placement="topLeft"
  234 + content={
  235 + <>
288 236 <span>
289   - <span style={{ color: "#999" }}>员工姓名:</span>
290   - <span style={{ color: "#333" }}>
291   - {record.staffName || "-"}
  237 + <span>
  238 + <span style={{ color: '#999' }}>员工姓名:</span>
  239 + <span style={{ color: '#333' }}>{record.staffName || '-'}</span>
292 240 </span>
293   - </span>
294   - <br />
295   - <span>
296   - <span style={{ color: "#999" }}>在职门店:</span>
297   - <span style={{ color: "#333" }}>
298   - {record.staffShopName || "-"}
  241 + <br />
  242 + <span>
  243 + <span style={{ color: '#999' }}>在职门店:</span>
  244 + <span style={{ color: '#333' }}>{record.staffShopName || '-'}</span>
299 245 </span>
300   - </span>
301   - <br />
302   - <span>
303   - <span style={{ color: "#999" }}>人员岗位:</span>
304   - <span style={{ color: "#333" }}>
305   - {record.staffPostName || "-"}
  246 + <br />
  247 + <span>
  248 + <span style={{ color: '#999' }}>人员岗位:</span>
  249 + <span style={{ color: '#333' }}>{record.staffPostName || '-'}</span>
306 250 </span>
307 251 </span>
308   - </span>
309   - </>
  252 + </>
310 253 }
311   - >
312   - <span>
313   - <span className="span_limit_1">
314   - <span style={{ color: "#999" }}>员工姓名:</span>
315   - <span style={{ color: "#333" }}>
316   - {record.staffName || "-"}
  254 + >
  255 + <span>
  256 + <span className="span_limit_1">
  257 + <span style={{ color: '#999' }}>员工姓名:</span>
  258 + <span style={{ color: '#333' }}>{record.staffName || '-'}</span>
317 259 </span>
318   - </span>
319   - <span className="span_limit_1">
320   - <span style={{ color: "#999" }}>在职门店:</span>
321   - <span style={{ color: "#333" }}>
322   - {record.staffShopName || "-"}
  260 + <span className="span_limit_1">
  261 + <span style={{ color: '#999' }}>在职门店:</span>
  262 + <span style={{ color: '#333' }}>{record.staffShopName || '-'}</span>
323 263 </span>
324   - </span>
325   - <span className="span_limit_1">
326   - <span style={{ color: "#999" }}>人员岗位:</span>
327   - <span style={{ color: "#333" }}>
328   - {record.staffPostName || "-"}
  264 + <span className="span_limit_1">
  265 + <span style={{ color: '#999' }}>人员岗位:</span>
  266 + <span style={{ color: '#333' }}>{record.staffPostName || '-'}</span>
329 267 </span>
330 268 </span>
331   - </span>
332   - </Popover>
  269 + </Popover>
333 270 ) : (
334   - <span style={{ color: "#999" }}>暂未使用</span>
335   - ))}
  271 + <span style={{ color: '#999' }}>暂未使用</span>
  272 + )
  273 + }
336 274 />
337 275 <Column
338 276 title="状态"
... ... @@ -350,8 +288,7 @@ export default function GroupMobileRecordList() {
350 288 align="left"
351 289 render={(record: GroupMobileRecord.List) => (
352 290 <>
353   - {record.status !== StatusText.待停用 &&
354   - record.status !== StatusText.停用 ? (
  291 + {record.status !== StatusText.待停用 && record.status !== StatusText.停用 ? (
355 292 <>
356 293 <a
357 294 onClick={() => {
... ... @@ -362,21 +299,21 @@ export default function GroupMobileRecordList() {
362 299 编辑
363 300 </a>
364 301 <Divider type="vertical" />
365   - <Popconfirm
366   - title={`确定停用【${record.mobile}】`}
367   - onConfirm={() => disableGroupMobile(record.id!)}
368   - >
369   - <a style={{ color: "red" }}>停用</a>
  302 + <Popconfirm title={`确定停用【${record.mobile}】`} onConfirm={() => disableGroupMobile(record.id!)}>
  303 + <a style={{ color: 'red' }}>停用</a>
370 304 </Popconfirm>
371 305 </>
372 306 ) : (
373   - <Popconfirm
374   - title={`确定启用【${record.mobile}】`}
375   - onConfirm={() => enableGroupMobile(record.id!)}
376   - >
  307 + <Popconfirm title={`确定启用【${record.mobile}】`} onConfirm={() => enableGroupMobile(record.id!)}>
377 308 <a>启用</a>
378 309 </Popconfirm>
379 310 )}
  311 + {record.status === StatusText.空闲 ? (
  312 + <>
  313 + <Divider type="vertical" />
  314 + <a onClick={() => provideMobile(record)}>发放</a>
  315 + </>
  316 + ) : null}
380 317 </>
381 318 )}
382 319 />
... ... @@ -395,7 +332,7 @@ export default function GroupMobileRecordList() {
395 332 }
396 333  
397 334 interface ShopModalProps {
398   - type?: "post" | "shop";
  335 + type?: 'post' | 'shop';
399 336 visible: boolean;
400 337 onCancel: () => void;
401 338 data?: GroupMobileRecord.List;
... ... @@ -404,9 +341,7 @@ interface ShopModalProps {
404 341 function ShopModal({ type, visible, onCancel, data }: ShopModalProps) {
405 342 return (
406 343 <Modal
407   - title={`${data?.mobile}-${
408   - type ? (type == "post" ? "适用岗位" : "归属门店") : ""
409   - }`}
  344 + title={`${data?.mobile}-${type ? (type == 'post' ? '适用岗位' : '归属门店') : ''}`}
410 345 visible={visible}
411 346 maskClosable={false}
412 347 onOk={onCancel}
... ... @@ -418,10 +353,8 @@ function ShopModal({ type, visible, onCancel, data }: ShopModalProps) {
418 353 ]}
419 354 // afterClose={() => setCurrentPostItem(undefined)}
420 355 >
421   - {type === "shop" &&
422   - data?.shopList?.map((shop) => <p key={shop.shopId}>{shop.shopName}</p>)}
423   - {type === "post" &&
424   - data?.postList?.map((post) => <p key={post.postId}>{post.postName}</p>)}
  356 + {type === 'shop' && data?.shopList?.map((shop) => <p key={shop.shopId}>{shop.shopName}</p>)}
  357 + {type === 'post' && data?.postList?.map((post) => <p key={post.postId}>{post.postName}</p>)}
425 358 </Modal>
426 359 );
427 360 }
... ...
src/pages/ehr/GroupMobileRecord/index.tsx
1 1 /*
2 2 * @Date: 2021-01-05 14:24:23
3 3 * @LastEditors: wangqiang@feewee.cn
4   - * @LastEditTime: 2023-09-02 16:16:24
  4 + * @LastEditTime: 2024-04-15 11:11:53
5 5 */
6 6 import React from 'react';
7 7 import { Card, Button, Row, Input, ConfigProvider, Select } from 'antd';
... ... @@ -10,6 +10,7 @@ import { PageHeaderWrapper } from &#39;@ant-design/pro-layout&#39;;
10 10 import { PlusOutlined } from '@ant-design/icons';
11 11 import List from './components/GroupMobileRecordList';
12 12 import Modal from './components/GroupMobileRecordModal';
  13 +import ProvideMobileModal from './components/GroupMobileProvideModal';
13 14 import { createStore } from '@/hooks/moz';
14 15 import store from './store';
15 16 import { debounce } from 'lodash';
... ... @@ -267,6 +268,7 @@ function GroupMobileRecord() {
267 268 <List />
268 269 </Card>
269 270 <Modal />
  271 + <ProvideMobileModal />
270 272 </ConfigProvider>
271 273 </PageHeaderWrapper>
272 274 );
... ...
src/pages/ehr/GroupMobileRecord/interface.d.ts
... ... @@ -70,4 +70,9 @@ declare namespace GroupMobileRecord {
70 70 value?: number;
71 71 desc?: string;
72 72 }
  73 +
  74 + interface ProvideParams {
  75 + id?: number;
  76 + staffId?: number;
  77 + }
73 78 }
... ...
src/pages/ehr/GroupMobileRecord/store.ts
... ... @@ -3,9 +3,9 @@
3 3 * @LastEditors: wangqiang@feewee.cn
4 4 * @LastEditTime: 2023-02-24 09:30:52
5 5 */
6   -import usePagination from "@/hooks/usePagination";
7   -import useInitail from "@/hooks/useInitail";
8   -import { useState } from "react";
  6 +import usePagination from '@/hooks/usePagination';
  7 +import useInitail from '@/hooks/useInitail';
  8 +import { useState } from 'react';
9 9 import {
10 10 getMobileListApi,
11 11 getPostListApi,
... ... @@ -13,9 +13,9 @@ import {
13 13 getCanRecordOperatorIdListApi,
14 14 disableGroupMobileApi,
15 15 enableGroupMobileApi,
16   -} from "./api";
17   -import { getStaffApi, getUnitCompanyListApi } from "@/common/api";
18   -import useMenuElement from "@/hooks/useMenuElement";
  16 +} from './api';
  17 +import { getStaffApi, getUnitCompanyListApi } from '@/common/api';
  18 +import useMenuElement from '@/hooks/useMenuElement';
19 19 import { message } from 'antd';
20 20  
21 21 export default function useStore() {
... ... @@ -26,46 +26,46 @@ export default function useStore() {
26 26 const { list: posts } = usePagination(getPostListApi);
27 27 const staffPagination = usePagination<CommonApi.StaffListVO>(getStaffApi, {});
28 28 const unitCompanyInitail = useInitail(getUnitCompanyListApi, [], {
29   - types: "131", // 默认查询 集团手机号往来单位
  29 + types: '131', // 默认查询 集团手机号往来单位
30 30 });
31 31 const operatorListInitail = useInitail(getOperatorListApi, [], undefined);
32   - const { data: canRecordOperatorIdList = [] } = useInitail(
33   - getCanRecordOperatorIdListApi,
34   - [],
35   - undefined
36   - );
  32 + const { data: canRecordOperatorIdList = [] } = useInitail(getCanRecordOperatorIdListApi, [], undefined);
37 33 const [editSubject, setEditSubject] = useState(false);
38 34  
  35 + const [provideMobileOpen, setProvideMobileOpen] = useState(false);
  36 +
39 37 enum StatusText {
40   - "空闲" = 1,
41   - "待领取",
42   - "使用中",
  38 + '空闲' = 1,
  39 + '待领取',
  40 + '使用中',
43 41 '待停用',
44 42 '停用',
45 43 }
46 44  
47 45 enum Status {
48   - "default" = 1,
49   - "warning",
50   - "processing",
51   - "success",
52   - "error",
  46 + 'default' = 1,
  47 + 'warning',
  48 + 'processing',
  49 + 'success',
  50 + 'error',
53 51 }
54 52  
55 53 const disableGroupMobile = (id: number) => {
56 54 const hide = message.loading('停用中,请稍后...', 0);
57   - disableGroupMobileApi(id).then(res => {
58   - hide();
59   - message.success(res.result);
60   - pagination.setLoading(true);
61   - }).catch(error => {
62   - hide();
63   - message.error(error.message || '停用手机号失败');
64   - });
  55 + disableGroupMobileApi(id)
  56 + .then((res) => {
  57 + hide();
  58 + message.success(res.result);
  59 + pagination.setLoading(true);
  60 + })
  61 + .catch((error) => {
  62 + hide();
  63 + message.error(error.message || '停用手机号失败');
  64 + });
65 65 };
66 66  
67 67 const enableGroupMobile = (id: number) => {
68   - const hide = message.loading("启用中,请稍后...", 0);
  68 + const hide = message.loading('启用中,请稍后...', 0);
69 69 enableGroupMobileApi(id)
70 70 .then((res) => {
71 71 hide();
... ... @@ -74,7 +74,7 @@ export default function useStore() {
74 74 })
75 75 .catch((error) => {
76 76 hide();
77   - message.error(error.message || "启用手机号失败");
  77 + message.error(error.message || '启用手机号失败');
78 78 });
79 79 };
80 80  
... ... @@ -94,6 +94,8 @@ export default function useStore() {
94 94 canRecordOperatorIdList,
95 95 editSubject,
96 96 setEditSubject,
  97 + provideMobileOpen,
  98 + setProvideMobileOpen,
97 99 disableGroupMobile,
98 100 enableGroupMobile,
99 101 };
... ...
src/pages/ehr/PeriodRoleSetting/components/SettingItem.tsx
1 1 /*
2 2 * @Date: 2021-02-03 10:42:42
3 3 * @LastEditors: wangqiang@feewee.cn
4   - * @LastEditTime: 2021-02-21 11:42:47
  4 + * @LastEditTime: 2024-04-17 15:34:22
5 5 */
6   -import React, { useEffect, useState } from 'react';
7   -import { Collapse, Card, Form, Input, Button, Row, message } from 'antd';
  6 +import React, { useEffect, useMemo, useState } from 'react';
  7 +import { Collapse, Card, Form, Input, Button, Row, message, Select } from 'antd';
  8 +import { getPriodRoleSettingDetailApi, savePriodRoleSettingDetailApi } from '../api';
  9 +import '../style.less';
  10 +import useInitail from '@/hooks/useInitail';
  11 +import { getAllRoleCodeApi } from '@/common/api';
  12 +import { RoleTypeEnum } from '@/pages/admin/Role/entity';
  13 +import { CommonUseType } from '@/common/utils';
  14 +import { validatorNumberMinAndMax } from '@/utils/validate';
  15 +import TableArrayColumnFormat from '@/pages/ehr/components/TableArrayColumnFormat';
8 16 import { useStore } from '../index';
9   -import { savePriodRoleSettingDetailApi } from '../api';
10   -import "../style.less";
11 17  
12 18 export default function SettingItem() {
13   - const { settingDetail } = useStore();
14   - const { data, loading, setLoading } = settingDetail;
  19 + const { setPriodRoleListLoading, elements } = useStore();
  20 + const canEditRoleList = elements.includes('period:roleList:setting');
  21 + const { data, loading, setLoading } = useInitail(getPriodRoleSettingDetailApi, {}, null);
  22 + const { data: roleList } = useInitail(getAllRoleCodeApi, [], { roleType: RoleTypeEnum.流程角色 });
  23 + const roles = useMemo(() => roleList.filter((role) => [CommonUseType.业务].includes(role.useType!)), [roleList]);
15 24 const [form] = Form.useForm();
16 25 const [edit, setEdit] = useState(false);
17 26 const [confirmLoading, setConfirmLoading] = useState(false);
18 27  
  28 + const showRoleList = useMemo(() => {
  29 + if (roles.length && data.roleList?.length) {
  30 + return roles.filter((role) => data.roleList?.includes(role.roleCode));
  31 + }
  32 + return [];
  33 + }, [roles, data]);
  34 +
19 35 useEffect(() => {
20   - if (!loading) {
21   - form.setFieldsValue({ ...data });
  36 + if (!loading && data) {
  37 + form.setFieldsValue({ ...data, roleList: data.roleList ?? [] });
22 38 }
23   - }, [loading]);
  39 + }, [loading, data]);
24 40  
25 41 const cancel = () => {
26 42 setEdit(false);
27   - form.setFieldsValue({ ...data });
28   - }
  43 + setLoading(true);
  44 + };
29 45  
30 46 const ok = () => {
31 47 setConfirmLoading(true);
32   - form.validateFields().then(val => {
33   - savePriodRoleSettingDetailApi(val).then(res => {
34   - message.success(res.result);
35   - setLoading(true);
36   - setConfirmLoading(false);
37   - setEdit(false);
38   - }).catch(error => {
  48 + form
  49 + .validateFields()
  50 + .then((val) => {
  51 + const hide = message.loading('保存中...', 0);
  52 + savePriodRoleSettingDetailApi({ ...val, roleList: canEditRoleList ? val.roleList : data.roleList })
  53 + .then((res) => {
  54 + hide();
  55 + message.success(res.result);
  56 + setLoading(true);
  57 + setPriodRoleListLoading(true);
  58 + setConfirmLoading(false);
  59 + setEdit(false);
  60 + })
  61 + .catch((error) => {
  62 + hide();
  63 + message.error(error.message);
  64 + setConfirmLoading(false);
  65 + });
  66 + })
  67 + .catch((error) => {
39 68 message.error(error.message);
40 69 setConfirmLoading(false);
41   - })
42   - }).catch(error => {
43   - message.error(error.message);
44   - setConfirmLoading(false);
45   - });
46   - }
  70 + });
  71 + };
47 72  
48 73 return (
49 74 <Collapse defaultActiveKey="时效角色配置">
... ... @@ -52,24 +77,59 @@ export default function SettingItem() {
52 77 <Row justify="end">
53 78 {edit ? (
54 79 <>
55   - <Button type="link" onClick={() => form.submit()} loading={confirmLoading}>确定</Button>
56   - <Button type="link" danger onClick={cancel} loading={confirmLoading}>取消</Button>
  80 + <Button type="link" onClick={() => form.submit()} loading={confirmLoading}>
  81 + 确定
  82 + </Button>
  83 + <Button type="link" danger onClick={cancel} loading={confirmLoading}>
  84 + 取消
  85 + </Button>
57 86 </>
58   - ) : <Button type="link" onClick={() => setEdit(true)}>编辑</Button>}
  87 + ) : (
  88 + <Button type="link" onClick={() => setEdit(true)}>
  89 + 编辑
  90 + </Button>
  91 + )}
59 92 </Row>
60 93 <Form form={form} size="small" onFinish={ok}>
61   - <Form.Item label="时效角色最长授权时间" name="maxDay">
  94 + <Form.Item
  95 + label="时效角色最长授权时间"
  96 + name="maxDay"
  97 + rules={[{ required: edit, validator: validatorNumberMinAndMax({ min: 0, required: edit, decimal: 0 }) }]}
  98 + >
62 99 <Input suffix="天" bordered={edit} readOnly={!edit} style={{ width: 70 }} />
63 100 </Form.Item>
64   - <Form.Item label="取消时效角色时间" name="extraDay">
  101 + <Form.Item
  102 + label="取消时效角色时间"
  103 + name="extraDay"
  104 + rules={[{ required: edit, validator: validatorNumberMinAndMax({ min: 0, required: edit, decimal: 0 }) }]}
  105 + >
65 106 <Input prefix="申请时效角色的截止日期加上" suffix="天" bordered={edit} readOnly={!edit} style={{ width: 240 }} />
66 107 </Form.Item>
67   - <Form.Item label="时效角色月度授权次数" name="authNum">
  108 + <Form.Item
  109 + label="时效角色月度授权次数"
  110 + name="authNum"
  111 + rules={[{ required: edit, validator: validatorNumberMinAndMax({ min: 0, required: edit, decimal: 0 }) }]}
  112 + >
68 113 <Input suffix="次" bordered={edit} readOnly={!edit} style={{ width: 70 }} />
69 114 </Form.Item>
  115 + {canEditRoleList ? (
  116 + <Form.Item label="时效角色最大范围" name="roleList" rules={[{ required: edit, message: '请选择时效角色最大范围' }]}>
  117 + {edit ? (
  118 + <Select allowClear placeholder="请选择时效角色最大范围" mode="multiple">
  119 + {roles.map((role) => (
  120 + <Select.Option key={role.roleCode} value={role.roleCode}>
  121 + {role.roleName}
  122 + </Select.Option>
  123 + ))}
  124 + </Select>
  125 + ) : (
  126 + <span>{<TableArrayColumnFormat data={showRoleList} showKey="roleName" key="roleCode" unit="个角色" maxCount={5} />}</span>
  127 + )}
  128 + </Form.Item>
  129 + ) : null}
70 130 </Form>
71 131 </Card>
72 132 </Collapse.Panel>
73 133 </Collapse>
74 134 );
75   -}
76 135 \ No newline at end of file
  136 +}
... ...
src/pages/ehr/PeriodRoleSetting/interface.d.ts
1 1 /*
2 2 * @Date: 2020-12-15 16:05:51
3 3 * @LastEditors: wangqiang@feewee.cn
4   - * @LastEditTime: 2020-12-16 09:26:16
  4 + * @LastEditTime: 2024-04-17 14:41:23
5 5 */
6 6 declare namespace PeriodRoleSetting {
7 7 interface QueryParams {
8   - keyword?: string // 搜索员工名或角色名
  8 + keyword?: string; // 搜索员工名或角色名
9 9 }
10 10  
11 11 interface Item {
12   - id?: number
13   - staffId?: number,
14   - staffName?: string,
15   - roleCode?: string,
16   - roleName?: string,
17   - periodRoleShopList?: ShopItem[]
  12 + id?: number;
  13 + staffId?: number;
  14 + staffName?: string;
  15 + roleCode?: string;
  16 + roleName?: string;
  17 + periodRoleShopList?: ShopItem[];
18 18 }
19 19  
20 20 interface ShopItem {
21   - shopId?: number,
22   - shopName?: string
  21 + shopId?: number;
  22 + shopName?: string;
23 23 }
24 24  
25 25 interface SettingDetail {
26   - maxDay?: 5, // 最大授权天数
27   - extraDay?: 3, // 取消额外新增天数
28   - authNum?: 3 // 最多授权次数
  26 + maxDay?: number; // 最大授权天数
  27 + extraDay?: number; // 取消额外新增天数
  28 + authNum?: number; // 最多授权次数
  29 + roleList?: string[]; // 最大范围
29 30 }
30 31  
31 32 interface RoleItem {
32   - roleCode?: string, //角色编码
33   - roleName?: string //角色名
  33 + roleCode?: string; //角色编码
  34 + roleName?: string; //角色名
34 35 }
35 36  
36 37 interface UserParams {
37   - keywords?: string
  38 + keywords?: string;
38 39 }
39 40  
40 41 interface User {
41   - id?: number
42   - name?: string
43   - mobile?: string
  42 + id?: number;
  43 + name?: string;
  44 + mobile?: string;
44 45 }
45   -}
46 46 \ No newline at end of file
  47 +}
... ...
src/pages/ehr/PeriodRoleSetting/store.ts
... ... @@ -12,8 +12,7 @@ import useMenuElement from &#39;@/hooks/useMenuElement&#39;;
12 12 export default function useStore() {
13 13 const { elements } = useMenuElement();
14 14 const pagination = usePagination(getListApi);
15   - const settingDetail = useInitail(getPriodRoleSettingDetailApi, {}, null);
16   - const { data: roleList = [] } = useInitail(getPriodRoleListApi, [], null);
  15 + const { data: roleList = [], setLoading: setPriodRoleListLoading } = useInitail(getPriodRoleListApi, [], null);
17 16 const { data: shopList = [] } = useInitail(getShopListApi, [], null);
18 17 const [visible, setVisible] = useState(false);
19 18 const [currentItem, setCurrentItem] = useState<PeriodRoleSetting.Item>();
... ... @@ -22,8 +21,8 @@ export default function useStore() {
22 21 return {
23 22 elements,
24 23 pagination,
25   - settingDetail,
26 24 roleList,
  25 + setPriodRoleListLoading,
27 26 shopList,
28 27 visible,
29 28 setVisible,
... ...
src/pages/finance/CompanyRelationAuth/index.tsx
... ... @@ -50,6 +50,7 @@ function CompanyRelationAuth() {
50 50 ...rowSelection,
51 51 }}
52 52 >
  53 + <Column title="单位ID" dataIndex="compId" width={'10%'}/>
53 54 <Column title="单位名称" dataIndex="compName" />
54 55 <Column title="简称" dataIndex="compShortName" />
55 56 <Column title="往来单位业务类型" dataIndex="compTypeName" />
... ...
src/pages/finance/SpecialAccount/FinancingCompany/components/List.tsx
... ... @@ -24,9 +24,14 @@ enum CarRepaymentAccountTypeEnum {
24 24 }
25 25 enum DraftModeEnum {
26 26 '未配置' = -1,
27   - '中信模式' = 100,
28   - '光大模式' = 200,
29   - '兵财模式' = 300
  27 + '模式A' = 100,
  28 + '模式B' = 200,
  29 + '模式C' = 300
  30 +}
  31 +enum DraftModeTip {
  32 + '每张票支付保证金,按票据已还总金额清票' = 100,
  33 + '每张票支付保证金,按单票到期金额清票' = 200,
  34 + '前期一次性支付保证金' = 300
30 35 }
31 36 export default function SalesFinanceList() {
32 37 const { setCurrent, setVisible, companyList, loading } = useStore();
... ... @@ -164,8 +169,13 @@ export default function SalesFinanceList() {
164 169 <Column
165 170 title="票据模式"
166 171 dataIndex="draftMode"
167   - render={(draftMode, row: any) => (DraftModeEnum[draftMode])}
168   - width={120}
  172 + render={(draftMode, row: any) => (
  173 + <div>
  174 + {DraftModeEnum[draftMode]}:
  175 + <span style={{color:"#999"}}>{DraftModeTip[draftMode]}</span>
  176 + </div>
  177 + )}
  178 + width={200}
169 179 />
170 180 <Column
171 181 title="操作"
... ...
src/pages/finance/TradeCompany/components/List.tsx
... ... @@ -72,6 +72,7 @@ export default function AccountList() {
72 72 return (
73 73 <>
74 74 <Table dataSource={comAccountList} pagination={paginationConfig} rowKey="id" loading={loading}>
  75 + <Column title="单位ID" dataIndex="id" width="5%" />
75 76 <Column title="名称" dataIndex="compName" width="15%" />
76 77 {/* <Column title="单位主体类型" dataIndex="subjectType" width="15%" /> */}
77 78 <Column title="简称" dataIndex="compShortName" width="15%" />
... ...
src/pages/finance/ViewCompanyRelationAuth/index.tsx
... ... @@ -50,6 +50,7 @@ function CompanyRelationAuth() {
50 50 // ...rowSelection,
51 51 // }}
52 52 >
  53 + <Column title="单位ID" dataIndex="compId" />
53 54 <Column title="单位名称" dataIndex="compName" />
54 55 <Column title="简称" dataIndex="compShortName" />
55 56 <Column title="往来单位业务类型" dataIndex="compTypeName" />
... ...
src/pages/finance/ViewTradeCompany/components/List.tsx
... ... @@ -72,6 +72,7 @@ export default function AccountList() {
72 72 return (
73 73 <>
74 74 <Table dataSource={comAccountList} pagination={paginationConfig} rowKey="id" loading={loading}>
  75 + <Column title="单位ID" dataIndex="id" width="6%" />
75 76 <Column title="名称" dataIndex="compName" width="15%" />
76 77 {/* <Column title="单位主体类型" dataIndex="subjectType" width="15%" /> */}
77 78 <Column title="简称" dataIndex="compShortName" width="15%" />
... ...
src/pages/mkt/ActivityCreate/BasicInformation/components/ImageUploader/index.tsx
... ... @@ -72,7 +72,7 @@ export default function Detail({ form, readOnly, bizType }: Props) {
72 72 return [];
73 73 }
74 74 if (status == 'removed' && response) {
75   - COS.cosDelete({ filePath: response.data });
  75 + // COS.cosDelete({ filePath: response.data });
76 76 }
77 77 return e?.fileList;
78 78 }
... ...
src/pages/mkt/ActivityCreate/ExternalPromotion/api.ts
... ... @@ -162,4 +162,9 @@ export function saveChangeAwardName(params: { giftId: number; awardName: string
162 162 /** 根据车系id查询门店信息 */
163 163 export function getSeriesToShopList(params: { seriesIds: string }): http.PromiseResp<ExternalPromotion.ShopList[]> {
164 164 return request.post(`${FVM_HOST}/select/sales/series/shop`, params, { contentType: 'form-urlencoded' });
  165 +}
  166 +
  167 +/** 授权配置促销范围门店查询(门店) */
  168 +export function getScopeShopAuthList(params: { bizType?: number }): http.PromiseResp<CommonApi.OptionVO[]> {
  169 + return request.get(`${MKT_HOST}/erp/activity/get/scope/shop/auth`, { params });
165 170 }
166 171 \ No newline at end of file
... ...
src/pages/mkt/ActivityCreate/ExternalPromotion/components/ActivityFlow/SignUp.tsx
1 1 import React, { useState, useEffect } from 'react';
2   -import { Button, Form, InputNumber, message, Space, Spin, Divider, Radio, Row } from 'antd';
  2 +import { Button, Form, InputNumber, message, Space, Spin, Divider, Radio, Row, Alert } from 'antd';
3 3 import useInitial from '@/hooks/useInitail';
4 4 import { PlusOutlined, MinusCircleOutlined } from '@ant-design/icons';
5 5 import { saveSignUpApi, getSignUpDetail, saveChangeSignUpApi } from '../../api'; //保存报名有礼
... ... @@ -163,6 +163,25 @@ export default function Index() {
163 163 <Radio value={1}>按报名顺序不同赠送不同优惠券</Radio>
164 164 </Radio.Group>
165 165 </Form.Item>
  166 + {changeEnable ? (
  167 + <Alert
  168 + message="提示"
  169 + type="warning"
  170 + showIcon
  171 + closable
  172 + style={{ marginBottom: 10 }}
  173 + description={
  174 + <div>
  175 + <p style={{ margin: 0, fontSize: 12, color: '#999' }}>
  176 + 1.优惠券编辑变更:范围只能扩大不能缩小,变更前所发优惠券与变更后所发优惠券的限制范围一致
  177 + </p>
  178 + <p style={{ margin: 0, fontSize: 12, color: '#999' }}>
  179 + 2.优惠券删除-新增变更:不限制优惠券变更范围,变更前所发优惠券使用范围不变,变更后所发优惠券限制范围变更
  180 + </p>
  181 + </div>
  182 + }
  183 + />
  184 + ) : null}
166 185 <Form.Item noStyle shouldUpdate={(prevValues, currentValues) => prevValues.ladderReward !== currentValues.ladderReward}>
167 186 {({ getFieldValue }) => {
168 187 return (
... ...
src/pages/mkt/ActivityCreate/index.tsx
... ... @@ -10,7 +10,6 @@ import { createStore } from &#39;@/hooks/moz&#39;;
10 10 import store from './store';
11 11 import { ActivityStatusEnums } from '@/pages/mkt/entity';
12 12 import { saveChangeApply } from '@/pages/mkt/ActivityCreate/ExternalPromotion/api';
13   -import { useLocation } from 'umi';
14 13  
15 14 export const { Provider, useStore } = createStore(store);
16 15 type Props = common.ConnectProps
... ... @@ -88,7 +87,7 @@ const ActivityCreate = (props: Props) =&gt; {
88 87 }
89 88 return (
90 89 <PageHeaderWrapper
91   - title="活动配置"
  90 + title={`活动配置${change ? '变更' : ''}`}
92 91 content={
93 92 <>
94 93 {activityNo && (<div>活动编号:{activityNo} | {ActivityStatusEnums[Number(status)]}</div>)}
... ... @@ -96,6 +95,7 @@ const ActivityCreate = (props: Props) =&gt; {
96 95 </>
97 96 }
98 97 >
  98 +
99 99 {baseInfo.changeEnable && (
100 100 <Card style={{ marginBottom: 15 }}>
101 101 <Row justify="space-between">
... ...
src/pages/mkt/ActivityManage/components/Operation.tsx
... ... @@ -45,7 +45,7 @@ export default function Oparetion(props: Props) {
45 45 }
46 46 });
47 47 }
48   - /** 变更信息 */
  48 + /** 变更操作 */
49 49 function changeBaseInfo() {
50 50 history.push({
51 51 pathname: '/mkt/manage/create',
... ... @@ -134,7 +134,7 @@ export default function Oparetion(props: Props) {
134 134 /* 审核成功、待发布 ;未开始,进行中*/
135 135 if ([4, 5].includes(status) && record.changeStatus !== 2) {
136 136 menus.push(
137   - <Button type="primary" ghost size="small" onClick={changeBaseInfo}>变更信息</Button>,
  137 + <Button type="primary" ghost size="small" onClick={changeBaseInfo}>变更操作</Button>,
138 138 <Button type="primary" ghost size="small" onClick={changeRecords}>变更记录</Button>,
139 139 // <Button danger ghost size="small" onClick={endActivity}>终止</Button>,
140 140 );
... ...
src/pages/oop/CarAlias/SpecCode/components/CarList.tsx
1 1 import React from 'react';
2   -import { Table } from 'antd';
  2 +import { Button, Table } from 'antd';
3 3 import { useStore } from '../index';
4 4 import style from '../index.less';
5 5 import { EnergyTypeEnum } from '../carData';
... ... @@ -51,6 +51,12 @@ export default function CarList() {
51 51 dataIndex: 'specCode',
52 52 },
53 53 {
  54 + title: '配置代码',
  55 + dataIndex: 'specCodeList',
  56 + align: "center",
  57 + render: (text: any, record: any) => (text ? text.map((res: any, index: any) => <div key={`item_${index}`}>{res}</div>) : '——'),
  58 + },
  59 + {
54 60 title: '能源类型',
55 61 dataIndex: 'energyType',
56 62 render: (text: number) => EnergyTypeEnum[text] || '--',
... ... @@ -73,8 +79,8 @@ export default function CarList() {
73 79 align: 'center',
74 80 render: (text: any, row: any) => (
75 81 <React.Fragment key="key">
76   - <a
77   - href="#"
  82 + <Button
  83 + type='link'
78 84 onClick={(e) => {
79 85 e.preventDefault();
80 86 setCurrentItem(row);
... ... @@ -82,7 +88,7 @@ export default function CarList() {
82 88 }}
83 89 >
84 90 编辑别名
85   - </a>
  91 + </Button>
86 92 </React.Fragment>
87 93 ),
88 94 },
... ...
src/pages/oop/CarAlias/SpecCode/components/CarModal.tsx
... ... @@ -62,8 +62,8 @@ export default function CarModal() {
62 62 >
63 63 <Form
64 64 form={form}
65   - labelCol={{ span: 6 }}
66   - wrapperCol={{ span: 15 }}
  65 + labelCol={{ span: 8 }}
  66 + wrapperCol={{ span: 12 }}
67 67 >
68 68 <FormItem style={{ width: 400 }} name="brandName" label="品牌">
69 69 <Input style={{ width: '100%' }} disabled bordered={false} />
... ...
src/pages/oop/CarbasicInfo/comps/Filter.tsx
1 1 import React, { useState, useEffect } from 'react';
2 2 import '@ant-design/compatible/assets/index.css';
3 3 import { Cascader, message } from 'antd';
4   -import { CascaderOptionType } from 'antd/lib/cascader';
  4 +import type { BaseOptionType, DefaultOptionType } from 'antd/lib/cascader';
5 5 import { getBrandApi, getSeriesApi, getSpecApi } from '../api';
6 6 import { getSpecCodeList } from '@/pages/oop/Car/api';
7 7  
... ... @@ -21,23 +21,23 @@ export default function Filter(props: FilterProps) {
21 21  
22 22 useEffect(() => {
23 23 getBrandApi()
24   - .then(res => {
  24 + .then((res) => {
25 25 const { data = [] } = res;
26   - const brandList = data.map(brand => ({ ...brand, isLeaf: false }));
  26 + const brandList = data.map((brand) => ({ ...brand, isLeaf: false }));
27 27 setSpecOptions(brandList);
28 28 })
29   - .catch(e => {
  29 + .catch((e) => {
30 30 message.error(e.message);
31 31 });
32 32 }, []);
33 33  
34   - function loadData(selectedOptions: CascaderOptionType) {
  34 + function loadData(selectedOptions: BaseOptionType) {
35 35 const length = selectedOptions.length;
36 36 const targetOption = selectedOptions[length - 1];
37 37 targetOption.loading = true;
38 38 if (length === 1) {
39 39 getSeriesApi(targetOption.id)
40   - .then(res => {
  40 + .then((res) => {
41 41 const { data = [] } = res;
42 42 targetOption.loading = false;
43 43 targetOption.children = [];
... ... @@ -50,32 +50,33 @@ export default function Filter(props: FilterProps) {
50 50 });
51 51 setSpecOptions([...specOptions]);
52 52 })
53   - .catch(e => {
  53 + .catch((e) => {
54 54 message.error(e.message);
55 55 });
56 56 }
57 57 if (length === 2) {
58 58 const fetchList = specCode ? getSpecCodeList({ seriesId: targetOption.id }) : getSpecApi(targetOption.id);
59 59 // getSpecCodeList({ seriesId: targetOption.id })
60   - fetchList.then(res => {
61   - const { data = [] } = res;
62   - targetOption.loading = false;
63   - targetOption.children = [];
64   - data.forEach((list: any) => {
65   - targetOption.children.push({
66   - id: specCode ? list.specCode : list.id,
67   - name: specCode ? `${list.specCode}[${list.specCodeName}]` : list.name,
  60 + fetchList
  61 + .then((res) => {
  62 + const { data = [] } = res;
  63 + targetOption.loading = false;
  64 + targetOption.children = [];
  65 + data.forEach((list: any) => {
  66 + targetOption.children.push({
  67 + id: specCode ? list.specCode : list.id,
  68 + name: specCode ? `${list.specCode}[${list.specCodeName}]` : list.name,
  69 + });
68 70 });
69   - });
70   - setSpecOptions([...specOptions]);
71   - })
72   - .catch(e => {
  71 + setSpecOptions([...specOptions]);
  72 + })
  73 + .catch((e) => {
73 74 message.error(e.message);
74 75 });
75 76 }
76 77 }
77 78  
78   - function filterByCar(value: any, options?: CascaderOptionType[]) {
  79 + function filterByCar(value: any, options?: BaseOptionType[]) {
79 80 const carFilter = {
80 81 brandId: value ? value[0] : undefined,
81 82 seriesId: value ? value[1] : undefined,
... ... @@ -84,6 +85,9 @@ export default function Filter(props: FilterProps) {
84 85 onChange(carFilter);
85 86 }
86 87  
  88 + const filter = (inputValue: string, path: DefaultOptionType[]) =>
  89 + path.some((option) => (option.name as string).toLowerCase().indexOf(inputValue.toLowerCase()) > -1);
  90 +
87 91 return (
88 92 <Cascader
89 93 allowClear
... ... @@ -94,6 +98,8 @@ export default function Filter(props: FilterProps) {
94 98 onChange={filterByCar}
95 99 fieldNames={{ value: 'id', label: 'name' }}
96 100 notFoundContent="暂无数据"
  101 + showSearch={{ filter }}
  102 + onSearch={(value) => console.log(value)}
97 103 style={{ minWidth: 200, flex: 1 }}
98 104 />
99 105 );
... ...
src/pages/order3/RebateCommission/api.ts 0 → 100644
  1 +import { http } from '@/typing/http';
  2 +import request from '@/utils/request';
  3 +import { ORDER3 } from '@/utils/host';
  4 +import { PageParams } from '@/typing/common';
  5 +
  6 +type Page<T> = http.PromisePageResp<T>;
  7 +
  8 +export interface RequestParams extends PageParams {}
  9 +
  10 +export interface SaveParams {
  11 + id?: number; // id
  12 + financialCompanyId?: number; // 金融公司id
  13 + financialCompanyName?: string; // 金融公司名称
  14 + productName?: string; // 金融公司产品名称
  15 + loanRate?: number; // 贷款利率
  16 + applyShopType?: number; // 适用门店类型(1.全部门店2.部分门店)
  17 + shopList?: ShopList[]; // 门店列表
  18 +}
  19 +
  20 +export interface ShopList {
  21 + shopId?: number; // 门店id
  22 + shopName?: string; // 门店名称
  23 +}
  24 +
  25 +export interface ListResult {
  26 + id?: number; // id
  27 + financialCompanyId?: number; // 金融公司id
  28 + financialCompanyName?: string; // 金融公司名称
  29 + productName?: string; // 产品名称
  30 + loanRate?: number; // 贷款利率
  31 + applyShopList?: ApplyShopList[]; // 适用门店
  32 + applyShopType?: number; // 适用门店类型(1.全部门店2.部分门店)
  33 +}
  34 +
  35 +interface ApplyShopList {
  36 + id?: number; // id
  37 + createTime?: number; // 创建时间
  38 + updateTime?: number; // 修改时间
  39 + financialProductsConfigId?: number; // 分期条件配置id
  40 + shopId?: number; // 门店id
  41 + shopName?: string; // 门店名称
  42 +}
  43 +
  44 +/**
  45 + * 查询金融产品配置列表
  46 + * @param params
  47 + * @returns
  48 + */
  49 +export function getListApi(params: RequestParams): Page<ListResult> {
  50 + return request.get(`${ORDER3}/erp/financial/loan/config/products/page`, { params });
  51 +}
  52 +
  53 +/**
  54 + * 查询金融产品详情
  55 + * @param id
  56 + * @returns
  57 + */
  58 +export function getFinancialDetailApi(id?: number): http.PromiseResp<ListResult> {
  59 + return request.get(`${ORDER3}/erp/financial/loan/config/products/queryInfo`, { params: {id} });
  60 +}
  61 +
  62 +/**
  63 + * 新增、编辑金融产品配置
  64 + * @param params
  65 + * @returns
  66 + */
  67 +export function saveConfigApi(params: SaveParams) {
  68 + return request.post(`${ORDER3}/erp/financial/loan/config/products/saveOrUpdate`, params);
  69 +}
  70 +
  71 +/**
  72 + * 删除金融产品配置
  73 + * @param params
  74 + * @returns
  75 + */
  76 +export function deleteConfigApi(params?: {id?: number}) {
  77 + return request.post(`${ORDER3}/erp/financial/loan/config/products/delete`, params, { contentType: 'form-urlencoded' });
  78 +}
... ...
src/pages/order3/RebateCommission/components/EditModal.tsx 0 → 100644
  1 +import React, { useEffect, useState } from 'react';
  2 +import FeeweeUploadAttachment from '@/components/FeeweeUploadAttachment';
  3 +import { Button, Card, Form, Input, message, Modal, Radio, Row, Select, Space } from 'antd';
  4 +import { debounce } from 'lodash';
  5 +import moment from 'moment';
  6 +import { SaveParams, saveConfigApi } from '../api';
  7 +import { useStore } from '../index';
  8 +import SelectorWithFull from '@/components/SelectorWithFull';
  9 +
  10 +export default function EditModal() {
  11 + const [form] = Form.useForm();
  12 + const { editModal, setEditModal, companyList, shopList, setLoading: setPageLoading } = useStore();
  13 + const [confirmLoading, setConfirmLoading] = useState<boolean>(false);
  14 +
  15 + useEffect(() => {
  16 + if (editModal.visible && editModal.data?.id) {
  17 + form.setFieldsValue({
  18 + financialCompany: { label: editModal.data?.financialCompanyName, value: editModal.data?.financialCompanyId },
  19 + productName: editModal.data?.productName,
  20 + loanRate: editModal.data?.loanRate,
  21 + applyShopType: editModal.data?.applyShopType,
  22 + shopList: editModal.data.applyShopList?.map(v => ({name: v.shopName, id: v.shopId})),
  23 + });
  24 + }
  25 + }, [editModal.visible]);
  26 +
  27 + function handleCancle() {
  28 + setEditModal({ visible: false, data: undefined, title: '' });
  29 + form.resetFields();
  30 + }
  31 +
  32 + async function handleSubmit() {
  33 + const params = await form.validateFields();
  34 + const _params: SaveParams = {
  35 + financialCompanyId: params.financialCompany?.value,
  36 + financialCompanyName: params.financialCompany?.label,
  37 + productName: params.productName,
  38 + loanRate: params.loanRate,
  39 + applyShopType: params.applyShopType,
  40 + };
  41 + if (params.applyShopType === 2) {
  42 + _params.shopList = params.shopList?.map((v: any) => ({shopId: v.id, shopName: v.name}))
  43 + }
  44 + if (editModal.data?.id) {
  45 + _params.id = editModal.data.id;
  46 + }
  47 + setConfirmLoading(true);
  48 + saveConfigApi(_params)
  49 + .then(res => {
  50 + message.success(res.result);
  51 + setConfirmLoading(false);
  52 + setPageLoading(true);
  53 + handleCancle();
  54 + })
  55 + .catch(e => {
  56 + message.error(e.message);
  57 + setConfirmLoading(false);
  58 + })
  59 + }
  60 +
  61 + return (
  62 + <Modal
  63 + title={editModal.title}
  64 + open={editModal.visible}
  65 + onCancel={handleCancle}
  66 + width="60%"
  67 + maskClosable={false}
  68 + footer={false}
  69 + destroyOnClose
  70 + afterClose={() => form.resetFields()}
  71 + >
  72 + <Card bordered={false}>
  73 + <Form form={form} labelCol={{ span: 6 }} wrapperCol={{ span: 15 }}>
  74 + <Form.Item label="车贷银行" name="financialCompany" rules={[{ required: true, message: '请选择' }]}>
  75 + <Select
  76 + labelInValue
  77 + showSearch
  78 + allowClear
  79 + filterOption
  80 + optionFilterProp='label'
  81 + options={companyList.map(v => ({label: v.name, value: v.id}))}
  82 + placeholder="请选择"
  83 + />
  84 + </Form.Item>
  85 + <Form.Item label="金融产品" name="productName" rules={[{ required: true, message: '请输入' }]}>
  86 + <Input placeholder="请输入" />
  87 + </Form.Item>
  88 + <Form.Item
  89 + label="促销利率"
  90 + name="loanRate"
  91 + rules={[
  92 + {
  93 + required: true,
  94 + pattern: /^(\d+|\d+\.\d{1,2})$/,
  95 + message: '请输入正确的促销利率',
  96 + },
  97 + ({ getFieldValue }) => ({
  98 + validator(_, value) {
  99 + if (value && +value <= 100 && +value >= 0) {
  100 + return Promise.resolve();
  101 + }
  102 + return Promise.reject(new Error('比例范围应在0~100!'));
  103 + },
  104 + }),
  105 + ]}
  106 + >
  107 + <Input placeholder='请输入' style={{ width: '100%' }} suffix="%" />
  108 + </Form.Item>
  109 + <Form.Item label="适用门店类型" name="applyShopType" rules={[{ required: true, message: '请选择' }]}>
  110 + <Radio.Group>
  111 + <Radio value={1}>全部门店</Radio>
  112 + <Radio value={2}>部分门店</Radio>
  113 + </Radio.Group>
  114 + </Form.Item>
  115 + <Form.Item noStyle shouldUpdate={(prevValues, currentValues) => prevValues.applyShopType != currentValues.applyShopType}>
  116 + {({ getFieldValue }) => {
  117 + const value = getFieldValue('applyShopType');
  118 + if (value !== 2) {
  119 + return;
  120 + }
  121 + return (
  122 + <Form.Item label="适用门店" name="shopList" rules={[{ required: true, message: '请选择' }]}>
  123 + <SelectorWithFull
  124 + placeholder="请选择"
  125 + multiple
  126 + showSearch
  127 + allowClear
  128 + labelInValue
  129 + treeNodeFilterProp='label'
  130 + data={shopList || []}
  131 + fieldKeyNames={{ keyName: 'id', valueName: 'id', labelName: 'name' }}
  132 + />
  133 + </Form.Item>
  134 + );
  135 + }}
  136 + </Form.Item>
  137 + </Form>
  138 +
  139 + <Row justify="center">
  140 + <Space>
  141 + <Button loading={confirmLoading} disabled={confirmLoading} onClick={handleCancle} type="default">
  142 + 取消
  143 + </Button>
  144 + <Button loading={confirmLoading} disabled={confirmLoading} onClick={debounce(handleSubmit, 380)} type="primary">
  145 + 确定
  146 + </Button>
  147 + </Space>
  148 + </Row>
  149 + </Card>
  150 + </Modal>
  151 + );
  152 +}
... ...
src/pages/order3/RebateCommission/components/List.tsx 0 → 100644
  1 +import React, { useState } from 'react';
  2 +import { message, Popconfirm, Space, Table, Typography, Divider } from 'antd';
  3 +import * as api from '../api';
  4 +import { useStore } from '../index';
  5 +import TextWithMore from '@/components/TextWithMore';
  6 +
  7 +const Column = Table.Column;
  8 +
  9 +export default function List() {
  10 + const { list,
  11 + paginationConfig,
  12 + loading, setLoading,
  13 + setEditModal, setSelectedItem,
  14 + setSelectKey, selectKey } = useStore();
  15 + const [pageLoading, setPageLoading] = useState<boolean>(false);
  16 +
  17 + /**
  18 + * 删除配置
  19 + * @param id
  20 + */
  21 + function onDelete(id?: number) {
  22 + setPageLoading(true);
  23 + api.deleteConfigApi({id})
  24 + .then(res => {
  25 + setPageLoading(false);
  26 + setLoading(true);
  27 + message.success(res.result);
  28 + })
  29 + .catch(e => {
  30 + setPageLoading(false);
  31 + message.error(e.message);
  32 + })
  33 + }
  34 +
  35 + function onEdit(record?: api.ListResult) {
  36 + setEditModal({visible: true, title: '编辑', data: record})
  37 + }
  38 +
  39 + const rowSelection = {
  40 + onChange: (selectedRowKeys: React.Key[], selectedRows: api.ListResult[]) => {
  41 + if (selectedRows.length) {
  42 + setSelectedItem(selectedRows[0]);
  43 + setSelectKey(selectedRowKeys);
  44 + } else {
  45 + setSelectedItem(undefined);
  46 + setSelectKey([]);
  47 + }
  48 + },
  49 + // getCheckboxProps: (record: api.ListResult) => ({
  50 + // disabled: record.name === 'Disabled User', // Column configuration not to be checked
  51 + // name: record.name,
  52 + // }),
  53 + };
  54 +
  55 + return (
  56 + <div>
  57 + <Table
  58 + dataSource={list}
  59 + pagination={paginationConfig}
  60 + loading={loading || pageLoading}
  61 + rowKey="id"
  62 + rowSelection={{
  63 + type: 'radio',
  64 + ...rowSelection,
  65 + selectedRowKeys: selectKey,
  66 + // defaultSelectedRowKeys: list.length ? [list[0].id] : undefined,
  67 + }}
  68 + >
  69 + <Column title="车贷银行" dataIndex="financialCompanyName" />
  70 + <Column title="金融产品" align="center" dataIndex="productName" />
  71 + <Column title="利率" align="center" dataIndex="loanRate" render={(_text) => <span>{(_text ?? '--') + '%'}</span>} />
  72 + <Column
  73 + title="适用门店"
  74 + align="center"
  75 + dataIndex="applyShopList"
  76 + render={(_text, record: api.ListResult) =>
  77 + record.applyShopType === 1 ? '全部门店' : _text && _text.length ? <TextWithMore unit="个门店" title="适用门店" dataIndex="shopName" list={record.applyShopList || []} /> : '--'
  78 + }
  79 + />
  80 + <Column
  81 + title="操作"
  82 + dataIndex="option"
  83 + align="center"
  84 + render={(_text, record: api.ListResult) => (
  85 + <Space wrap split={<Divider type="vertical" />}>
  86 + <Popconfirm title="确定删除?" onConfirm={() => onDelete(record.id)}>
  87 + <Typography.Link style={{ color: '#666' }}>删除</Typography.Link>
  88 + </Popconfirm>
  89 + <Typography.Link onClick={() => onEdit(record)} style={{ color: '#4189FD' }}>
  90 + 编辑
  91 + </Typography.Link>
  92 + </Space>
  93 + )}
  94 + />
  95 + </Table>
  96 + </div>
  97 + );
  98 +}
... ...
src/pages/order3/RebateCommission/entity.ts 0 → 100644
  1 +export const ConditionData = [
  2 + { label: '=', value: 1 },
  3 + { label: '>', value: 2 },
  4 + { label: '≥', value: 3 },
  5 + { label: '<', value: 4 },
  6 + { label: '≤', value: 5 },
  7 +];
  8 +
  9 +export enum ConditionEnum {
  10 + '=' = 1,
  11 + '>',
  12 + '≥',
  13 + '<',
  14 + '≤',
  15 +}
  16 +
  17 +export enum RebateTypeEnum {
  18 + "固定金额" = 1,
  19 + "按比例"
  20 +}
  21 +
  22 +export enum CommissionTypeEnum {
  23 + "计提成" = 1,
  24 + "计附加值"
  25 +}
0 26 \ No newline at end of file
... ...
src/pages/order3/RebateCommission/index.tsx 0 → 100644
  1 +import { createStore } from '@/hooks/moz';
  2 +import { PageHeaderWrapper } from '@ant-design/pro-layout';
  3 +import { Button, Card, Col, Input, Row, Space } from 'antd';
  4 +import { debounce } from 'lodash';
  5 +import React, { useEffect } from 'react';
  6 +import List from './components/List';
  7 +import store from './store';
  8 +import EditMOdal from './components/EditModal';
  9 +import { PlusOutlined } from '@ant-design/icons';
  10 +import LoanConditionPage from '@/pages/order3/RebateCommission/subpages/LoanCondition';
  11 +
  12 +export const { Provider, useStore } = createStore(store);
  13 +
  14 +function RebateCommission() {
  15 + const { setParams, innerParams, setEditModal, selectItem, selectKey } = useStore();
  16 +
  17 + function onFinancialSearch(value?: string) {
  18 + setParams({ ...innerParams, current: 1, financialCompanyName: value || null }, true);
  19 + }
  20 +
  21 + function onProductSearch(value?: string) {
  22 + setParams({ ...innerParams, current: 1, productName: value || null }, true);
  23 + }
  24 +
  25 + function onAdd() {
  26 + setEditModal({ visible: true, title: '新增' });
  27 + }
  28 +
  29 + return (
  30 + <PageHeaderWrapper title="车贷银行返利和提成配置">
  31 + <Card>
  32 + <Row>
  33 + <Col span={selectKey.length ? 12 : 24}>
  34 + <Row justify="space-between" style={{ marginBottom: '15px' }}>
  35 + <Space>
  36 + <Input.Search allowClear onSearch={debounce(onFinancialSearch, 380)} placeholder="请输入车贷银行名称" />
  37 + <Input.Search allowClear onSearch={debounce(onProductSearch, 380)} placeholder="请输入金融产品名称" />
  38 + </Space>
  39 + <Button onClick={onAdd} type="primary">
  40 + <PlusOutlined /> 新增
  41 + </Button>
  42 + </Row>
  43 + <List />
  44 + <EditMOdal />
  45 + </Col>
  46 + {selectKey.length ? (
  47 + <Col span={12}>
  48 + <LoanConditionPage id={selectItem?.id} financialCompanyName={selectItem?.financialCompanyName} productName={selectItem?.productName} />
  49 + </Col>
  50 + ) : null}
  51 + </Row>
  52 + </Card>
  53 + </PageHeaderWrapper>
  54 + );
  55 +}
  56 +
  57 +export default () => (
  58 + <Provider>
  59 + <RebateCommission />
  60 + </Provider>
  61 +);
... ...
src/pages/order3/RebateCommission/store.ts 0 → 100644
  1 +import usePagination from '@/hooks/usePagination';
  2 +import { useState } from 'react';
  3 +import { getListApi, ListResult } from './api';
  4 +import { getUnitCompanyListApi, fetchShopListByRangeTypeApi } from '@/common/api';
  5 +import useInitial from '@/hooks/useInitail';
  6 +
  7 +interface EditModal {
  8 + visible?: boolean;
  9 + data?: ListResult;
  10 + title: string;
  11 +}
  12 +
  13 +export default function useStore() {
  14 + const pagination = usePagination(getListApi, { current: 1, pageSize: 10 });
  15 + const [editModal, setEditModal] = useState<EditModal>({visible: false, title: ''});
  16 + const { data: companyList } = useInitial(getUnitCompanyListApi, [], { types: '20' });
  17 + const { data: shopList } = useInitial(fetchShopListByRangeTypeApi, [], { bizTypeList: '1' });
  18 + const [delay, setDelay] = useState<boolean>(true);
  19 + const [selectItem, setSelectedItem] = useState<ListResult>();
  20 + const [selectKey, setSelectKey] = useState<React.Key[]>([]);
  21 + return {
  22 + ...pagination,
  23 + editModal,
  24 + setEditModal,
  25 + companyList,
  26 + shopList,
  27 + delay,
  28 + setDelay,
  29 + selectItem,
  30 + setSelectedItem,
  31 + selectKey,
  32 + setSelectKey
  33 + };
  34 +}
... ...
src/pages/order3/RebateCommission/subpages/LoanCondition/api.ts 0 → 100644
  1 +import { http } from '@/typing/http';
  2 +import request from '@/utils/request';
  3 +import { ORDER3 } from '@/utils/host';
  4 +import { PageParams } from '@/typing/common';
  5 +
  6 +type Page<T> = http.PromisePageResp<T>;
  7 +
  8 +export interface RequestParams extends PageParams {}
  9 +
  10 +export interface SaveParams {
  11 + id?: number; // id
  12 + financialProductsConfigId?: number; // 金融产品id
  13 + loanPeriods?: number; // 贷款期数
  14 + periodsJudgeCondition?: number; // 贷款期数判断条件(1:小于,2:小于等于,3:大于,4:大于等于)
  15 + loanAmount?: number; // 贷款金额
  16 + amountJudgeCondition?: number; // 贷款金额判断条件(1:小于,2:小于等于,3:大于,4:大于等于)
  17 + rebateType?: number; // 车贷银行返利类型(1:固定金额,2:按比例)
  18 + rebateValue?: number; // 车贷银行返利标准(根据返利类型确定)
  19 + applyCarList?: [
  20 + // 适用车系列表
  21 + {
  22 + brandId?: number; // 品牌id
  23 + brandName?: string; // 品牌名称
  24 + seriesId?: number; // 车系id
  25 + seriesName?: string; // 车系名称
  26 + applyCarType?: number; // 适用车辆类型(1:全部车系,2:全部车型,3,部分车型)
  27 + specId?: number; // 车型id
  28 + specName?: string; // 车型名称
  29 + },
  30 + ];
  31 + roleCommissionList?: RoleCommissionList[]; // 角色提成设置
  32 +}
  33 +
  34 +export interface ListResult {
  35 + id?: number; // id
  36 + financialProductsConfigId?: number; // 金融产品id
  37 + brandId?: number; // 品牌id
  38 + brandName?: string; // 品牌名称
  39 + loanPeriods?: number; // 贷款期数
  40 + periodsJudgeCondition?: number; // 贷款期数判断条件(1:小于,2:小于等于,3:大于,4:大于等于)
  41 + loanAmount?: number; // 贷款金额
  42 + amountJudgeCondition?: number; // 贷款金额判断条件(1:小于,2:小于等于,3:大于,4:大于等于)
  43 + rebateType?: number; // 车贷银行返利类型(1:固定金额,2:按比例)
  44 + rebateValue?: number; // 车贷银行返利值(根据返利类型确定)
  45 + applyCarList?: ApplyCarList[]; // 适用车辆
  46 + roleCommissionList?: RoleCommissionList[]; // 角色提成
  47 +}
  48 +
  49 +export interface RoleCommissionList {
  50 + roleCode?: string; // 角色编码
  51 + roleName?: string; // 角色名称
  52 + rebateType?: number; // 返利类型(1.固定金额2.按比例)
  53 + rebateValue?: number; // 返利值(根据返利类型确定)
  54 + commissionType?: number; // 提成类型(1:计提成,2:计附加值)
  55 +}
  56 +
  57 +export interface ApplyCarList {
  58 + configId?: number; // id
  59 + brandId?: number; // 品牌id
  60 + brandName?: string; // 品牌名称
  61 + seriesList?: SeriesList[]; // 车系列表
  62 +}
  63 +
  64 +interface SeriesList {
  65 + configId?: number; // id
  66 + seriesId?: number; // 车系id
  67 + seriesName?: string; // 车系名称
  68 + specList?: SpecList[]; // 车型列表
  69 +}
  70 +
  71 +interface SpecList {
  72 + configId?: number; // 补贴配置id
  73 + specId?: number; // 车型id
  74 + specName?: string; // 车型名称
  75 +}
  76 +
  77 +/**
  78 + * 查询金融贷款条件配置列表
  79 + * @param params
  80 + * @returns
  81 + */
  82 +export function getListApi(params: RequestParams): Page<ListResult> {
  83 + return request.get(`${ORDER3}/erp/financial/loan/config/condition/page`, { params });
  84 +}
  85 +
  86 +/**
  87 + * 查询金融贷款条件详情
  88 + * @param id
  89 + * @returns
  90 + */
  91 +export function getLoanConditionApi(id?: number): http.PromiseResp<ListResult> {
  92 + return request.get(`${ORDER3}/erp/financial/loan/config/condition/queryInfo`, { params: { id } });
  93 +}
  94 +
  95 +/**
  96 + * 新增、编辑金融贷款条件配置
  97 + * @param params
  98 + * @returns
  99 + */
  100 +export function saveConfigApi(params: SaveParams) {
  101 + return request.post(`${ORDER3}/erp/financial/loan/config/condition/saveOrUpdate`, params);
  102 +}
  103 +
  104 +/**
  105 + * 删除金融贷款条件配置
  106 + * @param id 配置id
  107 + * @returns
  108 + */
  109 +export function deleteConfigApi(params: {id?: number}) {
  110 + return request.post(`${ORDER3}/erp/financial/loan/config/condition/delete`, params, { contentType: 'form-urlencoded' });
  111 +}
  112 +
  113 +
  114 +export interface RoleList {
  115 + roleCode?: string; // 角色代码
  116 + roleName?: string; // 角色名称
  117 +}
  118 +/**
  119 + * 查询角色
  120 + * @param params
  121 + * @returns
  122 + */
  123 +export function getRoleListApi(): http.PromiseResp<RoleList[]> {
  124 + return request.get(`${ORDER3}/erp/financial/loan/config/condition/queryRole`);
  125 +}
... ...
src/pages/order3/RebateCommission/subpages/LoanCondition/components/CarModal.tsx 0 → 100644
  1 +import React, { useState, useEffect } from 'react';
  2 +import { Modal } from 'antd';
  3 +import CarTableTreeAuth from '@/components/CarTableTreeAuth';
  4 +import { ApplyCarList } from '../api';
  5 +import { handleCarTreeTransform } from '@/pages/order3/Common/util';
  6 +
  7 +interface CarModal {
  8 + visible: boolean,
  9 + data?: ApplyCarList[];
  10 +}
  11 +
  12 +interface Props {
  13 + carModal: CarModal;
  14 + setCarModal: (value: CarModal) => void;
  15 +}
  16 +
  17 +const ShowModal = ({ carModal, setCarModal }: Props) => {
  18 + const [carData, setCarData] = useState<any>([]);
  19 +
  20 + useEffect(() => {
  21 + if (carModal.visible) {
  22 + const data = handleCarTreeTransform(carModal.data || []);
  23 + setCarData(data);
  24 + }
  25 + }, [carModal.visible]);
  26 +
  27 + function handleCancel() {
  28 + setCarModal({ visible: false, data: undefined });
  29 + };
  30 +
  31 + return (
  32 + <Modal
  33 + destroyOnClose
  34 + forceRender
  35 + open={carModal.visible}
  36 + title="适用车辆"
  37 + maskClosable={false}
  38 + onCancel={handleCancel}
  39 + onOk={handleCancel}
  40 + footer={null}
  41 + >
  42 + <CarTableTreeAuth value={carData} disabled brandMultiple={false} />
  43 + </Modal>
  44 + );
  45 +};
  46 +export default ShowModal;
... ...
src/pages/order3/RebateCommission/subpages/LoanCondition/components/ConditionModal.tsx 0 → 100644
  1 +import React from 'react';
  2 +import { Modal, Table } from 'antd';
  3 +import { RoleCommissionList } from '../api';
  4 +import { CommissionTypeEnum } from '@/pages/order3/RebateCommission/entity';
  5 +
  6 +interface ConditionModal {
  7 + visible: boolean;
  8 + data?: RoleCommissionList[];
  9 +}
  10 +
  11 +interface Props {
  12 + conditionModal: ConditionModal;
  13 + setConditionModal: (value: ConditionModal) => void;
  14 +}
  15 +
  16 +const Index = ({conditionModal, setConditionModal }: Props) => {
  17 +
  18 + function handleCancel() {
  19 + setConditionModal({ visible: false, data: undefined });
  20 + }
  21 +
  22 + return (
  23 + <Modal footer={null} destroyOnClose forceRender open={conditionModal.visible} title="角色提成" maskClosable={false} onCancel={handleCancel} onOk={handleCancel}>
  24 + <Table rowKey="roleCode" pagination={false} dataSource={conditionModal.data || []}>
  25 + <Table.Column title="角色" dataIndex="roleName" />
  26 + <Table.Column
  27 + title="提成标准"
  28 + dataIndex="rebateType"
  29 + render={(_text, record: RoleCommissionList) => {
  30 + let str = '';
  31 + if (!_text) {
  32 + str = '--';
  33 + } else if (_text === 2) {
  34 + str = `贷款额*期数*${record.rebateValue}%`;
  35 + } else if (_text === 1) {
  36 + str = `${record.rebateValue ?? '--'}元`;
  37 + }
  38 + return str;
  39 + }}
  40 + />
  41 + <Table.Column title="提成类型" dataIndex="commissionType" render={(_text) => (_text && CommissionTypeEnum[_text]) || '--'} />
  42 + </Table>
  43 + </Modal>
  44 + );
  45 +};
  46 +export default Index;
... ...
src/pages/order3/RebateCommission/subpages/LoanCondition/components/List.tsx 0 → 100644
  1 +import React, { useState } from 'react';
  2 +import { message, Popconfirm, Space, Table, Typography, Divider } from 'antd';
  3 +import * as api from '../api';
  4 +import CarModal from './CarModal';
  5 +import ConditionModal from './ConditionModal';
  6 +import { history } from 'umi';
  7 +import { ConditionEnum } from '@/pages/order3/RebateCommission/entity';
  8 +import TextWithMore from '@/components/TextWithMore';
  9 +
  10 +interface CarDetailModal {
  11 + visible: boolean;
  12 + data?: api.ApplyCarList[];
  13 +}
  14 +
  15 +interface ConditionDetailModal {
  16 + visible: boolean;
  17 + data?: api.RoleCommissionList[];
  18 +}
  19 +
  20 +interface Props {
  21 + id?: number;
  22 + list?: api.ListResult[]
  23 + paginationConfig: any;
  24 + loading: boolean;
  25 + setLoading: (bool: boolean) => void;
  26 +}
  27 +
  28 +const Column = Table.Column;
  29 +
  30 +export default function List({ id, list, paginationConfig, loading, setLoading }: Props) {
  31 + const [pageLoading, setPageLoading] = useState<boolean>(false);
  32 + const [carModal, setCarModal] = useState<CarDetailModal>({ visible: false, data: undefined });
  33 + const [conditionModal, setConditionModal] = useState<ConditionDetailModal>({ visible: false, data: undefined });
  34 +
  35 + /**
  36 + * 删除配置
  37 + * @param id
  38 + */
  39 + function onDelete(configId?: number) {
  40 + setPageLoading(true);
  41 + api
  42 + .deleteConfigApi({ id: configId })
  43 + .then((res) => {
  44 + setPageLoading(false);
  45 + setLoading(true);
  46 + message.success(res.result);
  47 + })
  48 + .catch((e) => {
  49 + setPageLoading(false);
  50 + message.error(e.message);
  51 + });
  52 + }
  53 +
  54 + function onEdit(record: api.ListResult, type: number) {
  55 + history.push(`/order3/rebateCommission/loanCondition/edit?financialProductsConfigId=${id}&id=${record.id}&type=${type}`);
  56 + }
  57 +
  58 + function handleBrand(value?: api.ApplyCarList[]) {
  59 + const brand = value?.map((v) => ({ brandId: v.brandId, brandName: v.brandName }));
  60 + return brand || [];
  61 + }
  62 +
  63 + return (
  64 + <div>
  65 + <Table dataSource={list} pagination={paginationConfig} loading={loading || pageLoading} rowKey="id" scroll={{ x: 1000 }}>
  66 + <Column
  67 + title="品牌"
  68 + dataIndex="brandName"
  69 + render={(_text, record: api.ListResult) => <TextWithMore dataIndex="brandName" list={handleBrand(record.applyCarList)} unit="个品牌" />}
  70 + />
  71 + <Column
  72 + title="适用车辆"
  73 + align="center"
  74 + dataIndex="applyCarList"
  75 + render={(_text) => (
  76 + <span style={{ color: '#4189FD' }} onClick={() => setCarModal({ visible: true, data: _text })}>
  77 + 查看
  78 + </span>
  79 + )}
  80 + />
  81 + <Column
  82 + title="分期条件"
  83 + align="center"
  84 + dataIndex="loanPeriods"
  85 + render={(_text, record: api.ListResult) => (
  86 + <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start' }}>
  87 + <span>
  88 + 分期期数{(record.periodsJudgeCondition && ConditionEnum[record.periodsJudgeCondition]) || '--'}
  89 + {record.loanPeriods}期;
  90 + </span>
  91 + <span>
  92 + 贷款额{(record.amountJudgeCondition && ConditionEnum[record.amountJudgeCondition]) || '--'}
  93 + {record.loanAmount}元
  94 + </span>
  95 + </div>
  96 + )}
  97 + />
  98 + <Column
  99 + title="车贷银行返利标准"
  100 + align="center"
  101 + dataIndex="rebateType"
  102 + render={(_text, record: api.ListResult) => {
  103 + if (_text === 1) {
  104 + return `${record.rebateValue}元/单`;
  105 + } else if (_text === 2) {
  106 + return `${record.rebateValue}%`;
  107 + }
  108 + return '--';
  109 + }}
  110 + />
  111 + <Column
  112 + title="角色提成"
  113 + align="center"
  114 + dataIndex="roleCommissionList"
  115 + render={(_text) => (
  116 + <span style={{ color: '#4189FD' }} onClick={() => setConditionModal({ visible: true, data: _text })}>
  117 + 查看
  118 + </span>
  119 + )}
  120 + />
  121 +
  122 + <Column
  123 + title="操作"
  124 + dataIndex="option"
  125 + align="center"
  126 + fixed="right"
  127 + render={(_text, record: api.ListResult) => (
  128 + <Space wrap split={<Divider type="vertical" />}>
  129 + <Popconfirm title="确定删除?" onConfirm={() => onDelete(record.id)}>
  130 + <Typography.Link style={{ color: '#666' }}>删除</Typography.Link>
  131 + </Popconfirm>
  132 + <Typography.Link onClick={() => onEdit(record, 2)} style={{ color: '#4189FD' }}>
  133 + 复制
  134 + </Typography.Link>
  135 + <Typography.Link onClick={() => onEdit(record, 1)} style={{ color: '#4189FD' }}>
  136 + 编辑
  137 + </Typography.Link>
  138 + </Space>
  139 + )}
  140 + />
  141 + </Table>
  142 + <CarModal carModal={carModal} setCarModal={setCarModal} />
  143 + <ConditionModal conditionModal={conditionModal} setConditionModal={setConditionModal} />
  144 + </div>
  145 + );
  146 +}
... ...
src/pages/order3/RebateCommission/subpages/LoanCondition/index.tsx 0 → 100644
  1 +import { Button, Card, Input, Row, Space } from 'antd';
  2 +import React, { useEffect, useState } from 'react';
  3 +import { PlusOutlined } from '@ant-design/icons';
  4 +import List from './components/List';
  5 +import { history } from 'umi';
  6 +import { getListApi } from '@/pages/order3/RebateCommission/subpages/LoanCondition/api';
  7 +import usePagination from '@/hooks/usePagination';
  8 +
  9 +interface Props {
  10 + id?: number; // id
  11 + financialCompanyName?: string; // 金融公司名称
  12 + productName?: string; // 产品名称
  13 +}
  14 +
  15 +const Index = ({ id, financialCompanyName, productName }: Props) => {
  16 + const [delay, setDelay] = useState<boolean>(true);
  17 + const { list, paginationConfig, loading, setLoading, setParams, innerParams } = usePagination(getListApi, { current: 1, pageSize: 10, financialProductsConfigId: id }, {delay});
  18 +
  19 + useEffect(() => {
  20 + if (id) {
  21 + setParams({ ...innerParams, financialProductsConfigId: id }, true);
  22 + setDelay(false);
  23 + }
  24 + }, [id])
  25 +
  26 + function onFinancialSearch(value?: string) {
  27 + setParams({ ...innerParams, current: 1, financialCompanyName: value }, true);
  28 + }
  29 +
  30 + function onAdd() {
  31 + history.push(`/order3/rebateCommission/loanCondition/edit?financialProductsConfigId=${id}&type=1`);
  32 + }
  33 +
  34 + return (
  35 + <Card style={{backgroundColor: '#f4f4f4'}}>
  36 + <Row style={{ marginBottom: '15px' }}>
  37 + <span style={{fontSize: '16px', color: '#333', fontWeight: '600'}}>
  38 + 当前选择:{financialCompanyName},{productName}
  39 + </span>
  40 + </Row>
  41 + <Row justify="end" style={{ marginBottom: '15px' }}>
  42 + <Button onClick={onAdd} type="primary">
  43 + <PlusOutlined /> 新增
  44 + </Button>
  45 + </Row>
  46 + <List list={list} paginationConfig={paginationConfig} loading={loading} setLoading={setLoading} id={id} />
  47 + </Card>
  48 + );
  49 +}
  50 +
  51 +export default Index;
... ...
src/pages/order3/RebateCommission/subpages/LoanCondition/subpages/LoanConditionEdit/components/EditModal.tsx 0 → 100644
  1 +import React, { useEffect } from 'react';
  2 +import { Button, Card, Form, Input, Modal, Radio, Row, Select, Space } from 'antd';
  3 +import { debounce } from 'lodash';
  4 +import { RoleCommissionList, RoleList } from '../../../api';
  5 +import { EditModal } from '../index';
  6 +
  7 +interface Props {
  8 + editStatus: EditModal;
  9 + setEditStatus: (value: EditModal) => void;
  10 + roleCommissionList: RoleCommissionList[];
  11 + setRoleCommissionList: (value: RoleCommissionList[]) => void;
  12 + roleList?: RoleList[];
  13 +}
  14 +
  15 +export default function Index({ editStatus, setEditStatus, roleCommissionList, setRoleCommissionList, roleList }: Props) {
  16 + const [form] = Form.useForm();
  17 +
  18 + useEffect(() => {
  19 + if (editStatus.visible && editStatus.data?.roleCode) {
  20 + form.setFieldsValue({
  21 + role: { label: editStatus.data.roleName, value: editStatus.data.roleCode },
  22 + rebateType: editStatus.data.rebateType,
  23 + rebateValue: editStatus.data.rebateValue,
  24 + commissionType: editStatus.data.commissionType,
  25 + });
  26 + }
  27 + }, [editStatus.visible, editStatus.data?.roleCode])
  28 +
  29 + function handleCancle() {
  30 + form.resetFields();
  31 + setEditStatus({visible: false, data: undefined});
  32 + }
  33 +
  34 + function handleRoleList() {
  35 + const codeList = roleCommissionList?.map(v => v.roleCode);
  36 + const _roleList = JSON.parse(JSON.stringify(roleList || []));
  37 + const data = _roleList.map((v: any) => {
  38 + if (codeList?.includes(v.roleCode)) {
  39 + v.disabled = true;
  40 + }
  41 + return {...v, label: v.roleName, value: v.roleCode}
  42 + });
  43 + return data || [];
  44 + }
  45 +
  46 + async function handleSubmit() {
  47 + const value = await form.validateFields();
  48 + const list = roleCommissionList || [];
  49 + if (editStatus.data?.roleCode) {
  50 + const result = list.map((v: RoleCommissionList) => {
  51 + const item = {...v};
  52 + if (editStatus.data?.roleCode == v.roleCode) {
  53 + item.roleCode = value?.role?.value;
  54 + item.roleName = value?.role?.label;
  55 + item.commissionType = value.commissionType;
  56 + item.rebateType = value.rebateType;
  57 + item.rebateValue = value.rebateValue;
  58 + }
  59 + return {...item};
  60 + })
  61 + setRoleCommissionList(result || []);
  62 + } else {
  63 + const item = {
  64 + roleCode: value?.role?.value,
  65 + roleName: value?.role?.label,
  66 + commissionType: value.commissionType,
  67 + rebateType: value.rebateType,
  68 + rebateValue: value.rebateValue,
  69 + }
  70 + setRoleCommissionList([...roleCommissionList, item]);
  71 + }
  72 + handleCancle();
  73 + }
  74 +
  75 + return (
  76 + <Modal
  77 + title={editStatus.data?.roleCode ? '编辑角色提成' : '新增角色提成'}
  78 + open={editStatus.visible}
  79 + onCancel={handleCancle}
  80 + width="50%"
  81 + maskClosable={false}
  82 + footer={false}
  83 + destroyOnClose
  84 + afterClose={() => form.resetFields()}
  85 + >
  86 + <Card bordered={false}>
  87 + <Form form={form}>
  88 + <Form.Item label="角色" name="role" rules={[{ required: true, message: '请输入' }]}>
  89 + <Select placeholder="请选择" labelInValue filterOption optionFilterProp="children" options={handleRoleList()} />
  90 + </Form.Item>
  91 +
  92 + <Form.Item label="提成类型" name="rebateType" rules={[{ required: true, message: '请输入' }]}>
  93 + <Radio.Group onChange={() => form.setFieldValue('rebateValue', undefined)}>
  94 + <Radio value={1}>固定金额</Radio>
  95 + <Radio value={2}>固定比例</Radio>
  96 + </Radio.Group>
  97 + </Form.Item>
  98 + <Form.Item noStyle shouldUpdate>
  99 + {({ getFieldValue }) => {
  100 + const value = getFieldValue('rebateType');
  101 + if (!value) {
  102 + return null;
  103 + }
  104 + return value == 1 ? (
  105 + <Form.Item
  106 + label="提成标准"
  107 + name="rebateValue"
  108 + rules={[
  109 + {
  110 + required: true,
  111 + pattern: /^(\d+|\d+\.\d{1,2})$/,
  112 + message: '请输入正确的提成标准',
  113 + },
  114 + ]}
  115 + >
  116 + <Input placeholder="请输入" suffix="元" />
  117 + </Form.Item>
  118 + ) : (
  119 + <Form.Item
  120 + label="提成标准"
  121 + name="rebateValue"
  122 + rules={[
  123 + {
  124 + required: true,
  125 + pattern: /^(\d+|\d+\.\d{1,4})$/,
  126 + message: '请输入正确的提成标准',
  127 + },
  128 + ({ getFieldValue }) => ({
  129 + validator(_, value) {
  130 + if (value && +value <= 100 && +value >= 0) {
  131 + return Promise.resolve();
  132 + }
  133 + return Promise.reject(new Error('比例范围应在0~100!'));
  134 + },
  135 + }),
  136 + ]}
  137 + >
  138 + <Input placeholder="请输入" suffix="%" />
  139 + </Form.Item>
  140 + );
  141 + }}
  142 + </Form.Item>
  143 + <Form.Item label="计提成类型" name="commissionType" rules={[{ required: true, message: '请输入' }]}>
  144 + <Radio.Group>
  145 + <Radio value={1}>计提成</Radio>
  146 + <Radio value={2}>计附加值</Radio>
  147 + </Radio.Group>
  148 + </Form.Item>
  149 + </Form>
  150 +
  151 + <Row style={{ marginTop: '30px' }} justify="center">
  152 + <Space>
  153 + <Button onClick={handleCancle} type="default">
  154 + 取消
  155 + </Button>
  156 + <Button onClick={debounce(handleSubmit, 380)} type="primary">
  157 + 确定
  158 + </Button>
  159 + </Space>
  160 + </Row>
  161 + </Card>
  162 + </Modal>
  163 + );
  164 +}
... ...
src/pages/order3/RebateCommission/subpages/LoanCondition/subpages/LoanConditionEdit/index.tsx 0 → 100644
  1 +import React, { useState, useEffect } from 'react';
  2 +import { PageHeaderWrapper } from '@ant-design/pro-layout';
  3 +import { Button, Card, Col, Input, Row, Typography, Form, Space, Select, Divider, InputNumber, Radio, Table, message, Popconfirm } from 'antd';
  4 +import { debounce } from 'lodash';
  5 +import { PlusOutlined, PlusSquareOutlined } from '@ant-design/icons';
  6 +import { ConditionData } from '@/pages/order3/RebateCommission/entity';
  7 +import { LoanPeriod } from '@/pages/order3/Common/entity';
  8 +import EditModal from './components/EditModal';
  9 +import { RoleCommissionList, getRoleListApi, getLoanConditionApi, saveConfigApi, ListResult } from '../../api';
  10 +import { getFinancialDetailApi } from '@/pages/order3/RebateCommission/api';
  11 +import useInitial from '@/hooks/useInitail';
  12 +import { history } from 'umi';
  13 +import SelectorWithFull from '@/components/SelectorWithFull';
  14 +import CarTableTreeAuth from '@/components/CarTableTreeAuth';
  15 +import { getUnitCompanyListApi, fetchShopListByRangeTypeApi } from '@/common/api';
  16 +import { CommissionTypeEnum } from '@/pages/order3/RebateCommission/entity';
  17 +import { handleCarTreeFlat, handleCarTreeTransform } from '@/pages/order3/Common/util';
  18 +
  19 +export interface EditModalSatus {
  20 + visible: boolean;
  21 + data?: RoleCommissionList;
  22 +}
  23 +
  24 +interface State {
  25 + id?: number;
  26 + financialProductsConfigId?: number;
  27 + type?: number,
  28 +}
  29 +
  30 +export default function Index() {
  31 + const { id, financialProductsConfigId, type } = history.location.query as State;
  32 + const [form] = Form.useForm();
  33 + const [roleCommissionList, setRoleCommissionList] = useState<RoleCommissionList[]>([]);
  34 + const [roleModal, setRoleModal] = useState<EditModalSatus>({ visible: false });
  35 + const { data: roleList } = useInitial(getRoleListApi, [], null);
  36 + const [loading, setLoading] = useState<boolean>(false);
  37 + const { data: companyList } = useInitial(getUnitCompanyListApi, [], { types: '20' });
  38 + const { data: shopList } = useInitial(fetchShopListByRangeTypeApi, [], { bizTypeList: '1' });
  39 + const { data: product, loading: productLoading } = useInitial(getFinancialDetailApi, {}, financialProductsConfigId);
  40 + const [cofrimLoading, setConfirmLoading] = useState<boolean>(false);
  41 +
  42 + useEffect(() => {
  43 + if (product?.id) {
  44 + form.setFieldsValue({
  45 + financialCompany: { label: product?.financialCompanyName, value: product?.financialCompanyId },
  46 + productName: product?.productName,
  47 + loanRate: product?.loanRate,
  48 + applyShopType: product?.applyShopType,
  49 + shopList: product?.applyShopList?.map((v) => ({ id: v.shopId, name: v.shopName })),
  50 + });
  51 + }
  52 + }, [product])
  53 +
  54 + useEffect(() => {
  55 + // initial();
  56 + if (id) {
  57 + setLoading(true);
  58 + getLoanConditionApi(id)
  59 + .then(res => {
  60 + const financial = res.data || {};
  61 + form.setFieldsValue({
  62 + loanPeriods: financial.loanPeriods,
  63 + periodsJudgeCondition: financial.periodsJudgeCondition,
  64 + loanAmount: financial.loanAmount,
  65 + amountJudgeCondition: financial.amountJudgeCondition,
  66 + rebateType: financial.rebateType,
  67 + rebateValue: financial.rebateValue,
  68 + optionalAuth: handleCarTreeTransform(financial.applyCarList || []),
  69 + });
  70 + setRoleCommissionList(financial.roleCommissionList || []);
  71 + setLoading(false);
  72 + })
  73 + .catch(e => {
  74 + message.error(e.message);
  75 + setLoading(false);
  76 + })
  77 + .finally(() => {
  78 + setLoading(false);
  79 + })
  80 + }
  81 + }, []);
  82 +
  83 + const initial = async () => {
  84 + try {
  85 + setLoading(true);
  86 + const { data: product } = await getFinancialDetailApi(financialProductsConfigId);
  87 + let financial: ListResult = {};
  88 + if (id) {
  89 + const _data = await getLoanConditionApi(id);
  90 + financial = _data?.data || {};
  91 + }
  92 + form.setFieldsValue({
  93 + financialCompany: { label: product?.financialCompanyName, value: product?.financialCompanyId },
  94 + productName: product?.productName,
  95 + loanRate: product?.loanRate,
  96 + applyShopType: product?.applyShopType,
  97 + shopList: product?.applyShopList?.map((v) => ({ id: v.shopId, name: v.shopName })),
  98 + loanPeriods: financial.loanPeriods,
  99 + periodsJudgeCondition: financial.periodsJudgeCondition,
  100 + loanAmount: financial.loanAmount,
  101 + amountJudgeCondition: financial.amountJudgeCondition,
  102 + rebateType: financial.rebateType,
  103 + rebateValue: financial.rebateValue,
  104 + optionalAuth: handleCarTreeTransform(financial.applyCarList || []),
  105 + });
  106 + setRoleCommissionList(financial.roleCommissionList || []);
  107 + setLoading(false);
  108 + } catch (e: any) {
  109 + setLoading(false);
  110 + message.error(e.message);
  111 + }
  112 + }
  113 +
  114 + function onBack() {
  115 + history.goBack();
  116 + }
  117 +
  118 + /**
  119 + * 添加提成角色设置
  120 + */
  121 + function onAddRole() {
  122 + setRoleModal({ visible: true, data: undefined });
  123 + }
  124 + /**
  125 + * 删除提成角色设置
  126 + * @param code
  127 + */
  128 + function onDeleteRoleCommission(code?: string) {
  129 + const list = roleCommissionList.filter(v => v.roleCode !== code);
  130 + setRoleCommissionList(list || []);
  131 + }
  132 +
  133 + /**
  134 + * 编辑提成角色设置
  135 + * @param record
  136 + */
  137 + function onEditRoleCommission(record?: RoleCommissionList) {
  138 + setRoleModal({visible: true, data: record});
  139 + }
  140 +
  141 + async function onSure() {
  142 + const value = await form.validateFields();
  143 + const params = {
  144 + loanPeriods: value.loanPeriods,
  145 + periodsJudgeCondition: value.periodsJudgeCondition,
  146 + loanAmount: value.loanAmount,
  147 + amountJudgeCondition: value.amountJudgeCondition,
  148 + rebateType: value.rebateType,
  149 + rebateValue: value.rebateValue,
  150 + applyCarList: handleCarTreeFlat(value.optionalAuth || []),
  151 + roleCommissionList,
  152 + id: type == 1 ? id : undefined,
  153 + financialProductsConfigId,
  154 + };
  155 + setConfirmLoading(true);
  156 + saveConfigApi(params)
  157 + .then(res => {
  158 + message.success(res.result);
  159 + history.goBack();
  160 + })
  161 + .catch(e => {
  162 + message.error(e.message);
  163 + setConfirmLoading(false);
  164 + })
  165 + .finally(() => {
  166 + setConfirmLoading(false);
  167 + })
  168 + }
  169 +
  170 + return (
  171 + <PageHeaderWrapper title="车贷银行返利和提成配置">
  172 + <Card loading={loading || productLoading}>
  173 + <Form form={form}>
  174 + <Row justify="center">
  175 + <Col span={14}>
  176 + <Form.Item label="车贷银行" name="financialCompany">
  177 + <Select
  178 + labelInValue
  179 + showSearch
  180 + allowClear
  181 + disabled
  182 + filterOption
  183 + optionFilterProp="label"
  184 + options={companyList.map((v) => ({ label: v.name, value: v.id }))}
  185 + placeholder="请选择"
  186 + />
  187 + </Form.Item>
  188 +
  189 + <Form.Item label="金融产品" name="productName">
  190 + <Input disabled placeholder="请输入" />
  191 + </Form.Item>
  192 +
  193 + <Form.Item label="适用门店类型" name="applyShopType">
  194 + <Radio.Group disabled>
  195 + <Radio value={1}>全部门店</Radio>
  196 + <Radio value={2}>部分门店</Radio>
  197 + </Radio.Group>
  198 + </Form.Item>
  199 +
  200 + <Form.Item noStyle shouldUpdate={(prevValues, currentValues) => prevValues.applyShopType != currentValues.applyShopType}>
  201 + {({ getFieldValue }) => {
  202 + const value = getFieldValue('applyShopType');
  203 + if (value !== 2) {
  204 + return;
  205 + }
  206 + return (
  207 + <Form.Item label="适用门店" name="shopList">
  208 + <SelectorWithFull
  209 + placeholder="请选择"
  210 + multiple
  211 + disabled
  212 + labelInValue
  213 + data={shopList}
  214 + fieldKeyNames={{ keyName: 'id', valueName: 'id', labelName: 'name' }}
  215 + />
  216 + </Form.Item>
  217 + );
  218 + }}
  219 + </Form.Item>
  220 +
  221 + <Form.Item
  222 + label="促销利率"
  223 + name="loanRate"
  224 + >
  225 + <Input disabled placeholder="请输入" style={{ width: '100%' }} suffix="%" />
  226 + </Form.Item>
  227 +
  228 + <Form.Item
  229 + name="optionalAuth"
  230 + label="适用车辆"
  231 + rules={[{ required: true, message: '选择车辆' }]}
  232 + tooltip={
  233 + <span style={{ color: '#ccc', fontSize: 12 }}>
  234 + *点击图标
  235 + <PlusSquareOutlined />
  236 + 展开详情
  237 + </span>
  238 + }
  239 + >
  240 + <CarTableTreeAuth />
  241 + </Form.Item>
  242 + </Col>
  243 + </Row>
  244 +
  245 + <Divider orientation="left">分期规则</Divider>
  246 + <Row justify="center">
  247 + <Col span={14}>
  248 + <Card headStyle={{ backgroundColor: '#eee', color: '#4189FD', fontSize: 14 }} title="分期条件">
  249 + <Form.Item>
  250 + <Form.Item label="分期期数" style={{ marginBottom: 0 }} required>
  251 + <Form.Item
  252 + name="periodsJudgeCondition"
  253 + rules={[{ required: true, message: '请选择' }]}
  254 + style={{ display: 'inline-block', width: 'calc(30% - 8px)' }}
  255 + >
  256 + <Select placeholder="请选择" allowClear options={ConditionData} />
  257 + </Form.Item>
  258 + <Form.Item
  259 + name="loanPeriods"
  260 + rules={[{ required: true, message: '请选择' }]}
  261 + style={{ display: 'inline-block', width: 'calc(30% - 8px)', margin: '0 8px' }}
  262 + >
  263 + <Select placeholder="请选择" allowClear options={LoanPeriod} />
  264 + </Form.Item>
  265 + </Form.Item>
  266 +
  267 + <Form.Item label="贷款额度" style={{ marginBottom: 0 }} required>
  268 + <Form.Item
  269 + name="amountJudgeCondition"
  270 + rules={[{ required: true, message: '请选择' }]}
  271 + style={{ display: 'inline-block', width: 'calc(30% - 8px)' }}
  272 + >
  273 + <Select placeholder="请选择" allowClear options={ConditionData} />
  274 + </Form.Item>
  275 + <Form.Item
  276 + name="loanAmount"
  277 + rules={[
  278 + {
  279 + required: true,
  280 + pattern: /^(\d+|\d+\.\d{1,2})$/,
  281 + message: '请输入正确的金额',
  282 + },
  283 + ]}
  284 + style={{ display: 'inline-block', width: 'calc(30% - 8px)', margin: '0 8px' }}
  285 + >
  286 + <Input style={{ width: '100%' }} suffix="元" placeholder="请输入" />
  287 + </Form.Item>
  288 + </Form.Item>
  289 +
  290 + <Form.Item label="返利类型" name="rebateType" rules={[{ required: true, message: '请选择' }]}>
  291 + <Radio.Group onChange={() => form.setFieldValue('rebateValue', undefined)}>
  292 + <Radio value={1}>固定金额</Radio>
  293 + <Radio value={2}>固定比例</Radio>
  294 + </Radio.Group>
  295 + </Form.Item>
  296 +
  297 + <Form.Item
  298 + noStyle
  299 + shouldUpdate={(prevValues, currValues) => {
  300 + return prevValues.rebateType !== currValues.rebateType;
  301 + }}
  302 + >
  303 + {({ getFieldValue }) => {
  304 + const value = getFieldValue('rebateType');
  305 + if (!value) {
  306 + return;
  307 + }
  308 + return value == 1 ? (
  309 + <Form.Item label="返利标准" style={{ marginBottom: 0 }} required>
  310 + <Form.Item
  311 + name="rebateValue"
  312 + rules={[
  313 + {
  314 + required: true,
  315 + pattern: /^(\d+|\d+\.\d{1,2})$/,
  316 + message: '请输入正确的返利标准',
  317 + },
  318 + ]}
  319 + style={{ display: 'inline-block', width: 'calc(30% - 8px)' }}
  320 + >
  321 + <Input style={{ width: '100%' }} placeholder="请输入" suffix="元/单" />
  322 + </Form.Item>
  323 + </Form.Item>
  324 + ) : (
  325 + <Form.Item label="返利标准" style={{ marginBottom: 0 }} required>
  326 + <Form.Item
  327 + name="rebateValue"
  328 + rules={[
  329 + {
  330 + required: true,
  331 + pattern: /^(\d+|\d+\.\d{1,2})$/,
  332 + message: '请输入正确的返利标准',
  333 + },
  334 + ({ getFieldValue }) => ({
  335 + validator(_, value) {
  336 + if (value && +value <= 100 && +value >= 0) {
  337 + return Promise.resolve();
  338 + }
  339 + return Promise.reject(new Error('比例范围应在0~100!'));
  340 + },
  341 + }),
  342 + ]}
  343 + style={{ display: 'inline-block', width: 'calc(30% - 8px)' }}
  344 + >
  345 + <Input style={{ width: '100%' }} placeholder="请输入" suffix="%" />
  346 + </Form.Item>
  347 + </Form.Item>
  348 + );
  349 + }}
  350 + </Form.Item>
  351 +
  352 + <div style={{ width: '100%' }}>
  353 + <Row style={{ marginBottom: '10px' }} justify="space-between">
  354 + <span>角色提成设置:</span>
  355 + <Typography.Link style={{ color: '#4189FD' }} onClick={onAddRole}>
  356 + <PlusOutlined />
  357 + 添加角色
  358 + </Typography.Link>
  359 + </Row>
  360 + <Table pagination={false} dataSource={roleCommissionList} rowKey="roleCode">
  361 + <Table.Column title="角色" dataIndex="roleName" />
  362 + <Table.Column
  363 + title="提成标准"
  364 + dataIndex="rebateType"
  365 + render={(_text, record: RoleCommissionList) => {
  366 + let str = '';
  367 + if (!_text) {
  368 + str = '--';
  369 + } else if (_text === 2) {
  370 + str = `贷款额*期数*${record.rebateValue}%`;
  371 + } else if (_text === 1) {
  372 + str = `${record.rebateValue ?? '--'}元`;
  373 + }
  374 + return str;
  375 + }}
  376 + />
  377 + <Table.Column title="提成类型" dataIndex="commissionType" render={(_text) => (_text && CommissionTypeEnum[_text]) || '--'} />
  378 + <Table.Column
  379 + title="操作"
  380 + dataIndex="option"
  381 + render={(_text, record: RoleCommissionList) => (
  382 + <Space>
  383 + <Popconfirm title="确定删除?" onConfirm={() => onDeleteRoleCommission(record.roleCode)}>
  384 + <Typography.Link style={{ color: '#999' }}>删除</Typography.Link>
  385 + </Popconfirm>
  386 + <Typography.Link onClick={() => onEditRoleCommission(record)} style={{ color: '#4189FD' }}>
  387 + 编辑
  388 + </Typography.Link>
  389 + </Space>
  390 + )}
  391 + />
  392 + </Table>
  393 + </div>
  394 + </Form.Item>
  395 + </Card>
  396 + </Col>
  397 + </Row>
  398 + </Form>
  399 +
  400 + <EditModal
  401 + roleCommissionList={roleCommissionList}
  402 + setRoleCommissionList={setRoleCommissionList}
  403 + editStatus={roleModal}
  404 + setEditStatus={setRoleModal}
  405 + roleList={roleList}
  406 + />
  407 + <Row style={{ marginTop: '30px' }} justify="center">
  408 + <Space>
  409 + <Button loading={cofrimLoading} onClick={onBack} type="default">
  410 + 取消
  411 + </Button>
  412 + <Button loading={cofrimLoading} onClick={debounce(onSure, 380)} type="primary">
  413 + 确定
  414 + </Button>
  415 + </Space>
  416 + </Row>
  417 + </Card>
  418 + </PageHeaderWrapper>
  419 + );
  420 +}
0 421 \ No newline at end of file
... ...
src/pages/performance/DataImport/api.ts 0 → 100644
  1 +import request from '@/utils/request';
  2 +import { MORAX_HOST } from '@/utils/host';
  3 +import type { Page } from '@/typing/common';
  4 +import type { Data } from 'ahooks/lib/useAntdTable/types';
  5 +
  6 +/**
  7 + * 展厅美化导入记录分页查询
  8 + * http://testgate.feewee.cn/morax/erp/eval-indicator-import/showroom-page
  9 + */
  10 +export async function DataImportAPi(params: MDataImport.DataImportListParams) {
  11 + const res = await request.get<Page<MDataImport.DataImportList>>(`${MORAX_HOST}/erp/eval-indicator-import/showroom-page`, {
  12 + params,
  13 + });
  14 + const data = res?.data || { total: 0, list: [], data: [] };
  15 + if (data) {
  16 + const d = data.data || [];
  17 + data.data = d;
  18 + data.list = d;
  19 + }
  20 + return data as Data;
  21 +}
  22 +
  23 +/**
  24 + * 数据导入指标列表
  25 + * http://testgate.feewee.cn/morax/erp/eval-indicator-import/indicators
  26 + */
  27 +export async function IndicatorListApi() {
  28 + const res = await request.get<MDataImport.IndicatorList[]>(`${MORAX_HOST}/erp/eval-indicator-import/indicators`);
  29 + return res.data ?? [];
  30 +}
  31 +
  32 +/**
  33 + * 展厅美化导入清单查询
  34 + * http://testgate.feewee.cn/morax/erp/eval-indicator-import/showroom-detail
  35 + */
  36 +
  37 +export async function DetailedListAPi(params: MDataImport.DataImportListParams) {
  38 + const res = await request.get<Page<MDataImport.DetailedList>>(`${MORAX_HOST}/erp/eval-indicator-import/showroom-detail`, {
  39 + params,
  40 + });
  41 + const data = res?.data || { total: 0, list: [], data: [] };
  42 + if (data) {
  43 + const d = data.data || [];
  44 + data.data = d;
  45 + data.list = d;
  46 + }
  47 + return data as Data;
  48 +}
  49 +/**
  50 + * 展厅美化导入记录详情查询
  51 + * http://testgate.feewee.cn/morax/erp/eval-indicator-import/showroom-import-detail
  52 + */
  53 +
  54 +export async function ImportListDetailApi(id?: number) {
  55 + const res = await request.get<MDataImport.DataImportList>(`${MORAX_HOST}/erp/eval-indicator-import/showroom-import-detail`, { params: { id } });
  56 + return res.data ?? {};
  57 +}
  58 +
  59 +/**
  60 + * 上传展厅美化数据
  61 + * http://testgate.feewee.cn/morax/erp/eval-indicator-import/analysis/showroom-shop
  62 + */
  63 +
  64 +export async function UploadImportApi(fid?: string, indicatorCode?: string) {
  65 + const res = await request.get<MDataImport.DataImportList>(`${MORAX_HOST}/erp/eval-indicator-import/analysis/showroom-shop`, {
  66 + params: { fid, indicatorCode },
  67 + });
  68 + return res.data ?? {};
  69 +}
  70 +
  71 +/**
  72 + * 下载展厅美化数据
  73 + * http://testgate.feewee.cn/morax/erp/eval-indicator-import/template-file
  74 + */
  75 +
  76 +export async function DownloadApi( indicatorCode?: string) {
  77 + const res = await request.get<any>(`${MORAX_HOST}/erp/eval-indicator-import/template-file`, {
  78 + params: { indicatorCode },
  79 + });
  80 + return res.data ?? {};
  81 +}
  82 +
  83 +/**
  84 + * 删除导入审批
  85 + * http://testgate.feewee.cn/morax/erp/eval-indicator/del-import
  86 + */
  87 +export function deleteApi(params: { id: number }) {
  88 + return request.get(`${MORAX_HOST}/erp/eval-indicator/del-import`, { params });
  89 +}
  90 +/**
  91 + * 撤销审批
  92 + * http://testgate.feewee.cn/morax/erp/eval-indicator/cancel
  93 + */
  94 +export function revokeApi(params: { id: number }) {
  95 + return request.get(`${MORAX_HOST}/erp/eval-indicator/cancel`, { params });
  96 +}
  97 +
  98 +/**
  99 + * 保存展厅美化数据
  100 + * http://testgate.feewee.cn/morax/erp/eval-indicator-import/save-showroom-import
  101 + */
  102 +export function SaveDataApi(params: { key: string }) {
  103 + return request.get(`${MORAX_HOST}/erp/eval-indicator-import/save-showroom-import`, { params });
  104 +}
0 105 \ No newline at end of file
... ...
src/pages/performance/DataImport/components/DetailList.tsx 0 → 100644
  1 +import React, { useEffect, useState } from 'react';
  2 +import { Table, message, DatePicker, Select } from 'antd';
  3 +import type { ColumnsType } from 'antd/es/table';
  4 +import type { DatePickerProps } from 'antd';
  5 +import { DetailedListAPi } from '../api';
  6 +import { useAntdTable } from 'ahooks';
  7 +import moment from 'moment';
  8 +import useInitial from '@/hooks/useInitail';
  9 +import { getShopApi } from '@/common/api';
  10 +
  11 +type Props = {
  12 + indicator?: MDataImport.IndicatorList;
  13 + onChange?: (value?: any) => void;
  14 +};
  15 +interface DataType {
  16 + key: string;
  17 + name: string;
  18 +}
  19 +export default function TableList({ indicator }: Props) {
  20 + const [monthly, setMonthly] = useState<number>();
  21 + const [shopId, setShopId] = useState<number>();
  22 + const { data } = useInitial(getShopApi, [], {});
  23 + const pickMonth: DatePickerProps['onChange'] = (date, _dateString) => {
  24 + setMonthly(date?.valueOf() ?? 0);
  25 + };
  26 + function shopChange(value: any) {
  27 + setShopId(value);
  28 + }
  29 + //@ts-ignore
  30 + const { tableProps, run } = useAntdTable<any, MDataImport.DataImportListParams[]>(DetailedListAPi, {
  31 + manual: true,
  32 + defaultParams: [
  33 + {
  34 + pageSize: 10,
  35 + current: 1,
  36 + shopId: undefined,
  37 + monthly: Date.now(),
  38 + indicatorCode: indicator?.code || '',
  39 + },
  40 + ],
  41 + onError: (e, _) => {
  42 + message.error(e.message);
  43 + },
  44 + });
  45 + const columns: ColumnsType<DataType> = [
  46 + {
  47 + title: '月度',
  48 + dataIndex: 'dataDate',
  49 + align: 'center',
  50 + width: 200,
  51 + key: 'dataDate',
  52 + render: (time: number) => (time ? moment(time).format('YYYY-MM') : '--'),
  53 + },
  54 + {
  55 + title: '门店',
  56 + dataIndex: 'shopName',
  57 + width: 300,
  58 + align: 'center',
  59 + key: 'shopName',
  60 + render: (name) => <span>{name || '--'}</span>,
  61 + },
  62 + {
  63 + title: '打分',
  64 + dataIndex: 'score',
  65 + width: 100,
  66 + align: 'center',
  67 + key: 'score',
  68 + render: (name) => <span>{name || '--'}</span>,
  69 + },
  70 + {
  71 + title: '总分',
  72 + key: 'totalScore',
  73 + width: 100,
  74 + align: 'center',
  75 + dataIndex: 'totalScore',
  76 + render: (name) => <span>{name || '--'}</span>,
  77 + },
  78 + {
  79 + title: '打分人员',
  80 + key: 'graderStaffName',
  81 + width: 200,
  82 + align: 'center',
  83 + dataIndex: 'graderStaffName',
  84 + render: (name) => <span>{name || '--'}</span>,
  85 + },
  86 + ];
  87 + useEffect(() => {
  88 + if (indicator?.code) {
  89 + run({
  90 + pageSize: 10,
  91 + current: 1,
  92 + monthly: monthly || Date.now(),
  93 + indicatorCode: indicator?.code || '',
  94 + shopId: shopId || undefined,
  95 + });
  96 + }
  97 + }, [indicator?.code, monthly, shopId]);
  98 + return (
  99 + <>
  100 + <div style={{ display: 'flex' }}>
  101 + <DatePicker
  102 + value={monthly ? moment(monthly) : undefined}
  103 + disabledDate={(currentDate) => moment().startOf('month').valueOf() < currentDate.startOf('month').valueOf()}
  104 + onChange={pickMonth}
  105 + picker="month"
  106 + style={{ marginRight: 20 }}
  107 + />
  108 + <Select allowClear style={{ width: 200, marginRight: 20 }} placeholder="请选择门店" onChange={shopChange}>
  109 + {(data || []).map((shop) => (
  110 + <Select.Option value={shop.id} key={shop.id}>
  111 + {shop.name}
  112 + </Select.Option>
  113 + ))}
  114 + </Select>
  115 + </div>
  116 + <Table columns={columns} {...tableProps} rowKey="id" style={{ marginTop: 20 }} />
  117 + </>
  118 + );
  119 +}
... ...
src/pages/performance/DataImport/components/ExcelTable.tsx 0 → 100644
  1 +import React, { useEffect, useState } from 'react';
  2 +import { Table, message, Modal } from 'antd';
  3 +import { UploadImportApi, SaveDataApi } from '../api';
  4 +import { useRequest } from 'ahooks';
  5 +import { ErrorType } from '../entity';
  6 +
  7 +import moment from 'moment';
  8 +interface Props {
  9 + fid?: string;
  10 + show?: boolean;
  11 + indicator?: MDataImport.IndicatorList;
  12 + setExcelvisible?: (bool: boolean) => void;
  13 + refresh?: () => void;
  14 +}
  15 +export default function ExcelTable({ fid, show, indicator, setExcelvisible, refresh }: Props) {
  16 + const { data, run, loading } = useRequest<MDataImport.DataImportList, string[]>(UploadImportApi, {
  17 + manual: true,
  18 + onError: (e, _) => {
  19 + message.error(e.message);
  20 + },
  21 + });
  22 +
  23 + useEffect(() => {
  24 + if (show) {
  25 + run(fid || '', indicator?.code || '');
  26 + }
  27 + }, [show, fid, indicator]);
  28 + const savesubmit = () => {
  29 + SaveDataApi({ key: data?.key || '' })
  30 + .then(() => {
  31 + message.success('操作成功');
  32 + setExcelvisible?.(false);
  33 + refresh?.();
  34 + })
  35 + .catch((error) => {
  36 + message.error(error.message);
  37 + setExcelvisible?.(false);
  38 + });
  39 + };
  40 + const tableItem: any = [
  41 + {
  42 + title: '月度',
  43 + align: 'center',
  44 + width: 100,
  45 + dataIndex: 'dataDate',
  46 + key: 'dataDate',
  47 + render: (time: number) => (time ? moment(time).format('YYYY-MM') : '--'),
  48 + },
  49 + {
  50 + title: '门店',
  51 + dataIndex: 'shopName',
  52 + align: 'center',
  53 + key: 'shopName',
  54 + render: (name: any) => <span>{name || '--'}</span>,
  55 + },
  56 + {
  57 + title: '打分',
  58 + width: 100,
  59 + dataIndex: 'score',
  60 + align: 'center',
  61 + key: 'score',
  62 + render: (name: any) => <span>{name || '--'}</span>,
  63 + },
  64 + {
  65 + title: '总分',
  66 + width: 100,
  67 + dataIndex: 'totalScore',
  68 + align: 'center',
  69 + key: 'totalScore',
  70 + render: (name: any) => <span>{name || '--'}</span>,
  71 + },
  72 + {
  73 + title: '打分人员',
  74 + width: 100,
  75 + dataIndex: 'graderStaffName',
  76 + align: 'center',
  77 + key: 'graderStaffName',
  78 + render: (name: any) => <span>{name || '--'}</span>,
  79 + },
  80 + {
  81 + title: '是否识别',
  82 + width: 100,
  83 + dataIndex: 'errorType',
  84 + align: 'center',
  85 + render: (_: any, record: any) => (record.errorType ? <div>未识别</div> : <div>已识别</div>),
  86 + },
  87 + {
  88 + title: '未识别原因',
  89 + width: 150,
  90 + dataIndex: 'errorType',
  91 + align: 'center',
  92 + render: (_: any, record: any) => (record.errorType ? ErrorType[record.errorType] : '--'),
  93 + },
  94 + ];
  95 + return (
  96 + <Modal
  97 + open={show}
  98 + destroyOnClose
  99 + onCancel={() => {
  100 + setExcelvisible?.(false);
  101 + }}
  102 + width="70%"
  103 + keyboard
  104 + onOk={savesubmit}
  105 + >
  106 + <Table loading={loading} dataSource={data?.details} columns={tableItem} pagination={{ pageSizeOptions: [10, 20, 50] }} />
  107 + <div>
  108 + 成功条数: <span style={{ color: '#20c688' }}>{data?.successNum}</span> 条
  109 + </div>
  110 + <div>
  111 + 失败条数: <span style={{ color: '#f4333c' }}>{data?.errorNum}</span> 条
  112 + </div>
  113 + </Modal>
  114 + );
  115 +}
... ...
src/pages/performance/DataImport/components/ImportList.tsx 0 → 100644
  1 +import React, { useEffect, useState, useRef } from 'react';
  2 +import { Table, Button, Tag, message, DatePicker, Select, Modal, Upload, Space, Divider, Popconfirm, Typography } from 'antd';
  3 +import type { UploadProps } from 'antd';
  4 +import type { ColumnsType } from 'antd/es/table';
  5 +import { DataImportAPi, ImportListDetailApi, DownloadApi, deleteApi, revokeApi } from '../api';
  6 +import { useAntdTable } from 'ahooks';
  7 +import moment from 'moment';
  8 +import type { DatePickerProps } from 'antd';
  9 +import useInitial from '@/hooks/useInitail';
  10 +import { getShopApi } from '@/common/api';
  11 +import { SelectStatus } from '../entity';
  12 +import { DownloadOutlined, PlusOutlined } from '@ant-design/icons';
  13 +import { useRequest } from 'ahooks';
  14 +import ApprovalProgressModal from '@/components/ApprovalProgressModal';
  15 +import type { ApprovalProgressModalRef } from '@/components/ApprovalProgressModal';
  16 +import dowloader from '@/utils/downloader';
  17 +import { IMGURL } from '@/utils';
  18 +import ExcelTable from './ExcelTable';
  19 +
  20 +type Props = {
  21 + indicator?: MDataImport.IndicatorList;
  22 +};
  23 +interface DataType {
  24 + key: string;
  25 + name: string;
  26 +}
  27 +export default function TableList({ indicator }: Props) {
  28 + const [monthly, setMonthly] = useState<number>();
  29 + const [shopId, setShopId] = useState<number>();
  30 + const [status, setStatus] = useState<number>();
  31 + const [fid, setFid] = useState<string>('');
  32 + const [dataVisible, setDataVisible] = useState<boolean>(false);
  33 + const [excelvisible, setExcelvisible] = useState<boolean>(false);
  34 + const tagColorArr = ['', 'warning', 'processing', 'error', 'success', 'warning'];
  35 +
  36 + const approvalProgressRef = useRef<ApprovalProgressModalRef | null>(null);
  37 +
  38 + const upload: UploadProps = {
  39 + name: 'file',
  40 + action: '/api2/file/upload',
  41 + accept: '.xlsx,.xls',
  42 + maxCount: 1,
  43 + showUploadList: false,
  44 + onChange(info) {
  45 + if (info.file.status !== 'uploading') {
  46 + }
  47 + if (info.file.status === 'done') {
  48 + setFid(info.file.response.data);
  49 + setExcelvisible(true);
  50 + } else if (info.file.status === 'error') {
  51 + message.error(`${info.file.name} 上传失败`);
  52 + }
  53 + },
  54 + };
  55 + const viewProcess = (record: MDataImport.DetailedList) => {
  56 + approvalProgressRef.current?.setApprovalProgressModalInfo({
  57 + visible: true,
  58 + orderNo: record?.approvalNo,
  59 + });
  60 + };
  61 + const pickMonth: DatePickerProps['onChange'] = (date, _dateString) => {
  62 + setMonthly(date?.valueOf() ?? 0);
  63 + };
  64 + function shopChange(value: any) {
  65 + setShopId(value);
  66 + }
  67 + function statusChange(value: any) {
  68 + setStatus(value);
  69 + }
  70 + const { data } = useInitial(getShopApi, [], {});
  71 + //@ts-ignore
  72 + const { tableProps, run, refresh } = useAntdTable<any, MDataImport.DataImportListParams[]>(DataImportAPi, {
  73 + manual: true,
  74 + defaultParams: [
  75 + {
  76 + pageSize: 10,
  77 + current: 1,
  78 + monthly: Date.now(),
  79 + indicatorCode: indicator?.code || '',
  80 + },
  81 + ],
  82 +
  83 + onError: (e, _) => {
  84 + message.error(e.message);
  85 + },
  86 + });
  87 + const {
  88 + run: running,
  89 + mutate,
  90 + loading,
  91 + data: list,
  92 + } = useRequest<MDataImport.DataImportList, (number | undefined)[]>(ImportListDetailApi, {
  93 + manual: true,
  94 + onError: (e, _) => {
  95 + message.error(e.message);
  96 + },
  97 + });
  98 + const deleteData = async (id: number) => {
  99 + const param = { id };
  100 + try {
  101 + const { success } = await deleteApi(param);
  102 + if (success) {
  103 + message.success('删除成功');
  104 + refresh();
  105 + }
  106 + } catch (error: any) {
  107 + message.error(error.message);
  108 + }
  109 + };
  110 + const revoke = async (id: number) => {
  111 + const param = { id };
  112 + try {
  113 + const { success } = await revokeApi(param);
  114 + if (success) {
  115 + message.success('删除成功');
  116 + refresh();
  117 + }
  118 + } catch (error: any) {
  119 + message.error(error.message);
  120 + }
  121 + };
  122 + const detailColumns: ColumnsType<DataType> = [
  123 + {
  124 + title: '月度',
  125 + align: 'center',
  126 + dataIndex: 'dataDate',
  127 + width: 200,
  128 + key: 'dataDate',
  129 + render: (time: number) => (time ? moment(time).format('YYYY-MM') : '--'),
  130 + },
  131 + {
  132 + title: '门店',
  133 + align: 'center',
  134 + dataIndex: 'shopName',
  135 + width: 300,
  136 + key: 'shopName',
  137 + render: (name) => <span>{name || '--'}</span>,
  138 + },
  139 + {
  140 + title: '打分',
  141 + align: 'center',
  142 + dataIndex: 'score',
  143 + width: 100,
  144 + key: 'score',
  145 + render: (name) => <span>{name || '--'}</span>,
  146 + },
  147 + {
  148 + title: '总分',
  149 + align: 'center',
  150 + key: 'totalScore',
  151 + width: 100,
  152 + dataIndex: 'totalScore',
  153 + render: (name) => <span>{name || '--'}</span>,
  154 + },
  155 + {
  156 + title: '打分人员',
  157 + align: 'center',
  158 + key: 'graderStaffName',
  159 + width: 200,
  160 + dataIndex: 'graderStaffName',
  161 + render: (name) => <span>{name || '--'}</span>,
  162 + },
  163 + ];
  164 + const columns: ColumnsType<MDataImport.DetailedList> = [
  165 + {
  166 + title: '导入时间',
  167 + align: 'center',
  168 + width: 300,
  169 + dataIndex: 'createTime',
  170 + key: 'createTime',
  171 + render: (time: number) => (time ? moment(time).format('YYYY-MM-DD hh:mm:ss') : '--'),
  172 + },
  173 + {
  174 + title: '导入人员',
  175 + align: 'center',
  176 + width: 300,
  177 + dataIndex: 'importUserName',
  178 + key: 'importUserName',
  179 + render: (name) => <span>{name || '--'}</span>,
  180 + },
  181 +
  182 + {
  183 + title: '导入数据',
  184 + align: 'center',
  185 + width: 100,
  186 + key: 'num',
  187 + dataIndex: 'num',
  188 + render: (name) => <span>{name + '条' || '--'}</span>,
  189 + },
  190 + {
  191 + title: '成功',
  192 + align: 'center',
  193 + width: 100,
  194 + key: 'successNum',
  195 + dataIndex: 'successNum',
  196 + render: (name) => <span>{name + '条' || '--'}</span>,
  197 + },
  198 + {
  199 + title: '失败',
  200 + align: 'center',
  201 + width: 100,
  202 + key: 'errorNum',
  203 + dataIndex: 'errorNum',
  204 + render: (name) => <span>{name + '条' || '--'}</span>,
  205 + },
  206 + {
  207 + title: '数据清单',
  208 + align: 'center',
  209 + width: 200,
  210 + key: 'importUserId',
  211 + render: (value, record: MDataImport.DetailedList) => {
  212 + return (
  213 + <Button
  214 + type="link"
  215 + onClick={() => {
  216 + {
  217 + setDataVisible(true);
  218 + running(record?.id);
  219 + }
  220 + }}
  221 + >
  222 + 查看
  223 + </Button>
  224 + );
  225 + },
  226 + },
  227 + {
  228 + title: '状态',
  229 + width: 100,
  230 + align: 'center',
  231 + key: 'status',
  232 + dataIndex: 'status',
  233 + render: (text: number) => <Tag color={tagColorArr[text]}>{text ? SelectStatus[text] : '--'}</Tag>,
  234 + },
  235 + {
  236 + title: '操作',
  237 + align: 'center',
  238 + width: 300,
  239 + key: 'tags',
  240 + render: (_: any, record: MDataImport.DetailedList) => {
  241 + return (
  242 + <Space split={<Divider type="vertical" />}>
  243 + {(record.status == 1 || record.status == 3 || record.status == 5) && (
  244 + <Popconfirm title="确定删除,提交后不可更改?" onConfirm={() => deleteData(record?.id || 0)} okText="确定" cancelText="取消">
  245 + <Typography.Link>删除</Typography.Link>
  246 + </Popconfirm>
  247 + )}
  248 + {(record.status == 2 || record.status == 3) && (
  249 + <>
  250 + {record.status == 2 && (
  251 + <Popconfirm title="确定撤销,提交后不可更改?" onConfirm={() => revoke(record?.id || 0)} okText="确定" cancelText="取消">
  252 + <Typography.Link>撤销</Typography.Link>
  253 + </Popconfirm>
  254 + )}
  255 + <Typography.Link
  256 + onClick={() => {
  257 + viewProcess(record);
  258 + }}
  259 + >
  260 + 流程进度
  261 + </Typography.Link>
  262 + </>
  263 + )}
  264 + {record.status == 4 && <div>--</div>}
  265 + </Space>
  266 + );
  267 + },
  268 + },
  269 + ];
  270 +
  271 + useEffect(() => {
  272 + if (indicator?.code) {
  273 + run({
  274 + pageSize: 10,
  275 + current: 1,
  276 + monthly: monthly || Date.now(),
  277 + indicatorCode: indicator?.code || '',
  278 + shopId: shopId || undefined,
  279 + status: status || undefined,
  280 + });
  281 + }
  282 + }, [indicator?.code, monthly, shopId, status]);
  283 + return (
  284 + <>
  285 + <div style={{ display: 'flex' }}>
  286 + <DatePicker
  287 + value={monthly ? moment(monthly) : undefined}
  288 + disabledDate={(currentDate) => moment().startOf('month').valueOf() < currentDate.startOf('month').valueOf()}
  289 + onChange={pickMonth}
  290 + picker="month"
  291 + style={{ marginRight: 20 }}
  292 + />
  293 + <Select allowClear style={{ width: 245, marginRight: 20 }} placeholder="请选择门店" onChange={shopChange}>
  294 + {(data || []).map((shop) => (
  295 + <Select.Option value={shop.id} key={shop.id}>
  296 + {shop.name}
  297 + </Select.Option>
  298 + ))}
  299 + </Select>
  300 + <Select allowClear style={{ width: 120 }} placeholder="状态筛选" onChange={statusChange}>
  301 + {[2, 3, 4, 5].map((text: number) => (
  302 + <Select.Option value={text} key={text}>
  303 + {SelectStatus[text]}
  304 + </Select.Option>
  305 + ))}
  306 + </Select>
  307 + <div style={{ marginLeft: 'auto' }}>
  308 + <Button
  309 + icon={<DownloadOutlined rev="" />}
  310 + type="primary"
  311 + onClick={() => {
  312 + DownloadApi(indicator?.code)
  313 + .then((res) => {
  314 + dowloader.downloadFile({ fileUrl: IMGURL.showImage(res) });
  315 + })
  316 + .catch((e) => {
  317 + message.error(e.message);
  318 + });
  319 + }}
  320 + >
  321 + 下载模版
  322 + </Button>
  323 + <Upload {...upload}>
  324 + <Button style={{ marginLeft: 10 }} icon={<PlusOutlined rev="" />} type="primary">
  325 + 导入
  326 + </Button>
  327 + </Upload>
  328 + </div>
  329 + </div>
  330 +
  331 + <Table rowKey="id" columns={columns} {...tableProps} style={{ marginTop: 20 }} />
  332 + <Modal
  333 + title="数据清单"
  334 + open={dataVisible}
  335 + footer={null}
  336 + destroyOnClose
  337 + onCancel={() => {
  338 + setDataVisible(false);
  339 + mutate(undefined);
  340 + }}
  341 + width="70%"
  342 + keyboard
  343 + >
  344 + <Table
  345 + rowKey="id"
  346 + loading={loading}
  347 + dataSource={list?.details ?? []}
  348 + columns={detailColumns}
  349 + pagination={{ pageSizeOptions: [10, 20, 50] }}
  350 + />
  351 + </Modal>
  352 + <ExcelTable refresh={refresh} setExcelvisible={setExcelvisible} show={excelvisible} fid={fid} indicator={indicator} />
  353 + <ApprovalProgressModal ref={approvalProgressRef} />
  354 + </>
  355 + );
  356 +}
... ...
src/pages/performance/DataImport/components/exhibitionHallFour.tsx 0 → 100644
  1 +import React, { useState } from 'react';
  2 +import { Radio, Empty } from 'antd';
  3 +import ImportList from './ImportList';
  4 +import DetailList from './DetailList';
  5 +
  6 +type Props = {
  7 + indicator?: MDataImport.IndicatorList;
  8 + onChange?: (value?: any) => void;
  9 +};
  10 +
  11 +export default function ExhibitionHallFour({ indicator }: Props) {
  12 + const [tableType, setTableType] = useState<number>(0);
  13 +
  14 + return (
  15 + <div style={{ flex: 1, overflowX: 'hidden', marginLeft: 20 }}>
  16 + {indicator?.code ? (
  17 + <div>
  18 + <div style={{ fontSize: 18, fontWeight: 'bold' }}>{`指标:${indicator?.name ?? '-'}`}</div>
  19 + <Radio.Group defaultValue="a" buttonStyle="solid" style={{ marginTop: 15, marginBottom: 20 }}>
  20 + <Radio.Button
  21 + value="a"
  22 + style={{ marginRight: 10 }}
  23 + onClick={() => {
  24 + setTableType(0);
  25 + }}
  26 + >
  27 + 导入记录
  28 + </Radio.Button>
  29 +
  30 + <Radio.Button
  31 + value="b"
  32 + onClick={() => {
  33 + setTableType(1);
  34 + }}
  35 + >
  36 + 数据清单
  37 + </Radio.Button>
  38 + </Radio.Group>
  39 + {tableType === 0 ? <ImportList indicator={indicator} /> : <DetailList indicator={indicator} />}
  40 + </div>
  41 + ) : (
  42 + <div style={{ marginTop: '25%', display: 'flex', justifyContent: 'center' }}>
  43 + <Empty description={'请选择指标'} />
  44 + </div>
  45 + )}
  46 + </div>
  47 + );
  48 +}
... ...
src/pages/performance/DataImport/components/truckloadBank.tsx 0 → 100644
  1 +import React from 'react';
  2 +import { Table, message } from 'antd';
  3 +import { IndicatorListApi } from '../api';
  4 +import { useRequest } from 'ahooks';
  5 +
  6 +type Props = {
  7 + value?: MDataImport.IndicatorList;
  8 + onChange?: (value?: any) => void;
  9 +};
  10 +export default function TruckloadBank({ value, onChange }: Props) {
  11 + const Column = Table.Column;
  12 + const { data } = useRequest<MDataImport.IndicatorList[], never[]>(IndicatorListApi, {
  13 + onSuccess: (data, _params) => {
  14 + onChange?.(data?.[0]);
  15 + },
  16 + onError: (e, _) => {
  17 + message.error(e.message);
  18 + },
  19 + });
  20 +
  21 + return (
  22 + <div style={{ width: 200 }}>
  23 + <Table
  24 + rowKey="code"
  25 + pagination={false}
  26 + rowSelection={{
  27 + type: 'radio',
  28 + selectedRowKeys: value?.code ? [value.code] : [],
  29 + onSelect: (record) => onChange?.(record),
  30 + }}
  31 + dataSource={data || []}
  32 + >
  33 + <Column title="指标" dataIndex="name" align="center" render={(text) => text ?? '-'} fixed />
  34 + </Table>
  35 + </div>
  36 + );
  37 +}
... ...
src/pages/performance/DataImport/entity.ts 0 → 100644
  1 +import e from 'express';
  2 +
  3 +export enum SelectStatus{
  4 + '未发布'=1,
  5 + '审批中',
  6 + '审批拒绝',
  7 + '审批同意',
  8 + '撤销审批'
  9 +}
  10 +export enum ErrorType{
  11 + '-' = 0,
  12 + '未匹配到人员',
  13 + '未匹配到门店',
  14 + '未匹配到指标',
  15 + '数据重复',
  16 + '时间错误'
  17 +}
... ...
src/pages/performance/DataImport/index.css 0 → 100644
src/pages/performance/DataImport/index.tsx 0 → 100644
  1 +import React, { useState } from 'react';
  2 +import { PageHeaderWrapper } from '@ant-design/pro-layout';
  3 +import { Card } from 'antd';
  4 +import TruckloadBank from './components/truckloadBank';
  5 +import ExhibitionHallFour from './components/exhibitionHallFour';
  6 +
  7 +export default () => {
  8 + const [indicator, setIndicator] = useState<MDataImport.IndicatorList>();
  9 + const onChange = (value?: MDataImport.IndicatorList) => {
  10 + setIndicator(value);
  11 + };
  12 + return (
  13 + <PageHeaderWrapper title="自定义数据导入">
  14 + <Card>
  15 + <div style={{ display: 'flex' }}>
  16 + <TruckloadBank value={indicator} onChange={onChange} />
  17 + <ExhibitionHallFour indicator={indicator} onChange={onChange} />
  18 + </div>
  19 + </Card>
  20 + </PageHeaderWrapper>
  21 + );
  22 +};
... ...
src/pages/performance/DataImport/interface.d.ts 0 → 100644
  1 +declare namespace MDataImport {
  2 + /**
  3 + * 展厅美化导入记录分页查询参数
  4 + */
  5 + interface DataImportListParams {
  6 + current: number;
  7 + pageSize: number;
  8 + indicatorCode?: string;
  9 + shopId?: number;
  10 + monthly?: number;
  11 + sorter?: any;
  12 + filter?: any;
  13 + extra?: any;
  14 + [key: string]: any;
  15 + }
  16 +
  17 + /**
  18 + * 展厅美化导入记录分页查询数据
  19 + */
  20 + interface DataImportList {
  21 + id?: number;
  22 + importUserId?: number;
  23 + importUserName?: string;
  24 + shopId?: number;
  25 + shopName?: string;
  26 + dimensionType?: string;
  27 + indicatorCode?: string;
  28 + indicatorName?: string;
  29 + valueType?: number;
  30 + dataDate?: string;
  31 + num?: number;
  32 + successNum?: number;
  33 + errorNum?: number;
  34 + groupId?: number;
  35 + createTime?: string;
  36 + key?: string;
  37 + status?: string;
  38 + approvalNo?: string;
  39 + dataTimeType?: string;
  40 + details?: [];
  41 + }
  42 + /**
  43 + * 数据导入指标列表数据
  44 + */
  45 + interface IndicatorList {
  46 + id?: number;
  47 + code?: string;
  48 + name?: 'melda.sanford';
  49 + }
  50 + /**
  51 + * 数据清单列表数据
  52 + */
  53 + interface DetailedList {
  54 + id?: number;
  55 + recordId?: number;
  56 + indicatorName?: string;
  57 + indicatorCode?: string;
  58 + shopId?: number;
  59 + shopName?: string;
  60 + score?: number;
  61 + totalScore?: number;
  62 + graderStaffName?: string;
  63 + graderStaffId?: number;
  64 + stageStartDataDate?: string;
  65 + stageEndDataDate?: string;
  66 + dataDate?: string;
  67 + errorType?: string;
  68 + valid?: boolean;
  69 + yn?: boolean;
  70 + approvalNo?: string;
  71 + status: number;
  72 + }
  73 +}
... ...
src/pages/performance/EvaDataImport/index.tsx
... ... @@ -51,7 +51,7 @@ export default () =&gt; {
51 51 }
52 52 const uploadPerson: UploadProps = {
53 53 name: 'file',
54   - action: '/api/morax/erp/eval-indicator/analysis-staff',
  54 + action: '/api/morax/erp/eval-indicator/analysis-staff',
55 55 maxCount: 1,
56 56 showUploadList: false,
57 57 // 设置headers里的token
... ... @@ -62,6 +62,7 @@ export default () =&gt; {
62 62 }
63 63 if (info.file.status === 'done') {
64 64 setFileData(info.file.response.data);
  65 +
65 66 setVisible(true);
66 67 } else if (info.file.status === 'error') {
67 68 message.error(`${info.file.name} 上传失败`);
... ...
src/pages/performance/EvaSetting/components/EditModal.tsx
... ... @@ -29,6 +29,7 @@ export default function EditModal({ onClose, item, roleList, customIndicatorList
29 29 const [customIndicator, setCustomIndicator] = useState<any[]>([]);
30 30 const [codeType, setCodeType] = useState<number>(0);
31 31 const [isOriginCode, setIsOriginCode] = useState<boolean>(!!currentItem.originCode);
  32 + const [pa, setPa] = useState();
32 33 useEffect(() => {
33 34 setIsOriginCode(!!item.currentItem?.originCode);
34 35 }, [item]);
... ... @@ -57,6 +58,12 @@ export default function EditModal({ onClose, item, roleList, customIndicatorList
57 58 setCodeType(currentItem.codeType);
58 59 if (currentItem.codeType == 2) {
59 60 const result = transformFormData(currentItem, roleList, list);
  61 + console.log(pa, 'asada');
  62 +
  63 + if (currentItem.roleCodes && currentItem.roleCodes.length > 0) {
  64 + result.roles = currentItem?.roleCodes?.map((item: any) => ({ value: item }));
  65 + }
  66 +
60 67 form.setFieldsValue({ ...result });
61 68 } else {
62 69 setDetailsParams({ id: currentItem.id }, true);
... ... @@ -110,6 +117,12 @@ export default function EditModal({ onClose, item, roleList, customIndicatorList
110 117 if (currentItem.combineCode) {
111 118 pa.combineCode = currentItem.combineCode;
112 119 }
  120 + const roles = values.roles ?? [];
  121 +
  122 + pa.roleCodes = roles.map((role: any) => role.value);
  123 + pa.roleNames = roles.map((role: any) => role.label);
  124 + console.log(pa.roleCodes, pa.roleNames, 'wwe333333');
  125 + setPa(pa);
113 126 console.log('提交指标pa', pa);
114 127 setSaveLoading(true);
115 128 if (pa.codeType == 2 || currentItem.codeType == 2) {
... ... @@ -442,6 +455,17 @@ export default function EditModal({ onClose, item, roleList, customIndicatorList
442 455 </Select>
443 456 </FormItem>
444 457 )}
  458 + {!isOriginCode && currentItem.id && currentItem.codeType !== 3 && (
  459 + <Form.Item name="roles" label="适用角色" rules={[{ required: true }]}>
  460 + <Select placeholder="请选择角色" showSearch allowClear mode="multiple" optionFilterProp="children" disabled={isOriginCode} labelInValue>
  461 + {roleList.map((item) => (
  462 + <Option value={item.roleCode} key={item.roleCode}>
  463 + {item.roleName}
  464 + </Option>
  465 + ))}
  466 + </Select>
  467 + </Form.Item>
  468 + )}
445 469 </Form>
446 470 </Spin>
447 471 </Modal>
... ...
src/pages/performance/EvaSetting/components/EditModalOne.tsx
... ... @@ -111,6 +111,9 @@ export default function EditModal({ onClose, item, roleList, customIndicatorList
111 111 pa.combineCode = currentItem.combineCode;
112 112 }
113 113 console.log('提交指标pa', pa);
  114 + const roles = values.roles ?? [];
  115 + pa.roleCodes = roles.map((role: any) => role.value);
  116 + pa.roleNames = roles.map((role: any) => role.label);
114 117 setSaveLoading(true);
115 118 if (pa.codeType == 2 || currentItem.codeType == 2) {
116 119 saveEvaIndicators(pa)
... ... @@ -442,6 +445,25 @@ export default function EditModal({ onClose, item, roleList, customIndicatorList
442 445 </Select>
443 446 </FormItem>
444 447 )}
  448 + {!isOriginCode && currentItem.codeType !== 3 && (
  449 + <Form.Item name="roles" label="适用角色" rules={[{ required: true }]}>
  450 + <Select
  451 + placeholder="请选择角色"
  452 + showSearch
  453 + allowClear
  454 + mode="multiple"
  455 + optionFilterProp="children"
  456 + disabled={isOriginCode}
  457 + labelInValue
  458 + >
  459 + {roleList.map((item) => (
  460 + <Option value={item.roleCode} key={item.roleCode}>
  461 + {item.roleName}
  462 + </Option>
  463 + ))}
  464 + </Select>
  465 + </Form.Item>
  466 + )}
445 467 </Form>
446 468 </Spin>
447 469 </Modal>
... ...
src/pages/performance/EvaSetting/interface.ts
... ... @@ -49,6 +49,8 @@ declare namespace EvaSetteing {
49 49 yn: boolean; // 逻辑删除
50 50 originCode?: string;
51 51 unit: string;
  52 + roleCodes?: string[];
  53 + roleNames?: string[]
52 54 }
53 55 /**
54 56 * 指标库列表项
... ...
src/pages/performance/KpiGroupSetting/EditComfirm/components/IndivatorsTable.tsx
... ... @@ -199,7 +199,7 @@ const IndivatorsTable = ({ value, onChange, personModal }: Props) =&gt; {
199 199 return _columns;
200 200 };
201 201 const onChangePag = (page: number, pageSize: number) => {
202   - console.log(page, pageSize, value);
  202 + console.log(page, pageSize, value,'0000000');
203 203 setPage(page - 1);
204 204 };
205 205 return (
... ...
src/pages/performance/KpiSetting/components/EditModal.tsx
... ... @@ -44,6 +44,7 @@ export default function EditModal({ onClose, setItem, item, roleList }: Props) {
44 44 useEffect(() => {
45 45 if (visible && currentItem) {
46 46 const result = transformFormData(currentItem, roleList, list);
  47 + console.log({...result},'1111dwewwwwwew')
47 48 form.setFieldsValue({ ...result });
48 49 }
49 50 }, [visible]);
... ... @@ -209,7 +210,7 @@ export default function EditModal({ onClose, setItem, item, roleList }: Props) {
209 210 >
210 211 {({ getFieldValue }) => {
211 212 return getFieldValue('roleType') === 3 ? (
212   - <Form.Item name="roles" label="适用角色" rules={[{ required: true }]}>
  213 + <Form.Item name="roles" label="适用角色1" rules={[{ required: true }]}>
213 214 <Select
214 215 placeholder="请选择角色"
215 216 showSearch
... ...
src/pages/performance/Portfolio/interface.d.ts
1   -import Enum from "@/utils/enum";
  1 +import Enum from '@/utils/enum';
2 2  
3 3 declare namespace ComIndicatorSetting {
4 4 /**
... ... @@ -83,6 +83,7 @@ declare namespace ComIndicatorSetting {
83 83 roleType: number[];
84 84 roleNames: string[];
85 85 unit: string;
  86 + roleCodes: string;
86 87 }
87 88 interface ComSaveParams {
88 89 id?: number;
... ...
src/pages/promotion/proengine/components/List.tsx
... ... @@ -33,7 +33,7 @@ export default function List(props: Props) {
33 33 render={(text, row: IF.ListVo) => (
34 34 <div>
35 35 <p>{text || '-'}</p>
36   - <p>{`(${row.proId || ''})`}</p>
  36 + <p style={{ margin: 0, color: '#666' }}>{`(${row.proId || ''})`}</p>
37 37 </div>
38 38 )}
39 39 />
... ... @@ -89,6 +89,7 @@ export default function List(props: Props) {
89 89 />
90 90 <Column
91 91 title="操作"
  92 + width={100}
92 93 render={(text, record: IF.ListVo) => (
93 94 //@ts-ignore
94 95 <Operation record={record} onRefreshing={() => onRefreshing()} />
... ...
src/pages/promotion/proengine/components/Operation.tsx
1 1 import React from 'react';
2 2 import { Button, Divider, Popconfirm, Tooltip, message, Modal } from 'antd';
3 3 import { history } from 'umi';
4   -import * as common from '@/typing/common';
  4 +import type * as common from '@/typing/common';
5 5 import * as API from '../api';
6   -import * as IF from '../interface.d';
  6 +import type * as IF from '../interface.d';
7 7 import { PromotionStatus } from '../entity';
8 8  
9 9 const { confirm } = Modal;
... ... @@ -107,53 +107,46 @@ export default function Oparetion(props: Props) {
107 107 <React.Fragment>
108 108 {record.proStatus == PromotionStatus['草稿'] && (
109 109 <span>
110   - <Button type="primary" ghost size="small" onClick={() => commitAudit(record)}>提审</Button>
111   - <Divider type="vertical" />
  110 + <Button type="link" size="small" onClick={() => commitAudit(record)}>提审</Button>
  111 + {/* <Divider type="vertical" /> */}
112 112 <Popconfirm placement="top" title="确定删除?" onConfirm={() => deleteItem(record)}>
113   - <Button type="danger" ghost size="small">删除</Button>
  113 + <Button type="link" danger size="small">删除</Button>
114 114 </Popconfirm>
115   - <Divider type="vertical" />
116   - <Button type="primary" ghost size="small" onClick={() => copyDetail(record)}>复制</Button>
117   - <Divider type="vertical" />
118   - <Button type="primary" ghost size="small" onClick={() => viewDetail(record)}>编辑</Button>
  115 + {/* <Divider type="vertical" /> */}
  116 + <Button type="link" size="small" onClick={() => copyDetail(record)}>复制</Button>
  117 + {/* <Divider type="vertical" /> */}
  118 + <Button type="link" size="small" onClick={() => viewDetail(record)}>编辑</Button>
119 119 </span>
120 120 )}
121 121 {record.proStatus == PromotionStatus['待审核'] && (
122 122 <span>
123 123 <Popconfirm placement="top" title="确定删除?" onConfirm={() => deleteItem(record)}>
124   - <Button type="danger" ghost size="small">删除</Button>
  124 + <Button type="link" danger size="small">删除</Button>
125 125 </Popconfirm>
126   - <Divider type="vertical" />
127   - <Button type="primary" ghost size="small" onClick={() => copyDetail(record)}>复制</Button>
128   - <Divider type="vertical" />
129   - <Button type="primary" ghost size="small" onClick={() => viewDetail(record)}>查看</Button>
  126 + <Button type="link" size="small" onClick={() => copyDetail(record)}>复制</Button>
  127 + <Button type="link" size="small" onClick={() => viewDetail(record)}>查看</Button>
130 128 </span>
131 129 )}
132 130 {record.proStatus == PromotionStatus['待发布'] && (
133 131 <span>
134 132 <Tooltip key="save" title="活动发布后,才能正常使用">
135   - <Button type="primary" ghost size="small" onClick={() => publishActivity(record)}>发布</Button>
  133 + <Button type="link" size="small" onClick={() => publishActivity(record)}>发布</Button>
136 134 </Tooltip>
137   - <Divider type="vertical" />
138   - <Button type="primary" ghost size="small" onClick={() => copyDetail(record)}>复制</Button>
139   - <Divider type="vertical" />
140   - <Button type="primary" ghost size="small" onClick={() => viewDetail(record)}>查看</Button>
  135 + <Button type="link" size="small" onClick={() => copyDetail(record)}>复制</Button>
  136 + <Button type="link" size="small" onClick={() => viewDetail(record)}>查看</Button>
141 137 </span>
142 138 )}
143 139 {[PromotionStatus['未开始'], PromotionStatus['进行中']].includes(record.proStatus) && (
144 140 <span>
145   - <Button type="danger" ghost size="small" onClick={() => endActivity(record)}>结束</Button>
146   - <Divider type="vertical" />
147   - <Button type="primary" ghost size="small" onClick={() => copyDetail(record)}>复制</Button>
148   - <Divider type="vertical" />
149   - <Button type="primary" ghost size="small" onClick={() => viewDetail(record)}>查看</Button>
  141 + <Button type="link" danger size="small" onClick={() => endActivity(record)}>结束</Button>
  142 + <Button type="link" size="small" onClick={() => copyDetail(record)}>复制</Button>
  143 + <Button type="link" size="small" onClick={() => viewDetail(record)}>查看</Button>
150 144 </span>
151 145 )}
152 146 {record.proStatus == PromotionStatus['已结束'] && (
153 147 <span>
154   - <Button type="primary" ghost size="small" onClick={() => copyDetail(record)}>复制</Button>
155   - <Divider type="vertical" />
156   - <Button type="primary" ghost size="small" onClick={() => viewDetail(record)}>查看</Button>
  148 + <Button type="link" size="small" onClick={() => copyDetail(record)}>复制</Button>
  149 + <Button type="link" size="small" onClick={() => viewDetail(record)}>查看</Button>
157 150 </span>
158 151 )}
159 152 </React.Fragment>
... ...
src/pages/promotion/proengineCreate/components/DealerSelector/api.ts
1   -import { OOP_HOST } from '@/utils/host';
2   -import { http } from '@/typing/http';
  1 +import { OOP_HOST, MKT_HOST } from '@/utils/host';
  2 +import type { http } from '@/typing/http';
3 3 import request from '@/utils/request';
4 4  
5 5 export interface DealerItem {
... ... @@ -21,5 +21,5 @@ export function getDealerApi(brandId: number): http.PromiseResp&lt;DealerItem[]&gt; {
21 21  
22 22 /** 根据当前登录人信息获取商家门店树 */
23 23 export function getDealerTreeApi(params: { bizType: number; brandId?: number }): http.PromiseRespA<DealersShop> {
24   - return request.get(`${OOP_HOST}/select/dealers/shops`, { params });
  24 + return request.get(`${MKT_HOST}/erp/activity/get/scope/auth`, { params });
25 25 }
... ...
src/pages/promotion/proengineCreate/components/DealerSelector/index.tsx
1   -import React, { useEffect, useState, Ref, forwardRef } from 'react';
  1 +import type { Ref } from 'react';
  2 +import React, { useEffect, useState, forwardRef } from 'react';
2 3 import { Radio, message, Select, TreeSelect } from 'antd';
3 4 import type { DefaultOptionType } from 'antd/es/select';
4 5 import { DealerItem, getDealerApi, getDealerTreeApi } from './api';
... ... @@ -32,8 +33,8 @@ const defaultValue: Value = {
32 33  
33 34 function DealerSelector(props: DealerSelectorProps, ref?: Ref<any>) {
34 35 const { brandId, value = defaultValue, bizType, joinDealerIds, onChange, disabled } = props;
35   - const { type = 1, selected = [] } = value;
36   - const [innerValue, setValue] = useState<Value>({ type, selected });
  36 + const { type = 2, selected = [] } = value;
  37 + const [innerValue, setValue] = useState<Value>({ type: 2, selected });
37 38 const [treeData, setTreeData] = useState<Omit<DefaultOptionType, 'label'>[]>([]);
38 39 const [loading, setLoading] = useState(false);
39 40  
... ... @@ -105,39 +106,39 @@ function DealerSelector(props: DealerSelectorProps, ref?: Ref&lt;any&gt;) {
105 106  
106 107 return (
107 108 <div>
108   - <RadioGroup value={innerValue.type} disabled={disabled} onChange={typeChange}>
  109 + {/* <RadioGroup value={innerValue.type} disabled={disabled} onChange={typeChange}>
109 110 <Radio value={1}>全部商家</Radio>
110 111 <Radio value={2}>部分商家</Radio>
111 112 </RadioGroup>
112 113 {innerValue.type === 2 && (
113   - // <TreeSelect
114   - // showSearch
115   - // allowClear
116   - // disabled={disabled}
117   - // treeCheckable
118   - // loading={loading}
119   - // placeholder="请选择商家/门店"
120   - // style={{ width: '100%' }}
121   - // value={innerValue.selected}
122   - // treeExpandAction="click"
123   - // multiple
124   - // dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
125   - // onChange={treeOnChange}
126   - // treeData={treeData}
127   - // />
128   - <SelectorWithFull
129   - data={treeData}
  114 + <TreeSelect
  115 + showSearch
130 116 allowClear
  117 + disabled={disabled}
  118 + treeCheckable
131 119 loading={loading}
132 120 placeholder="请选择商家/门店"
133   - value={innerValue.selected}
134 121 style={{ width: '100%' }}
135   - dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
  122 + value={innerValue.selected}
  123 + treeExpandAction="click"
136 124 multiple
137   - fieldKeyNames={{ keyName: 'key', valueName: 'value', labelName: 'title', childrenName: 'children' }}
  125 + dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
138 126 onChange={treeOnChange}
  127 + treeData={treeData}
139 128 />
140   - )}
  129 + )} */}
  130 + <SelectorWithFull
  131 + data={treeData}
  132 + allowClear
  133 + loading={loading}
  134 + placeholder="请选择商家/门店"
  135 + value={innerValue.selected}
  136 + style={{ width: '100%' }}
  137 + dropdownStyle={{ maxHeight: 400, overflow: 'auto' }}
  138 + multiple
  139 + fieldKeyNames={{ keyName: 'key', valueName: 'value', labelName: 'title', childrenName: 'children' }}
  140 + onChange={treeOnChange}
  141 + />
141 142 </div>
142 143 );
143 144 }
... ...
src/pages/promotion/proengineCreate/entity.ts
1 1 import moment from 'moment';
2 2 import _ from 'lodash';
3   -import { BaseInfoDto } from './interface';
  3 +import type { BaseInfoDto } from './interface';
4 4  
5 5 export const formItemLayout = {
6 6 labelCol: {
... ... @@ -176,7 +176,7 @@ export function setRules(config: BaseInfoDto) {
176 176  
177 177 /** 将管理UI状态的数据,转化为store中存储的数据 */
178 178 export function stateTransforToStore(formState) {
179   - let store = { ...base };
  179 + const store = { ...base };
180 180  
181 181 _.each(formState, (value, key) => {
182 182 switch (key) {
... ... @@ -196,11 +196,11 @@ export function stateTransforToStore(formState) {
196 196 case 'joinDealer':
197 197 const dealersIds: string[] = [];
198 198 const shopIds: string[] = [];
199   - value.selected.forEach((item:string) => {
  199 + value.selected.forEach((item: string) => {
200 200 dealersIds.push(item.split("-")[0]);
201 201 shopIds.push(item.split("-")[1]);
202 202 });
203   - store.joinDealerType = value.type;
  203 + store.joinDealerType = 2;
204 204 store.joinDealerIds = [...new Set(dealersIds)].join(",");
205 205 store.joinShopIds = shopIds.join(",");
206 206 break;
... ...