feat(cost): 新增成本趋势图表管理功能与汇总数据保存
1. 为comprehensive.vue新增保存汇总数据到报表配置的逻辑 2. 重构trend.vue的筛选交互为图表管理面板,支持显示隐藏、排序系列,配置持久化到localStorage 3. 替换原有的明细数据拉取逻辑为读取报表配置获取汇总数据
This commit is contained in:
@@ -938,6 +938,41 @@ export default {
|
|||||||
await batchSaveProdMetricResult({ resultIds, prodMetricResultList: metricResultList })
|
await batchSaveProdMetricResult({ resultIds, prodMetricResultList: metricResultList })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 保存汇总数据到报表config
|
||||||
|
const summaryRows = this.summaryRows
|
||||||
|
const sumRow = summaryRows.find(r => r.$summaryType === 'sum')
|
||||||
|
const avgRow = summaryRows.find(r => r.$summaryType === 'avg')
|
||||||
|
const buildSummaryMap = (row) => {
|
||||||
|
const map = {}
|
||||||
|
if (!row) return map
|
||||||
|
this.allCols.forEach(col => {
|
||||||
|
const hdr = col.$type === 'detail'
|
||||||
|
? (col.itemName || col.itemCode) + (col.unit ? '(' + col.unit + ')' : '')
|
||||||
|
: col.metricName + (col.unit ? '(' + col.unit + ')' : '')
|
||||||
|
if (col.isShift) {
|
||||||
|
if (col.$type === 'detail') {
|
||||||
|
const v1 = row['q' + col.itemId + '_1']
|
||||||
|
const v2 = row['q' + col.itemId + '_2']
|
||||||
|
if (v1 != null) map[hdr + '(甲)'] = v1
|
||||||
|
if (v2 != null) map[hdr + '(乙)'] = v2
|
||||||
|
} else {
|
||||||
|
const v1 = row['mv' + col.mIdx + '_1']
|
||||||
|
const v2 = row['mv' + col.mIdx + '_2']
|
||||||
|
if (v1 != null) map[hdr + '(甲)'] = v1
|
||||||
|
if (v2 != null) map[hdr + '(乙)'] = v2
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const v = col.$type === 'detail' ? row['q' + col.itemId] : row['mv' + col.mIdx]
|
||||||
|
if (v != null) map[hdr] = v
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return map
|
||||||
|
}
|
||||||
|
const cfg = JSON.parse(this.activeReport.colConfig || '{}')
|
||||||
|
cfg.avg = buildSummaryMap(avgRow)
|
||||||
|
cfg.sum = buildSummaryMap(sumRow)
|
||||||
|
await updateProdReport({ reportId: rid, colConfig: JSON.stringify(cfg) })
|
||||||
|
|
||||||
this.$modal.msgSuccess("保存成功"); await this.loadGrid()
|
this.$modal.msgSuccess("保存成功"); await this.loadGrid()
|
||||||
} catch(e) { this.$modal.msgError("保存失败") } finally { this.saving = false }
|
} catch(e) { this.$modal.msgError("保存失败") } finally { this.saving = false }
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -38,48 +38,54 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 图表筛选 -->
|
<!-- 图表管理 -->
|
||||||
<div v-if="reports.length > 0" class="chart-filter-bar">
|
<div v-if="reports.length > 0" class="series-panel">
|
||||||
<span class="filter-label">图表筛选:</span>
|
<span class="panel-label">图表:</span>
|
||||||
<el-select
|
<div class="series-chips">
|
||||||
v-model="selectedCategories"
|
<span
|
||||||
multiple
|
v-for="(sk, idx) in orderedSeriesKeys"
|
||||||
collapse-tags
|
:key="sk.key"
|
||||||
placeholder="成本类别(全部)"
|
:class="['series-chip', { off: sk._hidden }]"
|
||||||
clearable
|
:title="sk._hidden ? '点击显示' : '点击隐藏'"
|
||||||
size="small"
|
@click="toggleSeries(sk.key)"
|
||||||
style="width:150px"
|
>
|
||||||
@change="onCategoryChange"
|
<span class="chip-dot" :style="{ background: sk._color }" />
|
||||||
>
|
<span class="chip-name">{{ sk.name }}</span>
|
||||||
<el-option label="原料" value="原料" />
|
<span class="chip-arrows">
|
||||||
<el-option label="能耗" value="能耗" />
|
<i class="el-icon-caret-top" :class="{ disabled: idx === 0 }" @click.stop="moveSeries(idx, -1)" />
|
||||||
<el-option label="辅料" value="辅料" />
|
<i class="el-icon-caret-bottom" :class="{ disabled: idx === orderedSeriesKeys.length - 1 }" @click.stop="moveSeries(idx, 1)" />
|
||||||
<el-option label="设备" value="设备" />
|
</span>
|
||||||
<el-option label="人工" value="人工" />
|
</span>
|
||||||
<el-option label="产出" value="产出" />
|
</div>
|
||||||
<el-option label="轧辊" value="轧辊" />
|
<el-button size="mini" icon="el-icon-s-operation" circle @click="mgrOpen = true" />
|
||||||
<el-option label="其他" value="其他" />
|
|
||||||
</el-select>
|
|
||||||
<el-select
|
|
||||||
v-model="selectedItems"
|
|
||||||
multiple
|
|
||||||
collapse-tags
|
|
||||||
filterable
|
|
||||||
placeholder="成本项(全部)"
|
|
||||||
clearable
|
|
||||||
size="small"
|
|
||||||
style="width:200px"
|
|
||||||
@change="onItemChange"
|
|
||||||
>
|
|
||||||
<el-option
|
|
||||||
v-for="it in filteredItemOptions"
|
|
||||||
:key="it.itemId"
|
|
||||||
:label="it.itemName + ' (' + it.category + ')'"
|
|
||||||
:value="it.itemId"
|
|
||||||
/>
|
|
||||||
</el-select>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- 图表管理弹窗 -->
|
||||||
|
<el-dialog title="图表管理" :visible.sync="mgrOpen" width="680px" append-to-body top="8vh">
|
||||||
|
<el-table :data="orderedSeriesKeys" size="small" max-height="460">
|
||||||
|
<el-table-column label="排序" width="100" align="center">
|
||||||
|
<template slot-scope="s">
|
||||||
|
<el-button icon="el-icon-top" size="mini" circle :disabled="s.$index === 0" @click="moveSeries(s.$index, -1)" />
|
||||||
|
<el-button icon="el-icon-bottom" size="mini" circle :disabled="s.$index === orderedSeriesKeys.length - 1" @click="moveSeries(s.$index, 1)" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="名称" min-width="250">
|
||||||
|
<template slot-scope="s">
|
||||||
|
<span class="mgr-chip-dot" :style="{ background: s.row._color }" />
|
||||||
|
<span>{{ s.row.name }}</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="可见" width="80" align="center">
|
||||||
|
<template slot-scope="s">
|
||||||
|
<el-switch :value="!s.row._hidden" @change="toggleSeries(s.row.key)" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<div slot="footer">
|
||||||
|
<el-button type="primary" @click="mgrOpen = false">关 闭</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
|
||||||
<!-- 迷你图表网格 -->
|
<!-- 迷你图表网格 -->
|
||||||
<div v-loading="loading" class="mini-charts-grid">
|
<div v-loading="loading" class="mini-charts-grid">
|
||||||
<template v-if="seriesList.length">
|
<template v-if="seriesList.length">
|
||||||
@@ -102,8 +108,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import * as echarts from 'echarts'
|
import * as echarts from 'echarts'
|
||||||
import { mapGetters } from 'vuex'
|
import { mapGetters } from 'vuex'
|
||||||
import { listProdReport } from '@/api/cost/prodReport'
|
import { listProdReport, getProdReport } from '@/api/cost/prodReport'
|
||||||
import { listProdDetail } from '@/api/cost/prodDetail'
|
|
||||||
import { listItem } from '@/api/cost/item'
|
import { listItem } from '@/api/cost/item'
|
||||||
|
|
||||||
const COLORS = [
|
const COLORS = [
|
||||||
@@ -119,14 +124,15 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
selectedLine: null,
|
selectedLine: null,
|
||||||
selectedCategories: [],
|
|
||||||
selectedItems: [],
|
|
||||||
allItems: [],
|
allItems: [],
|
||||||
loading: false,
|
loading: false,
|
||||||
chartInstances: [],
|
chartInstances: [],
|
||||||
seriesList: [],
|
seriesList: [],
|
||||||
reports: [],
|
reports: [],
|
||||||
cachedDetails: [],
|
cachedConfigs: [],
|
||||||
|
allSeriesKeys: [],
|
||||||
|
seriesConfig: { hidden: {}, order: [] },
|
||||||
|
mgrOpen: false,
|
||||||
summary: { seriesCount: 0, maxLabel: '-' }
|
summary: { seriesCount: 0, maxLabel: '-' }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -136,9 +142,23 @@ export default {
|
|||||||
if (!this.selectedLine || !this.productionLines.length) return null
|
if (!this.selectedLine || !this.productionLines.length) return null
|
||||||
return this.productionLines.find(l => l.lineId === this.selectedLine) || null
|
return this.productionLines.find(l => l.lineId === this.selectedLine) || null
|
||||||
},
|
},
|
||||||
filteredItemOptions() {
|
orderedSeriesKeys() {
|
||||||
if (!this.selectedCategories.length) return this.allItems
|
const order = this.seriesConfig.order || []
|
||||||
return this.allItems.filter(it => this.selectedCategories.includes(it.category))
|
const hidden = this.seriesConfig.hidden || {}
|
||||||
|
const keys = [...this.allSeriesKeys]
|
||||||
|
// Sort: items in saved order first, then new items by original index
|
||||||
|
const orderMap = {}
|
||||||
|
order.forEach((k, i) => { orderMap[k] = i })
|
||||||
|
keys.sort((a, b) => {
|
||||||
|
const ai = orderMap[a.key] ?? 9999
|
||||||
|
const bi = orderMap[b.key] ?? 9999
|
||||||
|
if (ai !== bi) return ai - bi
|
||||||
|
// preserve original order for new items
|
||||||
|
return 0
|
||||||
|
})
|
||||||
|
// Attach _hidden flag
|
||||||
|
keys.forEach(k => { k._hidden = !!hidden[k.key] })
|
||||||
|
return keys
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
mounted() {
|
mounted() {
|
||||||
@@ -172,15 +192,47 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
onCategoryChange() {
|
/* ---- 系列配置(localStorage) ---- */
|
||||||
this.selectedItems = this.selectedItems.filter(id => {
|
storageKey() {
|
||||||
const it = this.allItems.find(item => item.itemId === id)
|
return 'cost_trend_cfg_' + (this.selectedLine || 'default')
|
||||||
return it && this.selectedCategories.includes(it.category)
|
},
|
||||||
})
|
loadSeriesConfig() {
|
||||||
|
try {
|
||||||
|
const raw = localStorage.getItem(this.storageKey())
|
||||||
|
if (raw) {
|
||||||
|
const cfg = JSON.parse(raw)
|
||||||
|
this.seriesConfig = {
|
||||||
|
hidden: cfg.hidden || {},
|
||||||
|
order: cfg.order || []
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.seriesConfig = { hidden: {}, order: [] }
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
this.seriesConfig = { hidden: {}, order: [] }
|
||||||
|
}
|
||||||
|
},
|
||||||
|
saveSeriesConfig() {
|
||||||
|
try {
|
||||||
|
localStorage.setItem(this.storageKey(), JSON.stringify(this.seriesConfig))
|
||||||
|
} catch (e) { /* ignore */ }
|
||||||
|
},
|
||||||
|
isSeriesHidden(key) {
|
||||||
|
return !!this.seriesConfig.hidden[key]
|
||||||
|
},
|
||||||
|
toggleSeries(key) {
|
||||||
|
this.$set(this.seriesConfig.hidden, key, !this.seriesConfig.hidden[key])
|
||||||
|
this.saveSeriesConfig()
|
||||||
this.renderFromCache()
|
this.renderFromCache()
|
||||||
},
|
},
|
||||||
|
moveSeries(idx, dir) {
|
||||||
onItemChange() {
|
const keys = this.orderedSeriesKeys.map(sk => sk.key)
|
||||||
|
const t = idx + dir
|
||||||
|
if (t < 0 || t >= keys.length) return
|
||||||
|
const item = keys.splice(idx, 1)[0]
|
||||||
|
keys.splice(t, 0, item)
|
||||||
|
this.seriesConfig.order = keys
|
||||||
|
this.saveSeriesConfig()
|
||||||
this.renderFromCache()
|
this.renderFromCache()
|
||||||
},
|
},
|
||||||
|
|
||||||
@@ -217,7 +269,7 @@ export default {
|
|||||||
}
|
}
|
||||||
this.reports = allReports
|
this.reports = allReports
|
||||||
|
|
||||||
await this.fetchDetailsAndCache(allReports)
|
await this.fetchConfigsAndCache(allReports)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('CostTrend fetchData error:', e)
|
console.error('CostTrend fetchData error:', e)
|
||||||
this.$modal.msgError('数据加载失败: ' + (e.message || '未知错误'))
|
this.$modal.msgError('数据加载失败: ' + (e.message || '未知错误'))
|
||||||
@@ -226,18 +278,21 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
async fetchDetailsAndCache(reports) {
|
async fetchConfigsAndCache(reports) {
|
||||||
const allDetailResults = await Promise.all(
|
const fullReports = await Promise.all(
|
||||||
reports.map(r =>
|
reports.map(r =>
|
||||||
listProdDetail({ reportId: r.reportId, pageNum: 1, pageSize: 99999 })
|
getProdReport(r.reportId)
|
||||||
.then(res => ({ reportId: r.reportId, rows: res.rows || [] }))
|
.then(res => res.data || {})
|
||||||
.catch(e => {
|
.catch(e => {
|
||||||
console.error('[CostTrend] detail fetch error for report', r.reportId, e)
|
console.error('[CostTrend] get report error', r.reportId, e)
|
||||||
return { reportId: r.reportId, rows: [] }
|
return {}
|
||||||
})
|
})
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
this.cachedDetails = allDetailResults
|
this.cachedConfigs = fullReports.map((r, i) => ({
|
||||||
|
reportId: reports[i].reportId,
|
||||||
|
colConfig: r.colConfig || '{}'
|
||||||
|
}))
|
||||||
|
|
||||||
if (!this.allItems.length) await this.loadItems()
|
if (!this.allItems.length) await this.loadItems()
|
||||||
|
|
||||||
@@ -246,47 +301,109 @@ export default {
|
|||||||
|
|
||||||
renderFromCache() {
|
renderFromCache() {
|
||||||
const reports = this.reports
|
const reports = this.reports
|
||||||
const allDetailResults = this.cachedDetails
|
const configs = this.cachedConfigs
|
||||||
if (!reports.length || !allDetailResults.length) return
|
if (!reports.length || !configs.length) return
|
||||||
|
|
||||||
const itemMap = {}
|
const itemMap = {}
|
||||||
this.allItems.forEach(it => { itemMap[String(it.itemId)] = it })
|
this.allItems.forEach(it => { itemMap[String(it.itemId)] = it })
|
||||||
|
|
||||||
const itemSet = this.selectedItems.length ? new Set(this.selectedItems.map(String)) : null
|
// Parse each report's colConfig to extract sum values
|
||||||
const categorySet = this.selectedCategories.length ? new Set(this.selectedCategories) : null
|
const reportData = []
|
||||||
|
const allKeys = new Map() // key -> { key, name, category, itemId }
|
||||||
|
|
||||||
const reportSums = {}
|
configs.forEach((cfg, i) => {
|
||||||
allDetailResults.forEach(({ reportId, rows }) => {
|
const report = reports[i]
|
||||||
const sums = {}
|
let parsed = {}
|
||||||
rows.forEach(d => {
|
try { parsed = JSON.parse(cfg.colConfig || '{}') } catch (e) { /* ignore */ }
|
||||||
if (!d.itemId) return
|
const sum = parsed.sum || {}
|
||||||
const key = String(d.itemId)
|
const columns = parsed.columns || []
|
||||||
if (itemSet && !itemSet.has(key)) return
|
|
||||||
if (categorySet) {
|
// Build header text -> category mapping from columns
|
||||||
const it = itemMap[key]
|
const headerMeta = {}
|
||||||
if (!it || !categorySet.has(it.category)) return
|
columns.forEach(col => {
|
||||||
|
if (col.t === 'd') {
|
||||||
|
const item = itemMap[String(col.id)]
|
||||||
|
if (item) {
|
||||||
|
const baseHdr = item.itemName + (item.unit ? '(' + item.unit + ')' : '')
|
||||||
|
headerMeta[baseHdr] = { category: item.category, itemId: item.itemId }
|
||||||
|
headerMeta[baseHdr + '(甲)'] = { category: item.category, itemId: item.itemId }
|
||||||
|
headerMeta[baseHdr + '(乙)'] = { category: item.category, itemId: item.itemId }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
sums[key] = (sums[key] || 0) + (parseFloat(d.quantity) || 0)
|
|
||||||
})
|
})
|
||||||
reportSums[reportId] = sums
|
|
||||||
|
const sumMap = {}
|
||||||
|
Object.entries(sum).forEach(([hdr, val]) => {
|
||||||
|
const num = parseFloat(val)
|
||||||
|
if (isNaN(num)) return
|
||||||
|
sumMap[hdr] = num
|
||||||
|
|
||||||
|
const meta = headerMeta[hdr] || {}
|
||||||
|
if (!allKeys.has(hdr)) {
|
||||||
|
allKeys.set(hdr, {
|
||||||
|
key: hdr,
|
||||||
|
name: hdr,
|
||||||
|
category: meta.category || null,
|
||||||
|
itemId: meta.itemId || null
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
reportData.push({
|
||||||
|
reportId: cfg.reportId,
|
||||||
|
sumMap,
|
||||||
|
label: this.reportLabel(report)
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
const allItemIds = new Set()
|
// Load saved config & merge
|
||||||
Object.values(reportSums).forEach(sums => {
|
this.allSeriesKeys = Array.from(allKeys.values())
|
||||||
Object.keys(sums).forEach(id => allItemIds.add(id))
|
|
||||||
|
// Assign stable colors to each key (before config load so orderedSeriesKeys gets them)
|
||||||
|
this.allSeriesKeys.forEach((sk, i) => {
|
||||||
|
sk._color = COLORS[i % COLORS.length]
|
||||||
})
|
})
|
||||||
|
|
||||||
const xLabels = reports.map(r => this.reportLabel(r))
|
this.loadSeriesConfig()
|
||||||
|
|
||||||
|
// Persist any new keys in order
|
||||||
|
const curKeys = this.allSeriesKeys.map(sk => sk.key)
|
||||||
|
const savedOrder = this.seriesConfig.order || []
|
||||||
|
const knownSet = new Set(savedOrder)
|
||||||
|
let dirty = false
|
||||||
|
curKeys.forEach(k => {
|
||||||
|
if (!knownSet.has(k)) {
|
||||||
|
savedOrder.push(k)
|
||||||
|
dirty = true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
// Remove stale keys from order
|
||||||
|
const curSet = new Set(curKeys)
|
||||||
|
const cleanOrder = savedOrder.filter(k => curSet.has(k))
|
||||||
|
if (cleanOrder.length !== savedOrder.length) dirty = true
|
||||||
|
this.seriesConfig.order = cleanOrder
|
||||||
|
if (dirty) this.saveSeriesConfig()
|
||||||
|
|
||||||
|
// Build series list (respect order & visibility)
|
||||||
|
const orderMap = {}
|
||||||
|
cleanOrder.forEach((k, i) => { orderMap[k] = i })
|
||||||
|
const ordered = [...this.allSeriesKeys].sort((a, b) => {
|
||||||
|
const ai = orderMap[a.key] ?? 9999
|
||||||
|
const bi = orderMap[b.key] ?? 9999
|
||||||
|
return ai - bi
|
||||||
|
})
|
||||||
|
|
||||||
|
const xLabels = reportData.map(r => r.label)
|
||||||
const list = []
|
const list = []
|
||||||
let globalMax = 0
|
let globalMax = 0
|
||||||
|
|
||||||
Array.from(allItemIds).forEach((key, idx) => {
|
ordered.forEach(sk => {
|
||||||
const it = itemMap[key]
|
// Skip hidden
|
||||||
const name = it ? it.itemName : `未知(${key})`
|
if (this.seriesConfig.hidden[sk.key]) return
|
||||||
const data = reports.map(r => {
|
|
||||||
const sums = reportSums[r.reportId] || {}
|
const data = reportData.map(r => {
|
||||||
const val = sums[key] ?? null
|
const v = r.sumMap[sk.key]
|
||||||
return val
|
return (v != null && isFinite(v)) ? v : 0
|
||||||
})
|
})
|
||||||
if (data.every(v => v === null)) return
|
if (data.every(v => v === null)) return
|
||||||
|
|
||||||
@@ -294,9 +411,9 @@ export default {
|
|||||||
if (maxVal > globalMax) globalMax = maxVal
|
if (maxVal > globalMax) globalMax = maxVal
|
||||||
|
|
||||||
list.push({
|
list.push({
|
||||||
key,
|
key: sk.key,
|
||||||
name,
|
name: sk.name,
|
||||||
color: COLORS[idx % COLORS.length],
|
color: sk._color,
|
||||||
data,
|
data,
|
||||||
xLabels,
|
xLabels,
|
||||||
maxVal
|
maxVal
|
||||||
@@ -477,17 +594,92 @@ export default {
|
|||||||
.stat-orange .stat-val { color: #e6a23c; }
|
.stat-orange .stat-val { color: #e6a23c; }
|
||||||
.stat-green .stat-val { color: #67c23a; }
|
.stat-green .stat-val { color: #67c23a; }
|
||||||
|
|
||||||
/* 图表筛选 */
|
/* 图表管理面板 */
|
||||||
.chart-filter-bar {
|
.series-panel {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 8px;
|
gap: 4px;
|
||||||
margin-bottom: 8px;
|
margin-bottom: 8px;
|
||||||
}
|
}
|
||||||
.chart-filter-bar .filter-label {
|
.series-panel .panel-label {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: #606266;
|
color: #909399;
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.series-chips {
|
||||||
|
display: flex;
|
||||||
|
gap: 4px;
|
||||||
|
flex: 1;
|
||||||
|
overflow-x: auto;
|
||||||
|
padding-bottom: 2px;
|
||||||
|
}
|
||||||
|
.series-chips::-webkit-scrollbar {
|
||||||
|
height: 4px;
|
||||||
|
}
|
||||||
|
.series-chips::-webkit-scrollbar-thumb {
|
||||||
|
background: #dcdfe6;
|
||||||
|
border-radius: 2px;
|
||||||
|
}
|
||||||
|
.series-chip {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 3px;
|
||||||
|
padding: 2px 7px;
|
||||||
|
border: 1px solid #dcdfe6;
|
||||||
|
border-radius: 3px;
|
||||||
|
font-size: 12px;
|
||||||
|
background: #fff;
|
||||||
|
cursor: pointer;
|
||||||
|
user-select: none;
|
||||||
|
white-space: nowrap;
|
||||||
|
flex-shrink: 0;
|
||||||
|
transition: opacity .15s, border-color .15s;
|
||||||
|
}
|
||||||
|
.series-chip:hover {
|
||||||
|
border-color: #409eff;
|
||||||
|
}
|
||||||
|
.series-chip.off {
|
||||||
|
opacity: 0.4;
|
||||||
|
}
|
||||||
|
.series-chip .chip-dot {
|
||||||
|
width: 7px;
|
||||||
|
height: 7px;
|
||||||
|
border-radius: 50%;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.series-chip .chip-name {
|
||||||
|
color: #303133;
|
||||||
|
max-width: 120px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
.series-chip .chip-arrows {
|
||||||
|
display: inline-flex;
|
||||||
|
flex-direction: column;
|
||||||
|
line-height: 1;
|
||||||
|
margin-left: 1px;
|
||||||
|
}
|
||||||
|
.series-chip .chip-arrows i {
|
||||||
|
font-size: 10px;
|
||||||
|
color: #c0c4cc;
|
||||||
|
cursor: pointer;
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
.series-chip .chip-arrows i:hover:not(.disabled) {
|
||||||
|
color: #409eff;
|
||||||
|
}
|
||||||
|
.series-chip .chip-arrows i.disabled {
|
||||||
|
color: #e8e8e8;
|
||||||
|
cursor: default;
|
||||||
|
}
|
||||||
|
.mgr-chip-dot {
|
||||||
|
display: inline-block;
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
border-radius: 50%;
|
||||||
|
vertical-align: middle;
|
||||||
|
margin-right: 6px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 迷你图表网格 */
|
/* 迷你图表网格 */
|
||||||
|
|||||||
Reference in New Issue
Block a user