1367 lines
44 KiB
Vue
1367 lines
44 KiB
Vue
<script setup lang="ts">
|
||
// ==================== 导入依赖 ====================
|
||
import { ref, computed, watch, onMounted, onUnmounted, nextTick } from 'vue'
|
||
import * as echarts from 'echarts'
|
||
import type { EChartsOption } from 'echarts'
|
||
import type { ColumnsType } from 'ant-design-vue/es/table/interface'
|
||
import { DownloadOutlined } from '@ant-design/icons-vue'
|
||
import BasicSearch from '@/components/BasicSearch/index.vue'
|
||
import BasicTable from '@/components/BasicTable/index.vue'
|
||
import { message } from 'ant-design-vue'
|
||
import { useModelStore } from '@/store/modules/model'
|
||
import { handleTabs } from '@/components/MapModal/setting.config'
|
||
import { getKendoList, getKendoListCust, getIntroduce } from '@/api/shuidiankaifa'
|
||
import { useJidiSelectEventStore } from '@/store/modules/jidiSelectEvent';
|
||
// ==================== 开发配置 ====================
|
||
const USE_MOCK_DATA = true // 开发时使用模拟数据,生产时改为 false
|
||
|
||
// ==================== Props 定义 ====================
|
||
const props = defineProps<{
|
||
defaultJidiInfo?: Record<string, string | number | boolean> // 默认基地信息
|
||
seriesName?: string // 系列名称(如:"装机容量(万kW)" 或 "数量(座)")
|
||
stateName?: string // 状态名称
|
||
themeecList?: any[] // 主题颜色列表(用于饼图颜色自定义)
|
||
defaultTab?: string // 默认显示的Tab值('已建'/'在建')
|
||
}>()
|
||
|
||
// ==================== 类型定义 ====================
|
||
interface TabItem {
|
||
key: string
|
||
value: string
|
||
col: string
|
||
unit: string
|
||
count: number
|
||
}
|
||
|
||
interface PieDataItem {
|
||
key: string
|
||
name: string
|
||
value: number
|
||
unit: string
|
||
dataDimensionRecursive?: string
|
||
index?: number
|
||
}
|
||
|
||
interface TableFilterParams {
|
||
baseId?: string | Array<{ field: string; operator: string; dataType: string; value: string[] }>
|
||
hbrvcd?: string | Array<{ field: string; operator: string; dataType: string; value: string[] }>
|
||
bldsttCcode?: string
|
||
stnmContains?: string | null
|
||
prsc?: Array<{ field: string; operator: string; dataType: string; value: string[] }>
|
||
ttpwr?: Array<{ field: string; operator: string }>
|
||
sttpCode?: string
|
||
}
|
||
|
||
interface ModalData {
|
||
sttp: string
|
||
stcd: string
|
||
titleName: string
|
||
}
|
||
|
||
// ==================== 响应式状态 ====================
|
||
|
||
// 引入 modelStore 用于控制全局 MapModal
|
||
const modelStore = useModelStore()
|
||
const JidiSelectEventStore = useJidiSelectEventStore();
|
||
// 基地相关
|
||
const qid = ref<string>('')
|
||
const jiDiList = ref<any[]>([])
|
||
const defJD = ref<string>(props.defaultJidiInfo?.basename as string || '')
|
||
|
||
// Tab栏相关
|
||
const tabIndex = ref<string>('')
|
||
const tabKey = ref<string>('')
|
||
const tabCol = ref<string>('')
|
||
const tabList = ref<TabItem[]>([])
|
||
|
||
// 搜索相关
|
||
const stnm = ref<string | null>(null)
|
||
const formRef = ref<any>()
|
||
|
||
// 饼图相关
|
||
const pieData = ref<PieDataItem[]>([])
|
||
const pieCode = ref<string[]>([])
|
||
const pikData = ref<PieDataItem[]>([])
|
||
const chartRef = ref<HTMLElement | null>(null)
|
||
const chartInstance = ref<any>(null)
|
||
const chartInitialized = ref<boolean>(false) // 关键:图表初始化标志
|
||
const dataLoading = ref<boolean>(false)
|
||
|
||
// 弹窗相关(已废弃,改用全局 MapModal)
|
||
// const mapModal = ref<boolean>(false)
|
||
// const modalData = ref<ModalData>({ sttp: '', stcd: '', titleName: '' })
|
||
|
||
// 单位配置
|
||
const unit = computed(() => getUnitConfigByCode('Other', 'ZJRL').unit)
|
||
|
||
// 颜色配置(优先使用themeecList,否则使用默认颜色)
|
||
const jdColor = ['#5B8FF9', '#5AD8A6', '#F6BD16', '#7262FD', '#FF6B6B', '#9556a4', '#30b7b9']
|
||
|
||
// 表格引用(用于导出功能)
|
||
const tableRef = ref<any>(null)
|
||
|
||
// ==================== API 服务层(待完善) ====================
|
||
|
||
/**
|
||
* 获取Tab统计数据
|
||
*/
|
||
async function fetchTabData(params: any): Promise<TabItem[]> {
|
||
console.log('【API】获取Tab数据,参数:', params)
|
||
|
||
// 开发模式:返回模拟数据
|
||
if (USE_MOCK_DATA) {
|
||
return new Promise((resolve) => {
|
||
setTimeout(() => {
|
||
const _name = `装机容量(${unit.value})`
|
||
const useTtpwrFilter = props.seriesName === _name
|
||
|
||
resolve([
|
||
{
|
||
col: useTtpwrFilter ? 'ttpwr' : 'engTotal',
|
||
key: 'build',
|
||
unit: useTtpwrFilter ? unit.value : '座',
|
||
value: '已建',
|
||
count: useTtpwrFilter ? 4568.50 : 120
|
||
},
|
||
{
|
||
col: useTtpwrFilter ? 'ttpwr' : 'engTotal',
|
||
key: 'building',
|
||
unit: useTtpwrFilter ? unit.value : '座',
|
||
value: '在建',
|
||
count: useTtpwrFilter ? 2345.30 : 85
|
||
},
|
||
{
|
||
col: useTtpwrFilter ? 'ttpwr' : 'engTotal',
|
||
key: 'noBuild',
|
||
unit: useTtpwrFilter ? unit.value : '座',
|
||
value: '未建',
|
||
count: useTtpwrFilter ? 1230.20 : 60
|
||
}
|
||
])
|
||
}, 300)
|
||
})
|
||
}
|
||
|
||
// 生产模式:调用真实 API
|
||
try {
|
||
const _name = `装机容量(${unit.value})`
|
||
const useTtpwrFilter = props.seriesName === _name
|
||
|
||
// 构建排序配置
|
||
const aggregates = [{ field: 'ttpwr', aggregate: 'sum' }]
|
||
const sortConfig = useTtpwrFilter
|
||
? [{ dir: 'desc', field: 'bldsttCcode', aggregates }]
|
||
: [{ dir: 'desc', field: 'bldsttCcode' }]
|
||
|
||
// 调用真实 API
|
||
const res = await getKendoList(
|
||
useTtpwrFilter
|
||
? { ...params, ttpwr: [{ field: 'ttpwr', operator: 'isnotnull' }] }
|
||
: params,
|
||
sortConfig
|
||
)
|
||
|
||
if (!res?.data || !Array.isArray(res.data)) {
|
||
console.warn('【API】Tab数据返回格式异常:', res)
|
||
return []
|
||
}
|
||
|
||
// 数据处理逻辑(与React版本一致)
|
||
let list: TabItem[] = []
|
||
|
||
res.data.reduce((acc: any, cur: any) => {
|
||
if (cur.key === '0') {
|
||
list.push({
|
||
col: useTtpwrFilter ? 'ttpwr' : 'engTotal',
|
||
key: 'noBuild',
|
||
unit: useTtpwrFilter ? unit.value : '座',
|
||
value: '未建',
|
||
count: (cur?.items[0]?.SUM_TTPWR && transUnit(cur?.items[0]?.SUM_TTPWR, 'Other', 'ZJRL')) || cur.items[0].COUNT_ENGID
|
||
})
|
||
} else if (cur.key === '1') {
|
||
list.push({
|
||
col: useTtpwrFilter ? 'ttpwr' : 'engTotal',
|
||
key: 'building',
|
||
unit: useTtpwrFilter ? unit.value : '座',
|
||
value: '在建',
|
||
count: (cur?.items[0]?.SUM_TTPWR && transUnit(cur?.items[0]?.SUM_TTPWR, 'Other', 'ZJRL')) || cur.items[0].COUNT_ENGID
|
||
})
|
||
} else if (cur.key === '2') {
|
||
list.push({
|
||
col: useTtpwrFilter ? 'ttpwr' : 'engTotal',
|
||
key: 'build',
|
||
unit: useTtpwrFilter ? unit.value : '座',
|
||
value: '已建',
|
||
count: (cur?.items[0]?.SUM_TTPWR && transUnit(cur?.items[0]?.SUM_TTPWR, 'Other', 'ZJRL')) || cur.items[0].COUNT_ENGID
|
||
})
|
||
}
|
||
return list
|
||
}, [])
|
||
|
||
console.log('【API】Tab数据处理结果:', list)
|
||
return list
|
||
} catch (error) {
|
||
console.error('【API】获取Tab数据失败:', error)
|
||
message.error('获取Tab数据失败')
|
||
return []
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取饼图分组数据(完整实现React版本的复杂逻辑)
|
||
*/
|
||
async function fetchPieData(params: any): Promise<PieDataItem[]> {
|
||
console.log('【API】获取饼图数据,参数:', params)
|
||
console.log('【API】USE_MOCK_DATA:', USE_MOCK_DATA, 'qid.value:', qid.value)
|
||
|
||
// 开发模式:返回模拟数据
|
||
if (USE_MOCK_DATA) {
|
||
return new Promise((resolve) => {
|
||
setTimeout(() => {
|
||
const _name = `装机容量(${unit.value})`
|
||
const useTtpwrFilter = props.seriesName === _name
|
||
|
||
console.log('【模拟数据】生成饼图数据,useTtpwrFilter:', useTtpwrFilter, 'qid.value:', qid.value)
|
||
|
||
let mockData: PieDataItem[]
|
||
|
||
if (qid.value === 'all') {
|
||
// 全基地模式:按基地分组
|
||
mockData = [
|
||
{ key: 'cj', name: '长江流域', value: useTtpwrFilter ? 4568.50 : 120, unit: '', dataDimensionRecursive: 'true' },
|
||
{ key: 'hh', name: '黄河流域', value: useTtpwrFilter ? 2345.30 : 85, unit: '', dataDimensionRecursive: 'true' },
|
||
{ key: 'zj', name: '珠江流域', value: useTtpwrFilter ? 1230.20 : 60, unit: '', dataDimensionRecursive: 'true' },
|
||
{ key: 'hh2', name: '淮河流域', value: useTtpwrFilter ? 890.10 : 45, unit: '', dataDimensionRecursive: 'true' },
|
||
{ key: 'hj', name: '海河流域', value: useTtpwrFilter ? 567.80 : 30, unit: '', dataDimensionRecursive: 'true' }
|
||
]
|
||
console.log('【模拟数据】全基地模式,生成 5 条数据')
|
||
} else if (qid.value === 'other') {
|
||
// other 模式:按河段分组
|
||
mockData = [
|
||
{ key: 'river1', name: '长江干流', value: useTtpwrFilter ? 890.50 : 25, unit: '', dataDimensionRecursive: 'true', index: 1 },
|
||
{ key: 'river2', name: '黄河干流', value: useTtpwrFilter ? 567.30 : 18, unit: '', dataDimensionRecursive: 'true', index: 2 },
|
||
{ key: 'river3', name: '珠江干流', value: useTtpwrFilter ? 345.20 : 12, unit: '', dataDimensionRecursive: 'true', index: 3 }
|
||
]
|
||
console.log('【模拟数据】other 模式,生成 3 条数据')
|
||
} else {
|
||
// 单个基地模式:按河段分组
|
||
mockData = [
|
||
{ key: 'cjgl', name: '长江干流', value: useTtpwrFilter ? 2345.60 : 50, unit: '', dataDimensionRecursive: 'true' },
|
||
{ key: 'cjzl', name: '长江支流', value: useTtpwrFilter ? 1234.50 : 35, unit: '', dataDimensionRecursive: 'true' },
|
||
{ key: 'cjxz', name: '长江下游', value: useTtpwrFilter ? 987.40 : 25, unit: '', dataDimensionRecursive: 'true' }
|
||
]
|
||
console.log('【模拟数据】单基地模式,生成 3 条数据')
|
||
}
|
||
|
||
console.log('【模拟数据】✅ 饼图数据生成成功:', mockData.length, '条')
|
||
console.log('【模拟数据】数据详情:', mockData)
|
||
|
||
resolve(mockData)
|
||
}, 300)
|
||
})
|
||
}
|
||
|
||
// 生产模式:调用真实 API
|
||
try {
|
||
const _name = `装机容量(${unit.value})`
|
||
const useTtpwrFilter = props.seriesName === _name
|
||
|
||
// 构建排序配置
|
||
const aggregates = [{ field: 'ttpwr', aggregate: 'sum' }]
|
||
const sortConfig = [
|
||
qid.value === 'all' || qid.value === 'other' ? null : { dir: 'asc', field: 'rvcdStepSort' },
|
||
qid.value === 'all' ? { dir: 'asc', field: 'baseStepSort' } : null,
|
||
{
|
||
dir: 'asc',
|
||
field: qid.value === 'all' ? 'baseId' : 'hbrvcd'
|
||
},
|
||
{
|
||
dir: 'desc',
|
||
field: qid.value === 'all' ? 'baseName' : 'hbrvcdName'
|
||
},
|
||
{
|
||
dir: 'desc',
|
||
field: 'bldsttCcode',
|
||
aggregates
|
||
}
|
||
].filter(Boolean)
|
||
|
||
// 调用真实 API
|
||
const res = await getKendoList(
|
||
useTtpwrFilter
|
||
? { ...params, ttpwr: [{ field: 'ttpwr', operator: 'isnotnull' }] }
|
||
: params,
|
||
sortConfig as any[]
|
||
)
|
||
|
||
if (!res?.data || !Array.isArray(res.data)) {
|
||
console.warn('【API】饼图数据返回格式异常:', res)
|
||
return []
|
||
}
|
||
|
||
// 数据处理逻辑(完全复刻React版本的flatMap逻辑)
|
||
let list = res.data.flatMap((item: any) => {
|
||
let arr: PieDataItem[] = []
|
||
|
||
// 关键:根据 qid 决定数据提取路径
|
||
const itmeData = (qid.value === 'all' || qid.value === 'other')
|
||
? item?.items
|
||
: item?.items[0]?.items
|
||
|
||
itmeData?.map((outItem: any) => {
|
||
const itemList = outItem?.items?.[0]?.items?.[0]
|
||
|
||
// 关键:根据 qid 决定 name 的来源
|
||
const name = qid.value === 'all'
|
||
? outItem?.items?.[0]?.items?.[0]?.items?.[0]?.BASENAME
|
||
: (outItem?.items?.[0]?.items?.[0]?.BASENAME || outItem?.items?.[0]?.items?.[0]?.HBRVCDNAME)
|
||
|
||
if (name) {
|
||
arr.push({
|
||
key: qid.value === 'all'
|
||
? item?.items[0].key
|
||
: (qid.value === 'other' ? item?.key : item?.items?.[0]?.key),
|
||
dataDimensionRecursive: 'true',
|
||
name: name,
|
||
unit: '',
|
||
value: qid.value === 'all' && useTtpwrFilter
|
||
? (itemList?.SUM_TTPWR && +transUnit(itemList?.SUM_TTPWR, 'Other', 'ZJRL'))
|
||
: (qid.value === 'all' && !useTtpwrFilter
|
||
? itemList?.COUNT_BASEID
|
||
: ((outItem?.items?.[0]?.items?.[0]?.SUM_TTPWR && +transUnit(outItem?.items?.[0]?.items?.[0]?.SUM_TTPWR, 'Other', 'ZJRL'))
|
||
|| outItem?.items?.[0]?.items?.[0]?.COUNT_BLDSTTCCODE))
|
||
})
|
||
}
|
||
})
|
||
|
||
return arr
|
||
})
|
||
|
||
// 特殊处理:other 类型需要调用 getIntroduce 接口排序
|
||
if (qid.value === 'other' && list.length > 0) {
|
||
const introRes = await getIntroduce(
|
||
[
|
||
{ field: 'wbsType', operator: 'eq', value: 'PSB_RVCD' },
|
||
{ field: 'objId', operator: 'eq', value: 'other' },
|
||
{ field: 'orderIndex', operator: 'isnotnull' }
|
||
],
|
||
[{ field: 'orderIndex', dir: 'asc' }],
|
||
false,
|
||
['wbsCode', 'wbsName', 'orderIndex']
|
||
)
|
||
|
||
if (introRes?.data?.length && list?.length) {
|
||
list = list.map((el: any) => {
|
||
const found = introRes.data.find((item: any) => el?.key === item?.wbsCode)
|
||
return {
|
||
...el,
|
||
index: found?.orderIndex
|
||
}
|
||
}).sort((a: any, b: any) => a?.index - b?.index)
|
||
}
|
||
}
|
||
|
||
console.log('【API】饼图数据处理结果:', list)
|
||
return list
|
||
} catch (error) {
|
||
console.error('【API】获取饼图数据失败:', error)
|
||
message.error('获取饼图数据失败')
|
||
return []
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取other类型的排序信息
|
||
*/
|
||
async function fetchIntroduce(params: any): Promise<any[]> {
|
||
// TODO: 替换为实际API调用
|
||
|
||
console.log('【模拟】获取排序信息,参数:', params)
|
||
|
||
return new Promise((resolve) => {
|
||
setTimeout(() => {
|
||
resolve([
|
||
{ wbsCode: 'river1', orderIndex: 1 },
|
||
{ wbsCode: 'river2', orderIndex: 2 },
|
||
{ wbsCode: 'river3', orderIndex: 3 }
|
||
])
|
||
}, 300)
|
||
})
|
||
}
|
||
|
||
/**
|
||
* 表格数据请求函数
|
||
*/
|
||
async function fetchTableData(params: any): Promise<any> {
|
||
console.log('【API】获取表格数据,参数:', params)
|
||
|
||
// 开发模式:返回模拟数据
|
||
if (USE_MOCK_DATA) {
|
||
return new Promise((resolve) => {
|
||
setTimeout(() => {
|
||
const buildStatus = params.bldsttCcode
|
||
const hbrvcdFilter = Array.isArray(params.hbrvcd)
|
||
? params.hbrvcd[0]?.value || []
|
||
: []
|
||
|
||
// 电站基础数据模板
|
||
const stationTemplates = [
|
||
{ name: '三峡', river: 'cjgl', baseName: '长江流域', province: '湖北省' },
|
||
{ name: '葛洲坝', river: 'cjgl', baseName: '长江流域', province: '湖北省' },
|
||
{ name: '向家坝', river: 'cjgl', baseName: '长江流域', province: '四川省' },
|
||
{ name: '溪洛渡', river: 'cjgl', baseName: '长江流域', province: '云南省' },
|
||
{ name: '白鹤滩', river: 'cjgl', baseName: '长江流域', province: '四川省' },
|
||
{ name: '乌东德', river: 'cjgl', baseName: '长江流域', province: '云南省' },
|
||
{ name: '小浪底', river: 'hhgl', baseName: '黄河流域', province: '河南省' },
|
||
{ name: '刘家峡', river: 'hhgl', baseName: '黄河流域', province: '甘肃省' },
|
||
{ name: '龙羊峡', river: 'hhgl', baseName: '黄河流域', province: '青海省' },
|
||
{ name: '拉西瓦', river: 'hhgl', baseName: '黄河流域', province: '青海省' },
|
||
{ name: '天生桥', river: 'zjgl', baseName: '珠江流域', province: '贵州省' },
|
||
{ name: '岩滩', river: 'zjgl', baseName: '珠江流域', province: '广西区' },
|
||
{ name: '大化', river: 'zjgl', baseName: '珠江流域', province: '广西区' },
|
||
{ name: '响洪甸', river: 'hhgl2', baseName: '淮河流域', province: '安徽省' },
|
||
{ name: '梅山', river: 'hhgl2', baseName: '淮河流域', province: '安徽省' },
|
||
{ name: '潘家口', river: 'hjgl', baseName: '海河流域', province: '河北省' },
|
||
{ name: '大黑汀', river: 'hjgl', baseName: '海河流域', province: '河北省' },
|
||
{ name: '丰满', river: 'sjgl', baseName: '松花江流域', province: '吉林省' },
|
||
{ name: '白山', river: 'sjgl', baseName: '松花江流域', province: '吉林省' },
|
||
{ name: '水丰', river: 'ljgl', baseName: '辽河流域', province: '辽宁省' }
|
||
]
|
||
|
||
// 根据建设状态筛选电站
|
||
let filteredStations = stationTemplates
|
||
|
||
// 如果有河段过滤,进一步筛选
|
||
if (hbrvcdFilter.length > 0) {
|
||
filteredStations = stationTemplates.filter(s => hbrvcdFilter.includes(s.river))
|
||
}
|
||
|
||
// 如果没有匹配的河段,返回空数组
|
||
if (filteredStations.length === 0) {
|
||
// 关键:返回 BasicTable 期望的格式 { data: { records: [], total: 0 } }
|
||
resolve({
|
||
data: {
|
||
records: [],
|
||
total: 0
|
||
}
|
||
})
|
||
return
|
||
}
|
||
|
||
// 生成模拟记录
|
||
const mockRecords = filteredStations.map((station, index) => {
|
||
// 根据不同状态生成不同的装机容量范围
|
||
let ttpwrRange
|
||
if (buildStatus === '2') {
|
||
// 已建:较大容量
|
||
ttpwrRange = [5000, 30000]
|
||
} else if (buildStatus === '1') {
|
||
// 在建:中等容量
|
||
ttpwrRange = [2000, 15000]
|
||
} else {
|
||
// 未建:较小容量或规划中
|
||
ttpwrRange = [1000, 8000]
|
||
}
|
||
|
||
const ttpwr = Math.floor(Math.random() * (ttpwrRange[1] - ttpwrRange[0]) + ttpwrRange[0])
|
||
const yrge = (ttpwr * 0.04).toFixed(2) // 年发电量约为装机容量的4%
|
||
const ttcp = (Math.random() * 200 + 50).toFixed(2)
|
||
|
||
// 投产时间根据状态设置
|
||
let piodt, aiodt
|
||
if (buildStatus === '2') {
|
||
// 已建:过去的时间
|
||
const year = 2000 + Math.floor(Math.random() * 20)
|
||
piodt = `${year}-0${Math.floor(Math.random() * 9 + 1)}-${Math.floor(Math.random() * 28 + 1)}`
|
||
aiodt = `${year + Math.floor(Math.random() * 5 + 1)}-0${Math.floor(Math.random() * 9 + 1)}-${Math.floor(Math.random() * 28 + 1)}`
|
||
} else if (buildStatus === '1') {
|
||
// 在建:未来1-3年
|
||
const year = 2026 + Math.floor(Math.random() * 3)
|
||
piodt = `${year}-0${Math.floor(Math.random() * 9 + 1)}-${Math.floor(Math.random() * 28 + 1)}`
|
||
aiodt = `${year + Math.floor(Math.random() * 3 + 1)}-0${Math.floor(Math.random() * 9 + 1)}-${Math.floor(Math.random() * 28 + 1)}`
|
||
} else {
|
||
// 未建:更远的未来
|
||
const year = 2028 + Math.floor(Math.random() * 7)
|
||
piodt = `${year}-0${Math.floor(Math.random() * 9 + 1)}-${Math.floor(Math.random() * 28 + 1)}`
|
||
aiodt = `${year + Math.floor(Math.random() * 5 + 2)}-0${Math.floor(Math.random() * 9 + 1)}-${Math.floor(Math.random() * 28 + 1)}`
|
||
}
|
||
|
||
return {
|
||
id: `station_${buildStatus}_${index}`,
|
||
stcd: `ST${String(index + 1).padStart(4, '0')}`,
|
||
stnm: `${station.name}水电站`,
|
||
dvtp: ['堤坝式', '引水式', '混合式'][index % 3],
|
||
dvtpName: ['堤坝式', '引水式', '混合式'][index % 3],
|
||
ttpwr: ttpwr,
|
||
yrge: yrge,
|
||
ttcp: ttcp,
|
||
baseName: station.baseName,
|
||
addvcdName: station.province,
|
||
hbrvcdName: station.name === '三峡' ? '长江干流' :
|
||
station.name === '葛洲坝' ? '长江干流' :
|
||
station.name === '小浪底' ? '黄河干流' :
|
||
station.name === '刘家峡' ? '黄河干流' :
|
||
station.name === '天生桥' ? '珠江干流' :
|
||
station.name === '响洪甸' ? '淮河干流' :
|
||
station.name === '潘家口' ? '海河干流' :
|
||
station.name === '丰满' ? '松花江干流' :
|
||
station.name === '水丰' ? '辽河干流' : `${station.name.substring(0, 2)}河段`,
|
||
piodt: piodt,
|
||
aiodt: aiodt
|
||
}
|
||
})
|
||
|
||
console.log('【调试】模拟表格数据:', {
|
||
records: mockRecords.length,
|
||
total: mockRecords.length,
|
||
firstRecord: mockRecords[0]
|
||
})
|
||
|
||
// 关键:返回 BasicTable 期望的格式 { data: { records: [], total: 0 } }
|
||
resolve({
|
||
data: {
|
||
records: mockRecords,
|
||
total: mockRecords.length
|
||
}
|
||
})
|
||
}, 400)
|
||
})
|
||
}
|
||
|
||
// 生产模式:调用真实 API
|
||
try {
|
||
// 根据 qid 决定使用哪个接口
|
||
const url = qid.value === 'all'
|
||
? '/dec-lygk-base-server/base/vmsstbprpt/GetKendoListCust'
|
||
: '/dec-lygk-base-server/base/vmsstbprpt/GetKendoList'
|
||
|
||
const apiFunc = qid.value === 'all' ? getKendoListCust : getKendoList
|
||
|
||
// 调用真实 API
|
||
const res = await apiFunc(params, [])
|
||
|
||
// 关键:确保返回格式为 { data: { records: [], total: 0 } }
|
||
return {
|
||
data: {
|
||
records: res?.data?.records || res?.data?.items || [],
|
||
total: res?.data?.total || 0
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error('【API】获取表格数据失败:', error)
|
||
message.error('获取表格数据失败')
|
||
return {
|
||
data: {
|
||
records: [],
|
||
total: 0
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// ==================== 工具函数 ====================
|
||
|
||
/**
|
||
* 单位转换函数
|
||
*/
|
||
function transUnit(value: number, from: string, to: string): number {
|
||
// 根据实际业务规则实现
|
||
// 当前假设:接口返回的单位是 kW,需要转换为万kW
|
||
if (from === 'Other' && to === 'ZJRL') {
|
||
return value / 10000 // kW → 万kW
|
||
}
|
||
return value
|
||
}
|
||
|
||
/**
|
||
* 获取单位配置
|
||
*/
|
||
function getUnitConfigByCode(code: string, type: string): { unit: string } {
|
||
// TODO: 从配置文件或API获取
|
||
// 根据项目实际情况,装机容量通常使用"万kW"作为单位
|
||
return { unit: '万kW' }
|
||
}
|
||
|
||
/**
|
||
* 处理导出功能
|
||
*/
|
||
function handleExport() {
|
||
if (tableRef.value && typeof tableRef.value.export === 'function') {
|
||
// 假设 BasicTable 暴露了 export 方法,请根据实际组件 API 调整
|
||
tableRef.value.export()
|
||
} else {
|
||
message.info('导出功能待实现:请确认 BasicTable 组件是否暴露了导出方法')
|
||
console.log('Table Ref:', tableRef.value)
|
||
}
|
||
}
|
||
|
||
// ==================== ECharts 饼图配置 ====================
|
||
|
||
/**
|
||
* 计算饼图配置(增强版,支持themeecList自定义颜色)
|
||
*/
|
||
const chartSetting = computed<EChartsOption>(() => {
|
||
console.log('【chartSetting】计算饼图配置,pieData.value 长度:', pieData.value.length)
|
||
console.log('【chartSetting】pieData.value 内容:', pieData.value)
|
||
|
||
const total = pieData.value.reduce((acc, item) => acc + item.value, 0).toFixed(2)
|
||
const flag = props.seriesName === '数量(座)'
|
||
|
||
const bbbb = flag ? '电站总数量' : '总装机容量'
|
||
const dddd = flag ? '电站数量' : '装机容量'
|
||
const cccc = flag ? ' (座)' : ` (${unit.value})`
|
||
|
||
console.log('【chartSetting】配置信息:', { total, bbbb, dddd, cccc, seriesDataLength: pieData.value.length })
|
||
|
||
// 关键:根据 themeecList 动态设置颜色(与React版本一致)
|
||
const colors = pieData.value.map((item: any, index: any) => {
|
||
if (props.themeecList && props.themeecList.length > 0) {
|
||
const themeColor = props.themeecList.find((i: any) => i.name === item.name)
|
||
return themeColor?.color || jdColor[index % jdColor.length]
|
||
}
|
||
return jdColor[index % jdColor.length]
|
||
})
|
||
|
||
return {
|
||
color: colors, // 添加颜色配置
|
||
tooltip: {
|
||
trigger: 'item',
|
||
formatter: (params: any) =>
|
||
`${dddd}<div>${params.marker}${params.name}: <span style="float:right">${params.value}${cccc}</span></div>`
|
||
},
|
||
title: [
|
||
{
|
||
text: String(total || 0), // 修复:确保text为string类型
|
||
left: 'center',
|
||
top: '40%',
|
||
textStyle: { fontSize: 24, fontWeight: 'bold', color: '#333' }
|
||
},
|
||
{
|
||
text: bbbb,
|
||
left: 'center',
|
||
top: '50%',
|
||
textStyle: { fontSize: 14, color: '#666' }
|
||
},
|
||
{
|
||
text: cccc,
|
||
left: 'center',
|
||
top: '55%',
|
||
textStyle: { fontSize: 12, color: '#999' }
|
||
}
|
||
],
|
||
legend: {
|
||
type: 'scroll',
|
||
orient: 'vertical',
|
||
right: 10,
|
||
top: 20,
|
||
bottom: 20,
|
||
textStyle: { fontSize: 12 }
|
||
},
|
||
series: [
|
||
{
|
||
name: dddd,
|
||
type: 'pie',
|
||
radius: ['40%', '70%'],
|
||
center: ['35%', '50%'],
|
||
avoidLabelOverlap: true,
|
||
itemStyle: {
|
||
borderRadius: 4,
|
||
borderColor: '#fff',
|
||
borderWidth: 2
|
||
},
|
||
label: {
|
||
show: true,
|
||
position: 'outside',
|
||
formatter: '{b}\n{c}',
|
||
fontSize: 12
|
||
},
|
||
emphasis: {
|
||
scale: true,
|
||
scaleSize: 10
|
||
},
|
||
data: pieData.value.map((item, index) => ({
|
||
...item
|
||
// 颜色已在顶层color中统一设置
|
||
}))
|
||
}
|
||
]
|
||
}
|
||
})
|
||
|
||
/**
|
||
* 初始化图表
|
||
*/
|
||
function initChart() {
|
||
console.log('【图表初始化】尝试初始化,chartRef.value:', chartRef.value)
|
||
console.log('【图表初始化】当前 pieData.value 长度:', pieData.value.length)
|
||
|
||
if (!chartRef.value) {
|
||
console.warn('【图表初始化】容器未就绪,延迟重试')
|
||
setTimeout(initChart, 200)
|
||
return
|
||
}
|
||
|
||
// 验证容器尺寸
|
||
const rect = chartRef.value.getBoundingClientRect()
|
||
const containerWidth = chartRef.value.offsetWidth
|
||
const containerHeight = chartRef.value.offsetHeight
|
||
|
||
console.log('【图表初始化】容器信息:', {
|
||
offsetWidth: containerWidth,
|
||
offsetHeight: containerHeight,
|
||
getBoundingClientRect: {
|
||
width: rect.width,
|
||
height: rect.height,
|
||
top: rect.top,
|
||
left: rect.left
|
||
},
|
||
display: window.getComputedStyle(chartRef.value).display,
|
||
visibility: window.getComputedStyle(chartRef.value).visibility,
|
||
parentDisplay: window.getComputedStyle(chartRef.value.parentElement).display
|
||
})
|
||
|
||
// 临时添加红色边框验证容器可见性
|
||
chartRef.value.style.border = '3px solid red'
|
||
chartRef.value.style.background = 'rgba(255, 0, 0, 0.1)'
|
||
|
||
if (containerWidth === 0 || containerHeight === 0) {
|
||
console.error('【图表初始化】❌ 容器尺寸为0,无法初始化')
|
||
console.log('【图表初始化】⏰ 200ms 后重试...')
|
||
setTimeout(initChart, 200)
|
||
return
|
||
}
|
||
|
||
console.log('【图表初始化】✅ 容器尺寸正常,开始初始化 ECharts')
|
||
|
||
// 关键:使用固定的图表尺寸(541px × 412px)
|
||
chartInstance.value = echarts.init(chartRef.value, null, {
|
||
width: 541,
|
||
height: 412
|
||
})
|
||
|
||
console.log('【图表初始化】ECharts 实例创建成功')
|
||
|
||
// 绑定图例选择事件
|
||
chartInstance.value.on('legendselectchanged', handleLegendSelectChanged)
|
||
|
||
// 绑定点击事件
|
||
chartInstance.value.on('click', handlePieClick)
|
||
|
||
// 关键:只有当有数据时才调用 updateChart
|
||
if (pieData.value.length > 0) {
|
||
console.log('【图表初始化】✅ 数据已就绪,渲染图表')
|
||
updateChart()
|
||
|
||
// 关键:延迟调用 resize 确保渲染完成
|
||
setTimeout(() => {
|
||
chartInstance.value?.resize()
|
||
console.log('【图表初始化】✅ resize 完成')
|
||
console.log('【图表初始化】图表数据:', pieData.value.length, '条')
|
||
chartInitialized.value = true
|
||
}, 100)
|
||
} else {
|
||
console.log('【图表初始化】⏰ 数据未就绪,标记为已初始化,等待 watch 触发更新')
|
||
// 设置标志,数据加载后会触发渲染
|
||
chartInitialized.value = true
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 更新图表
|
||
*/
|
||
function updateChart() {
|
||
if (!chartInstance.value) return
|
||
chartInstance.value.setOption(chartSetting.value, true)
|
||
}
|
||
|
||
/**
|
||
* 处理窗口大小变化
|
||
*/
|
||
function handleResize() {
|
||
chartInstance.value?.resize()
|
||
}
|
||
|
||
/**
|
||
* 饼图点击事件(完全复刻React版本逻辑)
|
||
*/
|
||
function handlePieClick(params: any) {
|
||
if (qid.value !== 'all') {
|
||
return // 仅在全基地模式下支持点击联动
|
||
}
|
||
|
||
// 关键:根据点击的扇区名称查找对应的基地ID
|
||
const matchedOption = searchList.value[0]?.options?.find(
|
||
(value: any) => value.label === params.name
|
||
)
|
||
|
||
if (matchedOption) {
|
||
qid.value = matchedOption.qid || 'other'
|
||
|
||
// 关键:获取 dataDimensionRecursive 标志(用于控制表格刷新行为)
|
||
const matchedData = pieData.value.find((item: any) => item.name === params.name)
|
||
console.log('【饼图点击】切换到基地:', params.name, 'qid:', qid.value, 'dataDimensionRecursive:', matchedData?.dataDimensionRecursive)
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 图例选择变化事件(完全复刻React版本逻辑)
|
||
*/
|
||
function handleLegendSelectChanged(e: any) {
|
||
const trueRivers = Object.keys(e.selected).filter(key => e.selected[key])
|
||
const allTrue = Object.values(e.selected).every(value => value === false)
|
||
|
||
const selectedKeys = pikData.value
|
||
.filter((item: any) => trueRivers.includes(item.name))
|
||
.map((item: any) => item.key)
|
||
|
||
// 关键:与React版本保持一致,全选时设置为 ["0"]
|
||
pieCode.value = allTrue ? ['0'] : selectedKeys
|
||
|
||
console.log('【图例联动】allTrue:', allTrue, 'selectedKeys:', selectedKeys, 'pieCode:', pieCode.value)
|
||
}
|
||
|
||
// ==================== 表格列配置 ====================
|
||
|
||
const columns: ColumnsType = [
|
||
{
|
||
title: '电站名称',
|
||
dataIndex: 'stnm',
|
||
key: 'stnm',
|
||
fixed: 'left',
|
||
ellipsis: true,
|
||
width: 150,
|
||
customRender: ({ record }: any) => ({
|
||
children: record.stnm,
|
||
attrs: {
|
||
onClick: () => handleRowClick(record)
|
||
},
|
||
style: {
|
||
color: '#1890ff',
|
||
cursor: 'pointer'
|
||
}
|
||
})
|
||
},
|
||
{
|
||
title: '开发方式',
|
||
dataIndex: 'dvtp',
|
||
key: 'dvtp',
|
||
ellipsis: true,
|
||
customRender: ({ record }: any) => record.dvtpName || '-'
|
||
},
|
||
{
|
||
title: `装机容量(${unit.value})`,
|
||
dataIndex: 'ttpwr',
|
||
key: 'ttpwr',
|
||
ellipsis: true,
|
||
customRender: ({ text }: any) => text ? transUnit(text, 'Other', 'ZJRL').toFixed(2) : '-'
|
||
},
|
||
{
|
||
title: '设计年发电量(亿kW·h)',
|
||
dataIndex: 'yrge',
|
||
key: 'yrge',
|
||
ellipsis: true,
|
||
},
|
||
{
|
||
title: '总库容(亿m³)',
|
||
dataIndex: 'ttcp',
|
||
key: 'ttcp',
|
||
ellipsis: true
|
||
},
|
||
{
|
||
title: '所属基地',
|
||
dataIndex: 'baseName',
|
||
key: 'baseName',
|
||
ellipsis: true
|
||
},
|
||
{
|
||
title: '所属行政区',
|
||
dataIndex: 'addvcdName',
|
||
key: 'addvcdName',
|
||
ellipsis: true
|
||
},
|
||
{
|
||
title: '所在河段',
|
||
dataIndex: 'hbrvcdName',
|
||
key: 'hbrvcdName',
|
||
ellipsis: true
|
||
},
|
||
{
|
||
title: '第一台机组投产时间',
|
||
dataIndex: 'piodt',
|
||
key: 'piodt',
|
||
ellipsis: true,
|
||
width: 170
|
||
},
|
||
{
|
||
title: '全部机组投产时间',
|
||
dataIndex: 'aiodt',
|
||
key: 'aiodt',
|
||
ellipsis: true
|
||
}
|
||
]
|
||
|
||
/**
|
||
* 处理表格行点击(打开详情弹窗)
|
||
*/
|
||
function handleRowClick(record: any) {
|
||
// 使用全局 MapModal 组件
|
||
modelStore.params = {
|
||
sttp: 'ENG',
|
||
stcd: record.stcd,
|
||
titleName: record.stnm
|
||
} as any // 临时类型断言,因为 store 定义不完整
|
||
modelStore.title = record.stnm ? `${record.stnm} ` : ''
|
||
modelStore.tabList = handleTabs(modelStore.params)
|
||
modelStore.modalVisible = true
|
||
|
||
console.log('【打开详情弹窗】参数:', modelStore.params)
|
||
}
|
||
|
||
// ==================== 搜索表单配置 ====================
|
||
|
||
const searchList = computed(() => [
|
||
{
|
||
type: 'Select',
|
||
name: 'Select',
|
||
label: '基地选择',
|
||
options: jiDiList.value?.map((item: any) => ({
|
||
qid: item.baseid,
|
||
value: item.basename,
|
||
label: item.basename
|
||
})) || []
|
||
},
|
||
{
|
||
type: 'Input',
|
||
name: 'stnm',
|
||
label: '',
|
||
fieldProps: {
|
||
allowClear: true,
|
||
placeholder: '请输入电站名称'
|
||
}
|
||
}
|
||
])
|
||
|
||
const initialValues = computed(() => ({
|
||
Select: defJD.value,
|
||
stnm: null
|
||
}))
|
||
|
||
/**
|
||
* 处理搜索提交(完全复刻React版本逻辑)
|
||
*/
|
||
function handleSearch(values: any) {
|
||
// 关键:根据选中的基地名称查找对应的qid
|
||
const matchedOption = searchList.value[0]?.options?.find(
|
||
(value: any) => value.label === values.Select
|
||
)
|
||
|
||
qid.value = matchedOption?.qid || 'other'
|
||
stnm.value = values.stnm
|
||
|
||
console.log('【搜索】基地:', values.Select, 'qid:', qid.value, '电站名称:', stnm.value)
|
||
}
|
||
|
||
/**
|
||
* 处理重置
|
||
*/
|
||
function handleReset() {
|
||
qid.value = ''
|
||
stnm.value = null
|
||
defJD.value = props.defaultJidiInfo?.basename as string || ''
|
||
}
|
||
|
||
// ==================== 数据监听与副作用 ====================
|
||
|
||
/**
|
||
* 表格过滤条件计算(完全复刻React版本逻辑)
|
||
*/
|
||
const tableFilter = computed<TableFilterParams>(() => {
|
||
let res: TableFilterParams = {
|
||
// 关键:根据 pieCode 和 qid 动态决定 baseId 的格式
|
||
baseId: (pieCode.value?.length > 0 && qid.value === 'all')
|
||
? [{
|
||
field: 'baseId',
|
||
operator: 'in',
|
||
dataType: 'string',
|
||
value: pieCode.value
|
||
}]
|
||
: (qid.value || (props.defaultJidiInfo?.baseid as string)),
|
||
|
||
sttpCode: 'ENG',
|
||
|
||
// 关键:根据 pieCode 和 qid 动态决定 hbrvcd 的格式
|
||
hbrvcd: (pieCode.value?.length > 0 && qid.value !== 'all')
|
||
? [{
|
||
field: 'hbrvcd',
|
||
operator: 'in',
|
||
dataType: 'string',
|
||
value: pieCode.value
|
||
}]
|
||
: undefined,
|
||
|
||
bldsttCcode: tabKey.value === 'noBuild' ? '0' : tabKey.value === 'building' ? '1' : '2',
|
||
stnmContains: stnm.value,
|
||
prsc: [
|
||
{ field: 'prsc', operator: 'in', dataType: 'string', value: ['1', '2', '3'] }
|
||
]
|
||
}
|
||
|
||
// 根据 seriesName 决定是否添加 ttpwr 过滤(与React版本保持一致,但注释掉)
|
||
// if (props.seriesName === `装机容量(${unit.value})`) {
|
||
// res = { ...res, ttpwr: [{ field: 'ttpwr', operator: 'isnotnull' }] }
|
||
// }
|
||
|
||
// 关键:清理无效字段(与React版本一致)
|
||
if (res.baseId === 'all') {
|
||
delete res.baseId
|
||
}
|
||
|
||
if (!res.hbrvcd) {
|
||
delete res.hbrvcd
|
||
}
|
||
|
||
if (stnm.value === null) {
|
||
delete res.stnmContains
|
||
}
|
||
|
||
return res
|
||
})
|
||
|
||
/**
|
||
* 监听基地变化,获取Tab数据
|
||
*/
|
||
watch(qid, async (newQid) => {
|
||
if (!newQid) return
|
||
|
||
dataLoading.value = true
|
||
try {
|
||
const params = {
|
||
baseId: newQid,
|
||
sttpCode: 'ENG',
|
||
prsc: [{ field: 'prsc', operator: 'in', dataType: 'string', value: ['1', '2', '3'] }]
|
||
}
|
||
|
||
const result = await fetchTabData(params)
|
||
tabList.value = result
|
||
|
||
if (result.length > 0) {
|
||
// 关键:根据 defaultTab 参数设置默认选中的 Tab
|
||
if (props.defaultTab) {
|
||
const matchedTab = result.find(item => item.value === props.defaultTab)
|
||
if (matchedTab) {
|
||
tabIndex.value = matchedTab.value
|
||
tabKey.value = matchedTab.key
|
||
tabCol.value = matchedTab.col
|
||
} else {
|
||
// 如果没匹配到,使用第一个 Tab
|
||
tabIndex.value = result[0].value
|
||
tabKey.value = result[0].key
|
||
tabCol.value = result[0].col
|
||
}
|
||
} else {
|
||
// 没有传入 defaultTab,使用默认行为(第一个 Tab)
|
||
tabIndex.value = result[0].value
|
||
tabKey.value = result[0].key
|
||
tabCol.value = result[0].col
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.error('获取Tab数据失败:', error)
|
||
message.error('获取Tab数据失败')
|
||
} finally {
|
||
dataLoading.value = false
|
||
}
|
||
}, { immediate: true })
|
||
|
||
/**
|
||
* 监听 defaultTab 变化,动态切换 Tab(解决第二次进入页面时失效的问题)
|
||
*/
|
||
watch(
|
||
() => props.defaultTab,
|
||
(newDefaultTab) => {
|
||
if (!newDefaultTab || tabList.value.length === 0) return
|
||
|
||
const matchedTab = tabList.value.find(item => item.value === newDefaultTab)
|
||
if (matchedTab) {
|
||
tabIndex.value = matchedTab.value
|
||
tabKey.value = matchedTab.key
|
||
tabCol.value = matchedTab.col
|
||
}
|
||
},
|
||
{ immediate: true }
|
||
)
|
||
|
||
/**
|
||
* 监听Tab和搜索条件变化,获取饼图数据
|
||
*/
|
||
watch(
|
||
[tabKey, tabCol, stnm, qid],
|
||
async () => {
|
||
console.log('【Watch触发】tabKey:', tabKey.value, 'tabCol:', tabCol.value, 'stnm:', stnm.value, 'qid:', qid.value)
|
||
|
||
if (!tabKey.value || !tabCol.value) {
|
||
console.log('【Watch触发】跳过:tabKey 或 tabCol 为空')
|
||
return
|
||
}
|
||
|
||
dataLoading.value = true
|
||
try {
|
||
const params: any = {
|
||
stnmContains: stnm.value,
|
||
baseId: qid.value,
|
||
sttpCode: 'ENG',
|
||
bldsttCcode: tabKey.value === 'noBuild' ? '0' : tabKey.value === 'building' ? '1' : '2',
|
||
prsc: [{ field: 'prsc', operator: 'in', dataType: 'string', value: ['1', '2', '3'] }]
|
||
}
|
||
|
||
if (props.seriesName === `装机容量(${unit.value})`) {
|
||
params.ttpwr = [{ field: 'ttpwr', operator: 'isnotnull' }]
|
||
}
|
||
|
||
if (stnm.value === null) {
|
||
delete params.stnmContains
|
||
}
|
||
|
||
console.log('【Watch触发】调用 fetchPieData,参数:', params)
|
||
const result = await fetchPieData(params)
|
||
|
||
console.log('【Watch触发】✅ fetchPieData 返回结果:', result.length, '条')
|
||
console.log('【Watch触发】数据内容:', result)
|
||
|
||
// 特殊处理 other 类型
|
||
if (qid.value === 'other' && result.length > 0) {
|
||
const introRes = await fetchIntroduce({
|
||
logic: 'and',
|
||
filters: [
|
||
{ field: 'wbsType', operator: 'eq', value: 'PSB_RVCD' },
|
||
{ field: 'objId', operator: 'eq', value: 'other' },
|
||
{ field: 'orderIndex', operator: 'isnotnull' }
|
||
]
|
||
})
|
||
|
||
if (introRes?.length) {
|
||
result.forEach((item: any) => {
|
||
const found = introRes.find((i: any) => item.key === i.wbsCode)
|
||
if (found) {
|
||
item.index = found.orderIndex
|
||
}
|
||
})
|
||
result.sort((a: any, b: any) => (a.index || 0) - (b.index || 0))
|
||
}
|
||
}
|
||
|
||
console.log('【Watch触发】更新 pieData,数据量:', result.length)
|
||
pikData.value = result
|
||
pieData.value = result
|
||
pieCode.value = []
|
||
|
||
console.log('【Watch触发】pieData.value 更新后的值:', pieData.value)
|
||
|
||
// 关键:等待 DOM 更新
|
||
await nextTick()
|
||
|
||
// 关键:如果图表未初始化,先初始化;否则直接更新
|
||
if (!chartInitialized.value) {
|
||
console.log('【Watch触发】图表未初始化,开始初始化图表')
|
||
// 延迟初始化,确保容器完全渲染
|
||
setTimeout(() => {
|
||
initChart()
|
||
}, 200)
|
||
} else {
|
||
// 图表已初始化,直接更新数据
|
||
console.log('【Watch触发】图表已初始化,更新数据')
|
||
updateChart()
|
||
chartInstance.value?.resize()
|
||
console.log('【图表更新】数据变化,重新渲染')
|
||
}
|
||
|
||
} catch (error) {
|
||
console.error('【API】获取饼图数据失败:', error)
|
||
message.error('获取饼图数据失败')
|
||
} finally {
|
||
dataLoading.value = false
|
||
}
|
||
},
|
||
{ deep: true }
|
||
)
|
||
|
||
/**
|
||
* 生命周期钩子
|
||
*/
|
||
onMounted(() => {
|
||
// 模拟加载基地列表(更真实的基地数据)
|
||
JidiSelectEventStore.jidiData.forEach(element => {
|
||
jiDiList.value.push({
|
||
label: element.wbsName,
|
||
value: element.wbsCode
|
||
})
|
||
});
|
||
|
||
// 设置默认选中"全部基地"
|
||
if (!qid.value) {
|
||
qid.value = 'all'
|
||
}
|
||
|
||
// 关键:不再在 onMounted 中初始化图表
|
||
// 图表初始化由 watch 监听数据加载后触发,避免竞态条件
|
||
console.log('【生命周期】onMounted 触发,等待数据加载后初始化图表')
|
||
|
||
window.addEventListener('resize', handleResize)
|
||
})
|
||
|
||
// 监听 Tab 切换,确保图表在 Tab 激活时正确渲染
|
||
watch(tabIndex, async (newTab, oldTab) => {
|
||
console.log('【Tab切换】从', oldTab, '切换到', newTab)
|
||
|
||
// 等待 DOM 更新
|
||
await nextTick()
|
||
|
||
setTimeout(() => {
|
||
if (chartInstance.value && chartInitialized.value) {
|
||
console.log('【Tab切换】重新调整图表尺寸')
|
||
chartInstance.value.resize()
|
||
updateChart()
|
||
} else if (!chartInitialized.value) {
|
||
console.log('【Tab切换】图表未初始化,等待数据加载后初始化')
|
||
// 不在此处初始化,由 watch([tabKey, tabCol...]) 处理
|
||
}
|
||
}, 300)
|
||
})
|
||
|
||
onUnmounted(() => {
|
||
chartInstance.value?.dispose()
|
||
window.removeEventListener('resize', handleResize)
|
||
})
|
||
</script>
|
||
|
||
<template>
|
||
<div class="shuidian-kaifa-container">
|
||
<!-- 搜索表单 -->
|
||
<BasicSearch
|
||
ref="formRef"
|
||
:searchList="searchList"
|
||
:initialValues="initialValues"
|
||
@finish="handleSearch"
|
||
@reset="handleReset"
|
||
>
|
||
<template #actions>
|
||
<!-- TODO: 集成ExportBtnFileWithSelect组件 -->
|
||
<!-- <ExportBtnFileWithSelect
|
||
authCode="sdkfzk-export"
|
||
tableRef="tableRef"
|
||
exportUrl="/dec-lygk-base-server/base/msstbprpt/export"
|
||
:select="['stnm', 'baseName', 'addvcdName', 'rvcdName', 'reachcdName', 'dvtp', 'ttpwr', 'yrge', 'ttcp', 'piodt', 'aiodt']"
|
||
:useDefaultColumnSelect="false"
|
||
fileName="水电开发情况数据"
|
||
/> -->
|
||
<a-button @click="handleExport">
|
||
<template #icon><DownloadOutlined /></template>
|
||
导出
|
||
</a-button>
|
||
</template>
|
||
</BasicSearch>
|
||
|
||
<!-- Tab切换 -->
|
||
<a-tabs v-model:activeKey="tabIndex" class="dwsta-table-tu">
|
||
<a-tab-pane
|
||
v-for="item in tabList"
|
||
:key="item.value"
|
||
:tab="`${item.value}(${item.count}${item.unit})`"
|
||
>
|
||
<!-- 图表+表格布局 -->
|
||
<div class="chart-table-layout">
|
||
<!-- 左侧饼图 -->
|
||
<div class="chart-section">
|
||
<div
|
||
ref="chartRef"
|
||
class="pie-chart"
|
||
style="width: 541px; height: 412px;"
|
||
></div>
|
||
</div>
|
||
|
||
<!-- 右侧表格 -->
|
||
<div class="table-section">
|
||
<BasicTable
|
||
ref="tableRef"
|
||
:columns="columns"
|
||
:listUrl="fetchTableData"
|
||
:searchParams="tableFilter"
|
||
:defaultPageSize="20"
|
||
:scrollY="380"
|
||
/>
|
||
</div>
|
||
</div>
|
||
</a-tab-pane>
|
||
</a-tabs>
|
||
|
||
<!-- 详情弹窗已改用全局 MapModal 组件,在 AppMain.vue 中统一管理 -->
|
||
<!-- 无需在此处添加 a-modal,点击表格行会自动触发 modelStore.modalVisible -->
|
||
</div>
|
||
</template>
|
||
|
||
<style scoped lang="scss">
|
||
.shuidian-kaifa-container {
|
||
display: flex;
|
||
flex-direction: column;
|
||
height: 700px;
|
||
padding: 16px;
|
||
background: #fff;
|
||
border-radius: 4px;
|
||
}
|
||
|
||
.dwsta-table-tu {
|
||
flex: 1;
|
||
display: flex;
|
||
flex-direction: column;
|
||
|
||
:deep(.ant-tabs-content) {
|
||
height: 100%;
|
||
}
|
||
|
||
:deep(.ant-tabs-tabpane) {
|
||
height: 100%;
|
||
}
|
||
|
||
:deep(.ant-tabs-nav) {
|
||
margin-bottom: 16px;
|
||
}
|
||
}
|
||
|
||
.chart-table-layout {
|
||
display: flex;
|
||
gap: 16px;
|
||
height: calc(100% - 40px);
|
||
min-height: 450px;
|
||
|
||
.chart-section {
|
||
flex: 0 0 541px;
|
||
background: #fafafa;
|
||
border-radius: 4px;
|
||
padding: 16px;
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
|
||
.pie-chart {
|
||
width: 541px;
|
||
height: 412px;
|
||
}
|
||
}
|
||
|
||
.table-section {
|
||
flex: 1;
|
||
overflow: hidden;
|
||
background: #fff;
|
||
border-radius: 4px;
|
||
}
|
||
}
|
||
|
||
.modal-content {
|
||
padding: 16px;
|
||
|
||
:deep(.ant-descriptions) {
|
||
margin-bottom: 16px;
|
||
}
|
||
}
|
||
</style> |