Commit 2829593b3f2bc1a07e973e9bd5d1640322f3ab2b

Authored by 曾柯
2 parents ff5026f5 c32e8db2

Merge remote-tracking branch 'origin/master' into kaiShopId

Showing 37 changed files with 1378 additions and 372 deletions
src/components/ApprovalProgress/api.ts
... ... @@ -16,6 +16,8 @@ export interface ApprovalProgressItemVO {
16 16 duration?: string; // 审批时长
17 17 opinion?: string; // 审批意见
18 18 expTime?: number;
  19 + efficientTime?: number;
  20 + createTime?: number;
19 21 roleNames: string[];
20 22 }
21 23  
... ... @@ -24,6 +26,7 @@ export enum ApprovalProgressStatus {
24 26 '审批通过',
25 27 '审批拒绝',
26 28 '审批超时',
  29 + '未开始' = 6,
27 30 }
28 31  
29 32 export enum ApprovalProgressStatusColor {
... ... @@ -31,6 +34,7 @@ export enum ApprovalProgressStatusColor {
31 34 'blue',
32 35 'red',
33 36 'orange',
  37 + '#DEDEDE' = 6,
34 38 }
35 39  
36 40 export enum ApprovalProgressDotColor {
... ... @@ -38,6 +42,7 @@ export enum ApprovalProgressDotColor {
38 42 '#4189FD',
39 43 '#F4333C',
40 44 '#FF921C',
  45 + '#DEDEDE' = 6,
41 46 }
42 47  
43 48 /**
... ...
src/components/ApprovalProgress/components/CountDown/index.tsx 0 → 100644
  1 +import React, { useState, useEffect, useRef } from 'react';
  2 +import { fix0 } from '../../util';
  3 +import classNames from 'classnames';
  4 +
  5 +export interface CountDownProps {
  6 + seconds: number; // 倒计时总秒数
  7 + size?: number;
  8 + color?: string;
  9 + chinesse?: boolean; // 是否中文格式
  10 + style?: any;
  11 + /** 当倒计时为0时的操作 */
  12 + onFinish?: () => void;
  13 + /** 只倒计时秒数 */
  14 + onlySecond?: boolean;
  15 + isDay?: boolean;
  16 + showMinute?: boolean;
  17 +}
  18 +
  19 +export default function CountDown({ seconds, size, color, chinesse, style = {}, onFinish, onlySecond, isDay = false, showMinute }: CountDownProps) {
  20 + const [text, setText] = useState<string>('--');
  21 + const [obj] = useState({ seconds });
  22 + const intervalId = useRef();
  23 +
  24 + useEffect(() => {
  25 + intervalId.current && clearInterval(intervalId.current);
  26 + obj.seconds = seconds - 1;
  27 + if (seconds > 0) {
  28 + handleText();
  29 + // @ts-ignore
  30 + intervalId.current = setInterval(() => {
  31 + handleText();
  32 + }, 1000);
  33 + }
  34 + return () => {
  35 + intervalId.current && clearInterval(intervalId.current);
  36 + };
  37 + }, [seconds]);
  38 +
  39 + function handleText() {
  40 + obj.seconds -= 1;
  41 + const seconds = obj.seconds;
  42 + if (seconds <= 0) {
  43 + intervalId.current && clearInterval(intervalId.current);
  44 + onFinish && onFinish();
  45 + }
  46 + let day = 0,
  47 + hour = 0,
  48 + minute = 0,
  49 + second = 0,
  50 + text = '';
  51 + if (seconds > 0) {
  52 + // 倒计时不计天
  53 + if (isDay) {
  54 + day = Math.floor(seconds / (60 * 60 * 24));
  55 + hour = Math.floor(seconds / (60 * 60)) - day * 24;
  56 + minute = Math.floor(seconds / 60) - day * 24 * 60 - hour * 60;
  57 + second = Math.floor(seconds) - day * 24 * 60 * 60 - hour * 60 * 60 - minute * 60;
  58 + } else {
  59 + hour = Math.floor(seconds / 3600);
  60 + minute = Math.floor(seconds / 60) - hour * 60;
  61 + second = Math.floor(seconds) - hour * 60 * 60 - minute * 60;
  62 + }
  63 + }
  64 + if (onlySecond) {
  65 + text = `${Math.floor(seconds)}${chinesse ? '秒' : 's'}`;
  66 + }
  67 + if (!onlySecond && day > 0) {
  68 + text = chinesse
  69 + ? `${fix0(day)}天${fix0(hour)}时${fix0(minute)}分${fix0(second)}秒`
  70 + : `${fix0(day)}d${fix0(hour)}:${fix0(minute)}:${fix0(second)}`;
  71 + }
  72 + if (!onlySecond && day <= 0) {
  73 + text = chinesse ? `${fix0(hour)}时${fix0(minute)}分${fix0(second)}秒` : `${fix0(hour)}:${fix0(minute)}:${fix0(second)}`;
  74 + }
  75 +
  76 + if (!onlySecond && day <= 0 && hour <= 0) {
  77 + text = chinesse ? `${fix0(minute)}分${fix0(second)}秒` : `${fix0(minute)}:${fix0(second)}`;
  78 + }
  79 +
  80 + if (showMinute && day > 0) {
  81 + text = chinesse ? `${fix0(day)}天${fix0(hour)}时${fix0(minute)}分` : `${fix0(day)}d${fix0(hour)}:${fix0(minute)}`;
  82 + }
  83 + if (showMinute && day <= 0) {
  84 + text = chinesse ? `${fix0(hour)}时${fix0(minute)}分${fix0(second)}秒` : `${fix0(hour)}:${fix0(minute)}:${fix0(second)}`;
  85 + }
  86 + if (showMinute && hour <= 0) {
  87 + text = chinesse ? `${fix0(minute)}分${fix0(second)}秒` : `${fix0(minute)}:${fix0(second)}`;
  88 + }
  89 + setText(text);
  90 + }
  91 +
  92 + return (
  93 + <span
  94 + className={classNames(style)}
  95 + style={{
  96 + fontSize: size ?? 10,
  97 + color: color ?? '#666',
  98 + }}
  99 + >
  100 + {text || ''}
  101 + </span>
  102 + );
  103 +}
... ...
src/components/ApprovalProgress/components/CountUp/index.tsx 0 → 100644
  1 +import React, { useState, useEffect, useRef } from 'react';
  2 +import { fix0 } from '../../util';
  3 +import classNames from 'classnames';
  4 +
  5 +export interface CountUpProps {
  6 + seconds: number; // 正计时秒数基数
  7 + size?: number; // 字体大小
  8 + color?: string; // 字体颜色
  9 + chinesse?: boolean; // 是否中文格式
  10 + style?: any;
  11 + /** 只正计时秒数 */
  12 + onlySecond?: boolean;
  13 + isDay?: boolean;
  14 + showMinute?: boolean;
  15 +}
  16 +
  17 +export default function CountUp({ seconds, size, color, chinesse, style, onlySecond, isDay = false, showMinute }: CountUpProps) {
  18 + const [text, setText] = useState<string>('--');
  19 + const [obj] = useState({ seconds });
  20 + const intervalId = useRef();
  21 +
  22 + useEffect(() => {
  23 + intervalId.current && clearInterval(intervalId.current);
  24 + obj.seconds = seconds + 1;
  25 + if (seconds > 0) {
  26 + handleText();
  27 + // @ts-ignore
  28 + intervalId.current = setInterval(() => {
  29 + handleText();
  30 + }, 1000);
  31 + }
  32 + return () => {
  33 + intervalId.current && clearInterval(intervalId.current);
  34 + };
  35 + }, [seconds]);
  36 +
  37 + function handleText() {
  38 + obj.seconds += 1;
  39 + const seconds = obj.seconds;
  40 +
  41 + let day = 0,
  42 + hour = 0,
  43 + minute = 0,
  44 + second = 0,
  45 + text = '';
  46 + if (seconds > 0) {
  47 + // 正计时不计天
  48 + if (isDay) {
  49 + day = Math.floor(seconds / (60 * 60 * 24));
  50 + hour = Math.floor(seconds / (60 * 60)) - day * 24;
  51 + minute = Math.floor(seconds / 60) - day * 24 * 60 - hour * 60;
  52 + second = Math.floor(seconds) - day * 24 * 60 * 60 - hour * 60 * 60 - minute * 60;
  53 + } else {
  54 + hour = Math.floor(seconds / 3600);
  55 + minute = Math.floor(seconds / 60) - hour * 60;
  56 + second = Math.floor(seconds) - hour * 60 * 60 - minute * 60;
  57 + }
  58 + }
  59 + if (onlySecond) {
  60 + text = `${Math.floor(seconds)}${chinesse ? '秒' : 's'}`;
  61 + }
  62 + if (!onlySecond && day > 0) {
  63 + text = chinesse
  64 + ? `${fix0(day)}天${fix0(hour)}时${fix0(minute)}分${fix0(second)}秒`
  65 + : `${fix0(day)}d${fix0(hour)}:${fix0(minute)}:${fix0(second)}`;
  66 + }
  67 + if (!onlySecond && day <= 0) {
  68 + text = chinesse ? `${fix0(hour)}时${fix0(minute)}分${fix0(second)}秒` : `${fix0(hour)}:${fix0(minute)}:${fix0(second)}`;
  69 + }
  70 +
  71 + if (!onlySecond && day <= 0 && hour <= 0) {
  72 + text = chinesse ? `${fix0(minute)}分${fix0(second)}秒` : `${fix0(minute)}:${fix0(second)}`;
  73 + }
  74 +
  75 + if (showMinute && day > 0) {
  76 + text = chinesse ? `${fix0(day)}天${fix0(hour)}时${fix0(minute)}分` : `${fix0(day)}d${fix0(hour)}:${fix0(minute)}`;
  77 + }
  78 + if (showMinute && day <= 0) {
  79 + text = chinesse ? `${fix0(hour)}时${fix0(minute)}分${fix0(second)}秒` : `${fix0(hour)}:${fix0(minute)}:${fix0(second)}`;
  80 + }
  81 + if (showMinute && hour <= 0) {
  82 + text = chinesse ? `${fix0(minute)}分${fix0(second)}秒` : `${fix0(minute)}:${fix0(second)}`;
  83 + }
  84 + setText(text);
  85 + }
  86 +
  87 + return (
  88 + <span
  89 + className={classNames(style)}
  90 + style={{
  91 + fontSize: size ?? 10,
  92 + color: color ?? '#666',
  93 + }}
  94 + >
  95 + {text || ''}
  96 + </span>
  97 + );
  98 +}
... ...
src/components/ApprovalProgress/components/TagPills/index.tsx 0 → 100644
  1 +import React from 'react';
  2 +import st from './style.less';
  3 +import { Row } from 'antd';
  4 +import classNames from 'classnames';
  5 +
  6 +interface Props {
  7 + style?: React.CSSProperties;
  8 + titleStyle?: React.CSSProperties;
  9 + contentStyle?: React.CSSProperties;
  10 + titleTextStyle?: React.CSSProperties;
  11 + contentTextStyle?: React.CSSProperties;
  12 + /** 类型 */
  13 + type?: 'default' | 'primary' | 'warn' | 'danger' | 'success';
  14 + /** 标题 */
  15 + title: string;
  16 + /** 内容 */
  17 + content: string;
  18 + /** 内容节点 */
  19 + children?: React.ReactNode;
  20 + onPress?: () => void;
  21 +}
  22 +
  23 +const colors = {
  24 + default: '#999',
  25 + primary: '#4189FD',
  26 + warn: '#FF921C',
  27 + danger: '#F4333C',
  28 + success: '#42E4A4',
  29 +};
  30 +
  31 +export default function TagPills({
  32 + style,
  33 + titleStyle,
  34 + titleTextStyle,
  35 + contentStyle,
  36 + contentTextStyle,
  37 + type = 'default',
  38 + title,
  39 + content,
  40 + children,
  41 + onPress,
  42 +}: Props) {
  43 + return (
  44 + <Row align="middle" justify="center" style={style} onClick={onPress}>
  45 + <div className={classNames(st.title, titleStyle)} style={{ backgroundColor: colors[type], borderColor: colors[type] }}>
  46 + <span className={classNames(st.titleText, titleTextStyle)}>{title}</span>
  47 + </div>
  48 + <div className={classNames(st.content, contentStyle)} style={{ borderColor: colors[type] }}>
  49 + {children ? (
  50 + children
  51 + ) : (
  52 + <span className={classNames(st.contentText, contentTextStyle)} style={{ color: colors[type] }}>
  53 + {content}
  54 + </span>
  55 + )}
  56 + </div>
  57 + </Row>
  58 + );
  59 +}
... ...
src/components/ApprovalProgress/components/TagPills/style.less 0 → 100644
  1 +.title {
  2 + align-items: center;
  3 + justify-content: center;
  4 + min-width: 35px;
  5 + height: 16px;
  6 + line-height: 12px;
  7 + padding: 0 5px;
  8 + background-color: #fff;
  9 + border-width: 0.5px;
  10 + border-style: solid;
  11 + border-color: #999;
  12 + border-right-width: 0px;
  13 + border-top-left-radius: 2px;
  14 + border-bottom-left-radius: 2px;
  15 +}
  16 +.titleText {
  17 + color: #fff;
  18 + font-weight: 500;
  19 + font-size: 10px;
  20 + text-align: center;
  21 +}
  22 +.content {
  23 + align-items: center;
  24 + justify-content: center;
  25 + min-width: 35px;
  26 + height: 16px;
  27 + line-height: 12px;
  28 + padding: 0 5px;
  29 + background-color: #fff;
  30 + border-width: 0.5px;
  31 + border-style: solid;
  32 + border-color: #999;
  33 + border-left-width: 0px;
  34 + border-top-right-radius: 2px;
  35 + border-bottom-right-radius: 2px;
  36 +}
  37 +.contentText {
  38 + color: #999;
  39 + font-weight: 500;
  40 + font-size: 10px;
  41 + text-align: center;
  42 +}
... ...
src/components/ApprovalProgress/components/TimeInfo/index.tsx 0 → 100644
  1 +import React, { useMemo } from 'react';
  2 +
  3 +import st from './style.less';
  4 +import { Row } from 'antd';
  5 +import classNames from 'classnames';
  6 +import { formatTime } from '../../util';
  7 +
  8 +interface PageProps {
  9 + style?: React.CSSProperties;
  10 + /** 是否有时效,没有时不展示时效值,默认展示 */
  11 + hasAging?: boolean;
  12 + /** 开始时间:ms */
  13 + startTime: number;
  14 + /** 结束时间:ms */
  15 + endTime: number;
  16 + /** 时效:需要用时 s */
  17 + requiredTime: number;
  18 + /** 实际用时:s */
  19 + usedTime: number;
  20 + /** 时效 label */
  21 + requiredLabel?: string;
  22 + /** 用时 label */
  23 + usedLabel?: string;
  24 +}
  25 +
  26 +/**
  27 + * 时效展示
  28 + * 时效:x分钟 用时:x分钟
  29 + * hasAging=false
  30 + * 时效:-- 用时:x分钟
  31 + */
  32 +export default function TimeInfo({
  33 + style = {},
  34 + hasAging = true,
  35 + startTime,
  36 + endTime,
  37 + requiredTime,
  38 + usedTime,
  39 + requiredLabel = '时效',
  40 + usedLabel = '用时',
  41 +}: PageProps) {
  42 + const time = useMemo(() => formatTime(requiredTime), [requiredTime]);
  43 + const timeUsed = useMemo(() => formatTime(usedTime), [usedTime]);
  44 +
  45 + return (
  46 + <Row style={style}>
  47 + {hasAging ? (
  48 + <span className={st.textGrayLightSm}>
  49 + {requiredLabel}:<span className={st.textWarnSm}>{time.value}</span>
  50 + {time.unit}
  51 + {!!time.remainValue && (
  52 + <>
  53 + <span className={st.textWarnSm}>{time.remainValue}</span>
  54 + {time.remainUnit}
  55 + </>
  56 + )}
  57 + </span>
  58 + ) : (
  59 + <span className={st.textGrayLightSm}>{requiredLabel}:--</span>
  60 + )}
  61 +
  62 + {timeUsed.value > 0 && (
  63 + <span className={classNames(st.textGrayLightSm, st.ml30)}>
  64 + {usedLabel}:<span className={st.textPrimarySm}>{timeUsed.value}</span>
  65 + {timeUsed.unit}
  66 + {!!timeUsed.remainValue && (
  67 + <>
  68 + <span className={st.textPrimarySm}>{timeUsed.remainValue}</span>
  69 + {timeUsed.remainUnit}
  70 + </>
  71 + )}
  72 + </span>
  73 + )}
  74 + </Row>
  75 + );
  76 +}
... ...
src/components/ApprovalProgress/components/TimeInfo/style.less 0 → 100644
  1 +.ml30 {
  2 + margin-left: 30px;
  3 +}
  4 +.textPrimarySm {
  5 + color: #4189fd;
  6 + font-weight: 500;
  7 + font-size: 12px;
  8 +}
  9 +.textWarnSm {
  10 + color: #ff921c;
  11 + font-weight: 500;
  12 + font-size: 12px;
  13 +}
  14 +.textGrayLightSm {
  15 + color: #999;
  16 + font-weight: 500;
  17 + font-size: 12px;
  18 +}
... ...
src/components/ApprovalProgress/components/TimerTag/index.tsx 0 → 100644
  1 +import React, { useMemo } from 'react';
  2 +import { calculateTime, formatTime } from '../../util';
  3 +import st from './style.less';
  4 +import TagPills from '../TagPills';
  5 +import CountUp from '../CountUp';
  6 +import type { CountUpProps } from '../CountUp';
  7 +import CountDown from '../CountDown';
  8 +import type { CountDownProps } from '../CountDown';
  9 +
  10 +interface PageProps {
  11 + style?: React.CSSProperties;
  12 + /** 开始时间:ms */
  13 + startTime: number;
  14 + /**
  15 + * 完成时间:ms
  16 + * 1.无结束时间,表示进行中
  17 + */
  18 + endTime?: number;
  19 + /**
  20 + * 预计完成时间: ms
  21 + * 1.无预计结束时间,不展示Tag
  22 + * 2.有预计结束时间,无endTime,标识进行中: 展示倒计时Tag
  23 + */
  24 + expectedTime?: number;
  25 + /** 已结束:是否展示已用时间 */
  26 + showUsedTime?: boolean;
  27 + /** 倒计时组件 props, 参考CountDown组件props */
  28 + countDownProps?: Partial<CountDownProps>;
  29 + countUpProps?: Partial<CountUpProps>;
  30 + onPress?: () => void;
  31 +}
  32 +
  33 +/**
  34 + * 时效计时器
  35 + * 1. 未超时,已结束,showUsedTime:true, 展示已用时,false 不展示
  36 + * 2. 未超时,进行中,展示倒计时Tag
  37 + * 3. 超时,展示已超时Tag
  38 + */
  39 +export default function TimerTag({
  40 + style = {},
  41 + startTime = 0,
  42 + endTime,
  43 + expectedTime = 0,
  44 + showUsedTime,
  45 + countDownProps,
  46 + countUpProps,
  47 + onPress,
  48 +}: PageProps) {
  49 + const { isTimeout, isCountdown, diffSeconds = 0, timeoutStr } = useMemo(() => calculateTime(expectedTime, endTime), [expectedTime, endTime]);
  50 +
  51 + // 已结束
  52 + const isFinished = (endTime || 0) > 0;
  53 +
  54 + function navToTimeDetail() {
  55 + onPress && onPress();
  56 + }
  57 +
  58 + // 超时
  59 + if (isTimeout) {
  60 + return isFinished ? (
  61 + <TagPills style={style} type="danger" title="超时" content={timeoutStr || ''} onPress={navToTimeDetail} />
  62 + ) : (
  63 + <TagPills style={style} type="danger" title="已超时" content={timeoutStr || ''} onPress={navToTimeDetail}>
  64 + <CountUp style={st.timeBarTimeTextDanger} seconds={diffSeconds} {...countUpProps} />
  65 + </TagPills>
  66 + );
  67 + }
  68 + // 未超时:进行中(倒计时)
  69 + if (isCountdown) {
  70 + return (
  71 + <TagPills style={style} type="warn" title="倒计时" content="" onPress={navToTimeDetail}>
  72 + <CountDown style={st.timeBarTimeTextWarn} seconds={diffSeconds} {...countDownProps} />
  73 + </TagPills>
  74 + );
  75 + }
  76 + // 未超时:已用时间
  77 + if (showUsedTime) {
  78 + const usedTime = ((endTime ?? Date.now()) - startTime) / 1000; // 已用时间:秒
  79 + const usedTimeObj = formatTime(usedTime);
  80 + let usedTimeStr = `${usedTimeObj.value}${usedTimeObj.unit}`;
  81 + if (usedTimeObj.remainValue) {
  82 + usedTimeStr += `${usedTimeObj.remainValue}${usedTimeObj.remainUnit}`;
  83 + }
  84 + return <TagPills style={style} type="primary" title={isFinished ? '用时' : '已用时'} content={usedTimeStr} onPress={navToTimeDetail} />;
  85 + }
  86 + // 未超时:不展示
  87 + return null;
  88 +}
... ...
src/components/ApprovalProgress/components/TimerTag/style.less 0 → 100644
  1 +// .ml30 {
  2 +// margin-left: 30px;
  3 +// }
  4 +// .textPrimarySm {
  5 +// color: #4189FD;
  6 +// font-weight: 500;
  7 +// font-size: 12px;
  8 +// }
  9 +// .textWarnSm {
  10 +// color: #FF921C;
  11 +// font-weight: 500;
  12 +// font-size: 12px;
  13 +// }
  14 +// .textGrayLightSm {
  15 +// color: #999;
  16 +// font-weight: 500;
  17 +// font-size: 12px;
  18 +// }
  19 +
  20 +.timeBarTimeTextWarn {
  21 + color: #FF921C;
  22 + font-size: 10px;
  23 + font-weight: 500;
  24 + text-align: center;
  25 +}
  26 +.timeBarTimeTextDanger {
  27 + color: #F4333C;
  28 + font-size: 10px;
  29 + font-weight: 500;
  30 + text-align: center;
  31 +}
... ...
src/components/ApprovalProgress/index.tsx
1   -/*
2   - * @Author: wangqiang@feewee.cn
3   - * @Date: 2022-10-26 09:09:04
4   - * @LastEditors: wangqiang@feewee.cn
5   - * @LastEditTime: 2022-10-26 10:30:00
6   - */
7 1 import { ApprovalProgressDotColor, ApprovalProgressItemVO, ApprovalProgressStatus, ApprovalProgressStatusColor, getApprovalProgressApi } from './api';
8 2 import { Row, Spin, Tag, Timeline } from 'antd';
9 3 import moment from 'moment';
... ... @@ -11,6 +5,9 @@ import React, { useEffect, useState } from &#39;react&#39;;
11 5 import style from './style.less';
12 6 import { IMGURL } from '@/utils';
13 7 import { ClockCircleFilled, CheckCircleFilled, CloseCircleFilled, QuestionCircleFilled } from '@ant-design/icons';
  8 +import TimerTag from './components/TimerTag';
  9 +import classNames from 'classnames';
  10 +import TimeInfo from './components/TimeInfo';
14 11  
15 12 interface Props {
16 13 orderNo?: string; // 审批单号
... ... @@ -24,33 +21,18 @@ interface Progress {
24 21  
25 22 const DotIcon = [
26 23 <QuestionCircleFilled size={14} />,
  24 + <ClockCircleFilled size={14} />, // 1
  25 + <CheckCircleFilled size={14} />, // 2
  26 + <CloseCircleFilled size={14} />, // 3
  27 + <ClockCircleFilled size={14} />, // 4
27 28 <ClockCircleFilled size={14} />,
28   - <CheckCircleFilled size={14} />,
29   - <CloseCircleFilled size={14} />,
30   - <ClockCircleFilled size={14} />,
  29 + <div style={{ width: 14, height: 14, borderRadius: '50%', backgroundColor: '#dedede' }} />, // 6
31 30 ];
32 31  
33 32 export default function ApprovalProgress({ orderNo }: Props) {
34 33 const [progress, setProgress] = useState<Progress>({
35 34 loading: false,
36   - // data: [
37   - // {
38   - // approveTime: Date.now(),
39   - // approverName: "廖洋铭",
40   - // approverId: 2143,
41   - // duration: "30分钟",
42   - // status: 2,
43   - // opinion: "看回放离开家阿斯卡戴假发哈吉手打拉法基",
44   - // },
45   - // {
46   - // approveTime: Date.now(),
47   - // approverName: "王强",
48   - // approverId: 1401,
49   - // duration: "1小时30分钟",
50   - // status: 3,
51   - // opinion: "看回放离开家阿斯卡戴假发哈吉手打拉法基",
52   - // },
53   - // ],
  35 + data: [],
54 36 });
55 37  
56 38 useEffect(() => {
... ... @@ -61,6 +43,41 @@ export default function ApprovalProgress({ orderNo }: Props) {
61 43 setProgress({
62 44 loading: false,
63 45 data: res.data || [],
  46 + // data: [
  47 + // {
  48 + // // 审批中倒计时
  49 + // approveTime: 1709741004000,
  50 + // approverName: 'Shinner',
  51 + // approverId: 2144,
  52 + // status: 1,
  53 + // opinion: '测试理由',
  54 + // efficientTime: 1709781004000,
  55 + // createTime: 1709721004000,
  56 + // roleNames: ['销售一级管理', '销售二级管理', '销售三级管理'],
  57 + // },
  58 + // {
  59 + // // 审批中超时
  60 + // approveTime: 1799722004000,
  61 + // approverName: 'Shinner',
  62 + // approverId: 2145,
  63 + // status: 1,
  64 + // opinion: '测试理由',
  65 + // efficientTime: 1709721004000,
  66 + // createTime: 1709721004000,
  67 + // roleNames: ['销售一级管理', '销售二级管理', '销售三级管理'],
  68 + // },
  69 + // {
  70 + // // 审批结束超时
  71 + // approveTime: 1799722004000,
  72 + // approverName: 'Shinner',
  73 + // approverId: 2146,
  74 + // status: 2,
  75 + // opinion: '测试理由',
  76 + // efficientTime: 1709781004000,
  77 + // createTime: 1709721004000,
  78 + // roleNames: ['销售一级管理', '销售二级管理', '销售三级管理'],
  79 + // },
  80 + // ],
64 81 });
65 82 })
66 83 .catch((error) => {
... ... @@ -75,67 +92,86 @@ export default function ApprovalProgress({ orderNo }: Props) {
75 92 return (
76 93 <Spin spinning={progress.loading}>
77 94 <Timeline className={style.ApprovalProgress_Timeline}>
78   - {(progress.data || []).map((item) => (
79   - <Timeline.Item
80   - className={style.ApprovalProgress_Timeline_Item}
81   - key={item.approverId}
82   - color={item.status ? ApprovalProgressDotColor[item.status] : undefined}
83   - dot={[1, 2, 3, 4].includes(item.status ?? 0) ? DotIcon[item.status ?? 0] : <QuestionCircleFilled size={14} />}
84   - >
85   - <div className={style.ApprovalProgress_Timeline_Item_container}>
86   - <Row align="middle">
87   - <img
88   - style={{
89   - width: 30,
90   - height: 30,
91   - borderRadius: '50%',
92   - marginRight: 5,
93   - }}
94   - src={IMGURL.getBAvatar(item.approverId || -1)}
95   - alt=""
96   - />
97   - <span className={style.name} style={{ marginRight: 5 }}>
98   - {item.approverName || '-'}
99   - </span>
100   - {item.status ? (
101   - <Tag className={style.tag} color={item.status ? ApprovalProgressStatusColor[item.status] : undefined}>
102   - {ApprovalProgressStatus[item.status]}
103   - </Tag>
104   - ) : null}
105   - </Row>
106   - {item.roleNames && item.roleNames.length > 0 ? (
107   - <Row align="middle" style={{ marginTop: 4 }}>
108   - {item.roleNames.map((name) => (
109   - <Tag key={name} className={style.tag} color="gold">
110   - {name}
  95 + {(progress.data || []).map((item) => {
  96 + const processNow = item.status === 1; // 审批中
  97 + const processEnd = item.status === 2 || item.status === 3; // 审批结束
  98 + const processfuture = item.status === 6; // 待审批
  99 + return (
  100 + <Timeline.Item
  101 + className={style.ApprovalProgress_Timeline_Item}
  102 + key={item.approverId}
  103 + color={item.status ? ApprovalProgressDotColor[item.status] : undefined}
  104 + dot={[1, 2, 3, 4, 6].includes(item.status ?? 0) ? DotIcon[item.status ?? 0] : <QuestionCircleFilled size={14} />}
  105 + >
  106 + <div className={style.ApprovalProgress_Timeline_Item_container}>
  107 + <Row align="middle">
  108 + {/* <img
  109 + style={{
  110 + width: 30,
  111 + height: 30,
  112 + borderRadius: '50%',
  113 + marginRight: 5,
  114 + }}
  115 + src={IMGURL.getBAvatar(item.approverId || -1)}
  116 + alt=""
  117 + /> */}
  118 + <span className={style.name} style={{ marginRight: 5 }}>
  119 + {item.approverName || '-'}
  120 + </span>
  121 + {item.status ? (
  122 + <Tag className={style.tag} color={item.status ? ApprovalProgressStatusColor[item.status] : undefined}>
  123 + {ApprovalProgressStatus[item.status]}
111 124 </Tag>
112   - ))}
  125 + ) : null}
  126 + {/* 审批中显示超时(当 > 时) | 倒计时(当 < 时) */}
  127 + {processNow && item.efficientTime ? <TimerTag startTime={moment().valueOf()} expectedTime={item.efficientTime} /> : null}
  128 + {/* 审批结束显示超时(完 > 时) */}
  129 + {processEnd && item.efficientTime && item.approveTime ? (
  130 + <TimerTag startTime={item.efficientTime} endTime={item.approveTime} expectedTime={item.efficientTime} />
  131 + ) : null}
113 132 </Row>
114   - ) : null}
115   - {item.approveTime && (item.status === 2 || item.status === 3) ? (
116   - <div className={style.time}>{moment(item.approveTime).format('YYYY-MM-DD HH:mm:ss')}</div>
117   - ) : null}
118   - {item.expTime && (item.status === 1 || item.status === 4) && (
119   - <div className={style.item}>
120   - <span className={style.label}>截止时间:</span>
121   - <span className={style.value}>{item.expTime ? moment(Number(item.expTime)).format('YYYY.MM.DD HH:mm:ss') : '--'}</span>
122   - </div>
123   - )}
124   - {(item.status === 2 || item.status === 3) && (
125   - <>
126   - <div className={style.item}>
127   - <span className={style.label}>审批意见:</span>
128   - <span className={style.value}>{item.opinion || '--'}</span>
129   - </div>
130   - <div className={style.item}>
131   - <span className={style.label}>审批时长:</span>
132   - <span className={style.value}>{item.duration || '--'}</span>
133   - </div>
134   - </>
135   - )}
136   - </div>
137   - </Timeline.Item>
138   - ))}
  133 + {item.roleNames && item.roleNames.length > 0 ? (
  134 + <Row align="middle" style={{ marginTop: 4 }}>
  135 + {item.roleNames.map((name) => (
  136 + <Tag key={name} className={style.tag} color="gold">
  137 + {name}
  138 + </Tag>
  139 + ))}
  140 + </Row>
  141 + ) : null}
  142 + {/* 详情 */}
  143 + {!processfuture && (
  144 + <>
  145 + {item.createTime ? (
  146 + <div className={style.infoItem}>
  147 + 推送时间:{item.createTime ? moment(Number(item.createTime)).format('YYYY.MM.DD HH:mm:ss') : '--'}
  148 + </div>
  149 + ) : null}
  150 + {item.approveTime && processEnd && (
  151 + <div className={style.infoItem}>
  152 + 完成时间:{item.approveTime ? moment(Number(item.approveTime)).format('YYYY.MM.DD HH:mm:ss') : '--'}
  153 + </div>
  154 + )}
  155 + {item.createTime && item.efficientTime ? (
  156 + <TimeInfo
  157 + style={{ marginTop: 8, lineHeight: '16px' }}
  158 + startTime={moment(Number(item.createTime)).valueOf()}
  159 + endTime={item.approveTime ?? moment().valueOf()}
  160 + requiredTime={(item.efficientTime - moment(Number(item.createTime)).valueOf()) / 1000}
  161 + usedTime={item.approveTime ? (item.approveTime - moment(Number(item.createTime)).valueOf()) / 1000 : 0}
  162 + />
  163 + ) : null}
  164 + {processEnd && (
  165 + <div className={classNames(style.infoItem, { marginBottom: 30 })}>
  166 + 审批意见:{item.opinion ? (item.opinion.trim() === '' ? '--' : item.opinion) : '--'}
  167 + </div>
  168 + )}
  169 + </>
  170 + )}
  171 + </div>
  172 + </Timeline.Item>
  173 + );
  174 + })}
139 175 </Timeline>
140 176 </Spin>
141 177 );
... ...
src/components/ApprovalProgress/style.less
... ... @@ -23,29 +23,11 @@
23 23 }
24 24 }
25 25 }
26   - .item {
27   - margin-top: 4px;
28   - }
29 26 .name {
30 27 color: #666;
31 28 font-weight: 500;
32 29 font-size: 14px;
33 30 }
34   - .time {
35   - color: #999;
36   - font-size: 10px;
37   - margin-top: 4px;
38   - }
39   - .label {
40   - color: #999;
41   - font-weight: 500;
42   - font-size: 12px;
43   - }
44   - .value {
45   - color: #666;
46   - font-weight: 500;
47   - font-size: 12px;
48   - }
49 31 .tag {
50 32 height: 16px;
51 33 line-height: 14px;
... ... @@ -53,4 +35,10 @@
53 35 padding: 0 5px;
54 36 }
55 37 }
  38 + .infoItem {
  39 + color: #999;
  40 + font-size: 12px;
  41 + margin-top: 8px;
  42 + line-height: 16px;
  43 + }
56 44 }
... ...
src/components/ApprovalProgress/util.ts 0 → 100644
  1 +/**
  2 + * 格式化时间展示,值为秒
  3 + * @param time 秒数
  4 + * @returns timeString + unit
  5 + */
  6 +export function formatTime(time: number = 0): {
  7 + value: number; // 时间值
  8 + unit: string; // 时间单位
  9 + remainValue?: number; // 剩余时间值
  10 + remainUnit?: string; // 剩余时间单位
  11 +} {
  12 + // 小于 60秒:展示为秒
  13 + if (time < 60) {
  14 + return {
  15 + value: Math.floor(time),
  16 + unit: time === 0 ? '分钟' : '秒',
  17 + };
  18 + }
  19 + // 小于 60分钟:展示为分钟 + 秒
  20 + if (time < 60 * 60) {
  21 + const remainSeconds = Math.floor(time % 60);
  22 +
  23 + if (remainSeconds === 0) {
  24 + // 整分钟
  25 + return {
  26 + value: Math.floor(time / 60),
  27 + unit: '分钟',
  28 + };
  29 + }
  30 +
  31 + // 分钟 + 秒
  32 + return {
  33 + value: Math.floor((time - remainSeconds) / 60),
  34 + unit: '分',
  35 + remainValue: remainSeconds,
  36 + remainUnit: '秒',
  37 + };
  38 + }
  39 + // 小于 24小时:展示为小时 + 分钟
  40 + if (time < 60 * 60 * 24) {
  41 + const diffMinutes = Math.floor(time / 60);
  42 + const remainMinutes = Math.floor(diffMinutes % 60);
  43 +
  44 + // 整小时
  45 + if (remainMinutes === 0) {
  46 + return {
  47 + value: Math.floor(diffMinutes / 60),
  48 + unit: '小时',
  49 + };
  50 + }
  51 +
  52 + // 小时 + 分钟
  53 + return {
  54 + value: Math.floor((diffMinutes - remainMinutes) / 60),
  55 + unit: '小时',
  56 + remainValue: remainMinutes, // 剩余分钟
  57 + remainUnit: '分',
  58 + };
  59 + }
  60 + // 大于 24小时:展示为天 + 小时
  61 + const diffHours = Math.floor(time / 60 / 60);
  62 + const remainHours = Math.floor(diffHours % 24);
  63 + // 整天
  64 + if (remainHours === 0) {
  65 + return {
  66 + value: diffHours / 24,
  67 + unit: '天',
  68 + };
  69 + }
  70 + // 天 + 小时
  71 + return {
  72 + value: Math.floor((diffHours - remainHours) / 24),
  73 + unit: '天',
  74 + remainValue: remainHours, // 剩余小时
  75 + remainUnit: '小时',
  76 + };
  77 +}
  78 +
  79 +interface CalculatedTime {
  80 + isTimeout: boolean; // 是否超时
  81 + isCountdown: boolean; // 是否倒计时
  82 + diffSeconds: number; // 时间差:超时或倒计时
  83 + timeoutStr?: string; // 超时时间字符串
  84 +}
  85 +
  86 +/**
  87 + * 计算时效
  88 + * @param expectedTime 预计完成时间
  89 + * @param endTime 结束时间
  90 + * @description
  91 + * 1. 如果 endTime 为 0,表示未结束:计算倒计时
  92 + * 2. 如果 endTime 不为 0,表示已结束:计算超时
  93 + * diffTime = endTime - expectedTime
  94 + * 2.1 diffTime <= 0, 未超时
  95 + * 2.2 diffTime > 0, 超时
  96 + * @returns CalculatedTime
  97 + */
  98 +export function calculateTime(expectedTime: number = 0, endTime: number = 0): CalculatedTime {
  99 + if (expectedTime === 0) {
  100 + return {
  101 + isTimeout: false,
  102 + isCountdown: false,
  103 + diffSeconds: 0,
  104 + };
  105 + }
  106 +
  107 + // 1. 如果 endTime 为 0,表示未结束:计算倒计时
  108 + if (endTime === 0) {
  109 + const diffTime = (Date.now() - expectedTime) / 1000; // 秒
  110 + if (diffTime < 0) {
  111 + return {
  112 + isTimeout: false,
  113 + isCountdown: true,
  114 + diffSeconds: Math.abs(diffTime),
  115 + };
  116 + }
  117 + // 超时:返回超时字符串 todo refactor
  118 + const passTime = formatTime(diffTime);
  119 + let passTimeStr = `${Math.floor(passTime.value)}${passTime.unit}`;
  120 + if (passTime.remainValue) {
  121 + passTimeStr += `${Math.floor(passTime.remainValue)}${passTime.remainUnit}`;
  122 + }
  123 + return {
  124 + isTimeout: true,
  125 + isCountdown: false,
  126 + diffSeconds: Math.floor(diffTime),
  127 + timeoutStr: passTimeStr,
  128 + };
  129 + }
  130 +
  131 + const diffTime = (endTime - expectedTime) / 1000; // 秒
  132 + // 2. 如果 endTime 不为 0,表示已结束
  133 + // 2.1 若 diffTime > 0,表示超时
  134 + if (diffTime > 0) {
  135 + // 超时:返回超时字符串
  136 + const passTime = formatTime(diffTime);
  137 + let passTimeStr = `${Math.floor(passTime.value)}${passTime.unit}`;
  138 + if (passTime.remainValue) {
  139 + passTimeStr += `${Math.floor(passTime.remainValue)}${passTime.remainUnit}`;
  140 + }
  141 + return {
  142 + isTimeout: true,
  143 + isCountdown: false,
  144 + diffSeconds: diffTime,
  145 + timeoutStr: passTimeStr,
  146 + };
  147 + }
  148 +
  149 + // 2.2. 若 diffTime <= 0, 表示未超时
  150 + return {
  151 + isTimeout: false,
  152 + isCountdown: false,
  153 + diffSeconds: 0,
  154 + };
  155 +}
  156 +
  157 +/**
  158 + * 补零
  159 + * @param number
  160 + * @param length
  161 + */
  162 +export const fix0 = (number: number, length: number = 2): string => {
  163 + let temp = number.toString();
  164 + while (temp.length < length) {
  165 + temp = `0${temp}`;
  166 + }
  167 + return temp;
  168 +};
... ...
src/components/ShopSelectNew/components/ShopModal.tsx
... ... @@ -35,8 +35,9 @@ interface Props {
35 35 */
36 36 disabledShopIds?: number[];
37 37 disabledOptions?: (keyof ShopSelectNewOptions)[];
38   - showBrand?: boolean //表格中是否展示授权品牌,默认展示
39   - showAfshop?: boolean //表格中是否展示对应售后门店,默认展示
  38 + showBrand?: boolean; //表格中是否展示授权品牌,默认展示
  39 + showAfshop?: boolean; //表格中是否展示对应售后门店,默认展示
  40 + destroyOnClose?: boolean;
40 41 }
41 42  
42 43 export default function ShopModal({
... ... @@ -58,6 +59,7 @@ export default function ShopModal({
58 59 disabledOptions,
59 60 showBrand = true,
60 61 showAfshop = true,
  62 + destroyOnClose = false,
61 63 }: Props) {
62 64 const optionsStatus = useMemo(() => {
63 65 return {
... ... @@ -190,12 +192,12 @@ export default function ShopModal({
190 192 setSelected(
191 193 checked
192 194 ? listInitial.data
193   - .map((item) => ({
194   - ...item,
195   - value: item.shopId,
196   - label: item.shopShortName,
197   - }))
198   - .filter((item) => (disabledShopIds.includes(item.shopId!) ? selected.find((s) => s.shopId === item.shopId) : true))
  195 + .map((item) => ({
  196 + ...item,
  197 + value: item.shopId,
  198 + label: item.shopShortName,
  199 + }))
  200 + .filter((item) => (disabledShopIds.includes(item.shopId!) ? selected.find((s) => s.shopId === item.shopId) : true))
199 201 : selected.filter((s) => disabledShopIds.includes(s.shopId!)),
200 202 );
201 203 };
... ... @@ -209,6 +211,7 @@ export default function ShopModal({
209 211 visible={visible}
210 212 onCancel={() => setVisible(false)}
211 213 onOk={() => onOk()}
  214 + destroyOnClose={destroyOnClose}
212 215 >
213 216 {multiple ? (
214 217 <>
... ... @@ -518,7 +521,7 @@ export default function ShopModal({
518 521 <Table.Column title="门店业态" dataIndex="bizType" align="left" render={(bizType) => (bizType ? BizType[bizType] : '-')} />
519 522 <Table.Column title="归属公司" dataIndex="dealer" align="left" render={(dealer) => dealer?.name || '-'} />
520 523 <Table.Column title="门店区域" dataIndex="region" align="left" render={(region) => region?.fullName || '-'} />
521   - {!!showBrand &&
  524 + {!!showBrand && (
522 525 <Table.Column
523 526 title="授权品牌"
524 527 dataIndex="brandList"
... ... @@ -542,10 +545,8 @@ export default function ShopModal({
542 545 )
543 546 }
544 547 />
545   - }
546   - {!!showAfshop &&
547   - <Table.Column title="对应售后门店" dataIndex="casShopName" align="left" render={(casShopName) => casShopName || '-'} />
548   - }
  548 + )}
  549 + {!!showAfshop && <Table.Column title="对应售后门店" dataIndex="casShopName" align="left" render={(casShopName) => casShopName || '-'} />}
549 550 </Table>
550 551 </Modal>
551 552 );
... ...
src/components/ShopSelectNew/index.tsx
... ... @@ -7,7 +7,7 @@
7 7 import useInitial from '@/hooks/useInitail';
8 8 import { isObjectChanged } from '@/utils/validate';
9 9 import { Tag } from 'antd';
10   -import type { Ref} from 'react';
  10 +import type { Ref } from 'react';
11 11 import React, { forwardRef, memo, useEffect, useImperativeHandle, useState } from 'react';
12 12 import type { QueryParams, ShopSelectNewOptions, Value } from './api';
13 13 import { getShopListApi, getShopListChooseOptionsApi } from './api';
... ... @@ -38,8 +38,9 @@ interface Props {
38 38 disabledShopIds?: number[];
39 39 disabledOptions?: (keyof ShopSelectNewOptions)[];
40 40 onDefaultOptionsChangeFetch?: boolean;
41   - showBrand?: boolean
42   - showAfshop?: boolean
  41 + showBrand?: boolean;
  42 + showAfshop?: boolean;
  43 + destroyOnClose?: boolean;
43 44 }
44 45  
45 46 export interface ShopSelectNewRef {
... ... @@ -64,6 +65,7 @@ function ShopSelectNew(
64 65 onDefaultOptionsChangeFetch,
65 66 showBrand,
66 67 showAfshop,
  68 + destroyOnClose,
67 69 }: Props,
68 70 ref: Ref<ShopSelectNewRef>,
69 71 ) {
... ... @@ -220,6 +222,7 @@ function ShopSelectNew(
220 222 disabledOptions={disabledOptions}
221 223 showBrand={showBrand}
222 224 showAfshop={showAfshop}
  225 + destroyOnClose={destroyOnClose}
223 226 />
224 227 )}
225 228 </div>
... ...
src/pages/carinsur/InsuranceShop/Condition/index.tsx
... ... @@ -74,7 +74,7 @@ export default function Condition(props: Props) {
74 74 useEffect(() => {
75 75 value.brandId != currentBrandId && setCurrentBrandId(value.brandId);
76 76 form.setFieldsValue({
77   - brand: brandLabelInValue && value.brandId ? { brandId: value.brandId, brandName: value.brandName } : value.brandId,
  77 + brand: brandLabelInValue && value.brandId ? { id: value.brandId, name: value.brandName } : value.brandId,
78 78 joinDealer: value
79 79 ? { type: value.joinDealerType || 1, selected: _.compact((value.joinDealerIds || '').split(',').map((item) => Number(item))) }
80 80 : defaultDealerValue,
... ... @@ -99,9 +99,9 @@ export default function Condition(props: Props) {
99 99 }
100 100  
101 101 function getTargetCustomer(customer: any[] = []) {
102   - let targetCustomer = defaultCustomer;
103   - let _checks: any = [];
104   - let _filter = defaultCustomer.filter || {};
  102 + const targetCustomer = defaultCustomer;
  103 + const _checks: any = [];
  104 + const _filter = defaultCustomer.filter || {};
105 105 customer.forEach((item) => {
106 106 let seriesValue = [];
107 107 _checks.push(item.type);
... ... @@ -154,7 +154,7 @@ export default function Condition(props: Props) {
154 154 )}
155 155 <FormItem noStyle shouldUpdate={(prevValues, currentValues) => prevValues.brand != currentValues.brand}>
156 156 {({ getFieldValue }): any => {
157   - const brandId = (brandLabelInValue && getFieldValue('brand') ? getFieldValue('brand').brandId : getFieldValue('brand')) || value.brandId;
  157 + const brandId = (brandLabelInValue && getFieldValue('brand') ? getFieldValue('brand').id : getFieldValue('brand')) || value.brandId;
158 158 return brandId ? (
159 159 <div>
160 160 {dealerShow && (
... ...
src/pages/carinsur/InsuranceShop/components/CreateModal.tsx
... ... @@ -5,6 +5,8 @@ import { getCompanyList, saveData } from &#39;../api&#39;;
5 5 import { getShopApi } from '@/common/api';
6 6 import useInitial from '@/hooks/useInitail';
7 7 import SendToRepairShop from './SendToRepairShop';
  8 +import ShopSelectNew from '@/components/ShopSelectNew';
  9 +import _ from 'lodash';
8 10  
9 11 const FormItem = Form.Item;
10 12 const Option = Select.Option;
... ... @@ -47,7 +49,7 @@ export default function CreateModal({ onCancel, item = {}, onRrfreshing, visible
47 49 const params = {
48 50 insurerId: fieldsValue.insurerId.value,
49 51 saleShops: fieldsValue.saleShops.map((item: any) => ({ shopId: item.value, shopName: item.label })),
50   - stmtShop: { shopId: fieldsValue.stmtShop.value, shopName: fieldsValue.stmtShop.label },
  52 + stmtShop: { shopId: fieldsValue.stmtShop[0].value, shopName: fieldsValue.stmtShop[0].label },
51 53 repairShops: fieldsValue.repairShops.map((item: any) => {
52 54 const _item = { ...item };
53 55 delete _item.key;
... ... @@ -87,7 +89,7 @@ export default function CreateModal({ onCancel, item = {}, onRrfreshing, visible
87 89 insurerId: item.insurerId ? { value: item.insurerId, label: item.insurerName } : undefined,
88 90 repairShops: item.repairShops ? item.repairShops.map((i, index) => ({ ...i, key: index + 1 })) : [],
89 91 saleShops: item.saleShops ? item.saleShops.map((i) => ({ value: i.shopId, label: i.shopName })) : [],
90   - stmtShop: item.stmtShop ? { value: item.stmtShop.shopId, label: item.stmtShop.shopName } : undefined,
  92 + stmtShop: !_.isEmpty(item.stmtShop) ? [{ value: item.stmtShop.shopId, label: item.stmtShop.shopName }] : undefined,
91 93 }}
92 94 >
93 95 <FormItem label="保险公司" name="insurerId" rules={[{ required: true, message: '该选项为必填项' }]}>
... ... @@ -107,22 +109,10 @@ export default function CreateModal({ onCancel, item = {}, onRrfreshing, visible
107 109 </Select>
108 110 </FormItem>
109 111 <FormItem label="出保门店" name="saleShops" rules={[{ required: true, message: '该选项为必填项' }]}>
110   - <Select placeholder="请选择" labelInValue mode="multiple" style={{ width: '100%' }} showSearch optionFilterProp="children">
111   - {allshopData.map((item: CommonApi.OptionVO) => (
112   - <Option key={item.id} value={item.id}>
113   - {item.name}
114   - </Option>
115   - ))}
116   - </Select>
  112 + <ShopSelectNew destroyOnClose multiple type={2} shopIds={allshopData.map((i) => i.id!)} placeholder="请选择" />
117 113 </FormItem>
118 114 <FormItem label="返利结算门店" name="stmtShop" rules={[{ required: true, message: '该选项为必填项' }]}>
119   - <Select placeholder="请选择" labelInValue style={{ width: '100%' }} showSearch optionFilterProp="children">
120   - {allshopData.map((item: CommonApi.OptionVO) => (
121   - <Option key={item.id} value={item.id}>
122   - {item.name}
123   - </Option>
124   - ))}
125   - </Select>
  115 + <ShopSelectNew destroyOnClose type={2} shopIds={allshopData.map((i) => i.id!)} placeholder="请选择" />
126 116 </FormItem>
127 117 <FormItem name="repairShops" label="送修门店" rules={[{ required: true, message: '送修门店配置不能为空' }]}>
128 118 <SendToRepairShop shopList={shopList} />
... ...
src/pages/carinsur/InsuranceShop/components/SendShopModal.tsx
1 1 import React, { useState, useEffect } from 'react';
2   -import { Modal, Skeleton, Table, Select, Form } from 'antd';
  2 +import { Modal, Select, Form } from 'antd';
3 3 import Condition from '../Condition';
  4 +import ShopSelectNew from '@/components/ShopSelectNew';
4 5  
5 6 interface Props {
6 7 shopList: CommonApi.OptionVO[];
... ... @@ -26,7 +27,7 @@ export default function Index({ shopList = [], row = {}, value = [], visible = f
26 27 useEffect(() => {
27 28 if (Object.keys(row).length) {
28 29 form.setFieldsValue({
29   - stmtShop: row.stmtShopId && { value: row.stmtShopId, label: row.stmtShopName },
  30 + stmtShop: row.stmtShopId ? [{ value: row.stmtShopId, label: row.stmtShopName }] : undefined,
30 31 key: row.key && row.key,
31 32 });
32 33 }
... ... @@ -52,8 +53,8 @@ export default function Index({ shopList = [], row = {}, value = [], visible = f
52 53 let list = [] as InsurType.repairStmtShopList[];
53 54 if (!key) {
54 55 const tableItem = {
55   - stmtShopId: stmtShop.value,
56   - stmtShopName: stmtShop.label,
  56 + stmtShopId: stmtShop[0].value,
  57 + stmtShopName: stmtShop[0].label,
57 58 brandId: brand.id,
58 59 brandName: brand.name,
59 60 key: value.length + 1,
... ... @@ -63,8 +64,8 @@ export default function Index({ shopList = [], row = {}, value = [], visible = f
63 64 list = value.map((it) => {
64 65 if (it.key === key) {
65 66 return {
66   - stmtShopId: stmtShop.value,
67   - stmtShopName: stmtShop.label,
  67 + stmtShopId: stmtShop[0].value,
  68 + stmtShopName: stmtShop[0].label,
68 69 brandId: brand.id,
69 70 brandName: brand.name,
70 71 key,
... ... @@ -88,21 +89,7 @@ export default function Index({ shopList = [], row = {}, value = [], visible = f
88 89 <Form form={form} labelCol={{ span: '6' }} onFinish={handleItemSave}>
89 90 <Condition form={form} value={carValue} brandLabelInValue brandShow />
90 91 <FormItem label="送修结算门店" name="stmtShop" rules={[{ required: true, message: '该选项为必填项' }]}>
91   - <Select
92   - showSearch
93   - filterOption={(input, option: any) => option?.children.indexOf(input) >= 0}
94   - placeholder="请选择"
95   - labelInValue
96   - style={{ width: '100%' }}
97   - >
98   - {shopList
99   - .filter((i) => i.bizType === 2)
100   - .map((item: CommonApi.OptionVO) => (
101   - <Option key={item.id} value={item.id}>
102   - {item.name}
103   - </Option>
104   - ))}
105   - </Select>
  92 + <ShopSelectNew destroyOnClose type={2} shopIds={shopList.filter((i) => i.bizType === 2).map((i) => i.id!)} placeholder="请选择" />
106 93 </FormItem>
107 94 <FormItem name="key" />
108 95 </Form>
... ...
src/pages/carinsur/InsuranceShop/components/ShopModal.tsx
1   -import React, { useState, useEffect } from 'react';
2   -import { Modal, Skeleton, Table, Select, Form } from 'antd';
3   -import Condition from '@/components/Condition';
  1 +import React, { useEffect } from 'react';
  2 +import { Modal, Select, Form } from 'antd';
4 3 import SendToRepairCloseShop from './SendToRepairCloseShop';
  4 +import ShopSelectNew from '@/components/ShopSelectNew';
5 5  
6 6 interface Props {
7 7 visible: boolean;
... ... @@ -25,7 +25,7 @@ function ShopModal({ visible, shopList, value = [], row = {}, onChange, setVisib
25 25 useEffect(() => {
26 26 if (Object.keys(row).length) {
27 27 form.setFieldsValue({
28   - repairShop: row.shopId && { value: row.shopId, label: row.shopName },
  28 + repairShop: row.shopId ? [{ value: row.shopId, label: row.shopName }] : undefined,
29 29 repairStmtShopList: row.repairStmtShopList ? row.repairStmtShopList.map((i, index) => ({ ...i, key: index + 1 })) : [],
30 30 key: row.key && row.key,
31 31 });
... ... @@ -47,8 +47,8 @@ function ShopModal({ visible, shopList, value = [], row = {}, onChange, setVisib
47 47 let list = [] as InsurType.sendToRepairShopTableList[];
48 48 if (!key) {
49 49 const tableItem = {
50   - shopId: repairShop.value,
51   - shopName: repairShop.label,
  50 + shopId: repairShop[0].value,
  51 + shopName: repairShop[0].label,
52 52 repairStmtShopList,
53 53 key: value.length + 1,
54 54 };
... ... @@ -57,8 +57,8 @@ function ShopModal({ visible, shopList, value = [], row = {}, onChange, setVisib
57 57 list = value.map((it) => {
58 58 if (it.key === key) {
59 59 return {
60   - shopId: repairShop.value,
61   - shopName: repairShop.label,
  60 + shopId: repairShop[0].value,
  61 + shopName: repairShop[0].label,
62 62 repairStmtShopList,
63 63 key,
64 64 };
... ... @@ -81,21 +81,7 @@ function ShopModal({ visible, shopList, value = [], row = {}, onChange, setVisib
81 81 >
82 82 <Form form={form} labelCol={{ span: '6' }} onFinish={handleItemSave}>
83 83 <FormItem label="送修门店" name="repairShop" rules={[{ required: true, message: '该选项为必填项' }]}>
84   - <Select
85   - showSearch
86   - filterOption={(input, option: any) => option?.children.indexOf(input) >= 0}
87   - placeholder="请选择"
88   - labelInValue
89   - style={{ width: '100%' }}
90   - >
91   - {shopList
92   - .filter((i) => i.bizType === 2)
93   - .map((item: CommonApi.OptionVO) => (
94   - <Option key={item.id} value={item.id}>
95   - {item.name}
96   - </Option>
97   - ))}
98   - </Select>
  84 + <ShopSelectNew destroyOnClose type={2} shopIds={shopList.filter((i) => i.bizType === 2).map((i) => i.id!)} placeholder="请选择" />
99 85 </FormItem>
100 86 <FormItem label="送修结算门店" name="repairStmtShopList" rules={[{ required: true, message: '该选项为必填项' }]}>
101 87 <SendToRepairCloseShop shopList={shopList} />
... ...
src/pages/carinsur/InsuranceShopNewMultiple/Condition/index.tsx
... ... @@ -74,7 +74,7 @@ export default function Condition(props: Props) {
74 74 useEffect(() => {
75 75 value.brandId != currentBrandId && setCurrentBrandId(value.brandId);
76 76 form.setFieldsValue({
77   - brand: brandLabelInValue && value.brandId ? { brandId: value.brandId, brandName: value.brandName } : value.brandId,
  77 + brand: brandLabelInValue && value.brandId ? { id: value.brandId, name: value.brandName } : value.brandId,
78 78 joinDealer: value
79 79 ? { type: value.joinDealerType || 1, selected: _.compact((value.joinDealerIds || '').split(',').map((item) => Number(item))) }
80 80 : defaultDealerValue,
... ... @@ -154,7 +154,7 @@ export default function Condition(props: Props) {
154 154 )}
155 155 <FormItem noStyle shouldUpdate={(prevValues, currentValues) => prevValues.brand != currentValues.brand}>
156 156 {({ getFieldValue }): any => {
157   - const brandId = (brandLabelInValue && getFieldValue('brand') ? getFieldValue('brand').brandId : getFieldValue('brand')) || value.brandId;
  157 + const brandId = (brandLabelInValue && getFieldValue('brand') ? getFieldValue('brand').id : getFieldValue('brand')) || value.brandId;
158 158 return brandId ? (
159 159 <div>
160 160 {dealerShow && (
... ...
src/pages/carinsur/InsuranceShopNewMultiple/components/CreateModal.tsx
... ... @@ -6,6 +6,8 @@ import { getShopApi } from &#39;@/common/api&#39;;
6 6 import SendToRepairShop from './SendToRepairShop';
7 7 import { getInsurList } from '../../companyConfig/api';
8 8 import { useRequest } from 'umi';
  9 +import ShopSelectNew from '@/components/ShopSelectNew';
  10 +import _ from 'lodash';
9 11  
10 12 const FormItem = Form.Item;
11 13 const Option = Select.Option;
... ... @@ -46,7 +48,7 @@ export default function CreateModal({ onCancel, item = {}, onRrfreshing, visible
46 48 id: fieldsValue.id,
47 49 insurerId: fieldsValue.insurerId.value,
48 50 saleShops: fieldsValue.saleShops.map((item: any) => ({ shopId: item.value, shopName: item.label })),
49   - stmtShop: { shopId: fieldsValue.stmtShop.value, shopName: fieldsValue.stmtShop.label },
  51 + stmtShop: { shopId: fieldsValue.stmtShop[0].value, shopName: fieldsValue.stmtShop[0].label },
50 52 repairShops: fieldsValue.repairShops.map((item: any) => {
51 53 const _item = { ...item };
52 54 delete _item.key;
... ... @@ -87,7 +89,7 @@ export default function CreateModal({ onCancel, item = {}, onRrfreshing, visible
87 89 insurerId: item.insurerId ? { value: item.insurerId, label: item.insurerName } : undefined,
88 90 repairShops: item.repairShops ? item.repairShops.map((i, index) => ({ ...i, key: index + 1 })) : [],
89 91 saleShops: item.saleShops ? item.saleShops.map((i) => ({ value: i.shopId, label: i.shopName })) : [],
90   - stmtShop: item.stmtShop ? { value: item.stmtShop.shopId, label: item.stmtShop.shopName } : undefined,
  92 + stmtShop: !_.isEmpty(item.stmtShop) ? [{ value: item.stmtShop.shopId, label: item.stmtShop.shopName }] : undefined,
91 93 }}
92 94 >
93 95 <FormItem name="id" hidden />
... ... @@ -109,32 +111,12 @@ export default function CreateModal({ onCancel, item = {}, onRrfreshing, visible
109 111 </Select>
110 112 </FormItem>
111 113 <FormItem label="出保门店" name="saleShops" rules={[{ required: true, message: '该选项为必填项' }]}>
112   - <Select
113   - placeholder="请选择"
114   - labelInValue
115   - mode="multiple"
116   - style={{ width: '100%' }}
117   - loading={getShopApiHook.loading}
118   - showSearch
119   - optionFilterProp="children"
120   - >
121   - {(getShopApiHook.data ?? []).map((item: CommonApi.OptionVO) => (
122   - <Option key={item.id} value={item.id}>
123   - {item.name}
124   - </Option>
125   - ))}
126   - </Select>
  114 + <ShopSelectNew destroyOnClose multiple type={2} shopIds={(getShopApiHook.data ?? []).map((i) => i.id!)} placeholder="请选择" />
127 115 </FormItem>
128 116 <FormItem label="返利结算门店" name="stmtShop" rules={[{ required: true, message: '该选项为必填项' }]}>
129   - <Select placeholder="请选择" labelInValue style={{ width: '100%' }} loading={getShopApiHook.loading} showSearch optionFilterProp="children">
130   - {(getShopApiHook.data ?? []).map((item: CommonApi.OptionVO) => (
131   - <Option key={item.id} value={item.id}>
132   - {item.name}
133   - </Option>
134   - ))}
135   - </Select>
  117 + <ShopSelectNew destroyOnClose type={2} shopIds={(getShopApiHook.data ?? []).map((i) => i.id!)} placeholder="请选择" />
136 118 </FormItem>
137   - <FormItem name="repairShops" label="送修门店" rules={[{ required: true, message: '送修门店配置不能为空' }]}>
  119 + <FormItem label="送修门店" name="repairShops" rules={[{ required: true, message: '送修门店配置不能为空' }]}>
138 120 <SendToRepairShop shopList={getShopApiHook.data ?? []} />
139 121 </FormItem>
140 122 </Form>
... ...
src/pages/carinsur/InsuranceShopNewMultiple/components/SendShopModal.tsx
1 1 import React, { useState, useEffect } from 'react';
2   -import { Modal, Select, Form } from 'antd';
  2 +import { Modal, Form } from 'antd';
3 3 import Condition from '../Condition';
  4 +import ShopSelectNew from '@/components/ShopSelectNew';
4 5  
5 6 interface Props {
6 7 shopList: CommonApi.OptionVO[];
... ... @@ -12,7 +13,6 @@ interface Props {
12 13 }
13 14  
14 15 const FormItem = Form.Item;
15   -const Option = Select.Option;
16 16  
17 17 export default function Index({ shopList = [], row = {}, value = [], visible = false, setVisible, onChange }: Props) {
18 18 const [form] = Form.useForm();
... ... @@ -26,7 +26,7 @@ export default function Index({ shopList = [], row = {}, value = [], visible = f
26 26 useEffect(() => {
27 27 if (Object.keys(row).length) {
28 28 form.setFieldsValue({
29   - stmtShop: row.stmtShopId && { value: row.stmtShopId, label: row.stmtShopName },
  29 + stmtShop: row.stmtShopId ? [{ value: row.stmtShopId, label: row.stmtShopName }] : undefined,
30 30 key: row.key && row.key,
31 31 });
32 32 }
... ... @@ -52,8 +52,8 @@ export default function Index({ shopList = [], row = {}, value = [], visible = f
52 52 let list = [] as InsurType.repairStmtShopList[];
53 53 if (!key) {
54 54 const tableItem = {
55   - stmtShopId: stmtShop.value,
56   - stmtShopName: stmtShop.label,
  55 + stmtShopId: stmtShop[0].value,
  56 + stmtShopName: stmtShop[0].label,
57 57 brandId: brand.id,
58 58 brandName: brand.name,
59 59 key: value.length + 1,
... ... @@ -63,8 +63,8 @@ export default function Index({ shopList = [], row = {}, value = [], visible = f
63 63 list = value.map((it) => {
64 64 if (it.key === key) {
65 65 return {
66   - stmtShopId: stmtShop.value,
67   - stmtShopName: stmtShop.label,
  66 + stmtShopId: stmtShop[0].value,
  67 + stmtShopName: stmtShop[0].label,
68 68 brandId: brand.id,
69 69 brandName: brand.name,
70 70 key,
... ... @@ -88,21 +88,7 @@ export default function Index({ shopList = [], row = {}, value = [], visible = f
88 88 <Form form={form} labelCol={{ span: '6' }} onFinish={handleItemSave}>
89 89 <Condition form={form} value={carValue} brandLabelInValue brandShow />
90 90 <FormItem label="送修结算门店" name="stmtShop" rules={[{ required: true, message: '该选项为必填项' }]}>
91   - <Select
92   - showSearch
93   - filterOption={(input, option: any) => option?.children.indexOf(input) >= 0}
94   - placeholder="请选择"
95   - labelInValue
96   - style={{ width: '100%' }}
97   - >
98   - {shopList
99   - .filter((i) => i.bizType === 2)
100   - .map((item: CommonApi.OptionVO) => (
101   - <Option key={item.id} value={item.id}>
102   - {item.name}
103   - </Option>
104   - ))}
105   - </Select>
  91 + <ShopSelectNew destroyOnClose type={2} shopIds={(shopList ?? []).filter((i) => i.bizType === 2).map((i) => i.id!)} placeholder="请选择" />
106 92 </FormItem>
107 93 <FormItem name="key" />
108 94 </Form>
... ...
src/pages/carinsur/InsuranceShopNewMultiple/components/ShopModal.tsx
1 1 import React, { useEffect } from 'react';
2   -import { Modal, Select, Form } from 'antd';
  2 +import { Modal, Form } from 'antd';
3 3 import SendToRepairCloseShop from './SendToRepairCloseShop';
  4 +import ShopSelectNew from '@/components/ShopSelectNew';
4 5  
5 6 interface Props {
6 7 visible: boolean;
... ... @@ -11,7 +12,6 @@ interface Props {
11 12 onChange?: (data: InsurType.sendToRepairShopTableList[]) => void;
12 13 }
13 14 const FormItem = Form.Item;
14   -const Option = Select.Option;
15 15  
16 16 function ShopModal({ visible, shopList, value = [], row = {}, onChange, setVisible }: Props) {
17 17 const [form] = Form.useForm();
... ... @@ -24,7 +24,7 @@ function ShopModal({ visible, shopList, value = [], row = {}, onChange, setVisib
24 24 useEffect(() => {
25 25 if (Object.keys(row).length) {
26 26 form.setFieldsValue({
27   - repairShop: row.shopId && { value: row.shopId, label: row.shopName },
  27 + repairShop: row.shopId ? [{ value: row.shopId, label: row.shopName }] : undefined,
28 28 repairStmtShopList: row.repairStmtShopList ? row.repairStmtShopList.map((i, index) => ({ ...i, key: index + 1 })) : [],
29 29 key: row.key && row.key,
30 30 });
... ... @@ -46,8 +46,8 @@ function ShopModal({ visible, shopList, value = [], row = {}, onChange, setVisib
46 46 let list = [] as InsurType.sendToRepairShopTableList[];
47 47 if (!key) {
48 48 const tableItem = {
49   - shopId: repairShop.value,
50   - shopName: repairShop.label,
  49 + shopId: repairShop[0].value,
  50 + shopName: repairShop[0].label,
51 51 repairStmtShopList,
52 52 key: value.length + 1,
53 53 };
... ... @@ -56,8 +56,8 @@ function ShopModal({ visible, shopList, value = [], row = {}, onChange, setVisib
56 56 list = value.map((it) => {
57 57 if (it.key === key) {
58 58 return {
59   - shopId: repairShop.value,
60   - shopName: repairShop.label,
  59 + shopId: repairShop[0].value,
  60 + shopName: repairShop[0].label,
61 61 repairStmtShopList,
62 62 key,
63 63 };
... ... @@ -80,21 +80,7 @@ function ShopModal({ visible, shopList, value = [], row = {}, onChange, setVisib
80 80 >
81 81 <Form form={form} labelCol={{ span: '6' }} onFinish={handleItemSave}>
82 82 <FormItem label="送修门店" name="repairShop" rules={[{ required: true, message: '该选项为必填项' }]}>
83   - <Select
84   - showSearch
85   - filterOption={(input, option: any) => option?.children.indexOf(input) >= 0}
86   - placeholder="请选择"
87   - labelInValue
88   - style={{ width: '100%' }}
89   - >
90   - {shopList
91   - .filter((i) => i.bizType === 2)
92   - .map((item: CommonApi.OptionVO) => (
93   - <Option key={item.id} value={item.id}>
94   - {item.name}
95   - </Option>
96   - ))}
97   - </Select>
  83 + <ShopSelectNew destroyOnClose type={2} shopIds={(shopList ?? []).filter((i) => i.bizType === 2).map((i) => i.id!)} placeholder="请选择" />
98 84 </FormItem>
99 85 <FormItem label="送修结算门店" name="repairStmtShopList" rules={[{ required: true, message: '该选项为必填项' }]}>
100 86 <SendToRepairCloseShop shopList={shopList} />
... ...
src/pages/carinsur/companyConfig/components/CreateModal.tsx
... ... @@ -12,7 +12,6 @@ interface Props {
12 12 onCancel: Function;
13 13 onRrfreshing: () => any;
14 14 visible: boolean;
15   - brands: string[];
16 15 }
17 16  
18 17 const formItemLayout = {
... ... @@ -32,7 +31,7 @@ const formItemLayoutWithOutLabel = {
32 31 },
33 32 };
34 33  
35   -export default function Create({ onCancel, item, brands, onRrfreshing, visible }: Props) {
  34 +export default function Create({ onCancel, item, onRrfreshing, visible }: Props) {
36 35 const [form] = Form.useForm();
37 36 const [firmLoading, setFirmLoading] = useState(false);
38 37 const [loading, setLoading] = useState(false);
... ... @@ -47,7 +46,6 @@ export default function Create({ onCancel, item, brands, onRrfreshing, visible }
47 46 if (!visible) {
48 47 form.setFieldsValue({
49 48 name: undefined,
50   - brand: '',
51 49 });
52 50 }
53 51 }, [visible]);
... ... @@ -75,7 +73,7 @@ export default function Create({ onCancel, item, brands, onRrfreshing, visible }
75 73 };
76 74 setFirmLoading(true);
77 75 saveInsur(params)
78   - .then((res) => {
  76 + .then(() => {
79 77 message.success('操作成功');
80 78 onRrfreshing && onRrfreshing();
81 79 setFirmLoading(false);
... ... @@ -123,15 +121,6 @@ export default function Create({ onCancel, item, brands, onRrfreshing, visible }
123 121 ))}
124 122 </Select>
125 123 </FormItem>
126   - <FormItem label="类型" name="brand" rules={[{ required: true, message: '该选项为必填项' }]}>
127   - <Select placeholder="请选择类型" showSearch optionFilterProp="children">
128   - {brands.map((item: string, index) => (
129   - <Select.Option key={index} value={item}>
130   - {item}
131   - </Select.Option>
132   - ))}
133   - </Select>
134   - </FormItem>
135 124 <FormItem label="送修差额是否包含驾意险" name="repairMarginContainsDai" rules={[{ required: true, message: '该选项为必选项' }]}>
136 125 <RadioGroup
137 126 options={[
... ... @@ -148,57 +137,72 @@ export default function Create({ onCancel, item, brands, onRrfreshing, visible }
148 137 ]}
149 138 />
150 139 </FormItem>
151   - <Form.List
152   - name="orderInsurerNames"
153   - rules={[
154   - {
155   - validator: async (_: any, names: any) => {
156   - if (!names || names.length < 1) {
157   - return Promise.reject(new Error('至少配置一个保险公司名称'));
158   - }
159   - },
160   - },
161   - ]}
  140 + <FormItem
  141 + noStyle
  142 + shouldUpdate={(prev, curr) => {
  143 + if (prev.id !== curr.id) {
  144 + const insurName = form.getFieldValue('id') ? form.getFieldValue('id').label : '';
  145 + form.setFieldValue('orderInsurerNames', [insurName]);
  146 + }
  147 + return prev.id !== curr.id;
  148 + }}
162 149 >
163   - {(fields, { add, remove }, { errors }) => (
164   - <>
165   - {fields.map((field, index) => (
166   - <Form.Item
167   - {...(index === 0 ? formItemLayout : formItemLayoutWithOutLabel)}
168   - label={index === 0 ? '保单上保险公司名称' : ''}
169   - required
170   - key={field.key}
171   - >
172   - <Form.Item
173   - {...field}
174   - validateTrigger={['onChange', 'onBlur']}
175   - rules={[
176   - {
177   - required: true,
178   - whitespace: true,
179   - message: '请输入',
180   - },
181   - ]}
182   - noStyle
183   - >
184   - <Input placeholder="请输入保单上保险公司名称" allowClear style={{ width: '80%' }} />
185   - </Form.Item>
186   - {fields.length > 1 ? <MinusCircleOutlined style={{ marginLeft: 10 }} onClick={() => remove(field.name)} /> : null}
187   - </Form.Item>
188   - ))}
189   - <Row justify="center">
190   - <Button icon={<PlusOutlined />} style={{ width: '50%' }} type="dashed" onClick={() => add()}>
191   - 添加保单上保险公司名称
192   - </Button>
193   - </Row>
194   - {errors.length > 0 && (
195   - <Form.Item {...formItemLayoutWithOutLabel} style={{ marginBottom: 0 }}>
196   - <Form.ErrorList errors={errors} />
197   - </Form.Item>
198   - )}
199   - </>
200   - )}
201   - </Form.List>
  150 + {() => {
  151 + return (
  152 + <Form.List
  153 + name="orderInsurerNames"
  154 + rules={[
  155 + {
  156 + validator: async (_: any, names: any) => {
  157 + if (!names || names.length < 1) {
  158 + return Promise.reject(new Error('至少配置一个保险公司名称'));
  159 + }
  160 + },
  161 + },
  162 + ]}
  163 + >
  164 + {(fields, { add, remove }, { errors }) => (
  165 + <>
  166 + {fields.map((field, index) => (
  167 + <Form.Item
  168 + {...(index === 0 ? formItemLayout : formItemLayoutWithOutLabel)}
  169 + label={index === 0 ? '保单上保险公司名称' : ''}
  170 + required
  171 + key={field.key}
  172 + >
  173 + <Form.Item
  174 + {...field}
  175 + validateTrigger={['onChange', 'onBlur']}
  176 + rules={[
  177 + {
  178 + required: true,
  179 + whitespace: true,
  180 + message: '请输入',
  181 + },
  182 + ]}
  183 + noStyle
  184 + >
  185 + <Input placeholder="请输入保单上保险公司名称" allowClear style={{ width: '80%' }} />
  186 + </Form.Item>
  187 + {fields.length > 1 ? <MinusCircleOutlined style={{ marginLeft: 10 }} onClick={() => remove(field.name)} /> : null}
  188 + </Form.Item>
  189 + ))}
  190 + <Row justify="center">
  191 + <Button icon={<PlusOutlined />} style={{ width: '50%' }} type="dashed" onClick={() => add()}>
  192 + 添加保单上保险公司名称
  193 + </Button>
  194 + </Row>
  195 + {errors.length > 0 && (
  196 + <Form.Item {...formItemLayoutWithOutLabel} style={{ marginBottom: 0 }}>
  197 + <Form.ErrorList errors={errors} />
  198 + </Form.Item>
  199 + )}
  200 + </>
  201 + )}
  202 + </Form.List>
  203 + );
  204 + }}
  205 + </FormItem>
202 206 </Form>
203 207 </Spin>
204 208 </Modal>
... ...
src/pages/carinsur/companyConfig/index.tsx
... ... @@ -2,13 +2,12 @@ import React, { useState } from &#39;react&#39;;
2 2 import zhCN from 'antd/lib/locale-provider/zh_CN';
3 3 import { PageHeaderWrapper } from '@ant-design/pro-layout';
4 4 import { PlusOutlined } from '@ant-design/icons';
5   -import { Button, Card, Popconfirm, Row, Divider, Select, Table, ConfigProvider, message } from 'antd';
  5 +import { Button, Card, Popconfirm, Row, Divider, Table, ConfigProvider, message } from 'antd';
6 6 import CreateModal from './components/CreateModal';
7 7 import usePagination from '@/hooks/usePagination';
8 8 import Search from 'antd/lib/input/Search';
9 9 import * as api from './api';
10 10 import debounce from 'lodash/debounce';
11   -import useInitial from '@/hooks/useInitail';
12 11 import TextWithMore from '@/components/TextWithMore';
13 12  
14 13 const indexS = require('./index.less');
... ... @@ -19,7 +18,6 @@ export default function WorkStationIndex() {
19 18 const [visible, setVisible] = useState(false);
20 19 const [item, setItem] = useState<ComInsur.ItemList>({});
21 20 const { loading, setLoading, list, setParams, paginationConfig } = usePagination(api.getInsurList, {});
22   - const { data: brands } = useInitial(api.getTypeList, [], {});
23 21  
24 22 function deleteData(id?: number) {
25 23 api
... ... @@ -49,21 +47,6 @@ export default function WorkStationIndex() {
49 47 style={{ width: 250, marginRight: 20 }}
50 48 onChange={(e) => _onChange({ name: e.target.value })}
51 49 />
52   - <Select
53   - allowClear
54   - placeholder="类型"
55   - style={{ width: 250 }}
56   - onChange={(value) => _onChange({ brand: value })}
57   - showSearch
58   - optionFilterProp="children"
59   - >
60   - {brands.length > 0 &&
61   - brands.map((item: string, index) => (
62   - <Select.Option key={index} value={item}>
63   - {item}
64   - </Select.Option>
65   - ))}
66   - </Select>
67 50 </Row>
68 51 <Button
69 52 icon={<PlusOutlined />}
... ... @@ -85,7 +68,6 @@ export default function WorkStationIndex() {
85 68 <TextWithMore title="保单上保险公司名称" dataIndex="companyName" list={names.map((i) => ({ companyName: i }))} unit="个名称" />
86 69 )}
87 70 />
88   - <Column width="8%" title="类型" dataIndex="brand" />
89 71 <Column
90 72 width="10%"
91 73 title="送修差额是否包含驾意险"
... ... @@ -125,7 +107,6 @@ export default function WorkStationIndex() {
125 107 </Card>
126 108 <CreateModal
127 109 visible={visible}
128   - brands={brands}
129 110 item={item}
130 111 onCancel={() => setVisible(false)}
131 112 onRrfreshing={() => {
... ...
src/pages/carinsur/companyConfig/interface.d.ts
... ... @@ -8,7 +8,6 @@ declare namespace ComInsur {
8 8 interface ItemList {
9 9 id?: number; //保险公司id
10 10 name?: String; //保险公司名称
11   - brand?: String; //保险公司品牌
12 11 orderInsurerNames?: string; //保单保险公司名称
13 12 shortName?: string;
14 13 }
... ...
src/pages/carinsur/insureSaleconfig/components/CreateModal.tsx
... ... @@ -63,10 +63,17 @@ export default function CreateModal({ onCancel, record, onRrfreshing, visible }:
63 63 }
64 64  
65 65 return (
66   - <Modal visible={visible} title={record.insurerId ? '编辑' : '新增'} onOk={handleOk} onCancel={() => onCancel()} confirmLoading={firmLoading}>
  66 + <Modal
  67 + width={700}
  68 + open={visible}
  69 + title={record.insurerId ? '编辑' : '新增'}
  70 + onOk={handleOk}
  71 + onCancel={() => onCancel()}
  72 + confirmLoading={firmLoading}
  73 + >
67 74 <Form
68   - labelCol={{ span: 8 }}
69   - wrapperCol={{ span: 12 }}
  75 + labelCol={{ span: 6 }}
  76 + wrapperCol={{ span: 18 }}
70 77 form={form}
71 78 onFinish={handleOk}
72 79 initialValues={{
... ...
src/pages/coupon/CouponConfig/components/WareLimitType/VehicleLimit.tsx 0 → 100644
  1 +import React, { useEffect, useState } from 'react';
  2 +import { Button, Popconfirm, Card, Table, Modal, Form, Select, Space, message, Radio, Tag } from 'antd';
  3 +import * as api from '../../api';
  4 +import TextWithMore from '@/components/TextWithMore';
  5 +import SelectorWithFull from '@/components/SelectorWithFull';
  6 +interface Props {
  7 + onChange?: (value: any[]) => any;
  8 + form: any;
  9 + value?: any;
  10 + readonly: boolean;
  11 + carSelectApi?: Function;
  12 +}
  13 +
  14 +interface Item {
  15 + label: string;
  16 + value: number;
  17 + key?: number;
  18 +}
  19 +const { Column } = Table;
  20 +const { Option } = Select;
  21 +
  22 +export default function Index({ onChange, value, readonly, carSelectApi }: Props) {
  23 + const [form] = Form.useForm();
  24 + // 控制Modal是否可见
  25 + const [visible, setVisible] = useState<boolean>(false);
  26 + //存储表格当前编辑项数据
  27 + const [currentItem, setCurrentItem] = useState<any>({});
  28 + //存储Modal中可选的车系
  29 + const [series, setSeries] = useState<any>([]);
  30 + //存储接口返回的车型
  31 + const [specList, setSpecList] = useState<any>([]);
  32 + //存储Modal选择的品牌和车系作为表格的数据源
  33 + const [savaData, setSavadata] = useState<any>([]);
  34 + /**车型类型是否允许编辑 */
  35 + const [authType, setAuthType] = useState<boolean>();
  36 +
  37 + useEffect(() => {
  38 + if (value && value.length) {
  39 + setSavadata([...value]);
  40 + }
  41 + }, [value]);
  42 +
  43 + const [carList, setCarList] = useState<any[]>([]);
  44 +
  45 + useEffect(() => {
  46 + async function fetchData() {
  47 + try {
  48 + const queryApi = carSelectApi || api.getSpecTree;
  49 + const { data = [] } = await queryApi();
  50 + setCarList(data);
  51 + } catch (e) {
  52 + message.error(e.message);
  53 + }
  54 + }
  55 + fetchData();
  56 + }, []);
  57 +
  58 + const _onOk = () => {
  59 + form
  60 + .validateFields()
  61 + .then((fileds) => {
  62 + const { brand, series, spec, type } = fileds;
  63 + const _specs =
  64 + spec &&
  65 + spec.map((item: Item) => ({
  66 + specId: item.value,
  67 + specName: item.label,
  68 + }));
  69 + const params = {
  70 + brandId: brand.value,
  71 + brandName: brand.label,
  72 + seriesId: series.value,
  73 + seriesName: series.label,
  74 + spec: _specs,
  75 + authType: type,
  76 + };
  77 + // 编辑
  78 + if (savaData.some((i) => i.seriesId === series.value)) {
  79 + const tempData = savaData.map((item: any) => (item.seriesId === currentItem.seriesId ? params : item));
  80 + setSavadata([...tempData]);
  81 + onChange && onChange(tempData);
  82 + } else {
  83 + const newData = savaData.concat(params);
  84 + setSavadata([...newData]);
  85 + onChange && onChange(newData);
  86 + }
  87 + setVisible(false);
  88 + })
  89 + .catch((err) => console.log(err.message));
  90 + };
  91 +
  92 + // 选择部分车辆表格==》删除车系
  93 + const onDelete = (record: any) => {
  94 + const _savaData = savaData.filter((item: any) => item.seriesId != record.seriesId);
  95 + setSavadata([..._savaData]);
  96 + onChange && onChange(_savaData);
  97 + };
  98 +
  99 + // 编辑车辆
  100 + const onSelectSpec = async (record: any) => {
  101 + form.setFieldsValue({
  102 + ...record,
  103 + brand: { value: record.brandId, label: record.brandName },
  104 + series: { value: record.seriesId, label: record.seriesName },
  105 + spec: record.spec && record.spec.map((i) => ({ value: i.specId, label: i.specName })),
  106 + type: Number(!!record.authType),
  107 + });
  108 + const currentBrands = carList.filter((item: { value: number }) => item.value == record.brandId);
  109 + const tempData = currentBrands.length ? currentBrands[0].children.filter((item: { value: number }) => item.value == record.seriesId) : [];
  110 + const currentSpec = tempData.length ? tempData[0].children : [];
  111 + setSpecList(currentSpec);
  112 + setAuthType(tempData[0] && tempData[0].all != null ? !!tempData[0].all : true);
  113 + setCurrentItem(record);
  114 + setVisible(true);
  115 + };
  116 +
  117 + return (
  118 + <>
  119 + <Card>
  120 + <div style={{ display: 'flex', justifyContent: 'flex-end' }}>
  121 + {!readonly && (
  122 + <Button
  123 + type="link"
  124 + disabled={readonly}
  125 + onClick={() => {
  126 + setVisible(true);
  127 + form.resetFields();
  128 + }}
  129 + >
  130 + 新增
  131 + </Button>
  132 + )}
  133 + </div>
  134 +
  135 + <Table dataSource={value} rowKey={(record) => String(record.seriesId)} bordered>
  136 + <Column
  137 + title="品牌"
  138 + dataIndex="brandName"
  139 + key="brandName"
  140 + filters={carList.map((item) => ({
  141 + text: item.name,
  142 + value: item.value,
  143 + }))}
  144 + onFilter={(value, record: any) => {
  145 + return record.brandName.indexOf(value) === 0;
  146 + }}
  147 + />
  148 + <Column title="车系" dataIndex="seriesName" key="seriesName" />
  149 + <Column
  150 + title="车型"
  151 + dataIndex="spec"
  152 + key="spec"
  153 + render={(_, record: any) =>
  154 + record.spec && record.spec.length ? (
  155 + <TextWithMore title="车型" dataIndex="specName" list={record.spec} />
  156 + ) : (
  157 + <Tag>全部车型</Tag>
  158 + )
  159 + }
  160 + />
  161 + {!readonly && (
  162 + <Column
  163 + title="操作"
  164 + key="operation"
  165 + render={(_, record) => (
  166 + <Space>
  167 + <Button type="link" style={{ padding: 0 }} onClick={() => onSelectSpec(record)}>
  168 + 编辑
  169 + </Button>
  170 + <Popconfirm title="确定删除?" okText="确定" style={{ padding: 0 }} cancelText="取消" onConfirm={() => onDelete(record)}>
  171 + <Button type="link" danger>
  172 + 删除
  173 + </Button>
  174 + </Popconfirm>
  175 + </Space>
  176 + )}
  177 + />
  178 + )}
  179 + </Table>
  180 + </Card>
  181 +
  182 + {/* 选择品牌和车系 */}
  183 + <Modal
  184 + title="选择车辆"
  185 + maskClosable={false}
  186 + visible={visible}
  187 + onOk={() => form.submit()}
  188 + onCancel={() => {
  189 + setVisible(false);
  190 + }}
  191 + afterClose={() => {
  192 + form.resetFields();
  193 + setCurrentItem({});
  194 + }}
  195 + >
  196 + <Form onFinish={_onOk} form={form}>
  197 + <Form.Item label="品牌" name="brand" rules={[{ required: true, message: '请选择品牌:' }]}>
  198 + <Select
  199 + labelInValue
  200 + disabled={!!currentItem.brandId}
  201 + placeholder="请选择品牌"
  202 + onChange={(v) => {
  203 + form.resetFields(['series']);
  204 + const selectedSeries = savaData.map((item: { seriesId: number }) => item.seriesId);
  205 + const tempData = carList?.filter((item) => item.value === v.value);
  206 + setSeries(tempData.length ? tempData[0].children.filter((i: any) => !selectedSeries.includes(i.value)) : []);
  207 + }}
  208 + >
  209 + {carList.map((item) => (
  210 + <Option value={item.value} key={item.value}>
  211 + {item.label}
  212 + </Option>
  213 + ))}
  214 + </Select>
  215 + </Form.Item>
  216 + <Form.Item label="车系" name="series" rules={[{ required: true, message: '请选择车系' }]}>
  217 + <Select
  218 + labelInValue
  219 + disabled={!!currentItem.brandId}
  220 + placeholder="请选择车系"
  221 + allowClear
  222 + onChange={(v) => {
  223 + form.resetFields(['spec']);
  224 + const tempData = series?.filter((item: { value: number }) => item.value === v.value);
  225 + const currentSpec = tempData.length ? tempData[0].children : [];
  226 + setSpecList(currentSpec);
  227 + currentSpec.length && form.setFieldValue('type', Number(tempData[0].all));
  228 + setAuthType(tempData[0].all != null ? !!tempData[0].all : true);
  229 + }}
  230 + >
  231 + {series.map((item: any) => (
  232 + <Option value={item.value} key={item.value}>
  233 + {item.label}
  234 + </Option>
  235 + ))}
  236 + </Select>
  237 + </Form.Item>
  238 + <Form.Item label="车型范围" name="type" rules={[{ required: true, message: '请选择' }]}>
  239 + <Radio.Group disabled={readonly || !authType}>
  240 + <Radio value={1}>全部</Radio>
  241 + <Radio value={0}>部分</Radio>
  242 + </Radio.Group>
  243 + </Form.Item>
  244 + <Form.Item noStyle shouldUpdate={(prevValues, currentValues) => prevValues.type != currentValues.type}>
  245 + {({ getFieldValue }): any => {
  246 + const type = getFieldValue('type');
  247 + if (!type) {
  248 + return (
  249 + <Form.Item label="车型" name="spec" rules={[{ required: true, message: '请选择车型' }]}>
  250 + <SelectorWithFull
  251 + data={specList}
  252 + allowClear
  253 + labelInValue
  254 + multiple
  255 + showSearch
  256 + fieldKeyNames={{ keyName: 'value', valueName: 'value', labelName: 'label' }}
  257 + treeNodeFilterProp="label"
  258 + />
  259 + </Form.Item>
  260 + );
  261 + }
  262 + }}
  263 + </Form.Item>
  264 + </Form>
  265 + </Modal>
  266 + </>
  267 + );
  268 +}
... ...
src/pages/coupon/CouponConfig/components/WareLimitType/index.tsx
... ... @@ -2,6 +2,7 @@ import React, { useState } from &#39;react&#39;;
2 2 import { Form, Input, Radio, Space } from 'antd';
3 3 import PartItem from '../UsesceneItems/PartLimit';
4 4 import SpecLimit from './SpecLimit';
  5 +import VehicleLimit from './VehicleLimit';
5 6 import VinLimit from './VinLimit';
6 7 import CarCodeLimit from './CarCodeLimit';
7 8 import DecorationPart from './decorationPart';
... ... @@ -32,7 +33,8 @@ const WareLimitType = (props: Props) =&gt; {
32 33 switch (key) {
33 34 case '1':
34 35 if (limitType == LimitTypeEnum['限制']) {
35   - return <SpecLimit readonly={editDisabled} form={form} carSelectApi={carSelectApi} />;
  36 + // return <SpecLimit readonly={editDisabled} form={form} carSelectApi={carSelectApi} />;
  37 + return <VehicleLimit readonly={editDisabled} form={form} carSelectApi={carSelectApi} />;
36 38 }
37 39 if (limitType == LimitTypeEnum['指定VIN限制']) {
38 40 return <VinLimit disabled={editDisabled} form={form} carSelectApi={carSelectApi} />;
... ...
src/pages/coupon/CouponConfig/entity.ts
... ... @@ -27,7 +27,7 @@ export enum LimitTypeEnum {
27 27 '不限制' = 0,
28 28 '限制',
29 29 '发券方限制',
30   - '指定VIN限制'
  30 + '指定VIN限制',
31 31 }
32 32 export const WorkTypeEnum = {
33 33 jxyhq: 1, //机修
... ... @@ -83,3 +83,47 @@ export const UseSceneItems = {
83 83 '7': '购车方式',
84 84 '8': '整车型号代码',
85 85 };
  86 +
  87 +/**车辆限制保存数据转换 */
  88 +export function carLimitTransfer(schemeList: MktConponSpace.LimitList[]) {
  89 + const carLimit: any[] = schemeList.filter((i) => i.limitType == 1 && i.schemeType == 1);
  90 + if (!carLimit.length) {
  91 + return schemeList;
  92 + }
  93 + const car: { applyValue: number; applyName: number; applyType: number }[] = [];
  94 + carLimit[0].valueList.forEach((i) => {
  95 + if (i.authType) {
  96 + car.push({ applyValue: i.seriesId, applyName: i.seriesName, applyType: 12 });
  97 + } else {
  98 + i.spec.forEach((s) => {
  99 + car.push({ applyValue: s.specId, applyName: s.specName, applyType: 13 });
  100 + });
  101 + }
  102 + });
  103 + const newLimits = schemeList.map((scheme) => (scheme.limitType == 1 && scheme.schemeType == 1 ? { ...scheme, valueList: car } : scheme));
  104 + return newLimits;
  105 +}
  106 +
  107 +/**车辆限制数据初始化*/
  108 +export function initCarLimit(data: MktConponSpace.ConListParams) {
  109 + if (!data.vehicleSchemeList || !data.vehicleSchemeList.length) {
  110 + return data.schemeList;
  111 + }
  112 + const carLimit: any[] = [];
  113 + data.vehicleSchemeList.forEach((item) => {
  114 + const newItem = item.secondaryList && item.secondaryList.map(i => ({
  115 + brandId: item.applyValue,
  116 + brandName: item.applyName,
  117 + seriesId: Number(i.applyValue),
  118 + seriesName: i.applyName,
  119 + authType: Number(i.all) || 0,
  120 + spec:
  121 + i.secondaryList
  122 + ? i.secondaryList.map((i) => ({ specId: Number(i.applyValue), specName: i.applyName }))
  123 + : [],
  124 + }))
  125 + carLimit.push(...newItem);
  126 + });
  127 + const newLimits = data.schemeList.map((scheme) => (scheme.limitType == 1 && scheme.schemeType == 1 ? { ...scheme, valueList: carLimit } : scheme));
  128 + return newLimits;
  129 +}
... ...
src/pages/coupon/CouponConfig/index.tsx
... ... @@ -2,6 +2,7 @@ import FullReduce from &#39;@/pages/coupon/CouponConfig/components/FullReduce&#39;;
2 2 import { Card, Form, Modal, Spin, message } from 'antd';
3 3 import React, { useEffect, useState } from 'react';
4 4 import * as api from './api';
  5 +import { carLimitTransfer, initCarLimit } from './entity';
5 6  
6 7 type Props = MktConponSpace.CouponProps;
7 8  
... ... @@ -26,6 +27,7 @@ export default function UpsertCoupon(
26 27 detailApi(no)
27 28 .then((res) => {
28 29 const data = res.data || ({} as any);
  30 + const schemeList = initCarLimit(data);
29 31 setInfo(data);
30 32 form.setFieldsValue({
31 33 ...data,
... ... @@ -34,6 +36,7 @@ export default function UpsertCoupon(
34 36 exchangeTypeId: data.exchangeTypeId ? { value: data.exchangeTypeId, label: data.exchangeTypeName } : undefined,
35 37 orgList: data.orgList ? data.orgList.map((i) => ({ value: i.orgId, label: i.orgName })) : undefined,
36 38 amsCode: data.amsCode ? [{ code: data.amsCode, name: data.amsName, spec: data.amsSpec }] : undefined,
  39 + schemeList,
37 40 });
38 41 setLoading(false);
39 42 })
... ... @@ -69,6 +72,7 @@ export default function UpsertCoupon(
69 72 id: info.id,
70 73 confNo: info.confNo,
71 74 ...fields,
  75 + schemeList: carLimitTransfer(fields.schemeList),
72 76 orgList: fields.orgList ? fields.orgList.map((i: { value: any; label: any }) => ({ orgId: i.value, orgName: i.label })) : [],
73 77 classifyCode: fields.classifyCode.value,
74 78 classifyName: fields.classifyCode.label,
... ...
src/pages/coupon/CouponConfig/interface.d.ts
... ... @@ -117,9 +117,11 @@ declare namespace MktConponSpace {
117 117 schemeList: LimitList[]; //限制规则
118 118 discountsType?: number; //折扣类型 1立减2兑换
119 119 giftItemId?: number;
  120 + vehicleSchemeList?: vehicleLimit[]; //组转车辆使用限制规则
120 121 }
121 122  
122 123 interface LimitList {
  124 + limitType: number;
123 125 schemeType: number;
124 126 limitScheme: boolean;
125 127 externalRely: boolean;
... ... @@ -127,11 +129,19 @@ declare namespace MktConponSpace {
127 129 }
128 130  
129 131 interface LimitRuls {
  132 + authType?: number;
130 133 applyType: number;
131 134 applyName: string;
132 135 applyValue: number;
133 136 }
134 137  
  138 + interface vehicleLimit {
  139 + applyName: string;
  140 + applyValue: number;
  141 + applyType: number;
  142 + all: boolean;
  143 + secondaryList: vehicleLimit[]
  144 + }
135 145 interface SaveResult {
136 146 confNo: string; //配置编号
137 147 classifyCode: string; //优惠券分类码
... ...
src/pages/mkt/ActivityCreate/index.tsx
... ... @@ -52,11 +52,12 @@ const ActivityCreate = (props: Props) =&gt; {
52 52 if (current == index) {
53 53 s = 'process';
54 54 }
55   - return s;
  55 + return activityNo ? s : null;
56 56 }
57 57  
58 58 function _onChange(cur: number) {
59 59 if (!activityNo && !baseInfo.activityNo) {
  60 + message.info("请先配置活动基本信息")
60 61 return;
61 62 }
62 63 if (cur < current) {
... ...
src/pages/order3/AddValueTaskConfig/components/InsureEditModal.tsx
... ... @@ -2,6 +2,7 @@ import React, { useState, useEffect } from &#39;react&#39;;
2 2 import { Modal, Form, Select, InputNumber, Row, Space, Button, Card, message } from 'antd';
3 3 import { useStore } from '../index';
4 4 import { InsureBuyRequireList, getBrandOrSeriesApi, CarResult } from '../api';
  5 +import { isEmpty } from 'lodash';
5 6  
6 7 interface DataItem {
7 8 label?: string;
... ... @@ -14,6 +15,7 @@ export default function Index() {
14 15 const [seriesData, setSeriesData] = useState<CarResult[]>([]);
15 16 const [loading, setLoading] = useState<boolean>(false);
16 17 const [seriesList, setSeriesList] = useState<DataItem[]>([]);
  18 + const [seriesLoading, setSeriesLoading] = useState<boolean>(false);
17 19 const [form] = Form.useForm();
18 20  
19 21 useEffect(() => {
... ... @@ -51,7 +53,11 @@ export default function Index() {
51 53 }
52 54  
53 55 function handleSelectSeries(options: any) {
54   - setSeriesList(options);
  56 + setSeriesList(options?.filter((v: any) => !isEmpty(v)));
  57 + form.setFieldValue(
  58 + 'series',
  59 + options?.filter((v: any) => !isEmpty(v)),
  60 + );
55 61 }
56 62  
57 63 function afterClose() {
... ... @@ -69,14 +75,24 @@ export default function Index() {
69 75 }
70 76  
71 77 function handleSelectBrand(brandId?: number) {
  78 + setSeriesLoading(true);
72 79 getBrandOrSeriesApi({ shopIds, brandId })
73 80 .then((res) => {
74 81 setSeriesData(res.data || []);
  82 + setSeriesLoading(false);
75 83 })
76 84 .catch((err) => {
77 85 message.error(err.message);
78 86 setSeriesData([]);
79   - });
  87 + setSeriesLoading(false);
  88 + }).finally(() => {
  89 + setSeriesLoading(false);
  90 + })
  91 + }
  92 +
  93 + function handleBrandChange(brandId?: number) {
  94 + handleSelectBrand(brandId);
  95 + form.setFieldValue('series', undefined);
80 96 }
81 97  
82 98 async function handleOk() {
... ... @@ -128,7 +144,7 @@ export default function Index() {
128 144 <Form form={form}>
129 145 <Form.Item label="品牌" name="brand" rules={[{ required: true, message: '请选择品牌!' }]}>
130 146 <Select
131   - onChange={(value) => handleSelectBrand(value.value)}
  147 + onChange={(value) => handleBrandChange(value.value)}
132 148 labelInValue
133 149 allowClear
134 150 showSearch
... ... @@ -147,6 +163,8 @@ export default function Index() {
147 163 filterOption
148 164 optionFilterProp="children"
149 165 mode="multiple"
  166 + loading={seriesLoading}
  167 + disabled={seriesLoading}
150 168 options={seriesData.map((v) => ({ label: v.dataName, value: v.dataId }))}
151 169 placeholder="请选择"
152 170 />
... ...
src/pages/order3/CarPurchaseSubsidy/AdditionalPurchaseSubsidy/components/List.tsx
... ... @@ -62,7 +62,7 @@ export default function List({ status }: Props) {
62 62 function renderOption(status: number, id: number, preId?: number, approveNumber?: string) {
63 63 if (status === 5 || status === 30) {
64 64 return (
65   - <Space>
  65 + <Space wrap>
66 66 <Popconfirm
67 67 title="是否删除?"
68 68 onConfirm={() => handleDeleteConfig(id)}
... ... @@ -84,7 +84,7 @@ export default function List({ status }: Props) {
84 84 );
85 85 } else if ([10, 20].includes(status) && approveNumber) {
86 86 return (
87   - <Space>
  87 + <Space wrap>
88 88 <Popconfirm
89 89 title="是否撤销审批?"
90 90 onConfirm={() => handleCancleApproval(approveNumber)}
... ... @@ -98,7 +98,7 @@ export default function List({ status }: Props) {
98 98 );
99 99 } else if (status === 15) {
100 100 return (
101   - <Space><a onClick={() => handleEdit(true, id)} style={{color: "#448EF7" }}>编辑</a>
  101 + <Space wrap><a onClick={() => handleEdit(true, id)} style={{color: "#448EF7" }}>编辑</a>
102 102 <Popconfirm
103 103 title="是否禁用"
104 104 onConfirm={() => handleApplyDisableOrEnabl(id, 2)}
... ... @@ -186,7 +186,8 @@ export default function List({ status }: Props) {
186 186 width={220}
187 187 render={(_text, record: any) => (
188 188 <>
189   - <Tooltip placement="topLeft" color="#FFF" title={<span style={{color: "#666"}}>{record.beginTime && moment(record.beginTime).format("YYYY-MM-DD HH:mm:ss")}至{record.endTime && moment(record.endTime).format("YYYY-MM-DD HH:mm:ss")}</span>}>
  189 + <Tooltip placement="topLeft" color="#FFF" title={<span style={{color: "#666"}}>
  190 + {record.beginTime && moment(record.beginTime).format("YYYY-MM-DD HH:mm:ss")}至{record.endTime && moment(record.endTime).format("YYYY-MM-DD HH:mm:ss")}</span>}>
190 191 <span>{record.beginTime && moment(record.beginTime).format("YYYY-MM-DD HH:mm:ss")}至{record.endTime && moment(record.endTime).format("YYYY-MM-DD HH:mm:ss")}</span>
191 192 </Tooltip>
192 193 </>
... ... @@ -209,7 +210,13 @@ export default function List({ status }: Props) {
209 210 align="left"
210 211 dataIndex="subsidyDataTips"
211 212 width={180}
212   - render={(_text, record: any) => <a style={{color: "#4189FD"}} onClick={() => onShow({subsidyDataUploadApply: record.subsidyDataUploadApply, subsidyDataUploadNDay: record.subsidyDataUploadNDay, deliveryAfterDays: record.deliveryAfterDays}, "补贴资料")}>{_text}</a>}
  213 + render={(_text, record: any) =>
  214 + <a
  215 + style={{color: "#4189FD"}}
  216 + onClick={() => onShow({
  217 + subsidyDataUploadApply: record.subsidyDataUploadApply,
  218 + subsidyDataUploadNDay: record.subsidyDataUploadNDay,
  219 + deliveryAfterDays: record.deliveryAfterDays}, "补贴资料")}>{_text}</a>}
213 220 />
214 221 <Column
215 222 title="状态"
... ... @@ -222,11 +229,11 @@ export default function List({ status }: Props) {
222 229 align="left"
223 230 render={(_text, record: any) => {
224 231 return (
225   - <Space>
  232 + <>
226 233 {
227 234 renderOption(record.status, record.id, record.preId, record.approveNumber)
228 235 }
229   - </Space>
  236 + </>
230 237 );
231 238 }}
232 239 />
... ...
src/pages/order3/CarPurchaseSubsidy/KeyCustomerSubsidy/components/List.tsx
... ... @@ -62,7 +62,7 @@ export default function List({status = 1}: Props) {
62 62 function renderOption(status: number, id: number, preId?: number, approveNumber?: string) {
63 63 if (status === 5 || status === 30) {
64 64 return (
65   - <Space>
  65 + <Space wrap>
66 66 <Popconfirm
67 67 title="是否删除?"
68 68 onConfirm={() => handleDeleteConfig(id)}
... ... @@ -84,7 +84,7 @@ export default function List({status = 1}: Props) {
84 84 );
85 85 } else if ([10, 20].includes(status) && approveNumber) {
86 86 return (
87   - <Space>
  87 + <Space wrap>
88 88 <Popconfirm
89 89 title="是否撤销审批?"
90 90 onConfirm={() => handleCancleApproval(approveNumber)}
... ... @@ -98,7 +98,7 @@ export default function List({status = 1}: Props) {
98 98 );
99 99 } else if (status === 15) {
100 100 return (
101   - <Space><a onClick={() => handleEdit(true, id)} style={{color: "#448EF7" }}>编辑</a>
  101 + <Space wrap><a onClick={() => handleEdit(true, id)} style={{color: "#448EF7" }}>编辑</a>
102 102 <Popconfirm
103 103 title="是否禁用"
104 104 onConfirm={() => handleApplyDisableOrEnabl(id, 2)}
... ... @@ -191,7 +191,8 @@ export default function List({status = 1}: Props) {
191 191 width={220}
192 192 render={(_text, record: any) => (
193 193 <>
194   - <Tooltip placement="topLeft" color="#FFF" title={<span style={{color: "#666"}}>{record.beginTime && moment(record.beginTime).format("YYYY-MM-DD HH:mm:ss")}至{record.endTime && moment(record.endTime).format("YYYY-MM-DD HH:mm:ss")}</span>}>
  194 + <Tooltip placement="topLeft" color="#FFF" title={<span style={{color: "#666"}}>
  195 + {record.beginTime && moment(record.beginTime).format("YYYY-MM-DD HH:mm:ss")}至{record.endTime && moment(record.endTime).format("YYYY-MM-DD HH:mm:ss")}</span>}>
195 196 <span>{record.beginTime && moment(record.beginTime).format("YYYY-MM-DD HH:mm:ss")}至{record.endTime && moment(record.endTime).format("YYYY-MM-DD HH:mm:ss")}</span>
196 197 </Tooltip>
197 198 </>
... ... @@ -201,10 +202,12 @@ export default function List({status = 1}: Props) {
201 202 title="大客户类型"
202 203 align="left"
203 204 dataIndex="customerTypeName"
204   - render={(_text, record:any) => (
  205 + render={(_text, record: any) => (
205 206 <>
206   - <Tooltip placement="topLeft" color="#FFF" title={<span style={{color: "#666"}}>{record.customerTypeNameList && record.customerTypeNameList.length && record.customerTypeNameList.join(",")}</span>}>
207   - <span style={{display: "block", maxWidth: 120, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap"}}>{record.customerTypeNameList && record.customerTypeNameList.length && record.customerTypeNameList.join(",")}</span>
  207 + <Tooltip placement="topLeft" color="#FFF" title={<span style={{color: "#666"}}>
  208 + {record.customerTypeNameList && record.customerTypeNameList.length && record.customerTypeNameList.join(",")}</span>}>
  209 + <span style={{display: "block", maxWidth: 120, overflow: "hidden", textOverflow: "ellipsis", whiteSpace: "nowrap"}}>
  210 + {record.customerTypeNameList && record.customerTypeNameList.length && record.customerTypeNameList.join(",")}</span>
208 211 </Tooltip>
209 212 </>
210 213 )}
... ... @@ -226,7 +229,13 @@ export default function List({status = 1}: Props) {
226 229 align="left"
227 230 dataIndex="subsidyDataTips"
228 231 width={180}
229   - render={(_text, record: any) => <a style={{color: "#4189FD"}} onClick={() => onShow({subsidyDataUploadApply: record.subsidyDataUploadApply, subsidyDataUploadNDay: record.subsidyDataUploadNDay, deliveryAfterDays: record.deliveryAfterDays}, "补贴资料")}>{_text}</a>}
  232 + render={(_text, record: any) =>
  233 + <a
  234 + style={{color: "#4189FD"}}
  235 + onClick={() => onShow({
  236 + subsidyDataUploadApply: record.subsidyDataUploadApply,
  237 + subsidyDataUploadNDay: record.subsidyDataUploadNDay,
  238 + deliveryAfterDays: record.deliveryAfterDays}, "补贴资料")}>{_text}</a>}
230 239 />
231 240 <Column
232 241 title="状态"
... ... @@ -239,11 +248,11 @@ export default function List({status = 1}: Props) {
239 248 align="left"
240 249 render={(_text, record: any) => {
241 250 return (
242   - <Space>
  251 + <>
243 252 {
244 253 renderOption(record.status, record.id, record.preId, record.approveNumber)
245 254 }
246   - </Space>
  255 + </>
247 256 );
248 257 }}
249 258 />
... ...
src/pages/order3/CarPurchaseSubsidy/ReplacementSubsidy/components/List.tsx
... ... @@ -62,7 +62,7 @@ export default function List({ status }: Props) {
62 62 function renderOption(status: number, id: number, preId?: number, approveNumber?: string) {
63 63 if (status === 5 || status === 30) {
64 64 return (
65   - <Space>
  65 + <Space wrap>
66 66 <Popconfirm title="是否删除?" onConfirm={() => handleDeleteConfig(id)} okText="确定" cancelText="取消">
67 67 <a style={{ color: '#999' }}>删除</a>
68 68 </Popconfirm>
... ... @@ -76,7 +76,7 @@ export default function List({ status }: Props) {
76 76 );
77 77 } else if ([10, 20].includes(status) && approveNumber) {
78 78 return (
79   - <Space>
  79 + <Space wrap>
80 80 <Popconfirm title="是否撤销审批?" onConfirm={() => handleCancleApproval(approveNumber)} okText="确定" cancelText="取消">
81 81 <a style={{ color: '#999' }}>撤销</a>
82 82 </Popconfirm>
... ... @@ -87,7 +87,7 @@ export default function List({ status }: Props) {
87 87 );
88 88 } else if (status === 15) {
89 89 return (
90   - <Space>
  90 + <Space wrap>
91 91 <a onClick={() => handleEdit(true, id)} style={{ color: '#448EF7' }}>
92 92 编辑
93 93 </a>
... ... @@ -238,7 +238,7 @@ export default function List({ status }: Props) {
238 238 title="操作"
239 239 align="left"
240 240 render={(_text, record: any) => {
241   - return <Space>{renderOption(record.status, record.id, record.preId, record.approveNumber)}</Space>;
  241 + return <>{renderOption(record.status, record.id, record.preId, record.approveNumber)}</>;
242 242 }}
243 243 />
244 244 </Table>
... ...
src/pages/order3/DeliverCentralGoals/components/InsureEditModal.tsx
... ... @@ -2,6 +2,7 @@ import React, { useState, useEffect } from &#39;react&#39;;
2 2 import { Modal, Form, Select, InputNumber, Row, Space, Button, Card, message } from 'antd';
3 3 import { useStore } from '../index';
4 4 import { InsureBuyRequireList, getBrandOrSeriesApi, CarResult } from '../api';
  5 +import { isEmpty } from 'lodash';
5 6  
6 7 interface DataItem {
7 8 label?: string;
... ... @@ -14,6 +15,7 @@ export default function Index() {
14 15 const [seriesData, setSeriesData] = useState<CarResult[]>([]);
15 16 const [loading, setLoading] = useState<boolean>(false);
16 17 const [seriesList, setSeriesList] = useState<DataItem[]>([]);
  18 + const [seriesLoading, setSeriesLoading] = useState<boolean>(false);
17 19 const [form] = Form.useForm();
18 20  
19 21 useEffect(() => {
... ... @@ -51,7 +53,11 @@ export default function Index() {
51 53 }
52 54  
53 55 function handleSelectSeries(options: any) {
54   - setSeriesList(options);
  56 + setSeriesList(options?.filter((v: any) => !isEmpty(v)));
  57 + form.setFieldValue(
  58 + 'series',
  59 + options?.filter((v: any) => !isEmpty(v)),
  60 + );
55 61 }
56 62  
57 63 function afterClose() {
... ... @@ -69,16 +75,27 @@ export default function Index() {
69 75 }
70 76  
71 77 function handleSelectBrand(brandId?: number) {
  78 + setSeriesLoading(true);
72 79 getBrandOrSeriesApi({ shopIds, brandId })
73 80 .then((res) => {
74 81 setSeriesData(res.data || []);
  82 + setSeriesLoading(false);
75 83 })
76 84 .catch((err) => {
77 85 message.error(err.message);
78 86 setSeriesData([]);
  87 + setSeriesLoading(false);
  88 + })
  89 + .finally(() => {
  90 + setSeriesLoading(false);
79 91 });
80 92 }
81 93  
  94 + function handleBrandChange(brandId?: number) {
  95 + handleSelectBrand(brandId);
  96 + form.setFieldValue('series', undefined);
  97 + }
  98 +
82 99 async function handleOk() {
83 100 const params = await form.validateFields();
84 101 if (
... ... @@ -128,7 +145,7 @@ export default function Index() {
128 145 <Form form={form}>
129 146 <Form.Item label="品牌" name="brand" rules={[{ required: true, message: '请选择品牌!' }]}>
130 147 <Select
131   - onChange={(value) => handleSelectBrand(value.value)}
  148 + onChange={(value) => handleBrandChange(value.value)}
132 149 labelInValue
133 150 allowClear
134 151 showSearch
... ... @@ -145,6 +162,8 @@ export default function Index() {
145 162 allowClear
146 163 showSearch
147 164 filterOption
  165 + loading={seriesLoading}
  166 + disabled={seriesLoading}
148 167 optionFilterProp="children"
149 168 mode="multiple"
150 169 options={seriesData.map((v) => ({ label: v.dataName, value: v.dataId }))}
... ...