| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711 |
- import * as echarts from 'echarts'
- import { getAllUnitList } from '@/api/auditEntityManage.js'
- // import { getAuditTaskList } from '@/api/auditInitiation.js'
- // , getAuditTaskList
- import { analyzeStatistics, getAuditTaskList } from '@/api/comprehensive'
- // 综合分析页面的通用mixin
- export const comprehensiveMixin = {
- data() {
- return {
- // 通用加载状态
- loading: false,
- projectOptions: [],
- auditedUnitOptions: [],
- // 指标树基础配置
- treeProps: {
- children: 'children',
- label: 'label',
- },
- // 图表实例集合
- chartInstances: {},
- // 组件是否已销毁标志
- isDestroyed: false,
- }
- },
- mounted() {
- this.getOptions()
- // 监听窗口大小变化
- window.addEventListener('resize', this.handleResize)
- },
- beforeDestroy() {
- // 标记组件已销毁
- this.isDestroyed = true
- // 销毁所有图表实例
- this.destroyCharts()
- // 移除窗口大小变化监听
- window.removeEventListener('resize', this.handleResize)
- },
- methods: {
- // 根据索引获取颜色
- getColorByIndex(index) {
- const colors = [
- '#5470C6', // 蓝色
- '#91CC75', // 绿色
- '#FAC858', // 黄色
- '#EE6666', // 红色
- '#73C0DE', // 浅蓝色
- '#3BA272', // 深绿色
- '#FC8452', // 橙色
- '#9A60B4', // 紫色
- ]
- return colors[index % colors.length]
- },
- // 将 costSurveysList 组装为树结构(用于左侧勾选)
- buildIndicatorTree(costSurveysList = []) {
- const processNode = (item) => {
- const nodeId =
- item.uniqueId ||
- item.id ||
- item.rowid ||
- item.rowId ||
- `${item.number || item.name || ''}`
- const node = {
- ...item,
- id: nodeId,
- label: this.buildIndicatorLabel(item),
- children: [],
- }
- // 如果有子项,递归处理
- if (Array.isArray(item.costSurveysVos) && item.costSurveysVos.length) {
- node.children = item.costSurveysVos.map(processNode)
- // 对子节点按照orderNum从小到大排序
- node.children.sort((a, b) => {
- const orderA = parseInt(a.orderNum || 0)
- const orderB = parseInt(b.orderNum || 0)
- return orderA - orderB
- })
- }
- return node
- }
- // 处理根节点并按照orderNum从小到大排序
- return costSurveysList.map(processNode).sort((a, b) => {
- const orderA = parseInt(a.orderNum || 0)
- const orderB = parseInt(b.orderNum || 0)
- return orderA - orderB
- })
- },
- // label 规则:parentId 为 -1 时,用 “number、name”,否则 “number.name”
- buildIndicatorLabel(item) {
- const num = item.number || ''
- const nm = item.name || ''
- if (!num) return nm
- const isRoot = !item.parentId || item.parentId === '-1'
- return isRoot ? `${num}、${nm}` : `${num}.${nm}`
- },
- // 聚合某节点及其后代的年度与汇总数据
- aggregateNodeData(node) {
- const yearMap = new Map()
- let surveysSum = 0
- const dfs = (n) => {
- if (!n) return
- // 累加叶子上的 surveysVos(年度值通常在这里)
- if (Array.isArray(n.surveysVos) && n.surveysVos.length) {
- n.surveysVos.forEach((sv) => {
- const yearKey = String(sv.name || sv.year || '').trim()
- const val = Number(sv.value) || 0
- if (yearKey) {
- yearMap.set(yearKey, (yearMap.get(yearKey) || 0) + val)
- }
- })
- surveysSum += n.surveysVos.reduce(
- (sum, sv) => sum + (Number(sv.value) || 0),
- 0
- )
- }
- // 若有子节点,继续向下汇总
- if (Array.isArray(n.costSurveysVos) && n.costSurveysVos.length) {
- n.costSurveysVos.forEach((child) => {
- // 如果子项有surveysVos,先处理这些数据
- if (Array.isArray(child.surveysVos) && child.surveysVos.length) {
- child.surveysVos.forEach((sv) => {
- const yearKey = String(sv.name || sv.year || '').trim()
- const val = Number(sv.value) || 0
- if (yearKey) {
- yearMap.set(yearKey, (yearMap.get(yearKey) || 0) + val)
- }
- })
- surveysSum += child.surveysVos.reduce(
- (sum, sv) => sum + (Number(sv.value) || 0),
- 0
- )
- }
- // 如果子项仍有子节点,递归;否则视为叶子年度数据
- if (
- Array.isArray(child.costSurveysVos) &&
- child.costSurveysVos.length
- ) {
- dfs(child)
- } else if (
- !Array.isArray(child.surveysVos) ||
- child.surveysVos.length === 0
- ) {
- // 只有当没有surveysVos时,才使用name/year和value
- const yearKey = String(child.name || child.year || '').trim()
- const val = Number(child.value) || 0
- if (yearKey) {
- yearMap.set(yearKey, (yearMap.get(yearKey) || 0) + val)
- }
- }
- })
- }
- // 汇总 surveysVos(构成图用)
- if (Array.isArray(n.surveysVos) && n.surveysVos.length) {
- surveysSum += n.surveysVos.reduce(
- (sum, sv) => sum + (Number(sv.value) || 0),
- 0
- )
- }
- }
- dfs(node)
- // 将年度数据转换为 surveysVos 格式,保留完整的年度信息
- const annualSurveysVos = Array.from(yearMap.entries()).map(
- ([name, value]) => ({
- name,
- value,
- })
- )
- return {
- costSurveysVos: annualSurveysVos,
- surveysVos: annualSurveysVos, // 返回完整的年度数据而不仅仅是合计值
- }
- },
- // 获取树的叶子 id 列表
- getLeafIds(tree = []) {
- const ids = []
- const dfs = (node) => {
- if (!node.children || node.children.length === 0) {
- ids.push(node.id)
- } else {
- node.children.forEach(dfs)
- }
- }
- tree.forEach(dfs)
- return ids
- },
- // 获取树的父级(拥有子节点)id 列表和节点列表
- getParentIds(tree = []) {
- const ids = []
- const items = []
- const dfs = (node) => {
- if (
- node &&
- (node.parentId === undefined ||
- node.parentId === null ||
- String(node.parentId) === '-1')
- ) {
- ids.push(node.id)
- items.push(node)
- }
- if (node && node.children && node.children.length > 0) {
- node.children.forEach(dfs)
- }
- }
- tree.forEach(dfs)
- return { ids, items }
- },
- // 查找节点到根的路径
- findPathByKey(
- id,
- tree = this.indicatorData ||
- this.leftDataItems ||
- this.rightDataItems ||
- []
- ) {
- const getKeys = (item) =>
- [
- item.id,
- item.uniqueId,
- item.rowid,
- item.rowId,
- item.number,
- item.name,
- ].filter(Boolean)
- const dfs = (items, path = []) => {
- for (const item of items) {
- const currentPath = [...path, item]
- if (getKeys(item).includes(id)) {
- return currentPath
- }
- if (item.children && item.children.length > 0) {
- const childPath = dfs(item.children, currentPath)
- if (childPath) return childPath
- }
- }
- return null
- }
- return dfs(tree)
- },
- // 过滤选中项:
- // - 点击根节点:只保留当前根节点及其已勾选的子节点,其它根与节点取消
- // - 点击子/孙节点:只保留当前节点及其已勾选的子节点,其它节点取消
- sanitizeCheckedKeys(
- data,
- checkedKeys = [],
- tree = this.leftDataItems ||
- this.rightDataItems ||
- this.indicatorData ||
- []
- ) {
- if (!checkedKeys || checkedKeys.length === 0) return []
- const useTree = Array.isArray(tree) && tree.length > 0 ? tree : []
- const paths = []
- checkedKeys.forEach((key) => {
- const path = this.findPathByKey(key, useTree)
- if (path && path.length > 0) {
- paths.push(path)
- }
- })
- const sanitized = new Set()
- if (data && String(data.parentId ?? '').trim() === '-1') {
- // 根节点:只保留当前根及其已勾选的子节点
- paths
- .filter((path) => path[0] && path[0].id === data.id)
- .forEach((path) => {
- path.forEach((node) => sanitized.add(node.id))
- })
- sanitized.add(data.id)
- } else if (data) {
- // 子/孙节点:保留当前节点及其已勾选的子节点,并保留已勾选的根节点
- paths
- .filter((path) => path[0] && path[0].rowid === data.parentId)
- .forEach((root) => {
- root.forEach((node) => {
- sanitized.add(node.id)
- node.children.forEach((child) => {
- if (checkedKeys.includes(child.id)) {
- sanitized.add(child.id)
- }
- })
- })
- })
- }
- return Array.from(sanitized)
- },
- // 过滤数据:选什么就返回什么(父子可同时保留各自曲线)
- filterCostSurveysListForHistoryAnalysis(
- costSurveysList = [],
- selectedItems = []
- ) {
- if (!selectedItems || selectedItems.length === 0) {
- return []
- }
- const toKey = (v) =>
- v === undefined || v === null ? null : String(v).trim()
- // 兼容 id / uniqueId / rowid,统一转成字符串避免类型不一致
- const selectedIdSet = new Set(
- selectedItems
- .flatMap((item) => [item.id, item.uniqueId, item.rowid, item.rowId])
- .map(toKey)
- .filter(Boolean)
- )
- // 递归过滤节点,父子都可保留
- const filterNode = (node) => {
- if (!node) return null
- const keys = [node.id, node.uniqueId, node.rowid, node.rowId].map(toKey)
- const isSelected = keys.some((k) => selectedIdSet.has(k))
- let filteredChildren = []
- if (Array.isArray(node.costSurveysVos) && node.costSurveysVos.length) {
- filteredChildren = node.costSurveysVos.map(filterNode).filter(Boolean)
- }
- if (isSelected) {
- // 选中的节点保留自身;不再用子节点汇总填充,避免父子数据重复
- const baseNode = {
- ...node,
- surveysVos: Array.isArray(node.surveysVos) ? node.surveysVos : [],
- costSurveysVos: filteredChildren, // 若子节点也选中则一并保留
- }
- return baseNode
- }
- // 未选中但有选中子节点,则只保留这些子节点
- if (filteredChildren.length > 0) {
- return {
- ...node,
- costSurveysVos: filteredChildren,
- }
- }
- return null
- }
- return costSurveysList.map(filterNode).filter(Boolean)
- },
- // 统一请求统计数据
- async requestStatistics(params = {}) {
- // 如果组件已销毁,直接返回 mock 数据
- if (this.isDestroyed) {
- return []
- }
- try {
- const res = await analyzeStatistics(params)
- // 再次检查组件是否已销毁
- if (this.isDestroyed) {
- return []
- }
- if (res && res.value) {
- return this.attachUniqueId(res.value)
- }
- } catch (e) {
- // 如果是请求中止错误,静默处理
- if (e && e.message && e.message.includes('aborted')) {
- return []
- }
- // 其他错误才打印警告
- if (!this.isDestroyed) {
- console.warn('analyzeStatistics 调用失败,使用 mock 数据', e)
- }
- }
- return []
- },
- // 为接口返回的列表生成唯一标识 uniqueId,兼容数组或 { costSurveysList } 结构
- attachUniqueId(res) {
- const markList = (list = [], parentKey = 'root', seen = new Set()) => {
- return (list || []).map((item, idx) => {
- // 使用多种可能的ID字段作为基础,但不修改原始ID
- const base =
- item.id ||
- item.rowid ||
- item.rowId ||
- `${parentKey}-${item.number || item.name || idx}`
- let key = base
- let suffix = 1
- while (seen.has(key)) {
- key = `${base}-${suffix++}`
- }
- seen.add(key)
- // 创建新对象并添加uniqueId,不修改原始item的id
- const cloned = {
- ...item,
- uniqueId: key,
- }
- if (
- Array.isArray(item.costSurveysVos) &&
- item.costSurveysVos.length
- ) {
- cloned.costSurveysVos = markList(item.costSurveysVos, key, seen)
- }
- return cloned
- })
- }
- if (Array.isArray(res)) {
- return markList(res)
- }
- if (res && Array.isArray(res.costSurveysList)) {
- return {
- ...res,
- costSurveysList: markList(res.costSurveysList),
- }
- }
- return res
- },
- getOptions() {
- // 获取监审任务列表
- getAuditTaskList()
- .then((res) => {
- if (!this.isDestroyed && res && res.value) {
- this.projectOptions = res.value
- }
- })
- .catch((e) => {
- // 如果是请求中止错误,静默处理
- if (e && e.message && !e.message.includes('aborted')) {
- console.warn('获取项目列表失败', e)
- }
- })
- getAllUnitList()
- .then((res) => {
- if (!this.isDestroyed && res && res.value) {
- this.auditedUnitOptions = res.value
- }
- })
- .catch((e) => {
- // 如果是请求中止错误,静默处理
- if (e && e.message && !e.message.includes('aborted')) {
- console.warn('获取单位列表失败', e)
- }
- })
- },
- // 根据选中的指标 ID 过滤 costSurveysList
- filterCostSurveysListBySelectedIds(costSurveysList = [], selectedIds = []) {
- if (!selectedIds || selectedIds.length === 0) {
- return []
- }
- // 统一转成字符串,避免数字/字符串类型不一致导致无法匹配
- const toKey = (v) =>
- v === undefined || v === null ? null : String(v).trim()
- const selectedIdSet = new Set(selectedIds.map(toKey).filter(Boolean))
- const isSelected = (node) => {
- const keys = [
- node?.id,
- node?.uniqueId,
- node?.rowid,
- node?.rowId,
- node?.number,
- node?.name,
- ]
- .map(toKey)
- .filter(Boolean)
- return keys.some((k) => selectedIdSet.has(k))
- }
- const filterNode = (node) => {
- if (!node) return null
- // 当前节点命中选中,直接保留,子节点也带上(用于饼图父级展示子项/趋势展示自身)
- if (isSelected(node)) {
- const filteredNode = { ...node }
- if (
- Array.isArray(node.costSurveysVos) &&
- node.costSurveysVos.length
- ) {
- filteredNode.costSurveysVos = node.costSurveysVos
- .map(filterNode)
- .filter(Boolean)
- }
- return filteredNode
- }
- // 未命中,检查子节点
- if (Array.isArray(node.costSurveysVos) && node.costSurveysVos.length) {
- const filteredChildren = node.costSurveysVos
- .map(filterNode)
- .filter(Boolean)
- if (filteredChildren.length > 0) {
- return {
- ...node,
- costSurveysVos: filteredChildren,
- }
- }
- }
- return null
- }
- return costSurveysList.map(filterNode).filter(Boolean)
- },
- // 将 costSurveysList 展平为趋势数据 [{indicatorName, year, value}]
- // 规则:只展示选中节点对应的年度数据(surveysVos),不追加“合计年”;若无则递归子节点
- transformTrendFromCostList(costSurveysList = []) {
- const trendArr = []
- const dfs = (node) => {
- if (!node) return
- if (Array.isArray(node.surveysVos) && node.surveysVos.length) {
- node.surveysVos.forEach((sv) => {
- const yearKey = String(sv.name || sv.year || '').trim()
- if (!yearKey) return
- const indicatorId =
- node.uniqueId || node.id || node.rowid || node.rowId || node.name
- trendArr.push({
- indicatorId: indicatorId,
- indicatorName: node.name,
- year: yearKey,
- value:
- Number(
- sv.value !== undefined && sv.value !== null
- ? sv.value
- : sv.rvalue
- ) || 0,
- })
- })
- }
- if (Array.isArray(node.costSurveysVos) && node.costSurveysVos.length) {
- node.costSurveysVos.forEach(dfs)
- }
- }
- costSurveysList.forEach(dfs)
- return trendArr
- },
- // 将 costSurveysList 汇总为构成数据 [{name, value}](显示所有surveysVos)
- transformCompositionFromCostList(
- costSurveysList = [],
- selectedItemsOrder = []
- ) {
- const result = []
- const dfs = (node) => {
- if (!node) return
- // 获取节点的唯一标识
- const indicatorId =
- node.uniqueId || node.id || node.rowid || node.rowId || node.name
- if (
- Array.isArray(node.costSurveysVos) &&
- node.costSurveysVos.length > 0
- ) {
- result.push({
- name: node.name,
- indicatorId: indicatorId, // 添加唯一标识
- value:
- Number(
- node.value !== undefined && node.value !== null
- ? node.value
- : node.rvalue
- ) || 0,
- surveysVos: node.surveysVos || [],
- })
- node.costSurveysVos.forEach((sv) => {
- const surveysList = Array.isArray(sv.surveysVos)
- ? sv.surveysVos
- : []
- // 子节点使用自己的唯一标识
- const childIndicatorId =
- sv.uniqueId || sv.id || sv.rowid || sv.rowId || sv.name
- result.push({
- name: sv.name,
- indicatorId: childIndicatorId, // 添加子节点的唯一标识
- value:
- surveysList.reduce((sum, item) => {
- const v =
- item.value !== undefined && item.value !== null
- ? item.value
- : item.rvalue
- return sum + (Number(v) || 0)
- }, 0) || 0,
- surveysVos: surveysList,
- })
- })
- } else {
- result.push({
- name: node.name,
- indicatorId: indicatorId, // 添加唯一标识
- value:
- Number(
- node.value !== undefined && node.value !== null
- ? node.value
- : node.rvalue
- ) || 0,
- surveysVos: node.surveysVos || [],
- })
- }
- }
- costSurveysList.forEach(dfs)
- // 统计相同名称的数量,用于区分显示
- const nameCountMap = {}
- result.forEach((item) => {
- nameCountMap[item.name] = (nameCountMap[item.name] || 0) + 1
- })
- const nameIndexMap = {}
- let orderedResult = [...result]
- // 为相同名称的项添加序号
- return orderedResult.map((item) => {
- const nameCount = nameCountMap[item.name] || 1
- let displayName = item.name
- if (nameCount > 1) {
- if (!nameIndexMap[item.name]) {
- nameIndexMap[item.name] = 0
- }
- nameIndexMap[item.name]++
- displayName = `${item.name}(${nameIndexMap[item.name]})`
- }
- return {
- ...item,
- name: displayName, // 使用带序号的名字
- originalName: item.name, // 保留原始名称
- }
- })
- },
- // 构建趋势图数据结构(xAxis + series)
- // selectedItemsOrder: 可选的选中项顺序数组,用于保持图例顺序与选中项顺序一致
- buildTrendSeries(trendArr = [], selectedItemsOrder = []) {
- const years = [
- ...new Set(
- trendArr
- .map((item) =>
- String(item.year || item.name || '').replace('年', '')
- )
- .filter(Boolean)
- ),
- ]
- .sort()
- .map((y) => `${y}年`)
- const indicatorMap = {}
- trendArr.forEach((item) => {
- const yearKey = String(item.year || item.name || '').replace('年', '')
- if (!yearKey) return
- // 优先使用 indicatorId(uniqueId/id/rowid),确保相同名称但不同ID的项能区分
- // 如果 indicatorId 为空,则使用 indicatorName 作为key(这种情况下相同名称会合并)
- const key = item.indicatorId || item.indicatorName
- if (!indicatorMap[key]) {
- indicatorMap[key] = {
- id: key,
- name: item.indicatorName,
- data: {},
- color: this.getColorByIndex(Object.keys(indicatorMap).length),
- }
- }
- // 如果同一年份已有数据,累加而不是覆盖(避免相同名称的项数据丢失)
- const existingValue = indicatorMap[key].data[yearKey] || 0
- indicatorMap[key].data[yearKey] =
- existingValue + Number(item.value) || 0
- })
- // 统计相同名称的数量,用于区分图例显示
- const nameCountMap = {}
- Object.values(indicatorMap).forEach((indicator) => {
- nameCountMap[indicator.name] = (nameCountMap[indicator.name] || 0) + 1
- })
- const nameIndexMap = {}
- // 按照选中项顺序来生成 series,如果没有提供顺序则使用默认顺序
- let orderedIndicators = []
- if (selectedItemsOrder && selectedItemsOrder.length > 0) {
- // 按照 selectedItemsOrder 的顺序来构建 series
- const indicatorIdToItem = {}
- selectedItemsOrder.forEach((item) => {
- const key =
- item.uniqueId || item.id || item.rowid || item.rowId || item.name
- if (key && indicatorMap[key]) {
- indicatorIdToItem[key] = indicatorMap[key]
- }
- })
- // 先添加按顺序的项
- orderedIndicators = selectedItemsOrder
- .map((item) => {
- const key =
- item.uniqueId || item.id || item.rowid || item.rowId || item.name
- return indicatorMap[key]
- })
- .filter(Boolean)
- // 再添加不在选中项顺序中的项(以防有遗漏)
- Object.keys(indicatorMap).forEach((key) => {
- if (!indicatorIdToItem[key]) {
- orderedIndicators.push(indicatorMap[key])
- }
- })
- } else {
- // 如果没有提供顺序,使用默认顺序
- orderedIndicators = Object.values(indicatorMap)
- }
- // 按照顺序重新分配颜色,确保每个项(即使名称相同)都使用不同的颜色
- const series = orderedIndicators.map((indicator, index) => {
- // 如果存在相同名称的项,在名称后添加序号来区分
- const nameCount = nameCountMap[indicator.name] || 1
- let displayName = indicator.name
- if (nameCount > 1) {
- if (!nameIndexMap[indicator.name]) {
- nameIndexMap[indicator.name] = 0
- }
- nameIndexMap[indicator.name]++
- displayName = `${indicator.name}(${nameIndexMap[indicator.name]})`
- }
- return {
- name: displayName,
- indicatorId: indicator.id, // 保留原始ID用于区分
- originalName: indicator.name, // 保留原始名称
- data: years.map((y) => {
- const key = y.replace('年', '')
- return indicator.data[key] !== undefined ? indicator.data[key] : 0
- }),
- color: this.getColorByIndex(index), // 按照顺序重新分配颜色,确保每个项都不同
- }
- })
- return { xAxis: years, series }
- },
- // 初始化图表
- initChart(chartId, containerId) {
- const chartDom = document.getElementById(containerId)
- if (chartDom) {
- this.chartInstances[chartId] = echarts.init(chartDom)
- return this.chartInstances[chartId]
- }
- return null
- },
- // 销毁指定图表实例
- destroyChart(chartId) {
- if (this.chartInstances[chartId]) {
- this.chartInstances[chartId].dispose()
- delete this.chartInstances[chartId]
- }
- },
- // 销毁所有图表实例
- destroyCharts() {
- Object.keys(this.chartInstances).forEach((chartId) => {
- this.destroyChart(chartId)
- })
- },
- // 处理窗口大小变化
- handleResize() {
- // 调整所有图表大小
- Object.values(this.chartInstances).forEach((chart) => {
- if (chart) {
- chart.resize()
- }
- })
- },
- // 更新图表数据
- updateChart(chartId, option) {
- if (this.chartInstances[chartId]) {
- this.chartInstances[chartId].setOption(option)
- this.$nextTick(() => {
- this.chartInstances[chartId].resize()
- })
- }
- },
- // 获取趋势图表基础配置
- getBaseTrendChartOption(
- seriesData = [],
- xAxisData = ['2022年', '2023年', '2024年']
- ) {
- return {
- tooltip: {
- trigger: 'axis',
- axisPointer: {
- type: 'cross',
- },
- },
- legend: {
- show: true,
- data: seriesData.map((item) => item.name),
- bottom: 0,
- left: 'center',
- itemGap: 15,
- textStyle: {
- fontSize: 12,
- color: '#333',
- },
- type: 'plain', // 改为 plain 去掉分页控件
- width: 'auto', // 自动宽度,确保能显示所有图例
- formatter: function (name) {
- return name // 确保显示完整的名称
- },
- },
- grid: {
- left: '3%',
- right: '4%',
- bottom: '15%',
- top: '10%',
- containLabel: true,
- },
- xAxis: {
- type: 'category',
- boundaryGap: false,
- data: xAxisData,
- show: true,
- axisLine: {
- show: true,
- lineStyle: {
- color: '#333',
- },
- },
- axisTick: {
- show: true,
- },
- axisLabel: {
- show: true,
- fontSize: 12,
- color: '#333',
- },
- },
- yAxis: {
- type: 'value',
- show: true,
- axisLine: {
- show: true,
- lineStyle: {
- color: '#333',
- },
- },
- axisTick: {
- show: false,
- },
- axisLabel: {
- show: true,
- fontSize: 12,
- color: '#333',
- },
- splitLine: {
- show: true,
- lineStyle: {
- type: 'dashed',
- color: '#e0e0e0',
- },
- },
- },
- series: seriesData.map((item) => ({
- name: item.name,
- type: 'line',
- data: item.data,
- symbol: 'circle',
- symbolSize: 8,
- smooth: false,
- lineStyle: {
- width: 2,
- color: item.color || '#37A2DA',
- },
- itemStyle: {
- color: item.color || '#37A2DA',
- },
- emphasis: {
- focus: 'series',
- },
- })),
- }
- },
- // 获取构成图表基础配置(historyAnalysis.vue 使用)
- getBaseCompositionChartOption(data = []) {
- // 为每个数据项分配不同的颜色
- const dataWithColors = data.map((item, index) => ({
- ...item,
- itemStyle: {
- color: this.getColorByIndex(index),
- },
- }))
- return {
- tooltip: {
- show: true,
- trigger: 'item',
- formatter: (p) => {
- const surveysVos = Array.isArray(p.data?.surveysVos)
- ? p.data.surveysVos
- : []
- // 计算所有年度值的总和
- const totalSum = surveysVos.reduce((sum, sv) => {
- const hasValue = sv.value !== undefined && sv.value !== null
- const hasRvalue = sv.rvalue !== undefined && sv.rvalue !== null
- const mainValue = hasValue
- ? Number(sv.value) || 0
- : hasRvalue
- ? Number(sv.rvalue) || 0
- : 0
- return sum + mainValue
- }, 0)
- // 使用总和作为显示值,如果没有年度数据则使用原始值
- const displayValue = totalSum > 0 ? totalSum : p.value
- const header = `${p.name}: ${displayValue} (${p.percent}%)`
- // 只展示年度和值,避免重复的字段名与分隔符
- const details = surveysVos
- .map((sv) => {
- const yearLabel = sv.name || sv.year || '-'
- const hasValue = sv.value !== undefined && sv.value !== null
- const hasRvalue = sv.rvalue !== undefined && sv.rvalue !== null
- const mainValue = hasValue
- ? sv.value
- : hasRvalue
- ? sv.rvalue
- : '-'
- // 如果 value 和 rvalue 同时存在且不同,附加 rvalue 以显示所有值
- const extra =
- hasValue && hasRvalue && sv.value !== sv.rvalue
- ? ` (rvalue: ${sv.rvalue})`
- : ''
- return `${yearLabel}: ${mainValue}${extra}`
- })
- .filter(Boolean)
- return details.length > 0
- ? `${header}<br/>${details.join('<br/>')}`
- : header
- },
- },
- legend: {
- show: true,
- orient: 'vertical',
- right: 10,
- top: 'top',
- data: data.map((item) => item.name),
- formatter: function (name) {
- return name
- },
- itemGap: 8,
- textStyle: {
- fontSize: 11,
- color: '#333',
- },
- itemWidth: 14,
- itemHeight: 14,
- type: 'scroll',
- pageButtonPosition: 'end',
- pageButtonItemGap: 5,
- pageButtonGap: 5,
- pageTextStyle: {
- fontSize: 10,
- },
- },
- series: [
- {
- name: '成本构成',
- type: 'pie',
- radius: '60%',
- center: ['35%', '50%'],
- avoidLabelOverlap: true,
- itemStyle: {
- borderRadius: 0,
- borderColor: '#fff',
- borderWidth: 1,
- },
- label: {
- show: true,
- position: 'outside',
- formatter: '{b}',
- fontSize: 12,
- color: '#333',
- },
- labelLine: {
- show: true,
- length: 10,
- length2: 5,
- lineStyle: {
- width: 1,
- color: 'auto', // 线条跟随扇区颜色
- },
- },
- emphasis: {
- label: {
- show: true,
- fontSize: 12,
- fontWeight: 'normal',
- },
- itemStyle: {
- shadowBlur: 0,
- shadowOffsetX: 0,
- shadowColor: 'transparent',
- },
- labelLine: {
- lineStyle: {
- color: 'auto',
- },
- },
- },
- data: dataWithColors,
- },
- ],
- }
- },
- // 获取构成图表基础配置(industryAnalysis.vue 使用)
- getIndustryCompositionChartOption(data = []) {
- // 为每个数据项分配不同的颜色,按照顺序使用 getColorByIndex
- const dataWithColors = data.map((item, index) => ({
- ...item,
- itemStyle: {
- color: this.getColorByIndex(index),
- },
- }))
- return {
- tooltip: {
- show: true,
- trigger: 'item',
- formatter: (p) => {
- const surveysVos = Array.isArray(p.data?.surveysVos)
- ? p.data.surveysVos
- : []
- // 计算所有年度值的总和
- const totalSum = surveysVos.reduce((sum, sv) => {
- const hasValue = sv.value !== undefined && sv.value !== null
- const hasRvalue = sv.rvalue !== undefined && sv.rvalue !== null
- const mainValue = hasValue
- ? Number(sv.value) || 0
- : hasRvalue
- ? Number(sv.rvalue) || 0
- : 0
- return sum + mainValue
- }, 0)
- // 使用总和作为显示值,如果没有年度数据则使用原始值
- const displayValue = totalSum > 0 ? totalSum : p.value
- const header = `${p.name}: ${displayValue} (${p.percent}%)`
- // 只展示年度和值,避免重复的字段名与分隔符
- const details = surveysVos
- .map((sv) => {
- const yearLabel = sv.name || sv.year || '-'
- const hasValue = sv.value !== undefined && sv.value !== null
- const hasRvalue = sv.rvalue !== undefined && sv.rvalue !== null
- const mainValue = hasValue
- ? sv.value
- : hasRvalue
- ? sv.rvalue
- : '-'
- // 如果 value 和 rvalue 同时存在且不同,附加 rvalue 以显示所有值
- const extra =
- hasValue && hasRvalue && sv.value !== sv.rvalue
- ? ` (rvalue: ${sv.rvalue})`
- : ''
- return `${yearLabel}: ${mainValue}${extra}`
- })
- .filter(Boolean)
- return details.length > 0
- ? `${header}<br/>${details.join('<br/>')}`
- : header
- },
- },
- legend: {
- show: true,
- orient: 'horizontal',
- bottom: 10,
- left: 'center',
- data: data.map((item) => item.name),
- icon: 'circle',
- itemGap: 20,
- textStyle: {
- fontSize: 12,
- color: '#333',
- },
- itemWidth: 10,
- itemHeight: 10,
- },
- series: [
- {
- name: '成本构成',
- type: 'pie',
- radius: ['40%', '60%'], // 环形图
- center: ['50%', '45%'],
- avoidLabelOverlap: true,
- silent: false,
- itemStyle: {
- borderRadius: 0,
- borderColor: '#fff',
- borderWidth: 1,
- },
- label: {
- show: true,
- position: 'outside',
- formatter: '{b}: {d}%', // 名称: 百分比
- fontSize: 12,
- color: '#333',
- },
- labelLine: {
- show: true,
- length: 10,
- length2: 5,
- lineStyle: {
- width: 1,
- color: 'auto', // 线条跟随扇区颜色
- },
- },
- emphasis: {
- label: {
- show: true,
- fontSize: 12,
- fontWeight: 'normal',
- },
- itemStyle: {
- shadowBlur: 0,
- shadowOffsetX: 0,
- shadowColor: 'transparent',
- },
- labelLine: {
- lineStyle: {
- color: 'auto',
- },
- },
- },
- data: dataWithColors,
- },
- ],
- }
- },
- // 轮播功能基础配置(仅historyAnalysis.vue使用)
- initCarousel(chartId, totalPages = 1, onPageChange) {
- // 若已有轮播,先清理旧定时器,避免重复计时导致数据持续跳动
- if (this.carouselConfigs && this.carouselConfigs[chartId]) {
- this.pauseCarousel(chartId)
- }
- const carouselConfig = {
- totalPages,
- currentPage: 0,
- timer: null,
- interval: 5000,
- onPageChange,
- }
- // 保存轮播配置
- this.carouselConfigs = this.carouselConfigs || {}
- this.carouselConfigs[chartId] = carouselConfig
- // 启动轮播
- if (totalPages > 1) {
- this.startCarousel(chartId)
- } else {
- // 单页不需要轮播,但仍确保跳转到第一页
- this.goToCarouselPage(chartId, 0)
- }
- },
- // 启动轮播
- startCarousel(chartId) {
- const config = this.carouselConfigs?.[chartId]
- if (!config) return
- // 清除现有定时器
- this.pauseCarousel(chartId)
- // 设置新定时器
- config.timer = setInterval(() => {
- config.currentPage = (config.currentPage + 1) % config.totalPages
- this.goToCarouselPage(chartId, config.currentPage)
- }, config.interval)
- },
- // 暂停轮播
- pauseCarousel(chartId) {
- const config = this.carouselConfigs?.[chartId]
- if (config?.timer) {
- clearInterval(config.timer)
- config.timer = null
- }
- },
- // 恢复轮播
- resumeCarousel(chartId) {
- this.startCarousel(chartId)
- },
- // 跳转到指定轮播页
- goToCarouselPage(chartId, pageIndex) {
- const config = this.carouselConfigs?.[chartId]
- if (!config) return
- config.currentPage = pageIndex
- if (typeof config.onPageChange === 'function') {
- config.onPageChange(pageIndex)
- }
- },
- },
- }
- // mock 数据
- export const mockStatisticsData = {
- costSurveysList: [
- {
- id: '1997964654884069376',
- name: '人数',
- orderNum: '1',
- rowid: 'ce2c23fd-0a60-4d98-a27e-d5344c7f2f7b',
- number: '一',
- rvalue: '2',
- taskId: '1995015537499938816',
- nianfen: null,
- parentId: '-1',
- surveysVos: [
- { name: '2024', value: '30' },
- { name: '2025', value: '35' },
- ],
- costSurveysVos: [
- {
- id: '1997964655014092800',
- name: '1班',
- orderNum: '2',
- rowid: '0a075d08-1805-4ce0-a5e1-c4f264db76bd',
- number: '1',
- rvalue: '4',
- taskId: '1995015537499938816',
- nianfen: null,
- parentId: 'ce2c23fd-0a60-4d98-a27e-d5344c7f2f7b',
- surveysVos: [
- { name: '2024', value: '12' },
- { name: '2025', value: '15' },
- ],
- costSurveysVos: null,
- },
- {
- id: '1997964655131533312',
- name: '2班',
- orderNum: '3',
- rowid: 'ab3efd32-9a10-4355-8d6e-19d99ebdbe4a',
- number: '2',
- rvalue: '7',
- taskId: '1995015537499938816',
- nianfen: null,
- parentId: 'ce2c23fd-0a60-4d98-a27e-d5344c7f2f7b',
- surveysVos: [
- { name: '2024', value: '18' },
- { name: '2025', value: '20' },
- ],
- costSurveysVos: null,
- },
- ],
- },
- {
- id: '1997964655244779520',
- name: '1班费用',
- orderNum: '4',
- rowid: '6a81c1d3-ea8b-48d5-bd11-27e5b150cea1',
- number: '二',
- rvalue: '53',
- taskId: '1995015537499938816',
- nianfen: null,
- parentId: '-1',
- surveysVos: [
- { name: '2024', value: '120' },
- { name: '2025', value: '140' },
- ],
- costSurveysVos: [
- {
- id: '1997964655387385856',
- name: '押金',
- orderNum: '5',
- rowid: 'bf17169e-9d03-49f0-b7c7-7502879a935b',
- number: '1',
- rvalue: '124',
- taskId: '1995015537499938816',
- nianfen: null,
- parentId: '6a81c1d3-ea8b-48d5-bd11-27e5b150cea1',
- surveysVos: [
- { name: '2024', value: '50' },
- { name: '2025', value: '60' },
- ],
- costSurveysVos: null,
- },
- {
- id: '1997964655559352320',
- name: '缴纳',
- orderNum: '6',
- rowid: 'e130564b-dc73-4fa9-a004-01c3af418021',
- number: '2',
- rvalue: '146',
- taskId: '1995015537499938816',
- nianfen: null,
- parentId: '6a81c1d3-ea8b-48d5-bd11-27e5b150cea1',
- surveysVos: [
- { name: '2024', value: '70' },
- { name: '2025', value: '80' },
- ],
- costSurveysVos: null,
- },
- ],
- },
- {
- id: '1997964655680987136',
- name: '班级乘法',
- orderNum: '7',
- rowid: 'aec785e0-86fa-436e-be7b-bb9d2d9eaf32',
- number: '三',
- rvalue: '246',
- taskId: '1995015537499938816',
- nianfen: null,
- parentId: '-1',
- surveysVos: [
- { name: '2024', value: '300' },
- { name: '2025', value: '320' },
- ],
- costSurveysVos: [],
- },
- {
- id: '1997964655794233344',
- name: '人均费用',
- orderNum: '8',
- rowid: '857ba697-aaf0-4e26-97a6-4bd97d8ff6f7',
- number: '四',
- rvalue: '246',
- taskId: '1995015537499938816',
- nianfen: null,
- parentId: '-1',
- surveysVos: [
- { name: '2024', value: '15' },
- { name: '2025', value: '16' },
- ],
- costSurveysVos: [],
- },
- {
- id: '1997964655903279104',
- name: '材料成本',
- orderNum: '9',
- rowid: 'f3a8b2c1-5d6e-4f7a-8b9c-0d1e2f3a4b5c',
- number: '五',
- rvalue: '580',
- taskId: '1995015537499938816',
- nianfen: null,
- parentId: '-1',
- surveysVos: [
- { name: '2022', value: '450' },
- { name: '2023', value: '520' },
- { name: '2024', value: '580' },
- { name: '2025', value: '620' },
- ],
- costSurveysVos: [
- {
- id: '1997964656012324864',
- name: '原材料',
- orderNum: '10',
- rowid: 'a1b2c3d4-e5f6-4a7b-8c9d-0e1f2a3b4c5d',
- number: '1',
- rvalue: '320',
- taskId: '1995015537499938816',
- nianfen: null,
- parentId: 'f3a8b2c1-5d6e-4f7a-8b9c-0d1e2f3a4b5c',
- surveysVos: [
- { name: '2022', value: '250' },
- { name: '2023', value: '290' },
- { name: '2024', value: '320' },
- { name: '2025', value: '350' },
- ],
- costSurveysVos: null,
- },
- {
- id: '1997964656121370624',
- name: '辅助材料',
- orderNum: '11',
- rowid: 'b2c3d4e5-f6a7-4b8c-9d0e-1f2a3b4c5d6e',
- number: '2',
- rvalue: '180',
- taskId: '1995015537499938816',
- nianfen: null,
- parentId: 'f3a8b2c1-5d6e-4f7a-8b9c-0d1e2f3a4b5c',
- surveysVos: [
- { name: '2022', value: '140' },
- { name: '2023', value: '160' },
- { name: '2024', value: '180' },
- { name: '2025', value: '200' },
- ],
- costSurveysVos: null,
- },
- {
- id: '1997964656230416384',
- name: '包装材料',
- orderNum: '12',
- rowid: 'c3d4e5f6-a7b8-4c9d-0e1f-2a3b4c5d6e7f',
- number: '3',
- rvalue: '80',
- taskId: '1995015537499938816',
- nianfen: null,
- parentId: 'f3a8b2c1-5d6e-4f7a-8b9c-0d1e2f3a4b5c',
- surveysVos: [
- { name: '2022', value: '60' },
- { name: '2023', value: '70' },
- { name: '2024', value: '80' },
- { name: '2025', value: '70' },
- ],
- costSurveysVos: null,
- },
- ],
- },
- {
- id: '1997964656339462144',
- name: '人工成本',
- orderNum: '13',
- rowid: 'd4e5f6a7-b8c9-4d0e-1f2a-3b4c5d6e7f8a',
- number: '六',
- rvalue: '850',
- taskId: '1995015537499938816',
- nianfen: null,
- parentId: '-1',
- surveysVos: [
- { name: '2022', value: '680' },
- { name: '2023', value: '750' },
- { name: '2024', value: '850' },
- { name: '2025', value: '920' },
- ],
- costSurveysVos: [
- {
- id: '1997964656448507904',
- name: '基本工资',
- orderNum: '14',
- rowid: 'e5f6a7b8-c9d0-4e1f-2a3b-4c5d6e7f8a9b',
- number: '1',
- rvalue: '520',
- taskId: '1995015537499938816',
- nianfen: null,
- parentId: 'd4e5f6a7-b8c9-4d0e-1f2a-3b4c5d6e7f8a',
- surveysVos: [
- { name: '2022', value: '420' },
- { name: '2023', value: '460' },
- { name: '2024', value: '520' },
- { name: '2025', value: '580' },
- ],
- costSurveysVos: null,
- },
- {
- id: '1997964656557553664',
- name: '绩效奖金',
- orderNum: '15',
- rowid: 'f6a7b8c9-d0e1-4f2a-3b4c-5d6e7f8a9b0c',
- number: '2',
- rvalue: '200',
- taskId: '1995015537499938816',
- nianfen: null,
- parentId: 'd4e5f6a7-b8c9-4d0e-1f2a-3b4c5d6e7f8a',
- surveysVos: [
- { name: '2022', value: '150' },
- { name: '2023', value: '180' },
- { name: '2024', value: '200' },
- { name: '2025', value: '220' },
- ],
- costSurveysVos: null,
- },
- {
- id: '1997964656666599424',
- name: '社保公积金',
- orderNum: '16',
- rowid: 'a7b8c9d0-e1f2-4a3b-4c5d-6e7f8a9b0c1d',
- number: '3',
- rvalue: '130',
- taskId: '1995015537499938816',
- nianfen: null,
- parentId: 'd4e5f6a7-b8c9-4d0e-1f2a-3b4c5d6e7f8a',
- surveysVos: [
- { name: '2022', value: '110' },
- { name: '2023', value: '120' },
- { name: '2024', value: '130' },
- { name: '2025', value: '120' },
- ],
- costSurveysVos: null,
- },
- ],
- },
- {
- id: '1997964656775645184',
- name: '设备成本',
- orderNum: '17',
- rowid: 'b8c9d0e1-f2a3-4b4c-5d6e-7f8a9b0c1d2e',
- number: '七',
- rvalue: '420',
- taskId: '1995015537499938816',
- nianfen: null,
- parentId: '-1',
- surveysVos: [
- { name: '2022', value: '380' },
- { name: '2023', value: '400' },
- { name: '2024', value: '420' },
- { name: '2025', value: '450' },
- ],
- costSurveysVos: [
- {
- id: '1997964656884690944',
- name: '设备折旧',
- orderNum: '18',
- rowid: 'c9d0e1f2-a3b4-4c5d-6e7f-8a9b0c1d2e3f',
- number: '1',
- rvalue: '280',
- taskId: '1995015537499938816',
- nianfen: null,
- parentId: 'b8c9d0e1-f2a3-4b4c-5d6e-7f8a9b0c1d2e',
- surveysVos: [
- { name: '2022', value: '260' },
- { name: '2023', value: '270' },
- { name: '2024', value: '280' },
- { name: '2025', value: '300' },
- ],
- costSurveysVos: null,
- },
- {
- id: '1997964656993736704',
- name: '设备维护',
- orderNum: '19',
- rowid: 'd0e1f2a3-b4c5-4d6e-7f8a-9b0c-1d2e3f4a5b4a',
- number: '2',
- rvalue: '90',
- taskId: '1995015537499938816',
- nianfen: null,
- parentId: 'b8c9d0e1-f2a3-4b4c-5d6e-7f8a9b0c1d2e',
- surveysVos: [
- { name: '2022', value: '80' },
- { name: '2023', value: '85' },
- { name: '2024', value: '90' },
- { name: '2025', value: '100' },
- ],
- costSurveysVos: null,
- },
- {
- id: '1997964657102782464',
- name: '设备租赁',
- orderNum: '20',
- rowid: 'e1f2a3b4-c5d6-4e7f-8a9b-0c1d2e3f4a5b',
- number: '3',
- rvalue: '50',
- taskId: '1995015537499938816',
- nianfen: null,
- parentId: 'b8c9d0e1-f2a3-4b4c-5d6e-7f8a9b0c1d2e',
- surveysVos: [
- { name: '2022', value: '40' },
- { name: '2023', value: '45' },
- { name: '2024', value: '50' },
- { name: '2025', value: '50' },
- ],
- costSurveysVos: null,
- },
- ],
- },
- {
- id: '1997964657211828224',
- name: '管理费用',
- orderNum: '21',
- rowid: 'f2a3b4c5-d6e7-4f8a-9b0c-1d2e3f4a5b6c',
- number: '八',
- rvalue: '360',
- taskId: '1995015537499938816',
- nianfen: null,
- parentId: '-1',
- surveysVos: [
- { name: '2022', value: '320' },
- { name: '2023', value: '340' },
- { name: '2024', value: '360' },
- { name: '2025', value: '380' },
- ],
- costSurveysVos: [
- {
- id: '1997964657320873984',
- name: '办公费用',
- orderNum: '22',
- rowid: 'a3b4c5d6-e7f8-4a9b-0c1d-2e3f4a5b6c7d',
- number: '1',
- rvalue: '150',
- taskId: '1995015537499938816',
- nianfen: null,
- parentId: 'f2a3b4c5-d6e7-4f8a-9b0c-1d2e3f4a5b6c',
- surveysVos: [
- { name: '2022', value: '130' },
- { name: '2023', value: '140' },
- { name: '2024', value: '150' },
- { name: '2025', value: '160' },
- ],
- costSurveysVos: null,
- },
- {
- id: '1997964657429919744',
- name: '差旅费用',
- orderNum: '23',
- rowid: 'b4c5d6e7-f8a9-4b0c-1d2e-3f4a5b6c7d8e',
- number: '2',
- rvalue: '120',
- taskId: '1995015537499938816',
- nianfen: null,
- parentId: 'f2a3b4c5-d6e7-4f8a-9b0c-1d2e3f4a5b6c',
- surveysVos: [
- { name: '2022', value: '110' },
- { name: '2023', value: '115' },
- { name: '2024', value: '120' },
- { name: '2025', value: '130' },
- ],
- costSurveysVos: null,
- },
- {
- id: '1997964657538965504',
- name: '培训费用',
- orderNum: '24',
- rowid: 'c5d6e7f8-a9b0-4c1d-2e3f-4a5b6c7d8e9f',
- number: '3',
- rvalue: '90',
- taskId: '1995015537499938816',
- nianfen: null,
- parentId: 'f2a3b4c5-d6e7-4f8a-9b0c-1d2e3f4a5b6c',
- surveysVos: [
- { name: '2022', value: '80' },
- { name: '2023', value: '85' },
- { name: '2024', value: '90' },
- { name: '2025', value: '90' },
- ],
- costSurveysVos: null,
- },
- ],
- },
- {
- id: '1997964657648011264',
- name: '能源成本',
- orderNum: '25',
- rowid: 'd6e7f8a9-b0c1-4d2e-3f4a-5b6c7d8e9f0a',
- number: '九',
- rvalue: '280',
- taskId: '1995015537499938816',
- nianfen: null,
- parentId: '-1',
- surveysVos: [
- { name: '2022', value: '250' },
- { name: '2023', value: '270' },
- { name: '2024', value: '280' },
- { name: '2025', value: '290' },
- ],
- costSurveysVos: [
- {
- id: '1997964657757057024',
- name: '电费',
- orderNum: '26',
- rowid: 'e7f8a9b0-c1d2-4e3f-4a5b-6c7d8e9f0a1b',
- number: '1',
- rvalue: '180',
- taskId: '1995015537499938816',
- nianfen: null,
- parentId: 'd6e7f8a9-b0c1-4d2e-3f4a-5b6c7d8e9f0a',
- surveysVos: [
- { name: '2022', value: '160' },
- { name: '2023', value: '170' },
- { name: '2024', value: '180' },
- { name: '2025', value: '190' },
- ],
- costSurveysVos: null,
- },
- {
- id: '1997964657866102784',
- name: '水费',
- orderNum: '27',
- rowid: 'f8a9b0c1-d2e3-4f4a-5b6c-7d8e9f0a1b2c',
- number: '2',
- rvalue: '60',
- taskId: '1995015537499938816',
- nianfen: null,
- parentId: 'd6e7f8a9-b0c1-4d2e-3f4a-5b6c7d8e9f0a',
- surveysVos: [
- { name: '2022', value: '55' },
- { name: '2023', value: '58' },
- { name: '2024', value: '60' },
- { name: '2025', value: '62' },
- ],
- costSurveysVos: null,
- },
- {
- id: '1997964657975148544',
- name: '燃气费',
- orderNum: '28',
- rowid: 'a9b0c1d2-e3f4-4a5b-6c7d-8e9f0a1b2c3d',
- number: '3',
- rvalue: '40',
- taskId: '1995015537499938816',
- nianfen: null,
- parentId: 'd6e7f8a9-b0c1-4d2e-3f4a-5b6c7d8e9f0a',
- surveysVos: [
- { name: '2022', value: '35' },
- { name: '2023', value: '38' },
- { name: '2024', value: '40' },
- { name: '2025', value: '38' },
- ],
- costSurveysVos: null,
- },
- ],
- },
- ],
- }
|