606 lines
14 KiB
Plaintext
606 lines
14 KiB
Plaintext
<template>
|
|
<view class="order-statistic-page">
|
|
<!-- 鏃堕棿閫夋嫨鍗$墖 -->
|
|
<view class="filter-card">
|
|
<view class="filter-item">
|
|
<text class="filter-label">鏃堕棿閫夋嫨锛?/text>
|
|
<view class="date-picker-mock">
|
|
<image class="calendar-icon" src="/static/icons/calendar.png" mode="aspectFit" />
|
|
<text class="date-range">2026/01/04 - 2026/02/02</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 鏁版嵁姹囨€诲崱鐗?-->
|
|
<view class="stat-cards-row">
|
|
<!-- 璁㈠崟閲?-->
|
|
<view class="stat-card">
|
|
<view class="icon-wrap blue-bg">
|
|
<view class="custom-icon icon-order"></view>
|
|
</view>
|
|
<view class="stat-info">
|
|
<text class="stat-value">209</text>
|
|
<text class="stat-desc">璁㈠崟閲?/text>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 璁㈠崟閿€鍞 -->
|
|
<view class="stat-card">
|
|
<view class="icon-wrap orange-bg">
|
|
<view class="custom-icon icon-money"></view>
|
|
</view>
|
|
<view class="stat-info">
|
|
<text class="stat-value">443254.62</text>
|
|
<text class="stat-desc">璁㈠崟閿€鍞</text>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 閫€娆捐鍗曟暟 -->
|
|
<view class="stat-card">
|
|
<view class="icon-wrap green-bg">
|
|
<view class="custom-icon icon-refund"></view>
|
|
</view>
|
|
<view class="stat-info">
|
|
<text class="stat-value">0</text>
|
|
<text class="stat-desc">閫€娆捐鍗曟暟</text>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 閫€娆鹃噾棰?-->
|
|
<view class="stat-card last-card">
|
|
<view class="icon-wrap pink-bg">
|
|
<view class="custom-icon icon-refund-money"></view>
|
|
</view>
|
|
<view class="stat-info">
|
|
<text class="stat-value">0</text>
|
|
<text class="stat-desc">閫€娆鹃噾棰?/text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 钀ヤ笟瓒嬪娍鍥捐〃 -->
|
|
<view class="chart-card">
|
|
<view class="card-header">
|
|
<text class="card-title">钀ヤ笟瓒嬪娍</text>
|
|
</view>
|
|
<view class="chart-container">
|
|
<EChartsView :option="trendOption" class="trend-chart" />
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 搴曢儴鍙屽浘琛ㄥ尯鍩?-->
|
|
<view class="bottom-charts-row">
|
|
<!-- 璁㈠崟鏉ユ簮鍒嗘瀽 -->
|
|
<view class="bottom-chart-card">
|
|
<view class="card-header-row">
|
|
<text class="card-title">璁㈠崟鏉ユ簮鍒嗘瀽</text>
|
|
<view class="style-toggle">
|
|
<text class="toggle-text">鍒囨崲鏍峰紡</text>
|
|
</view>
|
|
</view>
|
|
<view class="pie-chart-container">
|
|
<EChartsView :option="sourceOption" class="source-chart" />
|
|
</view>
|
|
</view>
|
|
|
|
<!-- 璁㈠崟绫诲瀷鍒嗘瀽 -->
|
|
<view class="bottom-chart-card">
|
|
<view class="card-header-row">
|
|
<text class="card-title">璁㈠崟绫诲瀷鍒嗘瀽</text>
|
|
<view class="style-toggle">
|
|
<text class="toggle-text">鍒囨崲鏍峰紡</text>
|
|
</view>
|
|
</view>
|
|
<view class="type-table-container">
|
|
<view class="table-header">
|
|
<text class="th-text col-id">搴忓彿</text>
|
|
<text class="th-text col-name">鏉ユ簮</text>
|
|
<text class="th-text col-money">閲戦</text>
|
|
<text class="th-text col-rate">鍗犳瘮鐜?/text>
|
|
</view>
|
|
<view class="table-body">
|
|
<view v-for="(item, index) in orderTypeData" :key="index" class="table-row">
|
|
<text class="td-text col-id">{{ index + 1 }}</text>
|
|
<text class="td-text col-name">{{ item.name }}</text>
|
|
<text class="td-text col-money">{{ item.amount }}</text>
|
|
<view class="col-rate rate-box">
|
|
<view class="progress-wrap">
|
|
<view class="progress-bar" :style="{ width: item.rate + '%', backgroundColor: '#1890ff' }"></view>
|
|
</view>
|
|
<text class="rate-val">{{ item.rate }}%</text>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
</template>
|
|
|
|
<script setup lang="uts">
|
|
import { ref, onMounted } from 'vue'
|
|
import EChartsView from '@/uni_modules/charts/EChartsView.vue'
|
|
|
|
const title = ref<string>('璁㈠崟缁熻')
|
|
const trendOption = ref<any>({})
|
|
const sourceOption = ref<any>({})
|
|
|
|
const orderTypeData = ref([
|
|
{ name: '鏅€氳鍗?, amount: '430986.62', rate: '97.23' },
|
|
{ name: '鎷煎洟璁㈠崟', amount: '7127', rate: '1.60' },
|
|
{ name: '棰勫敭璁㈠崟', amount: '4835', rate: '1.09' },
|
|
{ name: '绉掓潃璁㈠崟', amount: '306', rate: '0.06' },
|
|
{ name: '鐮嶄环璁㈠崟', amount: '0', rate: '0.00' }
|
|
])
|
|
|
|
onMounted(() => {
|
|
setTimeout(() => {
|
|
initCharts()
|
|
}, 300)
|
|
})
|
|
|
|
/**
|
|
* 杞崲 UTS 瀵硅薄涓虹函 JS 瀵硅薄锛岀‘淇?ECharts 鑳芥纭В鏋?
|
|
*/
|
|
function toPlainObject(obj: any): any {
|
|
if (obj == null) return null
|
|
if (typeof obj !== 'object') return obj
|
|
if (Array.isArray(obj)) {
|
|
return obj.map((item: any) : any => toPlainObject(item))
|
|
}
|
|
const plain: any = {}
|
|
const keys = Object.keys(obj)
|
|
for (let i = 0; i < keys.length; i++) {
|
|
const key = keys[i]
|
|
const value = obj[key]
|
|
if (typeof value === 'function' || key.startsWith('_') || key === 'toJSON') {
|
|
continue
|
|
}
|
|
if (value != null && typeof value === 'object') {
|
|
plain[key] = toPlainObject(value)
|
|
} else {
|
|
plain[key] = value
|
|
}
|
|
}
|
|
return plain
|
|
}
|
|
|
|
function initCharts() {
|
|
initTrendChart()
|
|
initSourceChart()
|
|
}
|
|
|
|
function initSourceChart() {
|
|
const option = {
|
|
tooltip: {
|
|
trigger: 'item',
|
|
formatter: '{b}: {c} ({d}%)'
|
|
},
|
|
legend: {
|
|
orient: 'vertical',
|
|
right: '10%',
|
|
top: 'center',
|
|
itemWidth: 10,
|
|
itemHeight: 10,
|
|
icon: 'circle',
|
|
textStyle: { color: '#8c8c8c' }
|
|
},
|
|
color: ['#1890ff', '#52c41a', '#597ef7', '#ffc53d', '#ff7875'],
|
|
series: [
|
|
{
|
|
name: '璁㈠崟鏉ユ簮',
|
|
type: 'pie',
|
|
radius: ['45%', '70%'],
|
|
center: ['40%', '50%'],
|
|
avoidLabelOverlap: false,
|
|
label: {
|
|
show: true,
|
|
position: 'outside',
|
|
formatter: '{b}'
|
|
},
|
|
labelLine: {
|
|
show: true
|
|
},
|
|
data: [
|
|
{ value: 1048, name: '鍏紬鍙? },
|
|
{ value: 735, name: '灏忕▼搴? },
|
|
{ value: 580, name: 'H5' },
|
|
{ value: 484, name: 'PC' },
|
|
{ value: 300, name: 'APP' }
|
|
]
|
|
}
|
|
]
|
|
}
|
|
sourceOption.value = toPlainObject(option)
|
|
}
|
|
|
|
function initTrendChart() {
|
|
const dates = [
|
|
'01-04', '01-05', '01-06', '01-07', '01-08', '01-09', '01-10', '01-11', '01-12', '01-13',
|
|
'01-14', '01-15', '01-16', '01-17', '01-18', '01-19', '01-20', '01-21', '01-22', '01-23',
|
|
'01-24', '01-25', '01-26', '01-27', '01-28', '01-29', '01-30', '01-31', '02-01', '02-02'
|
|
]
|
|
|
|
const orderAmount = [
|
|
8000, 2000, 9000, 1000, 138000, 6000, 1000, 500, 800, 200,
|
|
5000, 35000, 7000, 1000, 12000, 1000, 100000, 16000, 18000, 1000,
|
|
1200, 1500, 68000, 1000, 10000, 2000, 4000, 8000, 2000, 1000
|
|
]
|
|
|
|
const option = {
|
|
tooltip: {
|
|
trigger: 'axis',
|
|
axisPointer: {
|
|
type: 'line',
|
|
lineStyle: { color: '#ccc', width: 1 }
|
|
}
|
|
},
|
|
legend: {
|
|
data: ['璁㈠崟閲戦', '璁㈠崟閲?, '閫€娆鹃噾棰?, '閫€娆捐鍗曢噺'],
|
|
top: 10,
|
|
right: 'center',
|
|
icon: 'circle',
|
|
textStyle: { color: '#8c8c8c' }
|
|
},
|
|
grid: {
|
|
left: '3%',
|
|
right: '4%',
|
|
bottom: '10%',
|
|
top: '60px',
|
|
containLabel: true
|
|
},
|
|
xAxis: {
|
|
type: 'category',
|
|
boundaryGap: false,
|
|
data: dates,
|
|
axisLine: { lineStyle: { color: '#e8e8e8' } },
|
|
axisLabel: { color: '#8c8c8c', rotate: 45 }
|
|
},
|
|
yAxis: {
|
|
type: 'value',
|
|
splitLine: { lineStyle: { type: 'dashed', color: '#f0f0f0' } },
|
|
axisLabel: { color: '#8c8c8c' }
|
|
},
|
|
series: [
|
|
{
|
|
name: '璁㈠崟閲戦',
|
|
type: 'line',
|
|
smooth: false,
|
|
symbol: 'circle',
|
|
symbolSize: 6,
|
|
itemStyle: { color: '#5b8ff9' },
|
|
lineStyle: { width: 2 },
|
|
data: orderAmount
|
|
},
|
|
{
|
|
name: '璁㈠崟閲?,
|
|
type: 'line',
|
|
itemStyle: { color: '#5ad8a6' },
|
|
data: dates.map((_ : string) : number => Math.floor(Math.random() * 20))
|
|
},
|
|
{
|
|
name: '閫€娆鹃噾棰?,
|
|
type: 'line',
|
|
itemStyle: { color: '#ff9d4d' },
|
|
data: dates.map((_ : string) : number => 0)
|
|
},
|
|
{
|
|
name: '閫€娆捐鍗曢噺',
|
|
type: 'line',
|
|
itemStyle: { color: '#9270ca' },
|
|
data: dates.map((_ : string) : number => 0)
|
|
}
|
|
]
|
|
}
|
|
|
|
trendOption.value = toPlainObject(option)
|
|
}
|
|
</script>
|
|
|
|
<style scoped lang="scss">
|
|
.order-statistic-page {
|
|
/* padding removed */
|
|
|
|
min-height: 100%;
|
|
}
|
|
|
|
.filter-card {
|
|
background-color: #fff;
|
|
padding: 24px;
|
|
border-radius: 4px;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.filter-item {
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
}
|
|
|
|
.filter-label {
|
|
font-size: 14px;
|
|
color: #333;
|
|
margin-right: 12px;
|
|
}
|
|
|
|
.date-picker-mock {
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
border: 1px solid #d9d9d9;
|
|
border-radius: 4px;
|
|
padding: 4px 12px;
|
|
width: 240px;
|
|
}
|
|
|
|
.calendar-icon {
|
|
width: 16px;
|
|
height: 16px;
|
|
margin-right: 8px;
|
|
opacity: 0.45;
|
|
}
|
|
|
|
.date-range {
|
|
font-size: 14px;
|
|
color: #595959;
|
|
}
|
|
|
|
.stat-cards-row {
|
|
display: flex;
|
|
flex-direction: row;
|
|
gap: 16px;
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.chart-card {
|
|
background-color: #fff;
|
|
border-radius: 4px;
|
|
padding: 24px;
|
|
}
|
|
|
|
.card-header {
|
|
margin-bottom: 20px;
|
|
}
|
|
|
|
.card-title {
|
|
font-size: 16px;
|
|
font-weight: bold;
|
|
color: #1a1a1a;
|
|
}
|
|
|
|
.chart-container {
|
|
width: 100%;
|
|
height: 400px;
|
|
}
|
|
|
|
.trend-chart {
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
|
|
.bottom-charts-row {
|
|
display: flex;
|
|
flex-direction: row;
|
|
gap: 16px;
|
|
margin-top: 20px;
|
|
}
|
|
|
|
.bottom-chart-card {
|
|
flex: 1;
|
|
background-color: #fff;
|
|
border-radius: 4px;
|
|
padding: 24px;
|
|
}
|
|
|
|
.card-header-row {
|
|
display: flex;
|
|
flex-direction: row;
|
|
justify-content: space-between;
|
|
align-items: center;
|
|
margin-bottom: 24px;
|
|
}
|
|
|
|
.style-toggle {
|
|
border: 1px solid #d9d9d9;
|
|
border-radius: 4px;
|
|
padding: 2px 8px;
|
|
}
|
|
|
|
.toggle-text {
|
|
font-size: 12px;
|
|
color: #595959;
|
|
}
|
|
|
|
.pie-chart-container {
|
|
width: 100%;
|
|
height: 320px;
|
|
}
|
|
|
|
.source-chart {
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
|
|
.type-table-container {
|
|
width: 100%;
|
|
}
|
|
|
|
.table-header {
|
|
display: flex;
|
|
flex-direction: row;
|
|
background-color: #e6f7ff;
|
|
padding: 12px 0;
|
|
border-radius: 4px 4px 0 0;
|
|
}
|
|
|
|
.th-text {
|
|
font-size: 14px;
|
|
color: #595959;
|
|
}
|
|
|
|
/* 缁熶竴鍒楀涓庡榻愭柟寮?*/
|
|
.col-id { width: 80px; text-align: center; }
|
|
.col-name { flex: 1; text-align: left; padding-left: 40px; }
|
|
.col-money { width: 180px; text-align: left; }
|
|
.col-rate { width: 240px; text-align: left; }
|
|
|
|
.table-row {
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
padding: 16px 0;
|
|
border-bottom: 1px solid #f0f0f0;
|
|
}
|
|
|
|
.td-text {
|
|
font-size: 14px;
|
|
color: #262626;
|
|
}
|
|
|
|
.rate-box {
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
justify-content: flex-start; /* 鏀逛负宸﹀榻愶紝涓庤〃澶村榻愭牱寮忎竴鑷?*/
|
|
}
|
|
|
|
.progress-wrap {
|
|
width: 120px;
|
|
height: 6px;
|
|
background-color: #f5f5f5;
|
|
border-radius: 3px;
|
|
margin-right: 12px;
|
|
overflow: hidden;
|
|
}
|
|
|
|
.progress-bar {
|
|
height: 100%;
|
|
border-radius: 3px;
|
|
}
|
|
|
|
.rate-val {
|
|
font-size: 13px;
|
|
color: #595959;
|
|
width: 50px;
|
|
text-align: right;
|
|
}
|
|
|
|
.stat-card {
|
|
flex: 1;
|
|
background-color: #fff;
|
|
border-radius: 4px;
|
|
padding: 24px;
|
|
display: flex;
|
|
flex-direction: row;
|
|
align-items: center;
|
|
}
|
|
|
|
.icon-wrap {
|
|
width: 54px;
|
|
height: 54px;
|
|
border-radius: 27px;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin-right: 20px;
|
|
}
|
|
|
|
/* 棰滆壊鑳屾櫙 - 1:1 鍖归厤鎴浘 */
|
|
.blue-bg {
|
|
background-color: #e6f7ff;
|
|
border: 2px solid #bae7ff;
|
|
}
|
|
.orange-bg {
|
|
background-color: #fff7e6;
|
|
border: 2px solid #ffe58f;
|
|
}
|
|
.green-bg {
|
|
background-color: #f6ffed;
|
|
border: 2px solid #b7eb8f;
|
|
}
|
|
.pink-bg {
|
|
background-color: #fff0f6;
|
|
border: 2px solid #ffadd2;
|
|
}
|
|
|
|
.stat-info {
|
|
display: flex;
|
|
flex-direction: column;
|
|
}
|
|
|
|
.stat-value {
|
|
font-size: 28px;
|
|
font-weight: 500;
|
|
color: #1a1a1a;
|
|
line-height: 1.2;
|
|
}
|
|
|
|
.stat-desc {
|
|
font-size: 13px;
|
|
color: #8c8c8c;
|
|
margin-top: 4px;
|
|
}
|
|
|
|
/* 鑷畾涔夊浘鏍?1:1 褰㈢姸妯℃嫙 - 鍐呴儴浣跨敤浼厓绱犳垨褰㈢姸妯℃嫙鎴浘褰㈢姸 */
|
|
.custom-icon {
|
|
width: 24px;
|
|
height: 24px;
|
|
position: relative;
|
|
}
|
|
|
|
.icon-order {
|
|
background-color: #1890ff;
|
|
border-radius: 4px;
|
|
&::before {
|
|
content: '';
|
|
position: absolute;
|
|
top: 6px; left: 4px; right: 4px; height: 2px;
|
|
background: #fff;
|
|
}
|
|
}
|
|
|
|
.icon-money {
|
|
background-color: #faad14;
|
|
border-radius: 50%;
|
|
&::after {
|
|
content: '锟?;
|
|
color: #fff;
|
|
font-size: 12px;
|
|
font-weight: bold;
|
|
display: flex; justify-content: center; align-items: center; height: 100%;
|
|
}
|
|
}
|
|
|
|
.icon-refund {
|
|
background-color: #52c41a;
|
|
border-radius: 4px;
|
|
&::before {
|
|
content: '鈫?;
|
|
color: #fff;
|
|
font-size: 16px;
|
|
display: flex; justify-content: center; align-items: center; height: 100%;
|
|
}
|
|
}
|
|
|
|
.icon-refund-money {
|
|
background-color: #eb2f96;
|
|
border-radius: 50%;
|
|
&::after {
|
|
content: '鈭?;
|
|
color: #fff;
|
|
font-size: 18px;
|
|
display: flex; justify-content: center; align-items: center; height: 100%;
|
|
}
|
|
}
|
|
</style>
|
|
|
|
<style scoped lang="scss">
|
|
@import '@/uni.scss';
|
|
.page { padding: $space-lg; }
|
|
.header { padding: $space-lg; border-radius: $radius; background: $background-primary; box-shadow: $shadow-xs; }
|
|
.title { font-size: $font-size-lg; font-weight: $font-weight-bold; color: $text-primary; }
|
|
.sub-title { margin-top: $space-xs; font-size: $font-size-md; color: $text-secondary; }
|
|
</style>
|
|
|
|
|