247 lines
6.4 KiB
Plaintext
247 lines
6.4 KiB
Plaintext
<template>
|
|
<view class="chart-wrap" :style="{ height: heightPx }">
|
|
<EChartsView :option="chartOption" class="chart" />
|
|
</view>
|
|
</template>
|
|
|
|
<script lang="uts">
|
|
import EChartsView from '@/uni_modules/charts/EChartsView.vue'
|
|
|
|
export default {
|
|
components: {
|
|
EChartsView
|
|
},
|
|
props: {
|
|
xLabels: { type: Array, default: () => [] },
|
|
gmv: { type: Array, default: () => [] },
|
|
orders: { type: Array, default: () => [] },
|
|
height: { type: Number, default: 320 }
|
|
},
|
|
|
|
data() {
|
|
return {
|
|
heightPx: '320px',
|
|
chartOption: {} as any
|
|
}
|
|
},
|
|
|
|
watch: {
|
|
xLabels: {
|
|
handler() {
|
|
if (this.xLabels && this.xLabels.length > 0) {
|
|
this.updateOption()
|
|
}
|
|
},
|
|
deep: true,
|
|
immediate: false
|
|
},
|
|
gmv: {
|
|
handler() {
|
|
if (this.gmv && this.gmv.length > 0) {
|
|
this.updateOption()
|
|
}
|
|
},
|
|
deep: true,
|
|
immediate: false
|
|
},
|
|
orders: {
|
|
handler() {
|
|
if (this.orders && this.orders.length > 0) {
|
|
this.updateOption()
|
|
}
|
|
},
|
|
deep: true,
|
|
immediate: false
|
|
},
|
|
height: {
|
|
handler() {
|
|
this.heightPx = `${this.height}px`
|
|
}
|
|
}
|
|
},
|
|
|
|
mounted() {
|
|
this.heightPx = `${this.height}px`
|
|
// 延迟初始化,确保 props 已传递
|
|
setTimeout(() => {
|
|
if (this.xLabels && this.xLabels.length > 0 && this.gmv && this.gmv.length > 0) {
|
|
this.updateOption()
|
|
}
|
|
}, 100)
|
|
},
|
|
|
|
methods: {
|
|
// 工具函数:将 UTS 对象转换为纯 JavaScript 对象
|
|
toPlainObject(obj: any): any {
|
|
if (obj == null) return null
|
|
if (typeof obj !== 'object') return obj
|
|
if (Array.isArray(obj)) {
|
|
return obj.map((item) => this.toPlainObject(item))
|
|
}
|
|
const plain: any = {}
|
|
for (const key in obj) {
|
|
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
const value = obj[key]
|
|
if (typeof value === 'function' || key.startsWith('_') || key === 'toJSON') {
|
|
continue
|
|
}
|
|
if (value != null && typeof value === 'object' && !Array.isArray(value)) {
|
|
let isSimple = true
|
|
for (const k in value) {
|
|
if (typeof value[k] === 'object' && value[k] !== null) {
|
|
isSimple = false
|
|
break
|
|
}
|
|
}
|
|
plain[key] = isSimple ? { ...value } : this.toPlainObject(value)
|
|
} else {
|
|
plain[key] = value
|
|
}
|
|
}
|
|
}
|
|
return plain
|
|
},
|
|
|
|
updateOption() {
|
|
// 检查数据是否有效
|
|
if (!this.xLabels || !this.gmv || !this.orders ||
|
|
this.xLabels.length === 0 || this.gmv.length === 0 || this.orders.length === 0) {
|
|
return
|
|
}
|
|
|
|
const x = (this.xLabels as Array<any>).map((s) => String(s))
|
|
const bar = (this.gmv as Array<any>).map((v) => {
|
|
const n = Number(v)
|
|
return isFinite(n) ? n : 0
|
|
})
|
|
const line = (this.orders as Array<any>).map((v) => {
|
|
const n = Number(v)
|
|
return isFinite(n) ? n : 0
|
|
})
|
|
|
|
// 构建图表配置并转换为纯 JS 对象
|
|
const option = {
|
|
grid: { left: 60, right: 60, top: 70, bottom: 40 },
|
|
tooltip: {
|
|
trigger: 'axis',
|
|
axisPointer: { type: 'shadow' },
|
|
formatter: (params: any) => {
|
|
let result = params[0].name + '<br/>'
|
|
for (let i = 0; i < params.length; i++) {
|
|
const p = params[i]
|
|
if (p.seriesName === '订单金额' || p.seriesName === 'GMV') {
|
|
const val = Number(p.value)
|
|
const formatted = val >= 10000 ? (val / 10000).toFixed(1) + '万' : val.toFixed(0)
|
|
result += `${p.marker} ${p.seriesName}: ¥${formatted}<br/>`
|
|
} else {
|
|
result += `${p.marker} ${p.seriesName}: ${p.value}<br/>`
|
|
}
|
|
}
|
|
return result
|
|
}
|
|
},
|
|
legend: {
|
|
top: 8,
|
|
left: 'center',
|
|
itemWidth: 10,
|
|
itemHeight: 10,
|
|
textStyle: { fontSize: 12 },
|
|
data: ['订单金额', '订单数'],
|
|
bottom: 'auto'
|
|
},
|
|
xAxis: {
|
|
type: 'category',
|
|
data: x,
|
|
axisTick: { alignWithLabel: true },
|
|
axisLine: { lineStyle: { color: 'rgba(0,0,0,0.12)' } },
|
|
axisLabel: {
|
|
color: 'rgba(0,0,0,0.55)',
|
|
rotate: x.length > 20 ? 45 : 0,
|
|
interval: x.length > 15 ? 1 : 0
|
|
}
|
|
},
|
|
yAxis: [
|
|
{
|
|
type: 'value',
|
|
name: '金额',
|
|
position: 'left',
|
|
axisLine: { show: false },
|
|
splitLine: { lineStyle: { color: 'rgba(0,0,0,0.06)' } },
|
|
axisLabel: {
|
|
color: 'rgba(0,0,0,0.55)',
|
|
formatter: (value: number) => {
|
|
if (value >= 10000) {
|
|
return (value / 10000).toFixed(0) + '万'
|
|
}
|
|
return String(Math.round(value))
|
|
}
|
|
}
|
|
},
|
|
{
|
|
type: 'value',
|
|
name: '数量',
|
|
position: 'right',
|
|
alignTicks: true,
|
|
axisLine: { show: false },
|
|
splitLine: { show: false },
|
|
axisLabel: {
|
|
color: 'rgba(0,0,0,0.55)',
|
|
formatter: (value: number) => String(Math.round(value))
|
|
}
|
|
}
|
|
],
|
|
series: [
|
|
{
|
|
name: '订单金额',
|
|
type: 'bar',
|
|
yAxisIndex: 0,
|
|
data: bar,
|
|
barMaxWidth: 14,
|
|
barCategoryGap: '35%',
|
|
itemStyle: {
|
|
borderRadius: [2, 2, 0, 0],
|
|
color: '#3b82f6'
|
|
}
|
|
},
|
|
{
|
|
name: '订单数',
|
|
type: 'line',
|
|
yAxisIndex: 1,
|
|
data: line,
|
|
smooth: false,
|
|
symbol: 'circle',
|
|
symbolSize: 6,
|
|
lineStyle: {
|
|
width: 2,
|
|
color: '#10b981'
|
|
},
|
|
itemStyle: {
|
|
color: '#10b981'
|
|
}
|
|
}
|
|
]
|
|
}
|
|
|
|
// 转换为纯 JS 对象确保 ECharts 能正确接收
|
|
this.chartOption = this.toPlainObject(option)
|
|
}
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style>
|
|
.chart-wrap {
|
|
width: 100%;
|
|
position: relative;
|
|
overflow: hidden;
|
|
box-sizing: border-box;
|
|
}
|
|
.chart {
|
|
width: 100%;
|
|
height: 100%;
|
|
position: absolute;
|
|
top: 0;
|
|
left: 0;
|
|
}
|
|
</style>
|