Commit 764fb26a1827cbf7adf8ec698c5d3fc369814094

Authored by 莫红玲
2 parents 75355b2c f55edb50

Merge branch 'order_direct_car' into 'master'

Order direct car



See merge request !698
config/routers/order3.ts
... ... @@ -296,6 +296,11 @@ export default [
296 296 component: './order3/InstallmentCommissionAllocation',
297 297 },
298 298 {
  299 + // 车辆订单确认配件配置
  300 + path: '/order3/carChargePile',
  301 + component: './order3/CarChargePile',
  302 + },
  303 + {
299 304 // 上户和抵押时效配置
300 305 path: '/order3/registerTiming',
301 306 component: './order3/RegisterTiming',
... ... @@ -310,4 +315,9 @@ export default [
310 315 path: '/order3/loanPurchaseCost/addConfig',
311 316 component: './order3/BuyCarLoanCost/subpages/AddConfig',
312 317 },
  318 + {
  319 + // 直营策车厂家促销设置
  320 + path: '/order3/directCarPromotion',
  321 + component: './order3/DirectCarPromotion',
  322 + },
313 323 ];
... ...
src/pages/order3/CarChargePile/api.ts 0 → 100644
  1 +import { http } from '@/typing/http';
  2 +import request from '@/utils/request';
  3 +import { ORDER3, DECORATION } from '@/utils/host';
  4 +import { PageParams } from '@/typing/common';
  5 +
  6 +type P<T> = http.PromiseResp<T>;
  7 +type Page<T> = http.PromisePageResp<T>;
  8 +
  9 +interface Params extends PageParams {
  10 + brandId?: number; // 品牌id
  11 + seriesId?: number; // 车系id
  12 + specId?: number; // 车型id
  13 + partName?: string; // 配件名称
  14 +}
  15 +
  16 +export interface ListResult {
  17 + id?: number; // id
  18 + partName?: string; // 配件名称
  19 + partCode?: string; // 配置编码
  20 + clientAmount?: number; // 收客户金额
  21 + clientAmountFlow?: number; // 收客户款流向
  22 + clientFlowAmount?: number; // 收客户款流向金额
  23 + partCost?: number; // 配件采购成本
  24 + partCostFlow?: number; // 配件款款项流向
  25 + partFlowCost?: number; // 配件款款项流向金额
  26 + beginTime?: number; // 开始时间
  27 + endTime?: number; // 结束时间
  28 + carVOList?: CarVOList[]; // 车辆详情
  29 +}
  30 +
  31 +interface CarVOList {
  32 + applyCarType?: number; // 适用车辆类型(1:全部车系,2:全部车型,3,部分车型)
  33 + brandId?: number; // 品牌id
  34 + brandName?: string; // 品牌名称
  35 + seriesList?: SeriesVo[]; // 车系列表
  36 +}
  37 +
  38 +interface SeriesVo {
  39 + seriesId?: number; // 车系id
  40 + seriesName?: string; // 车系名称
  41 + specList?: SpecVo[]; // 车型列表
  42 +}
  43 +
  44 +interface SpecVo {
  45 + specId?: number; // 车型id
  46 + specName?: string; // 车型名称
  47 +}
  48 +
  49 +interface CarDtoList {
  50 + applyCarType?: number; // 适用车辆类型(1:全部车系,2:全部车型,3,部分车型)
  51 + brandId?: number; // 品牌id
  52 + brandName?: string; // 品牌名称
  53 + seriesId?: number; // 车系id
  54 + seriesName?: string; // 车系名称
  55 + specId?: number; // 车型id
  56 + specName?: string; // 车型名称
  57 +}
  58 +
  59 +export interface SaveParams {
  60 + id?: number; // id
  61 + partName?: string; // 配件名称
  62 + partCode?: string; // 配置编码
  63 + clientAmount?: number; // 收客户金额
  64 + clientAmountFlow?: number; // 收客户款流向
  65 + clientFlowAmount?: number; // 收客户款流向金额
  66 + partCost?: number; // 配件采购成本
  67 + partCostFlow?: number; // 配件款款项流向
  68 + partFlowCost?: number; // 配件款款项流向金额
  69 + beginTime?: number; // 开始时间
  70 + endTime?: number; // 结束时间
  71 + carDtoList?: CarDtoList[]; // 车辆详情
  72 +}
  73 +
  74 +/**
  75 + * 获取车辆订单确认配件配置
  76 + * @param params
  77 + * @returns
  78 + */
  79 +export function getListApi(params: Params): Page<ListResult> {
  80 + return request.get(`${ORDER3}/erp/vehicle/part/config/page`, { params });
  81 +}
  82 +
  83 +/**
  84 + * 保存车辆订单确认配置配置
  85 + * @param params
  86 + * @returns
  87 + */
  88 +export function saveConfigApi(params: SaveParams): P<string> {
  89 + return request.post(`${ORDER3}/erp/vehicle/part/config/saveOrUpdate`, params);
  90 +}
  91 +
  92 +/**
  93 + * 删除车辆订单确认配置配置
  94 + * @param params
  95 + * @returns
  96 + */
  97 +export function fetchDeleteApi(params: {id?: number}): P<string> {
  98 + return request.post(`${ORDER3}/erp/vehicle/part/config/delete`, params, { contentType: 'form-urlencoded' });
  99 +}
  100 +
  101 +export interface DecoParams {
  102 + cars?: Cars[];
  103 + bizType?: number; // 0汽车装潢1配件零售
  104 +}
  105 +
  106 +interface Cars {
  107 + brandId?: number; // 品牌id
  108 + seriesId?: number; // 车系id
  109 + specId?: number; // 车型id
  110 +}
  111 +
  112 +export interface DecoResult {
  113 + goodId?: number; // 装潢商品id
  114 + partId?: number; // 配件id
  115 + partCode?: string; // 配件编码
  116 + partName?: string; // 配件名称
  117 + mainImgFids?: string; // 装潢图片
  118 + value?: string; // 配件编码值
  119 + label?: string; // 配件名称值
  120 +}
  121 +
  122 +export enum CostFlowEnum {
  123 + '销售折让收回' = 1,
  124 +}
  125 +
  126 +export enum AmountFlow {
  127 + '扣减销售折让' = 1,
  128 +}
  129 +
  130 +/**
  131 + * 查询充电桩列表
  132 + * @param params
  133 + * @returns
  134 + */
  135 +export function getDecoListApi(params: DecoParams): P<DecoResult[]> {
  136 + return request.post(`${DECORATION}/deco/goods/get/spec/category/goods`, params);
  137 +}
... ...
src/pages/order3/CarChargePile/components/CarModal.tsx 0 → 100644
  1 +import React, { useState, useEffect } from 'react';
  2 +import { Modal } from 'antd';
  3 +import _ from 'lodash';
  4 +import { useStore } from '../index';
  5 +import CarTableTreeAuth from '@/components/CarTableTreeAuth';
  6 +
  7 +const CarModal = () => {
  8 + const { carModal, setCarModal } = useStore();
  9 + const [carData, setCarData] = useState<any>([]);
  10 +
  11 + const handleCancel = () => {
  12 + setCarModal({ data: {}, visible: false });
  13 + };
  14 + useEffect(() => {
  15 + if (carModal.visible) {
  16 + const data = JSON.parse(JSON.stringify(carModal.data?.carVOList || []));
  17 + if (data.length) {
  18 + data.forEach((v: any) => {
  19 + if (v.seriesList?.length) {
  20 + v.children = v.seriesList;
  21 + v.seriesList.forEach((k: any) => {
  22 + if (k.specList?.length) {
  23 + k.children = k.specList;
  24 + }
  25 + });
  26 + }
  27 + });
  28 + setCarData(data);
  29 + } else {
  30 + setCarData([]);
  31 + }
  32 + }
  33 + }, [carModal.visible]);
  34 + return (
  35 + <Modal destroyOnClose forceRender open={carModal.visible} title="适用车辆" maskClosable={false} onCancel={handleCancel} onOk={handleCancel}>
  36 + <CarTableTreeAuth value={carData} disabled brandMultiple={false} />
  37 + </Modal>
  38 + );
  39 +};
  40 +export default CarModal;
... ...
src/pages/order3/CarChargePile/components/ChargePileModal.tsx 0 → 100644
  1 +import { Modal, Table, Row, message, Col } from 'antd';
  2 +import React, { useState } from 'react';
  3 +import { getDecoListApi, DecoResult, DecoParams } from '../api';
  4 +import Filters from '@/pages/order3/Common/Filter';
  5 +
  6 +interface Props {
  7 + value?: DecoResult[];
  8 + visible: boolean;
  9 + setVisible: React.Dispatch<React.SetStateAction<boolean>>;
  10 + selected: DecoResult[];
  11 + setSelected: React.Dispatch<React.SetStateAction<DecoResult[]>>;
  12 + onChange?: Function;
  13 + tableType: '列表' | '选择';
  14 + setTableType: React.Dispatch<React.SetStateAction<'列表' | '选择'>>;
  15 +}
  16 +
  17 +export default function ChargePileModal({ value, visible, setVisible, selected, setSelected, onChange, tableType, setTableType }: Props) {
  18 + const [list, setList] = useState<DecoResult[]>([]);
  19 + const [loading, setLoading] = useState<boolean>(false);
  20 +
  21 + /**
  22 + * @description: 当一个配件被点击时,触发该回调函数。
  23 + * @param {DecoResult} record 当前门店数据
  24 + * @param {boolean} _selected 是否选中
  25 + */
  26 + function onSelect(record: DecoResult, _selected: boolean) {
  27 + if (_selected) {
  28 + setSelected([{ ...record, value: record.partCode, label: record.partName }]);
  29 + } else {
  30 + setSelected([{ ...record, value: record.partCode, label: record.partName }]);
  31 + }
  32 + }
  33 +
  34 + function rowClick(record: DecoResult) {
  35 + if (selected.length && selected[0].partCode == record.partCode) {
  36 + return;
  37 + }
  38 + setSelected([{ ...record, value: record.partCode, label: record.partName }]);
  39 + }
  40 +
  41 + function onOk() {
  42 + onChange && onChange(selected);
  43 + setVisible(false);
  44 + }
  45 +
  46 + function onChangBrand(value?: any) {
  47 + if (!value.specId) {
  48 + return;
  49 + }
  50 + const params = {
  51 + brandId: value.brandId,
  52 + seriesId: value.seriesId,
  53 + specId: value.specId,
  54 + };
  55 + getDeco(params);
  56 + }
  57 +
  58 + function getDeco(params: DecoParams) {
  59 + setLoading(true);
  60 + setTableType('选择');
  61 + getDecoListApi(params)
  62 + .then((res) => {
  63 + setList(res.data || []);
  64 + setSelected([]);
  65 + setLoading(false);
  66 + })
  67 + .catch((e) => {
  68 + setList([]);
  69 + setLoading(false);
  70 + setSelected([]);
  71 + message.error(e.message);
  72 + });
  73 + }
  74 +
  75 + return (
  76 + <Modal destroyOnClose title="选择配件" width="50%" zIndex={1001} maskClosable={false} open={visible} onCancel={() => setVisible(false)} onOk={() => onOk()}>
  77 + {visible ? (
  78 + <Row style={{ marginBottom: '15px' }}>
  79 + <Col style={{ minWidth: '300px' }}>
  80 + <Filters onFilter={(value: any) => onChangBrand(value)} />
  81 + </Col>
  82 + </Row>
  83 + ) : null}
  84 + <Table
  85 + dataSource={tableType === '列表' ? value : list}
  86 + loading={loading}
  87 + pagination={false}
  88 + rowKey="partCode"
  89 + size="small"
  90 + onRow={(record) => ({
  91 + onClick: () => rowClick(record),
  92 + })}
  93 + rowSelection={{
  94 + type: 'radio',
  95 + selectedRowKeys: selected.map((item) => item.partCode || -1),
  96 + onSelect,
  97 + getCheckboxProps: (record) => ({}),
  98 + }}
  99 + scroll={{ y: '60vh', scrollToFirstRowOnChange: true }}
  100 + >
  101 + <Table.Column title="配件编码" dataIndex="partCode" align="left" />
  102 + <Table.Column title="配件名称" dataIndex="partName" align="left" />
  103 + </Table>
  104 + </Modal>
  105 + );
  106 +}
... ...
src/pages/order3/CarChargePile/components/ChargePileSelect.tsx 0 → 100644
  1 +import React, { forwardRef, Ref, useCallback, useEffect, useImperativeHandle, useMemo, useState } from 'react';
  2 +import { DecoResult } from '../api';
  3 +import Close from './Close';
  4 +import ChargePileModal from './ChargePileModal';
  5 +import './style.less';
  6 +
  7 +interface Props {
  8 + value?: DecoResult[];
  9 + onChange?: (value?: DecoResult[]) => void;
  10 +}
  11 +
  12 +export default forwardRef(StaffSelectNew);
  13 +
  14 +function StaffSelectNew(
  15 + {
  16 + value=[],
  17 + onChange,
  18 + }: Props,
  19 + ref: Ref<any>,
  20 +) {
  21 + const [visible, setVisible] = useState(false);
  22 + const [selected, setSelected] = useState<DecoResult[]>([]);
  23 + const [tableType, setTableType] = useState<'列表' | '选择'>('列表');
  24 +
  25 + useEffect(() => {
  26 + if (!visible) {
  27 + setTableType('列表');
  28 + }
  29 + }, [visible])
  30 +
  31 + useEffect(() => {
  32 + if (value.length) {
  33 + setSelected(value);
  34 + }
  35 + }, [value])
  36 +
  37 + useImperativeHandle(ref, () => ({
  38 + reset: () => {
  39 + setSelected([]);
  40 + },
  41 + }));
  42 +
  43 + const deleteAll = () => {
  44 + setSelected([]);
  45 + onChange && onChange([]);
  46 + };
  47 +
  48 + const openModal = useCallback(() => {
  49 + setVisible(true);
  50 + if (value.length) {
  51 + setSelected([{...value[0], label: value[0].partName, value: value[0].partCode}])
  52 + } else {
  53 + setSelected([]);
  54 + }
  55 + }, [value]);
  56 +
  57 + const showList = useMemo(() => (value?.every(item => item.partName) ? value : selected), [value, selected]);
  58 +
  59 + return (
  60 + <div style={{ width: '100%' }}>
  61 + <span className={`ant-input-affix-wrapper`} onClick={openModal}>
  62 + <div className={`StaffSelectNew_Container`}>
  63 + {showList?.length ? (
  64 + // @ts-ignore
  65 + <span className="text">{value?.[0]?.label || '-'}</span>
  66 + ) : (
  67 + <span className="placeholder">请选择配件</span>
  68 + )}
  69 + </div>
  70 + {value?.length ? <Close deleteAll={deleteAll} /> : null}
  71 + </span>
  72 + <ChargePileModal
  73 + onChange={onChange}
  74 + selected={selected}
  75 + setSelected={setSelected}
  76 + visible={visible}
  77 + setVisible={setVisible}
  78 + tableType={tableType}
  79 + setTableType={setTableType}
  80 + value={value}
  81 + />
  82 + </div>
  83 + );
  84 +}
... ...
src/pages/order3/CarChargePile/components/Close.tsx 0 → 100644
  1 +import React from 'react';
  2 +
  3 +interface Props {
  4 + deleteAll: () => void;
  5 +}
  6 +
  7 +export default function Close({ deleteAll }: Props) {
  8 + return (
  9 + <span
  10 + className="ant-input-suffix"
  11 + onClick={(e) => {
  12 + e.stopPropagation();
  13 + deleteAll();
  14 + }}
  15 + >
  16 + <span className="ant-input-clear-icon" role="button" tabIndex={-1}>
  17 + <span role="img" aria-label="close-circle" className="anticon anticon-close-circle">
  18 + <svg viewBox="64 64 896 896" focusable="false" data-icon="close-circle" width="1em" height="1em" fill="currentColor" aria-hidden="true">
  19 + <path d="M512 64C264.6 64 64 264.6 64 512s200.6 448 448 448 448-200.6 448-448S759.4 64 512
  20 + 64zm165.4 618.2l-66-.3L512 563.4l-99.3 118.4-66.1.3c-4.4 0-8-3.5-8-8 0-1.9.7-3.7 1.9-5.2l130.1-155L340.5
  21 + 359a8.32 8.32 0 01-1.9-5.2c0-4.4 3.6-8 8-8l66.1.3L512 464.6l99.3-118.4 66-.3c4.4 0 8 3.5 8 8 0 1.9-.7 3.7-1.9 5.2L553.5 514l130
  22 + 155c1.2 1.5 1.9 3.3 1.9 5.2 0 4.4-3.6 8-8 8z" />
  23 + </svg>
  24 + </span>
  25 + </span>
  26 + </span>
  27 + );
  28 +}
... ...
src/pages/order3/CarChargePile/components/EditModal.tsx 0 → 100644
  1 +import React, { useState, useEffect } from 'react';
  2 +import { Modal, Button, message, Form, InputNumber, DatePicker, Select, Tag } from 'antd';
  3 +import { useStore } from '../index';
  4 +import { saveConfigApi, SaveParams, CostFlowEnum, AmountFlow, getDecoListApi, DecoResult } from '../api';
  5 +import { debounce } from 'lodash';
  6 +import CarTableTreeAuth from '@/components/CarTableTreeAuth';
  7 +import moment from 'moment';
  8 +import { PlusSquareOutlined } from '@ant-design/icons';
  9 +import { CarAuthList } from '@/components/CarTableTreeAuth/entity';
  10 +// import ChargePileSelect from '@/pages/order3/CarChargePile/components/ChargePileSelect';
  11 +
  12 +const { RangePicker } = DatePicker;
  13 +const Option = Select.Option;
  14 +
  15 +// const _partList = [{partCode: 'asdasd', partName: '配件1213'}]
  16 +
  17 +export default function DetailModal() {
  18 + const { setModalData, setLoading, modalData } = useStore();
  19 + const [confirm, setConfirm] = useState<boolean>(false);
  20 + const [clientFlowAmount, setClientFlowAmount] = useState<number>();
  21 + const [partFlowCost, setPartFlowCost] = useState<number>();
  22 + const [form] = Form.useForm();
  23 + const [partList, setPartList] = useState<DecoResult[]>([]);
  24 + const [selectPart, setSelectPart] = useState<any>();
  25 +
  26 + useEffect(() => {
  27 + if (modalData.visible && modalData.data?.id) {
  28 + const applyCarList: any = JSON.parse(JSON.stringify(modalData.data?.carVOList || []));
  29 + applyCarList.forEach((v: any) => {
  30 + if (v.seriesList?.length) {
  31 + v.children = v.seriesList;
  32 + v.seriesList.forEach((k: any) => {
  33 + if (k.specList?.length) {
  34 + k.children = k.specList;
  35 + }
  36 + });
  37 + }
  38 + });
  39 + setClientFlowAmount(modalData.data?.clientAmount);
  40 + setPartFlowCost(modalData.data?.partFlowCost);
  41 + const result: any = [];
  42 + modalData.data?.carVOList?.forEach((v) => {
  43 + if (v.seriesList?.length) {
  44 + v.seriesList.forEach((k) => {
  45 + if (k.specList?.length) {
  46 + k.specList.forEach((r) => {
  47 + const item = {
  48 + brandId: v.brandId,
  49 + seriesId: k.seriesId,
  50 + specId: r.specId,
  51 + };
  52 + result.push(item);
  53 + });
  54 + } else {
  55 + const item = { brandId: v.brandId, seriesId: k.seriesId };
  56 + result.push(item);
  57 + }
  58 + });
  59 + } else {
  60 + const item = { brandId: v.brandId };
  61 + result.push(item);
  62 + }
  63 + });
  64 + getDecoListApi({cars: result, bizType: 0})
  65 + .then(res => {
  66 + setPartList(res.data || []);
  67 + })
  68 + .catch(e => {
  69 + message.error(e.message);
  70 + setPartList([]);
  71 + })
  72 + setSelectPart({partName: modalData.data?.partName, partCode: modalData.data?.partCode});
  73 + form.setFieldsValue({
  74 + part: modalData.data?.partCode,
  75 + clientAmount: modalData.data?.clientAmount,
  76 + partCost: modalData.data?.partCost,
  77 + time: [moment(modalData.data.beginTime), moment(modalData.data.endTime)],
  78 + optionalAuth: applyCarList,
  79 + });
  80 + }
  81 + }, [modalData.visible]);
  82 +
  83 + function onCancle() {
  84 + setClientFlowAmount(undefined);
  85 + setPartFlowCost(undefined);
  86 + setModalData({ visible: false, data: undefined });
  87 + setPartList([]);
  88 + setSelectPart(undefined);
  89 + }
  90 +
  91 + function onCarChange(value?: any) {
  92 + if (value && value.length) {
  93 + const params = handlePartParams(value);
  94 + getDecoListApi({cars: params, bizType: 0})
  95 + .then(res => {
  96 + setPartList(res.data || []);
  97 + })
  98 + .catch(e => {
  99 + setPartList([]);
  100 + })
  101 + }
  102 + }
  103 +
  104 + function onPartChange(option: any) {
  105 + if(option?.value) {
  106 + setSelectPart({ partCode: option.value, partName: option.label})
  107 + }
  108 + }
  109 +
  110 + /**
  111 + * 将树形车辆结构转换成扁平结构
  112 + * @param value
  113 + * @returns
  114 + */
  115 + function handlePartParams(value: CarAuthList[]) {
  116 + const result: any = [];
  117 + value.forEach((v) => {
  118 + if (v.children?.length) {
  119 + v.children.forEach((k) => {
  120 + if (k.children?.length) {
  121 + k.children.forEach((r) => {
  122 + const item = {
  123 + brandId: v.brandId,
  124 + seriesId: k.seriesId,
  125 + specId: r.specId,
  126 + };
  127 + result.push(item);
  128 + });
  129 + } else {
  130 + const item = { brandId: v.brandId, seriesId: k.seriesId };
  131 + result.push(item);
  132 + }
  133 + });
  134 + } else {
  135 + const item = { brandId: v.brandId };
  136 + result.push(item);
  137 + }
  138 + });
  139 + return result;
  140 + }
  141 +
  142 + /**
  143 + * 将树形车辆结构转换成扁平结构
  144 + * applyCarType为最细化层级的类型
  145 + * @param value
  146 + * @returns
  147 + */
  148 + function handleCarData(value: CarAuthList[]) {
  149 + const result: any = [];
  150 + value.forEach((v) => {
  151 + if (v.children?.length) {
  152 + v.children.forEach((k) => {
  153 + if (k.children?.length) {
  154 + k.children.forEach((r) => {
  155 + const item = {
  156 + brandId: v.brandId,
  157 + brandName: v.brandName,
  158 + seriesId: k.seriesId,
  159 + seriesName: k.seriesName,
  160 + specId: r.specId,
  161 + specName: r.specName,
  162 + applyCarType: 3,
  163 + };
  164 + result.push(item);
  165 + });
  166 + } else {
  167 + const item = { brandId: v.brandId, brandName: v.brandName, seriesId: k.seriesId, seriesName: k.seriesName, applyCarType: 2 };
  168 + result.push(item);
  169 + }
  170 + });
  171 + } else {
  172 + const item = { brandId: v.brandId, brandName: v.brandName, applyCarType: 1 };
  173 + result.push(item);
  174 + }
  175 + });
  176 + return result;
  177 + }
  178 +
  179 + async function onFinish() {
  180 + const params = await form.validateFields();
  181 + const _params: SaveParams = {
  182 + partName: selectPart?.partName,
  183 + partCode: selectPart?.partCode,
  184 + beginTime: moment(params.time[0]).startOf('day').valueOf(),
  185 + endTime: moment(params.time[1]).endOf('day').valueOf(),
  186 + clientAmount: params.clientAmount,
  187 + clientFlowAmount: params.clientAmount,
  188 + clientAmountFlow: 1,
  189 + partCost: params.partCost,
  190 + partCostFlow: 1,
  191 + partFlowCost: params.partCost,
  192 + carDtoList: handleCarData(params.optionalAuth),
  193 + id: modalData.data?.id,
  194 + };
  195 + setConfirm(true);
  196 + saveConfigApi(_params)
  197 + .then((res) => {
  198 + message.success(res.result);
  199 + setConfirm(false);
  200 + setLoading(true);
  201 + onCancle();
  202 + })
  203 + .catch((e) => {
  204 + message.error(e.message);
  205 + setConfirm(false);
  206 + });
  207 + }
  208 +
  209 + return (
  210 + <Modal
  211 + destroyOnClose
  212 + forceRender
  213 + title={modalData.data?.id ? '编辑随车配件零售配置' : '新增随车配件零售配置'}
  214 + open={modalData.visible}
  215 + maskClosable={false}
  216 + onCancel={onCancle}
  217 + afterClose={form.resetFields}
  218 + width="60%"
  219 + footer={[
  220 + <Button key="cancel" loading={confirm} disabled={confirm} onClick={onCancle} style={{ marginLeft: 10 }}>
  221 + 取消
  222 + </Button>,
  223 + <Button key="submit" disabled={confirm} onClick={debounce(onFinish, 380)} type="primary" htmlType="submit" loading={confirm}>
  224 + 确认
  225 + </Button>,
  226 + ]}
  227 + >
  228 + <Form form={form}>
  229 + <Form.Item
  230 + name="optionalAuth"
  231 + label="适用车辆"
  232 + rules={[{ required: true, message: '选择车辆' }]}
  233 + tooltip={
  234 + <span style={{ color: '#ccc', fontSize: 12 }}>
  235 + *点击图标
  236 + <PlusSquareOutlined />
  237 + 展开授权详情
  238 + </span>
  239 + }
  240 + >
  241 + <CarTableTreeAuth onChange={(value) => onCarChange(value)} brandMultiple={false} />
  242 + </Form.Item>
  243 + <Form.Item label="配件名称" name="part" rules={[{ required: true, message: '请选择' }]}>
  244 + <Select
  245 + onChange={(value, option) => onPartChange(option)}
  246 + placeholder="请选择"
  247 + showSearch
  248 + allowClear
  249 + optionFilterProp="children"
  250 + >
  251 + {partList.map((v) => (
  252 + <Option key={v.partCode} value={v.partCode} label={v.partName}>
  253 + {v.partName}
  254 + ({v.partCode})
  255 + </Option>
  256 + ))}
  257 + </Select>
  258 + </Form.Item>
  259 + {/* <Form.Item label="配件名称" name="part" rules={[{ required: true, message: '请选择' }]}>
  260 + <ChargePileSelect />
  261 + </Form.Item> */}
  262 + <Form.Item label="收客户款款项流向" required style={{ marginBottom: 0 }}>
  263 + <Form.Item
  264 + name="clientAmount"
  265 + rules={[{ required: true, message: '请输入' }]}
  266 + style={{ display: 'inline-block', width: 'calc(50% - 8px)' }}
  267 + >
  268 + <InputNumber
  269 + style={{ width: '100%' }}
  270 + controls={false}
  271 + min={0}
  272 + formatter={(value: any) => `${value}元`}
  273 + precision={2}
  274 + parser={(value?: string) => value?.replace('元', '')}
  275 + onChange={(value) => setClientFlowAmount(value)}
  276 + />
  277 + </Form.Item>
  278 + {clientFlowAmount ? (
  279 + <Form.Item name="clientAmountFlow" style={{ display: 'inline-block' }}>
  280 + ({AmountFlow[1]}
  281 + <span style={{ color: '#ff921c' }}>{clientFlowAmount}</span>元)
  282 + </Form.Item>
  283 + ) : null}
  284 + </Form.Item>
  285 + <Form.Item label="配件款款项流向" required style={{ marginBottom: 0 }}>
  286 + <Form.Item name="partCost" rules={[{ required: true, message: '请输入' }]} style={{ display: 'inline-block', width: 'calc(50% - 8px)' }}>
  287 + <InputNumber
  288 + style={{ width: '100%' }}
  289 + controls={false}
  290 + min={0}
  291 + formatter={(value: any) => `${value}元`}
  292 + precision={2}
  293 + parser={(value?: string) => value?.replace('元', '')}
  294 + onChange={(value) => setPartFlowCost(value)}
  295 + />
  296 + </Form.Item>
  297 + {partFlowCost ? (
  298 + <Form.Item name="costFlowAmount" style={{ display: 'inline-block' }}>
  299 + <span>
  300 + ({CostFlowEnum[1]}
  301 + <span style={{ color: '#ff921c' }}>{partFlowCost}</span>元)
  302 + </span>
  303 + </Form.Item>
  304 + ) : null}
  305 + </Form.Item>
  306 + <Form.Item label="生效时间" name="time" rules={[{ required: true, message: '请输入' }]}>
  307 + <RangePicker
  308 + disabledDate={(current) => current && current < moment().startOf('day')}
  309 + format="YYYY.MM.DD HH:mm:ss"
  310 + style={{ width: '100%' }}
  311 + showTime={{ defaultValue: [moment('00:00:00', 'HH:mm:ss'), moment('23:59:59', 'HH:mm:ss')] }}
  312 + />
  313 + </Form.Item>
  314 + </Form>
  315 + </Modal>
  316 + );
  317 +}
... ...
src/pages/order3/CarChargePile/components/List.tsx 0 → 100644
  1 +import React from 'react';
  2 +import { message, Popconfirm, Space, Table, Tooltip, Typography } from 'antd';
  3 +import moment from 'moment';
  4 +import { useStore } from '../index';
  5 +import { fetchDeleteApi, ListResult, CostFlowEnum, AmountFlow } from '../api';
  6 +
  7 +const Column = Table.Column;
  8 +
  9 +export default function List() {
  10 + const { list, paginationConfig, loading, setLoading, setModalData, setCarModal } = useStore();
  11 +
  12 + function onConfirm(id?: number) {
  13 + fetchDeleteApi({ id })
  14 + .then((res) => {
  15 + message.success(res.result);
  16 + setLoading(true);
  17 + })
  18 + .catch((e) => {
  19 + message.error(e.message);
  20 + });
  21 + }
  22 +
  23 + function onEdit(value: ListResult) {
  24 + setModalData({ visible: true, data: value });
  25 + }
  26 +
  27 + return (
  28 + <div>
  29 + <Table scroll={{ x: 1300 }} bordered dataSource={list} pagination={paginationConfig} loading={loading} rowKey="id">
  30 + <Column fixed="left" title="配件名称" align="left" dataIndex="partName" />
  31 + <Column fixed="left" title="配件编码" align="left" dataIndex="partCode" />
  32 + <Table.ColumnGroup title="收客户款款项流向">
  33 + <Column
  34 + title="收客户金额"
  35 + dataIndex="clientAmount"
  36 + align="left"
  37 + render={(_text) => (
  38 + <span>
  39 + <span style={{ color: '#ff921c' }}>{_text ?? '--'}</span>元
  40 + </span>
  41 + )}
  42 + />
  43 + <Column
  44 + title="款项流向"
  45 + dataIndex="clientAmountFlow"
  46 + align="left"
  47 + render={(_text, record: ListResult) => (
  48 + <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start' }}>
  49 + <span>{_text && AmountFlow[_text]}</span>
  50 + <span>
  51 + <span style={{ color: '#ff921c' }}>{record.clientFlowAmount ?? '--'}</span>元
  52 + </span>
  53 + </div>
  54 + )}
  55 + />
  56 + </Table.ColumnGroup>
  57 +
  58 + {/* <Table.ColumnGroup title="配件款款项流向"> */}
  59 + {/* <Column
  60 + title="配件款款项流向"
  61 + dataIndex="partCost"
  62 + align="left"
  63 + render={(_text) => (
  64 + <span>
  65 + <span style={{ color: '#ff921c' }}>{_text ?? '--'}</span>元
  66 + </span>
  67 + )}
  68 + /> */}
  69 + <Column
  70 + title="配件款款项流向"
  71 + dataIndex="partCostFlow"
  72 + align="left"
  73 + render={(_text, record: ListResult) => (
  74 + <div style={{ display: 'flex', flexDirection: 'column', alignItems: 'flex-start' }}>
  75 + <span>{_text && CostFlowEnum[_text]}</span>
  76 + <span>
  77 + <span style={{ color: '#ff921c' }}>{record.partCost ?? '--'}</span>元
  78 + </span>
  79 + </div>
  80 + )}
  81 + />
  82 + {/* </Table.ColumnGroup> */}
  83 +
  84 + <Column
  85 + title="适用车辆"
  86 + align="left"
  87 + dataIndex="cars"
  88 + render={(_text, record: ListResult) => (
  89 + <span onClick={() => setCarModal({ visible: true, data: record })} style={{ color: '#4189FD' }}>
  90 + 查看
  91 + </span>
  92 + )}
  93 + />
  94 + <Column
  95 + title="生效时段"
  96 + align="left"
  97 + dataIndex="beginTime"
  98 + width={200}
  99 + render={(_text, record: any) => (
  100 + <>
  101 + <Tooltip
  102 + placement="topLeft"
  103 + color="#FFF"
  104 + title={
  105 + <span style={{ color: '#666' }}>
  106 + {record.beginTime && moment(record.beginTime).format('YYYY-MM-DD HH:mm:ss')}至
  107 + {record.endTime && moment(record.endTime).format('YYYY-MM-DD HH:mm:ss')}
  108 + </span>
  109 + }
  110 + >
  111 + <Space direction="vertical">
  112 + <span> {record.beginTime && moment(record.beginTime).format('YYYY-MM-DD HH:mm:ss')}至</span>
  113 + <span> {record.endTime && moment(record.endTime).format('YYYY-MM-DD HH:mm:ss')}</span>
  114 + </Space>
  115 + </Tooltip>
  116 + </>
  117 + )}
  118 + />
  119 + <Column
  120 + title="操作"
  121 + align="left"
  122 + dataIndex="cars"
  123 + render={(_text, record: ListResult) => (
  124 + <Space wrap>
  125 + <Popconfirm title="是否删除?" onConfirm={() => onConfirm(record.id)}>
  126 + <Typography.Link style={{ color: '#999' }}>删除</Typography.Link>
  127 + </Popconfirm>
  128 +
  129 + <Typography.Link onClick={() => onEdit(record)} style={{ color: '#4189FD' }}>
  130 + 编辑
  131 + </Typography.Link>
  132 + </Space>
  133 + )}
  134 + />
  135 + </Table>
  136 + </div>
  137 + );
  138 +}
... ...
src/pages/order3/CarChargePile/components/style.less 0 → 100644
  1 +.span_limit_1 {
  2 + display: -webkit-box;
  3 + overflow: hidden;
  4 + text-align: left;
  5 + text-overflow: ellipsis;
  6 + /*! autoprefixer: off */
  7 + -webkit-box-orient: vertical;
  8 + -webkit-line-clamp: 1;
  9 +}
  10 +
  11 +.ant-input-affix-wrapper {
  12 + box-sizing: border-box;
  13 + min-height: 32px;
  14 + padding: 1px 11px 1px 4px;
  15 +
  16 + .StaffSelectNew_Container {
  17 + display: flex;
  18 + flex-wrap: wrap;
  19 + align-items: center;
  20 + justify-content: flex-start;
  21 + box-sizing: border-box;
  22 + width: 100%;
  23 + padding-top: 2px;
  24 + cursor: text;
  25 +
  26 + .tag {
  27 + position: relative;
  28 + display: flex;
  29 + align-items: center;
  30 + flex: none;
  31 + box-sizing: border-box;
  32 + max-width: 100%;
  33 + height: 24px;
  34 + margin: 2px 4px 2px 0;
  35 + margin-top: 2px;
  36 + margin-bottom: 2px;
  37 + padding: 0 4px 0 8px;
  38 + line-height: 22px;
  39 + background: #f5f5f5;
  40 + border: 1px solid #f0f0f0;
  41 + border-radius: 2px;
  42 + cursor: default;
  43 + transition: font-size 0.3s, line-height 0.3s, height 0.3s;
  44 + user-select: none;
  45 + padding-inline-end: 4px;
  46 + font-size: 14px;
  47 + }
  48 +
  49 + .text {
  50 + padding: 0 7px;
  51 + }
  52 +
  53 + .placeholder {
  54 + color: #ccc;
  55 + padding: 0 7px;
  56 + }
  57 + }
  58 +
  59 + .StaffSelectNew_Container_disabled {
  60 + cursor: not-allowed;
  61 + }
  62 +}
  63 +
  64 +.ant-input-affix-wrapper_disabled {
  65 + background-color: rgb(245, 245, 245);
  66 + cursor: not-allowed;
  67 +
  68 + &:hover {
  69 + border-color: #d9d9d9 !important;
  70 + cursor: not-allowed !important;
  71 + }
  72 +}
  73 +
  74 +.StaffSelectNew_checkbox {
  75 + align-items: center;
  76 +
  77 + .ant-checkbox {
  78 + top: 0 !important;
  79 + }
  80 +}
  81 +
  82 +.StaffSelectNew_Filter_Container>.ant-select.ant-select-in-form-item {
  83 + width: auto !important;
  84 +}
0 85 \ No newline at end of file
... ...
src/pages/order3/CarChargePile/index.tsx 0 → 100644
  1 +import React from 'react';
  2 +import { Card, Button, Row, Col, Input } from 'antd';
  3 +import { PageHeaderWrapper } from '@ant-design/pro-layout';
  4 +import { createStore } from '@/hooks/moz';
  5 +import store from './store';
  6 +import List from './components/List';
  7 +import EditMoadal from './components/EditModal';
  8 +import CarModal from './components/CarModal';
  9 +import { debounce } from 'lodash';
  10 +import Filters from '@/pages/order3/Common/Filter';
  11 +
  12 +
  13 +export const { Provider, useStore } = createStore(store);
  14 +
  15 +function AddValueTaskConfig() {
  16 + const { setModalData, setParams, innerParams } = useStore();
  17 +
  18 +
  19 + function handleAdd() {
  20 + setModalData({ visible: true, data: {} });
  21 + }
  22 +
  23 + const handleSearch = debounce((text?: string) => {
  24 + setParams({ ...innerParams, partName: text, current: 1, pageSize: 10 }, true);
  25 + }, 380);
  26 +
  27 + return (
  28 + <PageHeaderWrapper title="随车配件零售配置">
  29 + <Card>
  30 + <Row justify="space-between" style={{ marginBottom: 20 }}>
  31 + <Row>
  32 + <Col>
  33 + <Filters
  34 + onFilter={(value: any) => setParams({ ...innerParams, brandId: value.brandId, seriesId: value.seriesId, specId: value.specId }, true)}
  35 + />
  36 + </Col>
  37 + <Col style={{marginLeft: '20px'}}>
  38 + <Input.Search placeholder="请输入配件名称" allowClear onSearch={(value) => handleSearch(value)} />
  39 + </Col>
  40 + </Row>
  41 + <Col>
  42 + <Button type="primary" onClick={handleAdd}>
  43 + 新增
  44 + </Button>
  45 + </Col>
  46 + </Row>
  47 + <List />
  48 + </Card>
  49 + <EditMoadal />
  50 + <CarModal />
  51 + </PageHeaderWrapper>
  52 + );
  53 +}
  54 +
  55 +export default () => (
  56 + <Provider>
  57 + <AddValueTaskConfig />
  58 + </Provider>
  59 +);
... ...
src/pages/order3/CarChargePile/store.ts 0 → 100644
  1 +import React, { useState } from 'react';
  2 +import * as api from './api';
  3 +import usePagination from '@/hooks/usePagination';
  4 +
  5 +interface ModalData {
  6 + visible: boolean;
  7 + data?: api.ListResult;
  8 +}
  9 +
  10 +export default function useStore() {
  11 + const { list, loading, setLoading, paginationConfig, setParams, innerParams } = usePagination(api.getListApi, {});
  12 + const [modalData, setModalData] = useState<ModalData>({visible: false});
  13 + const [carModal, setCarModal] = useState<ModalData>({visible: false});
  14 +
  15 + return {
  16 + setParams,
  17 + paginationConfig,
  18 + loading,
  19 + setLoading,
  20 + list,
  21 + innerParams,
  22 + modalData,
  23 + setModalData,
  24 + carModal,
  25 + setCarModal
  26 + };
  27 +}
... ...
src/pages/order3/DirectCarPromotion/api.ts 0 → 100644
  1 +import request from '@/utils/request';
  2 +import { FINANCE2_HOST, FVM_HOST, ORDER3 } from '@/utils/host';
  3 +import { http } from '@/typing/http';
  4 +
  5 +type Page<T> = http.PromisePageResp<T>;
  6 +
  7 +export enum DeductionMode {
  8 + '固定金额' = 1,
  9 + '固定比例',
  10 +}
  11 +
  12 +export interface SaveParams {
  13 + id?: number; // 主键id
  14 + brandId?: number; // 品牌id
  15 + brandName?: string; // 品牌名称
  16 + carConfigDtoList: ApplySeriesList[]; // 适用车系列表
  17 + startTime?: number; // 开始时间
  18 + endTime?: number; // 结束时间
  19 + sellName?: string; // 促销名称
  20 + sellAmount?: number; // 促销金额
  21 + dealerAdvancePayment?: boolean; // 经销商垫付
  22 + receivableDeduction?: number; // 挂厂家应收款扣除比例或金额
  23 + fundFlow?: number; // 款项流向(1:折让确认,3:资金账户)
  24 + sellShopConfigDtoList?: AccountShopList[]; // 资金账户列表
  25 + deductionMode?: number; // 扣除方式1.按固定金额2.按比例
  26 +}
  27 +
  28 +export interface ListResult {
  29 + id?: number; // id
  30 + brandId?: number; // 品牌id
  31 + brandName?: string; // 品牌名称
  32 + carSeriesVoList?: ApplySeriesList[]; // 适用车系列表
  33 + startTime?: number; // 开始时间
  34 + endTime?: number; // 结束时间
  35 + sellName?: string; // 促销名称
  36 + sellAmount?: number; // 促销金额
  37 + dealerAdvancePayment?: boolean; // 经销商垫付(经销商垫付(0:否,1:是))
  38 + receivableDeduction?: number; // 挂厂家应收扣除比例
  39 + fundFlow?: number; // 款项流向
  40 + shopAccountVoList?: AccountShopList[]; // 资金账户列表
  41 + deductionMode?: number; // 扣除方式
  42 +}
  43 +
  44 +interface SeriesList {
  45 + seriesId?: number; // 车系id
  46 + seriesName?: string; // 车系名称
  47 + specList: SpecList[]; // 车型列表
  48 +}
  49 +
  50 +interface SpecList {
  51 + specId?: number; // 车型id
  52 + specName?: string; // 车型名称
  53 +}
  54 +
  55 +interface AccountShopList {
  56 + accountId?: number; // 账户id
  57 + accountName?: string; // 账户名称
  58 + shopList: ShopVo[]; // 账户对应门店列表
  59 +}
  60 +
  61 +interface ApplySeriesList {
  62 + brandId?: number; // 品牌id
  63 + brandName?: string; // 品牌名称
  64 + seriesId?: number; // 车系id
  65 + seriesName?: string; // 车系名称
  66 + applyCarType?: number; // 适用车辆类型
  67 + specId?: number; // 车系id
  68 + specName?: string; // 车系名称
  69 + seriesList?: SeriesList[]; // 车系列表(列表返回值)
  70 +}
  71 +
  72 +/**
  73 + * 查询集团在售上架的品牌列表
  74 + * @returns
  75 + */
  76 +export function getOnsaleBrandApi(): http.PromiseResp<EnclosureSpace.OptionVo[]> {
  77 + return request.get(`${FVM_HOST}/erp/putaway/vehicle/support/brand`);
  78 +}
  79 +
  80 +/**
  81 + * 查询集团在售上架的车系列表
  82 + * @param brandId
  83 + * @returns
  84 + */
  85 +export function getOnsaleSeriesApi(brandId: number): http.PromiseResp<EnclosureSpace.OptionVo[]> {
  86 + return request.get(`${FVM_HOST}/erp/putaway/vehicle/support/series`, {
  87 + params: { brandId },
  88 + });
  89 +}
  90 +
  91 +/**
  92 + * 查询直营车厂家促销列表
  93 + * @param params
  94 + * @returns
  95 + */
  96 +export function getListApi(params: any): Page<ListResult> {
  97 + return request.get(`${ORDER3}/erp/factory/direct/sell/config/page`, { params });
  98 +}
  99 +
  100 +/**
  101 + * 新增直营车厂家促销配置
  102 + * @param params
  103 + * @returns
  104 + */
  105 +export function saveConfigApi(params: SaveParams) {
  106 + return request.post(`${ORDER3}/erp/factory/direct/sell/config/save`, params);
  107 +}
  108 +
  109 +/**
  110 + * 编辑直营车厂家促销配置
  111 + * @param params
  112 + * @returns
  113 + */
  114 +export function updateConfigApi(params: SaveParams) {
  115 + return request.post(`${ORDER3}/erp/factory/direct/sell/config/update`, params);
  116 +}
  117 +
  118 +/**
  119 + * 删除直营车厂家促销设置
  120 + * @param id
  121 + * @returns
  122 + */
  123 +export function fetchDeleteConfigApi(id: number) {
  124 + return request.post(`${ORDER3}/erp/factory/direct/sell/config/delete`, { id }, { contentType: 'form-urlencoded' });
  125 +}
  126 +
  127 +export interface GetShopListByAccountReq {
  128 + accountId: number;
  129 +}
  130 +export interface ShopVo {
  131 + shopId: number;
  132 + shopName: string;
  133 +}
  134 +
  135 +export interface AccountShopItem {
  136 + accountId?: number;
  137 + accountName?: string;
  138 + shopList: ShopVo[];
  139 +}
  140 +
  141 +/**
  142 + * 获取授权账户下门店列表
  143 + * @param params
  144 + * @returns
  145 + */
  146 +export function getShopListByAccount(params: GetShopListByAccountReq): http.PromiseResp<ShopVo[]> {
  147 + return request.get(`${FINANCE2_HOST}/common/account/authed/shop/list`, { params });
  148 +}
... ...
src/pages/order3/DirectCarPromotion/components/AccountModal.tsx 0 → 100644
  1 +import React, { useState, useEffect } from 'react';
  2 +import { Modal, Button, message, Form, Select } from 'antd';
  3 +import { useStore } from '../index';
  4 +import { getShopListByAccount } from '../api';
  5 +
  6 +const FormItem = Form.Item;
  7 +export default function AccountModal() {
  8 + const { accountModal, setAccountModal, accountList, setAccountShopList, accountShopList } = useStore();
  9 + const [form] = Form.useForm();
  10 + const [shopData, setShopData] = useState<any>([]);
  11 +
  12 + useEffect(() => {
  13 + if (accountModal.visible && accountModal.data?.accountId) {
  14 + form.setFieldsValue({
  15 + account: {label: accountModal?.data.accountName, value: accountModal?.data.accountId},
  16 + shopList: accountModal.data?.shopList?.map(v => ({label: v.shopName, value: v.shopId})),
  17 + });
  18 + getShopData(accountModal.data.accountId)
  19 + }
  20 + }, [accountModal.visible])
  21 +
  22 + function onAccountChange(accountId?: number) {
  23 + if (!accountId) {
  24 + form.setFieldValue('shopList', undefined);
  25 + return
  26 + }
  27 + getShopListByAccount({ accountId })
  28 + .then((res) => {
  29 + setShopData(res.data || []);
  30 + form.setFieldsValue({
  31 + shopList: res.data?.map((v) => ({ label: v.shopName, value: v.shopId })),
  32 + });
  33 + })
  34 + .catch((e) => {
  35 + message.error(e.message);
  36 + setShopData([]);
  37 + });
  38 + }
  39 +
  40 + function getShopData(accountId: number) {
  41 + getShopListByAccount({ accountId })
  42 + .then((res) => {
  43 + setShopData(res.data || []);
  44 + })
  45 + .catch((e) => {
  46 + message.error(e.message);
  47 + setShopData([]);
  48 + });
  49 + }
  50 +
  51 + function onCancle() {
  52 + form.resetFields();
  53 + setAccountModal({title: '', visible: false, data: undefined});
  54 + }
  55 +
  56 + async function onOK() {
  57 + const params = await form.validateFields();
  58 + const value = {
  59 + accountId: params.account?.value,
  60 + accountName: params.account?.label,
  61 + shopList: params.shopList.map((v: any) => ({shopId: v.value, shopName: v.label})),
  62 + };
  63 + if (accountModal.data?.accountId) {
  64 + const data = JSON.parse(JSON.stringify(accountShopList || []));
  65 + const list = data?.map((v: any) => {
  66 + const item = {
  67 + accountId: v.accountId,
  68 + accountName: v.accountName,
  69 + shopList: v.shopList,
  70 + };
  71 + if (accountModal.data?.accountId === params.account.value) {
  72 + item.shopList = value.shopList;
  73 + }
  74 + return item
  75 + })
  76 + setAccountShopList(list);
  77 + } else {
  78 + setAccountShopList([...accountShopList, value]);
  79 + }
  80 + setAccountModal({visible: false, title: '', data: undefined});
  81 + }
  82 +
  83 + return (
  84 + <Modal
  85 + destroyOnClose
  86 + forceRender
  87 + title={accountModal.title}
  88 + open={accountModal.visible}
  89 + maskClosable={false}
  90 + onCancel={onCancle}
  91 + afterClose={form.resetFields}
  92 + footer={[
  93 + <Button key="cancel" onClick={onCancle} style={{ marginRight: 10 }}>
  94 + 取消
  95 + </Button>,
  96 + <Button key="submit" onClick={onOK} type="primary" htmlType="submit">
  97 + 确认
  98 + </Button>,
  99 + ]}
  100 + >
  101 + <Form form={form}>
  102 + <FormItem label="资金账户" name="account" rules={[{ required: true, message: '请选择资金账户' }]}>
  103 + <Select
  104 + showSearch
  105 + allowClear
  106 + labelInValue
  107 + optionFilterProp="label"
  108 + placeholder="请选择资金账户"
  109 + style={{ width: '100%' }}
  110 + disabled={!!accountModal.data?.accountId}
  111 + onChange={value => onAccountChange(value?.value)}
  112 + options={accountList.map((v) => ({ label: v.name, value: v.id }))}
  113 + />
  114 + </FormItem>
  115 + <FormItem label="适用门店" name="shopList" rules={[{ required: true, message: '请选择适用门店' }]}>
  116 + <Select
  117 + showSearch
  118 + allowClear
  119 + labelInValue
  120 + mode="multiple"
  121 + optionFilterProp="label"
  122 + placeholder="请选择适用门店"
  123 + style={{ width: '100%' }}
  124 + options={shopData.map((v: any) => ({ label: v.shopName, value: v.shopId }))}
  125 + />
  126 + </FormItem>
  127 + </Form>
  128 + </Modal>
  129 + );
  130 +}
... ...
src/pages/order3/DirectCarPromotion/components/CarShopModal.tsx 0 → 100644
  1 +import React, { useMemo } from 'react';
  2 +import { Modal } from 'antd';
  3 +import _ from 'lodash';
  4 +import { useStore } from '../index';
  5 +import CarTableTreeAuth from '@/components/CarTableTreeAuth';
  6 +
  7 +const ShowModal = () => {
  8 + const { showModal, setShowModal } = useStore();
  9 +
  10 + const handleCancel = () => {
  11 + setShowModal({ ...showModal, visible: false });
  12 + };
  13 +
  14 + const carData = useMemo(() => {
  15 + const data: any[] = JSON.parse(JSON.stringify(showModal.data || []));
  16 + if (data.length) {
  17 + data.forEach((v) => {
  18 + if (v.seriesList?.length) {
  19 + v.children = v.seriesList;
  20 + v.seriesList.forEach((k: any) => {
  21 + if (k.specList?.length) {
  22 + k.children = k.specList;
  23 + }
  24 + });
  25 + }
  26 + });
  27 + }
  28 + return data;
  29 + }, [showModal.visible]);
  30 +
  31 + return (
  32 + <Modal
  33 + destroyOnClose
  34 + forceRender
  35 + open={showModal.visible}
  36 + title={showModal.title || ''}
  37 + maskClosable={false}
  38 + onCancel={handleCancel}
  39 + onOk={handleCancel}
  40 + >
  41 + <CarTableTreeAuth value={carData} disabled brandMultiple={false} />
  42 + </Modal>
  43 + );
  44 +};
  45 +export default ShowModal;
... ...
src/pages/order3/DirectCarPromotion/components/EditModal.tsx 0 → 100644
  1 +import React, { useState, useEffect } from 'react';
  2 +import { Modal, Form, DatePicker, Button, message, Input, Select, Typography, Card, Row, Radio, Table, Space, Popconfirm, Tooltip } from 'antd';
  3 +import * as api from '../api';
  4 +import { useStore } from '../index';
  5 +import moment from 'moment';
  6 +import { PlusSquareOutlined, ExclamationCircleFilled } from '@ant-design/icons';
  7 +import CarTableTreeAuth from '@/components/CarTableTreeAuth';
  8 +import { CarAuthList } from '@/components/CarTableTreeAuth/entity';
  9 +import { CapitalFlowEnum } from '../entity';
  10 +
  11 +const FormItem = Form.Item;
  12 +const { Option } = Select;
  13 +
  14 +export default function EditModal() {
  15 + const [form] = Form.useForm();
  16 + const [confirmLoading, setConfirmLoading] = useState<boolean>(false);
  17 + const { editModal, setEditModal, setLoading, accountShopList, setAccountModal, setAccountShopList, edit, setEdit } = useStore();
  18 +
  19 + useEffect(() => {
  20 + if (editModal.visible && editModal.data?.id) {
  21 + const applyCarList = editModal.data?.carSeriesVoList?.map((v) => {
  22 + const item: any = {
  23 + brandId: v.brandId,
  24 + brandName: v.brandName,
  25 + };
  26 + if (v.seriesList?.length) {
  27 + const series = v.seriesList.map((k) => {
  28 + const seriesItem: any = { seriesId: k.seriesId, seriesName: k.seriesName };
  29 + if (k.specList.length) {
  30 + const spec = k.specList.map((l) => ({ specId: l.specId, specName: l.specName }));
  31 + seriesItem.children = spec;
  32 + }
  33 + return seriesItem;
  34 + });
  35 + item.children = series;
  36 + }
  37 + return item;
  38 + });
  39 +
  40 + const current = editModal.data || {};
  41 + form.setFieldsValue({
  42 + time: [moment(current.startTime), moment(current.endTime)],
  43 + sellName: current.sellName,
  44 + sellAmount: current.sellAmount,
  45 + dealerAdvancePayment: current.dealerAdvancePayment,
  46 + deductionMode: current.deductionMode,
  47 + receivableDeduction: current.receivableDeduction,
  48 + fundFlow: current.fundFlow,
  49 + optionalAuth: applyCarList,
  50 + // accountShopList,
  51 + });
  52 + if (current.fundFlow === 3) {
  53 + setAccountShopList(current.shopAccountVoList || []);
  54 + }
  55 + }
  56 + }, [editModal.visible]);
  57 +
  58 + const handleSave = async () => {
  59 + const value = await form.validateFields();
  60 + const _accountShopList: any = [];
  61 + accountShopList?.map((v: any) => {
  62 + v.shopList?.map((k: any) => {
  63 + const item = { shopId: k.shopId, shopName: k.shopName, accountId: v.accountId, accountName: v.accountName };
  64 + _accountShopList.push(item);
  65 + return {...k};
  66 + })
  67 + return {...v};
  68 + })
  69 + setConfirmLoading(true);
  70 + const params: any = {
  71 + brandId: value.optionalAuth[0].brandId, // 品牌id
  72 + brandName: value.optionalAuth[0].brandName, // 品牌名称
  73 + startTime: moment(value.time[0]).valueOf(),
  74 + endTime: moment(value.time[1]).valueOf(),
  75 + carConfigDtoList: handleCarData(value.optionalAuth),
  76 + sellName: value.sellName,
  77 + sellAmount: value.sellAmount,
  78 + dealerAdvancePayment: value.dealerAdvancePayment,
  79 + receivableDeduction: value.receivableDeduction,
  80 + deductionMode: value.deductionMode,
  81 + fundFlow: value.fundFlow,
  82 + };
  83 + if (value.fundFlow === 3) {
  84 + params.sellShopConfigDtoList = _accountShopList;
  85 + }
  86 + if (edit) {
  87 + params.id = editModal.data?.id;
  88 + }
  89 + const requestApi = edit ? api.updateConfigApi : api.saveConfigApi;
  90 + requestApi({ ...params })
  91 + .then((res) => {
  92 + message.success(res.result);
  93 + setConfirmLoading(false);
  94 + setLoading(true);
  95 + setEdit(false);
  96 + setEditModal({ visible: false, data: undefined });
  97 + })
  98 + .catch((e) => {
  99 + setConfirmLoading(false);
  100 + message.error(e.message);
  101 + setEdit(false)
  102 + });
  103 + };
  104 +
  105 + /**
  106 + * 将树形车辆结构转换成扁平结构
  107 + * applyCarType为最细化层级的类型
  108 + * @param value
  109 + * @returns
  110 + */
  111 + function handleCarData(value: CarAuthList[]) {
  112 + const result: any = [];
  113 + value.forEach((v) => {
  114 + if (v.children?.length) {
  115 + v.children.forEach((k) => {
  116 + if (k.children?.length) {
  117 + k.children.forEach((r) => {
  118 + const item = {
  119 + brandId: v.brandId,
  120 + brandName: v.brandName,
  121 + seriesId: k.seriesId,
  122 + seriesName: k.seriesName,
  123 + specId: r.specId,
  124 + specName: r.specName,
  125 + applyCarType: 3,
  126 + };
  127 + result.push(item);
  128 + });
  129 + } else {
  130 + const item = {
  131 + brandId: v.brandId,
  132 + brandName: v.brandName,
  133 + seriesId: k.seriesId,
  134 + seriesName: k.seriesName,
  135 + applyCarType: 2,
  136 + };
  137 + result.push(item);
  138 + }
  139 + });
  140 + } else {
  141 + const item = {
  142 + brandId: v.brandId,
  143 + brandName: v.brandName,
  144 + applyCarType: 1,
  145 + };
  146 + result.push(item);
  147 + }
  148 + });
  149 + return result;
  150 + }
  151 +
  152 + function close() {
  153 + setEditModal({visible: false, data: undefined})
  154 + setAccountShopList([]);
  155 + setEdit(false);
  156 + };
  157 +
  158 + function accountShopListRules(getFieldValue: any) {
  159 + const capitalFlow = getFieldValue('fundFlow');
  160 + // const accountShopList = getFieldValue('accountShopList');
  161 + if (capitalFlow === 3 && accountShopList.length <= 0) {
  162 + return Promise.reject(new Error('请添加至少一个门店资金账户'));
  163 + }
  164 + return Promise.resolve();
  165 + };
  166 +
  167 + function onChangeMode() {
  168 + form.setFieldsValue({ deductionRatio: undefined });
  169 + }
  170 +
  171 + function deleteAccount(id?: number) {
  172 + const list = accountShopList.filter(v => v.accountId !== id);
  173 + setAccountShopList(list);
  174 + }
  175 +
  176 + function renderShop(value?: api.ShopVo[]) {
  177 + if (!value?.length) {
  178 + return '--'
  179 + }
  180 + if (value.length <= 3) {
  181 + const text = value.map(v => v.shopName).join(",");
  182 + return text;
  183 + }
  184 + const len = value.length;
  185 + const str = value.slice(0, 3).map(v => v.shopName).join(",");
  186 + const text = value.map(v => v.shopName).join(",");
  187 + return (
  188 + <Tooltip
  189 + placement="topLeft"
  190 + color="#FFF"
  191 + title={
  192 + <span style={{ color: '#666' }}>
  193 + {text}
  194 + </span>
  195 + }
  196 + >
  197 + <span>{str}等{len}个门店</span>
  198 + </Tooltip>
  199 + );
  200 + }
  201 +
  202 + return (
  203 + <Modal
  204 + title={`${edit ? '编辑' : '新增'}直营车厂家促销`}
  205 + open={editModal.visible}
  206 + onCancel={close}
  207 + style={{ minWidth: 1000 }}
  208 + maskClosable={false}
  209 + destroyOnClose
  210 + forceRender
  211 + afterClose={() => {
  212 + setEditModal({ visible: false, data: undefined });
  213 + form.resetFields();
  214 + }}
  215 + footer={null}
  216 + >
  217 + <div id="modal">
  218 + <Form form={form} labelAlign="left" onFinish={handleSave} preserve={false}>
  219 + <Form.Item
  220 + name="optionalAuth"
  221 + label="促销车辆"
  222 + rules={[{ required: true, message: '选择车辆' }]}
  223 + tooltip={
  224 + <span style={{ color: '#ccc', fontSize: 12 }}>
  225 + *点击图标
  226 + <PlusSquareOutlined />
  227 + 展开授权详情
  228 + </span>
  229 + }
  230 + >
  231 + <CarTableTreeAuth brandMultiple={false} />
  232 + </Form.Item>
  233 + <Form.Item name="time" label="生效时间段" rules={[{ type: 'array' as const, required: true, message: '请选择生效时间段!' }]}>
  234 + <DatePicker.RangePicker
  235 + style={{ width: '100%' }}
  236 + disabledDate={(current) => !!current && current < moment().startOf('day')}
  237 + picker="date"
  238 + />
  239 + </Form.Item>
  240 + <FormItem name="sellName" label="促销名称" rules={[{ required: true, message: '请输入促销名称' }]}>
  241 + <Input placeholder="请输入促销名称" style={{ width: '100%' }} />
  242 + </FormItem>
  243 + <FormItem
  244 + name="sellAmount"
  245 + label="促销金额"
  246 + rules={[{ required: true, pattern: /^(\d+|\d+\.\d{1,2})$/, message: '请输入促销金额' }]}
  247 + >
  248 + <Input placeholder="请输入促销金额" style={{ width: '100%' }} suffix="元" />
  249 + </FormItem>
  250 + <FormItem
  251 + extra={
  252 + <div style={{ marginTop: '5px' }}>
  253 + <ExclamationCircleFilled style={{ fontSize: '11px', color: '#ff921c' }} />
  254 + <span style={{ color: '#999', fontSize: '12px', paddingLeft: '5px' }}>
  255 + 示例:双十二前下定锁车额外享6000元现金(经销商先兑换给客户,然后厂家折让方式返回;挂应收,折让收回后冲抵应收)
  256 + </span>
  257 + </div>
  258 + }
  259 + name="dealerAdvancePayment"
  260 + label="经销商是否垫付给客户"
  261 + rules={[{ required: true, message: '请选择经销商是否垫付给客户' }]}
  262 + >
  263 + <Select placeholder="清选择">
  264 + <Select.Option key={1} value={true}>
  265 + 是
  266 + </Select.Option>
  267 + <Select.Option key={2} value={false}>
  268 + 否
  269 + </Select.Option>
  270 + </Select>
  271 + </FormItem>
  272 +
  273 + <Form.Item
  274 + noStyle
  275 + shouldUpdate={(prevValues, currValues) => {
  276 + return prevValues.dealerAdvancePayment !== currValues.dealerAdvancePayment;
  277 + }}
  278 + >
  279 + {({getFieldValue}) => {
  280 + const value = getFieldValue('dealerAdvancePayment');
  281 + if (!value) {
  282 + return;
  283 + }
  284 + return (
  285 + <>
  286 + <FormItem name="deductionMode" label="扣除方式" rules={[{ required: true, message: '请选择扣除方式' }]}>
  287 + <Radio.Group onChange={() => onChangeMode()}>
  288 + <Radio value={1}>固定金额</Radio>
  289 + <Radio value={2}>固定比例</Radio>
  290 + </Radio.Group>
  291 + </FormItem>
  292 + <Form.Item
  293 + noStyle
  294 + shouldUpdate={(prevValues, currValues) => {
  295 + return prevValues.deductionMode !== currValues.deductionMode;
  296 + }}
  297 + >
  298 + {({ getFieldValue }) => {
  299 + const value = getFieldValue('deductionMode');
  300 + if (!value) {
  301 + return null;
  302 + }
  303 + return value === 1 ? (
  304 + <FormItem
  305 + name="receivableDeduction"
  306 + label="挂厂家应收扣除金额"
  307 + dependencies={['sellAmount']}
  308 + rules={[
  309 + {
  310 + required: true,
  311 + pattern: /^(\d+|\d+\.\d{1,2})$/,
  312 + message: '请输入挂厂家应收扣除金额',
  313 + },
  314 + ({ getFieldValue }) => ({
  315 + validator(_, value) {
  316 + if (!value || +(getFieldValue('sellAmount') || 0) >= +value) {
  317 + return Promise.resolve();
  318 + }
  319 + return Promise.reject(new Error('挂厂家应收扣除金额不能大于代收金额!'));
  320 + },
  321 + }),
  322 + ]}
  323 + >
  324 + <Input placeholder="请输入扣除金额" style={{ width: '100%' }} suffix="元" />
  325 + </FormItem>
  326 + ) : (
  327 + <FormItem
  328 + name="receivableDeduction"
  329 + extra="比例值为0~100"
  330 + label="挂厂家应收扣除比例"
  331 + rules={[
  332 + {
  333 + required: true,
  334 + pattern: /^(\d+|\d+\.\d{1,2})$/,
  335 + message: '请输入挂厂家应收扣除比例',
  336 + },
  337 + ({ getFieldValue }) => ({
  338 + validator(_, value) {
  339 + if (value && +value <= 100 && +value >= 0) {
  340 + return Promise.resolve();
  341 + }
  342 + return Promise.reject(new Error('比例范围应在0~100!'));
  343 + },
  344 + }),
  345 + ]}
  346 + >
  347 + <Input placeholder="请输入扣除比例" style={{ width: '100%' }} suffix="%" />
  348 + </FormItem>
  349 + );
  350 + }}
  351 + </Form.Item>
  352 + <FormItem noStyle>
  353 + <FormItem label="款项流向" name="fundFlow" rules={[{ required: true, message: '请选择款项流向' }]}>
  354 + <Select placeholder="请选择款项流向">
  355 + {[1, 2, 3].map((item) => (
  356 + <Option key={item} value={item}>
  357 + {CapitalFlowEnum[item]}
  358 + </Option>
  359 + ))}
  360 + </Select>
  361 + </FormItem>
  362 + <Form.Item
  363 + noStyle
  364 + shouldUpdate={(prevValues, currValues) => {
  365 + return prevValues.fundFlow !== currValues.fundFlow;
  366 + }}
  367 + >
  368 + {({ getFieldValue }) => {
  369 + if (getFieldValue('fundFlow') !== 3) {
  370 + return null;
  371 + }
  372 + return (
  373 + <FormItem
  374 + label="&emsp;&emsp;&emsp;&emsp;&emsp;"
  375 + name="accountShopList"
  376 + colon={false}
  377 + rules={[
  378 + ({ getFieldValue }) => ({
  379 + validator(_, value) {
  380 + return accountShopListRules(getFieldValue);
  381 + },
  382 + }),
  383 + ]}
  384 + >
  385 + <Card>
  386 + <Row justify="end" style={{ marginBottom: 10 }}>
  387 + <Typography.Link
  388 + onClick={() => setAccountModal({ title: '新增', visible: true, data: undefined })}
  389 + style={{ color: '#4189FD' }}
  390 + >
  391 + 新增
  392 + </Typography.Link>
  393 + </Row>
  394 + <Table dataSource={accountShopList} pagination={false} rowKey="accountId">
  395 + <Table.Column title="资金账户" dataIndex="accountName" align="left" />
  396 + <Table.Column title="适用门店" dataIndex="shopList" align="left" render={(_text) => renderShop(_text)} />
  397 + <Table.Column
  398 + title="操作"
  399 + dataIndex="option"
  400 + align="left"
  401 + render={(_text, record: any) => (
  402 + <Space wrap>
  403 + <Popconfirm onConfirm={() => deleteAccount(record.accountId)} title="是否删除?">
  404 + <Typography.Link style={{ color: '#999' }}>删除</Typography.Link>
  405 + </Popconfirm>
  406 + <Typography.Link
  407 + onClick={() => setAccountModal({ title: '编辑', data: record, visible: true })}
  408 + style={{ color: '#4189FD' }}
  409 + >
  410 + 编辑
  411 + </Typography.Link>
  412 + </Space>
  413 + )}
  414 + />
  415 + </Table>
  416 + </Card>
  417 + </FormItem>
  418 + );
  419 + }}
  420 + </Form.Item>
  421 + </FormItem>
  422 + </>
  423 + );
  424 + }}
  425 + </Form.Item>
  426 + </Form>
  427 + <Row justify="center">
  428 + <Space>
  429 + <Button key="cancel" onClick={close} style={{ marginLeft: 10 }}>
  430 + 取消
  431 + </Button>
  432 + <Button key="submit" onClick={handleSave} type="primary" htmlType="submit" loading={confirmLoading}>
  433 + 确认
  434 + </Button>
  435 + </Space>
  436 + </Row>
  437 + </div>
  438 + </Modal>
  439 + );
  440 +}
... ...
src/pages/order3/DirectCarPromotion/components/Filter.tsx 0 → 100644
  1 +import React, { useCallback } from 'react';
  2 +import { Button, Row } from 'antd';
  3 +import { useStore } from '../index';
  4 +import Filters from '@/pages/order3/Common/Filter/index';
  5 +import { debounce } from 'lodash';
  6 +
  7 +export default function Filter() {
  8 + const { setEditModal, setParams, innerParams } = useStore();
  9 +
  10 + const _onChange = useCallback(
  11 + debounce((params: any) => {
  12 + setParams({ ...innerParams, ...params, current: 1 }, true);
  13 + }, 500),
  14 + [innerParams],
  15 + );
  16 +
  17 + return (
  18 + <Row justify="space-between" style={{ marginBottom: 20 }}>
  19 + <div style={{ width: 200 }}>
  20 + <Filters
  21 + onFilter={(value: any) => {
  22 + _onChange({
  23 + brandId: value?.brandId,
  24 + seriesId: value?.seriesId,
  25 + specId: value?.specId,
  26 + });
  27 + }}
  28 + />
  29 + </div>
  30 + <Button
  31 + type="primary"
  32 + onClick={() => {
  33 + setEditModal({visible: true, data: undefined})
  34 + }}
  35 + >
  36 + 新增
  37 + </Button>
  38 + </Row>
  39 + );
  40 +}
... ...
src/pages/order3/DirectCarPromotion/components/List.tsx 0 → 100644
  1 +import React, { useState } from 'react';
  2 +import { message, Modal, Popconfirm, Space, Table, Typography, Divider } from 'antd';
  3 +import moment from 'moment';
  4 +import { useStore } from '../index';
  5 +import * as api from '../api';
  6 +import { CapitalFlowEnum } from '../entity';
  7 +import ShopAccountTable from './ShopAccountTable';
  8 +import { isNil } from 'lodash';
  9 +
  10 +const Column = Table.Column;
  11 +
  12 +export default function List() {
  13 + const { list, paginationConfig, loading, setLoading, setShowModal, setEdit, setEditModal } = useStore();
  14 + const [visible, setVisible] = useState(false);
  15 + const [accounts, setAccounts] = useState<api.AccountShopItem[]>([] as api.AccountShopItem[]);
  16 +
  17 + function handleDelete(id: number) {
  18 + api
  19 + .fetchDeleteConfigApi(id)
  20 + .then((res) => {
  21 + message.success(res.result);
  22 + setLoading(true);
  23 + })
  24 + .catch((e) => {
  25 + message.error(e.message);
  26 + });
  27 + }
  28 +
  29 + function onShow(value: any, title: string) {
  30 + setShowModal({
  31 + visible: true,
  32 + title,
  33 + data: value,
  34 + });
  35 + }
  36 + return (
  37 + <div>
  38 + <Table scroll={{ x: 1400 }} dataSource={list} pagination={paginationConfig} rowKey="id" loading={loading}>
  39 + <Column fixed="left" title="品牌" dataIndex="brandName" align="left" />
  40 + <Column
  41 + title="适用车辆"
  42 + align="left"
  43 + dataIndex="factorySellCarSeriesVoList"
  44 + render={(_text, record: api.ListResult) => (
  45 + <span style={{ display: 'block' }}>
  46 + <a onClick={() => onShow(record.carSeriesVoList, '适用车辆')}>查看</a>
  47 + </span>
  48 + )}
  49 + />
  50 + <Column
  51 + title="生效时间段"
  52 + dataIndex="startTime"
  53 + align="left"
  54 + render={(text, record: api.ListResult) => {
  55 + return `${moment(record.startTime).format('YYYY.MM.DD')}~${moment(record.endTime).format('YYYY.MM.DD')}`;
  56 + }}
  57 + />
  58 + <Column title="促销名称" dataIndex="sellName" align="left" />
  59 + <Column title="促销金额" dataIndex="sellAmount" align="left" render={(text: any) => <span>{text + '元'}</span>} />
  60 + <Column
  61 + title="经销商垫付给客户"
  62 + dataIndex="dealerAdvancePayment"
  63 + align="left"
  64 + render={(_text) => (isNil(_text) ? null : _text ? '是' : '否')}
  65 + />
  66 +
  67 + <Column
  68 + title="挂厂家应收款"
  69 + dataIndex="deductionRatio"
  70 + align="left"
  71 + render={(text: any, record: api.ListResult) =>
  72 + !!record.dealerAdvancePayment ? (
  73 + <div style={{ display: 'flex', flexDirection: 'column' }}>
  74 + <span>扣除方式:{(record.deductionMode && api.DeductionMode[record.deductionMode]) || '--'}</span>
  75 + {record.deductionMode ? (
  76 + <span>
  77 + {record.deductionMode === 1
  78 + ? `扣除金额:${record.receivableDeduction ?? '--'}元`
  79 + : `扣除比例:${record.receivableDeduction ?? '--'}%`}
  80 + </span>
  81 + ) : null}
  82 + </div>
  83 + ) : null
  84 + }
  85 + />
  86 + <Column
  87 + title="款项流向"
  88 + dataIndex="fundFlow"
  89 + align="left"
  90 + render={(text: any, record: api.ListResult) => {
  91 + if (!record.dealerAdvancePayment) {
  92 + return '--';
  93 + }
  94 + if (record.fundFlow !== 3) {
  95 + return text ? CapitalFlowEnum[text] : '--';
  96 + }
  97 + return (
  98 + <Typography.Link
  99 + onClick={() => {
  100 + setVisible(true);
  101 + setAccounts(record.shopAccountVoList ?? []);
  102 + }}
  103 + style={{ color: '#4189FD' }}
  104 + >
  105 + {CapitalFlowEnum[text]}
  106 + </Typography.Link>
  107 + );
  108 + }}
  109 + />
  110 + <Column
  111 + title="操作"
  112 + align="left"
  113 + // width={200}
  114 + render={(text, record: api.ListResult) => (
  115 + <Space size={0} wrap split={<Divider type="vertical" />}>
  116 + <Popconfirm title="是否删除此项?" onConfirm={() => handleDelete(record.id || 0)} okText="确定" cancelText="取消">
  117 + <Typography.Link style={{ color: '#999' }}>删除</Typography.Link>
  118 + </Popconfirm>
  119 + <Typography.Link
  120 + onClick={() => {
  121 + setEditModal({ visible: true, data: record });
  122 + setEdit(false);
  123 + }}
  124 + style={{ color: '#4189FD' }}
  125 + >
  126 + 复制
  127 + </Typography.Link>
  128 + <Typography.Link
  129 + onClick={() => {
  130 + setEditModal({ visible: true, data: record });
  131 + setEdit(true);
  132 + }}
  133 + style={{ color: '#4189FD' }}
  134 + >
  135 + 编辑
  136 + </Typography.Link>
  137 + </Space>
  138 + )}
  139 + />
  140 + </Table>
  141 + <Modal width="60%" title="资金账户列表" open={visible} onCancel={() => setVisible(false)} destroyOnClose footer={null}>
  142 + <ShopAccountTable data={accounts} />
  143 + </Modal>
  144 + </div>
  145 + );
  146 +}
... ...
src/pages/order3/DirectCarPromotion/components/ShopAccountTable.tsx 0 → 100644
  1 +import React, { useState } from 'react';
  2 +import { Button, Modal, List, Table } from 'antd';
  3 +import { AccountShopItem, ShopVo } from '../api';
  4 +
  5 +const { Column } = Table;
  6 +
  7 +// 门店资金账户弹框
  8 +interface ShopAccountTableProps {
  9 + data: AccountShopItem[];
  10 +}
  11 +
  12 +const ShopAccountTable = ({ data }: ShopAccountTableProps) => {
  13 + const [shopData, setShopData] = useState<ShopVo[]>([]);
  14 + const [visible, setVisible] = useState(false);
  15 +
  16 + return (
  17 + <>
  18 + <Table dataSource={data} pagination={false} rowKey="accountId" loading={false}>
  19 + <Column title="资金账户" dataIndex="accountName" />
  20 + <Column
  21 + title="授权门店"
  22 + dataIndex="shopList"
  23 + render={(val: ShopVo[], record: AccountShopItem) => {
  24 + let displayName = '';
  25 + if (val.length > 2) {
  26 + displayName = val
  27 + .slice(0, 2)
  28 + .map((item) => item.shopName)
  29 + .join(',');
  30 + displayName = `${displayName}等${val.length}个门店`;
  31 + } else {
  32 + displayName = val.map((item) => item.shopName).join(',');
  33 + }
  34 + return (
  35 + <span
  36 + style={{ color: '#4189FD', cursor: 'pointer' }}
  37 + onClick={() => {
  38 + setShopData(record.shopList || []);
  39 + setVisible(true);
  40 + }}
  41 + >
  42 + {displayName}
  43 + </span>
  44 + );
  45 + }}
  46 + />
  47 + </Table>
  48 + <Modal destroyOnClose title="适用门店" open={visible} maskClosable={false} onCancel={() => setVisible(false)} footer={[]}>
  49 + <List
  50 + size="large"
  51 + style={{ maxHeight: '300px', overflow: 'scroll' }}
  52 + dataSource={shopData}
  53 + renderItem={(item: any) => <List.Item style={{ justifyContent: 'center' }}>{item.shopName}</List.Item>}
  54 + />
  55 + <div
  56 + style={{
  57 + textAlign: 'center',
  58 + marginTop: 12,
  59 + height: 32,
  60 + lineHeight: '32px',
  61 + }}
  62 + >
  63 + <Button type="primary" onClick={() => setVisible(false)}>
  64 + 返回
  65 + </Button>
  66 + </div>
  67 + </Modal>
  68 + </>
  69 + );
  70 +};
  71 +
  72 +export default ShopAccountTable;
... ...
src/pages/order3/DirectCarPromotion/entity.ts 0 → 100644
  1 +export enum CapitalFlowEnum {
  2 + '折让确认' = 1,
  3 + '现金启票账户',
  4 + '资金账户',
  5 +}
0 6 \ No newline at end of file
... ...
src/pages/order3/DirectCarPromotion/index.tsx 0 → 100644
  1 +import React from 'react';
  2 +import { Card } from 'antd';
  3 +import { PageHeaderWrapper } from '@ant-design/pro-layout';
  4 +import Filter from './components/Filter';
  5 +import List from './components/List';
  6 +import { createStore } from '@/hooks/moz';
  7 +import store from './store';
  8 +import EditModal from './components/EditModal';
  9 +import CarShopModal from './components/CarShopModal';
  10 +import AccountModal from '@/pages/order3/DirectCarPromotion/components/AccountModal';
  11 +
  12 +export const { Provider, useStore } = createStore(store);
  13 +
  14 +function ManufactureCollection() {
  15 + return (
  16 + <PageHeaderWrapper title="直营车厂家促销设置">
  17 + <Card>
  18 + <Filter />
  19 + <List />
  20 + </Card>
  21 + <EditModal />
  22 + <CarShopModal />
  23 + <AccountModal />
  24 + </PageHeaderWrapper>
  25 + );
  26 +}
  27 +
  28 +export default () => (
  29 + <Provider>
  30 + <ManufactureCollection />
  31 + </Provider>
  32 +);
... ...
src/pages/order3/DirectCarPromotion/store.ts 0 → 100644
  1 +import { useState } from 'react';
  2 +import usePagination from '@/hooks/usePagination';
  3 +import useInitial from '@/hooks/useInitail';
  4 +import * as API from './api';
  5 +import { getAccountApi } from '@/pages/finance/SpecialAccount/DeductAccount/api';
  6 +import { CapitalAccountTypeEnum } from '@/pages/finance/entitys';
  7 +
  8 +
  9 +interface Show {
  10 + title?: string;
  11 + visible?: boolean;
  12 + data?: any;
  13 +}
  14 +
  15 +interface AccountModal {
  16 + title?: string;
  17 + data?: API.AccountShopItem;
  18 + visible?: boolean;
  19 +}
  20 +
  21 +interface EditModal {
  22 + visible: boolean;
  23 + data?: API.ListResult;
  24 +}
  25 +
  26 +export default function useStore() {
  27 + const { list, paginationConfig, setParams, innerParams, setLoading, loading } = usePagination(API.getListApi, {});
  28 + const [seriesVisiable, setSeriesVisiable] = useState<boolean>(false);
  29 + const { data: brandData } = useInitial(API.getOnsaleBrandApi, [], {});
  30 + const [timer, setTimer] = useState<any>();
  31 + const [result, setResult] = useState<[][]>([]);
  32 + const [showModal, setShowModal] = useState<Show>({ title: '', visible: false, data: [] });
  33 + const [accountModal, setAccountModal] = useState<AccountModal>({title: '新增', visible: false});
  34 + const [accountShopList, setAccountShopList] = useState<API.AccountShopItem[]>([]);
  35 + const { list: accountList } = usePagination(getAccountApi, { current: 1, pageSize: 1000, type: CapitalAccountTypeEnum['银行账户'] }, {});
  36 + const [edit, setEdit] = useState<boolean>(false);
  37 + const [editModal, setEditModal] = useState<EditModal>({visible: false, data: undefined});
  38 +
  39 + return {
  40 + list,
  41 + paginationConfig,
  42 + seriesVisiable,
  43 + setSeriesVisiable,
  44 + setParams,
  45 + innerParams,
  46 + data: brandData,
  47 + timer,
  48 + loading,
  49 + setLoading,
  50 + result,
  51 + setResult,
  52 + showModal,
  53 + setShowModal,
  54 + accountModal,
  55 + setAccountModal,
  56 + accountShopList,
  57 + setAccountShopList,
  58 + accountList,
  59 + edit,
  60 + setEdit,
  61 + editModal,
  62 + setEditModal
  63 + };
  64 +}
... ...
src/pages/order3/ManufactureCollection/components/EditModal.tsx
... ... @@ -209,7 +209,7 @@ export default function EditModal() {
209 209 ]}
210 210 >
211 211 <div id="modal">
212   - <Form form={form} labelAlign="left" labelCol={{ span: 5 }} wrapperCol={{ span: 19 }} onFinish={handleSave} preserve={false}>
  212 + <Form form={form} labelAlign="left" onFinish={handleSave} preserve={false}>
213 213 <Form.Item
214 214 name="optionalAuth"
215 215 label="授权车辆范围"
... ...