Commit b77a515f09fbb0b729b58a4be7b1928301f831d8

Authored by 张志伟
2 parents 346196a5 6e9d80c3

Merge branch 'd-cas' into 'master'

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



See merge request !266
config/routers/cas.ts
... ... @@ -246,6 +246,10 @@ export default [
246 246 path: '/cas/cassetting/manHoursDiscountConfig/create/:id/:brandId?', //工时减免规则配置
247 247 component: './cas/afterSaleConfiguration/manHoursDiscountConfig/subpages/ConfigCreate'
248 248 },
  249 + {
  250 + path: '/cas/cassetting/manhoursProportionConfig', // 机修组工时分成比例配置
  251 + component: './cas/afterSaleConfiguration/manhoursProportionConfig'
  252 + },
249 253 // {
250 254 // path: '/cas/part/plantype', // 备件计划类型
251 255 // component: './cas/spareParts/PlanType'
... ...
src/pages/cas/afterSaleConfiguration/manhoursProportionConfig/api.ts 0 → 100644
  1 +import { http } from "@/typing/http";
  2 +import request from "@/utils/request";
  3 +import { CAS_HOST } from "@/utils/host";
  4 +
  5 +type PromisePageResp<T> = http.PromisePageResp<T>;
  6 +
  7 +// 列表参数
  8 +interface ListParam {
  9 + current?: number;
  10 + pageSize?: number;
  11 + keyword?: string;
  12 + shopId?: number;
  13 + groupId?: number;
  14 + userId?: number;
  15 + userName?: string;
  16 + teamId?: number;
  17 +}
  18 +
  19 +// 列表结果
  20 +export interface ListResult {
  21 + groupId: number; // 集团id
  22 + shopId: number; // 门店id
  23 + shopName: string; // 门店名称
  24 + roleCode: string; // 角色编码
  25 + roleName: string; // 角色名称
  26 + teamId: number; // 组id
  27 + teamName: string; // 组名
  28 + userInfoVOS: Staff[]; // 组员工信息
  29 +}
  30 +
  31 +// 机修组员工信息
  32 +export interface Staff {
  33 + id?: string; // 配置id
  34 + staffId: string; // 组员id
  35 + staffName: string; // 组员名称
  36 + manHoursProp: number; // 工时分成占比(以小数表示,如0.5表示50%,全部组员相加为1)
  37 +}
  38 +
  39 +/** 工时分成配置列表 */
  40 +export function getListApi(params: ListParam): PromisePageResp<ListResult[]> {
  41 + return request.get(`${CAS_HOST}/erp/team/setting/list`, { params });
  42 +}
  43 +
  44 +export interface SaveParams {
  45 + shopId: number; // 门店id
  46 + shopName: string; // 门店名称
  47 + teamId: number; // 小组id
  48 + teamName: string; // 小组名称
  49 + userId?: number; // 用户id
  50 + userName?: string; // 用户名称
  51 + groupId?: number; // 集团id
  52 + userInfoVOS?: Staff[]; // 各组员分成比例配置
  53 +}
  54 +
  55 +/** 修改配置 */
  56 +export function saveApi(params: SaveParams) {
  57 + return request.post(`${CAS_HOST}/erp/team/setting/save`, params);
  58 +}
... ...
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/index.tsx 0 → 100644
  1 +import React, { useState, useEffect } from "react";
  2 +import { Card, Table, Button, Tag, message, Select, Input } from "antd";
  3 +import { PageHeaderWrapper } from "@ant-design/pro-layout";
  4 +
  5 +import usePagination from "@/hooks/usePagination";
  6 +import { getShopApi } from "@/common/api";
  7 +import { getListApi, ListResult, Staff } from "./api";
  8 +
  9 +import ConfigModal from "./components/ConfigModal";
  10 +
  11 +const { Option } = Select;
  12 +const { Column } = Table;
  13 +
  14 +export default function ManhoursProportionConfig() {
  15 + const { loading, setLoading, list, paginationConfig, setParams } = usePagination(getListApi, {});
  16 +
  17 + const [shops, setShops] = useState<CommonApi.OptionVO[]>([]);
  18 + const [visible, setVisible] = useState(false);
  19 + const [detail, setDetail] = useState<any>({});
  20 +
  21 + useEffect(() => {
  22 + getShopApi({})
  23 + .then((res) => {
  24 + const { data = [] } = res;
  25 + setShops(data);
  26 + })
  27 + .catch((e) => {
  28 + message.error(e.message);
  29 + });
  30 + }, []);
  31 +
  32 + return (
  33 + <PageHeaderWrapper title="机修组工时分成比例配置">
  34 + <Card>
  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)}
  43 + >
  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({ keyword: value }, true)}
  56 + />
  57 + </div>
  58 +
  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>
  92 + </Card>
  93 +
  94 + <ConfigModal
  95 + visible={visible}
  96 + detail={detail}
  97 + onCancel={() => {
  98 + setVisible(false);
  99 + setDetail({});
  100 + setLoading(true);
  101 + }}
  102 + />
  103 + </PageHeaderWrapper>
  104 + );
  105 +}
... ...