Commit 418006970227a1980c7f10c498ff8afe362002c4

Authored by 杜志良
1 parent 8477c4f6

feature: 新增门店机修组工时分成比例配置

src/pages/cas/afterSaleConfiguration/manhoursProportionConfig/api.ts
... ... @@ -29,11 +29,11 @@ export interface ListResult {
29 29 }
30 30  
31 31 // 机修组员工信息
32   -interface Staff {
  32 +export interface Staff {
33 33 id?: string; // 配置id
34 34 staffId: string; // 组员id
35 35 staffName: string; // 组员名称
36   - manHoursProp?: string; // 工时分成占比(以小数表示,如0.5表示50%,全部组员相加为1)
  36 + manHoursProp: number; // 工时分成占比(以小数表示,如0.5表示50%,全部组员相加为1)
37 37 }
38 38  
39 39 /** 工时分成配置列表 */
... ... @@ -41,77 +41,18 @@ export function getListApi(params: ListParam): PromisePageResp<ListResult[]> {
41 41 return request.get(`${CAS_HOST}/erp/team/setting/list`, { params });
42 42 }
43 43  
44   -/** 机修施工组 */
45   -export interface RepairGroup {
46   - id: number; // 小组id
47   - name: string; // 小组名称
48   - groupId: number; // 集团id
49   - shopId: number; // 门店id
50   - shopName: string; // 门店名称
51   - roIeCode: string; // 角色编码
52   - roIeName: string; // 角色名称
53   - remark: string; // 备注
54   - staffList: Staff[]; // 组员
55   -}
56   -
57   -/** 门店机修施工组:列表 */
58   -export function getShopRepairGroupsApi(shopId: number): PromisePageResp<RepairGroup[]> {
59   - return request.get(`${CAS_HOST}/erp/team/setting/team/list`, { params: { shopId } });
60   -}
61   -
62 44 export interface SaveParams {
63 45 shopId: number; // 门店id
64 46 shopName: string; // 门店名称
65   - teamId: string; // 小组id
  47 + teamId: number; // 小组id
66 48 teamName: string; // 小组名称
67   - userId?: string; // 用户id
  49 + userId?: number; // 用户id
68 50 userName?: string; // 用户名称
69   - groupId?: string; // 集团id
  51 + groupId?: number; // 集团id
70 52 userInfoVOS?: Staff[]; // 各组员分成比例配置
71 53 }
72 54  
73   -/** 保存/修改配置 */
  55 +/** 修改配置 */
74 56 export function saveApi(params: SaveParams) {
75 57 return request.post(`${CAS_HOST}/erp/team/setting/save`, params);
76 58 }
77   -
78   -export interface DeleteParams {
79   - groupId?: string; // 集团id
80   - shopId?: number; // 门店id
81   - userId?: string; // 用户id
82   - userName?: string; // 用户名称
83   - current?: number;
84   - pageSize?: number;
85   - id: number; // 配置id
86   -}
87   -
88   -/** 删除配置 */
89   -export function deleteApi(params: DeleteParams) {
90   - return request.post(`${CAS_HOST}/erp/team/setting/delete`, params);
91   -}
92   -
93   -/** 删除 */
94   -export function endApi(id?: number): http.PromiseResp<string> {
95   - return request.post(`${CAS_HOST}/erp/help/part/setting/delete`, { id });
96   -}
97   -
98   -/**
99   - * 查询领辅料角色
100   - */
101   -
102   -export interface User {
103   - id: string;
104   - name: string;
105   -}
106   -
107   -export function getUserListApi(): http.PromiseResp<User[]> {
108   - return request.get(`${CAS_HOST}/erp/help/part/setting/role/list`);
109   -}
110   -
111   -/**
112   - * 查询辅料类型
113   - */
114   -
115   -export function getTypeListApi(): http.PromiseResp<User[]> {
116   - return request.get(`${CAS_HOST}/erp/help/part/setting/type/list`);
117   -}
... ...
src/pages/cas/afterSaleConfiguration/manhoursProportionConfig/components/ConfigModal.tsx 0 → 100644
  1 +import React, { useState, useEffect } from "react";
  2 +import "@ant-design/compatible/assets/index.css";
  3 +import { Alert, Modal, Form, message, InputNumber } from "antd";
  4 +import { ListResult, SaveParams, saveApi, Staff } from "../api";
  5 +
  6 +const FormItem = Form.Item;
  7 +const maxFormProps = {
  8 + labelCol: { span: 6 },
  9 + wrapperCol: { span: 14 },
  10 +};
  11 +
  12 +interface Props {
  13 + visible: boolean;
  14 + onCancel: () => any;
  15 + detail?: ListResult;
  16 +}
  17 +
  18 +export default function ConfigModal({ visible, onCancel, detail }: Props) {
  19 + const [form] = Form.useForm();
  20 + console.log("detail", detail);
  21 +
  22 + const [confirmLoading, setConfirmLoading] = useState<boolean>(false);
  23 +
  24 + useEffect(() => {
  25 + if (visible) {
  26 + form.resetFields();
  27 + // init form values
  28 + const initialValues: any = {};
  29 + detail?.userInfoVOS?.forEach((user: Staff) => {
  30 + initialValues[user.staffId] = user.manHoursProp;
  31 + });
  32 + form.setFieldsValue(initialValues);
  33 + }
  34 + }, [visible]);
  35 +
  36 + function handleSubmit(fieldsValue: any) {
  37 + const configItems = detail!.userInfoVOS.map((user: Staff) => ({
  38 + id: user.id,
  39 + staffId: user.staffId,
  40 + staffName: user.staffName,
  41 + manHoursProp: fieldsValue[user.staffId],
  42 + }));
  43 + const sum = configItems.reduce((acc: number, cur: Staff) => acc + cur.manHoursProp, 0);
  44 + if (sum !== 1) {
  45 + message.error("所有人员分成占比之和必须等于1");
  46 + return;
  47 + }
  48 +
  49 + const params: SaveParams = {
  50 + shopId: detail!.shopId,
  51 + shopName: detail!.shopName,
  52 + teamId: detail!.teamId,
  53 + teamName: detail!.teamName,
  54 + userInfoVOS: configItems,
  55 + };
  56 +
  57 + setConfirmLoading(true);
  58 + saveApi(params)
  59 + .then(() => {
  60 + message.success("配置成功!");
  61 + setConfirmLoading(false);
  62 + onCancel();
  63 + })
  64 + .catch((e) => {
  65 + setConfirmLoading(false);
  66 + message.error(e.message);
  67 + });
  68 + }
  69 +
  70 + return (
  71 + <Modal
  72 + title="配置机修组工时分成比例"
  73 + width={640}
  74 + open={visible}
  75 + confirmLoading={confirmLoading}
  76 + onOk={() => form.submit()}
  77 + onCancel={onCancel}
  78 + >
  79 + <Alert message="每人分成占比以小数表示,所有人员分成占比之和必须等于1" type="info" style={{ marginBottom: 20 }} />
  80 +
  81 + <Form form={form} onFinish={handleSubmit}>
  82 + {detail?.userInfoVOS?.map((user: Staff) => (
  83 + <FormItem
  84 + key={user.id}
  85 + name={user.staffId}
  86 + label={user.staffName}
  87 + rules={[{ required: true, message: "必填" }]}
  88 + {...maxFormProps}
  89 + >
  90 + <InputNumber style={{ width: 200 }} max={1} min={0} step={0.1} defaultValue={user.manHoursProp} />
  91 + </FormItem>
  92 + ))}
  93 + </Form>
  94 + </Modal>
  95 + );
  96 +}
... ...
src/pages/cas/afterSaleConfiguration/manhoursProportionConfig/components/Filter.tsx deleted
1   -import React, { useState, useEffect } from "react";
2   -import { Row, Select } from "antd";
3   -import { LabeledValue } from "antd/lib/select";
4   -
5   -const Option = Select.Option;
6   -
7   -const userData = [
8   - { value: 7, label: "工作车辆" },
9   - { value: 8, label: "员工车辆" },
10   - { value: 9, label: "大客户" },
11   -];
12   -
13   -export interface FilterPara {
14   - discountType?: number;
15   - discountTypeName?: string;
16   - [key: string]: any;
17   -}
18   -
19   -interface Props {
20   - onChange: Function;
21   - filterParam: FilterPara;
22   -}
23   -
24   -export default function Filter({ onChange, filterParam}: Props) {
25   - const [type, setType] = useState<LabeledValue>();
26   -
27   - useEffect(() => {
28   - if (filterParam) {
29   - filterParam.discountType && filterParam.discountTypeName
30   - ? setType({
31   - key: `${filterParam.discountType}`,
32   - value: filterParam.discountType,
33   - label: filterParam.discountTypeName,
34   - })
35   - : null;
36   - }
37   - }, [filterParam]);
38   -
39   - function onTypeChange(value: LabeledValue) {
40   - const _value = value || ({} as LabeledValue);
41   - const { key, label } = _value;
42   - onChange &&
43   - onChange(
44   - { discountType: key || undefined, current: 1, discountTypeName: label },
45   - true
46   - );
47   - }
48   -
49   - return (
50   - <Row style={{ width: "100%", alignItems: "center" }}>
51   - <span>客户类型:</span>
52   - <Select
53   - allowClear={false}
54   - placeholder="请选择"
55   - labelInValue
56   - value={type}
57   - onChange={onTypeChange}
58   - style={{ width: 150, marginLeft: 10 }}
59   - >
60   - {userData.map((r) => (
61   - <Option key={r.value} value={r.value}>
62   - {r.label || ""}
63   - </Option>
64   - ))}
65   - </Select>
66   - </Row>
67   - );
68   -}
src/pages/cas/afterSaleConfiguration/manhoursProportionConfig/components/Modal.tsx deleted
1   -import React, { useState, useEffect } from "react";
2   -import "@ant-design/compatible/assets/index.css";
3   -import { Select, Modal, Form, message } from "antd";
4   -import * as api from "../api";
5   -import { ListResult, SaveParams, saveApi, getShopRepairGroupsApi, ShopGroup } from "../api";
6   -import useInitial from "@/hooks/useInitail";
7   -
8   -const FormItem = Form.Item;
9   -const { Option } = Select;
10   -const maxFormProps = {
11   - labelCol: { span: 5 },
12   - wrapperCol: { span: 15 },
13   -};
14   -
15   -interface Props {
16   - visible: boolean;
17   - onCancel: () => any;
18   - shopId?: number;
19   - detail?: ListResult;
20   -}
21   -
22   -export default function SaveModal({ visible, onCancel, shopId, detail }: Props) {
23   - const isNew = !detail.id;
24   -
25   - const [form] = Form.useForm();
26   -
27   - const [confirmLoading, setConfirmLoading] = useState<boolean>(false);
28   - const [userData, setUserData] = useState<api.User[]>([]);
29   -
30   - const { data: userList } = useInitial(getShopRepairGroupsApi, [], { shopId });
31   -
32   - useEffect(() => {
33   - if (detail.id) {
34   - console.log(detail);
35   - // form.setFieldsValue({ ..._item });
36   - } else {
37   - form.resetFields();
38   - }
39   - }, [visible]);
40   -
41   - function save(fieldsValue: any) {
42   - console.log(fieldsValue);
43   - setConfirmLoading(true);
44   - const datas = {
45   - id: detail.id || undefined,
46   - roleCode: fieldsValue.roleCode.value,
47   - partTypes: (fieldsValue.partTypes || []).map((e: any) => e.value),
48   - };
49   - api
50   - .saveApi(datas)
51   - .then(() => {
52   - message.success("操作成功!");
53   - setConfirmLoading(false);
54   - onCancel();
55   - })
56   - .catch((e) => {
57   - setConfirmLoading(false);
58   - message.error(e.message);
59   - });
60   - }
61   -
62   - return (
63   - <Modal
64   - title={`${isNew ? "新增" : "编辑"}工时分成比例`}
65   - width={600}
66   - open={visible}
67   - confirmLoading={confirmLoading}
68   - onOk={() => form.submit()}
69   - onCancel={onCancel}
70   - >
71   - <Form form={form} onFinish={save}>
72   - <FormItem name="roleCode" label="角色" rules={[{ required: true, message: "必填" }]} {...maxFormProps}>
73   - <Select labelInValue placeholder="请选择角色">
74   - {userData.map((r) => (
75   - <Select.Option key={r.id} value={r.id}>
76   - {r.name || "--"}
77   - </Select.Option>
78   - ))}
79   - </Select>
80   - </FormItem>
81   - <FormItem
82   - label="可领辅料类型"
83   - name="partTypes"
84   - rules={[{ required: true, message: "该选项为必选项" }]}
85   - {...maxFormProps}
86   - >
87   - <Select placeholder="请选择辅料类型" mode="multiple" labelInValue>
88   - {[].map((r) => (
89   - <Select.Option key={r.id} value={r.id}>
90   - {r.name || "--"}
91   - </Select.Option>
92   - ))}
93   - </Select>
94   - </FormItem>
95   - </Form>
96   - </Modal>
97   - );
98   -}
src/pages/cas/afterSaleConfiguration/manhoursProportionConfig/index.tsx
1   -import React, { useState } from "react";
2   -import { Card, ConfigProvider, Table, Button, Popconfirm, message } from "antd";
  1 +import React, { useState, useEffect } from "react";
  2 +import { Card, Table, Button, Tag, message, Select, Input } from "antd";
3 3 import { PageHeaderWrapper } from "@ant-design/pro-layout";
4   -import zhCN from "antd/lib/locale-provider/zh_CN";
5 4  
6 5 import usePagination from "@/hooks/usePagination";
7   -// import useInitial from "@/hooks/useInitail";
8   -import { getListApi, ListResult, deleteApi } from "./api";
  6 +import { getShopApi } from "@/common/api";
  7 +import { getListApi, ListResult, Staff } from "./api";
9 8  
10   -// import Filter from "./components/Filter";
11   -import CreateModal from "./components/Modal";
  9 +import ConfigModal from "./components/ConfigModal";
12 10  
  11 +const { Option } = Select;
13 12 const { Column } = Table;
14 13  
15 14 export default function ManhoursProportionConfig() {
16   - // const { data, loading, setLoading } = useInitial(getListApi, [], {});
17   - const { loading, setLoading, list, paginationConfig } = usePagination(getListApi, {});
  15 + const { loading, setLoading, list, paginationConfig, setParams } = usePagination(getListApi, {});
18 16  
  17 + const [shops, setShops] = useState<CommonApi.OptionVO[]>([]);
19 18 const [visible, setVisible] = useState(false);
20 19 const [detail, setDetail] = useState<any>({});
21 20  
22   - function handleDelete(row: ListResult) {
23   - const deleteParams = {
24   - id: row.teamId,
25   - };
26   - deleteApi(deleteParams)
  21 + useEffect(() => {
  22 + getShopApi({})
27 23 .then((res) => {
28   - message.success("删除成功");
29   - setLoading(true);
  24 + const { data = [] } = res;
  25 + setShops(data);
30 26 })
31 27 .catch((e) => {
32   - message.error(`删除失败:${e.message}`);
  28 + message.error(e.message);
33 29 });
34   - }
  30 + }, []);
35 31  
36 32 return (
37 33 <PageHeaderWrapper title="机修组工时分成比例配置">
38 34 <Card>
39   - <div
40   - style={{
41   - display: "flex",
42   - justifyContent: "flex-end",
43   - marginBottom: "20px",
44   - }}
45   - >
46   - {/* <Filter
47   - filterParam={innerParams}
48   - onChange={setParams}
49   - /> */}
50   - <Button type="primary" onClick={() => setVisible(true)}>
51   - 新增
52   - </Button>
53   - </div>
54   -
55   - <ConfigProvider locale={zhCN}>
56   - <Table
57   - dataSource={list}
58   - loading={loading}
59   - pagination={paginationConfig}
60   - rowKey="teamId"
  35 + <div style={{ display: "flex", marginBottom: "20px" }}>
  36 + <Select
  37 + style={{ width: 240 }}
  38 + showSearch
  39 + allowClear
  40 + optionFilterProp="children"
  41 + placeholder="请选择门店搜索"
  42 + onChange={(value) => setParams({ shopId: value }, true)}
61 43 >
62   - <Column
63   - title="门店"
64   - dataIndex="shopName"
65   - width={200}
66   - />
67   - <Column
68   - title="机修组"
69   - dataIndex="teamName"
70   - width={200}
71   - />
72   - <Column
73   - title="分成比例"
74   - dataIndex="shopName"
75   - />
  44 + {shops.map((shop) => (
  45 + <Option value={shop.id} key={shop.id}>
  46 + {shop.name}
  47 + </Option>
  48 + ))}
  49 + </Select>
  50 +
  51 + <Input.Search
  52 + style={{ width: 240, marginLeft: 20 }}
  53 + placeholder="请输入机修组名称搜索"
  54 + allowClear
  55 + onSearch={(value) => setParams({ keywords: value }, true)}
  56 + />
  57 + </div>
76 58  
77   - <Column
78   - title="操作"
79   - dataIndex="teamId"
80   - align="center"
81   - width={150}
82   - render={(val, record: ListResult) => (
83   - <>
84   - <Button
85   - size="small"
86   - type="primary"
87   - onClick={() => {
88   - setDetail(record);
89   - setVisible(true);
90   - }}
91   - >
92   - 编辑
93   - </Button>
94   - <Popconfirm title="确认删除" onConfirm={() => handleDelete(row)}>
95   - <Button danger type="link" size="small" style={{ marginLeft: 10 }}>
96   - 删除
97   - </Button>
98   - </Popconfirm>
99   - </>
100   - )}
101   - />
102   - </Table>
103   - </ConfigProvider>
  59 + <Table dataSource={list} loading={loading} pagination={paginationConfig} rowKey="teamId">
  60 + <Column title="门店" dataIndex="shopName" width={200} />
  61 + <Column title="机修组" dataIndex="teamName" width={200} />
  62 + <Column
  63 + title="分成比例"
  64 + dataIndex="userInfoVOS"
  65 + render={(users: Staff[]) => {
  66 + return users.map((user) => (
  67 + <Tag color="blue" key={user.id} style={{ marginRight: 10 }}>
  68 + {user.staffName}:{user.manHoursProp}
  69 + </Tag>
  70 + ));
  71 + }}
  72 + />
  73 + <Column
  74 + title="操作"
  75 + dataIndex="teamId"
  76 + align="center"
  77 + width={150}
  78 + render={(val, record: ListResult) => (
  79 + <Button
  80 + size="small"
  81 + type="primary"
  82 + onClick={() => {
  83 + setDetail(record);
  84 + setVisible(true);
  85 + }}
  86 + >
  87 + 编辑
  88 + </Button>
  89 + )}
  90 + />
  91 + </Table>
104 92 </Card>
105 93  
106   - <CreateModal
  94 + <ConfigModal
107 95 visible={visible}
108 96 detail={detail}
109 97 onCancel={() => {
... ...