<template>
    <div class="default-main operational-quality" :style="{ height: pageHeight.height }" v-loading="loading">
        <TableHeader ref="headerRef" datePicker :showReset="false" @selectChange="onSelectChange">
            <template #operation>
                <el-button v-if="drillEng" icon="el-icon-Back" plain @click="backToEng">返回</el-button>
            </template>
        </TableHeader>

        <div class="oq-body">
            <aside class="oq-left pr5">
                <!-- 台账统计 -->
                <section class="oq-panel oq-ledger">
                    <div class="oq-panel__hd">
                        <div class="oq-panel__title">
                            <span class="oq-panel__icon">
                                <el-icon><DataBoard /></el-icon>
                            </span>
                            <span>台账统计</span>
                        </div>
                        <el-tooltip
                            v-if="drillEng"
                            :content="`当前工程：${drillEng}`"
                            placement="top"
                            :show-after="300"
                        >
                            <span class="oq-breadcrumb">
                                <el-icon><HomeFilled /></el-icon>
                                <span class="oq-breadcrumb__text">当前工程：{{ drillEng }}</span>
                            </span>
                        </el-tooltip>
                    </div>
                    <div class="oq-ledger__grid">
                        <div
                            v-for="item in ledgerCards"
                            :key="item.label"
                            class="oq-kpi"
                            :style="{ '--c': item.color, '--bg': item.iconBg }"
                        >
                            <div class="oq-kpi__icon">
                                <el-icon :size="item.size"><component :is="item.icon" /></el-icon>
                            </div>
                            <div class="oq-kpi__body">
                                <span class="oq-kpi__label">{{ item.label }}</span>
                                <div class="oq-kpi__val">
                                    <em>{{ item.value }}</em>
                                    <i>{{ item.unit }}</i>
                                </div>
                            </div>
                        </div>
                    </div>
                </section>

                <!-- 设备类型统计 -->
                <section class="oq-panel oq-pie">
                    <div class="oq-panel__hd">
                        <div class="oq-panel__title">
                            <span class="oq-panel__icon">
                                <el-icon><PieChart /></el-icon>
                            </span>
                            <span>设备类型统计</span>
                        </div>
                    </div>
                    <div class="oq-pie__main">
                        <MyEchart :options="typeChart" class="oq-chart" />
                    </div>
                </section>

                <!-- 通讯质量统计 -->
                <section class="oq-panel oq-pie">
                    <div class="oq-panel__hd">
                        <div class="oq-panel__title">
                            <span class="oq-panel__icon">
                                <el-icon><Connection /></el-icon>
                            </span>
                            <span>通讯质量统计</span>
                        </div>
                        <el-radio-group v-model="commTypeFilter" size="small">
                            <el-radio-button value="all">全部</el-radio-button>
                            <el-radio-button value="治理设备">治理设备</el-radio-button>
                            <el-radio-button value="监测设备">监测设备</el-radio-button>
                        </el-radio-group>
                    </div>
                    <div class="oq-pie__main">
                        <MyEchart :options="pieChart" class="oq-chart" />
                    </div>
                </section>
            </aside>

            <div class="oq-right pl5">
                <!-- 右上：在线率 / 完整性 -->
                <section class="oq-panel oq-rate">
                    <div class="oq-panel__hd">
                        <div class="oq-panel__title">
                            <span class="oq-panel__icon">
                                <el-icon><TrendCharts /></el-icon>
                            </span>
                            <span>运行质量统计</span>
                        </div>
                        <el-radio-group v-model="rateFilter" size="small" class="oq-rate-filter">
                            <el-radio-button value="all">全部</el-radio-button>
                            <el-radio-button value="le90">≤90%</el-radio-button>
                            <el-radio-button value="le60">≤60%</el-radio-button>
                        </el-radio-group>
                    </div>

                    <div class="oq-rate__main">
                        <!-- 左侧：上下两个水球 -->
                        <div class="oq-rate__charts">
                            <div class="oq-rate__ball">
                                <MyEchart :options="onlineLiquidChart" class="oq-chart" />
                            </div>
                            <div class="oq-rate__ball">
                                <MyEchart :options="integrityLiquidChart" class="oq-chart" />
                            </div>
                        </div>

                        <!-- 右侧：在线率 + 完整性合并列表 -->
                        <div class="oq-rate__body">
                            <div class="oq-rate__col">
                                <div class="oq-rank">
                                    <div class="oq-rank__hd oq-rank__hd--merge">
                                        <span class="oq-rank__hd-name">{{ levelLabel }}名称</span>
                                        <div class="oq-rank__hd-block oq-rank__hd-block--online">
                                            <span class="oq-rank__hd-group">设备在线率(%)</span>
                                            <span>数量</span>
                                            <span>低于90%</span>
                                            <span class="oq-sort" @click="toggleRateSort('onlineRate')">
                                                在线率
                                                <el-icon class="oq-sort__icon" :size="14"><DCaret /></el-icon>
                                            </span>
                                        </div>
                                        <div class="oq-rank__hd-block oq-rank__hd-block--integrity">
                                            <span class="oq-rank__hd-group">监测点完整性(%)</span>
                                            <span>数量</span>
                                            <span>低于90%</span>
                                            <span class="oq-sort" @click="toggleRateSort('integrityRate')">
                                                完整性
                                                <el-icon class="oq-sort__icon" :size="14"><DCaret /></el-icon>
                                            </span>
                                        </div>
                                    </div>
                                    <div
                                        v-for="item in displayMergedRateList"
                                        :key="item.name"
                                        class="oq-rank__item oq-rank__item--merge"
                                        :class="{ clickable: !drillEng }"
                                        @click="onRankClick(item)"
                                    >
                                        <span class="oq-rank__title">
                                            <el-tooltip :content="item.name" placement="top" :show-after="300">
                                                <span class="oq-rank__name">{{ item.name }}</span>
                                            </el-tooltip>
                                        </span>
                                        <span class="oq-rank__link">{{ item.onlineCount }}</span>
                                        <span class="oq-rank__link">{{ item.onlineLow }}</span>
                                        <span class="oq-rank__pct" :class="rateLevel(item.onlineRate)">
                                            {{ item.onlineRate }}
                                        </span>
                                        <span class="oq-rank__link">{{ item.integrityCount }}</span>
                                        <span class="oq-rank__link">{{ item.integrityLow }}</span>
                                        <span class="oq-rank__pct" :class="rateLevel(item.integrityRate)">
                                            {{ item.integrityRate }}
                                        </span>
                                    </div>
                                    <el-empty
                                        v-if="!displayMergedRateList.length"
                                        description="暂无数据"
                                        :image-size="80"
                                    />
                                </div>
                            </div>
                        </div>
                    </div>
                </section>

                <!-- 右下：数据质量统计 -->
                <section class="oq-panel oq-quality">
                    <div class="oq-panel__hd">
                        <div class="oq-panel__title">
                            <span class="oq-panel__icon">
                                <el-icon><Warning /></el-icon>
                            </span>
                            <span>数据质量统计</span>
                        </div>
                    </div>
                    <div class="oq-quality__main">
                        <div
                            v-for="panel in qualityPanels"
                            :key="panel.key"
                            class="oq-quality__list-wrap"
                            :style="{ '--c': panel.color }"
                        >
                            <div class="oq-quality__list-hd">
                                <span>{{ panel.title }}</span>
                            </div>
                            <div class="oq-quality__list-cols">
                                <span>指标名称</span>
                                <span>{{ panel.rangeLabel }}</span>
                                <span>{{ panel.countLabel }}</span>
                            </div>
                            <div class="oq-quality__list">
                                <div v-for="item in panel.list" :key="item.name" class="oq-quality__list-item">
                                    <span class="oq-quality__list-name" :title="item.name">{{ item.name }}</span>
                                    <span class="oq-quality__list-range">{{ item.range }}</span>
                                    <span class="oq-quality__list-count">{{ item.count }}</span>
                                </div>
                                <el-empty v-if="!panel.list.length" description="暂无数据" :image-size="80" />
                            </div>
                        </div>
                    </div>
                </section>
            </div>
        </div>
    </div>
</template>

<script setup lang="ts">
import { ref, computed, provide, onMounted, watch } from 'vue'
import {
    HomeFilled,
    List,
    Platform,
    LocationFilled,
    DataBoard,
    TrendCharts,
    PieChart,
    Warning,
    Connection,
    Cpu,
    CircleCloseFilled,
    SuccessFilled,
    DCaret
} from '@element-plus/icons-vue'
import TableHeader from '@/components/table/header/index.vue'
import MyEchart from '@/components/echarts/MyEchart.vue'
import TableStore from '@/utils/tableStore'
import { mainHeight } from '@/utils/layout'
/** 来自设备树：level0工程/1项目/2设备/3监测点；MQTT=治理设备，CLD=监测设备 */
import treeMock from './treeMock.json'

defineOptions({
    name: 'operationalQuality'
})

type RateFilter = 'all' | 'le90' | 'le60'
type CommTypeFilter = 'all' | '治理设备' | '监测设备'
type RankItem = { name: string; value: number; count: number; lowCount: number }
type RateSortKey = 'onlineRate' | 'integrityRate'

const pageHeight = mainHeight(20)
const headerRef = ref()
const loading = ref(false)
const rateFilter = ref<RateFilter>('all')
const commTypeFilter = ref<CommTypeFilter>('all')
/** 合并列表排序：默认按在线率升序 */
const rateSortKey = ref<RateSortKey>('onlineRate')
const rateSortAsc = ref(true)
/** 下钻到项目层级时的工程名 */
const drillEng = ref('')
const pieChart = ref<any>({})
const typeChart = ref<any>({})
const onlineLiquidChart = ref<any>({})
const integrityLiquidChart = ref<any>({})

const tableStore = new TableStore({
    url: '',
    method: 'POST',
    showPage: false,
    column: [],
    beforeSearchFun: () => {
        loadData()
        return false
    },
    loadCallback: () => {}
})
provide('tableStore', tableStore)

const deviceOnlineTable = ref(treeMock.deviceOnlineTable)
const pointIntegrityTable = ref(treeMock.pointIntegrityTable)

const levelLabel = computed(() => (drillEng.value ? '项目' : '工程'))

const scopedDevices = computed(() => {
    if (!drillEng.value) return deviceOnlineTable.value
    return deviceOnlineTable.value.filter(d => (d.engName || '未分组') === drillEng.value)
})

const scopedPoints = computed(() => {
    if (!drillEng.value) return pointIntegrityTable.value
    return pointIntegrityTable.value.filter(p => (p.engName || '未分组') === drillEng.value)
})

const ledgerCards = computed(() => {
    const devices = scopedDevices.value
    const points = scopedPoints.value
    const engSet = new Set(devices.map(d => d.engName || '未分组'))
    const projSet = new Set(devices.map(d => `${d.engName}||${d.projName}`))
    const global = Object.fromEntries(
        (treeMock.ledgerMetrics as { label: string; value: number }[]).map(i => [i.label, i.value])
    )
    const inDrill = !!drillEng.value
    return [
        {
            label: '工程',
            value: inDrill ? 1 : global['工程'] ?? engSet.size,
            unit: '个',
            size: 18,
            icon: HomeFilled,
            color: '#376CF3',
            iconBg: 'rgba(55, 108, 243, 0.14)'
        },
        {
            label: '项目',
            value: inDrill ? projSet.size : global['项目'] ?? projSet.size,
            unit: '个',
            size: 18,
            icon: List,
            color: '#0E42D2',
            iconBg: 'rgba(14, 66, 210, 0.12)'
        },
        {
            label: '设备',
            value: inDrill ? devices.length : global['设备'] ?? devices.length,
            unit: '台',
            size: 18,
            icon: Platform,
            color: '#0EA5E9',
            iconBg: 'rgba(14, 165, 233, 0.14)'
        },
        {
            label: '监测点',
            value: inDrill ? points.length : global['监测点'] ?? points.length,
            unit: '个',
            size: 20,
            icon: LocationFilled,
            color: '#14B8A6',
            iconBg: 'rgba(20, 184, 166, 0.14)'
        }
    ]
})

const qualityIndicators = [
    '频率',
    '谐波电压相角',
    '相电压有效值',
    '电流有效值',
    '正序电压',
    '负序电压',
    '零序电压',
    '线电压有效值',
    '功率因数'
]

/** mock：异常 / 告警按指标汇总（后续接接口） */
const qualityData = ref({
    outOfRange: [
        { name: '频率', range: '42.5Hz ~ 57.5Hz', count: 12 },
        { name: '谐波电压相角', range: '-360.0 ~ 360.0', count: 8 },
        { name: '相电压有效值', range: '10.0%U ~ 150.0%U', count: 28 },
        { name: '电流有效值', range: '1.0 ~ 1250.0', count: 15 },
        { name: '正序电压', range: '0.0%U ~ 150.0%U', count: 18 },
        { name: '负序电压', range: '0.0%U ~ 150.0%U', count: 14 },
        { name: '零序电压', range: '0.0%U ~ 150.0%U', count: 9 },
        { name: '线电压有效值', range: '10.0%U ~ 150.0%U', count: 19 },
        { name: '功率因数', range: '-1.0 ~ 1.0', count: 11 }
    ],
    overLimit: qualityIndicators.map((name, idx) => ({
        name,
        range: '',
        count: [6, 4, 18, 9, 10, 7, 5, 14, 8][idx]
    }))
})

const aggregateBy = (list: any[], keyFn: (row: any) => string, valueFn: (row: any) => number): RankItem[] => {
    const map = new Map<string, { sum: number; count: number; lowCount: number }>()
    list.forEach(row => {
        const key = keyFn(row)
        const val = valueFn(row)
        const cur = map.get(key) || { sum: 0, count: 0, lowCount: 0 }
        cur.sum += val
        cur.count += 1
        if (val < 90) cur.lowCount += 1
        map.set(key, cur)
    })
    return [...map.entries()]
        .map(([name, v]) => ({
            name,
            value: Number((v.sum / v.count).toFixed(2)),
            count: v.count,
            lowCount: v.lowCount
        }))
        .sort((a, b) => a.value - b.value)
}

const onlineList = computed(() =>
    aggregateBy(
        scopedDevices.value,
        d => (drillEng.value ? d.projName || '未分组' : d.engName || '未分组'),
        d => d.onlineRate
    )
)

const integrityList = computed(() =>
    aggregateBy(
        scopedPoints.value,
        p => (drillEng.value ? p.projName || '未分组' : p.engName || '未分组'),
        p => p.integrity
    )
)

const matchRateFilter = (v: number) => {
    if (rateFilter.value === 'le90') return v <= 90
    if (rateFilter.value === 'le60') return v <= 60
    return true
}

/** 按名称合并在线率 / 完整性；筛选时任一指标达标即展示，另一项仍显示真实值 */
const displayMergedRateList = computed(() => {
    const onlineMap = new Map(onlineList.value.map(i => [i.name, i]))
    const integrityMap = new Map(integrityList.value.map(i => [i.name, i]))
    const names = new Set([...onlineMap.keys(), ...integrityMap.keys()])
    const list = [...names]
        .map(name => {
            const online = onlineMap.get(name)
            const integrity = integrityMap.get(name)
            const onlineRate = online?.value ?? 0
            const integrityRate = integrity?.value ?? 0
            return {
                name,
                onlineCount: online?.count ?? 0,
                onlineLow: online?.lowCount ?? 0,
                onlineRate,
                integrityCount: integrity?.count ?? 0,
                integrityLow: integrity?.lowCount ?? 0,
                integrityRate
            }
        })
        .filter(item => matchRateFilter(item.onlineRate) || matchRateFilter(item.integrityRate))

    const key = rateSortKey.value
    const asc = rateSortAsc.value ? 1 : -1
    return list.sort((a, b) => {
        const av = a[key]
        const bv = b[key]
        if (typeof av === 'string' || typeof bv === 'string') {
            return String(av).localeCompare(String(bv), 'zh-CN') * asc
        }
        return ((Number(av) || 0) - (Number(bv) || 0)) * asc
    })
})

const toggleRateSort = (key: RateSortKey) => {
    if (rateSortKey.value === key) {
        rateSortAsc.value = !rateSortAsc.value
    } else {
        rateSortKey.value = key
        rateSortAsc.value = true
    }
}

const rateSortClass = (key: RateSortKey) => ({
    'is-active': rateSortKey.value === key,
    'is-asc': rateSortKey.value === key && rateSortAsc.value,
    'is-desc': rateSortKey.value === key && !rateSortAsc.value
})

const avgRate = (list: any[], key: string) => {
    if (!list.length) return 0
    const sum = list.reduce((s, i) => s + (Number(i[key]) || 0), 0)
    return Number((sum / list.length).toFixed(2))
}

const totalOnlineRate = computed(() => avgRate(scopedDevices.value, 'onlineRate'))
const totalIntegrityRate = computed(() => avgRate(scopedPoints.value, 'integrity'))

const commStatusTable = computed(() => {
    const list =
        commTypeFilter.value === 'all'
            ? scopedDevices.value
            : scopedDevices.value.filter(d => d.type === commTypeFilter.value)
    const normal = list.filter(i => i.normal).length
    const abnormal = list.length - normal
    const total = list.length || 1
    return [
        {
            name: '正常',
            count: normal,
            ratio: Number(((normal / total) * 100).toFixed(2)),
            color: '#67c23a',
            icon: SuccessFilled
        },
        {
            name: '异常',
            count: abnormal,
            ratio: Number(((abnormal / total) * 100).toFixed(2)),
            color: '#e26257',
            icon: CircleCloseFilled
        }
    ]
})

const typeLegend = computed(() => {
    const list = scopedDevices.value
    const total = list.length || 1
    const mqtt = list.filter(d => d.type === '治理设备').length
    const cld = list.length - mqtt
    return [
        {
            name: '治理设备',
            count: mqtt,
            ratio: Number(((mqtt / total) * 100).toFixed(2)),
            color: '#14b8a6',
            icon: Cpu
        },
        {
            name: '监测设备',
            count: cld,
            ratio: Number(((cld / total) * 100).toFixed(2)),
            color: '#3b82f6',
            icon: Connection
        }
    ]
})

const qualityBadge = computed(() => ({
    outOfRange: qualityData.value.outOfRange.reduce((s, i) => s + i.count, 0),
    overLimit: qualityData.value.overLimit.reduce((s, i) => s + i.count, 0)
}))

const qualityPanels = computed(() => [
    {
        key: 'outOfRange',
        title: '异常数据',
        rangeLabel: '合理范围',
        countLabel: '异常测点数',
        color: '#FF9500',
        total: qualityBadge.value.outOfRange,
        list: qualityData.value.outOfRange
    },
    {
        key: 'overLimit',
        title: '告警数据',
        rangeLabel: '限值',
        countLabel: '告警测点数',
        color: '#e26257',
        total: qualityBadge.value.overLimit,
        list: qualityData.value.overLimit
    }
])

const rateLevel = (v: number) => {
    if (v >= 90) return 'is-ok'
    if (v >= 60) return 'is-warn'
    return 'is-danger'
}

const rateColorHex = (v: number) => {
    if (v >= 90) return '#67c23a'
    if (v >= 60) return '#e6a23c'
    return '#e26257'
}

const onRankClick = (item: RankItem) => {
    if (drillEng.value) return
    drillEng.value = item.name
}

const backToEng = () => {
    drillEng.value = ''
}

const buildLiquidChart = (value: number, title: string, shortName: string) => {
    const color = rateColorHex(value)
    const ratio = Math.max(0, Math.min(100, Number(value) || 0)) / 100
    const pct = Number(value).toFixed(2)
    return {
        title: { show: false },
        legend: { show: false },
        xAxis: { show: false },
        yAxis: { show: false },
        dataZoom: { show: false },
        toolbox: { show: false },
        tooltip: { show: false },
        exportFileName: title,
        series: [
            {
                type: 'liquidFill',
                name: title,
                data: [ratio],
                radius: '78%',
                center: ['50%', '50%'],
                color: [color],
                backgroundStyle: {
                    color:
                        color === '#67c23a'
                            ? 'rgba(103,194,58,0.08)'
                            : color === '#e6a23c'
                            ? 'rgba(230,162,60,0.08)'
                            : 'rgba(245,108,108,0.08)'
                },
                outline: {
                    show: true,
                    borderDistance: 4,
                    itemStyle: {
                        borderColor: color,
                        borderWidth: 2,
                        shadowBlur: 0
                    }
                },
                itemStyle: {
                    opacity: 0.85,
                    shadowBlur: 0
                },
                label: {
                    formatter: () => `{pct|${pct}%}\n{name|${shortName}}`,
                    rich: {
                        pct: {
                            fontSize: 15,
                            fontWeight: 700,
                            lineHeight: 22
                        },
                        name: {
                            fontSize: 12,
                            fontWeight: 500,
                            lineHeight: 18
                        }
                    }
                }
            }
        ]
    }
}

const buildLiquidCharts = () => {
    onlineLiquidChart.value = buildLiquidChart(totalOnlineRate.value, '设备在线率', '在线率')
    integrityLiquidChart.value = buildLiquidChart(totalIntegrityRate.value, '监测点数据完整性', '完整性')
}

const buildPieLegend = (formatter?: (name: string) => string) => ({
    show: true,
    type: 'scroll',
    orient: 'vertical',
    right: 4,
    top: 'middle',
    height: '78%',
    itemWidth: 10,
    itemHeight: 10,
    itemGap: 8,
    pageIconSize: 12,
    pageButtonItemGap: 6,
    pageButtonGap: 8,
    pageButtonPosition: 'end',
    pageIconColor: '#376CF3',
    pageIconInactiveColor: '#c0c4cc',
    pageTextStyle: { color: '#86909c', fontSize: 11 },
    formatter: formatter || ((name: string) => name),
    textStyle: {
        fontSize: 12,
        width: 140,
        overflow: 'truncate',
        ellipsis: '...'
    },
    tooltip: { show: true }
})

const buildDonutChart = (
    legend: { name: string; count: number; color: string; ratio?: number }[],
    exportFileName: string,
    valueUnit = '台'
) => {
    const countMap = Object.fromEntries(legend.map(i => [i.name, i.count]))
    return {
        title: { show: false },
        tooltip: { trigger: 'item', formatter: `{b}<br/>{c} ${valueUnit} ({d}%)` },
        legend: buildPieLegend(name => `${name}  ${countMap[name] ?? 0}${valueUnit}`),
        xAxis: { show: false },
        yAxis: { show: false },
        dataZoom: { show: false },
        toolbox: { show: false },
        exportFileName,
        options: {
            series: [
                {
                    type: 'pie',
                    radius: ['51%', '79%'],
                    center: ['34%', '50%'],
                    itemStyle: { borderRadius: 3, borderColor: '#fff', borderWidth: 1 },
                    label: { show: false },
                    labelLine: { show: false },
                    data: legend.map(i => ({
                        name: i.name,
                        value: i.count,
                        itemStyle: { color: i.color }
                    }))
                }
            ]
        }
    }
}

const buildCommPieChart = () => {
    pieChart.value = buildDonutChart(commStatusTable.value, '通讯状态')
}

const buildTypePieChart = () => {
    typeChart.value = buildDonutChart(typeLegend.value, '设备类型')
}

const buildPieChart = () => {
    buildCommPieChart()
    buildTypePieChart()
}

const loadData = () => {
    loading.value = true
    setTimeout(() => {
        buildPieChart()
        buildLiquidCharts()
        loading.value = false
    }, 120)
}

const onSelectChange = (_showSelect: any, _height: any, datePickerValue?: any) => {
    if (datePickerValue?.timeValue) {
        tableStore.table.params.searchBeginTime = datePickerValue.timeValue[0]
        tableStore.table.params.searchEndTime = datePickerValue.timeValue[1]
    }
    loadData()
}

watch([drillEng, scopedDevices, scopedPoints], () => {
    buildPieChart()
    buildLiquidCharts()
})

/** 通讯类型切换只刷新通讯质量饼图，不影响其它模块 */
watch(commTypeFilter, () => {
    buildCommPieChart()
})

onMounted(() => loadData())
</script>

<style lang="scss" scoped>
.operational-quality {
    display: flex;
    flex-direction: column;
    overflow: hidden;
}

.oq-body {
    flex: 1;
    min-height: 0;
    overflow: hidden;
    padding: 10px;
    display: grid;
    grid-template-columns: minmax(260px, 1fr) minmax(0, 2.6fr);
    //gap: 4px;
}

.oq-left,
.oq-right {
    min-width: 0;
    min-height: 0;
    display: flex;
    flex-direction: column;
    gap: 10px;
    overflow: hidden;
    /* 给卡片左右阴影留出空间，避免被 overflow 裁切 */
    // padding: 4px 8px;
}

.oq-left > .oq-panel {
    flex: 1;
    min-height: 0;
}

.oq-left > .oq-ledger {
    flex: 0 0 200px;
    height: 200px;
    min-height: 200px;
    max-height: 200px;
}

.oq-right > .oq-rate {
    flex: 1.1;
    min-height: 0;
}

.oq-right > .oq-quality {
    flex: 1;
    min-height: 0;
}

.oq-panel {
    min-width: 0;
    min-height: 0;
    background: #fff;
    border-radius: 5px;
    border: 1px solid #eef2f7;
    padding: 10px;
    display: flex;
    flex-direction: column;
    box-shadow: 0 2px 8px rgba(15, 35, 80, 0.04);

    &__hd {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 8px;
        font-size: 15px;
        font-weight: 600;
        color: #1d2129;
        margin-bottom: 10px;
        flex-shrink: 0;
        min-width: 0;

        > :deep(.el-tooltip__trigger) {
            max-width: 55%;
            min-width: 0;
            overflow: hidden;
        }
    }

    &__title {
        display: inline-flex;
        align-items: center;
        gap: 8px;
        min-width: 0;
    }

    &__icon {
        width: 28px;
        height: 28px;
        border-radius: 8px;
        background: rgba(55, 108, 243, 0.1);
        color: var(--el-color-primary);
        display: inline-flex;
        align-items: center;
        justify-content: center;
        flex-shrink: 0;

        .el-icon {
            font-size: 16px;
        }
    }
}

.oq-breadcrumb {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    max-width: 100%;
    min-width: 0;
    font-size: 12px;
    font-weight: 400;
    color: #376cf3;
    background: rgba(55, 108, 243, 0.08);
    padding: 2px 8px;
    border-radius: 999px;
    overflow: hidden;
    vertical-align: middle;

    .el-icon {
        font-size: 13px;
        flex-shrink: 0;
    }

    &__text {
        min-width: 0;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }
}

.oq-ledger {
    &__grid {
        flex: 1;
        display: grid;
        grid-template-columns: 1fr 1fr;
        gap: 8px;
        align-content: stretch;
    }
}

.oq-kpi {
    position: relative;
    height: 100%;
    min-height: 62px;
    max-height: 70px;
    box-sizing: border-box;
    background: #fff;
    border: 1px solid #eef2f7;
    border-radius: 8px;
    padding: 8px 10px;
    display: flex;
    align-items: center;
    gap: 10px;
    overflow: hidden;
    box-shadow: 0 2px 8px rgba(15, 35, 80, 0.04);
    transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease;

    &:hover {
        transform: translateY(-1px);
        border-color: #e5eaf3;
        box-shadow: 0 4px 12px rgba(15, 35, 80, 0.08);
    }

    &__icon {
        width: 40px;
        height: 40px;
        border-radius: 50%;
        background: var(--bg);
        color: var(--c);
        display: flex;
        align-items: center;
        justify-content: center;
        flex-shrink: 0;
    }

    &__body {
        min-width: 0;
        display: flex;
        flex-direction: column;
        gap: 4px;
    }

    &__label {
        font-size: 12px;
        color: #6e7681;
        line-height: 1.2;
    }

    &__val {
        display: flex;
        align-items: baseline;
        gap: 3px;

        em {
            font-style: normal;
            font-size: 22px;
            font-weight: 700;
            color: #1d2129;
            line-height: 1;
            font-variant-numeric: tabular-nums;
        }

        i {
            font-style: normal;
            font-size: 12px;
            color: #6e7681;
        }
    }
}

.oq-rate {
    overflow: hidden;

    &__main {
        flex: 1;
        min-height: 0;
        display: grid;
        grid-template-columns: 200px minmax(0, 1fr);
        gap: 10px;
        overflow: hidden;
    }

    &__charts {
        min-width: 0;
        min-height: 0;
        display: grid;
        grid-template-rows: 1fr 1fr;
        gap: 4px;
        overflow: hidden;
    }

    &__ball {
        min-width: 0;
        min-height: 0;
        display: flex;
        overflow: hidden;

        .oq-chart {
            flex: 1;
            width: 100%;
            min-height: 0;
        }
    }

    &__body {
        flex: 1;
        min-height: 0;
        display: flex;
        flex-direction: column;
        overflow: hidden;
    }

    &__col {
        position: relative;
        min-width: 0;
        min-height: 0;
        height: 100%;
        display: flex;
        flex-direction: column;
        border: 1px solid #eef2f7;
        border-radius: 5px;
        padding: 10px;
        background: #fafbfd;
        overflow: hidden;
        --oq-merge-cols: minmax(0, 1.2fr) 70px 75px 75px 70px 75px 75px;
        --oq-merge-group: 70px 75px 75px;
        --oq-merge-group-w: 236px; /* 70 + 8 + 75 + 8 + 75 */
    }

    &__sub {
        font-size: 13px;
        font-weight: 600;
        color: #1d2129;
        margin-bottom: 8px;
        flex-shrink: 0;
        display: flex;
        align-items: center;
        gap: 6px;

        .el-icon {
            color: var(--el-color-primary);
            font-size: 16px;
        }

        em {
            font-style: normal;
            font-size: 12px;
            font-weight: 400;
            color: #6e7681;
            background: #eef2f7;
            padding: 1px 8px;
            border-radius: 999px;
            margin-left: 2px;
        }
    }
}

.oq-rate-filter {
    flex-shrink: 0;
}

.oq-rank {
    flex: 1 1 0;
    min-height: 0;
    height: 0;
    overflow-x: hidden;
    overflow-y: auto;
    display: flex;
    flex-direction: column;
    gap: 6px;
    padding-right: 4px;

    &::-webkit-scrollbar {
        width: 6px;
    }

    &::-webkit-scrollbar-thumb {
        background: var(--el-color-primary);
        border-radius: 3px;
    }

    &::-webkit-scrollbar-track {
        background: transparent;
    }

    &:hover::-webkit-scrollbar-thumb {
        background: var(--el-color-primary);
    }

    &__hd {
        display: grid;
        grid-template-columns: minmax(0, 1fr) 70px 100px 70px;
        gap: 8px;
        align-items: center;
        box-sizing: border-box;
        padding: 0 12px 6px 16px;
        margin: 0;
        border: 1px solid transparent;
        flex-shrink: 0;
        position: sticky;
        top: 0;
        z-index: 2;
        background: #fafbfd;
        font-size: 12px;
        font-weight: 600;
        color: #4e5969;
        line-height: 1.2;

        > span {
            text-align: center;
            display: flex;
            align-items: center;
            justify-content: center;
            min-width: 0;
        }

        .oq-rank__hd-name {
            justify-content: flex-start;
            text-align: left;
        }

        &--merge {
            display: grid;
            grid-template-columns: minmax(0, 1.2fr) var(--oq-merge-group-w) var(--oq-merge-group-w);
            gap: 8px;
            align-items: stretch;

            .oq-rank__hd-name {
                align-self: center;
            }
        }
    }

    &__hd-block {
        display: grid;
        grid-template-columns: var(--oq-merge-group);
        grid-template-rows: auto auto;
        gap: 4px 8px;
        align-items: center;
        box-sizing: border-box;
        padding: 4px 0;
        border-radius: 6px;
        min-width: 0;

        > span {
            display: flex;
            align-items: center;
            justify-content: center;
            min-width: 0;
            text-align: center;
        }

        .oq-rank__hd-group {
            grid-column: 1 / -1;
            grid-row: 1;
            background: transparent;
            padding: 2px 0 4px;
            border-bottom: 1px solid rgba(78, 89, 105, 0.12);
        }

        &--online {
            background: rgba(149, 151, 153, 0.1);
            padding: 4px 0;
        }

        &--integrity {
            background: rgba(149, 151, 153, 0.1);
            padding: 4px 0;
        }
    }

    &__hd-group {
        font-size: 12px;
        font-weight: 700;
        color: #4e5969;
        justify-content: center;
    }

    .oq-sort {
        display: inline-flex;
        flex-direction: row;
        align-items: center;
        justify-content: center;
        gap: 4px;
        cursor: pointer;
        user-select: none;
        line-height: 1;
        vertical-align: middle;
        color: #4e5969;

        &:hover,
        &.is-active {
            color: var(--el-color-primary);
        }

        &__icon {
            display: inline-flex;
            align-items: center;
            justify-content: center;
            font-size: 16px;
            width: 16px;
            height: 16px;
            cursor: pointer;
            color: #c0c4cc;
            transition: color 0.15s ease;
            flex-shrink: 0;
        }

        &:hover .oq-sort__icon,
        &.is-active .oq-sort__icon {
            color: var(--el-color-primary);
            cursor: pointer;
        }
    }

    &__item {
        position: relative;
        display: grid;
        grid-template-columns: minmax(0, 1fr) 70px 100px 70px;
        gap: 8px;
        align-items: center;
        box-sizing: border-box;
        min-height: 36px;
        padding: 6px 12px 6px 16px;
        border-radius: 6px;
        background: #fff;
        border: 1px solid #eef2f7;
        overflow: hidden;
        transition: background 0.15s ease, border-color 0.15s ease;

        &--merge {
            grid-template-columns: var(--oq-merge-cols);
        }

        &::before {
            content: '';
            position: absolute;
            left: 0;
            top: 6px;
            bottom: 6px;
            width: 4px;
            border-radius: 2px;
            background: var(--el-color-primary);
        }

        &.is-ok::before {
            background: #67c23a;
        }

        &.is-warn::before {
            background: #e6a23c;
        }

        &.is-danger::before {
            background: #e26257;
        }

        &.clickable {
            cursor: pointer;

            &:hover {
                background: #f0f5ff;
                border-color: #d6e4ff;

                .oq-rank__chevron {
                    opacity: 1;
                    transform: translateX(0);
                }
            }
        }
    }

    &__title {
        min-width: 0;
        display: flex;
        align-items: center;
        gap: 2px;
        overflow: hidden;

        :deep(.el-tooltip__trigger) {
            min-width: 0;
            flex: 1;
            overflow: hidden;
        }
    }

    &__name {
        display: block;
        width: 100%;
        min-width: 0;
        font-size: 13px;
        color: #1d2129;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
        line-height: 1.3;
    }

    &__chevron {
        font-size: 12px;
        color: var(--el-color-primary);
        opacity: 0.35;
        transform: translateX(-2px);
        transition: all 0.15s ease;
        flex-shrink: 0;
    }

    &__link {
        text-align: center;
        font-size: 13px;
        font-weight: 600;
        color: var(--el-color-primary);
        font-variant-numeric: tabular-nums;
        line-height: 1.2;
    }

    &__pct {
        text-align: center;
        font-size: 13px;
        font-weight: 700;
        font-variant-numeric: tabular-nums;
        line-height: 1.2;

        &.is-ok {
            color: #67c23a;
        }

        &.is-warn {
            color: #e6a23c;
        }

        &.is-danger {
            color: #e26257;
        }
    }
}

.oq-pie {
    overflow: hidden;

    &__main {
        flex: 1;
        min-height: 0;
        display: flex;
        align-items: stretch;
    }

    .oq-chart {
        width: 100%;
        height: 100%;
        min-height: 0;
    }
}

.oq-chart {
    width: 100%;
    height: 100%;
    min-height: 100px;
}

.oq-quality {
    overflow: hidden;

    &__main {
        flex: 1;
        min-height: 0;
        display: grid;
        grid-template-columns: 1fr 1fr;
        gap: 10px;
        overflow: hidden;
    }

    &__list-wrap {
        min-width: 0;
        min-height: 0;
        display: flex;
        flex-direction: column;
        border: 1px solid #eef2f7;
        border-radius: 5px;
        padding: 10px;
        background: #fafbfd;
        overflow: hidden;
    }

    &__list-hd {
        display: flex;
        align-items: center;
        gap: 6px;
        margin-bottom: 8px;
        flex-shrink: 0;
        font-size: 13px;
        font-weight: 600;
        color: #1d2129;

        em {
            font-style: normal;
            font-size: 12px;
            font-weight: 400;
            color: #6e7681;
            background: #eef2f7;
            padding: 1px 8px;
            border-radius: 999px;
            margin-left: 2px;
        }
    }

    &__list-cols {
        display: grid;
        grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr) 90px;
        gap: 8px;
        align-items: center;
        box-sizing: border-box;
        padding: 0 12px 3px 16px;
        margin: 0 4px 0 0;
        border: 1px solid transparent;
        flex-shrink: 0;
        font-size: 12px;
        font-weight: 600;
        color: #4e5969;

        span:nth-child(2),
        span:last-child {
            text-align: center;
        }
    }

    &__list {
        flex: 1;
        min-height: 0;
        overflow-x: hidden;
        overflow-y: auto;
        display: flex;
        flex-direction: column;
        gap: 6px;
        padding-right: 4px;
        scrollbar-gutter: stable;

        &::-webkit-scrollbar {
            width: 6px;
        }

        &::-webkit-scrollbar-thumb {
            background: var(--el-color-primary);
            border-radius: 3px;
        }
    }

    &__list-item {
        position: relative;
        display: grid;
        grid-template-columns: minmax(0, 1.2fr) minmax(0, 1fr) 90px;
        gap: 8px;
        align-items: center;
        box-sizing: border-box;
        min-height: 36px;
        padding: 6px 12px 6px 16px;
        border-radius: 6px;
        background: #fff;
        border: 1px solid #eef2f7;

        &::before {
            content: '';
            position: absolute;
            left: 0;
            top: 6px;
            bottom: 6px;
            width: 4px;
            border-radius: 2px;
            background: var(--c);
        }
    }

    &__list-name {
        min-width: 0;
        font-size: 13px;
        color: #1d2129;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    &__list-range {
        font-size: 12px;
        font-weight: 700;
        color: var(--el-color-primary);
        text-align: center;
        overflow: hidden;
        text-overflow: ellipsis;
        white-space: nowrap;
    }

    &__list-count {
        text-align: center;
        font-size: 14px;
        font-weight: 700;
        color: var(--c);
        font-variant-numeric: tabular-nums;
    }
}

@media (max-width: 1200px) {
    .oq-body {
        grid-template-columns: 1fr;
        overflow: auto;
    }

    .oq-left,
    .oq-right {
        overflow: visible;
    }
}
</style>
