diff --git a/customer_service.html b/cs_chart.html
similarity index 100%
rename from customer_service.html
rename to cs_chart.html
diff --git a/index.html b/index.html
index 4e1999c..60d7ccd 100644
--- a/index.html
+++ b/index.html
@@ -85,16 +85,7 @@
-
- 客服完成率分布
-
-
+
客服完成率分布
-
客服多指标对比
+
+ 客服多指标对比
+
+
@@ -203,152 +204,29 @@
}
/*****************************************************************
- * 1. 原始数据明细 + 2025年1月-10月随机数据
+ * 1. 初始化空数据
*****************************************************************/
-// 基础11月数据
- const baseNovData = [
- ["2025-11","陈航明",891,415,750,"84.18%",50,"5.61%",68,"7.63%",91,"10.21%",2,"0.22%",3906.60,"4.28%",4,"0.45%",0,"0%",793,"99.13%",101,"11.34%",274,"30.75%",36,"4.04%",24,"2.69%",35,"3.93%"],
- ["2025-11","周汝琪",305,149,249,"81.64%",18,"5.9%",8,"2.62%",38,"12.46%",2,"0.66%",859.00,"2.91%",2,"0.66%",0,"0%",255,"95.51%",15,"4.92%",68,"22.3%",7,"2.3%",2,"0.66%",9,"2.95%"],
- ["2025-11","卢紫嫣",705,353,579,"82.13%",37,"5.25%",31,"4.4%",89,"12.62%",0,"0%",3382.00,"5.3%",3,"0.43%",2,"0.28%",616,"100%",79,"11.21%",194,"27.52%",37,"5.25%",24,"3.4%",66,"9.36%"],
- ["2025-11","徐婉茹",1010,466,840,"83.17%",52,"5.15%",62,"6.14%",118,"11.68%",1,"0.1%",2728.00,"2.66%",1,"0.1%",2,"0.2%",881,"98.77%",105,"10.4%",287,"28.42%",33,"3.27%",30,"2.97%",60,"5.94%"],
- ["2025-11","刘春霞",986,493,813,"82.45%",53,"5.38%",63,"6.39%",120,"12.17%",0,"0%",5342.00,"5.34%",4,"0.41%",0,"0%",861,"99.42%",85,"8.62%",295,"29.92%",29,"2.94%",38,"3.85%",59,"5.98%"],
- ["2025-11","王燕燕",336,160,275,"81.85%",21,"6.25%",15,"4.46%",40,"11.9%",1,"0.3%",1765.00,"4.97%",1,"0.3%",0,"0%",287,"96.96%",14,"4.17%",70,"20.83%",15,"4.46%",8,"2.38%",11,"3.27%"],
- ["2025-11","何丹妮",39,21,32,"82.05%",5,"12.82%",0,"0%",2,"5.13%",0,"0%",0.00,"0%",0,"0%",0,"0%",35,"94.59%",4,"10.26%",6,"15.38%",2,"5.13%",1,"2.56%",3,"7.69%"],
- ["2025-11","技术部测试",4,0,0,"0%",0,"0%",0,"0%",4,"100%",0,"0%",0.00,"0%",0,"0%",0,"0%",0,"0%",0,"0%",0,"0%",0,"0%",0,"0%",0,"0%",0,"0%"],
- ["2025-11","蒋卡泽",174,63,128,"73.56%",9,"5.17%",17,"9.77%",37,"21.26%",8,"4.6%",1355.00,"8.05%",0,"0%",0,"0%",137,"100%",13,"7.47%",56,"32.18%",4,"2.3%",2,"1.15%",6,"3.45%"],
- ["2025-11","冯雪",841,393,708,"84.19%",46,"5.47%",50,"5.95%",87,"10.34%",0,"0%",3697.00,"4.14%",2,"0.24%",1,"0.12%",690,"91.51%",68,"8.09%",233,"27.71%",26,"3.09%",18,"2.14%",53,"6.3%"],
- ["2025-11","啾啾AI",5070,6,5056,"99.72%",0,"0%",1,"0.02%",14,"0.28%",0,"0%",0.00,"0%",8,"0.16%",1,"0.02%",0,"0%",1,"0.02%",454,"8.95%",0,"0%",111,"2.19%",5050,"99.61%"],
- ["2025-11","王淑静",903,425,756,"83.72%",65,"7.2%",71,"7.86%",82,"9.08%",0,"0%",4089.00,"4.04%",6,"0.66%",1,"0.11%",780,"95.01%",94,"10.41%",269,"29.79%",22,"2.44%",14,"1.55%",60,"6.64%"],
- ["2025-11","方文明",521,292,433,"83.11%",38,"7.29%",22,"4.22%",50,"9.6%",0,"0%",2158.00,"3.83%",5,"0.96%",0,"0%",442,"93.84%",39,"7.49%",123,"23.61%",17,"3.26%",9,"1.73%",27,"5.18%"],
- ["2025-11","陈家南",11,3,9,"81.82%",0,"0%",2,"18.18%",2,"18.18%",0,"0%",850.00,"16.66%",0,"0%",0,"0%",2,"22.22%",1,"9.09%",2,"18.18%",0,"0%",0,"0%",8,"72.73%"],
- ["2025-11","全部",11796,3239,10628,"90.1%",394,"3.34%",410,"3.48%",774,"6.56%",14,"0.12%",30131.60,"2.53%",36,"0.31%",7,"0.06%",5779,"52.43%",619,"5.25%",2331,"19.76%",228,"1.93%",281,"2.38%",5447,"46.18%"]
- ];
-
- // 生成2025年1月-10月随机数据
- function generateMonthlyData(baseData, months) {
- const allData = [...baseData];
-
- for (let month = 1; month <= 10; month++) {
- const monthStr = month.toString().padStart(2, '0');
- const yearMonth = `2025-${monthStr}`;
-
- baseData.forEach(row => {
- if (row[1] !== '全部') { // 跳过"全部"汇总行
- const newRow = [...row];
- newRow[0] = yearMonth; // 更新月份
-
- // 为数值字段添加随机变化 (±20%)
- for (let i = 2; i <= 30; i += 2) {
- if (typeof newRow[i] === 'number') {
- const variation = 0.8 + Math.random() * 0.4; // 0.8-1.2
- newRow[i] = Math.round(newRow[i] * variation);
- }
- }
-
- // 为百分比字段添加随机变化 (±5%)
- for (let i = 3; i <= 31; i += 2) {
- if (typeof newRow[i] === 'string' && newRow[i].includes('%')) {
- const baseValue = parseFloat(newRow[i]);
- const variation = 0.95 + Math.random() * 0.1; // 0.95-1.05
- const newValue = Math.max(0, Math.min(100, baseValue * variation));
- newRow[i] = newValue.toFixed(2) + '%';
- }
- }
-
- allData.push(newRow);
- }
- });
-
- // 添加月度汇总行
- const summaryRow = [...baseData.find(row => row[1] === '全部')];
- summaryRow[0] = yearMonth;
- allData.push(summaryRow);
- }
-
- return allData;
- }
-
- const rawNov = generateMonthlyData(baseNovData, 10);
-
- /* 解析为统一对象 - 修复变量名问题,添加错误处理 */
- function parseRow(r){
- // 辅助函数:安全解析百分比或数值
- function safeParse(value, defaultValue = 0) {
- if (value === null || value === undefined) return defaultValue;
- if (typeof value === 'number') return value;
- if (typeof value === 'string') {
- // 移除 % 符号并解析
- const cleaned = value.replace('%', '').trim();
- // 检查是否包含无效字符
- if (cleaned === '' || cleaned === '极' || isNaN(cleaned)) {
- return defaultValue;
- }
- const parsed = parseFloat(cleaned);
- return isNaN(parsed) ? defaultValue : parsed;
- }
- return defaultValue;
- }
-
- const [
- 月份,调度,订单总数,考核数,完成数,完成率,
- 空驶数,空驶率,线下数,线下率,关闭数,关闭率,
- 拒单数,拒单率,溢价金额,溢价率,不满数,不满率,
- 投诉数,投诉率,审单数,审单率,预约数,预约率,
- 未接50数,未接50率,超时数,超时率,不符数,不符率,
- 未联系数,未联系率
- ] = r;
-
- return {
- 月份,调度,
- 订单总数: safeParse(订单总数, 0),
- 考核数: safeParse(考核数, 0),
- 完成数: safeParse(完成数, 0),
- 完成率: safeParse(完成率, 0),
- 空驶数: safeParse(空驶数, 0),
- 空驶率: safeParse(空驶率, 0),
- 线下数: safeParse(线下数, 0),
- 线下率: safeParse(线下率, 0),
- 关闭数: safeParse(关闭数, 0),
- 关闭率: safeParse(关闭率, 0),
- 拒单数: safeParse(拒单数, 0),
- 拒单率: safeParse(拒单率, 0),
- 溢价金额: safeParse(溢价金额, 0),
- 溢价率: safeParse(溢价率, 0),
- 不满数: safeParse(不满数, 0),
- 不满率: safeParse(不满率, 0),
- 投诉数: safeParse(投诉数, 0),
- 投诉率: safeParse(投诉率, 0),
- 审单数: safeParse(审单数, 0),
- 审单率: safeParse(审单率, 0),
- 预约数: safeParse(预约数, 0),
- 预约率: safeParse(预约率, 0),
- 未接50数: safeParse(未接50数, 0),
- 未接50率: safeParse(未接50率, 0),
- 超时数: safeParse(超时数, 0),
- 超时率: safeParse(超时率, 0),
- 不符数: safeParse(不符数, 0),
- 不符率: safeParse(不符率, 0),
- 未联系数: safeParse(未联系数, 0),
- 未联系率: safeParse(未联系率, 0)
- };
- }
- // 解析数据,添加错误处理
+ // 初始化为空数组,等待接口数据
let novData = [];
- try {
- novData = rawNov.map(parseRow);
- console.log('数据解析成功,共', novData.length, '条记录');
- } catch (error) {
- console.error('数据解析失败:', error);
- // 如果解析失败,至少显示空数据,避免页面完全无法显示
- novData = [];
- }
- /* 生成 30 天趋势(Demo) */
+ /* 生成趋势数据(基于真实月度数据模拟日度数据) */
function genTrend(base){
const arr=[];
+ // 如果没有数据,返回空数组
+ if (!base || base.length === 0) return arr;
+
+ // 获取最新月份
+ const months = [...new Set(base.map(b => b.月份))].sort();
+ const latestMonth = months[months.length - 1];
+
+ if (!latestMonth) return arr;
+
+ // 使用最新月份的数据生成30天趋势
+ const latestData = base.filter(b => b.月份 === latestMonth);
+
for(let d=1;d<=30;d++){
- const date=dayjs('2025-11-01').add(d-1,'day');
- base.forEach(b=>{
+ const date=dayjs(latestMonth + '-01').add(d-1,'day');
+ latestData.forEach(b=>{
arr.push({
日期:date.format('YYYY-MM-DD'),
客服:b.调度,
@@ -363,18 +241,21 @@
}
return arr;
}
- const trendData = genTrend(novData.filter(b=>b.调度!=='全部' && b.调度!=='技术部测试'));
+
+ // 初始化为空数组
+ let trendData = [];
/* 全局状态 */
const state = {
- month:'2025-11',
- staff:[],
- index:'完成率',
- trendData,
- novData,
+ month: '',
+ staff: [],
+ index: '完成率',
+ trendData: [],
+ novData: [],
drillName: null,
selectedStaffs: [], // 从柱状图点击选中的客服列表
- pieChartIndex: '完成率' // 饼图/柱状图切换的指标
+ pieChartIndex: '完成率', // 饼图/柱状图切换的指标
+ barChartIndex: '完成率' // 柱状图切换的指标
};
/* ECharts 实例 */
@@ -466,13 +347,13 @@
Object.keys(monthData).forEach(month => {
const m = monthData[month];
if (staffs.length === 0 || staffs.length > 1) {
- // 全部客服:动态计算百分比
+ // 全部客服:动态计算百分比,保留2位小数
if (m.订单总数 > 0) {
// 完成率 = (完成数 + 空驶数) / 订单总数 * 100
- m.完成率 = ((m.完成数 + m.空驶数) / m.订单总数 * 100);
- m.投诉率 = (m.投诉数 / m.订单总数 * 100);
- m.空驶率 = (m.空驶数 / m.订单总数 * 100);
- m.关闭率 = (m.关闭数 / m.订单总数 * 100);
+ m.完成率 = parseFloat(((m.完成数 + m.空驶数) / m.订单总数 * 100).toFixed(2));
+ m.投诉率 = parseFloat((m.投诉数 / m.订单总数 * 100).toFixed(2));
+ m.空驶率 = parseFloat((m.空驶数 / m.订单总数 * 100).toFixed(2));
+ m.关闭率 = parseFloat((m.关闭数 / m.订单总数 * 100).toFixed(2));
} else {
m.完成率 = 0;
m.投诉率 = 0;
@@ -480,6 +361,8 @@
m.关闭率 = 0;
}
}
+ // 溢价金额也保留2位小数
+ m.溢价金额 = parseFloat(m.溢价金额.toFixed(2));
});
return Object.values(monthData).sort((a, b) => a.月份.localeCompare(b.月份));
@@ -795,18 +678,48 @@
const months = [...new Set(state.novData.map(d => d.月份))].sort();
const latestMonth = months[months.length - 1] || months[0];
- // 无论是否选择客服,都显示所有客服的数据
+ console.log('饼图数据 - 最新月份:', latestMonth, '指标:', pieIndex);
+ console.log('当前novData:', state.novData);
+
+ // 无论是否选择客服,都显示所有客服的数据(排除"全部"和"技术部测试")
const data = state.novData
- .filter(d => d.调度 !== '全部' && d.调度 !== '技术部测试' && d.月份 === latestMonth)
+ .filter(d => {
+ const isValid = d.调度 !== '全部' && d.调度 !== '技术部测试' && d.月份 === latestMonth;
+ if (isValid) {
+ console.log('饼图客服:', d.调度, pieIndex + ':', d[pieIndex]);
+ }
+ return isValid;
+ })
.map(d => {
const value = d[pieIndex];
+ // 确保值是有效的数字
+ const parsedValue = parseFloat(value);
return {
name: d.调度,
- value: (value !== undefined && value !== null && !isNaN(value)) ? value : 0
+ value: (!isNaN(parsedValue) && parsedValue > 0) ? parsedValue : 0
};
})
.filter(d => d.value > 0); // 过滤掉值为0的数据
+ console.log('饼图最终数据:', data);
+
+ // 如果没有有效数据,显示提示
+ if (data.length === 0) {
+ console.warn('饼图没有有效数据');
+ pie.setOption({
+ title: {
+ text: '暂无数据',
+ left: 'center',
+ top: 'center',
+ textStyle: {
+ color: '#999',
+ fontSize: 16
+ }
+ }
+ }, true);
+ return;
+ }
+
// 如果是完成率,显示饼图;其他指标显示柱状图
if (pieIndex === '完成率') {
const option = {
@@ -887,23 +800,27 @@
}
}
- /* 2. 折线 - 月度趋势(按日) */
+ /* 2. 折线 - 月度趋势(使用真实月度数据) */
function updateLine(){
if (!line) {
console.warn('折线图实例未初始化');
return;
}
- const {trendData, staff, index} = state;
- if (!trendData || trendData.length === 0) {
+ const {novData, staff, index} = state;
+
+ // 过滤掉 '全部' 和 '测试' 账号
+ const validData = novData.filter(d => !d.调度.includes('全部') && !d.调度.includes('测试'));
+
+ if (!validData || validData.length === 0) {
console.warn('趋势数据为空');
return;
}
- const days = [...new Set(trendData.map(d => d.日期))].sort();
+ const months = [...new Set(validData.map(d => d.月份))].sort();
// 如果没有选择客服,显示所有客服
- const staffsToShow = staff.length > 0 ? staff : [...new Set(trendData.map(d => d.客服))];
+ const staffsToShow = staff.length > 0 ? staff : [...new Set(validData.map(d => d.调度))];
const series = staffsToShow.map((s, idx) => ({
name: s,
@@ -912,8 +829,8 @@
symbol: 'circle',
symbolSize: 6,
lineStyle: { width: 3 },
- data: days.map(day => {
- const item = trendData.find(d => d.日期 === day && d.客服 === s);
+ data: months.map(month => {
+ const item = validData.find(d => d.月份 === month && d.调度 === s);
return item ? Math.max(0, item[index]) : 0;
})
}));
@@ -937,7 +854,7 @@
},
xAxis: {
type: 'category',
- data: days,
+ data: months,
boundaryGap: false,
axisLabel: {
rotate: 45
@@ -960,14 +877,21 @@
return;
}
- const {novData, staff, index} = state;
+ const {novData, staff} = state;
+ const index = state.barChartIndex || '完成率';
+
if (!novData || novData.length === 0) {
console.warn('没有数据可显示');
return;
}
+ // 获取最新月份
+ const months = [...new Set(novData.map(d => d.月份))].sort();
+ const latestMonth = months[months.length - 1];
+
const filterData = novData.filter(d =>
- staff.length ? staff.includes(d.调度) : (d.调度 !== '全部' && d.调度 !== '技术部测试')
+ d.月份 === latestMonth &&
+ d.调度 !== '全部' && !d.调度.includes('测试')
);
const barData = filterData.map(d => {
@@ -1001,10 +925,10 @@
},
yAxis: {
type: 'value',
- name: index + (state.index.includes('率') ? '(%)' : '')
+ name: index + (index.includes('率') ? '(%)' : '')
},
series: [{
- name: state.index,
+ name: index,
type: 'bar',
data: barData.map(d => d.value),
barMaxWidth: 40
@@ -1022,10 +946,10 @@
if (!state.selectedStaffs) {
state.selectedStaffs = [];
}
- const index = state.selectedStaffs.indexOf(name);
- if (index > -1) {
+ const idx = state.selectedStaffs.indexOf(name);
+ if (idx > -1) {
// 已选中,取消选择
- state.selectedStaffs.splice(index, 1);
+ state.selectedStaffs.splice(idx, 1);
} else {
// 未选中,添加选择
state.selectedStaffs.push(name);
@@ -1040,7 +964,7 @@
});
}
- /* 4. 历史 - 单个或多个客服趋势 */
+ /* 4. 历史 - 单个或多个客服趋势(使用真实月度数据) */
function updateHist(){
if (!hist) {
console.warn('历史图表实例未初始化');
@@ -1064,24 +988,24 @@
}
// 最后使用默认客服
else {
- const defaultStaff = state.novData.find(d => d.调度 !== '全部' && d.调度 !== '技术部测试')?.调度 || '陈航明';
+ const defaultStaff = state.novData.find(d => !d.调度.includes('全部') && !d.调度.includes('测试'))?.调度 || '陈航明';
staffsToShow = [defaultStaff];
}
- // 获取所有选中客服的数据
- const allData = state.trendData.filter(d => staffsToShow.includes(d.客服));
+ // 获取所有选中客服的数据(使用 novData)
+ const allData = state.novData.filter(d => staffsToShow.includes(d.调度));
if (!allData || allData.length === 0) {
console.warn('没有找到客服数据:', staffsToShow);
return;
}
- // 获取所有日期
- const days = [...new Set(allData.map(d => d.日期))].sort();
+ // 获取所有月份
+ const months = [...new Set(allData.map(d => d.月份))].sort();
// 为每个客服创建一条折线
const series = staffsToShow.map((staffName, idx) => {
- const staffData = allData.filter(d => d.客服 === staffName);
+ const staffData = allData.filter(d => d.调度 === staffName);
return {
name: staffName,
type: 'line',
@@ -1089,8 +1013,8 @@
symbol: 'circle',
symbolSize: 6,
lineStyle: { width: 3 },
- data: days.map(day => {
- const item = staffData.find(d => d.日期 === day);
+ data: months.map(month => {
+ const item = staffData.find(d => d.月份 === month);
return item ? Math.max(0, item[state.index]) : null;
})
};
@@ -1132,7 +1056,7 @@
},
xAxis: {
type: 'category',
- data: days,
+ data: months,
boundaryGap: false,
axisLabel: {
rotate: 45
@@ -1181,11 +1105,14 @@
// 重新生成趋势数据
const trendBase = novData.filter(b => b.调度 !== '全部' && b.调度 !== '技术部测试');
- state.trendData = genTrend(trendBase);
+ trendData = genTrend(trendBase);
+ state.trendData = trendData;
// 重新初始化客服列表
initCtrl();
renderAll();
+ } else {
+ alert('获取数据失败,请检查网络连接或联系管理员');
}
});
@@ -1229,7 +1156,8 @@
renderAll();
});
- // 饼图指标切换
+ // 饼图指标切换(已移除,改为柱状图指标切换)
+ /*
const pieChartIndexSel = document.getElementById('pieChartIndex');
if (pieChartIndexSel) {
pieChartIndexSel.value = state.pieChartIndex || '完成率';
@@ -1238,6 +1166,17 @@
updatePie();
});
}
+ */
+
+ // 柱状图指标切换
+ const barChartIndexSel = document.getElementById('barChartIndex');
+ if (barChartIndexSel) {
+ barChartIndexSel.value = state.barChartIndex || '完成率';
+ barChartIndexSel.addEventListener('change', e => {
+ state.barChartIndex = e.target.value;
+ updateBar();
+ });
+ }
document.getElementById('export').addEventListener('click', () => {
if (bar) {
@@ -1323,11 +1262,14 @@
// 重新生成趋势数据
const trendBase = novData.filter(b => b.调度 !== '全部' && b.调度 !== '技术部测试');
- state.trendData = genTrend(trendBase);
+ trendData = genTrend(trendBase);
+ state.trendData = trendData;
console.log('使用接口数据,共', novData.length, '条记录');
} else {
- console.warn('接口数据异常,使用本地默认数据');
+ console.warn('接口数据获取失败,请检查接口配置');
+ // 显示友好提示
+ alert('数据加载失败,请检查网络连接或联系管理员');
}
initCtrl();