From cd0aa26c981f1ebfbea9d2dd705012d5f5992737 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?M=E5=85=88=E7=94=9F?= <1211120248@qq.com>
Date: Tue, 14 Jul 2026 02:04:35 +0800
Subject: [PATCH] fix: refine smart management DeepSeek reports
---
src/services/smartArtifactApi.js | 770 +--------------------------
src/services/smartContextBuilder.js | 157 +++++-
src/views/wisdomManagement/index.vue | 50 +-
3 files changed, 143 insertions(+), 834 deletions(-)
diff --git a/src/services/smartArtifactApi.js b/src/services/smartArtifactApi.js
index 87cb466..7d26c97 100644
--- a/src/services/smartArtifactApi.js
+++ b/src/services/smartArtifactApi.js
@@ -60,7 +60,7 @@ export async function generateSmartArtifact({
throw new Error("代码模型没有返回完整HTML")
}
if (isDataDumpArtifactHtml(html)) {
- throw new Error("专题页面仍为原始数据表,已切换报告模板")
+ throw new Error("专题页面仍为原始数据表,已拒绝展示,请重新生成")
}
return {
@@ -69,670 +69,6 @@ export async function generateSmartArtifact({
}
}
-export function buildLocalSmartArtifact({
- question,
- answer = "",
- charts = [],
- datasets = [],
- context = null,
- reason = "",
-} = {}) {
- const data = JSON.parse(buildArtifactDataJson({ question, answer, charts, datasets, context }))
- const report = buildLocalReport(data, reason)
-
- return `
-
-
-
-
- ${escapeHtml(report.title)}
-
-
-
-
-
-
- ${renderReportMetrics(report.metrics)}
-
-
-
- 总体态势
- ${renderReportParagraphs(report.summary)}
-
-
-
- ${escapeHtml(report.primaryChart.title)}
- ${renderReportBars(report.primaryChart)}
-
-
-
- ${escapeHtml(report.table.title)}
- ${renderReportTable(report.table)}
-
-
- ${report.secondaryChart ? `${escapeHtml(report.secondaryChart.title)}
${renderReportBars(report.secondaryChart)}` : ""}
-
-
- 需关注事项
- ${renderReportList(report.attention)}
-
-
-
- 工作建议
- ${renderReportList(report.recommendations)}
-
-
-
- ${report.footnotes.length ? `` : ""}
-
-
-`
-}
-
-function buildLocalReport(data, reason) {
- const kind = getReportKind(data)
- if (kind === "balance") return buildBalanceReport(data, reason)
- if (kind === "yak") return buildYakReport(data, reason)
- return buildGenericReport(data, reason)
-}
-
-function getReportKind(data) {
- const question = String(data?.question || "")
- const intentText = normalizeList(data?.context?.intents)
- .map((item) => `${item?.key || ""}${item?.label || ""}`)
- .join(" ")
- const text = `${question} ${intentText}`
- if (/草畜|平衡|承载|载畜|超载|放牧压力/.test(text)) return "balance"
- if (/牦牛|存栏|畜群|牛群|出栏|交易|预测/.test(text)) return "yak"
- return "generic"
-}
-
-function buildBalanceReport(data, reason) {
- const scope = data.context?.scope || "红原县"
- const county = firstRow(data, "balance.county") || firstRow(data, "balance.target") || findFirstRow(data, (row) => row?.type === "county") || {}
- const townRows = rowsOf(data, "balance.townRows").length
- ? rowsOf(data, "balance.townRows")
- : findRows(data, (row) => row?.type === "town" && row?.overloadRate !== undefined)
- const chartRows = rowsOfFirstDataset(data, (dataset) => /草畜平衡|超载率/.test(dataset?.title || "") && normalizeList(dataset?.rows).some((row) => row?.value !== undefined))
- const pressureRows = (townRows.length ? townRows : chartRows)
- .map((row) => ({
- name: row.name || row.areaName || row.key,
- overloadRate: toLocalNumber(row.overloadRate ?? row.value),
- level: row.level || row.extra || "",
- livestockCapacity: toLocalNumber(row.livestockCapacity ?? row.yakNumber),
- bearingCapacity: toLocalNumber(row.bearingCapacity),
- }))
- .filter((row) => row.name && Number.isFinite(row.overloadRate))
- .sort((a, b) => b.overloadRate - a.overloadRate)
- const topPressures = pressureRows.slice(0, 5)
- const countyRate = toLocalNumber(county.overloadRate)
- const livestock = toLocalNumber(county.livestockCapacity ?? county.yakNumber)
- const bearing = toLocalNumber(county.bearingCapacity)
- const area = toLocalNumber(county.areaRange)
- const level = county.level || (Number.isFinite(countyRate) ? "基本实现草畜平衡" : "待核算")
- const pressureNames = topPressures.map((row) => `${row.name}${formatRateSuffix(row.overloadRate)}`).join("、")
- const advice = splitAdvice(county.advice)
-
- return {
- title: "草畜平衡情况",
- subtitle: `${scope} · 草畜平衡专题`,
- sourceText: "数据来源:平台草畜平衡核算数据",
- metrics: [
- metric("全县超载率", formatRate(countyRate), level),
- metric("实际载畜量", formatHead(livestock), "当前核算口径"),
- metric("理论承载力", formatHead(bearing), "按草地面积折算"),
- metric("草地面积", formatWanMu(area), "纳入平衡核算"),
- ],
- summary: [
- `${scope}当前草畜平衡总体处于“${level}”状态,超载率为${formatRate(countyRate)},实际载畜量${formatHead(livestock)},理论承载力${formatHead(bearing)}。`,
- pressureNames
- ? `乡镇层面,放牧压力相对较高的区域集中在${pressureNames},这些区域应作为后续动态监测和调控的重点。`
- : "乡镇层面的草畜平衡压力整体可控,后续仍需结合季节牧草产量和牲畜流动持续校核。",
- ],
- primaryChart: {
- title: "乡镇超载率对比",
- note: "按超载率由高到低展示前几项",
- rows: pressureRows.slice(0, 8).map((row) => ({
- label: row.name,
- value: row.overloadRate,
- unit: "%",
- })),
- },
- secondaryChart: buildCapacityGapChart(pressureRows),
- table: {
- title: "重点区域与平衡状态",
- columns: ["区域", "超载率", "实际载畜量", "理论承载力", "状态"],
- rows: pressureRows.slice(0, 6).map((row) => [
- row.name,
- formatRate(row.overloadRate),
- formatHead(row.livestockCapacity),
- formatHead(row.bearingCapacity),
- row.level || "待跟踪",
- ]),
- },
- attention: [
- topPressures.length ? `${topPressures[0].name}超载率最高,为${formatRate(topPressures[0].overloadRate)},建议优先复核载畜量和可利用草地面积。` : "暂无明显高压乡镇,需保持常态化监测。",
- "草畜平衡结果受季节牧草产量、牲畜出入栏、补饲能力影响,应定期更新核算口径。",
- "接近平衡上限的区域不宜继续扩大放牧规模,避免从基本平衡转为超载。",
- ],
- recommendations: advice.length ? advice : [
- "对高压力乡镇开展轮牧、休牧和补饲组合管理。",
- "将超载率、载畜量和草地面积纳入月度跟踪,及时发现压力变化。",
- "结合出栏计划和饲草供应能力,动态优化畜群规模。",
- ],
- footnotes: buildReportFootnotes(data, reason),
- }
-}
-
-function buildYakReport(data, reason) {
- const scope = data.context?.scope || "红原县"
- const townRows = rowsOf(data, "yak.townDistribution")
- .map((row) => ({
- name: row.name || row.areaName || row.key,
- count: toLocalNumber(row.count ?? row.value ?? row.total),
- }))
- .filter((row) => row.name && Number.isFinite(row.count))
- .sort((a, b) => b.count - a.count)
- const total = townRows.reduce((sum, row) => sum + row.count, 0)
- const topRows = townRows.slice(0, 5)
- const topShare = total ? topRows.reduce((sum, row) => sum + row.count, 0) / total * 100 : NaN
- const structureRows = rowsOf(data, "yak.structure")
- .map((row) => ({
- name: row.name || row.label || row.key,
- ratio: toLocalNumber(row.ratio ?? row.value),
- count: toLocalNumber(row.count),
- }))
- .filter((row) => row.name && (Number.isFinite(row.ratio) || Number.isFinite(row.count)))
- const forecastRows = rowsOf(data, "yak.forecast")
- const latestForecast = forecastRows[forecastRows.length - 1] || {}
- const latestStock = toLocalNumber(latestForecast.stock)
- const topTown = townRows[0]
-
- return {
- title: "牦牛分布情况",
- subtitle: `${scope} · 牦牛存栏专题`,
- sourceText: "数据来源:平台牦牛存栏与畜群结构数据",
- metrics: [
- metric("牦牛存栏", formatHead(total), "乡镇汇总"),
- metric("最高乡镇", topTown ? topTown.name : "--", topTown ? formatHead(topTown.count) : ""),
- metric("覆盖乡镇", `${townRows.length || "--"}个`, "当前展示范围"),
- metric("预测存栏", formatHead(latestStock), latestForecast.label || "趋势预测"),
- ],
- summary: [
- `${scope}当前牦牛存栏约${formatHead(total)},主要分布在${topRows.map((row) => row.name).join("、") || "重点牧区"}等区域。`,
- Number.isFinite(topShare)
- ? `前五个乡镇合计占全县存栏的${formatRate(topShare)},存栏分布具有较明显的重点区域特征。`
- : "当前已完成乡镇存栏数据整理,可用于后续联动草畜平衡、出栏计划和疫病防控管理。",
- ],
- primaryChart: {
- title: "乡镇牦牛存栏分布",
- note: Number.isFinite(topShare) ? `前五项占比${formatRate(topShare)}` : "",
- rows: townRows.slice(0, 9).map((row) => ({
- label: row.name,
- value: row.count,
- unit: "头",
- })),
- },
- secondaryChart: structureRows.length ? {
- title: "畜群结构占比",
- rows: structureRows.slice(0, 8).map((row) => ({
- label: row.name,
- value: Number.isFinite(row.ratio) ? row.ratio : row.count,
- unit: Number.isFinite(row.ratio) ? "%" : "头",
- })),
- } : null,
- table: {
- title: "重点乡镇存栏情况",
- columns: ["乡镇", "存栏量", "全县占比"],
- rows: townRows.slice(0, 6).map((row) => [
- row.name,
- formatHead(row.count),
- total ? formatRate(row.count / total * 100) : "--",
- ]),
- },
- attention: [
- topTown ? `${topTown.name}存栏量最高,约${formatHead(topTown.count)},建议同步关注草地承载和疫病防控压力。` : "暂无可排序的乡镇存栏数据。",
- "存栏密集区域需要与草畜平衡结果联动研判,避免单看数量忽略草场承载能力。",
- "预测存栏继续上行时,应提前校核出栏节奏、补饲能力和畜群结构。",
- ],
- recommendations: [
- "对高存栏乡镇建立动态监测清单,联动草地面积、出栏和补饲指标。",
- "围绕能繁母牛、犊牛和种公牛结构优化畜群质量,避免单纯追求数量增长。",
- "将存栏分布结果用于牧户服务、疫病防控和草畜平衡调控的优先级排序。",
- ],
- footnotes: buildReportFootnotes(data, reason),
- }
-}
-
-function buildGenericReport(data, reason) {
- const scope = data.context?.scope || "红原县"
- const metrics = normalizeLocalMetrics(data.context?.insights)
- const metricRows = metrics.length ? metrics : deriveLocalMetrics(data.datasets)
- const firstChart = normalizeList(data.datasets)
- .map((dataset) => ({
- title: dataset?.title || "指标对比",
- rows: normalizeList(dataset?.rows)
- .map((row, index) => ({
- label: pickRowLabel(row, index),
- value: pickNumericValue(row),
- unit: row?.unit || dataset?.unit || "",
- }))
- .filter((row) => row.label && Number.isFinite(row.value))
- .slice(0, 8),
- }))
- .find((chart) => chart.rows.length)
- return {
- title: buildLocalTitle(data.question),
- subtitle: `${scope} · 智慧管理专题`,
- sourceText: "数据来源:平台业务汇总数据",
- metrics: metricRows.slice(0, 4).map((item) => metric(item.label, item.value, item.note)),
- summary: [
- `${scope}已完成本次问题相关业务数据整理,并形成专题分析结果。`,
- "报告优先呈现关键指标、主要分布和可执行建议,原始字段明细不作为页面主体展示。",
- ],
- primaryChart: firstChart || { title: "核心指标对比", rows: [] },
- secondaryChart: null,
- table: buildGenericReportTable(data),
- attention: ["需结合业务口径持续复核关键指标变化。", "若涉及乡镇、牧户或生态区域,应按重点对象继续下钻。"],
- recommendations: ["将本报告作为专题研判入口,后续结合业务部门口径补充结论。", "对指标波动较大的对象建立跟踪清单。"],
- footnotes: buildReportFootnotes(data, reason),
- }
-}
-
-function buildCapacityGapChart(rows) {
- const gapRows = rows
- .map((row) => ({
- label: row.name,
- value: Number.isFinite(row.livestockCapacity) && Number.isFinite(row.bearingCapacity)
- ? Math.max(0, row.livestockCapacity - row.bearingCapacity)
- : NaN,
- unit: "头",
- }))
- .filter((row) => row.label && Number.isFinite(row.value) && row.value > 0)
- .slice(0, 8)
- if (!gapRows.length) return null
- return {
- title: "超承载量估算",
- note: "仅展示实际载畜量高于理论承载力的区域",
- rows: gapRows,
- }
-}
-
-function buildGenericReportTable(data) {
- const rows = normalizeList(data.datasets)
- .flatMap((dataset) => normalizeList(dataset?.rows).map((row, index) => ({
- target: pickRowLabel(row, index),
- value: pickNumericValue(row),
- unit: row?.unit || dataset?.unit || "",
- note: dataset?.title || "",
- })))
- .filter((row) => row.target && Number.isFinite(row.value))
- .slice(0, 6)
- return {
- title: "重点指标明细",
- columns: ["对象", "数值", "说明"],
- rows: rows.map((row) => [
- row.target,
- `${formatLocalNumber(row.value)}${row.unit || ""}`,
- row.note,
- ]),
- }
-}
-
-function renderReportMetrics(metrics) {
- const rows = normalizeList(metrics).filter(Boolean).slice(0, 4)
- if (!rows.length) return ""
- return `${rows.map((item) => `
-
- ${escapeHtml(item.label)}
- ${escapeHtml(item.value)}
- ${escapeHtml(item.note || "")}
- `).join("")}`
-}
-
-function renderReportParagraphs(paragraphs) {
- return normalizeList(paragraphs)
- .filter(Boolean)
- .map((item) => `${escapeHtml(item)}
`)
- .join("")
-}
-
-function renderReportBars(chart) {
- const rows = normalizeList(chart?.rows).filter((row) => row?.label && Number.isFinite(Number(row.value))).slice(0, 9)
- if (!rows.length) return ''
- const maxValue = Math.max(...rows.map((row) => Math.abs(Number(row.value))), 1)
- return `
- ${rows.map((row) => {
- const value = Number(row.value)
- const width = Math.max(3, Math.round(Math.abs(value) / maxValue * 100))
- return `
-
${escapeHtml(row.label)}
-
-
${escapeHtml(formatLocalNumber(value))}${escapeHtml(row.unit || "")}
-
`
- }).join("")}
- ${chart?.note ? `
${escapeHtml(chart.note)}
` : ""}
-
`
-}
-
-function renderReportTable(table) {
- const rows = normalizeList(table?.rows).slice(0, 8)
- const columns = normalizeList(table?.columns)
- if (!rows.length || !columns.length) return ''
- return `
- ${columns.map((column) => `| ${escapeHtml(column)} | `).join("")}
-
- ${rows.map((row) => `${normalizeList(row).map((cell) => `| ${escapeHtml(cell)} | `).join("")}
`).join("")}
-
-
`
-}
-
-function renderReportList(items) {
- const rows = normalizeList(items).filter(Boolean).slice(0, 6)
- if (!rows.length) return ''
- return `${rows.map((item) => `- ${escapeHtml(item)}
`).join("")}
`
-}
-
-function metric(label, value, note = "") {
- return {
- label: label || "指标",
- value: value === undefined || value === null || value === "" ? "--" : String(value),
- note: note || "",
- }
-}
-
-function rowsOf(data, id) {
- return normalizeList(normalizeList(data?.datasets).find((dataset) => dataset?.id === id)?.rows)
-}
-
-function firstRow(data, id) {
- return rowsOf(data, id).find(Boolean)
-}
-
-function findRows(data, predicate) {
- return normalizeList(data?.datasets)
- .flatMap((dataset) => normalizeList(dataset?.rows))
- .filter((row) => row && typeof row === "object" && predicate(row))
-}
-
-function findFirstRow(data, predicate) {
- return findRows(data, predicate).find(Boolean)
-}
-
-function rowsOfFirstDataset(data, predicate) {
- const dataset = normalizeList(data?.datasets).find(predicate)
- return normalizeList(dataset?.rows)
-}
-
-function splitAdvice(value) {
- return uniqueText(String(value || "").split(/[;;。.\n]+/)).slice(0, 5)
-}
-
-function buildReportFootnotes(data) {
- const notes = uniqueText([...(data.context?.notes || [])])
- return notes.slice(0, 3)
-}
-
-function formatWanMu(value) {
- const number = Number(value)
- if (!Number.isFinite(number)) return "--"
- return `${formatLocalNumber(number / 10000)}万亩`
-}
-
-function formatHead(value) {
- const number = Number(value)
- if (!Number.isFinite(number)) return "--"
- return `${formatLocalNumber(number)}头`
-}
-
-function formatRate(value) {
- const number = Number(value)
- if (!Number.isFinite(number)) return "--"
- return `${formatLocalNumber(number)}%`
-}
-
-function formatRateSuffix(value) {
- const text = formatRate(value)
- return text === "--" ? "" : `(${text})`
-}
-
function buildArtifactDataJson({ question, answer, charts, datasets, context }) {
const payload = {
question: String(question || "").trim(),
@@ -761,110 +97,6 @@ function buildArtifactDataJson({ question, answer, charts, datasets, context })
return serialized
}
-function buildLocalTitle(question) {
- const text = String(question || "").replace(/[??。!!]+$/g, "").trim()
- return text ? `${text}专题分析` : "智慧管理专题分析"
-}
-
-function normalizeList(value) {
- return Array.isArray(value) ? value : []
-}
-
-function uniqueText(values) {
- const seen = new Set()
- return normalizeList(values)
- .map((item) => String(item || "").trim())
- .filter((item) => {
- if (!item || seen.has(item)) return false
- seen.add(item)
- return true
- })
-}
-
-function normalizeLocalMetrics(insights) {
- return normalizeList(insights)
- .map((item) => ({
- label: item?.label || item?.name || "指标",
- value: item?.value ?? "--",
- note: item?.note || item?.title || "",
- }))
- .filter((item) => item.label && item.value !== undefined && item.value !== null)
- .slice(0, 6)
-}
-
-function deriveLocalMetrics(datasets = []) {
- return normalizeList(datasets)
- .map((dataset) => {
- const row = normalizeList(dataset?.rows).find(Boolean)
- if (!row || typeof row !== "object") return null
- const value = pickNumericValue(row)
- if (!Number.isFinite(value)) return null
- return {
- label: pickRowLabel(row, 0),
- value: `${formatLocalNumber(value)}${row?.unit || dataset?.unit || ""}`,
- note: dataset?.title || "",
- }
- })
- .filter(Boolean)
- .slice(0, 6)
-}
-
-function pickRowLabel(row, index) {
- if (!row || typeof row !== "object") return `指标${index + 1}`
- const direct = row.name || row.label || row.areaName || row.town || row.title || row.key
- if (direct) return String(direct)
- const firstText = Object.values(row).find((value) => typeof value === "string" && value.trim())
- return firstText || `指标${index + 1}`
-}
-
-function pickNumericValue(row) {
- if (!row || typeof row !== "object") return NaN
- const preferredKeys = [
- "value",
- "count",
- "total",
- "rate",
- "ratio",
- "yakNumber",
- "livestockCapacity",
- "overloadRate",
- "onlineRate",
- "areaRange",
- "amount",
- "number",
- ]
- for (const key of preferredKeys) {
- const number = toLocalNumber(row[key])
- if (Number.isFinite(number)) return number
- }
- for (const value of Object.values(row)) {
- const number = toLocalNumber(value)
- if (Number.isFinite(number)) return number
- }
- return NaN
-}
-
-function toLocalNumber(value) {
- if (value === undefined || value === null || value === "") return NaN
- const number = Number(String(value).replace(/,/g, "").replace(/%/g, ""))
- return Number.isFinite(number) ? number : NaN
-}
-
-function formatLocalNumber(value) {
- const number = Number(value)
- if (!Number.isFinite(number)) return value || "--"
- return number.toLocaleString("zh-CN", { maximumFractionDigits: 2 })
-}
-
-function escapeHtml(value) {
- return String(value || "")
- .replace(/&/g, "&")
- .replace(//g, ">")
- .replace(/"/g, """)
- .replace(/'/g, "'")
-}
-
function sanitizeContext(context) {
if (!context || typeof context !== "object") return null
const {
diff --git a/src/services/smartContextBuilder.js b/src/services/smartContextBuilder.js
index 9732993..6846e14 100644
--- a/src/services/smartContextBuilder.js
+++ b/src/services/smartContextBuilder.js
@@ -1,35 +1,44 @@
import { getSmartChartsForQuestion } from "./smartChartApi"
+import {
+ getYakForecastRows as fetchYakForecastRows,
+ getYakStructureRows as fetchYakStructureRows,
+ getYakTownRows as fetchYakTownRows,
+ getYakTradeRows as fetchYakTradeRows,
+} from "./yakApi"
import {
businessMatrixRows,
+ centerOrbitNodes,
dataFlowRows,
homeHeroStats,
homeTopStats,
resourceCompositionRows,
+ supportRows,
townSynergyRows,
+ tradeRows,
yakTownRows as homeYakTownRows,
} from "@/views/home/data"
-import {
- forecastFactorRows,
- forecastPeriods,
- genderAgeRows,
- structureRows,
- townRows as yakTownRows,
- tradeImpactRows as yakTradeRows,
-} from "@/views/yakManagement/data"
import {
pastureAgeRows,
pastureCoreRows,
pastureGenderRows,
+ pastureGrasslandRows,
pastureListRows,
+ pastureMapRows,
pastureOnlineRows,
+ pastureSeasonRows,
+ pastureStructureRows,
+ pastureTopStats,
pastureTownRows,
} from "@/views/pastureManagement/data"
import {
BALANCE_CALCULATION_PARAMS,
+ balanceFallbackRows,
balanceTownRows,
countyBalanceRow,
} from "@/views/grassLivestockBalance/data"
import {
+ engineeringAnalysisPanels,
+ engineeringAnalysisProjectFeatures,
engineeringAnalysisTopStats,
parkCoreRows,
parkPanels,
@@ -39,14 +48,20 @@ import {
restorationTopStats,
} from "@/views/ecologicalProtection/data"
import {
+ breedingLevelRows,
forageSecurityRows,
+ industryChainListRows,
+ industryFinancialRows,
industryOverviewRows,
+ industryPointRows,
industryStageRows,
industryTopStats,
industryValueRows,
+ processingPointRows,
processingAbilityRows,
serviceCapabilityRows,
stageCompositionRows,
+ supportPointRows,
} from "@/views/yakIndustryChain/data"
const PAGE_NAME = "智慧管理"
@@ -99,7 +114,8 @@ export async function buildSmartQuestionContext(question, options = {}) {
const datasetResults = await Promise.all(selectedIntents.map((intent) => intent.build(normalizedQuestion, warnings)))
const datasets = uniqueDatasets(datasetResults.flat())
const charts = await chartsPromise
- const mergedDatasets = datasets.length ? datasets : buildOverviewDatasets()
+ const isOverviewIntent = selectedIntents.length === 1 && selectedIntents[0]?.key === "overview"
+ const mergedDatasets = datasets.length ? datasets : (isOverviewIntent ? buildOverviewDatasets() : [])
const scope = inferScope(normalizedQuestion, mergedDatasets)
const context = {
question: normalizedQuestion,
@@ -158,28 +174,112 @@ function buildOverviewDatasets() {
dataset("home.heroStats", "资源与草畜概览", homeHeroStats, "平台业务汇总数据"),
dataset("home.resourceComposition", "资源构成", resourceCompositionRows, "平台业务汇总数据"),
dataset("home.businessMatrix", "业务矩阵", businessMatrixRows, "平台业务汇总数据"),
+ dataset("home.townSynergy", "乡镇林草湿与牦牛协同", townSynergyRows, "平台业务汇总数据"),
+ dataset("home.yakTown", "首页牦牛乡镇排行", homeYakTownRows, "平台业务汇总数据"),
+ dataset("home.trade", "首页交易结构", tradeRows, "平台业务汇总数据"),
+ dataset("home.support", "首页支撑产业", supportRows, "平台业务汇总数据"),
dataset("home.dataFlow", "数据流汇总", dataFlowRows, "平台业务汇总数据"),
+ dataset("home.centerOrbit", "首页中心节点", centerOrbitNodes, "平台业务汇总数据"),
]
}
-function buildYakDatasets() {
+async function buildYakDatasets(question, warnings = []) {
+ const [townResult, structureResult, forecastResult, tradeResult] = await Promise.allSettled([
+ fetchYakTownRows(),
+ fetchYakStructureRows(),
+ fetchYakForecastRows(),
+ fetchYakTradeRows(),
+ ])
+ appendDatasetWarning(townResult, warnings, "牦牛乡镇存栏数据暂未取得")
+ appendDatasetWarning(structureResult, warnings, "牦牛畜群结构数据暂未取得")
+ appendDatasetWarning(forecastResult, warnings, "牦牛预测数据暂未取得")
+ appendDatasetWarning(tradeResult, warnings, "牦牛交易数据暂未取得")
+
+ const townRows = normalizeActualYakTownRows(getSettledRows(townResult))
+ const structureRows = normalizeActualYakMetricRows(getSettledRows(structureResult))
+ const forecastRows = normalizeActualYakForecastRows(getSettledRows(forecastResult))
+ const tradeRows = normalizeActualYakMetricRows(getSettledRows(tradeResult))
+
return [
- dataset("yak.townDistribution", "乡镇牦牛存栏分布", yakTownRows.length ? yakTownRows : homeYakTownRows, "平台业务汇总数据"),
- dataset("yak.structure", "畜群结构", structureRows, "平台业务汇总数据"),
- dataset("yak.genderAge", "年龄性别结构", genderAgeRows, "平台业务汇总数据"),
- dataset("yak.forecast", "牦牛存栏预测", forecastPeriods, "平台业务汇总数据"),
- dataset("yak.forecastFactors", "预测因素", forecastFactorRows, "平台业务汇总数据"),
- dataset("yak.tradeImpact", "交易影响", yakTradeRows, "平台业务汇总数据"),
+ dataset("yak.town", "乡镇牦牛存栏分布", townRows, "牦牛管理接口数据"),
+ dataset("yak.structureRows", "畜群结构数量", structureRows, "牦牛管理接口数据"),
+ dataset("yak.forecastRows", "牦牛月度预测", forecastRows, "牦牛管理接口数据"),
+ dataset("yak.tradeImpact", "交易影响", tradeRows, "牦牛交易接口数据"),
]
}
+function getSettledRows(result) {
+ return result.status === "fulfilled" && Array.isArray(result.value) ? result.value : []
+}
+
+function appendDatasetWarning(result, warnings, fallback) {
+ if (result.status === "rejected") {
+ warnings.push(getFriendlyContextError(result.reason, fallback))
+ }
+}
+
+function normalizeActualYakTownRows(rows = []) {
+ return rows
+ .map((row, index) => {
+ const name = String(row.name || row.areaName || row.area_name || row.townName || row.zoneName || "").trim()
+ const count = toDatasetNumber(row.count ?? row.value ?? row.number ?? row.yakCount ?? row.yak_count)
+ return {
+ key: row.key || row.id || row.areaCode || row.area_code || `${name || "yak-town"}-${index}`,
+ name,
+ count,
+ value: count,
+ unit: row.unit || "头",
+ }
+ })
+ .filter((row) => row.name && Number.isFinite(row.count))
+ .sort((a, b) => b.count - a.count)
+}
+
+function normalizeActualYakMetricRows(rows = []) {
+ return rows
+ .map((row, index) => {
+ const name = String(row.name || row.label || row.type || row.key || "").trim()
+ const count = toDatasetNumber(row.count ?? row.value ?? row.number ?? row.yakCount ?? row.headCount ?? row.amount)
+ return {
+ key: row.key || row.id || `${name || "yak-metric"}-${index}`,
+ name,
+ count,
+ value: count,
+ unit: row.unit || "头",
+ extra: row.extra || "",
+ }
+ })
+ .filter((row) => row.name && Number.isFinite(row.count))
+ .sort((a, b) => b.count - a.count)
+}
+
+function normalizeActualYakForecastRows(rows = []) {
+ return rows
+ .map((row, index) => {
+ const name = String(row.name || row.label || row.month || row.date || row.time || "").trim()
+ const stock = toDatasetNumber(row.stock ?? row.count ?? row.value ?? row.number ?? row.yakCount)
+ return {
+ key: row.key || row.id || `${name || "yak-forecast"}-${index}`,
+ name,
+ label: name,
+ stock,
+ count: stock,
+ value: stock,
+ unit: row.unit || "头",
+ extra: row.extra || "",
+ }
+ })
+ .filter((row) => row.name && Number.isFinite(row.stock))
+}
+
function buildBalanceDatasets(question) {
const focusRow = findBalanceContextRow(question)
const focusName = focusRow?.name || "红原县"
return [
dataset("balance.target", `${focusName}草畜平衡`, [focusRow], "平台业务汇总数据"),
dataset("balance.county", "全县草畜平衡", [countyBalanceRow], "平台业务汇总数据"),
- dataset("balance.townRows", "乡镇草畜平衡", balanceTownRows, "平台业务汇总数据"),
+ dataset("balance.town", "乡镇草畜平衡", balanceTownRows, "平台业务汇总数据"),
+ dataset("balance.fallbackRows", "草畜平衡兜底数据", balanceFallbackRows, "平台业务汇总数据"),
dataset("balance.params", "草畜平衡计算参数", [BALANCE_CALCULATION_PARAMS], "平台业务汇总数据"),
]
}
@@ -187,11 +287,16 @@ function buildBalanceDatasets(question) {
function buildPastureDatasets() {
return [
dataset("pasture.core", "牧户核心指标", pastureCoreRows, "平台业务汇总数据"),
- dataset("pasture.townDistribution", "牧户乡镇分布", pastureTownRows, "平台业务汇总数据"),
+ dataset("pasture.topStats", "牧户顶部指标", pastureTopStats, "平台业务汇总数据"),
+ dataset("pasture.town", "牧户乡镇分布", pastureTownRows, "平台业务汇总数据"),
dataset("pasture.online", "牧户在线率", pastureOnlineRows, "平台业务汇总数据"),
+ dataset("pasture.structure", "牧户年龄性别结构", pastureStructureRows, "平台业务汇总数据"),
dataset("pasture.age", "牧户年龄结构", pastureAgeRows, "平台业务汇总数据"),
dataset("pasture.gender", "牧户性别结构", pastureGenderRows, "平台业务汇总数据"),
- dataset("pasture.examples", "牧户示例列表", pastureListRows, "平台业务汇总数据"),
+ dataset("pasture.list", "牧户示例列表", pastureListRows, "平台业务汇总数据"),
+ dataset("pasture.grassland", "牧户草场资源", pastureGrasslandRows, "平台业务汇总数据"),
+ dataset("pasture.season", "牧户季节草场", pastureSeasonRows, "平台业务汇总数据"),
+ dataset("pasture.map", "牧户地图统计", pastureMapRows, "平台业务汇总数据"),
]
}
@@ -202,8 +307,10 @@ function buildEcologyDatasets() {
dataset("ecology.parkTownRows", "国家公园乡镇数据", parkTownRows, "平台业务汇总数据"),
dataset("ecology.restorationTopStats", "生态修复项目汇总", restorationTopStats, "平台业务汇总数据"),
dataset("ecology.engineeringTopStats", "生态工程分析指标", engineeringAnalysisTopStats, "平台业务汇总数据"),
+ dataset("ecology.engineeringProjectFeatures", "工程分析项目点", engineeringAnalysisProjectFeatures, "平台业务汇总数据"),
dataset("ecology.parkPanels", "生态保护图表数据", flattenPanelRows(parkPanels), "平台业务汇总数据"),
dataset("ecology.restorationPanels", "生态修复图表数据", flattenPanelRows(restorationPanels), "平台业务汇总数据"),
+ dataset("ecology.engineeringAnalysisPanels", "工程分析图表数据", flattenPanelRows(engineeringAnalysisPanels), "平台业务汇总数据"),
dataset("home.resourceComposition", "林草湿资源构成", resourceCompositionRows, "平台业务汇总数据"),
dataset("home.townSynergy", "乡镇林草湿与牦牛协同", townSynergyRows, "平台业务汇总数据"),
]
@@ -215,10 +322,16 @@ function buildIndustryDatasets() {
dataset("industry.overview", "产业链总览", industryOverviewRows, "平台业务汇总数据"),
dataset("industry.stageComposition", "产业阶段构成", stageCompositionRows, "平台业务汇总数据"),
dataset("industry.stageRows", "产业阶段说明", industryStageRows, "平台业务汇总数据"),
+ dataset("industry.breedingLevel", "养殖水平", breedingLevelRows, "平台业务汇总数据"),
dataset("industry.forageSecurity", "饲草保障能力", forageSecurityRows, "平台业务汇总数据"),
dataset("industry.value", "产业价值指标", industryValueRows, "平台业务汇总数据"),
dataset("industry.processingAbility", "加工能力指标", processingAbilityRows, "平台业务汇总数据"),
dataset("industry.serviceCapability", "服务能力指标", serviceCapabilityRows, "平台业务汇总数据"),
+ dataset("industry.financial", "金融服务", industryFinancialRows, "平台业务汇总数据"),
+ dataset("industry.supportPoints", "饲草储备点位", supportPointRows, "平台业务汇总数据"),
+ dataset("industry.processingPoints", "加工屠宰点位", processingPointRows, "平台业务汇总数据"),
+ dataset("industry.points", "产业链点位", industryPointRows, "平台业务汇总数据"),
+ dataset("industry.list", "产业链主体列表", industryChainListRows, "平台业务汇总数据"),
]
}
@@ -369,6 +482,12 @@ function getFriendlyContextError(error, fallback) {
return fallback
}
+function toDatasetNumber(value) {
+ if (value === undefined || value === null || value === "") return NaN
+ const number = Number(String(value).replace(/,/g, "").replace(/%/g, ""))
+ return Number.isFinite(number) ? number : NaN
+}
+
function formatNumber(value) {
const number = Number(String(value).replace(/,/g, ""))
if (!Number.isFinite(number)) return value || "--"
diff --git a/src/views/wisdomManagement/index.vue b/src/views/wisdomManagement/index.vue
index eee522e..6641ab5 100644
--- a/src/views/wisdomManagement/index.vue
+++ b/src/views/wisdomManagement/index.vue
@@ -217,7 +217,7 @@