From 581d9ee24deca14d376671343791eec6479fe725 Mon Sep 17 00:00:00 2001 From: fengxiang <361798944@qq.com> Date: Wed, 13 Dec 2023 14:54:40 +0800 Subject: [PATCH] =?UTF-8?q?feat():=20=E6=96=B0=E5=A2=9E=E5=AE=A2=E8=AF=89?= =?UTF-8?q?=E7=BB=9F=E8=AE=A1=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ComplainStat/echartsOptions.jsx | 563 +++++++++++++++++++++ .../CustomerServieMgm/ComplainStat/index.scss | 115 ++++- .../CustomerServieMgm/ComplainStat/loadable.jsx | 211 ++++++-- .../OperationCenter/CustomerManage/index.js | 19 +- 4 files changed, 848 insertions(+), 60 deletions(-) create mode 100644 src/pages/OperationCenter/CustomerServieMgm/ComplainStat/echartsOptions.jsx diff --git a/src/pages/OperationCenter/CustomerServieMgm/ComplainStat/echartsOptions.jsx b/src/pages/OperationCenter/CustomerServieMgm/ComplainStat/echartsOptions.jsx new file mode 100644 index 0000000..07a98aa --- /dev/null +++ b/src/pages/OperationCenter/CustomerServieMgm/ComplainStat/echartsOptions.jsx @@ -0,0 +1,563 @@ +const hexToRgba = (hex, alpha = 1) => { + const color = hex.slice(1); + const rgba = [ + parseInt("0x" + color.slice(0, 2)), + parseInt("0x" + color.slice(2, 4)), + parseInt("0x" + color.slice(4, 6)), + alpha, + ]; + return `rgba(${rgba.toString()})`; +}; +const colorList = [ + "#9baaff", + "#ff9ba7", + "#c8d6f4", + "#9bfff3", + "#ff9bf3", + "#eeb08e", + "#79d3fb", +]; +function linearColors1(color) { + return { + type: "linear", + x: 0, + y: 0, + x2: 0, + y2: 1, + colorStops: [ + { + offset: 0.3, + color: color, + }, + { + offset: 1, + color: "#fff", + }, + ], + global: false, + }; +} +function linearColors2(color) { + return { + type: "linear", + x: 0, + y: 0, + x2: 1, + y2: 1, + colorStops: [ + { + offset: 0, + color: hexToRgba(color, 1), + }, + { + offset: 1, + color: hexToRgba(color, 0.2), + }, + ], + global: false, + }; +} + +// 条形图 +export const barChartOption = (data) => { + const { x_axis = [], y_axis = [] } = data; + // console.log(data); + const len = parseInt(x_axis?.length || 0); + const zoomSpan = 100 / ((len < 8 ? 8 : len) / 8); + return { + backgroundColor: "transparent", + tooltip: { + trigger: "axis", + axisPointer: { + type: "shadow", + }, + backgroundColor: "rgba(0, 0, 0, 0.5)", + borderColor: "rgba(75, 253, 238, 0.4)", + textStyle: { color: "#FFFFFF", fontSize: 12 }, + }, + grid: { + top: 15, + left: "5%", + right: "3%", + bottom: len > 8 ? 25 : 15, + containLabel: true, + }, + // legend: { + // top: 0, + // right: "3%", + // itemWidth: 15, + // itemHeight: 10, + // textStyle: { color: "#999999", fontSize: 12 }, + // data: getLegend(y_axis), + // }, + dataZoom: [ + { + type: "inside", + // span: zoomSpan, + minSpan: zoomSpan, + maxSpan: zoomSpan, + zoomLock: true, + }, + { + type: "slider", + backgroundColor: "rgba(255,255,255,.3)", + dataBackground: { + areaStyle: "#fff", + }, + height: 9, + bottom: 15, + show: len > 8 ? true : false, + zoomLock: true, + moveHandleSize: 10, + handleIcon: + "path://M512 512m-208 0a6.5 6.5 0 1 0 416 0 6.5 6.5 0 1 0-416 0Z M512 192C335.264 192 192 335.264 192 512c0 176.736 143.264 320 320 320s320-143.264 320-320C832 335.264 688.736 192 512 192zM512 800c-159.072 0-288-128.928-288-288 0-159.072 128.928-288 288-288s288 128.928 288 288C800 671.072 671.072 800 512 800z", + handleColor: "#0260FF", + handleSize: "95%", + // handleStyle: { + // shadowBlur: 3, + // shadowOffsetX: 1, + // shadowOffsetY: 1, + // shadowColor: "rgba(0, 0, 0, 0.6)", + // }, + textStyle: { + fontSize: 12, + color: "#9A9A9A", + }, + showDetail: false, + }, + ], + xAxis: [ + { + // type: "value", + type: "category", + axisTick: { show: false }, + axisLine: { show: false }, + axisLabel: { fontSize: 12, color: "#FFFFFF", interval: 0 }, + data: x_axis || [], + }, + ], + yAxis: [ + { + axisTick: { show: false }, + axisLine: { + lineStyle: { + color: "#9A9A9A", + opacity: 0.6, + }, + }, + splitLine: { + lineStyle: { + opacity: 0.4, + }, + }, + axisLabel: { fontSize: 12, color: "#FFFFFF" }, + type: "value", + // type: "category", + }, + ], + series: renderBars(y_axis), + }; +}; + +// 生成条形 +function renderBars(arr) { + let newArr = []; + if (arr?.length) { + newArr = arr.map((v, i) => { + return { + type: "bar", + name: v?.name || "数量", + // yAxisIndex: 0, + // barMaxWidth: "auto", + // barCategoryGap:20, + barWidth: 20, + label: { + show: true, + position: "top", + formatter: function (params) { + let value = params.value || params.value != 0 ? params.value : ""; + return value; + }, + }, + itemStyle: { + color: i % 2 ? linearColors2("#fd9a9a") : linearColors2("#4a70f4"), + }, + data: v?.value || [], + }; + }); + } + return newArr; +} + +// 饼图 +export const pieChartOption = (data) => { + const list = [...data]; + const labels = list?.length ? list.map((v) => v.name) : []; + const option = { + color: colorList, + backgroundColor: "transparent", + tooltip: { + trigger: "item", + backgroundColor: "rgba(0, 0, 0, 0.5)", + textStyle: { color: "#FFFFFF", fontSize: 14 }, + }, + grid: { + top: "middle", + eft: "center", + containLabel: false, + }, + legend: { + orient: "horizontal", + top: "bottom", + left: "center", + data: labels, + type: "scroll", + // formatter(name) { + // let num = labels.findIndex((v) => v == name); + // return name + " {a|" + list[num]?.value + "}"; + // }, + // itemWidth: 10, + // itemHeight: 10, + textStyle: { + color: "#FFFFFF", + // width: 120, + // overflow: "breakAll", + // lineHeight: 16, + // rich: { + // a: { color: "#3f7ff7" }, + // }, + }, + // itemGap: 16, + // selectedMode: false + }, + series: [ + { + // id: "1", + type: "pie", + center: ["50%", "46%"], + radius: ["50%", "70%"], + data: list, + // z: 0, + // itemStyle: { + // color: ({ dataIndex }: any) => linearColors2[dataIndex % 20], + // }, + emphasis: { label: { show: true } }, + label: { + show: false, + position: "center", + // borderRadius: 10, + // backgroundColor: "#ffffff", + // width: "120", + // overflow: "breakAll", + formatter(param) { + // return "{num|" + param.value + "}" + "\n {name|" + param.name + "}"; + return ( + "{num|" + param.percent + "%}" + "\n {name|" + param.name + "}" + ); + }, + rich: { + num: { + fontSize: 16, + fontWeight: 500, + padding: [0, 0, 5], + color: "#3f7ff7", + }, + name: { + fontSize: 14, + color: "#FFFFFF", + }, + }, + }, + }, + ], + }; + return option; +}; + +// 折线图 +export const lineChartOption = (data) => { + const { x_axis = [], y_axis = [] } = data; + // console.log(data); + const len = parseInt(x_axis?.length || 0); + const zoomSpan = 100 / ((len < 8 ? 8 : len) / 8); + return { + backgroundColor: "transparent", + tooltip: { + trigger: "axis", + backgroundColor: "rgba(0, 0, 0, 0.5)", + borderColor: "rgba(75, 253, 238, 0.4)", + textStyle: { color: "#FFFFFF", fontSize: 12 }, + }, + grid: { + // top: y_axis?.length > 5 ? 60 : 40, + top: 30, + left: "5%", + right: "3%", + bottom: len > 8 ? 25 : 15, + containLabel: true, + }, + legend: { + top: 0, + right: "3%", + // itemWidth: 15, + // itemHeight: 10, + textStyle: { color: "#FFFFFF", fontSize: 12 }, + data: getLegend(y_axis), + }, + dataZoom: [ + { + type: "inside", + // span: zoomSpan, + minSpan: zoomSpan, + maxSpan: zoomSpan, + zoomLock: true, + }, + { + type: "slider", + backgroundColor: "rgba(255,255,255,.3)", + dataBackground: { + areaStyle: "#fff", + }, + height: 9, + bottom: 15, + show: len > 8 ? true : false, + zoomLock: true, + moveHandleSize: 10, + handleIcon: + "path://M512 512m-208 0a6.5 6.5 0 1 0 416 0 6.5 6.5 0 1 0-416 0Z M512 192C335.264 192 192 335.264 192 512c0 176.736 143.264 320 320 320s320-143.264 320-320C832 335.264 688.736 192 512 192zM512 800c-159.072 0-288-128.928-288-288 0-159.072 128.928-288 288-288s288 128.928 288 288C800 671.072 671.072 800 512 800z", + handleColor: "#0260FF", + handleSize: "95%", + // handleStyle: { + // shadowBlur: 3, + // shadowOffsetX: 1, + // shadowOffsetY: 1, + // shadowColor: "rgba(0, 0, 0, 0.6)", + // }, + textStyle: { + fontSize: 12, + color: "#9A9A9A", + }, + showDetail: false, + }, + ], + xAxis: [ + { + type: "category", + boundaryGap: false, + axisLabel: { fontSize: 12, color: "#FFFFFF" }, + axisLine: { + lineStyle: { + color: "#9A9A9A", + opacity: 0.55, + }, + }, + data: x_axis || [], + }, + ], + yAxis: [ + { + type: "value", + axisLabel: { fontSize: 12, color: "#FFFFFF" }, + axisLine: { + lineStyle: { + color: "#9A9A9A", + opacity: 0.6, + }, + }, + axisTick: { + show: false, + }, + splitLine: { + lineStyle: { + opacity: 0.4, + }, + }, + }, + ], + series: renderLines(y_axis), + // series: { + // name: "数量", + // type: "line", + // smooth: true, + // lineStyle: { + // width: 2, + // color: "#58b8ff", + // }, + // data: y_axis || [], + // }, + }; +}; + +// 获取图例 +function getLegend(arr) { + let newArr = []; + if (arr?.length) { + arr.forEach((v, i) => { + newArr.push(v?.name || "数量" + i); + }); + } + return newArr; +} + +// 生成折线 +function renderLines(arr) { + let newArr = []; + if (arr?.length) { + newArr = arr.map((v) => { + return { + name: v?.name || "数量", + type: "line", + // stack: "Total", + yAxisIndex: 0, + // smooth: true, + // lineStyle: { + // width: 2, + // color: `#30b4ea`, + // }, + // itemStyle: { + // color: `#30b4ea`, + // }, + // areaStyle: { + // opacity: 0.8, + // color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [ + // { + // offset: 0, + // color: "rgba(59,255,244,0.3)", + // }, + // { + // offset: 1, + // color: "rgba(59,255,244,0)", + // }, + // ]), + // }, + data: v?.value || [], + }; + }); + } + return newArr; +} + +// 水球 +export function waterOption(data) { + let num = (data / 100).toFixed(2); + return { + backgroundColor: "transparent", + // title: [ + // { + // text: "比率", + // x: "22%", + // y: "70%", + // textStyle: { + // fontSize: 14, + // fontWeight: "100", + // color: "#5dc3ea", + // lineHeight: 16, + // textAlign: "center", + // }, + // }, + // ], + series: [ + { + type: "liquidFill", + radius: "85%", + // center: ["25%", "45%"], + color: [ + { + type: "linear", + x: 0, + y: 0, + x2: 0, + y2: 1, + colorStops: [ + { + offset: 0, + color: "#7A7BF0", + }, + { + offset: 0.25, + color: "#7F76F1", + }, + { + offset: 0.5, + color: "#9170F7", + }, + { + offset: 0.75, + color: "#BF7EFB", + }, + { + offset: 1, + color: "#C89EF9", + }, + ], + globalCoord: false, + }, + ], + data: [num, num], // data个数代表波浪数 + backgroundStyle: { + borderWidth: 1, + opacity: 0.5, + color: { + type: "radial", + x: 0.5, + y: 0.5, + r: 0.5, + colorStops: [ + { + offset: 0, + color: "#E9EAFB", + }, + { + offset: 0.4, + color: "#E9EAFB", + }, + { + offset: 1, + color: "#CDCDF9", + }, + ], + global: false, + }, + }, + label: { + fontSize: 14, + color: "#fff", + formatter(param) { + return "{num|" + data + "%}" + "\n {name|完好率}"; + }, + rich: { + num: { + fontSize: 20, + padding: [10, 0, 5], + color: "#ffffff", + }, + name: { + fontSize: 10, + color: "#ffffff", + }, + }, + }, + outline: { + // show: false, + borderDistance: 0, + itemStyle: { + borderWidth: 3, + borderColor: "#CCD5F6", + }, + }, + }, + { + type: "pie", + radius: ["85%", "100%"], + silent: true, + labelLine: { + show: false, + }, + itemStyle: { + color: "#EDEDFD", + }, + data: [{ value: 100 }], + }, + ], + }; +} diff --git a/src/pages/OperationCenter/CustomerServieMgm/ComplainStat/index.scss b/src/pages/OperationCenter/CustomerServieMgm/ComplainStat/index.scss index fea4653..dac5473 100644 --- a/src/pages/OperationCenter/CustomerServieMgm/ComplainStat/index.scss +++ b/src/pages/OperationCenter/CustomerServieMgm/ComplainStat/index.scss @@ -17,7 +17,7 @@ $color-primary : var(--color-primary); align-items: center; padding: 20px; background: var(--color-user-list-bg); - border-radius: 20px; + border-radius: 10px; box-shadow: 0px 3px 8px 0px rgba(0, 0, 0, 0.08); .ant-select-selector, @@ -85,39 +85,91 @@ $color-primary : var(--color-primary); width: 100%; margin-top: 20px; flex: 1; - padding: 20px; display: flex; flex-direction: column; - background: var(--color-user-list-bg); - border-radius: 20px; - box-shadow: 0px 3px 8px 0px rgba(0, 0, 0, 0.08); .paid-summary { display: flex; justify-content: space-between; width: 100%; - // height: 100px; + height: 120px; >div { - width: calc(25% - 15px); + width: calc(20% - 16px); height: 100%; - background-color: #ffc0cb; - &.sum-item{ + background: var(--color-user-list-bg); + border-radius: 5px; + box-shadow: 0px 3px 8px 0px rgba(0, 0, 0, 0.08); + + &.sum-item { display: flex; flex-direction: column; - .sum-con{ + padding: 6px; + + .sum-con { flex: 1; display: flex; align-items: center; - justify-content: space-between; + // justify-content: space-between; + overflow: hidden; + + .num-box { + padding-left: 30px; + + .anticon { + color: var(--color-primary); + transform: rotate(95deg); + } + + + >span { + margin-right: 5px; + font-size: 28px; + font-weight: 600; + } + } - .per-box{ - transform: scale(0.5); + .per-box { + transform: scale(0.4); } } - .sum-txt{ - padding: 3px; + + .sum-txt { + height: 34px; + line-height: 34px; + font-size: 16px; text-align: center; + border-radius: 4px; + // background-color: rgba($color: pink, $alpha: 0.4); + } + + @mixin setColor($color) { + .num-box>span { + color: $color; + } + .sum-txt { + background-color: rgba($color: $color, $alpha: 0.2); + } + } + + &:nth-child(1) { + @include setColor(#459CFC); + } + + &:nth-child(2) { + @include setColor(#F3511D); + } + + &:nth-child(3) { + @include setColor(#F8BF4D); + } + + &:nth-child(4) { + @include setColor(#9CC811); + } + + &:nth-child(5) { + @include setColor(#8182E6); } } } @@ -135,10 +187,39 @@ $color-primary : var(--color-primary); margin-top: 20px; } - >div { + .chart-box { width: 100%; height: calc(50% - 10px); - background-color: yellow; + display: flex; + flex-direction: column; + justify-content: space-between; + padding: 10px; + background: var(--color-user-list-bg); + border-radius: 10px; + box-shadow: 0px 3px 8px 0px rgba(0, 0, 0, 0.08); + + .title { + width: 100%; + height: 32px; + display: flex; + align-items: center; + + &::before { + content: ""; + display: inline-block; + width: 6px; + height: 20px; + margin-right: 8px; + border-radius: 3px; + background-color: var(--color-primary); + } + } + + .wraper { + flex: 1; + width: 100%; + // height: calc(100% - 32px); + } } &.col-one { diff --git a/src/pages/OperationCenter/CustomerServieMgm/ComplainStat/loadable.jsx b/src/pages/OperationCenter/CustomerServieMgm/ComplainStat/loadable.jsx index 98e8dc5..0f52130 100644 --- a/src/pages/OperationCenter/CustomerServieMgm/ComplainStat/loadable.jsx +++ b/src/pages/OperationCenter/CustomerServieMgm/ComplainStat/loadable.jsx @@ -1,42 +1,106 @@ -import React, { useState, useRef, useEffect } from "react"; +import React, { useState, useRef, useEffect, useCallback } from "react"; import { message, Button, DatePicker, Progress, Modal } from "antd"; -import { SearchOutlined, DeleteOutlined } from "@ant-design/icons"; +import { SearchOutlined, PhoneOutlined } from "@ant-design/icons"; import * as echarts from "echarts"; import ReactEcharts from "echarts-for-react"; // import { dictionary, utils } from "@/config/common"; -// import moment from 'moment' +import moment from "moment"; // import { useSessionStorageState, useUpdateEffect, useSize, useUpdate } from 'ahooks'; -// import ajax from "@/services" -// import { FormInput, FormSelect, OptionPanel, ResultPanel, FormSliderPicker, AreaCascader, ImgResize, ImgZoom, } from "@/components" +import ajax from "@/services"; +import { + pieChartOption, + barChartOption, + lineChartOption, +} from "./echartsOptions"; import "./index.scss"; -// import errorImg from "@/assets/images/layout/error.png" // import { useLocation } from "react-router-dom"; function ComplainStat() { - function renderItem() { + // 查询数据 + const [formData, setFormData] = useState({ + s_time: moment().subtract(7, "days").format("YYYY-MM-DD"), //起始时间 + e_time: moment().format("YYYY-MM-DD"), //截止时间 + }); + // 结果数据 + const [resultData, setResultData] = useState({ + total: 0, // 投诉总量 + wait_num: 0, // 待处理数量 + follow_num: 0, // 跟进中数量 + deal_num: 0, // 已处理数量 + deal_rate: 0, // 按时处理比率 + }); + // 饼图数据1 + const [chartData1, setChartData1] = useState([]); + // 饼图数据2 + const [chartData2, setChartData2] = useState([]); + // 折线图数据 + const [chartData3, setChartData3] = useState({}); + // 条形图数据 + const [chartData4, setChartData4] = useState({}); + + useEffect(() => { + getData(); + }, []); + + // 获取列表数据 + const getData = () => { + ajax.getComplainStatistics(formData).then( + (res) => { + if (parseInt(res?.status) === 20000) { + let { base_data, pie1_data, pie2_data, line_data, bar_data } = + res?.data || {}; + setResultData(base_data || {}); + setChartData1(pie1_data || []); + setChartData2(pie2_data || []); + setChartData3(line_data || {}); + setChartData4(bar_data || {}); + } else { + message.error(res?.message); + } + }, + (err) => { + console.log(err); + message.error("服务器异常"); + } + ); + }; + + // 计算百分比 + const getPercent = (val) => { + const a = parseInt(resultData?.total); + const b = parseInt(val); + if (a && b) { + return ((b * 100) / a).toFixed(2); + } else { + return 0; + } + }; + + // 模版 + function renderItem(text, html, num, color) { + const bool = typeof num !== "undefined"; return (