Commit 96ee8bdc83a843965ad19a8cf50913f5268e65ec
Merge branch 'Shinner-carinsur-LoanClientRequires' into 'master'
Browse Code » feat(carinsur): 新增贷款期客户保险配置功能 See merge request !703
Showing
12 changed files
with
813 additions
and
2 deletions
config/routers/angel.ts
... | ... | @@ -10,8 +10,8 @@ export default [ |
10 | 10 | component: './carinsur/PurchaseConfig', |
11 | 11 | }, |
12 | 12 | { |
13 | - path: '/angel/loan/client/config', //贷款客户保险要求配置 | |
14 | - component: './carinsur/LoanClientConfig', | |
13 | + path: '/angel/loanClientRequires', // 贷款期客户保险配置 | |
14 | + component: './carinsur/LoanClientRequires', | |
15 | 15 | }, |
16 | 16 | |
17 | 17 | { | ... | ... |
src/pages/carinsur/LoanClientRequires/api.ts
0 → 100644
1 | +import type { http } from '@/typing/http'; | |
2 | +import request from '@/utils/request'; | |
3 | +import type { IdNameOption, ShopOption } from '../entity'; | |
4 | +import { ANGEL_Host } from '@/utils/host'; | |
5 | +import type { Pagination } from '@/typing/common'; | |
6 | + | |
7 | +export interface LoanType { | |
8 | + type: number; | |
9 | + amount: number; | |
10 | +} | |
11 | +export interface brandSeriesItem { | |
12 | + id: number; | |
13 | + name: string; | |
14 | + series: IdNameOption[]; | |
15 | +} | |
16 | +export interface Item { | |
17 | + id?: number; | |
18 | + allBrandSeries: boolean; | |
19 | + brandSeries: brandSeriesItem[]; | |
20 | + useShopIds: ShopOption[]; | |
21 | + newTci: LoanType; | |
22 | + newVci: LoanType; | |
23 | + newTai: LoanType; | |
24 | + reNewTci: LoanType; | |
25 | + reNewVci: LoanType; | |
26 | + reNewTai: LoanType; | |
27 | +} | |
28 | + | |
29 | +export interface PageParams extends Pagination { | |
30 | + shopId?: number; | |
31 | + brandId?: number; | |
32 | + seriesId?: number; | |
33 | +} | |
34 | + | |
35 | +/** | |
36 | + * 分页查询 | |
37 | + */ | |
38 | +export function getLCRPage(params?: PageParams): http.PromisePageResp<Item> { | |
39 | + return request.get(`${ANGEL_Host}/loancusinsureq/page`, { params }); | |
40 | +} | |
41 | + | |
42 | +/** | |
43 | + * 新增/编辑 | |
44 | + */ | |
45 | +export function addLCR(data?: Item): http.PromiseResp<void> { | |
46 | + return request.post(`${ANGEL_Host}/loancusinsureq/add`, data); | |
47 | +} | |
48 | + | |
49 | +/** | |
50 | + * 删除 | |
51 | + */ | |
52 | +export function delLCR(id: number): http.PromisePageResp<void> { | |
53 | + return request.post(`${ANGEL_Host}/loancusinsureq/delete`, { id }); | |
54 | +} | ... | ... |
src/pages/carinsur/LoanClientRequires/components/AddModal/index.tsx
0 → 100644
1 | +import React, { useEffect, memo } from 'react'; | |
2 | +import { Modal, Form, message, Radio } from 'antd'; | |
3 | +import * as API from '../../api'; | |
4 | +import { useRequest } from 'umi'; | |
5 | +import BrandSeriesFormItem from '../BrandSeriesFormItem'; | |
6 | +import ShopSelectorByAll from '../ShopSelectorByAll'; | |
7 | +import LoanTypeFormItem from '../LoanTypeFormItem'; | |
8 | +import type { LabeledValue } from 'antd/lib/select'; | |
9 | + | |
10 | +interface Props { | |
11 | + visible: boolean; | |
12 | + row?: API.Item; | |
13 | + onCancel: () => void; | |
14 | + onRefresh: () => void; | |
15 | +} | |
16 | + | |
17 | +function AddModal({ visible, row, onCancel, onRefresh }: Props) { | |
18 | + const { id } = row ?? {}; | |
19 | + const [form] = Form.useForm(); | |
20 | + | |
21 | + const saveHook = useRequest(API.addLCR, { | |
22 | + manual: true, | |
23 | + throwOnError: true, | |
24 | + }); | |
25 | + | |
26 | + useEffect(() => { | |
27 | + if (visible) { | |
28 | + if (row) { | |
29 | + form.setFieldsValue({ | |
30 | + ...row, | |
31 | + useShopIds: row.useShopIds.map((i) => ({ label: i.shopName, value: i.shopId })), | |
32 | + }); | |
33 | + } else { | |
34 | + form.setFieldsValue({ | |
35 | + allBrandSeries: true, | |
36 | + }); | |
37 | + } | |
38 | + } else { | |
39 | + form.resetFields(); | |
40 | + } | |
41 | + }, [visible, row]); | |
42 | + | |
43 | + const handleSave = (feildValue: any) => { | |
44 | + saveHook | |
45 | + .run({ | |
46 | + ...feildValue, | |
47 | + useShopIds: feildValue.useShopIds.map((i: LabeledValue) => ({ shopId: i.value, shopName: i.label })), | |
48 | + brandSeries: feildValue.allBrandSeries ? [] : feildValue.brandSeries, | |
49 | + }) | |
50 | + .then(() => { | |
51 | + message.success('操作成功'); | |
52 | + onCancel(); | |
53 | + onRefresh(); | |
54 | + }) | |
55 | + .catch((e) => { | |
56 | + message.error(e.message); | |
57 | + }); | |
58 | + }; | |
59 | + | |
60 | + return ( | |
61 | + <Modal title={`${id ? '编辑' : '新增'}`} open={visible} confirmLoading={saveHook.loading} onCancel={onCancel} onOk={form.submit} width={1000}> | |
62 | + <Form form={form} onFinish={handleSave} labelCol={{ span: 4 }} wrapperCol={{ span: 18 }}> | |
63 | + <Form.Item name="id" hidden /> | |
64 | + <Form.Item label="适用品牌车系" name="allBrandSeries" rules={[{ required: true, message: '请选择' }]}> | |
65 | + <Radio.Group> | |
66 | + <Radio value={true}>全部品牌全部车系</Radio> | |
67 | + <Radio value={false}>部分品牌部分车系</Radio> | |
68 | + </Radio.Group> | |
69 | + </Form.Item> | |
70 | + <Form.Item | |
71 | + noStyle | |
72 | + shouldUpdate={(prev, curr) => { | |
73 | + // 切换全部|部分时交互未清空部分品牌部分车系数据 | |
74 | + return prev.allBrandSeries !== curr.allBrandSeries; | |
75 | + }} | |
76 | + > | |
77 | + {({ getFieldValue }) => { | |
78 | + const allBrandSeries = getFieldValue('allBrandSeries'); | |
79 | + if (allBrandSeries) { | |
80 | + return null; | |
81 | + } | |
82 | + return ( | |
83 | + <Form.Item label="部分品牌部分车系" name="brandSeries" rules={[{ required: true, message: '请选择' }]}> | |
84 | + <BrandSeriesFormItem /> | |
85 | + </Form.Item> | |
86 | + ); | |
87 | + }} | |
88 | + </Form.Item> | |
89 | + <Form.Item label="适用门店" name="useShopIds" rules={[{ required: true, message: '请选择' }]}> | |
90 | + <ShopSelectorByAll /> | |
91 | + </Form.Item> | |
92 | + <LoanTypeFormItem form={form} key="newTci" name="newTci" label="新保交强险" /> | |
93 | + <LoanTypeFormItem form={form} key="newVci" name="newVci" label="新保商业险" /> | |
94 | + <LoanTypeFormItem form={form} key="newTai" name="newTai" label="新保驾意险" /> | |
95 | + <LoanTypeFormItem form={form} key="reNewTci" name="reNewTci" label="续保交强险" /> | |
96 | + <LoanTypeFormItem form={form} key="reNewVci" name="reNewVci" label="续保商业险" /> | |
97 | + <LoanTypeFormItem form={form} key="reNewTai" name="reNewTai" label="续保驾意险" /> | |
98 | + </Form> | |
99 | + </Modal> | |
100 | + ); | |
101 | +} | |
102 | + | |
103 | +export default memo(AddModal); | ... | ... |
src/pages/carinsur/LoanClientRequires/components/BrandSeriesAddModal.tsx
0 → 100644
1 | +import React, { useEffect } from 'react'; | |
2 | +import { Modal, Form, Select } from 'antd'; | |
3 | +import type { brandSeriesItem } from '../api'; | |
4 | +import type { LabeledValue } from 'antd/lib/select'; | |
5 | +import SeriesSelectorByAll from './SeriesSelectorByAll'; | |
6 | +import _ from 'lodash'; | |
7 | + | |
8 | +interface Props { | |
9 | + brands?: CommonApi.OptionVO[]; | |
10 | + visible: boolean; | |
11 | + value?: brandSeriesItem; | |
12 | + onCancel: () => void; | |
13 | + onChange: (data: brandSeriesItem) => void; | |
14 | +} | |
15 | +const FormItem = Form.Item; | |
16 | + | |
17 | +export default function Index({ brands, visible, value, onCancel, onChange }: Props) { | |
18 | + const [form] = Form.useForm(); | |
19 | + | |
20 | + useEffect(() => { | |
21 | + if (visible && value) { | |
22 | + const { id, name, series } = value; | |
23 | + form.setFieldsValue({ | |
24 | + brand: { label: name, value: id }, | |
25 | + series: series.map((i) => ({ label: i.name, value: i.id })), | |
26 | + }); | |
27 | + } else { | |
28 | + form.resetFields(); | |
29 | + } | |
30 | + }, [visible]); | |
31 | + | |
32 | + const closeModal = () => { | |
33 | + onCancel(); | |
34 | + form.resetFields(); | |
35 | + }; | |
36 | + | |
37 | + function handleSave(feildValue: any) { | |
38 | + const { brand, series } = feildValue; | |
39 | + onChange && | |
40 | + onChange({ | |
41 | + id: brand.value, | |
42 | + name: brand.label, | |
43 | + series: series.map((i: LabeledValue) => ({ id: i.value, name: i.label })), | |
44 | + }); | |
45 | + closeModal(); | |
46 | + } | |
47 | + | |
48 | + return ( | |
49 | + <Modal title={`${value ? '编辑' : '新增'}品牌车系`} style={{ width: 600 }} open={visible} onCancel={closeModal} onOk={form.submit}> | |
50 | + <Form form={form} labelCol={{ span: '6' }} onFinish={handleSave}> | |
51 | + <FormItem label="品牌" name="brand" rules={[{ required: true, message: '请选择品牌' }]}> | |
52 | + <Select | |
53 | + disabled={!!value} // 编辑时不可编辑品牌 | |
54 | + placeholder="请选择" | |
55 | + options={brands} | |
56 | + fieldNames={{ label: 'name', value: 'id' }} | |
57 | + showSearch | |
58 | + labelInValue | |
59 | + optionFilterProp="children" | |
60 | + /> | |
61 | + </FormItem> | |
62 | + <Form.Item | |
63 | + noStyle | |
64 | + shouldUpdate={(prev, curr) => { | |
65 | + if (!_.isEqual(prev.brand, curr.brand)) { | |
66 | + if (value) { | |
67 | + form.setFieldValue('series', value.id === curr.brand.value ? value.series.map((i) => ({ label: i.name, value: i.id })) : []); | |
68 | + } else { | |
69 | + form.setFieldValue('series', []); | |
70 | + } | |
71 | + } | |
72 | + return !_.isEqual(prev.brand, curr.brand); | |
73 | + }} | |
74 | + > | |
75 | + {({ getFieldValue }) => { | |
76 | + const brandId = (getFieldValue('brand') ?? {}).value; | |
77 | + return ( | |
78 | + <FormItem label="车系" name="series" rules={[{ required: true, message: '请选择车系' }]}> | |
79 | + <SeriesSelectorByAll brandId={brandId} disabled={!brandId} disabledTip="请先选择品牌" /> | |
80 | + </FormItem> | |
81 | + ); | |
82 | + }} | |
83 | + </Form.Item> | |
84 | + </Form> | |
85 | + </Modal> | |
86 | + ); | |
87 | +} | ... | ... |
src/pages/carinsur/LoanClientRequires/components/BrandSeriesFormItem.tsx
0 → 100644
1 | +import React, { useEffect, useMemo, useState } from 'react'; | |
2 | +import { Table, Divider, Popconfirm, Button, Row } from 'antd'; | |
3 | +import { PlusOutlined } from '@ant-design/icons'; | |
4 | +import BrandSeriesAddModal from './BrandSeriesAddModal'; | |
5 | +import type { brandSeriesItem } from '../api'; | |
6 | +import TextWithMore from '@/components/TextWithMore'; | |
7 | +import useInitial from '@/hooks/useInitail'; | |
8 | +import { getBrandFilterApi } from '@/common/api'; | |
9 | + | |
10 | +const { Column } = Table; | |
11 | + | |
12 | +interface Props { | |
13 | + readOnly?: boolean; | |
14 | + value?: brandSeriesItem[]; | |
15 | + onChange?: (data: brandSeriesItem[]) => void; | |
16 | +} | |
17 | + | |
18 | +export default function Index({ readOnly = false, value = [], onChange }: Props) { | |
19 | + const [delay, setDelay] = useState(true); | |
20 | + const { data: brands, setParams } = useInitial(getBrandFilterApi, [], {}, delay); | |
21 | + | |
22 | + useEffect(() => { | |
23 | + if (!readOnly) { | |
24 | + setDelay(false); | |
25 | + setParams(undefined, true); | |
26 | + } | |
27 | + }, []); | |
28 | + | |
29 | + const theRestBrands = useMemo(() => { | |
30 | + const selectedBrands = value.map((i) => i.id); | |
31 | + return brands.filter((b) => !selectedBrands.includes(b.id!)); | |
32 | + }, [value, brands]); | |
33 | + | |
34 | + // 新增 | 编辑 | |
35 | + const [addModal, setAddModal] = useState<{ | |
36 | + visible: boolean; | |
37 | + row?: brandSeriesItem; | |
38 | + }>({ | |
39 | + visible: false, | |
40 | + }); | |
41 | + | |
42 | + const handleEdit = (row: brandSeriesItem) => { | |
43 | + setAddModal({ | |
44 | + visible: true, | |
45 | + row, | |
46 | + }); | |
47 | + }; | |
48 | + | |
49 | + const handleDelete = (row: brandSeriesItem) => { | |
50 | + const newVal = [...value]; | |
51 | + const currIdx = newVal.findIndex((i) => i.id === row.id); | |
52 | + newVal.splice(currIdx, 1); | |
53 | + onChange && onChange(newVal); | |
54 | + }; | |
55 | + | |
56 | + const handleUpdate = (res: brandSeriesItem) => { | |
57 | + const newVal = [...value]; | |
58 | + const currIdx = newVal.findIndex((i) => i.id === res.id); // 品牌 id | |
59 | + if (currIdx > -1) { | |
60 | + newVal[currIdx] = res; | |
61 | + } else { | |
62 | + newVal.push(res); | |
63 | + } | |
64 | + onChange && onChange(newVal); | |
65 | + }; | |
66 | + | |
67 | + return ( | |
68 | + <> | |
69 | + {!readOnly && ( | |
70 | + <Row align="middle" justify="end" style={{ marginBottom: 20 }}> | |
71 | + <Button | |
72 | + type="primary" | |
73 | + icon={<PlusOutlined />} | |
74 | + onClick={() => { | |
75 | + setAddModal({ | |
76 | + visible: true, | |
77 | + }); | |
78 | + }} | |
79 | + > | |
80 | + 新增 | |
81 | + </Button> | |
82 | + </Row> | |
83 | + )} | |
84 | + <Table dataSource={value} rowKey="id" size="small"> | |
85 | + <Column title="品牌" dataIndex="name" width={200} render={(t) => t || '-'} /> | |
86 | + <Column title="车系" dataIndex="series" width={200} render={(t) => <TextWithMore title="车系" dataIndex="name" list={t} />} /> | |
87 | + {!readOnly && ( | |
88 | + <Column | |
89 | + title="操作" | |
90 | + width={100} | |
91 | + dataIndex="unit" | |
92 | + render={(text, row: brandSeriesItem) => ( | |
93 | + <span> | |
94 | + <a | |
95 | + onClick={() => { | |
96 | + handleEdit(row); | |
97 | + }} | |
98 | + > | |
99 | + 编辑 | |
100 | + </a> | |
101 | + <Divider type="vertical" /> | |
102 | + <Popconfirm title="是否删除?" onConfirm={() => handleDelete(row)} okText="确定" cancelText="取消"> | |
103 | + <a | |
104 | + onClick={(e) => { | |
105 | + e.preventDefault(); | |
106 | + }} | |
107 | + style={{ color: 'red' }} | |
108 | + > | |
109 | + 删除 | |
110 | + </a> | |
111 | + </Popconfirm> | |
112 | + </span> | |
113 | + )} | |
114 | + /> | |
115 | + )} | |
116 | + </Table> | |
117 | + <BrandSeriesAddModal | |
118 | + brands={theRestBrands} | |
119 | + visible={addModal.visible} | |
120 | + value={addModal.row} | |
121 | + onCancel={() => setAddModal({ visible: false })} | |
122 | + onChange={handleUpdate} | |
123 | + /> | |
124 | + </> | |
125 | + ); | |
126 | +} | ... | ... |
src/pages/carinsur/LoanClientRequires/components/BrandSeriesModal.tsx
0 → 100644
1 | +import React from 'react'; | |
2 | +import { Modal } from 'antd'; | |
3 | +import BrandSeriesFormItem from './BrandSeriesFormItem'; | |
4 | +import type { brandSeriesItem } from '../api'; | |
5 | + | |
6 | +interface Props { | |
7 | + brandSeriesModal: { visible: boolean; brandSeries?: brandSeriesItem[] }; | |
8 | + onCancel: () => void; | |
9 | +} | |
10 | + | |
11 | +export default function Index({ brandSeriesModal, onCancel }: Props) { | |
12 | + const { visible, brandSeries = [] } = brandSeriesModal; | |
13 | + return ( | |
14 | + <Modal title="部分品牌部分车系" open={visible} onCancel={() => onCancel()} footer={null} width={600}> | |
15 | + <BrandSeriesFormItem value={brandSeries} readOnly /> | |
16 | + </Modal> | |
17 | + ); | |
18 | +} | ... | ... |
src/pages/carinsur/LoanClientRequires/components/Filter/index.tsx
0 → 100644
1 | +import React from 'react'; | |
2 | +import { Row, Select } from 'antd'; | |
3 | +import _ from 'lodash'; | |
4 | +import type { PageParams } from '../../api'; | |
5 | +import { getBrandFilterApi, getSaleSeries, getShopApi } from '@/common/api'; | |
6 | +import useInitial from '@/hooks/useInitail'; | |
7 | + | |
8 | +interface Props { | |
9 | + innerParams?: any; | |
10 | + setParams: (params: PageParams, refreshing: boolean) => void; | |
11 | +} | |
12 | + | |
13 | +function Filter({ innerParams, setParams }: Props) { | |
14 | + const { data: brands } = useInitial(getBrandFilterApi, [], {}); | |
15 | + const { data: series } = useInitial(getSaleSeries, [], {}); | |
16 | + const { data: shops } = useInitial<CommonApi.OptionVO[], undefined>(getShopApi, [], undefined); | |
17 | + | |
18 | + const onChange = _.debounce((newParams) => { | |
19 | + setParams({ ...innerParams, ...newParams }, true); | |
20 | + }, 600); | |
21 | + | |
22 | + return ( | |
23 | + <Row style={{ display: 'flex', flex: 1 }}> | |
24 | + <Select | |
25 | + style={{ width: 200 }} | |
26 | + showSearch | |
27 | + allowClear | |
28 | + // optionFilterProp="children" | |
29 | + placeholder="筛选品牌" | |
30 | + options={brands} | |
31 | + fieldNames={{ label: 'name', value: 'id' }} | |
32 | + onChange={(value) => { | |
33 | + onChange({ brandId: value }); | |
34 | + }} | |
35 | + /> | |
36 | + <Select | |
37 | + style={{ width: 200, marginLeft: 20 }} | |
38 | + showSearch | |
39 | + allowClear | |
40 | + // optionFilterProp="children" | |
41 | + placeholder="筛选车系" | |
42 | + options={series} | |
43 | + fieldNames={{ label: 'name', value: 'id' }} | |
44 | + onChange={(value) => { | |
45 | + onChange({ seriesId: value }); | |
46 | + }} | |
47 | + /> | |
48 | + <Select | |
49 | + style={{ width: 200, marginLeft: 20 }} | |
50 | + showSearch | |
51 | + allowClear | |
52 | + // optionFilterProp="children" | |
53 | + placeholder="筛选门店" | |
54 | + options={shops} | |
55 | + fieldNames={{ label: 'name', value: 'id' }} | |
56 | + onChange={(value) => { | |
57 | + onChange({ shopId: value }); | |
58 | + }} | |
59 | + /> | |
60 | + </Row> | |
61 | + ); | |
62 | +} | |
63 | + | |
64 | +export default Filter; | ... | ... |
src/pages/carinsur/LoanClientRequires/components/LoanTypeFormItem.tsx
0 → 100644
1 | +import { Form, InputNumber, Radio } from 'antd'; | |
2 | +import React from 'react'; | |
3 | +import { LoanTypeEnum } from '../../entity'; | |
4 | + | |
5 | +interface Props { | |
6 | + form: any; | |
7 | + name: string; | |
8 | + label: string; | |
9 | +} | |
10 | + | |
11 | +export default function Index({ form, name, label }: Props) { | |
12 | + return ( | |
13 | + <Form.Item label={label} name={[name, 'type']} rules={[{ required: true, message: '请选择' }]}> | |
14 | + <Radio.Group> | |
15 | + <Radio value={LoanTypeEnum.购买即可}>购买即可</Radio> | |
16 | + <Radio value={LoanTypeEnum.购保要求金额}> | |
17 | + <div style={{ display: 'flex', alignItems: 'center', flexDirection: 'row' }}> | |
18 | + <span>购保要求金额</span> | |
19 | + <Form.Item | |
20 | + noStyle | |
21 | + shouldUpdate={(prev, curr) => { | |
22 | + if (prev[name] && curr[name] && prev[name].type !== curr[name].type && curr[name].type !== LoanTypeEnum.购保要求金额) { | |
23 | + form.setFieldValue([name, 'amount'], undefined); | |
24 | + } | |
25 | + return prev[name] !== curr[name]; | |
26 | + }} | |
27 | + > | |
28 | + {({ getFieldValue }) => { | |
29 | + const type = (getFieldValue(name) ?? {}).type; | |
30 | + if (type !== LoanTypeEnum.购保要求金额) return null; | |
31 | + return ( | |
32 | + <Form.Item | |
33 | + style={{ marginBottom: 0, marginLeft: 10 }} | |
34 | + name={[name, 'amount']} | |
35 | + required | |
36 | + rules={[{ required: true, message: '请填写金额' }]} | |
37 | + > | |
38 | + <InputNumber addonBefore="≥" addonAfter="元" style={{ minWidth: 120 }} min={0.01} precision={2} placeholder="请填写金额" /> | |
39 | + </Form.Item> | |
40 | + ); | |
41 | + }} | |
42 | + </Form.Item> | |
43 | + </div> | |
44 | + </Radio> | |
45 | + <Radio value={LoanTypeEnum.无要求}>无要求</Radio> | |
46 | + </Radio.Group> | |
47 | + </Form.Item> | |
48 | + ); | |
49 | +} | ... | ... |
src/pages/carinsur/LoanClientRequires/components/SeriesSelectorByAll.tsx
0 → 100644
1 | +import React, { useEffect, useState } from 'react'; | |
2 | +import { Select, Spin } from 'antd'; | |
3 | +import type { LabelInValueType } from 'rc-select/lib/Select'; | |
4 | +import { getSeriesApi } from '@/common/api'; | |
5 | +import useInitial from '@/hooks/useInitail'; | |
6 | + | |
7 | +interface Props { | |
8 | + brandId: number; // 根据品牌 id 筛选车系 | |
9 | + value?: LabelInValueType[]; | |
10 | + onChange?: (value: LabelInValueType[]) => void; | |
11 | + disabled?: boolean; | |
12 | + disabledTip?: string; | |
13 | + defaultTip?: string; | |
14 | + style?: React.CSSProperties; | |
15 | +} | |
16 | + | |
17 | +export default function Index({ | |
18 | + brandId, | |
19 | + value = [], | |
20 | + onChange, | |
21 | + disabled = false, | |
22 | + disabledTip = '请选择车系', | |
23 | + defaultTip = '请选择车系', | |
24 | + style, | |
25 | +}: Props) { | |
26 | + const [delay, setDelay] = useState(true); | |
27 | + const [allList, setAllList] = useState<any[]>([]); | |
28 | + const { data: list, setParams, loading } = useInitial<CommonApi.OptionVO[], number>(getSeriesApi, [], brandId, delay); | |
29 | + | |
30 | + useEffect(() => { | |
31 | + if (brandId) { | |
32 | + setParams(brandId, true); | |
33 | + setDelay(false); | |
34 | + } | |
35 | + }, [brandId]); | |
36 | + | |
37 | + useEffect(() => { | |
38 | + if (list && list.length > 0) { | |
39 | + // @ts-ignore | |
40 | + const newPostList = [{ id: -1, name: '全部车系' }].concat(list); // 前端手动在列表头部增加一个 {id: -1, name: '全部车系'} 的选项 | |
41 | + setAllList(newPostList); | |
42 | + } else { | |
43 | + setAllList([]); | |
44 | + } | |
45 | + }, [list]); | |
46 | + | |
47 | + return ( | |
48 | + <Spin spinning={loading && !disabled}> | |
49 | + <Select | |
50 | + labelInValue | |
51 | + value={value} | |
52 | + mode="multiple" | |
53 | + onChange={(value) => { | |
54 | + if (value && value.findIndex((i) => i.value === -1) > -1) { | |
55 | + onChange && onChange([{ key: '-1', label: '全部车系', value: -1 }]); | |
56 | + } else { | |
57 | + onChange && onChange(value); | |
58 | + } | |
59 | + }} | |
60 | + style={style || { flex: 1 }} | |
61 | + allowClear | |
62 | + placeholder={disabled ? disabledTip : defaultTip} | |
63 | + showSearch | |
64 | + optionFilterProp="children" | |
65 | + disabled={disabled} | |
66 | + // getPopupContainer={(triggerNode) => triggerNode.parentNode} | |
67 | + > | |
68 | + {allList.map((brand) => ( | |
69 | + <Select.Option key={brand.id} value={brand.id!} disabled={value.findIndex((i) => i.value === -1) > -1 && brand.id !== -1}> | |
70 | + {brand.name} | |
71 | + </Select.Option> | |
72 | + ))} | |
73 | + </Select> | |
74 | + </Spin> | |
75 | + ); | |
76 | +} | ... | ... |
src/pages/carinsur/LoanClientRequires/components/ShopSelectorByAll.tsx
0 → 100644
1 | +import React, { useEffect, useState } from 'react'; | |
2 | +import { Select, Spin } from 'antd'; | |
3 | +import type { LabelInValueType } from 'rc-select/lib/Select'; | |
4 | +import { getShopApi } from '@/common/api'; | |
5 | +import useInitial from '@/hooks/useInitail'; | |
6 | + | |
7 | +interface Props { | |
8 | + value?: LabelInValueType[]; | |
9 | + onChange?: (value: LabelInValueType[]) => void; | |
10 | + disabled?: boolean; | |
11 | + disabledTip?: string; | |
12 | + defaultTip?: string; | |
13 | + style?: React.CSSProperties; | |
14 | +} | |
15 | + | |
16 | +export default function Index({ value = [], onChange, disabled = false, disabledTip = '请选择门店', defaultTip = '请选择门店', style }: Props) { | |
17 | + const [delay, setDelay] = useState(true); | |
18 | + const [allList, setAllList] = useState<any[]>([]); | |
19 | + const { data: list, setParams, loading } = useInitial<CommonApi.OptionVO[], undefined>(getShopApi, [], undefined, delay); | |
20 | + | |
21 | + useEffect(() => { | |
22 | + if (list && list.length > 0) { | |
23 | + // @ts-ignore | |
24 | + const newPostList = [{ id: -1, name: '全部门店' }].concat(list); // 前端手动在列表头部增加一个 {id: -1, name: '全部门店'} 的选项 | |
25 | + setAllList(newPostList); | |
26 | + } else { | |
27 | + setAllList([]); | |
28 | + } | |
29 | + }, [list]); | |
30 | + | |
31 | + useEffect(() => { | |
32 | + setParams(undefined, true); | |
33 | + setDelay(false); | |
34 | + }, []); | |
35 | + | |
36 | + return ( | |
37 | + <Spin spinning={loading && !disabled}> | |
38 | + <Select | |
39 | + labelInValue | |
40 | + value={value} | |
41 | + mode="multiple" | |
42 | + onChange={(value) => { | |
43 | + if (value && value.findIndex((i) => i.value === -1) > -1) { | |
44 | + onChange && onChange([{ key: '-1', label: '全部门店', value: -1 }]); | |
45 | + } else { | |
46 | + onChange && onChange(value); | |
47 | + } | |
48 | + }} | |
49 | + style={style || { flex: 1 }} | |
50 | + allowClear | |
51 | + placeholder={disabled ? disabledTip : defaultTip} | |
52 | + showSearch | |
53 | + optionFilterProp="children" | |
54 | + disabled={disabled} | |
55 | + // getPopupContainer={(triggerNode) => triggerNode.parentNode} | |
56 | + > | |
57 | + {allList.map((brand) => ( | |
58 | + <Select.Option key={brand.id} value={brand.id!} disabled={value.findIndex((i) => i.value === -1) > -1 && brand.id !== -1}> | |
59 | + {brand.name} | |
60 | + </Select.Option> | |
61 | + ))} | |
62 | + </Select> | |
63 | + </Spin> | |
64 | + ); | |
65 | +} | ... | ... |
src/pages/carinsur/LoanClientRequires/index.tsx
0 → 100644
1 | +import React, { useState } from 'react'; | |
2 | +import { Button, Card, ConfigProvider, Divider, message, Popconfirm, Row, Table } from 'antd'; | |
3 | +import { PageHeaderWrapper } from '@ant-design/pro-layout'; | |
4 | +import zhCN from 'antd/lib/locale-provider/zh_CN'; | |
5 | +import usePagination from '@/hooks/usePagination'; | |
6 | +import { PlusOutlined } from '@ant-design/icons'; | |
7 | +import AddModal from './components/AddModal'; | |
8 | +import Filter from './components/Filter'; | |
9 | +import * as API from './api'; | |
10 | +import _ from 'lodash'; | |
11 | +import TextWithMore from '@/components/TextWithMore'; | |
12 | +import { LoanTypeEnum } from '../entity'; | |
13 | +import BrandSeriesModal from './components/BrandSeriesModal'; | |
14 | + | |
15 | +const { Column } = Table; | |
16 | + | |
17 | +export default function Index() { | |
18 | + // 新增 | 编辑 | |
19 | + const [addModal, setAddModal] = useState<{ | |
20 | + visible: boolean; | |
21 | + row?: API.Item; | |
22 | + }>({ | |
23 | + visible: false, | |
24 | + }); | |
25 | + // 查看部分品牌车系 | |
26 | + const [brandSeriesModal, setBrandSeriesModal] = useState<{ | |
27 | + visible: boolean; | |
28 | + brandSeries?: API.brandSeriesItem[]; | |
29 | + }>({ | |
30 | + visible: false, | |
31 | + brandSeries: [], | |
32 | + }); | |
33 | + | |
34 | + const { list, paginationConfig, loading, innerParams, setParams } = usePagination<API.Item>(API.getLCRPage); | |
35 | + | |
36 | + const handleDelete = (row: API.Item) => { | |
37 | + const { id } = row; | |
38 | + API.delLCR(id!) | |
39 | + .then((res) => { | |
40 | + message.success(res.result); | |
41 | + setParams({ ...innerParams }, true); | |
42 | + }) | |
43 | + .catch((e) => { | |
44 | + message.error(e.message); | |
45 | + }); | |
46 | + }; | |
47 | + | |
48 | + const handleEdit = (row: API.Item) => { | |
49 | + setAddModal({ | |
50 | + visible: true, | |
51 | + row, | |
52 | + }); | |
53 | + }; | |
54 | + | |
55 | + const renderLoanTypeEle = (value: API.LoanType) => { | |
56 | + return value ? (value.type !== LoanTypeEnum.购保要求金额 ? LoanTypeEnum[value.type] : `保费 ≥ ${value.amount}元`) : '--'; | |
57 | + }; | |
58 | + | |
59 | + return ( | |
60 | + <PageHeaderWrapper title="贷款期客户保险要求"> | |
61 | + <ConfigProvider locale={zhCN}> | |
62 | + <Card> | |
63 | + <Row align="middle" justify="space-between" style={{ marginBottom: 20 }}> | |
64 | + <Filter innerParams={innerParams} setParams={setParams} /> | |
65 | + <Button | |
66 | + type="primary" | |
67 | + icon={<PlusOutlined />} | |
68 | + onClick={() => { | |
69 | + setAddModal({ | |
70 | + visible: true, | |
71 | + }); | |
72 | + }} | |
73 | + > | |
74 | + 新增 | |
75 | + </Button> | |
76 | + </Row> | |
77 | + <Table | |
78 | + loading={loading} | |
79 | + dataSource={list} | |
80 | + pagination={paginationConfig} | |
81 | + scroll={{ y: 800 }} | |
82 | + rowKey="id" | |
83 | + onChange={(_pagination) => setParams({ ..._pagination }, true)} | |
84 | + > | |
85 | + <Column | |
86 | + title="适用品牌车系" | |
87 | + dataIndex="allBrandSeries" | |
88 | + render={(value, row: API.Item) => | |
89 | + value ? ( | |
90 | + '全部品牌全部车系' | |
91 | + ) : ( | |
92 | + <a | |
93 | + onClick={(e) => { | |
94 | + e.preventDefault(); | |
95 | + setBrandSeriesModal({ visible: true, brandSeries: row.brandSeries }); | |
96 | + }} | |
97 | + > | |
98 | + 查看 | |
99 | + </a> | |
100 | + ) | |
101 | + } | |
102 | + /> | |
103 | + <Column title="适用门店" dataIndex="useShopIds" render={(value) => <TextWithMore title="适用门店" dataIndex="shopName" list={value} />} /> | |
104 | + <Column title="新保交强险" dataIndex="newTci" render={renderLoanTypeEle} /> | |
105 | + <Column title="新保商业险" dataIndex="newVci" render={renderLoanTypeEle} /> | |
106 | + <Column title="新保驾意险" dataIndex="newTai" render={renderLoanTypeEle} /> | |
107 | + <Column title="续保交强险" dataIndex="reNewTci" render={renderLoanTypeEle} /> | |
108 | + <Column title="续保商业险" dataIndex="reNewVci" render={renderLoanTypeEle} /> | |
109 | + <Column title="续保驾意险" dataIndex="reNewTai" render={renderLoanTypeEle} /> | |
110 | + <Column | |
111 | + title="操作" | |
112 | + dataIndex="btns" | |
113 | + render={(text, row: API.Item) => ( | |
114 | + <span> | |
115 | + <a | |
116 | + onClick={(e) => { | |
117 | + e.preventDefault(); | |
118 | + handleEdit(row); | |
119 | + }} | |
120 | + > | |
121 | + 编辑 | |
122 | + </a> | |
123 | + <Divider type="vertical" /> | |
124 | + <Popconfirm title="是否删除?" onConfirm={() => handleDelete(row)}> | |
125 | + <a | |
126 | + onClick={(e) => { | |
127 | + e.preventDefault(); | |
128 | + }} | |
129 | + style={{ color: 'red' }} | |
130 | + > | |
131 | + 删除 | |
132 | + </a> | |
133 | + </Popconfirm> | |
134 | + </span> | |
135 | + )} | |
136 | + /> | |
137 | + </Table> | |
138 | + </Card> | |
139 | + <AddModal | |
140 | + visible={addModal.visible} | |
141 | + row={addModal.row} | |
142 | + onCancel={() => { | |
143 | + setAddModal({ visible: false }); | |
144 | + }} | |
145 | + onRefresh={() => setParams({ ...innerParams }, true)} | |
146 | + /> | |
147 | + <BrandSeriesModal | |
148 | + brandSeriesModal={brandSeriesModal} | |
149 | + onCancel={() => { | |
150 | + setBrandSeriesModal({ visible: false }); | |
151 | + }} | |
152 | + /> | |
153 | + </ConfigProvider> | |
154 | + </PageHeaderWrapper> | |
155 | + ); | |
156 | +} | ... | ... |
src/pages/carinsur/entity.ts
0 → 100644