Commit 6fa5a02d05b6c6f8d80c0d6d4868ab6c2329d301

Authored by 莫红玲
2 parents f644473e a3b4a681

Merge remote-tracking branch 'origin/bug_fix' into test

src/components/CarTableTreeAuth/SelectSpec.tsx 0 → 100644
  1 +import React, { useEffect, useState } from "react";
  2 +import {
  3 + Table,
  4 + Select,
  5 +} from "antd";
  6 +import {
  7 + CarOptionVo,
  8 +} from "@/pages/stock/Components/api";
  9 +import { ColumnsType } from 'antd/lib/table';
  10 +
  11 +interface Props {
  12 + onChange?: (pa: any) => void;
  13 + value?: any[];
  14 + disabled?: boolean;
  15 + specList?: CarOptionVo[]
  16 +}
  17 +
  18 +interface Item {
  19 + label: string;
  20 + value: number;
  21 + key?: number;
  22 +}
  23 +const { Column } = Table;
  24 +const { Option } = Select;
  25 +
  26 +const columns: ColumnsType<CarOptionVo> = [
  27 + {
  28 + title: '车型',
  29 + dataIndex: 'name',
  30 + },
  31 + {
  32 + title: '配置代码',
  33 + dataIndex: 'specConfigCodeList',
  34 + render: (t) => t.map((i: string, key: number) => (<div key={i}>{key + 1}.{i}</div>))
  35 + },
  36 +];
  37 +export default function index({ onChange: onSelected, value, disabled, specList = [] }: Props) {
  38 + const rowSelection = {
  39 + onChange: (selectedRowKeys: React.Key[], selectedRows: CarOptionVo[]) => {
  40 + onSelected && onSelected(selectedRows);
  41 + },
  42 + };
  43 +
  44 + return (
  45 + <>
  46 + <Table
  47 + rowSelection={{
  48 + type: "checkbox",
  49 + selectedRowKeys: value && value.map(item => item.id),
  50 + ...rowSelection,
  51 + }}
  52 + pagination={false}
  53 + // loading={}
  54 + rowKey="id"
  55 + size="small"
  56 + scroll={{ y: 500 }}
  57 + columns={columns}
  58 + dataSource={specList}
  59 + />
  60 + </>
  61 + );
  62 +}
... ...
src/components/CarTableTreeAuth/entity.ts 0 → 100644
  1 +export interface CarAuthList {
  2 + authType: number;
  3 + brandId: number;
  4 + brandName: number;
  5 + children?: Array<SeriesAuth>
  6 +}
  7 +
  8 +interface SeriesAuth {
  9 + authType: number;
  10 + seriesId: number;
  11 + seriesName: string;
  12 + children?: Array<SpecAuth>
  13 +}
  14 +interface SpecAuth {
  15 + authType: number;
  16 + specId: number;
  17 + specName: string;
  18 +}
0 19 \ No newline at end of file
... ...
src/components/CarTableTreeAuth/index.tsx 0 → 100644
  1 +import React, { useEffect, useState } from "react";
  2 +import {
  3 + Button,
  4 + Card,
  5 + Table,
  6 + Modal,
  7 + Form,
  8 + Select,
  9 + Space,
  10 + Spin,
  11 + message,
  12 +} from "antd";
  13 +import {
  14 + CarOptionVo,
  15 + getOnsaleSeriesApi,
  16 + getOnsaleSpecApi,
  17 +} from "@/pages/stock/Components/api";
  18 +import { PlusOutlined } from '@ant-design/icons';
  19 +import { ColumnsType } from 'antd/lib/table';
  20 +import SelectSpec from './SelectSpec';
  21 +import { CarAuthList } from './entity';
  22 +import { getBrandFilterApi } from '@/common/api';
  23 +
  24 +interface Props {
  25 + onChange?: (pa: any) => void;
  26 + value?: CarAuthList[];
  27 + disabled?: boolean;
  28 + brandList?: CommonApi.OptionVO[];
  29 + /**品牌是否多选,默认true */
  30 + brandMultiple?: boolean;
  31 +}
  32 +
  33 +interface Item {
  34 + label: string;
  35 + value: number;
  36 + key?: number;
  37 +}
  38 +const { Column } = Table;
  39 +const { Option } = Select;
  40 +
  41 +export default function index({ onChange, value, disabled, brandMultiple = true, brandList }: Props) {
  42 + const [form] = Form.useForm();
  43 + // 控制Modal是否可见
  44 + const [visible, setVisible] = useState<boolean>(false);
  45 + // 控制是否可见车型下拉框
  46 + const [level, setLevel] = useState<number>(1);
  47 + //存储表格当前车系
  48 + const [currentItem, setCurrentItem] = useState<any>();
  49 + const [brandData, setBrandData] = useState<CommonApi.OptionVO[]>([]);
  50 + //存储Modal中可选的车系
  51 + const [series, setSeries] = useState<any>([]);
  52 + //存储接口返回的车型
  53 + const [specList, setSpecList] = useState<any>([]);
  54 + //存储Modal选择的品牌和车系作为表格的数据源
  55 + const [savaData, setSavadata] = useState<any>([]);
  56 + // 存储已经选中的车系
  57 + const [selectedSeries, setSelectedSeries] = useState<any[]>([]);
  58 + const [fetchLoading, setFetchLoading] = useState(false);
  59 +
  60 + useEffect(() => {
  61 + if (value && value.length) {
  62 + setSelectedSeries([...value]);
  63 + setSavadata([...value]);
  64 + }
  65 + }, [value]);
  66 +
  67 + useEffect(() => {
  68 + if (brandList && Array.isArray(brandList)) {
  69 + setBrandData(brandList || []);
  70 + }
  71 + }, [brandList]);
  72 +
  73 + function getBrands() {
  74 + if (brandData.length) {
  75 + return;
  76 + }
  77 + setFetchLoading(true);
  78 + getBrandFilterApi().then(res => {
  79 + setBrandData(res.data || []);
  80 + setFetchLoading(false);
  81 + }).catch(err => {
  82 + message.error(err.message);
  83 + setFetchLoading(false);
  84 + });
  85 + }
  86 +
  87 + const _onOk = () => {
  88 + form
  89 + .validateFields()
  90 + .then((fileds) => {
  91 + if (level === 2) {
  92 + const { series } = fileds;
  93 + const _series = series.map((item: Item) => ({
  94 + seriesId: item.value,
  95 + seriesName: item.label,
  96 + authType: 1,
  97 + }));
  98 +
  99 + const currentSeries = (savaData.filter((i: any) => i.brandId === currentItem.brandId)[0] || {}).children || [];
  100 + /**判断已有数据保留 */
  101 + if (currentSeries.length) {
  102 + const selectedIds = currentSeries.map(i => i.seriesId);
  103 + let newAuthSeries: any[] = [];
  104 + _series.forEach((item: any) => {
  105 + if (selectedIds.includes(item.seriesId)) {
  106 + newAuthSeries = newAuthSeries.concat(currentSeries.filter((list: any) => list.seriesId === item.seriesId));
  107 + } else {
  108 + newAuthSeries.push(item);
  109 + }
  110 + });
  111 + currentItem.children = newAuthSeries;
  112 + } else {
  113 + currentItem.children = _series;
  114 + }
  115 +
  116 + const tempData = savaData.map((item: any) => (item.brandId === currentItem.brandId ? { ...currentItem, authType: series.length ? 2 : 1 } : { ...item, authType: 1 }));
  117 + setSavadata([...tempData]);
  118 + onChange && onChange(tempData);
  119 + setVisible(false);
  120 + return;
  121 + }
  122 + if (level === 3) {
  123 + const { spec } = fileds;
  124 + const _spec = spec.map((item: any) => ({
  125 + specId: item.id,
  126 + specName: item.name,
  127 + }));
  128 + currentItem.children = _spec;
  129 + currentItem.authType = 2;
  130 + const tempData = savaData.map((item: any) => {
  131 + if (item.children) {
  132 + const newChildren = item.children.map((i: any) => (i.seriesId === currentItem.seriesId ? { ...currentItem, authType: spec.length ? 2 : 1 } : i));
  133 + item.children = newChildren;
  134 + }
  135 + return { ...item, authType: 1 };
  136 + });
  137 + setSavadata([...tempData]);
  138 + onChange && onChange(tempData);
  139 + setVisible(false);
  140 + return;
  141 + }
  142 + const { brand } = fileds;
  143 + const tempArray = (brandMultiple ? brand : [brand]).map((item: any) => ({
  144 + brandName: item.label,
  145 + brandId: item.value,
  146 + authType: 1, //1全部2部分
  147 + }));
  148 + const selectedBrands: number[] = savaData.map((i: any) => i.brandId);
  149 + let newAuthCar: any[] = [];
  150 + tempArray.forEach((item: any) => {
  151 + if (selectedBrands.includes(item.brandId)) {
  152 + newAuthCar = newAuthCar.concat(savaData.filter(list => list.brandId === item.brandId));
  153 + } else {
  154 + newAuthCar.push(item);
  155 + }
  156 + });
  157 + setSavadata(newAuthCar);
  158 + onChange && onChange(newAuthCar);
  159 + setVisible(false);
  160 + })
  161 + .catch((err) => console.log(err.message));
  162 + };
  163 +
  164 + // 选择部分车辆表格==》删除车系
  165 + const onDelete = (record: any) => {
  166 + const _savaData = savaData.filter(
  167 + (item: any) => item.seriesId != record.seriesId
  168 + );
  169 + const _selectedSeries = selectedSeries.filter(
  170 + (item) => item.seriesId != record.seriesId
  171 + );
  172 + setSavadata([..._savaData]);
  173 + onChange && onChange(_savaData);
  174 + setSelectedSeries([..._selectedSeries]);
  175 + };
  176 +
  177 + // 选择部分车辆表格==》编辑选择部分车系、车型
  178 + const onSelectSpec = async (record: any) => {
  179 + setCurrentItem(record);
  180 + setVisible(true);
  181 + if (record.brandId) {
  182 + setLevel(2);
  183 + setFetchLoading(true);
  184 + try {
  185 + const { data } = await getOnsaleSeriesApi(record.brandId);
  186 + const selectedSeries = savaData.filter((brand: any) => brand.brandId === record.brandId)[0].children;
  187 + selectedSeries && form.setFieldValue("series", selectedSeries.map(i => ({ value: i.seriesId, label: i.seriesName })));
  188 + setSeries(data);
  189 + setFetchLoading(false);
  190 + } catch (err: any) {
  191 + message.error(err.message);
  192 + setFetchLoading(false);
  193 + }
  194 + }
  195 + if (record.seriesId) {
  196 + setLevel(3);
  197 + setFetchLoading(true);
  198 + try {
  199 + const { data } = await getOnsaleSpecApi(record.seriesId);
  200 + record.children && form.setFieldValue("spec", record.children.map(i => ({ id: i.specId, name: i.specName })));
  201 + setSpecList(data);
  202 + setFetchLoading(false);
  203 + } catch (err: any) {
  204 + message.error(err.message);
  205 + setFetchLoading(false);
  206 + }
  207 + }
  208 + };
  209 +
  210 + return (
  211 + <>
  212 + <Card>
  213 + {!disabled && (
  214 + <div style={{ display: "flex", justifyContent: "flex-end" }}>
  215 + <Button
  216 + type="link"
  217 + disabled={disabled}
  218 + icon={<PlusOutlined />}
  219 + onClick={() => {
  220 + getBrands();
  221 + setVisible(true);
  222 + setLevel(1);
  223 + savaData.length && form.setFieldValue("brand", savaData.map((i: any) => ({ value: i.brandId, label: i.brandName })));
  224 + // form.resetFields();
  225 + }}
  226 + >
  227 + 新增
  228 + </Button>
  229 + </div>
  230 + )}
  231 +
  232 + <Table
  233 + dataSource={value}
  234 + rowKey={(record) => String(record.brandId || record.seriesId || record.specId)}
  235 + >
  236 + <Column
  237 + title="品牌"
  238 + dataIndex="brandName"
  239 + key="brandId"
  240 + />
  241 + <Column
  242 + title="车系"
  243 + dataIndex="seriesName"
  244 + key="seriesId"
  245 + render={(text, record: any) => {
  246 + return (text || record.specName || (record.children && record.children.length !== 0) ? (
  247 + <span>
  248 + {text}
  249 + </span>
  250 + ) : (
  251 + <span style={{ color: "#999" }}>全部车系</span>
  252 + ));
  253 + }}
  254 + />
  255 + <Column
  256 + title="车型"
  257 + dataIndex="specName"
  258 + key="specId"
  259 + render={(text, record: any) => {
  260 + return (text || (record.children && record.children.length !== 0) ? (
  261 + <span>
  262 + {text}
  263 + </span>
  264 + ) : (
  265 + <span style={{ color: "#999" }}>全部车型</span>
  266 + ));
  267 + }}
  268 + />
  269 + {!disabled && (
  270 + <Column
  271 + title="操作"
  272 + render={(_, record: any, index) => {
  273 + return (
  274 + <Space>
  275 + <Button
  276 + type={record.brandId ? "primary" : "link"}
  277 + style={{ padding: 2 }}
  278 + onClick={() => onSelectSpec(record)}
  279 + disabled={disabled}
  280 + >
  281 + {record.brandId ? "编辑车系" : (record.seriesId ? "编辑车型" : "")}
  282 + </Button>
  283 + {/* <Popconfirm
  284 + title="确定删除?"
  285 + okText="确定"
  286 + style={{ padding: 0 }}
  287 + cancelText="取消"
  288 + onConfirm={() => onDelete(record)}
  289 + >
  290 + <Button type="link" danger disabled={disabled}>
  291 + 删除
  292 + </Button>
  293 + </Popconfirm> */}
  294 + </Space>
  295 + );
  296 + }}
  297 + />
  298 + )}
  299 + </Table>
  300 + </Card>
  301 +
  302 + {/* 选择品牌和车系 */}
  303 + <Modal
  304 + title="选择车辆信息"
  305 + visible={visible}
  306 + onOk={() => form.submit()}
  307 + onCancel={() => {
  308 + setVisible(false);
  309 + }}
  310 + maskClosable={false}
  311 + afterClose={() => {
  312 + form.resetFields();
  313 + setCurrentItem({});
  314 + }}
  315 + >
  316 + <Spin spinning={fetchLoading}>
  317 + <Form onFinish={_onOk} form={form}>
  318 + {level === 1 ? (
  319 + <Form.Item
  320 + label="品牌"
  321 + name="brand"
  322 + >
  323 + <Select
  324 + labelInValue
  325 + mode={brandMultiple ? "multiple" : undefined}
  326 + placeholder="选择品牌"
  327 + >
  328 + {brandData.map((item) => (
  329 + <Option value={item.id} key={item.id}>
  330 + {item.name}
  331 + </Option>
  332 + ))}
  333 + </Select>
  334 + </Form.Item>
  335 + ) : null}
  336 +
  337 + {/* 车系 */}
  338 + {level === 2 && (
  339 + <Form.Item
  340 + label="车系"
  341 + name="series"
  342 + // rules={[{ required: true, message: "请选择车系" }]}
  343 + >
  344 + <Select labelInValue mode="multiple" placeholder="选择车系" allowClear>
  345 + {series.map((item: any) => (
  346 + <Option value={item.id} key={item.id}>
  347 + {item.name}
  348 + </Option>
  349 + ))}
  350 + </Select>
  351 + </Form.Item>
  352 + )}
  353 + {level === 3 ? (
  354 + <Form.Item
  355 + label=""
  356 + name="spec"
  357 + >
  358 + {/* <Select labelInValue mode="multiple" placeholder="选择车型" allowClear>
  359 + {specList.map((item: any) => (
  360 + <Option value={item.id} key={item.id}>
  361 + {item.name}
  362 + </Option>
  363 + ))}
  364 + </Select> */}
  365 + <SelectSpec specList={specList} />
  366 + </Form.Item>
  367 + ) : null}
  368 + </Form>
  369 + </Spin>
  370 + </Modal>
  371 + </>
  372 + );
  373 +}
... ...
src/pages/coupon/CouponConfig/components/UsesceneItems/PreDecoration/Components/WorkItemLimit.tsx
... ... @@ -6,9 +6,9 @@ import * as API from &#39;../api&#39;;
6 6 import * as IF from '../interface';
7 7 import usePagination from '@/hooks/usePagination';
8 8 import { deRepetition } from '@/components/Condition/SeriesSelector/components/utils';
9   -import useDebounce from '@/hooks/useDebounce';
10 9 import useInitial from '@/hooks/useInitail';
11   -import { getBrandFilterApi } from '@/common/api';
  10 +import { getBrandFilterApi, getSeriesApi } from '@/common/api';
  11 +import { debounce } from 'lodash';
12 12  
13 13 const st = require('@/pages/coupon/CouponConfig/style.less');
14 14  
... ... @@ -30,19 +30,23 @@ function PartItem(props: Props, ref?: Ref&lt;any&gt;) {
30 30 const params = useScene === 1 ? { workTypes: "1,2,5,9" } : { workType: useScene };
31 31 const { loading, setLoading, list, setParams, paginationConfig, innerParams } = usePagination(API.fetchListApi, params);
32 32 const { data: brandList } = useInitial(getBrandFilterApi, [], {});
  33 + const [seriesList, setSeriesList] = useState<CommonApi.OptionVO[]>([]);
33 34 const [visible, setVisible] = useState(false);
34 35 const [listData, setListData] = useState<IF.ListVO[]>([]);
35   - const [keyWords, setKeyWords] = useState<string>('');
36   - const keyWordsTerm = useDebounce<string>(keyWords, 800);
37 36  
38 37 useEffect(() => {
39 38 value && setListData(value.map((v: any) => ({ itemCode: v.applyValue, itemName: v.applyName })));
40 39 }, []);
41 40  
42   - useEffect(() => {
43   - setParams({ keywords: keyWordsTerm || undefined, current: 1 }, true);
44   - }, [keyWordsTerm]);
  41 + function getSeries(brandId: number) {
  42 + getSeriesApi(brandId).then((res) => {
  43 + setSeriesList(res.data || []);
  44 + }).catch((err) => {
  45 + message.error(err.message);
  46 + });
  47 + }
45 48  
  49 + const onFilter = debounce((params) => setParams({ ...innerParams, ...params }, true), 600);
46 50 function selectedItems(value: any[]) {
47 51 const partIds = value.length && value.map((i: any) => i.applyValue).toString();
48 52 API.fetItemsApi({ partIds }).then(res => {
... ... @@ -93,7 +97,7 @@ function PartItem(props: Props, ref?: Ref&lt;any&gt;) {
93 97 width="20%"
94 98 render={(text, record: IF.ListVO) => {
95 99 return (
96   - <a onClick={() => remove(record.itemCode)}>删除</a>
  100 + <a onClick={() => remove(record.itemCode!)}>删除</a>
97 101 );
98 102 }}
99 103 />
... ... @@ -120,16 +124,38 @@ function PartItem(props: Props, ref?: Ref&lt;any&gt;) {
120 124 showSearch
121 125 allowClear
122 126 placeholder="搜索品牌"
123   - style={{ width: 200, marginRight: 15 }}
124   - onChange={(value) => setParams({ ...innerParams, brandId: value }, true)}
  127 + style={{ width: 200, margin: 5 }}
  128 + onChange={(value) => {
  129 + setParams({ ...innerParams, brandId: value }, true);
  130 + getSeries(value);
  131 + }}
125 132 >
126 133 {brandList.map((item) => <Select.Option key={item.id} value={item.id}>{item.name}</Select.Option>)}
127 134 </Select>,
  135 + <Select
  136 + optionFilterProp="children"
  137 + mode="multiple"
  138 + showSearch
  139 + allowClear
  140 + placeholder="搜索车系"
  141 + style={{ width: 200, margin: 5 }}
  142 + onChange={(v) => { console.log(v); setParams({ ...innerParams, seriesIds: v.join(',') }, true); }}
  143 + >
  144 + {seriesList.map((item) => <Select.Option key={item.id} value={item.id}>{item.name}</Select.Option>)}
  145 + </Select>,
128 146 <Search
129 147 allowClear
130   - value={keyWords}
  148 + value={innerParams.keywords}
  149 + style={{ margin: 5 }}
131 150 placeholder="搜索作业名称/代码"
132   - onChange={(e) => setKeyWords(e.target.value)}
  151 + onChange={(e) => onFilter({ keywords: e.target.value })}
  152 + />,
  153 + <Search
  154 + allowClear
  155 + value={innerParams.specCode}
  156 + style={{ margin: 5 }}
  157 + placeholder="搜索整车代码"
  158 + onChange={(e) => onFilter({ specCode: e.target.value })}
133 159 />
134 160 ]}
135 161 />
... ...
src/pages/coupon/CouponConfig/components/UsesceneItems/PreDecoration/interface.d.ts
... ... @@ -3,7 +3,8 @@ export interface QueryList {
3 3 workType?: number, //作业项类型 1:机修 2:电器 3:钣喷 4:装潢 5:基础保养 6:贴膜
4 4 workTypes?: string, //多个作业项组合参数
5 5 keywords?: string, //关键词
6   - specGroupCode?: string, //车型分组代码
  6 + seriesIds?:string, //车系id集合,查询多个车系
  7 + specCode?: string, //整车代码
7 8 current?: number,
8 9 pageSize?: number,
9 10 seriesId?: number,
... ... @@ -48,51 +49,51 @@ export interface PartListVO {
48 49  
49 50 /**查询保养套餐卡列表通用接口 */
50 51 export interface MpList {
51   - id: number, //null
52   - maintainId: number, //套餐卡配置ID
53   - name: string, //套餐卡名称
54   - remark: string, // 备注
55   - times: number, // 套餐次数
56   - validity: number, // 有效期(年)
57   - mainItemCode: string, //机油项编码
58   - mainItemName: string, // 机油项名称
59   - groupId: number, //集团id
60   - brandName: string, //品牌名称
61   - seriesName: string, //车系名称
62   - status: number, //状态 1待发布 2已发布 3已下架
  52 + id: number, //null
  53 + maintainId: number, //套餐卡配置ID
  54 + name: string, //套餐卡名称
  55 + remark: string, // 备注
  56 + times: number, // 套餐次数
  57 + validity: number, // 有效期(年)
  58 + mainItemCode: string, //机油项编码
  59 + mainItemName: string, // 机油项名称
  60 + groupId: number, //集团id
  61 + brandName: string, //品牌名称
  62 + seriesName: string, //车系名称
  63 + status: number, //状态 1待发布 2已发布 3已下架
63 64 }
64 65  
65 66 /**保养套餐卡查询条件 */
66 67 export interface Params {
67   - mpName?: string, // 套餐名称
68   - startDate?: number, // 创建日期开始
69   - endDate?: number, // 创建日期结束
  68 + mpName?: string, // 套餐名称
  69 + startDate?: number, // 创建日期开始
  70 + endDate?: number, // 创建日期结束
70 71 brandId?: string, //品牌ID
71   - status?: number, // 状态 1:待发布 4:已发
  72 + status?: number, // 状态 1:待发布 4:已发
72 73 current?: number,
73 74 pageSize?: number,
74 75 }
75 76  
76 77 /**查询集团VIP卡列表 */
77 78 export interface VipList {
78   - id: number, //null
79   - warrantyId: number, // 质保卡id
80   - name: string, // 质保卡名称
81   - price: number, // 价格
82   - remark: string, //备注
83   - groupId: number, //集团id
84   - brandId: number, //品牌id
85   - brandName: string, //品牌名称
86   - seriesId: number, //车系id
87   - seriesName: string, // 车系名称
88   - status: number, //状态 1 待上架 2 已上架 2 已下架
  79 + id: number, //null
  80 + warrantyId: number, // 质保卡id
  81 + name: string, // 质保卡名称
  82 + price: number, // 价格
  83 + remark: string, //备注
  84 + groupId: number, //集团id
  85 + brandId: number, //品牌id
  86 + brandName: string, //品牌名称
  87 + seriesId: number, //车系id
  88 + seriesName: string, // 车系名称
  89 + status: number, //状态 1 待上架 2 已上架 2 已下架
89 90 }
90 91  
91 92 /**vip卡查询条件 */
92 93 export interface VipParams {
93   - brandId?: number, // 品牌ID
94   - status?: number, // 状态 1:待发布 4:已发
  94 + brandId?: number, // 品牌ID
  95 + status?: number, // 状态 1:待发布 4:已发
95 96 current?: number,
96 97 pageSize?: number,
97   - name?: number, //质保卡名称
  98 + name?: number, //质保卡名称
98 99 }
... ...