Commit 418006970227a1980c7f10c498ff8afe362002c4
1 parent
8477c4f6
feature: 新增门店机修组工时分成比例配置
Showing
5 changed files
with
172 additions
and
313 deletions
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={() => { | ... | ... |