1209 lines
36 KiB
Vue
1209 lines
36 KiB
Vue
|
|
<script setup lang="ts">
|
|||
|
|
// ==================== 导入依赖 ====================
|
|||
|
|
import { ref, computed, watch, onMounted, onUnmounted } 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'
|
|||
|
|
// TODO: 需要确认项目中是否有这些组件
|
|||
|
|
// import { ModalTabs } from '@/components/MapModal/modalTabs'
|
|||
|
|
// import { handleTabs, modalTabSetting } from '@/components/MapModal/setting.config'
|
|||
|
|
|
|||
|
|
// ==================== 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
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ==================== 响应式状态 ====================
|
|||
|
|
|
|||
|
|
// 基地相关
|
|||
|
|
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 dataLoading = ref<boolean>(false)
|
|||
|
|
|
|||
|
|
// 弹窗相关
|
|||
|
|
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[]> {
|
|||
|
|
// TODO: 替换为实际API调用
|
|||
|
|
// const res = await shuiDianKaifaZhuangKung.GetKendoList(params, sortConfig)
|
|||
|
|
|
|||
|
|
console.log('【模拟】获取Tab数据,参数:', params)
|
|||
|
|
|
|||
|
|
return new Promise((resolve) => {
|
|||
|
|
setTimeout(() => {
|
|||
|
|
// 根据 seriesName 决定是否包含 ttpwr 过滤
|
|||
|
|
const _name = `装机容量(${unit.value})`
|
|||
|
|
const useTtpwrFilter = props.seriesName === _name
|
|||
|
|
|
|||
|
|
// 模拟返回数据结构(与后端GetKendoList返回格式一致)
|
|||
|
|
const mockResponse = [
|
|||
|
|
{
|
|||
|
|
key: '2',
|
|||
|
|
items: [{
|
|||
|
|
SUM_TTPWR: 1069300000, // kW单位
|
|||
|
|
COUNT_ENGID: 150
|
|||
|
|
}]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
key: '1',
|
|||
|
|
items: [{
|
|||
|
|
SUM_TTPWR: 331000000,
|
|||
|
|
COUNT_ENGID: 80
|
|||
|
|
}]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
key: '0',
|
|||
|
|
items: [{
|
|||
|
|
SUM_TTPWR: 179000000,
|
|||
|
|
COUNT_ENGID: 30
|
|||
|
|
}]
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
// 模拟数据处理逻辑(与React版本一致)
|
|||
|
|
let list: TabItem[] = []
|
|||
|
|
|
|||
|
|
const result = mockResponse.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
|
|||
|
|
}, [])
|
|||
|
|
|
|||
|
|
resolve(result)
|
|||
|
|
}, 500)
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取饼图分组数据(完整实现React版本的复杂逻辑)
|
|||
|
|
*/
|
|||
|
|
async function fetchPieData(params: any): Promise<PieDataItem[]> {
|
|||
|
|
// TODO: 替换为实际API调用
|
|||
|
|
|
|||
|
|
console.log('【模拟】获取饼图数据,参数:', params)
|
|||
|
|
|
|||
|
|
return new Promise((resolve) => {
|
|||
|
|
setTimeout(() => {
|
|||
|
|
const _name = `装机容量(${unit.value})`
|
|||
|
|
const useTtpwrFilter = props.seriesName === _name
|
|||
|
|
|
|||
|
|
// 模拟后端返回的嵌套数据结构(与GetKendoList返回格式一致)
|
|||
|
|
let mockResponse: any[]
|
|||
|
|
|
|||
|
|
if (qid.value === 'all') {
|
|||
|
|
// 全基地模式:按baseId分组
|
|||
|
|
mockResponse = [
|
|||
|
|
{
|
|||
|
|
key: 'cj',
|
|||
|
|
items: [{
|
|||
|
|
key: 'cj_base',
|
|||
|
|
items: [{
|
|||
|
|
BASENAME: '长江流域',
|
|||
|
|
SUM_TTPWR: 456800000, // kW
|
|||
|
|
COUNT_BASEID: 120
|
|||
|
|
}]
|
|||
|
|
}]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
key: 'hh',
|
|||
|
|
items: [{
|
|||
|
|
key: 'hh_base',
|
|||
|
|
items: [{
|
|||
|
|
BASENAME: '黄河流域',
|
|||
|
|
SUM_TTPWR: 234500000,
|
|||
|
|
COUNT_BASEID: 85
|
|||
|
|
}]
|
|||
|
|
}]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
key: 'zj',
|
|||
|
|
items: [{
|
|||
|
|
key: 'zj_base',
|
|||
|
|
items: [{
|
|||
|
|
BASENAME: '珠江流域',
|
|||
|
|
SUM_TTPWR: 123000000,
|
|||
|
|
COUNT_BASEID: 60
|
|||
|
|
}]
|
|||
|
|
}]
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
} else if (qid.value === 'other') {
|
|||
|
|
// other模式:特殊处理
|
|||
|
|
mockResponse = [
|
|||
|
|
{
|
|||
|
|
key: 'river1',
|
|||
|
|
items: [{
|
|||
|
|
HBRVCDNAME: '长江干流',
|
|||
|
|
SUM_TTPWR: 89000000,
|
|||
|
|
COUNT_BLDSTTCCODE: 25
|
|||
|
|
}]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
key: 'river2',
|
|||
|
|
items: [{
|
|||
|
|
HBRVCDNAME: '黄河干流',
|
|||
|
|
SUM_TTPWR: 56000000,
|
|||
|
|
COUNT_BLDSTTCCODE: 18
|
|||
|
|
}]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
key: 'river3',
|
|||
|
|
items: [{
|
|||
|
|
HBRVCDNAME: '珠江干流',
|
|||
|
|
SUM_TTPWR: 34000000,
|
|||
|
|
COUNT_BLDSTTCCODE: 12
|
|||
|
|
}]
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
} else {
|
|||
|
|
// 单个基地模式:按hbrvcd分组
|
|||
|
|
mockResponse = [
|
|||
|
|
{
|
|||
|
|
items: [{
|
|||
|
|
key: 'cjgl',
|
|||
|
|
items: [{
|
|||
|
|
HBRVCDNAME: '长江干流',
|
|||
|
|
SUM_TTPWR: 456800000,
|
|||
|
|
COUNT_BLDSTTCCODE: 50
|
|||
|
|
}]
|
|||
|
|
}]
|
|||
|
|
},
|
|||
|
|
{
|
|||
|
|
items: [{
|
|||
|
|
key: 'hhgl',
|
|||
|
|
items: [{
|
|||
|
|
HBRVCDNAME: '黄河干流',
|
|||
|
|
SUM_TTPWR: 234500000,
|
|||
|
|
COUNT_BLDSTTCCODE: 35
|
|||
|
|
}]
|
|||
|
|
}]
|
|||
|
|
}
|
|||
|
|
]
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// 模拟数据处理逻辑(完全复刻React版本的flatMap逻辑)
|
|||
|
|
let list = mockResponse.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) {
|
|||
|
|
// 模拟 getIntroduce 接口返回
|
|||
|
|
const introRes = [
|
|||
|
|
{ wbsCode: 'river1', orderIndex: 1 },
|
|||
|
|
{ wbsCode: 'river2', orderIndex: 2 },
|
|||
|
|
{ wbsCode: 'river3', orderIndex: 3 }
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
if (introRes?.length && list?.length) {
|
|||
|
|
list = list.map((el: any) => {
|
|||
|
|
const found = introRes.find((item: any) => el?.key === item?.wbsCode)
|
|||
|
|
return {
|
|||
|
|
...el,
|
|||
|
|
index: found?.orderIndex
|
|||
|
|
}
|
|||
|
|
}).sort((a: any, b: any) => a?.index - b?.index)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
resolve(list)
|
|||
|
|
}, 500)
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 获取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<{ records: any[], total: number }> {
|
|||
|
|
// TODO: 替换为实际API调用
|
|||
|
|
// const url = qid.value === 'all'
|
|||
|
|
// ? '/dec-lygk-base-server/base/vmsstbprpt/GetKendoListCust'
|
|||
|
|
// : '/dec-lygk-base-server/base/vmsstbprpt/GetKendoList'
|
|||
|
|
|
|||
|
|
console.log('【模拟】获取表格数据,参数:', params)
|
|||
|
|
|
|||
|
|
return new Promise((resolve) => {
|
|||
|
|
setTimeout(() => {
|
|||
|
|
// 根据建设状态和河段过滤生成对应的模拟数据
|
|||
|
|
const buildStatus = params.bldsttCcode
|
|||
|
|
const hbrvcdFilter = params.hbrvcd?.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: 'hhgl', baseName: '淮河基地', province: '安徽省' },
|
|||
|
|
{ name: '梅山', river: 'hhgl', 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) {
|
|||
|
|
resolve({ 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
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
resolve({
|
|||
|
|
records: mockRecords,
|
|||
|
|
total: mockRecords.length
|
|||
|
|
})
|
|||
|
|
}, 600)
|
|||
|
|
})
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ==================== 工具函数 ====================
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 单位转换函数(待完善)
|
|||
|
|
*/
|
|||
|
|
function transUnit(value: number, from: string, to: string): number {
|
|||
|
|
// TODO: 根据实际业务规则实现
|
|||
|
|
// 当前假设:接口返回的单位是 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)
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 处理弹窗关闭
|
|||
|
|
*/
|
|||
|
|
function handleModalCancel() {
|
|||
|
|
mapModal.value = false
|
|||
|
|
modalData.value = { sttp: '', stcd: '', titleName: '' }
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ==================== ECharts 饼图配置 ====================
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 计算饼图配置(增强版,支持themeecList自定义颜色)
|
|||
|
|
*/
|
|||
|
|
const chartSetting = computed<EChartsOption>(() => {
|
|||
|
|
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})`
|
|||
|
|
|
|||
|
|
// 关键:根据 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() {
|
|||
|
|
if (!chartRef.value) return
|
|||
|
|
|
|||
|
|
// 关键:使用固定的图表尺寸(541px × 412px)
|
|||
|
|
chartInstance.value = echarts.init(chartRef.value, null, {
|
|||
|
|
width: 541,
|
|||
|
|
height: 412
|
|||
|
|
})
|
|||
|
|
updateChart()
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 更新图表
|
|||
|
|
*/
|
|||
|
|
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) {
|
|||
|
|
modalData.value = {
|
|||
|
|
sttp: 'ENG',
|
|||
|
|
stcd: record.stcd,
|
|||
|
|
titleName: record.stnm
|
|||
|
|
}
|
|||
|
|
mapModal.value = true
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
// ==================== 搜索表单配置 ====================
|
|||
|
|
|
|||
|
|
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 () => {
|
|||
|
|
if (!tabKey.value || !tabCol.value) 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
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
const result = await fetchPieData(params)
|
|||
|
|
|
|||
|
|
// 特殊处理 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))
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
pikData.value = result
|
|||
|
|
pieData.value = result
|
|||
|
|
pieCode.value = []
|
|||
|
|
} catch (error) {
|
|||
|
|
console.error('获取饼图数据失败:', error)
|
|||
|
|
message.error('获取饼图数据失败')
|
|||
|
|
} finally {
|
|||
|
|
dataLoading.value = false
|
|||
|
|
}
|
|||
|
|
},
|
|||
|
|
{ deep: true }
|
|||
|
|
)
|
|||
|
|
|
|||
|
|
/**
|
|||
|
|
* 生命周期钩子
|
|||
|
|
*/
|
|||
|
|
onMounted(() => {
|
|||
|
|
// 模拟加载基地列表(更真实的基地数据)
|
|||
|
|
jiDiList.value = [
|
|||
|
|
{ baseid: 'all', basename: '全部基地' },
|
|||
|
|
{ baseid: 'cj', basename: '长江流域' },
|
|||
|
|
{ baseid: 'hh', basename: '黄河流域' },
|
|||
|
|
{ baseid: 'zj', basename: '珠江流域' },
|
|||
|
|
{ baseid: 'hh2', basename: '淮河流域' },
|
|||
|
|
{ baseid: 'hj', basename: '海河流域' },
|
|||
|
|
{ baseid: 'sj', basename: '松花江流域' },
|
|||
|
|
{ baseid: 'lj', basename: '辽河流域' },
|
|||
|
|
{ baseid: 'other', basename: '其他' }
|
|||
|
|
]
|
|||
|
|
|
|||
|
|
// 设置默认选中"全部基地"
|
|||
|
|
if (!qid.value) {
|
|||
|
|
qid.value = 'all'
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
initChart()
|
|||
|
|
window.addEventListener('resize', handleResize)
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
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>
|
|||
|
|
|
|||
|
|
<!-- 详情弹窗 -->
|
|||
|
|
<a-modal
|
|||
|
|
v-if="mapModal && modalData.sttp"
|
|||
|
|
:open="mapModal"
|
|||
|
|
:title="modalData.titleName ? `${modalData.titleName} 详情信息` : '详情信息'"
|
|||
|
|
:width="1200"
|
|||
|
|
:footer="null"
|
|||
|
|
:maskClosable="false"
|
|||
|
|
class="tabsModal"
|
|||
|
|
style="padding-bottom: 0"
|
|||
|
|
@cancel="handleModalCancel"
|
|||
|
|
>
|
|||
|
|
<!-- TODO: 集成ModalTabs组件 -->
|
|||
|
|
<!-- <ModalTabs v-if="modalData" :tabs="handleTabs(modalData)" :params="modalData" /> -->
|
|||
|
|
|
|||
|
|
<!-- 临时替代方案 -->
|
|||
|
|
<div class="modal-content">
|
|||
|
|
<a-descriptions bordered :column="2">
|
|||
|
|
<a-descriptions-item label="电站编码">
|
|||
|
|
{{ modalData.stcd }}
|
|||
|
|
</a-descriptions-item>
|
|||
|
|
<a-descriptions-item label="电站类型">
|
|||
|
|
{{ modalData.sttp }}
|
|||
|
|
</a-descriptions-item>
|
|||
|
|
</a-descriptions>
|
|||
|
|
<p style="margin-top: 16px; color: #999;">
|
|||
|
|
TODO: 请确认 ModalTabs 组件路径后启用上方注释代码
|
|||
|
|
</p>
|
|||
|
|
</div>
|
|||
|
|
</a-modal>
|
|||
|
|
</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>
|