Commit 8bd54c91b7a06b98e203e822c3059ffc5e9cf4ec

Authored by 徐欣
1 parent 6e8bd92c

feat(carinsur): 新增贷款期客户保险配置功能

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 InsuranceItem {
  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: InsuranceItem;
  22 + newVci: InsuranceItem;
  23 + newTai: InsuranceItem;
  24 + reNewTci: InsuranceItem;
  25 + reNewVci: InsuranceItem;
  26 + reNewTai: InsuranceItem;
  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}/erp/express/logistics/article/page`, { params });
  40 +}
  41 +
  42 +/**
  43 + * 新增/编辑
  44 + */
  45 +export function addLCR(params?: Item): http.PromiseResp<void> {
  46 + return request.post(`${ANGEL_Host}/erp/express/logistics/article/save`, { ...params });
  47 +}
  48 +
  49 +/**
  50 + * 删除
  51 + */
  52 +export function delLCR(id: number): http.PromisePageResp<void> {
  53 + return request.post(`${ANGEL_Host}/erp/express/logistics/article/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 +
  6 +interface Props {
  7 + visible: boolean;
  8 + row?: API.Item;
  9 + onCancel: () => void;
  10 + onRefresh: () => void;
  11 +}
  12 +
  13 +function AddModal({ visible, row, onCancel, onRefresh }: Props) {
  14 + const { id } = row ?? {};
  15 + const [form] = Form.useForm();
  16 +
  17 + const saveHook = useRequest(API.addLCR, {
  18 + manual: true,
  19 + formatResult: (res) => res,
  20 + });
  21 +
  22 + useEffect(() => {
  23 + if (visible && row) {
  24 + form.setFieldsValue({
  25 + ...row,
  26 + });
  27 + } else {
  28 + form.resetFields();
  29 + }
  30 + }, [visible, row]);
  31 +
  32 + const handleSave = (feildValue: any) => {
  33 + saveHook
  34 + .run({
  35 + ...feildValue,
  36 + })
  37 + .then((res) => {
  38 + message.success(res.result);
  39 + onCancel();
  40 + onRefresh();
  41 + })
  42 + .catch((e) => {
  43 + message.error(e.message);
  44 + });
  45 + };
  46 +
  47 + return (
  48 + <Modal title={`${id ? '编辑' : '新增'}`} open={visible} confirmLoading={saveHook.loading} onCancel={onCancel} onOk={form.submit} width="50%">
  49 + <Form form={form} onFinish={handleSave} labelCol={{ span: 4 }} wrapperCol={{ span: 18 }}>
  50 + <Form.Item name="id" hidden />
  51 + <Form.Item label="适用品牌车系" name="allBrandSeries" rules={[{ required: true, message: '请选择' }]}>
  52 + <Radio.Group>
  53 + <Radio value={true}>全部品牌全部车系</Radio>
  54 + <Radio value={false}>部分品牌部分车系</Radio>
  55 + </Radio.Group>
  56 + </Form.Item>
  57 + <Form.Item
  58 + noStyle
  59 + shouldUpdate={(prev, curr) => {
  60 + return prev.allBrandSeries !== curr.allBrandSeries;
  61 + }}
  62 + >
  63 + {({ getFieldValue }) => {
  64 + const allBrandSeries = getFieldValue('allBrandSeries');
  65 + if (allBrandSeries) {
  66 + return null;
  67 + }
  68 + return null; // todo
  69 + }}
  70 + </Form.Item>
  71 + </Form>
  72 + </Modal>
  73 + );
  74 +}
  75 +
  76 +export default memo(AddModal);
... ...
src/pages/carinsur/LoanClientRequires/components/BrandSelectorByAll.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 { getAllBrandApi } 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 [brandList, setBrandList] = useState<any[]>([]);
  19 + const { data: list, setParams, loading } = useInitial<CommonApi.OptionVO[], undefined>(getAllBrandApi, [], 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 + setBrandList(newPostList);
  26 + } else {
  27 + setBrandList([]);
  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 + {brandList.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/components/Filter/index.tsx 0 → 100644
  1 +import React from 'react';
  2 +import { Row } from 'antd';
  3 +import _ from 'lodash';
  4 +import type { PageParams } from '../../api';
  5 +
  6 +interface Props {
  7 + innerParams?: any;
  8 + setParams: (params: PageParams, refreshing: boolean) => void;
  9 +}
  10 +
  11 +function Filter({ innerParams, setParams }: Props) {
  12 + const onChange = _.debounce((newParams) => {
  13 + setParams({ ...innerParams, ...newParams }, true);
  14 + }, 600);
  15 +
  16 + return (
  17 + <Row style={{ display: 'flex', flex: 1 }}>
  18 + {/* <Select
  19 + showSearch
  20 + allowClear
  21 + optionFilterProp="children"
  22 + placeholder="筛选授权角色"
  23 + onChange={(value) => {
  24 + onChange({ roles: value });
  25 + }}
  26 + >
  27 + {roleList.map((i) => (
  28 + <Option value={i.roleCode} key={i.roleCode}>
  29 + {i.roleName}
  30 + </Option>
  31 + ))}
  32 + </Select>
  33 + <Select
  34 + showSearch
  35 + allowClear
  36 + optionFilterProp="children"
  37 + placeholder="筛选往来对象类型"
  38 + options={TradeTypeOptions}
  39 + onChange={(value) => {
  40 + onChange({ tradeObjType: value });
  41 + }}
  42 + /> */}
  43 + </Row>
  44 + );
  45 +}
  46 +
  47 +export default Filter;
... ...
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 [brandList, setBrandList] = useState<any[]>([]);
  28 + const { data: list, setParams, loading } = useInitial<CommonApi.OptionVO[], number>(getSeriesApi, [], brandId, delay);
  29 +
  30 + useEffect(() => {
  31 + if (list && list.length > 0) {
  32 + // @ts-ignore
  33 + const newPostList = [{ id: -1, name: '全部车系' }].concat(list); // 前端手动在列表头部增加一个 {id: -1, name: '全部车系'} 的选项
  34 + setBrandList(newPostList);
  35 + } else {
  36 + setBrandList([]);
  37 + }
  38 + }, [list]);
  39 +
  40 + useEffect(() => {
  41 + setParams(brandId, true);
  42 + setDelay(false);
  43 + }, []);
  44 +
  45 + return (
  46 + <Spin spinning={loading && !disabled}>
  47 + <Select
  48 + labelInValue
  49 + value={value}
  50 + mode="multiple"
  51 + onChange={(value) => {
  52 + if (value && value.findIndex((i) => i.value === -1) > -1) {
  53 + onChange && onChange([{ key: '-1', label: '全部车系', value: -1 }]);
  54 + } else {
  55 + onChange && onChange(value);
  56 + }
  57 + }}
  58 + style={style || { flex: 1 }}
  59 + allowClear
  60 + placeholder={disabled ? disabledTip : defaultTip}
  61 + showSearch
  62 + optionFilterProp="children"
  63 + disabled={disabled}
  64 + // getPopupContainer={(triggerNode) => triggerNode.parentNode}
  65 + >
  66 + {brandList.map((brand) => (
  67 + <Select.Option key={brand.id} value={brand.id!} disabled={value.findIndex((i) => i.value === -1) > -1 && brand.id !== -1}>
  68 + {brand.name}
  69 + </Select.Option>
  70 + ))}
  71 + </Select>
  72 + </Spin>
  73 + );
  74 +}
... ...
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 +
  13 +const { Column } = Table;
  14 +
  15 +export default function Index() {
  16 + // 新增 | 编辑
  17 + const [addModal, setAddModal] = useState<{
  18 + visible: boolean;
  19 + row?: API.Item;
  20 + }>({
  21 + visible: false,
  22 + });
  23 +
  24 + const { list, paginationConfig, loading, innerParams, setParams } = usePagination<API.Item>(API.getLCRPage);
  25 +
  26 + const handleDelete = (row: API.Item) => {
  27 + const { id } = row;
  28 + API.delLCR(id!)
  29 + .then((res) => {
  30 + message.success(res.result);
  31 + setParams({ ...innerParams }, true);
  32 + })
  33 + .catch((e) => {
  34 + message.error(e.message);
  35 + });
  36 + };
  37 +
  38 + const handleEdit = (row: API.Item) => {
  39 + setAddModal({
  40 + visible: true,
  41 + row,
  42 + });
  43 + };
  44 +
  45 + return (
  46 + <PageHeaderWrapper title="贷款期客户保险要求">
  47 + <ConfigProvider locale={zhCN}>
  48 + <Card>
  49 + <Row align="middle" justify="space-between" style={{ marginBottom: 20 }}>
  50 + <Filter innerParams={innerParams} setParams={setParams} />
  51 + <Button
  52 + type="primary"
  53 + icon={<PlusOutlined />}
  54 + onClick={() => {
  55 + setAddModal({
  56 + visible: true,
  57 + });
  58 + }}
  59 + >
  60 + 新增
  61 + </Button>
  62 + </Row>
  63 + <Table
  64 + loading={loading}
  65 + dataSource={list}
  66 + pagination={paginationConfig}
  67 + scroll={{ y: 800 }}
  68 + rowKey="id"
  69 + onChange={(_pagination) => setParams({ ..._pagination }, true)}
  70 + >
  71 + <Column title="适用品牌车系" dataIndex="allBrandSeries" render={(value) => (value ? '全部品牌全部车系' : '查看 todo')} />
  72 + <Column title="适用门店" dataIndex="useShopIds" render={(value) => <TextWithMore title="适用门店" dataIndex="shopName" list={value} />} />
  73 + <Column title="新保交强险" dataIndex="newTci" />
  74 + <Column title="新保商业险" dataIndex="newVci" />
  75 + <Column title="新保驾意险" dataIndex="newTai" />
  76 + <Column title="续保交强险" dataIndex="renewTci" />
  77 + <Column title="续保商业险" dataIndex="renewVci" />
  78 + <Column title="续保驾意险" dataIndex="renewTai" />
  79 + <Column
  80 + title="操作"
  81 + dataIndex="btns"
  82 + render={(text, row: API.Item) => (
  83 + <span>
  84 + <a
  85 + onClick={(e) => {
  86 + e.preventDefault();
  87 + handleEdit(row);
  88 + }}
  89 + >
  90 + 编辑
  91 + </a>
  92 + <Divider type="vertical" />
  93 + <Popconfirm title="是否删除?" onConfirm={() => handleDelete(row)}>
  94 + <a
  95 + onClick={(e) => {
  96 + e.preventDefault();
  97 + }}
  98 + style={{ color: 'red' }}
  99 + >
  100 + 删除
  101 + </a>
  102 + </Popconfirm>
  103 + </span>
  104 + )}
  105 + />
  106 + </Table>
  107 + </Card>
  108 + <AddModal
  109 + visible={addModal.visible}
  110 + row={addModal.row}
  111 + onCancel={() => {
  112 + setAddModal({ visible: false });
  113 + }}
  114 + onRefresh={() => setParams({ ...innerParams }, true)}
  115 + />
  116 + </ConfigProvider>
  117 + </PageHeaderWrapper>
  118 + );
  119 +}
... ...
src/pages/carinsur/entity.ts 0 → 100644
  1 +export interface ShopOption {
  2 + shopId: number;
  3 + shopName: string;
  4 +}
  5 +export interface IdNameOption {
  6 + id: number;
  7 + name: string;
  8 +}
0 9 \ No newline at end of file
... ...