Commit 9c252c1c1c84dbace248909415422fb1473c1dc6

Authored by 莫红玲
1 parent 1a0ae662

feat(fvm): 分销价格配置联调

config/routers/fvm.ts
... ... @@ -403,4 +403,8 @@ export default [
403 403 path: 'fvm/distribute/record', //分销商备案
404 404 component: './stock/Distribute/Records',
405 405 },
  406 + {
  407 + path: 'fvm/distribute/price', //分销价格配置
  408 + component: './stock/Distribute/Price',
  409 + },
406 410 ];
... ...
src/pages/stock/Components/CarTableSelect.tsx 0 → 100644
  1 +import React, { useEffect, useState } from 'react';
  2 +import { Button, Popconfirm, Card, Table, Modal, Form, Select, Space } from 'antd';
  3 +import useInitial from '@/hooks/useInitail';
  4 +import { getOnsaleBrandApi, getOnsaleSeriesApi, getOnsaleSpecApi } from '@/pages/stock/Components/api';
  5 +import { getGroupCasBrandApi, getSeriesApi, getSpecApi } from '@/common/api';
  6 +
  7 +interface Props {
  8 + onChange?: (pa: any) => void;
  9 + value?: any[];
  10 + disabled?: boolean;
  11 + bizType?: number;
  12 +}
  13 +
  14 +interface Item {
  15 + label: string;
  16 + value: number;
  17 + key?: number;
  18 +}
  19 +const { Column } = Table;
  20 +const { Option } = Select;
  21 +
  22 +export default function Index({ onChange, value, disabled, bizType = 1 }: Props) {
  23 + const [form] = Form.useForm();
  24 + const { data: brandList } = useInitial(bizType == 2 ? getGroupCasBrandApi : getOnsaleBrandApi, [], {});
  25 + // 控制Modal是否可见
  26 + const [visible, setVisible] = useState<boolean>(false);
  27 + // 控制是否可见车型下拉框
  28 + const [specVisible, setSpecVisible] = useState<boolean>(false);
  29 + //存储表格当前车系
  30 + const [currentItem, setCurrentItem] = useState<any>();
  31 + //存储Modal中可选的车系
  32 + const [series, setSeries] = useState<any>([]);
  33 + //存储接口返回的车型
  34 + const [specList, setSpecList] = useState<any>([]);
  35 + //存储Modal选择的品牌和车系作为表格的数据源
  36 + const [savaData, setSavadata] = useState<any>([]);
  37 + // 存储已经选中的车系
  38 + const [selectedSeries, setSelectedSeries] = useState<any[]>([]);
  39 +
  40 + useEffect(() => {
  41 + if (value && value.length) {
  42 + setSelectedSeries([...value]);
  43 + setSavadata([...value]);
  44 + }
  45 + }, [value]);
  46 +
  47 + const _onOk = () => {
  48 + form
  49 + .validateFields()
  50 + .then((fileds) => {
  51 + if (specVisible) {
  52 + const { Spec } = fileds;
  53 + const _specs = Spec.map((item: Item) => ({
  54 + specId: item.value,
  55 + specName: item.label,
  56 + }));
  57 + currentItem.specs = _specs;
  58 + currentItem.authType = 2;
  59 + const tempData = savaData.map((item: any) => (item.seriesId === currentItem.seriesId ? currentItem : item));
  60 + setSavadata([...tempData]);
  61 + onChange && onChange(tempData);
  62 + setVisible(false);
  63 + setSpecVisible(false);
  64 + return;
  65 + }
  66 + const { Series, brand } = fileds;
  67 +
  68 + const tempArray = Series.map((item: any) => ({
  69 + brandName: brand.label,
  70 + brandId: brand.value,
  71 + seriesName: item.label,
  72 + seriesId: Number(item.key),
  73 + authType: 1, //1全部2部分
  74 + }));
  75 +
  76 + selectedSeries.push(...tempArray);
  77 +
  78 + savaData.push(...tempArray);
  79 + onChange && onChange(selectedSeries);
  80 + setVisible(false);
  81 + })
  82 + .catch((err) => console.log(err.message));
  83 + };
  84 +
  85 + // 选择部分车辆表格==》删除车系
  86 + const onDelete = (record: any) => {
  87 + const _savaData = savaData.filter((item: any) => item.seriesId != record.seriesId);
  88 + const _selectedSeries = selectedSeries.filter((item) => item.seriesId != record.seriesId);
  89 + setSavadata([..._savaData]);
  90 + onChange && onChange(_savaData);
  91 + setSelectedSeries([..._selectedSeries]);
  92 + };
  93 +
  94 + // 选择部分车辆表格==》编辑选择部分车型
  95 + const onSelectSpec = async (record: any) => {
  96 + const specApi = bizType == 1 ? getOnsaleSpecApi : getSpecApi;
  97 + const { data } = await specApi(record.seriesId);
  98 + setCurrentItem(record);
  99 + setSpecList(data);
  100 + setSpecVisible(true);
  101 + setVisible(true);
  102 + };
  103 +
  104 + return (
  105 + <>
  106 + <Card>
  107 + <div style={{ display: 'flex', justifyContent: 'flex-end' }}>
  108 + {!disabled && (
  109 + <Button
  110 + type="link"
  111 + disabled={disabled}
  112 + onClick={() => {
  113 + setVisible(true);
  114 + form.resetFields();
  115 + }}
  116 + >
  117 + 新增
  118 + </Button>
  119 + )}
  120 + </div>
  121 +
  122 + <Table dataSource={value} rowKey={(record) => String(record.seriesId)} bordered>
  123 + <Column
  124 + title="品牌"
  125 + dataIndex="brandName"
  126 + key="brandName"
  127 + filters={brandList.map((item) => ({
  128 + text: item.name,
  129 + value: item.name,
  130 + }))}
  131 + onFilter={(value, record: any) => {
  132 + return record.brandName.indexOf(value) === 0;
  133 + }}
  134 + />
  135 + <Column title="车系" dataIndex="seriesName" key="seriesName" />
  136 + <Column
  137 + title="车型"
  138 + dataIndex="specs"
  139 + key="specs"
  140 + render={(_, record: any) =>
  141 + record.specs && record.specs.length ? <span>{record.specs.map((item: any) => item.specName).join(',')}</span> : <span>全部</span>
  142 + }
  143 + />
  144 + {!disabled && (
  145 + <Column
  146 + title="操作"
  147 + key="operation"
  148 + render={(_, record) => (
  149 + <Space>
  150 + <Button type="link" style={{ padding: 0 }} onClick={() => onSelectSpec(record)} disabled={disabled}>
  151 + 编辑车型
  152 + </Button>
  153 +
  154 + <Popconfirm title="确定删除?" okText="确定" style={{ padding: 0 }} cancelText="取消" onConfirm={() => onDelete(record)}>
  155 + <Button type="link" danger disabled={disabled}>
  156 + 删除
  157 + </Button>
  158 + </Popconfirm>
  159 + </Space>
  160 + )}
  161 + />
  162 + )}
  163 + </Table>
  164 + </Card>
  165 +
  166 + {/* 选择品牌和车系 */}
  167 + <Modal
  168 + title="选择车辆信息"
  169 + maskClosable={false}
  170 + visible={visible}
  171 + onOk={() => form.submit()}
  172 + onCancel={() => {
  173 + setVisible(false);
  174 + setSpecVisible(false);
  175 + }}
  176 + afterClose={() => {
  177 + form.resetFields();
  178 + setCurrentItem({});
  179 + }}
  180 + >
  181 + <Form onFinish={_onOk} form={form}>
  182 + {specVisible ? (
  183 + <Form.Item label="车型" name="Spec" rules={[{ required: true, message: '请选择车型' }]}>
  184 + <Select labelInValue mode="multiple" allowClear>
  185 + {specList.map((item: any) => (
  186 + <Option value={item.id} key={item.id}>
  187 + {item.name}
  188 + </Option>
  189 + ))}
  190 + </Select>
  191 + </Form.Item>
  192 + ) : (
  193 + <>
  194 + {/* 品牌 */}
  195 + <Form.Item label="品牌" name="brand" rules={[{ required: true, message: '请选择品牌:' }]}>
  196 + <Select
  197 + labelInValue
  198 + placeholder="请选择品牌"
  199 + onChange={async (value) => {
  200 + form.resetFields(['Series']);
  201 + const api = bizType == 1 ? getOnsaleSeriesApi : getSeriesApi;
  202 + const { data: seriesList } = await api(value?.key);
  203 + const tempArray = selectedSeries.map((item) => item.seriesId);
  204 + const tempData = seriesList?.filter((value) => !tempArray.includes(value.id));
  205 + setSeries(tempData);
  206 + }}
  207 + >
  208 + {brandList.map((item) => (
  209 + <Option value={item.id} key={item.id}>
  210 + {item.name}
  211 + </Option>
  212 + ))}
  213 + </Select>
  214 + </Form.Item>
  215 + {/* 车系 */}
  216 + <Form.Item label="车系" name="Series" rules={[{ required: true, message: '请选择车系' }]}>
  217 + <Select labelInValue mode="multiple" placeholder="请选择车系" allowClear>
  218 + {series.map((item: any) => (
  219 + <Option value={item.id} key={item.id}>
  220 + {item.name}
  221 + </Option>
  222 + ))}
  223 + </Select>
  224 + </Form.Item>
  225 + </>
  226 + )}
  227 + </Form>
  228 + </Modal>
  229 + </>
  230 + );
  231 +}
... ...
src/pages/stock/Distribute/Price/api.ts 0 → 100644
  1 +import type { http } from '@/typing/http';
  2 +import request, { host } from '@/utils/request';
  3 +import { FVM_HOST } from '@/utils/host';
  4 +
  5 +export interface QueryParams {
  6 + subjectId?: number;
  7 + brandId?: number;
  8 + seriesId?: number;
  9 + current?: number;
  10 + pageSize?: number;
  11 +}
  12 +
  13 +interface User {
  14 + userName: string; // 备案人员姓名
  15 + userSfz: string; // 备案人员身份证
  16 +}
  17 +
  18 +
  19 +/**
  20 + * 保存
  21 + */
  22 +export interface ListVO {
  23 + id?: number; //id
  24 + /**车辆加价金额 */
  25 + upAmount?: number;
  26 + /** 是否全部分销商 1是 0否*/
  27 + allDist?: boolean; //品牌名称
  28 + authDistList?: AuthSubjectList[],
  29 + authSeriesList?: AuthSeriesList[],
  30 +}
  31 +
  32 +interface AuthSubjectList {
  33 + subjectId: number;
  34 + subjectName: string;
  35 +}
  36 +
  37 +export interface AuthSeriesList {
  38 + brandId: number;
  39 + brandName: string;
  40 + seriesId: number;
  41 + seriesName: string;
  42 + allSpec: boolean;
  43 + authSpecList: AuthSpecList[]
  44 +}
  45 +
  46 +interface AuthSpecList {
  47 + specId: number,
  48 + specName: string
  49 +}
  50 +
  51 +export enum StatusEnum {
  52 + '禁用' = 1,
  53 + '待审核',
  54 + '审批通过',
  55 + '审批未通过',
  56 + '变更审批中',
  57 +}
  58 +
  59 +export const StatusColor: Record<number, string> = {
  60 + 1: 'default',
  61 + 2: 'orange',
  62 + 3: 'green',
  63 + 4: 'red',
  64 + 5: 'orange',
  65 +}
  66 +/**
  67 + * 查询列表
  68 + */
  69 +export function queryListApi(params: QueryParams): http.PromisePageResp<ListVO> {
  70 + return request.get(`${FVM_HOST}/erp/dist/price/get/page`, { params });
  71 +}
  72 +
  73 +/** 保存修改分销价格设置 */
  74 +export function saveApi(params: ListVO): http.PromiseResp<string> {
  75 + return request.post<string>(`${FVM_HOST}/erp/dist/price/save`, params);
  76 +}
  77 +
  78 +/**
  79 + * 删除分销价格设置
  80 + */
  81 +export function deleteApi(params: { distPriceId?: number }): http.PromiseResp<any> {
  82 + return request.get(`${FVM_HOST}/erp/dist/price/del`, { params });
  83 +}
... ...
src/pages/stock/Distribute/Price/components/CreatelModal.tsx 0 → 100644
  1 +import React, { useEffect, useState } from 'react';
  2 +import { message, Modal, Form, Select, InputNumber, Radio } from 'antd';
  3 +import { getUnitCompanyListApi } from '@/common/api';
  4 +import useInitial from '@/hooks/useInitail';
  5 +import type { ListVO } from '../api';
  6 +import { saveApi } from '../api';
  7 +import CarTableSelect from '@/pages/stock/Components/CarTableSelect';
  8 +
  9 +const Option = Select.Option;
  10 +interface Props {
  11 + visible: boolean;
  12 + onCancel: Function;
  13 + item: ListVO;
  14 + delearData: CommonApi.OptionVO[];
  15 + fetchList: () => any;
  16 +}
  17 +
  18 +export default function CreateModal(props: Props) {
  19 + const [form] = Form.useForm();
  20 + const { visible, onCancel, fetchList, item = {} as ListVO } = props;
  21 + const [loading, setLoading] = useState(false);
  22 + const [delay, setDelay] = useState(true);
  23 + const { data: companyList, setParams } = useInitial(getUnitCompanyListApi, [], { types: '164' });
  24 +
  25 + useEffect(() => {
  26 + if (visible && item.id) {
  27 + form.setFieldsValue({
  28 + ...item,
  29 + authSeriesList: item.authSeriesList && item.authSeriesList.map(i => ({ ...i, authType: i.allSpec ? 1 : 2, specs: i.authSpecList })),
  30 + allDist: Number(item.allDist),
  31 + authDistList: item.authDistList && item.authDistList.map(i => ({ value: i.subjectId, label: i.subjectName })),
  32 + });
  33 + if (delay) {
  34 + setDelay(false);
  35 + }
  36 + } else {
  37 + form.resetFields();
  38 + }
  39 + }, [visible]);
  40 +
  41 +
  42 + function okHandle(fieldsValue: any) {
  43 + setLoading(true);
  44 + const temp: any = {
  45 + ...fieldsValue,
  46 + authDistList: fieldsValue.authDistList && fieldsValue.authDistList.map((i) => ({ subjectId: i.value, subjectName: i.label })),
  47 + authSeriesList: fieldsValue.authSeriesList.map((i) => ({
  48 + ...i,
  49 + allSpec: i.authType,
  50 + authSpecList: i.specs
  51 + })),
  52 + };
  53 +
  54 + saveApi(temp)
  55 + .then((res) => {
  56 + message.success('操作成功');
  57 + setLoading(false);
  58 + fetchList && fetchList();
  59 + onCancel();
  60 + })
  61 + .catch((e) => {
  62 + setLoading(false);
  63 + message.error(e.message);
  64 + });
  65 + }
  66 +
  67 + return (
  68 + <Modal
  69 + title={item.id ? '编辑' : '新增'}
  70 + confirmLoading={loading}
  71 + width="60%"
  72 + style={{}}
  73 + open={visible}
  74 + maskClosable={false}
  75 + onOk={() => form.submit()}
  76 + onCancel={() => onCancel()}
  77 + >
  78 + <Form labelWrap form={form} labelCol={{ span: 4 }} onFinish={okHandle}>
  79 + <Form.Item name="authSeriesList" label="分销车辆范围" required>
  80 + <CarTableSelect />
  81 + </Form.Item>
  82 + <Form.Item name="upAmount" label="车辆加价金额" rules={[{ required: true, message: '请输入' }]}>
  83 + <InputNumber min={0} placeholder="请输入" style={{ width: '100%' }} addonAfter="元" />
  84 + </Form.Item>
  85 + <Form.Item name="allDist" label="分销商范围" rules={[{ required: true, message: '请选择' }]}>
  86 + <Radio.Group >
  87 + <Radio value={1} key={1}>全部</Radio>
  88 + <Radio value={0} key={0}>部分</Radio>
  89 + </Radio.Group>
  90 + </Form.Item>
  91 + <Form.Item noStyle shouldUpdate={(prevValues, currentValues) => prevValues.allDist != currentValues.allDist}>
  92 + {({ getFieldValue }): any => {
  93 + const type = getFieldValue("allDist") || 0;
  94 + if (!type) {
  95 + return (
  96 + <Form.Item name="authDistList" label="适用分销商" hasFeedback rules={[{ required: true, message: '请选择' }]}>
  97 + <Select mode="multiple" showSearch optionFilterProp="children" labelInValue placeholder="请选择分销商" filterOption>
  98 + {companyList.map((d) => (
  99 + <Option key={d.id} value={d.id!}>
  100 + {d.name}
  101 + </Option>
  102 + ))}
  103 + </Select>
  104 + </Form.Item>
  105 + )
  106 + }
  107 + }}
  108 + </Form.Item>
  109 + </Form>
  110 + </Modal>
  111 + );
  112 +}
... ...
src/pages/stock/Distribute/Price/index.tsx 0 → 100644
  1 +import React, { useState } from 'react';
  2 +import { Table, Card, Popconfirm, message, Button, Select, Tag } from 'antd';
  3 +import Detail from './components/CreatelModal';
  4 +import { PageHeaderWrapper } from '@ant-design/pro-layout';
  5 +import type { ListVO } from './api';
  6 +import { queryListApi, deleteApi } from './api';
  7 +import usePagination from '@/hooks/usePagination';
  8 +import useInitial from '@/hooks/useInitail';
  9 +import { PlusOutlined } from '@ant-design/icons';
  10 +import { getUnitCompanyListApi } from '@/common/api';
  11 +import TextWithMore from '@/components/TextWithMore';
  12 +
  13 +const { Column } = Table;
  14 +
  15 +export default function Index() {
  16 + const { list, loading, setParams, paginationConfig, setLoading, innerParams } = usePagination(queryListApi, {});
  17 + const { data: delearData } = useInitial(getUnitCompanyListApi, [], { types: '164' });
  18 + const [visible, setVisible] = useState(false);
  19 + const [item, setItem] = useState<ListVO>({});
  20 + const [timer, setTimer] = useState<any>();
  21 + const onFilter = (option: any = {}) => {
  22 + setParams({ ...innerParams, ...option, current: 1 }, true);
  23 + };
  24 +
  25 + function _delete(params: ListVO) {
  26 + deleteApi({ distPriceId: params.id, })
  27 + .then(() => {
  28 + message.success('操作成功');
  29 + setParams({ ...innerParams, current: 1 }, true);
  30 + })
  31 + .catch((e) => {
  32 + message.error(e.message);
  33 + });
  34 + }
  35 +
  36 + function filter(param: any) {
  37 + timer && clearTimeout(timer);
  38 + setTimer(
  39 + setTimeout(() => {
  40 + setParams({ ...innerParams, ...param }, true);
  41 + }, 500),
  42 + );
  43 + }
  44 +
  45 + return (
  46 + <PageHeaderWrapper title="分销价格设置">
  47 + <Card>
  48 + <div style={{ display: 'flex', flexDirection: 'row', justifyContent: 'space-between', marginBottom: 20 }}>
  49 + <div style={{ display: 'flex', flexDirection: 'row' }}>
  50 + <Select
  51 + allowClear
  52 + showSearch
  53 + placeholder="请选择商家"
  54 + notFoundContent="暂无数据"
  55 + style={{ minWidth: 200, maxWidth: 300, marginLeft: 10 }}
  56 + onChange={(v) => filter({ dealerId: (v && Number(v)) || undefined, current: 1 })}
  57 + >
  58 + {delearData.map((d) => (
  59 + <Select.Option key={d.id} value={d.id!}>
  60 + {d.name}
  61 + </Select.Option>
  62 + ))}
  63 + </Select>
  64 + </div>
  65 + <Button
  66 + type="primary"
  67 + icon={<PlusOutlined translate="yes" />}
  68 + onClick={() => {
  69 + setVisible(true), setItem({});
  70 + }}
  71 + >
  72 + 新增
  73 + </Button>
  74 + </div>
  75 + <Table
  76 + dataSource={list}
  77 + pagination={paginationConfig}
  78 + rowKey="id"
  79 + loading={loading}
  80 + >
  81 + <Column
  82 + title="适用分销商"
  83 + dataIndex="authDistList"
  84 + render={(t, record: ListVO) => (!record.allDist ? <TextWithMore list={t && t.map((i) => i.subjectName)} /> : <Tag>全部经销商</Tag>)}
  85 + />
  86 + <Column title="车辆加价金额" dataIndex="upAmount" />
  87 + <Column
  88 + title="操作"
  89 + fixed="right"
  90 + render={(text, record: ListVO) => (
  91 + <>
  92 + <Popconfirm placement="top" title={`确认删除?`} onConfirm={() => _delete(record)}>
  93 + <Button type="link">删除</Button>
  94 + </Popconfirm>
  95 + <Button
  96 + type="link"
  97 + onClick={() => {
  98 + setVisible(true);
  99 + setItem(record);
  100 + }}
  101 + >
  102 + 编辑
  103 + </Button>
  104 + </>
  105 + )}
  106 + />
  107 + </Table>
  108 + </Card>
  109 + <Detail
  110 + visible={visible}
  111 + item={item}
  112 + delearData={delearData}
  113 + onCancel={() => {
  114 + setVisible(false);
  115 + setItem({});
  116 + }}
  117 + fetchList={() => setParams({ ...innerParams, current: 1 }, true)}
  118 + />
  119 + </PageHeaderWrapper>
  120 + );
  121 +}
... ...
src/pages/stock/Distribute/Records/components/CreatelModal.tsx
... ... @@ -177,9 +177,19 @@ export default function CreateModal(props: Props) {
177 177 <Radio value={2} key={2}>固定比例</Radio>
178 178 </Radio.Group>
179 179 </Form.Item>
180   - <Form.Item name="installmentValue" label="分期服务费" rules={[{ required: true, message: '请输入' }]}>
181   - <InputNumber min={0} disabled={disabled} placeholder='请输入' style={{ width: '100%' }} addonAfter="" />
  180 + <Form.Item noStyle shouldUpdate={(prevValues, currentValues) => prevValues.installmentType != currentValues.installmentType}>
  181 + {({ getFieldValue }): any => {
  182 + const type = getFieldValue("installmentType");
  183 + if (type) {
  184 + return (
  185 + <Form.Item name="installmentValue" label={`分期服务费${type == 1 ? "金额" : "比例"}`} rules={[{ required: true, message: '请输入' }]}>
  186 + <InputNumber min={0} max={type == 2 ? 100 : undefined} disabled={disabled} placeholder='请输入' style={{ width: '100%' }} addonAfter={type == 1 ? "元" : "%"} />
  187 + </Form.Item>
  188 + )
  189 + }
  190 + }}
182 191 </Form.Item>
  192 +
183 193 <Form.Item name="overstatedTaxRatio" label="高开税金" rules={[{ required: true, message: '请输入' }]}>
184 194 <InputNumber min={0} disabled={disabled} placeholder='请输入' style={{ width: '100%' }} addonAfter="%" />
185 195 </Form.Item>
... ... @@ -197,7 +207,7 @@ export default function CreateModal(props: Props) {
197 207 </Select>
198 208 </Form.Item>
199 209  
200   - <Form.Item noStyle name="authBrandList" >
  210 + <Form.Item noStyle name="authBrandList">
201 211 <CarTable disabled={disabled} />
202 212 </Form.Item>
203 213 <Form.Item label="分销商小程序管理人员:" required labelCol={{ span: 8 }} />
... ...
src/pages/stock/Distribute/Records/index.tsx
... ... @@ -110,7 +110,7 @@ export default function Index() {
110 110 <Column title="回款账户(公户)" dataIndex="companyAccountName" />
111 111 <Column title="回款账户(私户)" dataIndex="personalAccountName" />
112 112 <Column title="销售品牌" dataIndex="authBrandList" render={(t) => <TextWithMore list={t.map((i) => i.brandName)} />} />
113   - <Column title="映射售后门店" dataIndex="" />
  113 + <Column title="映射售后门店" dataIndex="casShopName" />
114 114 <Column title="分期服务费" dataIndex="installmentValue" />
115 115 <Column title="高开税金" dataIndex="overstatedTaxRatio" />
116 116 <Column title="运费承担方" dataIndex="distBear" render={(t) => (t ? '分销商自提' : '签约商家')} />
... ... @@ -134,7 +134,7 @@ export default function Index() {
134 134 fixed="right"
135 135 render={(text, record: ListVO) => (
136 136 <>
137   - <Popconfirm placement="top" title={`确认${record.status == 1 ? '禁用' : '启用'}?`} onConfirm={() => _status(record)}>
  137 + <Popconfirm placement="top" title={`确认${record.status == 1 ? '启用' : '禁用'}?`} onConfirm={() => _status(record)}>
138 138 <Button type="link">{record.status == 1 ? '启用' : '禁用'}</Button>
139 139 </Popconfirm>
140 140 <Button
... ...