From e4b4bf530f8d290e079f806a218ab8116abdff5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?M=E5=85=88=E7=94=9F?= <1211120248@qq.com> Date: Tue, 7 Jul 2026 02:29:32 +0800 Subject: [PATCH] fix: align pasture and smart management views --- src/services/openApi.js | 252 ++++++++----- src/services/pastureApi.js | 8 +- src/services/smartApi.js | 2 +- src/services/smartChartApi.js | 12 +- .../forestGrassWet/components/LayerPanel.vue | 25 +- .../ProportionPopulationConsumption.vue | 74 +--- src/views/gdMap/livestockData.js | 39 +- src/views/pastureManagement/index.vue | 199 +++++++++- src/views/platform/index.vue | 79 +++- .../components/SmartChartRenderer.vue | 30 +- src/views/wisdomManagement/index.vue | 72 ++-- src/views/yakIndustryChain/index.vue | 5 +- src/views/yakManagement/YakOlMap.vue | 153 +++++++- src/views/yakManagement/data.js | 76 ++-- src/views/yakManagement/index.vue | 352 +++++++++++------- 15 files changed, 934 insertions(+), 444 deletions(-) diff --git a/src/services/openApi.js b/src/services/openApi.js index 412bac1..b69ee63 100644 --- a/src/services/openApi.js +++ b/src/services/openApi.js @@ -5,7 +5,15 @@ import { getHongyuanTownshipNameByCode, hongyuanTownshipDisplayOrder } from "@/c const OPEN_API_BASE = import.meta.env.VITE_OPEN_API_BASE || "/openApi" const CXPT_GRASSLAND_AREA_WAN_MU = 756.37 +const CXPT_GRASSLAND_YIELD_WAN_TON = 297.29 const CXPT_WETLAND_AREA_WAN_MU = 291.82 +const CXPT_WETLAND_EDIBLE_YIELD_WAN_TON = 16.47 +const GRASSLAND_DEGRADATION_SOURCE_ROWS = [ + { key: "none", name: "未退化", value: 481.8, unit: "万亩" }, + { key: "light", name: "轻度退化", value: 231.25, unit: "万亩" }, + { key: "middle", name: "中度退化", value: 23.7, unit: "万亩" }, + { key: "heavy", name: "重度退化", value: 20.21, unit: "万亩" }, +] const HONGYUAN_TOWN_DISPLAY_ORDER = hongyuanTownshipDisplayOrder.map(normalizeTownName) const WETLAND_CHART_PALETTE = ["#32DCFF", "#24F6C4", "#6EA8FF", "#FFE06B", "#7EE269", "#9EA7FF"] const WOODLAND_CHART_PALETTE = ["#2BD17E", "#8AE611", "#24F6C4", "#FFE06B", "#32DCFF", "#9EA7FF"] @@ -101,6 +109,20 @@ function normalizeDataItems(data) { return rows.map(normalizeMetricItem).filter((item) => item.name) } +function normalizeGrasslandDataItems(data) { + return normalizeDataItems(data).map((item) => { + const signal = `${item?.key || ""}${item?.name || ""}` + if (/gransLand_produce|grassland_grass_yield|草地生产力|草地鲜草产量|鲜草产量/.test(signal)) { + return { + ...item, + value: CXPT_GRASSLAND_YIELD_WAN_TON, + unit: "万吨", + } + } + return item + }) +} + function normalizeWoodlandDistribution(data) { const core = data?.core || data?.summary || data || {} const mainType = (data?.typeDistribution || data?.typeStats || [])[0] || {} @@ -720,6 +742,48 @@ function withPercentExtra(rows = [], total = sumRowValues(rows), label = "") { }) } +function appendRemainderRowToTotal(rows = [], total, options = {}) { + const { + key = "unclassified", + name = "未分类", + unit = "万亩", + sort = true, + } = options + const target = toNumber(total) + if (target === undefined) return rows + const currentTotal = sumRowValues(rows) + const remainder = formatNumber(target - currentTotal) + if (remainder === undefined || remainder <= 0.01) return rows + const result = [ + ...rows, + { + key, + name, + value: remainder, + unit, + }, + ] + return sort ? result.sort((a, b) => (Number(b.value) || 0) - (Number(a.value) || 0)) : result +} + +function scaleRowsToTotal(rows = [], total) { + const target = toNumber(total) + const sourceTotal = sumRowValues(rows) + if (target === undefined || !sourceTotal) return rows + let assignedTotal = 0 + return rows.map((row, index) => { + const isLast = index === rows.length - 1 + const value = isLast + ? formatNumber(Math.max(target - assignedTotal, 0)) + : formatNumber(((Number(row.value) || 0) / sourceTotal) * target) + assignedTotal += Number(value) || 0 + return { + ...row, + value, + } + }) +} + function colorRowsByPalette(rows = [], palette = []) { if (!palette.length) return rows return rows.map((row, index) => ({ @@ -953,6 +1017,22 @@ function buildWetlandYieldCapacityRows(yieldSummary = {}, yieldRows = []) { ].filter((row) => row.value !== undefined) } +function normalizeWetlandYieldSummary(yieldSummary = {}, edibleYieldRows = [], yieldRows = []) { + const unit = yieldSummary?.unit || pickValue(edibleYieldRows?.[0], ["unit"]) || pickValue(yieldRows?.[0], ["unit"]) || "万吨" + const total = toNumber(yieldSummary?.xc) ?? sumRowValues(yieldRows) + const summaryEdible = toNumber(yieldSummary?.edibleXc) + const distributionEdible = sumRowValues(edibleYieldRows) + const edible = summaryEdible !== undefined || distributionEdible > 0 + ? Math.max(summaryEdible || 0, distributionEdible || 0) + : undefined + return { + ...yieldSummary, + xc: formatNumber(total), + edibleXc: formatNumber(edible), + unit, + } +} + function buildWetlandDefaultPanels({ yieldSummary, townAreaRows, @@ -976,10 +1056,10 @@ function buildWetlandDefaultPanels({ const edibleYieldPanel = buildPanel("wetland-edible-yield", "可食鲜草产量", topRows(edibleYieldRows, hongyuanTownshipDisplayOrder.length), "column", { span: 1, variant: "slim" }) const muYieldPanel = buildPanel("wetland-mu-yield", "湿地亩产效率", topRows(muYieldRows, hongyuanTownshipDisplayOrder.length), "heat", { span: 1, limit: hongyuanTownshipDisplayOrder.length }) const levelPanel = buildPanel("wetland-level", "湿地级别面积", levelRows, "column", { span: 1 }) - const levelTownPanel = buildPanel("wetland-level-town", "1级湿地分布", topRows(levelTownRows, 6), "bar", { span: 1, variant: "distribution" }) - const typeTownPanel = buildPanel("wetland-type-town", "湿地类型乡镇分布", topRows(typeTownRows, 6), "bar", { span: 1, variant: "distribution" }) + const levelTownPanel = buildPanel("wetland-level-town", "1级湿地分布", topRows(levelTownRows, hongyuanTownshipDisplayOrder.length), "bar", { span: 1, variant: "distribution" }) + const typeTownPanel = buildPanel("wetland-type-town", "湿地类型乡镇分布", topRows(typeTownRows, hongyuanTownshipDisplayOrder.length), "bar", { span: 1, variant: "distribution" }) const edibleMuPanel = buildPanel("wetland-edible-mu", "可食鲜草亩产", topRows(edibleMuRows, hongyuanTownshipDisplayOrder.length), "bar", { span: 1 }) - const minTownPanel = buildPanel("wetland-min-town", "优势小类乡镇分布", topRows(minTownRows, 6), "bar", { span: 1, variant: "distribution" }) + const minTownPanel = buildPanel("wetland-min-town", "优势小类乡镇分布", topRows(minTownRows, hongyuanTownshipDisplayOrder.length), "bar", { span: 1, variant: "distribution" }) const leftPanels = diversifyPanelColumn([ townAreaPanel, typePanel, @@ -1198,57 +1278,27 @@ async function getGrasslandDegradationRows() { return (await getGrasslandDegradationStats()).levelRows } -async function getGrasslandDegradationStats() { - const data = await getWfsFeatureProperties("ne:t_grassland_result_caodi", { - propertyName: ["area_code", "degradation_level", "acreage"], - maxFeatures: 200000, - timeout: 25000, - }) - const levelGrouped = new Map() - const townGrouped = new Map() - ;(data?.features || []).forEach((feature) => { - const properties = feature.properties || {} - const name = normalizeGrasslandDegradationName(properties.degradation_level) - const acreage = toNumber(properties.acreage) - if (!name || acreage === undefined) return - const areaWanMu = acreage / 10000 - levelGrouped.set(name, (levelGrouped.get(name) || 0) + areaWanMu) - if (name === "未退化") return - const townCode = String(properties.area_code || "").slice(0, 9) - const townName = getHongyuanTownshipNameByCode(townCode) - if (!townName) return - townGrouped.set(townName, (townGrouped.get(townName) || 0) + areaWanMu) - }) - const levelOrder = ["未退化", "轻度退化", "中度退化", "重度退化", "未分级"] - const levelRows = Array.from(levelGrouped.entries()) - .map(([name, value]) => ({ - key: name, - name, - value: formatNumber(value), - unit: "万亩", - })) - .filter((row) => Number(row.value) > 0) - .sort((a, b) => { - const orderA = levelOrder.indexOf(a.name) - const orderB = levelOrder.indexOf(b.name) - if (orderA !== -1 || orderB !== -1) { - return (orderA === -1 ? 99 : orderA) - (orderB === -1 ? 99 : orderB) - } - return (Number(b.value) || 0) - (Number(a.value) || 0) - }) - const townRows = Array.from(townGrouped.entries()) - .map(([name, value]) => ({ - key: name, - name, - value: formatNumber(value), - unit: "万亩", - })) - .filter((row) => Number(row.value) > 0) - .sort((a, b) => (Number(b.value) || 0) - (Number(a.value) || 0)) - return { - levelRows: withPercentExtra(levelRows), - townRows, +function buildGrasslandDegradationLevelRows(rows = GRASSLAND_DEGRADATION_SOURCE_ROWS) { + const levelRows = rows.map((row) => ({ + ...row, + value: formatNumber(row.value), + unit: row.unit || "万亩", + })) + const totalDiff = formatNumber(CXPT_GRASSLAND_AREA_WAN_MU - sumRowValues(levelRows)) + const noneRow = levelRows.find((row) => row.name === "未退化") + if (noneRow && totalDiff !== undefined && Math.abs(totalDiff) > 0.01) { + noneRow.value = formatNumber(Math.max((Number(noneRow.value) || 0) + totalDiff, 0)) } + const levelOrder = ["未退化", "轻度退化", "中度退化", "重度退化"] + return withPercentExtra(levelRows.sort((a, b) => { + const orderA = levelOrder.indexOf(a.name) + const orderB = levelOrder.indexOf(b.name) + return (orderA === -1 ? 99 : orderA) - (orderB === -1 ? 99 : orderB) + }), CXPT_GRASSLAND_AREA_WAN_MU) +} + +async function getGrasslandDegradationStats() { + return fallbackGrasslandDegradationStats() } function findGrasslandCoreRow(rows = [], keywords = []) { @@ -1325,10 +1375,18 @@ function buildGrasslandTopRows(core = [], yieldSummary = {}, yieldRows = []) { ].filter((row) => row?.value !== undefined) } +function normalizeGrasslandYieldSummary(yieldSummary = {}, yieldRows = []) { + return { + ...yieldSummary, + xc: CXPT_GRASSLAND_YIELD_WAN_TON, + unit: yieldSummary?.unit || pickValue(yieldRows?.[0], ["unit"]) || "万吨", + } +} + function buildGrasslandDegradationDistributionRows(rows = []) { const total = sumRowValues(rows) return rows - .filter((row) => row.name && row.name !== "未退化" && Number(row.value) > 0) + .filter((row) => row.name && Number(row.value) > 0) .map((row) => ({ ...row, color: getGrasslandDegradationColor(row.name), @@ -1339,8 +1397,9 @@ function buildGrasslandDegradationDistributionRows(rows = []) { function getGrasslandDegradationColor(name = "") { const text = String(name) + if (text.includes("未退化")) return "#32DCFF" if (text.includes("重度")) return "#FFE06B" - if (text.includes("中度")) return "#32DCFF" + if (text.includes("中度")) return "#6EA8FF" return "#24F6C4" } @@ -1412,12 +1471,7 @@ function fallbackGrasslandDegradationRows() { } function fallbackGrasslandDegradationStats() { - const levelRows = withPercentExtra([ - { key: "none", name: "未退化", value: 642.42, unit: "万亩" }, - { key: "light", name: "轻度退化", value: 83.95, unit: "万亩" }, - { key: "middle", name: "中度退化", value: 17.49, unit: "万亩" }, - { key: "heavy", name: "重度退化", value: 13.09, unit: "万亩" }, - ]) + const levelRows = buildGrasslandDegradationLevelRows() const townRows = [ { key: "qiongxi", name: "邛溪镇", value: 14.72, unit: "万亩" }, { key: "sedi", name: "色地镇", value: 13.86, unit: "万亩" }, @@ -1528,7 +1582,7 @@ function buildFallbackTopicStats(topicKey, layerKey) { { key: "unhealthy", name: "不健康", value: 20.27, unit: "万亩" }, ]) const forageRows = buildGrasslandForageCapacityRows({ - xc: 297.29, + xc: CXPT_GRASSLAND_YIELD_WAN_TON, edibleXc: 192.26, unit: "万吨", }, yieldRows) @@ -1558,7 +1612,7 @@ function buildFallbackTopicStats(topicKey, layerKey) { const result = { status: "fallback", ...grasslandPanels, - top: buildGrasslandTopRows(core, { xc: 297.29, unit: "万吨" }, yieldRows), + top: buildGrasslandTopRows(core, { xc: CXPT_GRASSLAND_YIELD_WAN_TON, unit: "万吨" }, yieldRows), } return applyLayerPanelSelection(result, topicKey, layerKey) } @@ -1569,18 +1623,24 @@ function buildFallbackTopicStats(topicKey, layerKey) { { key: "wetland-yield", name: "湿地生产力(鲜草)", value: 108.84, unit: "万吨" }, { key: "wetland-level-1-area", name: "1级湿地面积", value: 213.22, unit: "万亩" }, ], topicKey) - const typeRows = aggregateTailRows([ + const typeRows = appendRemainderRowToTotal(aggregateTailRows([ { name: "高寒草甸类", value: 190.32, unit: "万亩" }, { name: "山地草甸类", value: 49.48, unit: "万亩" }, { name: "低地草甸类", value: 3.21, unit: "万亩" }, - ], { limit: 4, minPercent: 1, otherName: "其他湿地类型" }) - const levelRows = [ + ], { limit: 4, minPercent: 1, otherName: "其他湿地类型" }), CXPT_WETLAND_AREA_WAN_MU, { + key: "wetland-type-unclassified", + name: "未分类湿地", + }) + const levelRows = appendRemainderRowToTotal([ { name: "1级湿地", value: 213.22, unit: "万亩" }, { name: "2级湿地", value: 22.99, unit: "万亩" }, { name: "3级湿地", value: 6.42, unit: "万亩" }, { name: "4级湿地", value: 0.36, unit: "万亩" }, { name: "5级湿地", value: 0.02, unit: "万亩" }, - ] + ], CXPT_WETLAND_AREA_WAN_MU, { + key: "wetland-level-unclassified", + name: "未分级湿地", + }) const yieldRows = normalizeRankRows([ { areaName: "色地镇", yield: 21.32 }, { areaName: "瓦切镇", yield: 17.49 }, @@ -1605,20 +1665,23 @@ function buildFallbackTopicStats(topicKey, layerKey) { { name: "江茸乡", value: 827.92, unit: "斤" }, { name: "龙日镇", value: 825.59, unit: "斤" }, ] - const townAreaRows = [ + const townAreaRows = scaleRowsToTotal([ { name: "瓦切镇", value: 36.38, unit: "万亩" }, { name: "邛溪镇", value: 29.29, unit: "万亩" }, { name: "阿木乡", value: 25.26, unit: "万亩" }, { name: "龙日镇", value: 24.59, unit: "万亩" }, { name: "色地镇", value: 24.62, unit: "万亩" }, - ].sort((a, b) => b.value - a.value) - const minTypeRows = [ + ].sort((a, b) => b.value - a.value), CXPT_WETLAND_AREA_WAN_MU) + const minTypeRows = appendRemainderRowToTotal([ { name: "矮生嵩草、杂类草型", value: 108.35, unit: "万亩" }, { name: "高山嵩草、矮生嵩草型", value: 34.36, unit: "万亩" }, { name: "西藏嵩草、杂类草型", value: 32.37, unit: "万亩" }, { name: "早熟禾、杂类草型", value: 20.99, unit: "万亩" }, { name: "苔草、杂类草型", value: 14.63, unit: "万亩" }, - ] + ], CXPT_WETLAND_AREA_WAN_MU, { + key: "wetland-min-type-unclassified", + name: "未分类湿地", + }) const edibleYieldRows = normalizeRankRows([ { areaName: "色地镇", yield: 2.82 }, { areaName: "邛溪镇", yield: 2.71 }, @@ -1635,7 +1698,7 @@ function buildFallbackTopicStats(topicKey, layerKey) { ], { nameKeys: ["areaName"], valueKeys: ["yield"], unit: "斤" }) const wetlandPanels = buildWetlandDefaultPanels({ core, - yieldSummary: { xc: 108.84, edibleXc: 1.65, unit: "万吨" }, + yieldSummary: { xc: 108.84, edibleXc: CXPT_WETLAND_EDIBLE_YIELD_WAN_TON, unit: "万吨" }, townAreaRows, typeRows, levelRows, @@ -1842,11 +1905,14 @@ function normalizeWetlandBusiness(items, stats = {}) { const levelDistributionRows = stats.levelDistributionRows || [] const minTypeDistributionRows = stats.minTypeDistributionRows || [] const yieldSummary = stats.yieldSummary || {} - const wetlandTypeRows = aggregateTailRows(normalizeMetricRows(typeRows, { + const wetlandTypeRows = appendRemainderRowToTotal(aggregateTailRows(normalizeMetricRows(typeRows, { nameKeys: ["name"], valueKeys: ["value"], unit: "万亩", - }), { limit: 4, minPercent: 1, otherName: "其他湿地类型" }) + }), { limit: 4, minPercent: 1, otherName: "其他湿地类型" }), CXPT_WETLAND_AREA_WAN_MU, { + key: "wetland-type-unclassified", + name: "未分类湿地", + }) const wetlandYieldRows = normalizeYieldRows(yieldRows, stats.muYieldRows || [], { showMuYieldExtra: false }) const wetlandMuYieldRows = normalizeMetricRows(stats.muYieldRows || [], { nameKeys: ["areaName", "name"], @@ -1854,12 +1920,18 @@ function normalizeWetlandBusiness(items, stats = {}) { unit: "斤", max: hongyuanTownshipDisplayOrder.length, }) - const wetlandLevelRows = normalizeMetricRows(levelRows, { + const wetlandLevelRows = appendRemainderRowToTotal(normalizeMetricRows(levelRows, { nameKeys: ["name"], valueKeys: ["value"], unit: "万亩", - }).map((row) => ({ ...row, name: normalizeWetlandLevelName(row.name) })) - const wetlandMinTypeRows = normalizeMuRows(minTypeRows) + }).map((row) => ({ ...row, name: normalizeWetlandLevelName(row.name) })), CXPT_WETLAND_AREA_WAN_MU, { + key: "wetland-level-unclassified", + name: "未分级湿地", + }) + const wetlandMinTypeRows = aggregateTailRows(appendRemainderRowToTotal(normalizeMuRows(minTypeRows), CXPT_WETLAND_AREA_WAN_MU, { + key: "wetland-min-type-unclassified", + name: "未分类湿地", + }), { limit: 6, minPercent: 1, otherName: "其他小类" }) const wetlandEdibleRows = normalizeYieldRows(edibleRows, edibleMuRows, { showMuYieldExtra: false }) const wetlandEdibleMuRows = normalizeMetricRows(edibleMuRows, { nameKeys: ["areaName", "name"], @@ -1867,33 +1939,34 @@ function normalizeWetlandBusiness(items, stats = {}) { unit: pickValue(edibleMuRows?.[0], ["unit"]) || "斤", max: hongyuanTownshipDisplayOrder.length, }) - const wetlandTownAreaRows = aggregateAreaWanMuRows(typeDistributionRows, { + const wetlandTownAreaRows = scaleRowsToTotal(aggregateAreaWanMuRows(typeDistributionRows, { nameKeys: ["areaName", "name"], valueKeys: ["acreage", "areaWanMu", "value"], - }) - const wetlandTypeTownRows = aggregateAreaWanMuRows(typeDistributionRows, { + }), CXPT_WETLAND_AREA_WAN_MU) + const wetlandTypeTownRows = scaleRowsToTotal(aggregateAreaWanMuRows(typeDistributionRows, { nameKeys: ["areaName", "name"], valueKeys: ["acreage", "areaWanMu", "value"], extra: (row) => `${row.source?.length || 0}类`, - }) - const wetlandLevelTownRows = aggregateAreaWanMuRows(levelDistributionRows, { + }), CXPT_WETLAND_AREA_WAN_MU) + const wetlandLevelTownRows = scaleRowsToTotal(aggregateAreaWanMuRows(levelDistributionRows, { nameKeys: ["areaName", "name"], valueKeys: ["acreage", "areaWanMu", "value"], - }) + }), CXPT_WETLAND_AREA_WAN_MU) const wetlandLevelOneTownRows = aggregateAreaWanMuRows(levelDistributionRows, { nameKeys: ["areaName", "name"], valueKeys: ["acreage", "areaWanMu", "value"], filter: (row) => normalizeWetlandLevelName(pickValue(row, ["level", "name"])).startsWith("1级"), }) - const wetlandMinTypeTownRows = aggregateAreaWanMuRows(minTypeDistributionRows, { + const wetlandMinTypeTownRows = scaleRowsToTotal(aggregateAreaWanMuRows(minTypeDistributionRows, { nameKeys: ["areaName", "name"], valueKeys: ["acreage", "areaWanMu", "value"], extra: (row) => `${row.source?.filter((item) => normalizeAreaValueToWanMu(item, ["acreage", "areaWanMu", "value"]) > 0).length || 0}型`, - }) + }), CXPT_WETLAND_AREA_WAN_MU) + const wetlandYieldSummary = normalizeWetlandYieldSummary(yieldSummary, wetlandEdibleRows, wetlandYieldRows) const wetlandCore = ensureCoreShape(normalized, "wetland") const levelOneArea = wetlandLevelRows.find((row) => /^1级/.test(row.name)) || wetlandLevelRows[0] const wetlandPanels = buildWetlandDefaultPanels({ - yieldSummary, + yieldSummary: wetlandYieldSummary, townAreaRows: wetlandTownAreaRows, typeRows: wetlandTypeRows, levelRows: wetlandLevelRows, @@ -2086,6 +2159,7 @@ export async function getTopicBusinessStats(topicKey, layerKey) { const degradationRows = degradationStats.levelRows || [] const core = ensureCoreShape(normalizeBasicGrassland(data), topicKey) const grassYieldRows = normalizeYieldRows(yieldRows || [], muYieldRows || [], { showMuYieldExtra: false }) + const grassYieldSummary = normalizeGrasslandYieldSummary(yieldSummary, grassYieldRows) const muYieldRankRows = normalizeRankRows(muYieldRows || [], { nameKeys: ["areaName", "name"], valueKeys: ["yield", "value"], @@ -2122,13 +2196,13 @@ export async function getTopicBusinessStats(topicKey, layerKey) { healthRows: normalizedHealthRows, productivityRows: muYieldRankRows, edibleYieldRows, - forageRows: buildGrasslandForageCapacityRows(yieldSummary, grassYieldRows), + forageRows: buildGrasslandForageCapacityRows(grassYieldSummary, grassYieldRows), degradationRows, }) const result = { status: "success", ...grasslandPanels, - top: buildGrasslandTopRows(core, yieldSummary, grassYieldRows), + top: buildGrasslandTopRows(core, grassYieldSummary, grassYieldRows), } return applyLayerPanelSelection(result, topicKey, layerKey) } @@ -2212,7 +2286,7 @@ export async function getTopicCoreStats(topicKey) { async () => normalizeResponseData(await get("/index/dataItem", { group: "grassLand" })), async () => normalizeResponseData(await get("/index/dataItem", { group: "grassland" })), ]) - const items = Array.isArray(data) ? normalizeDataItems(data) : normalizeBasicGrassland(data) + const items = Array.isArray(data) ? normalizeGrasslandDataItems(data) : normalizeBasicGrassland(data) return { status: "success", items: ensureCoreShape(items, topicKey), diff --git a/src/services/pastureApi.js b/src/services/pastureApi.js index 20c0688..f037eac 100644 --- a/src/services/pastureApi.js +++ b/src/services/pastureApi.js @@ -74,6 +74,12 @@ export function getGrasslandClickInfo(params = {}) { return get("/api/home/grasslandClickInfo", params, { timeout: 20000 }).then((data) => data?.data || data || null) } +export function getYakMarksByGrasslandIds(ids = []) { + const grasslandIds = Array.from(new Set((Array.isArray(ids) ? ids : [ids]).map((id) => String(id || "").trim()).filter(Boolean))) + if (!grasslandIds.length) return Promise.resolve([]) + return get("/api/home/yakMarksByGrasslandIds", { ids: grasslandIds.join(",") }, { timeout: 20000 }).then(normalizeRows) +} + export function getPastureDetailsById(id) { return get("/api/pastureDetailsById", { id }).then((data) => data?.data || data || {}) } @@ -93,7 +99,7 @@ export async function getPastureProfile() { getPastureAge(), getPastureOnline(), getPastureList(), - getMuhuList("", 1, 10000), + getMuhuList("", 1, 20), ]) const pastureRows = pastureList.status === "fulfilled" ? pastureList.value : [] const muhuRows = muhuList.status === "fulfilled" ? muhuList.value : [] diff --git a/src/services/smartApi.js b/src/services/smartApi.js index 4c28f91..80c11eb 100644 --- a/src/services/smartApi.js +++ b/src/services/smartApi.js @@ -62,7 +62,7 @@ export function askStatQuestion(question, pagePath = window.location.hash || win pagePath, pageName, }, - { timeout: 45000 }, + { timeout: 90000 }, ) } diff --git a/src/services/smartChartApi.js b/src/services/smartChartApi.js index 08f73b3..9d7446f 100644 --- a/src/services/smartChartApi.js +++ b/src/services/smartChartApi.js @@ -43,7 +43,7 @@ function normalizeChart(chart, index) { id: chart?.id || `smart-chart-${index}`, type: normalizeChartType(chart?.type), title: chart?.title || `统计图表${index + 1}`, - subtitle: chart?.subtitle || buildSourceSubtitle(chart?.source), + subtitle: cleanChartSubtitle(chart?.subtitle), unit: chart?.unit || "", source: chart?.source || {}, rows: rows @@ -65,10 +65,12 @@ function normalizeChartType(type) { return "bar" } -function buildSourceSubtitle(source = {}) { - const table = source.table ? `来源:${source.table}` : "来源:openApi 统计库" - const period = source.period ? `,${source.period}` : "" - return `${table}${period}` +function cleanChartSubtitle(value) { + const text = String(value || "").trim() + if (!text) return "" + if (/^(数据)?来源[::]/.test(text)) return "" + if (text.includes("固定展示")) return "" + return text } function toNumber(value) { diff --git a/src/views/forestGrassWet/components/LayerPanel.vue b/src/views/forestGrassWet/components/LayerPanel.vue index eac5484..608ab26 100644 --- a/src/views/forestGrassWet/components/LayerPanel.vue +++ b/src/views/forestGrassWet/components/LayerPanel.vue @@ -7,8 +7,8 @@ :key="layer.key" class="layer-panel-item" :class="{ - 'is-active': layer.key === modelValue, - 'is-checked': layer.key === modelValue, + 'is-active': isLayerActive(layer.key), + 'is-checked': isLayerActive(layer.key), }" type="button" @mousedown.prevent @@ -70,7 +70,7 @@ const props = defineProps({ required: true, }, modelValue: { - type: String, + type: [String, Array], default: "", }, legends: { @@ -97,12 +97,20 @@ const props = defineProps({ type: Boolean, default: false, }, + multiple: { + type: Boolean, + default: false, + }, }) const emit = defineEmits(["update:modelValue", "update:selectedLegendKey"]) const panelLayers = computed(() => Array.isArray(props.layers) ? props.layers : getLayersByTopic(props.topicKey)) const panelLegends = computed(() => Array.isArray(props.legends) ? props.legends : []) +const selectedLayerKeys = computed(() => { + if (Array.isArray(props.modelValue)) return props.modelValue.map(String).filter(Boolean) + return props.modelValue ? [String(props.modelValue)] : [] +}) const legendSelectable = computed(() => panelLegends.value.filter(isSelectableLegend).length > 1) const legendSingle = computed(() => panelLegends.value.length === 1) const panelHeight = computed(() => { @@ -112,9 +120,20 @@ const panelHeight = computed(() => { function handleLayerClick(event, layerKey) { event.currentTarget?.blur?.() + if (props.multiple) { + const nextKeys = new Set(selectedLayerKeys.value) + if (nextKeys.has(layerKey)) nextKeys.delete(layerKey) + else nextKeys.add(layerKey) + emit("update:modelValue", Array.from(nextKeys)) + return + } emit("update:modelValue", props.modelValue === layerKey ? "" : layerKey) } +function isLayerActive(layerKey) { + return selectedLayerKeys.value.includes(String(layerKey)) +} + function handleLegendClick(event, item) { event.currentTarget?.blur?.() if (!isLegendItemSelectable(item)) return diff --git a/src/views/gdMap/components/ProportionPopulationConsumption.vue b/src/views/gdMap/components/ProportionPopulationConsumption.vue index 5d606ad..936ebe6 100644 --- a/src/views/gdMap/components/ProportionPopulationConsumption.vue +++ b/src/views/gdMap/components/ProportionPopulationConsumption.vue @@ -24,8 +24,17 @@ import mCard from "@/components/mCard/index.vue" import VChart from "vue-echarts" import { herdStructureRows } from "../livestockData" +const pieGradients = [ + ["rgba(3,65,128,1)", "rgba(115,208,255,1)"], + ["rgba(11, 77, 44, 1)", "rgba(77, 255, 181, 1)"], + ["rgba(117, 117, 117, 1)", "rgba(230, 230, 230, 1)"], + ["rgba(153, 105, 38, 1)", "rgba(255, 200, 89, 1)"], + ["rgba(114, 54, 36, 1)", "rgba(255, 122, 69, 1)"], + ["rgba(41, 55, 130, 1)", "rgba(126, 158, 255, 1)"], +] + const state = reactive({ - pieDataColor: ["#17E6C3", "#40CFFF", "#1979FF", "#FFC472", "#FF7A45"], + pieDataColor: ["#17E6C3", "#40CFFF", "#1979FF", "#FFC472", "#FF7A45", "#8BA8FF"], pieData: herdStructureRows.map((item) => ({ name: item.name, value: item.ratio, @@ -59,65 +68,22 @@ const option = ref({ radius: ["55%", "70%"], color: ["#c487ee", "#deb140", "#49dff0", "#034079", "#6f81da", "#00ffb4"], - data: [ - { - value: herdStructureRows[0].ratio, - name: herdStructureRows[0].name, - itemStyle: { - //颜色渐变 - color: new echarts.graphic.LinearGradient(0, 0, 1, 1, [ - { offset: 0, color: "rgba(3,65,128,1)" }, - { offset: 1, color: "rgba(115,208,255,1)" }, - ]), - }, - }, - { - value: herdStructureRows[1].ratio, - name: herdStructureRows[1].name, - itemStyle: { - //颜色渐变 - color: new echarts.graphic.LinearGradient(0, 0, 1, 1, [ - { offset: 0, color: "rgba(11, 77, 44, 1)" }, - { offset: 1, color: "rgba(77, 255, 181, 1)" }, - ]), - }, - }, - { - value: herdStructureRows[2].ratio, - name: herdStructureRows[2].name, - itemStyle: { - //颜色渐变 - color: new echarts.graphic.LinearGradient(0, 0, 1, 1, [ - { offset: 0, color: "rgba(117, 117, 117, 1)" }, - { offset: 1, color: "rgba(230, 230, 230, 1)" }, - ]), - }, - }, - { - value: herdStructureRows[3].ratio, - name: herdStructureRows[3].name, - itemStyle: { - //颜色渐变 - color: new echarts.graphic.LinearGradient(0, 0, 1, 1, [ - { offset: 0, color: "rgba(153, 105, 38, 1)" }, - { offset: 1, color: "rgba(255, 200, 89, 1)" }, - ]), - }, - }, - { - value: herdStructureRows[4].ratio, - name: herdStructureRows[4].name, + data: herdStructureRows.map((item, index) => { + const colors = pieGradients[index % pieGradients.length] + return { + value: item.ratio, + name: item.name, itemStyle: { color: new echarts.graphic.LinearGradient(0, 0, 1, 1, [ - { offset: 0, color: "rgba(114, 54, 36, 1)" }, - { offset: 1, color: "rgba(255, 122, 69, 1)" }, + { offset: 0, color: colors[0] }, + { offset: 1, color: colors[1] }, ]), }, - }, - ], + } + }), }, ], -}) +}) diff --git a/src/views/wisdomManagement/index.vue b/src/views/wisdomManagement/index.vue index 9f9c165..6bc4df1 100644 --- a/src/views/wisdomManagement/index.vue +++ b/src/views/wisdomManagement/index.vue @@ -81,7 +81,7 @@ AI -

智慧管理问答

+

小牧智慧问答

{{ welcomeText }}