ShowCountCard 功能迭代:新增周月对比属性,完善数据可视化场景

一、功能定位

该功能用于数据统计卡片的 "同比 / 环比比较展示",适配 "对话次数、发起需求、职位发布、平均时间、筛选次数、合格候选人、人才简历库"7 类核心业务数据,支持 "全部时间、周、月"3 种时间维度切换,最终在前端卡片中显示 "较上周 / 较上月" 的数值变化,并通过颜色区分数值正负,帮助用户快速识别数据趋势。

二、核心逻辑要点​

1.时间维度适配规则​

  • 当时间维度选择 "all(全部时间)" 时:不显示任何比较数据(返回空字符串);
  • 当选择 "week(本周)" 时:比较文案前缀固定为 "较上周";
  • 当选择 "month(本月)" 时:比较文案前缀固定为 "较上月"。

2.数据类型与单位匹配​

按数据类别区分数值类型、单位,确保展示符合业务认知:

|-------|-------------|-----|--------------|-------------|
| 数据类别 | 数据类型(比较值) | 单位 | 示例(正数) | 示例(负数) |
| 对话次数 | number | 次 | 较上周 +5 次 | 较上周 -3 次 |
| 发起需求 | number | 次 | 较上月 +5 次 | 较上周 -8 次 |
| 职位发布 | number | 个 | 较上周 +12 个 | 较上月 -30 个 |
| 平均时间 | string(含数值) | min | 较上周 +2.2 min | 较上周 -3.3min |
| 筛选次数 | number | 次 | 较上周 +6 次 | 较上周 -3 次 |
| 合格候选人 | number | 人 | 较上周 +1 人 | 较上月 -3 次 |
| 人才简历库 | number | 个 | 较上周 +5 个 | 较上周 -2 次 |

3.正负值处理规则​

  • 数值类型(前 3 类数据):
    • 大于 0:前缀加 "+"(如 "较上周 +3 次"),显示绿色(rgb (22, 163, 74));
    • 小于 0:直接显示负值(如 "较上周 -2 个"),显示红色(rgb (220, 38, 38));
    • 等于0:直接显示(如"较上周 0 次"),显示绿色(默认正向色)
  • 字符串类型(平均时间):
    • 先将字符串转为数值判断正负,正数加 "+",显示(如"较上周 + 2.5min"),负数直接显示(如字符串 "-2.5" 转为 - 2.5,显示 "较上周 -2.5min",显示红色(rgb (220, 38, 38))),0直接显示"较上周 0min"。

三、组件交互与实现

在ShowCountCard组件调用时,需传入对应 "数据类型标识":

复制代码
<Spin spinning={loading}>
                {coreData && <Flex gap={10}>
                  <ShowCountCard title="对话次数" countTitle={`${coreData.conversationCount || 0}次`} JDCompareNum={getCompareNum(coreData, 'conversation')} />
                  <ShowCountCard title="发起需求" countTitle={`${coreData.demandInitiatedCount || 0}次`} JDCompareNum={getCompareNum(coreData, 'demand')} />
                  <ShowCountCard title="职位发布" countTitle={`${coreData.positionPostedCount || 0}个`} JDCompareNum={getCompareNum(coreData, 'position')} />
                  <ShowCountCard title="平均时间" countTitle={`${coreData.averageTime || '0'}min`} JDCompareNum={getCompareNum(coreData, 'time')} />
                </Flex>}
              </Spin>

<Spin spinning={loading}>
                {coreData && <Flex gap={10}>
                  <ShowCountCard title="筛选次数" countTitle={`${coreData.screeningCount || 0}次`} resumeNum={getCompareNum(coreData, 'screening')} />
                  <ShowCountCard title="合格候选人" countTitle={`${coreData.qualifiedCandidateCount || 0}人`} resumeNum={getCompareNum(coreData, 'qualifiedCandidate')} />
                  <ShowCountCard title="人才库简历" countTitle={`${coreData.talentPoolResumeCount || 0}个`} resumeNum={getCompareNum(coreData, 'resume')} />
                  {/* <ShowCountCard title="面试汇总次数" countTitle={`${coreData.interviewSummariesTotal || 0}次`} /> */}
                </Flex>}
              </Spin>

数据传递 :通过getCompareNum函数返回包含text(显示文本)和color(颜色值)的对象

复制代码
const getCompareNum = (coreData: CoreModel, type: 'conversation' | 'demand' | 'position' | 'time' | 'screening' | 'qualifiedCandidate' | 'resume') => {
    if (dateType === "all") {
      return { text: "", color: "" };
    }

    let compareValue;
    let unit;
    switch (type) {
      case 'conversation':
        compareValue = coreData.conversationCountCompare;
        unit = '次';
        break;
      case 'demand':
        compareValue = coreData.demandInitiatedCountCompare;
        unit = '次';
        break;
      case 'position':
        compareValue = coreData.positionPostedCountCompare;
        unit = '个';
        break;
      case 'time':
        compareValue = coreData.averageTimeCompare;
        break;
      case 'screening':
        compareValue = coreData.screeningCountCompare;
        unit = '次';
        break;
      case 'qualifiedCandidate':
        compareValue = coreData.qualifiedCandidateCountCompare;
        unit = '个';
        break;
      case 'resume':
        compareValue = coreData.talentPoolResumeCountCompare;
        unit = '个';
        break;
      default:
        return { text: "", color: "" };
    }

    const timePrefix = dateType === "week" ? "较上周" : "较上月";
    let text = "";
    let isNegative = false;

    if (typeof compareValue === 'number') {
      text = compareValue > 0
        ? `${timePrefix} +${compareValue}${unit}`
        : `${timePrefix} ${compareValue}${unit}`;

      isNegative = compareValue < 0;
    }

    if (typeof compareValue === 'string') {
      const numValue = parseFloat(compareValue);
      if (!isNaN(numValue)) {
        text = numValue > 0
          ? `${timePrefix} +${compareValue}min`
          : `${timePrefix} ${compareValue}min`;

        isNegative = numValue < 0;
      }
    }

    const color = isNegative ? "rgb(220, 38, 38)" : "rgb(22, 163, 74)";

    return { text, color };
  };

组件渲染ShowCountCard组件接收上述对象,将颜色样式直接应用于比较数值元素

复制代码
import styles from './styles.module.scss'
import { Card, Flex, Progress } from 'antd';

const ShowCountCard = ({ title, countTitle, compare, processTitle, processSubtitle, processTitleNum, processSubtitleNum, percent, modelNum, modelMonthNum, JDCompareNum, resumeNum }: { title: string, countTitle?: string, compare?: string, processTitle?: string, processSubtitle?: string, processTitleNum?: number, processSubtitleNum?: number, percent?: number, modelNum?: string, modelMonthNum?: string, JDCompareNum?: { text: string, color: string },resumeNum?: { text: string, color: string }}) => {
    return (
        <Flex vertical justify='space-between' className={styles.showCountCard}>
            <div>{title}</div>
            {countTitle && <div className={styles.cardCount}>{countTitle}</div>}
            {compare && <div className={styles.cardCompare}>{compare}</div>}

            {modelNum && <div className={styles.cardModelNum}>{modelNum}</div>}
            {modelMonthNum && <div className={styles.cardModelMonthNum}>{modelMonthNum}</div>}

            {JDCompareNum && JDCompareNum.text && (
              <div 
                className={styles.cardJDCompareNum} 
                style={{ color: JDCompareNum.color }}
              >
                {JDCompareNum.text}
              </div>
            )}

            {resumeNum && resumeNum.text && (
              <div 
                className={styles.cardResumeNum} 
                style={{ color: resumeNum.color }}
              >
                {resumeNum.text}
              </div>
            )}

            {processTitle && <Flex justify='space-between' className={styles.progressBox}>
                <Flex gap={7} justify='flex-end' vertical className={styles.progressTitle}>
                    <Flex gap={14}>
                        <span>{processTitle}</span>
                        <span>{processTitleNum}</span>
                    </Flex>
                    <Flex gap={14}>
                        <span>{processSubtitle}</span>
                        <span style={{ color: '#D64595' }}>{processSubtitleNum}</span>
                    </Flex>
                </Flex>
                <Progress type="circle" percent={percent} size={56} strokeColor="#D64595" format={(percent)=> percent + '%'} />
            </Flex>}
        </Flex>
    )
}

export default ShowCountCard

四、示例效果

该功能通过清晰的规则定义和样式区分,使数据变化趋势一目了然,提升了统计信息的可读性和实用性。

相关推荐
IT_陈寒4 小时前
Redis性能翻倍的7个冷门技巧:从P5到P8都在偷偷用的优化策略!
前端·人工智能·后端
深空数字孪生4 小时前
从代码实现到概念创新:AIGC如何重塑数据可视化的价值链条?
信息可视化·aigc
Moonbit4 小时前
MoonBit Meetup 丨 手把手带你走进 AI 编程新世代
前端·后端·程序员
携欢4 小时前
PortSwigger靶场之 CSRF where token is not tied to user session通关秘籍
前端·csrf
HHHHHY5 小时前
使用阿里lowcode,手搓一个Carousel 走马灯容器组件
前端·react.js
小芒果nana5 小时前
React入门-JSX
react.js
用户352120195605 小时前
React-router v7
前端
Mintopia5 小时前
⚡ AI 时代,全栈 Next.js 开发的激情在哪里?
前端·aigc·全栈
Hello123网站5 小时前
300多个Html5小游戏列表和下载地址
前端·html·html5