fix:修改时间趋势分析图的指标问题

This commit is contained in:
hangyu.tao 2026-02-02 11:16:34 +08:00
parent 1134943bb2
commit a2ad9cafde
2 changed files with 96 additions and 99 deletions

View File

@ -479,10 +479,16 @@
<div class="card-header"> <div class="card-header">
<h3><i class="fas fa-chart-line"></i> 时间趋势分析</h3> <h3><i class="fas fa-chart-line"></i> 时间趋势分析</h3>
<div class="chart-controls"> <div class="chart-controls">
<select id="trendTypeSelect" class="chart-field-select">
<option value="all" selected>全部指标</option>
<option value="customer">客户数</option>
<option value="demand">需求数</option>
<option value="issue">反馈数</option>
</select>
<select id="trendChartFieldSelect" class="chart-field-select"> <select id="trendChartFieldSelect" class="chart-field-select">
<option value="month">按月统计</option> <option value="day" selected>按天统计</option>
<option value="week">按周统计</option> <option value="week">按周统计</option>
<option value="day">按天统计</option> <option value="month">按月统计</option>
</select> </select>
</div> </div>
</div> </div>

View File

@ -61,7 +61,7 @@ function canDelete() {
} }
document.addEventListener("DOMContentLoaded", function () { document.addEventListener("DOMContentLoaded", function () {
console.log("CRM System Main JS v4.0 Loaded - " + new Date().toLocaleString()); console.log("CRM System Main JS v4.5 Loaded - " + new Date().toLocaleString());
// 登录守卫 // 登录守卫
const token = localStorage.getItem("crmToken"); const token = localStorage.getItem("crmToken");
if (!token && !window.location.pathname.endsWith("login.html")) { if (!token && !window.location.pathname.endsWith("login.html")) {
@ -1653,7 +1653,9 @@ document.addEventListener("DOMContentLoaded", function () {
const ctx = canvas.getContext("2d"); const ctx = canvas.getContext("2d");
const trendType = const trendType =
document.getElementById("trendTypeSelect")?.value || "customer"; document.getElementById("trendTypeSelect")?.value || "all";
const dateGrain =
document.getElementById("trendChartFieldSelect")?.value || "day";
if (trendChartInstance) { if (trendChartInstance) {
trendChartInstance.destroy(); trendChartInstance.destroy();
@ -1663,11 +1665,23 @@ document.addEventListener("DOMContentLoaded", function () {
const dateMap = {}; const dateMap = {};
customers.forEach((customer) => { customers.forEach((customer) => {
const dateStr = normalizeDateValue(customer.intendedProduct); const dateValue = normalizeDateValue(customer.intendedProduct);
if (!dateStr) return; if (!dateValue) return;
if (!dateMap[dateStr]) { let dateKey = dateValue;
dateMap[dateStr] = { if (dateGrain === "month") {
dateKey = dateValue.substring(0, 7); // YYYY-MM
} else if (dateGrain === "week") {
// Find Monday of that week
const d = new Date(dateValue);
const day = d.getDay();
const diff = d.getDate() - day + (day === 0 ? -6 : 1);
const monday = new Date(d.setDate(diff));
dateKey = monday.toISOString().split("T")[0] + " (周)";
}
if (!dateMap[dateKey]) {
dateMap[dateKey] = {
customers: new Set(), customers: new Set(),
demands: 0, demands: 0,
issues: 0, issues: 0,
@ -1676,16 +1690,20 @@ document.addEventListener("DOMContentLoaded", function () {
// Count customers // Count customers
if (customer.customerName) { if (customer.customerName) {
dateMap[dateStr].customers.add(customer.customerName); dateMap[dateKey].customers.add(customer.customerName);
} }
// Count demands and issues based on type // Count demands and issues based on type
const type = (customer.type || "").toLowerCase(); const type = (customer.type || "").toLowerCase();
if (type.includes("需求")) { if (type.includes("需求")) {
dateMap[dateStr].demands++; dateMap[dateKey].demands++;
} }
if (type.includes("问题") || type.includes("功能问题")) { if (
dateMap[dateStr].issues++; type.includes("问题") ||
type.includes("功能问题") ||
type.includes("反馈")
) {
dateMap[dateKey].issues++;
} }
}); });
@ -1695,96 +1713,60 @@ document.addEventListener("DOMContentLoaded", function () {
// Prepare datasets based on selected trend type // Prepare datasets based on selected trend type
const datasets = []; const datasets = [];
const customerDataset = {
label: "客户数",
data: sortedDates.map((date) => dateMap[date].customers.size),
borderColor: "#FF6B35",
backgroundColor: "rgba(255, 107, 53, 0.1)",
borderWidth: 3,
pointRadius: 5,
pointHoverRadius: 7,
pointBackgroundColor: "#FF6B35",
pointBorderColor: "#fff",
pointBorderWidth: 2,
tension: 0.4,
fill: trendType === "customer",
};
const demandDataset = {
label: "需求数",
data: sortedDates.map((date) => dateMap[date].demands),
borderColor: "#4CAF50",
backgroundColor: "rgba(76, 175, 80, 0.1)",
borderWidth: 3,
pointRadius: 5,
pointHoverRadius: 7,
pointBackgroundColor: "#4CAF50",
pointBorderColor: "#fff",
pointBorderWidth: 2,
tension: 0.4,
fill: trendType === "demand",
};
const issueDataset = {
label: "反馈数",
data: sortedDates.map((date) => dateMap[date].issues),
borderColor: "#2196F3",
backgroundColor: "rgba(33, 150, 243, 0.1)",
borderWidth: 3,
pointRadius: 5,
pointHoverRadius: 7,
pointBackgroundColor: "#2196F3",
pointBorderColor: "#fff",
pointBorderWidth: 2,
tension: 0.4,
fill: trendType === "issue",
};
if (trendType === "customer") { if (trendType === "customer") {
datasets.push({ datasets.push(customerDataset);
label: "客户数",
data: sortedDates.map((date) => dateMap[date].customers.size),
borderColor: "#FF6B35",
backgroundColor: "rgba(255, 107, 53, 0.1)",
borderWidth: 3,
pointRadius: 5,
pointHoverRadius: 7,
pointBackgroundColor: "#FF6B35",
pointBorderColor: "#fff",
pointBorderWidth: 2,
tension: 0.4,
fill: true,
});
} else if (trendType === "demand") { } else if (trendType === "demand") {
datasets.push({ datasets.push(demandDataset);
label: "需求数",
data: sortedDates.map((date) => dateMap[date].demands),
borderColor: "#4CAF50",
backgroundColor: "rgba(76, 175, 80, 0.1)",
borderWidth: 3,
pointRadius: 5,
pointHoverRadius: 7,
pointBackgroundColor: "#4CAF50",
pointBorderColor: "#fff",
pointBorderWidth: 2,
tension: 0.4,
fill: true,
});
} else if (trendType === "issue") { } else if (trendType === "issue") {
datasets.push({ datasets.push(issueDataset);
label: "问题数", } else {
data: sortedDates.map((date) => dateMap[date].issues), // all
borderColor: "#F28C28", datasets.push(customerDataset, demandDataset, issueDataset);
backgroundColor: "rgba(242, 140, 40, 0.1)",
borderWidth: 3,
pointRadius: 5,
pointHoverRadius: 7,
pointBackgroundColor: "#F28C28",
pointBorderColor: "#fff",
pointBorderWidth: 2,
tension: 0.4,
fill: true,
});
} else if (trendType === "all") {
datasets.push(
{
label: "客户数",
data: sortedDates.map((date) => dateMap[date].customers.size),
borderColor: "#FF6B35",
backgroundColor: "rgba(255, 107, 53, 0.1)",
borderWidth: 3,
pointRadius: 5,
pointHoverRadius: 7,
pointBackgroundColor: "#FF6B35",
pointBorderColor: "#fff",
pointBorderWidth: 2,
tension: 0.4,
fill: false,
},
{
label: "需求数",
data: sortedDates.map((date) => dateMap[date].demands),
borderColor: "#4CAF50",
backgroundColor: "rgba(76, 175, 80, 0.1)",
borderWidth: 3,
pointRadius: 5,
pointHoverRadius: 7,
pointBackgroundColor: "#4CAF50",
pointBorderColor: "#fff",
pointBorderWidth: 2,
tension: 0.4,
fill: false,
},
{
label: "问题数",
data: sortedDates.map((date) => dateMap[date].issues),
borderColor: "#F28C28",
backgroundColor: "rgba(242, 140, 40, 0.1)",
borderWidth: 3,
pointRadius: 5,
pointHoverRadius: 7,
pointBackgroundColor: "#F28C28",
pointBorderColor: "#fff",
pointBorderWidth: 2,
tension: 0.4,
fill: false,
},
);
} }
trendChartInstance = new Chart(ctx, { trendChartInstance = new Chart(ctx, {
@ -1928,6 +1910,15 @@ document.addEventListener("DOMContentLoaded", function () {
}); });
} }
const trendChartFieldSelect = document.getElementById("trendChartFieldSelect");
if (trendChartFieldSelect) {
trendChartFieldSelect.addEventListener("change", function () {
const startDate = document.getElementById("startDate").value;
const endDate = document.getElementById("endDate").value;
applyDateFilter(startDate, endDate);
});
}
// ========== Follow-up Management ========== // ========== Follow-up Management ==========
const followupSection = document.getElementById("followupSection"); const followupSection = document.getElementById("followupSection");
const addFollowUpBtn = document.getElementById("addFollowUpBtn"); const addFollowUpBtn = document.getElementById("addFollowUpBtn");