import { palette as C } from './data' const tipOpt = { backgroundColor: 'rgba(12,43,94,.94)', borderWidth: 0, textStyle: { color: '#fff', fontSize: 12 }, padding: [8, 12], extraCssText: 'box-shadow:0 6px 18px rgba(12,43,94,.3);border-radius:6px;', } function axisStyle(opts = {}) { return { axisLine: { lineStyle: { color: '#E4E9F2' } }, axisTick: { show: false }, axisLabel: { color: C.mut, fontSize: 11 }, splitLine: { lineStyle: { color: '#F2F5FA', type: 'dashed' } }, ...opts, } } const sliceDate = (s) => s.map((x) => String(x[0]).slice(2, 7)) const sliceDateFull = (s) => s.map((x) => String(x[0]).slice(0, 10)) const upGrad = (color) => ({ type: 'linear', x: 0, y: 0, x2: 0, y2: 1, colorStops: [{ offset: 0, color: '#3D7BE0' }, { offset: 1, color }], }) const downGrad = (color) => ({ type: 'linear', x: 0, y: 0, x2: 0, y2: 1, colorStops: [{ offset: 0, color: '#4FCB9A' }, { offset: 1, color }], }) // ============== 月度累计值(柱图) ============== export function monthlyAccumulatedOption(series, { title = '累计值', color = C.brand, unit = '亿元' } = {}) { const cats = sliceDate(series) const values = series.map((x) => x[1]) return { tooltip: { ...tipOpt, trigger: 'axis', axisPointer: { type: 'shadow' } }, grid: { top: 18, left: 60, right: 24, bottom: 28 }, xAxis: { type: 'category', data: cats, ...axisStyle({ axisLabel: { color: C.mut, fontSize: 10 } }) }, yAxis: { type: 'value', name: unit, ...axisStyle() }, series: [{ name: title, type: 'bar', data: values, barWidth: 12, itemStyle: { color: (p) => (p.value >= 0 ? upGrad(color) : downGrad(C.down)), borderRadius: [3, 3, 0, 0], }, label: { show: false }, }], } } // ============== 月度累计同比(柱图,含 0 轴线) ============== export function monthlyYoYOption(series, { title = '累计同比', color = C.brand, unit = '%' } = {}) { const cats = sliceDate(series) const values = series.map((x) => x[1]) return { tooltip: { ...tipOpt, trigger: 'axis', axisPointer: { type: 'shadow' } }, grid: { top: 18, left: 50, right: 18, bottom: 28 }, xAxis: { type: 'category', data: cats, ...axisStyle({ axisLabel: { color: C.mut, fontSize: 10 } }) }, yAxis: { type: 'value', name: unit, ...axisStyle() }, series: [{ name: title, type: 'bar', data: values, barWidth: 12, itemStyle: { color: (p) => (p.value >= 0 ? upGrad(color) : downGrad(C.down)), borderRadius: [3, 3, 0, 0], }, label: { show: true, position: 'top', fontSize: 10, color: C.mut, formatter: (p) => (typeof p.value === 'number' ? p.value.toFixed(2) : ''), }, markLine: { symbol: ['none', 'none'], data: [{ yAxis: 0, lineStyle: { color: C.faint, type: 'dashed', width: 1 } }], label: { show: false }, }, }], } } // ============== 收入 vs 支出 双柱 ============== export function incomeVsExpenseOption(income, expense) { const cats = sliceDate(income) return { tooltip: { ...tipOpt, trigger: 'axis', axisPointer: { type: 'shadow' } }, legend: { data: ['收入', '支出'], top: 4, textStyle: { color: C.mut, fontSize: 11 }, itemWidth: 14, itemHeight: 8, }, grid: { top: 32, left: 60, right: 24, bottom: 28 }, xAxis: { type: 'category', data: cats, ...axisStyle({ axisLabel: { color: C.mut, fontSize: 10 } }) }, yAxis: { type: 'value', name: '亿元', ...axisStyle() }, series: [ { name: '收入', type: 'bar', data: income.map((x) => x[1]), barWidth: 8, itemStyle: { color: upGrad(C.brand), borderRadius: [2, 2, 0, 0] }, }, { name: '支出', type: 'bar', data: expense.map((x) => x[1]), barWidth: 8, itemStyle: { color: upGrad(C.warn), borderRadius: [2, 2, 0, 0] }, }, ], } } // ============== 政府性基金 收入 vs 支出 双柱 ============== export function fundIncVsExpOption(income, expense) { const cats = sliceDate(income) return { tooltip: { ...tipOpt, trigger: 'axis', axisPointer: { type: 'shadow' } }, legend: { data: ['基金收入', '基金支出'], top: 4, textStyle: { color: C.mut, fontSize: 11 }, itemWidth: 14, itemHeight: 8, }, grid: { top: 32, left: 60, right: 24, bottom: 28 }, xAxis: { type: 'category', data: cats, ...axisStyle({ axisLabel: { color: C.mut, fontSize: 10 } }) }, yAxis: { type: 'value', name: '亿元', ...axisStyle() }, series: [ { name: '基金收入', type: 'bar', data: income.map((x) => x[1]), barWidth: 8, itemStyle: { color: upGrad(C.purple), borderRadius: [2, 2, 0, 0] }, }, { name: '基金支出', type: 'bar', data: expense.map((x) => x[1]), barWidth: 8, itemStyle: { color: upGrad(C.brand), borderRadius: [2, 2, 0, 0] }, }, ], } } // ============== 赤字 / 专项债 年度(柱图) ============== export function yearlyBarOption(series, { title = '年度', color = C.warn, unit = '亿元' } = {}) { const cats = series.map((x) => String(x[0]).slice(0, 4)) const values = series.map((x) => x[1]) return { tooltip: { ...tipOpt, trigger: 'axis', axisPointer: { type: 'shadow' } }, grid: { top: 18, left: 60, right: 24, bottom: 28 }, xAxis: { type: 'category', data: cats, ...axisStyle({ axisLabel: { color: C.mut, fontSize: 10 } }) }, yAxis: { type: 'value', name: unit, ...axisStyle() }, series: [{ name: title, type: 'bar', data: values, barWidth: 18, itemStyle: { color: upGrad(color), borderRadius: [3, 3, 0, 0] }, label: { show: true, position: 'top', fontSize: 10, color: C.mut, formatter: (p) => (typeof p.value === 'number' ? p.value.toLocaleString() : ''), }, }], } } // ============== 赤字率 折线 ============== export function yearlyLineOption(series, { title = '赤字率', color = C.up, unit = '%' } = {}) { const cats = series.map((x) => String(x[0]).slice(0, 4)) const values = series.map((x) => x[1]) return { tooltip: { ...tipOpt, trigger: 'axis' }, grid: { top: 18, left: 50, right: 18, bottom: 28 }, xAxis: { type: 'category', data: cats, ...axisStyle({ axisLabel: { color: C.mut, fontSize: 10 } }) }, yAxis: { type: 'value', name: unit, ...axisStyle() }, series: [{ name: title, type: 'line', smooth: true, showSymbol: true, symbol: 'circle', symbolSize: 5, data: values, lineStyle: { color, width: 2.5 }, itemStyle: { color }, areaStyle: { type: 'linear', x: 0, y: 0, x2: 0, y2: 1, colorStops: [{ offset: 0, color: 'rgba(225,59,59,.16)' }, { offset: 1, color: 'rgba(225,59,59,0)' }], }, label: { show: true, position: 'top', color: C.mut, fontSize: 10, formatter: (p) => (typeof p.value === 'number' ? p.value.toFixed(1) + '%' : ''), }, }], } } // ============== 政府债余额 月度 折线 ============== export function monthlyLineOption(series, { title = '政府债余额', color = C.brand, unit = '万亿元' } = {}) { const cats = sliceDate(series) const values = series.map((x) => x[1]) return { tooltip: { ...tipOpt, trigger: 'axis' }, grid: { top: 18, left: 60, right: 24, bottom: 28 }, xAxis: { type: 'category', data: cats, ...axisStyle({ axisLabel: { color: C.mut, fontSize: 10 } }) }, yAxis: { type: 'value', name: unit, ...axisStyle() }, series: [{ name: title, type: 'line', smooth: true, showSymbol: false, symbol: 'circle', symbolSize: 4, data: values, lineStyle: { color, width: 2.5 }, itemStyle: { color }, areaStyle: { type: 'linear', x: 0, y: 0, x2: 0, y2: 1, colorStops: [{ offset: 0, color: 'rgba(21,82,208,.16)' }, { offset: 1, color: 'rgba(21,82,208,0)' }], }, label: { show: false }, }], } } // ============== 收支差 ============== export function gapOption(income, expense, { title = '财政收支差', color = C.up, unit = '亿元' } = {}) { const cats = sliceDate(income) const inc = new Map(income) const exp = new Map(expense) const gap = cats.map((_, i) => { const d = String(income[i][0]) const iv = inc.get(d)?.[1] const ev = exp.get(d)?.[1] if (typeof iv !== 'number' || typeof ev !== 'number') return null return iv - ev }) return { tooltip: { ...tipOpt, trigger: 'axis', axisPointer: { type: 'shadow' } }, grid: { top: 18, left: 60, right: 24, bottom: 28 }, xAxis: { type: 'category', data: cats, ...axisStyle({ axisLabel: { color: C.mut, fontSize: 10 } }) }, yAxis: { type: 'value', name: unit, ...axisStyle() }, series: [{ name: title, type: 'bar', data: gap, barWidth: 12, itemStyle: { color: (p) => (p.value >= 0 ? upGrad(color) : downGrad(C.down)), borderRadius: [3, 3, 0, 0], }, label: { show: false }, markLine: { symbol: ['none', 'none'], data: [{ yAxis: 0, lineStyle: { color: C.faint, type: 'dashed', width: 1 } }], label: { show: false }, }, }], } }