feat(admin): full integration of order, product, and finance modules with real RPC data streams
This commit is contained in:
@@ -8,16 +8,16 @@
|
||||
:key="index"
|
||||
class="date-tab-item"
|
||||
:class="{ active: activeDateTab === index }"
|
||||
@click="activeDateTab = index"
|
||||
@click="handleDateTabChange(index)"
|
||||
>{{ item }}</text>
|
||||
</view>
|
||||
<view class="date-picker-wrap">
|
||||
<text class="calendar-icon">D</text>
|
||||
<text class="date-range-text">2026-02-03 - 2026-02-03</text>
|
||||
<text class="date-range-text">{{ displayDateRange }}</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 交易概况区块 (CRMEB 1:1 复刻) -->
|
||||
<!-- 交易概况区块 -->
|
||||
<view class="overview-card">
|
||||
<view class="overview-header">
|
||||
<view class="header-left">
|
||||
@@ -25,11 +25,8 @@
|
||||
<text class="info-tag">?</text>
|
||||
</view>
|
||||
<view class="header-right">
|
||||
<view class="date-picker-inline">
|
||||
<text class="date-text">2026/01/05 - 2026/02/03</text>
|
||||
</view>
|
||||
<button class="btn-query">查询</button>
|
||||
<button class="btn-export">导出</button>
|
||||
<button class="btn-query" @click="loadData">刷新数据</button>
|
||||
<button class="btn-export">导出报表</button>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
@@ -41,10 +38,10 @@
|
||||
<view class="icon-box blue"><text class="icon">🕒</text></view>
|
||||
<view class="item-info">
|
||||
<text class="item-label">营业额</text>
|
||||
<text class="item-value">{{ stats.revenue }}</text>
|
||||
<text class="item-value">¥{{ stats.revenue }}</text>
|
||||
<view class="trend-row">
|
||||
<text class="trend-label">环比增长:</text>
|
||||
<text class="trend-value up">44275370% ▲</text>
|
||||
<text class="trend-label">昨日对比:</text>
|
||||
<text class="trend-value">{{ stats.revenueTrend }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -52,10 +49,10 @@
|
||||
<view class="icon-box green"><text class="icon">¥</text></view>
|
||||
<view class="item-info">
|
||||
<text class="item-label">商品支付金额</text>
|
||||
<text class="item-value">{{ stats.payAmount }}</text>
|
||||
<text class="item-value">¥{{ stats.payAmount }}</text>
|
||||
<view class="trend-row">
|
||||
<text class="trend-label">环比增长:</text>
|
||||
<text class="trend-value up">43469352% ▲</text>
|
||||
<text class="trend-label">交易笔数:</text>
|
||||
<text class="trend-value">{{ stats.orderCount }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -63,10 +60,10 @@
|
||||
<view class="icon-box orange"><text class="icon">🔒</text></view>
|
||||
<view class="item-info">
|
||||
<text class="item-label">购买会员金额</text>
|
||||
<text class="item-value">{{ stats.memberAmount }}</text>
|
||||
<text class="item-value">¥{{ stats.memberAmount }}</text>
|
||||
<view class="trend-row">
|
||||
<text class="trend-label">环比增长:</text>
|
||||
<text class="trend-value up">805918% ▲</text>
|
||||
<text class="trend-label">占比:</text>
|
||||
<text class="trend-value">-</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -74,10 +71,10 @@
|
||||
<view class="icon-box purple"><text class="icon">💰</text></view>
|
||||
<view class="item-info">
|
||||
<text class="item-label">充值金额</text>
|
||||
<text class="item-value">{{ stats.rechargeAmount }}</text>
|
||||
<text class="item-value">¥{{ stats.rechargeAmount }}</text>
|
||||
<view class="trend-row">
|
||||
<text class="trend-label">环比增长:</text>
|
||||
<text class="trend-value">0% -</text>
|
||||
<text class="trend-label">笔数:</text>
|
||||
<text class="trend-value">{{ stats.rechargeCount }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -85,10 +82,10 @@
|
||||
<view class="icon-box cyan"><text class="icon">🛒</text></view>
|
||||
<view class="item-info">
|
||||
<text class="item-label">线下收银金额</text>
|
||||
<text class="item-value">{{ stats.offlineAmount }}</text>
|
||||
<text class="item-value">¥{{ stats.offlineAmount }}</text>
|
||||
<view class="trend-row">
|
||||
<text class="trend-label">环比增长:</text>
|
||||
<text class="trend-value up">100% ▲</text>
|
||||
<text class="trend-label">占比:</text>
|
||||
<text class="trend-value">-</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -99,33 +96,33 @@
|
||||
<view class="overview-item">
|
||||
<view class="icon-box light-green"><text class="icon">↘</text></view>
|
||||
<view class="item-info">
|
||||
<text class="item-label">支出金额</text>
|
||||
<text class="item-value">{{ stats.expenditure }}</text>
|
||||
<text class="item-label">支出金额 (提现)</text>
|
||||
<text class="item-value">¥{{ stats.expenditure }}</text>
|
||||
<view class="trend-row">
|
||||
<text class="trend-label">环比增长:</text>
|
||||
<text class="trend-value up">44275269% ▲</text>
|
||||
<text class="trend-label">笔数:</text>
|
||||
<text class="trend-value">{{ stats.extractCount }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="overview-item">
|
||||
<view class="icon-box gold"><text class="icon">💳</text></view>
|
||||
<view class="item-info">
|
||||
<text class="item-label">余额支付金额</text>
|
||||
<text class="item-value">{{ stats.balancePay }}</text>
|
||||
<text class="item-label">全站余额存量</text>
|
||||
<text class="item-value">¥{{ stats.balancePay }}</text>
|
||||
<view class="trend-row">
|
||||
<text class="trend-label">环比增长:</text>
|
||||
<text class="trend-value up">5293.00% ▲</text>
|
||||
<text class="trend-label">用户总数:</text>
|
||||
<text class="trend-value">-</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view class="overview-item">
|
||||
<view class="icon-box red-purple"><text class="icon">%</text></view>
|
||||
<view class="item-info">
|
||||
<text class="item-label">支付佣金金额</text>
|
||||
<text class="item-value">{{ stats.commissionPay }}</text>
|
||||
<text class="item-label">佣金总存量</text>
|
||||
<text class="item-value">¥{{ stats.commissionPay }}</text>
|
||||
<view class="trend-row">
|
||||
<text class="trend-label">环比增长:</text>
|
||||
<text class="trend-value">0% -</text>
|
||||
<text class="trend-label">待结算:</text>
|
||||
<text class="trend-value">-</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -133,147 +130,22 @@
|
||||
<view class="icon-box blue-gray"><text class="icon">📦</text></view>
|
||||
<view class="item-info">
|
||||
<text class="item-label">商品退款金额</text>
|
||||
<text class="item-value">{{ stats.refundAmount }}</text>
|
||||
<text class="item-value">¥{{ stats.refundAmount }}</text>
|
||||
<view class="trend-row">
|
||||
<text class="trend-label">环比增长:</text>
|
||||
<text class="trend-value">0% -</text>
|
||||
<text class="trend-label">退款率:</text>
|
||||
<text class="trend-value">-</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 占位使其对齐 -->
|
||||
<view class="overview-item transparent"></view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 概况图表区 -->
|
||||
<view class="overview-chart-section">
|
||||
<view class="overview-chart-legend">
|
||||
<view class="legend-dot blue"><text class="legend-text">营业额</text></view>
|
||||
<view class="legend-dot green"><text class="legend-text">商品支付金额</text></view>
|
||||
<view class="legend-dot gray-blue"><text class="legend-text">购买会员金额</text></view>
|
||||
<view class="legend-dot red"><text class="legend-text">充值金额</text></view>
|
||||
<view class="legend-dot orange"><text class="legend-text">支出金额</text></view>
|
||||
</view>
|
||||
<view class="overview-chart-box">
|
||||
<EChartsView v-if="overviewTrendOption != null" :option="overviewTrendOption" class="main-trend-chart" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 之前的统计卡片区域 -->
|
||||
<view class="stats-section">
|
||||
<!-- 左侧:今日订单金额 -->
|
||||
<view class="stats-card-main">
|
||||
<view class="card-header">
|
||||
<text class="card-title">今日订单金额</text>
|
||||
</view>
|
||||
<view class="card-content">
|
||||
<view class="amount-wrap">
|
||||
<text class="currency">¥</text>
|
||||
<text class="amount-value">0</text>
|
||||
</view>
|
||||
<view class="chart-legend">
|
||||
<view class="legend-item">
|
||||
<view class="dot blue"></view>
|
||||
<text>今天</text>
|
||||
</view>
|
||||
<view class="legend-item">
|
||||
<view class="dot gray"></view>
|
||||
<text>昨天</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="chart-box">
|
||||
<EChartsView v-if="orderAmountOption != null" :option="orderAmountOption" class="stats-chart" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 侧边统计网格 -->
|
||||
<view class="stats-side-grid">
|
||||
<view class="side-column">
|
||||
<!-- 今日订单数 -->
|
||||
<view class="side-stat-card">
|
||||
<view class="card-header">
|
||||
<text class="card-title">今日订单数</text>
|
||||
</view>
|
||||
<view class="card-body">
|
||||
<text class="main-val">0</text>
|
||||
<view class="compare-row">
|
||||
<text class="label">昨日:</text>
|
||||
<text class="val">4</text>
|
||||
</view>
|
||||
<view class="compare-row">
|
||||
<text class="label">日环比:</text>
|
||||
<text class="val down">-100% ▼</text>
|
||||
</view>
|
||||
<view class="mini-chart-placeholder">
|
||||
<view class="blue-line"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 本月订单数 -->
|
||||
<view class="side-stat-card">
|
||||
<view class="card-header">
|
||||
<text class="card-title">本月订单数</text>
|
||||
</view>
|
||||
<view class="card-body">
|
||||
<text class="main-val">12</text>
|
||||
<view class="compare-row">
|
||||
<text class="label">上月:</text>
|
||||
<text class="val">206</text>
|
||||
</view>
|
||||
<view class="compare-row">
|
||||
<text class="label">月环比:</text>
|
||||
<text class="val down">-94% ▼</text>
|
||||
</view>
|
||||
<view class="mini-chart-placeholder">
|
||||
<view class="blue-line"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="side-column">
|
||||
<!-- 今日支付人数 -->
|
||||
<view class="side-stat-card">
|
||||
<view class="card-header">
|
||||
<text class="card-title">今日支付人数</text>
|
||||
</view>
|
||||
<view class="card-body">
|
||||
<text class="main-val">0</text>
|
||||
<view class="compare-row">
|
||||
<text class="label">昨日:</text>
|
||||
<text class="val">4</text>
|
||||
</view>
|
||||
<view class="compare-row">
|
||||
<text class="label">日环比:</text>
|
||||
<text class="val down">-100% ▼</text>
|
||||
</view>
|
||||
<view class="mini-chart-placeholder">
|
||||
<view class="blue-line"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<!-- 本月支付人数 -->
|
||||
<view class="side-stat-card">
|
||||
<view class="card-header">
|
||||
<text class="card-title">本月支付人数</text>
|
||||
</view>
|
||||
<view class="card-body">
|
||||
<text class="main-val">7</text>
|
||||
<view class="compare-row">
|
||||
<text class="label">上月:</text>
|
||||
<text class="val">134</text>
|
||||
</view>
|
||||
<view class="compare-row">
|
||||
<text class="label">月环比:</text>
|
||||
<text class="val down">-94% ▼</text>
|
||||
</view>
|
||||
<view class="mini-chart-placeholder">
|
||||
<view class="blue-line"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="loading" class="chart-loading"><text>统计加载中...</text></view>
|
||||
<EChartsView v-else-if="overviewTrendOption != null" :option="overviewTrendOption" class="main-trend-chart" />
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -281,142 +153,129 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
import { ref, onMounted, reactive } from 'vue'
|
||||
import { ref, onMounted, reactive, computed } from 'vue'
|
||||
import EChartsView from '@/uni_modules/charts/EChartsView.vue'
|
||||
import { fetchFinanceOverview } from '@/services/admin/financeService.uts'
|
||||
import { rpcOrNull } from '@/services/analytics/rpc.uts'
|
||||
import { fetchFinanceOverview, fetchFinanceBillSummary } from '@/services/admin/financeService.uts'
|
||||
import { fetchOrderStats } from '@/services/orderService.uts'
|
||||
|
||||
const dateOptions = ['今天', '昨天', '最近7天', '最近30天', '本月', '本年']
|
||||
const dateOptions = ['最近30天', '最近7天', '本月', '本年']
|
||||
const activeDateTab = ref(0)
|
||||
const loading = ref(false)
|
||||
|
||||
// 响应式统计数据
|
||||
const stats = reactive({
|
||||
revenue: '0.00', // 营业额
|
||||
payAmount: '0.00', // 商品支付金额
|
||||
memberAmount: '0.00', // 购买会员金额
|
||||
rechargeAmount: '0.00', // 充值金额
|
||||
offlineAmount: '0.00', // 线下收银金额
|
||||
expenditure: '0.00', // 支出金额
|
||||
balancePay: '0.00', // 余额支付金额
|
||||
commissionPay: '0.00', // 支付佣金金额
|
||||
refundAmount: '0.00', // 商品退款金额
|
||||
|
||||
// 环比数据 (示例暂留)
|
||||
revenueTrend: '0%',
|
||||
rechargeTrend: '0%'
|
||||
revenue: '0.00',
|
||||
payAmount: '0.00',
|
||||
orderCount: '0',
|
||||
memberAmount: '0.00',
|
||||
rechargeAmount: '0.00',
|
||||
rechargeCount: '0',
|
||||
offlineAmount: '0.00',
|
||||
expenditure: '0.00',
|
||||
extractCount: '0',
|
||||
balancePay: '0.00',
|
||||
commissionPay: '0.00',
|
||||
refundAmount: '0.00',
|
||||
revenueTrend: '-'
|
||||
})
|
||||
|
||||
const orderAmountOption = ref<any>(null)
|
||||
const overviewTrendOption = ref<any>(null)
|
||||
|
||||
/**
|
||||
* 加载统计数据
|
||||
*/
|
||||
const displayDateRange = computed(() : string => {
|
||||
const now = new Date()
|
||||
const start = getStartTime()
|
||||
return `${start.substring(0, 10)} - ${now.toISOString().substring(0, 10)}`
|
||||
})
|
||||
|
||||
function getStartTime() : string {
|
||||
const now = Date.now()
|
||||
let days = 30
|
||||
if (activeDateTab.value == 1) days = 7
|
||||
if (activeDateTab.value == 2) days = 30 // 简化处理
|
||||
if (activeDateTab.value == 3) days = 365
|
||||
return new Date(now - days * 24 * 60 * 60 * 1000).toISOString()
|
||||
}
|
||||
|
||||
async function loadData() {
|
||||
// TODO: 根据 activeDateTab 计算具体的 startTime 和 endTime
|
||||
const startTime = '2026-01-01T00:00:00Z'
|
||||
const endTime = '2026-12-31T23:59:59Z'
|
||||
loading.value = true
|
||||
const endTime = new Date().toISOString()
|
||||
const startTime = getStartTime()
|
||||
|
||||
try {
|
||||
// 1. 获取财务概况 (充值、提现等)
|
||||
const financeRes = await fetchFinanceOverview(startTime, endTime)
|
||||
if (financeRes != null) {
|
||||
stats.rechargeAmount = financeRes.recharge_amount.toFixed(2)
|
||||
// 支出金额暂以提现成功金额为例
|
||||
stats.expenditure = financeRes.extract_amount.toFixed(2)
|
||||
// 1. 获取财务概况
|
||||
const finRes = await fetchFinanceOverview(startTime, endTime)
|
||||
if (finRes != null) {
|
||||
stats.rechargeAmount = finRes.recharge_amount.toFixed(2)
|
||||
stats.rechargeCount = String(finRes.recharge_count)
|
||||
stats.expenditure = finRes.extract_amount.toFixed(2)
|
||||
stats.extractCount = String(finRes.extract_count)
|
||||
stats.balancePay = finRes.total_user_balance.toFixed(2)
|
||||
stats.commissionPay = finRes.total_user_brokerage.toFixed(2)
|
||||
}
|
||||
|
||||
// 2. 获取订单统计 (营业额、退款等)
|
||||
const orderRes = await rpcOrNull('rpc_admin_order_stats', {
|
||||
p_start_time: startTime,
|
||||
p_end_time: endTime
|
||||
} as UTSJSONObject)
|
||||
|
||||
// 2. 获取订单统计
|
||||
const orderRes = await fetchOrderStats(startTime, endTime)
|
||||
if (orderRes != null) {
|
||||
stats.revenue = ((orderRes as any).total_amount ?? 0).toFixed(2)
|
||||
stats.payAmount = stats.revenue // 简单处理
|
||||
stats.refundAmount = ((orderRes as any).refund_amount ?? 0).toFixed(2)
|
||||
stats.revenue = orderRes.total_amount.toFixed(2)
|
||||
stats.payAmount = orderRes.total_amount.toFixed(2)
|
||||
stats.orderCount = String(orderRes.order_count)
|
||||
stats.refundAmount = orderRes.refund_amount.toFixed(2)
|
||||
}
|
||||
|
||||
// 3. 获取趋势数据驱动图表
|
||||
const trendRes = await fetchFinanceBillSummary(startTime, endTime, 'day')
|
||||
updateChart(trendRes)
|
||||
} catch (e) {
|
||||
console.error('Failed to load transaction stats:', e)
|
||||
uni.showToast({ title: '加载统计失败', icon: 'none' })
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
function handleDateTabChange(index : number) {
|
||||
activeDateTab.value = index
|
||||
loadData()
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
loadData()
|
||||
// 延迟初始化图表确保容器就位
|
||||
setTimeout(() => {
|
||||
initCharts()
|
||||
}, 300)
|
||||
})
|
||||
|
||||
function initCharts() {
|
||||
// 模拟趋势数据
|
||||
const todayData = [120, 132, 101, 134, 90, 230, 210]
|
||||
const yesterdayData = [220, 182, 191, 234, 290, 330, 310]
|
||||
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 updateChart(data : any[]) {
|
||||
const dates = data.map(item => item.date_group.substring(5))
|
||||
const incomes = data.map(item => item.income)
|
||||
const expenses = data.map(item => item.expense)
|
||||
|
||||
const option = {
|
||||
grid: { left: '3%', right: '4%', bottom: '3%', top: '5%', containLabel: true },
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
lineStyle: { color: '#1890ff', type: 'dashed' }
|
||||
}
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: ['00:00', '04:00', '08:00', '12:00', '16:00', '20:00', '23:59'],
|
||||
axisLine: { lineStyle: { color: '#f0f0f0' } },
|
||||
axisLabel: { color: '#999' }
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
splitLine: { show: false },
|
||||
axisLine: { show: false },
|
||||
axisLabel: { color: '#999' }
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '今天',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
data: todayData,
|
||||
lineStyle: { color: '#1890ff', width: 2 },
|
||||
areaStyle: {
|
||||
color: {
|
||||
type: 'linear',
|
||||
x: 0, y: 0, x2: 0, y2: 1,
|
||||
colorStops: [
|
||||
{ offset: 0, color: 'rgba(24, 144, 255, 0.2)' },
|
||||
{ offset: 1, color: 'rgba(24, 144, 255, 0)' }
|
||||
]
|
||||
}
|
||||
},
|
||||
itemStyle: { color: '#1890ff' }
|
||||
},
|
||||
{
|
||||
name: '昨天',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
data: yesterdayData,
|
||||
lineStyle: { color: '#d9d9d9', width: 2, type: 'dashed' },
|
||||
itemStyle: { color: '#d9d9d9' }
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
// 核心修复:传递给 ECharts 的 Option 必须是 Plain Object
|
||||
orderAmountOption.value = toPlainObject(option)
|
||||
|
||||
// 初始化交易概况趋势图 (多曲线)
|
||||
const overviewOption = {
|
||||
grid: { left: '3%', right: '4%', bottom: '10%', top: '5%', containLabel: true },
|
||||
tooltip: { trigger: 'axis' },
|
||||
legend: { data: ['收入', '支出'], bottom: 0 },
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
boundaryGap: false,
|
||||
data: ['2026-01-05', '01-10', '01-15', '01-20', '01-25', '01-31', '02-03'],
|
||||
data: dates,
|
||||
axisLine: { lineStyle: { color: '#f0f0f0' } },
|
||||
axisLabel: { color: '#999', fontSize: 10 }
|
||||
},
|
||||
@@ -428,29 +287,24 @@ function initCharts() {
|
||||
},
|
||||
series: [
|
||||
{
|
||||
name: '营业额',
|
||||
name: '收入',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
data: [1000, 5000, 20000, 15000, 80000, 20000, 5000],
|
||||
itemStyle: { color: '#1890ff' }
|
||||
data: incomes,
|
||||
itemStyle: { color: '#1890ff' },
|
||||
areaStyle: { color: 'rgba(24, 144, 255, 0.1)' }
|
||||
},
|
||||
{
|
||||
name: '商品支付金额',
|
||||
name: '支出',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
data: [800, 4000, 18000, 12000, 75000, 18000, 4000],
|
||||
itemStyle: { color: '#52c41a' }
|
||||
},
|
||||
{
|
||||
name: '支出金额',
|
||||
type: 'line',
|
||||
smooth: true,
|
||||
data: [500, 3000, 15000, 10000, 90000, 15000, 3000],
|
||||
itemStyle: { color: '#fa8c16' }
|
||||
data: expenses,
|
||||
itemStyle: { color: '#fa8c16' },
|
||||
areaStyle: { color: 'rgba(250, 140, 22, 0.1)' }
|
||||
}
|
||||
]
|
||||
}
|
||||
overviewTrendOption.value = toPlainObject(overviewOption)
|
||||
overviewTrendOption.value = toPlainObject(option)
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -461,7 +315,6 @@ function initCharts() {
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
/* 头部筛选 */
|
||||
.header-filters {
|
||||
background: #fff;
|
||||
padding: 12px 20px;
|
||||
@@ -487,15 +340,8 @@ function initCharts() {
|
||||
border-right: 1px solid #d9d9d9;
|
||||
cursor: pointer;
|
||||
|
||||
&:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
&.active {
|
||||
background-color: #1890ff;
|
||||
color: #fff;
|
||||
border-color: #1890ff;
|
||||
}
|
||||
&:last-child { border-right: none; }
|
||||
&.active { background-color: #1890ff; color: #fff; border-color: #1890ff; }
|
||||
}
|
||||
|
||||
.date-picker-wrap {
|
||||
@@ -503,167 +349,17 @@ function initCharts() {
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 4px 12px;
|
||||
border: 1px solid #d9d9d9;
|
||||
border: 1px solid #dcdfe6;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.calendar-icon {
|
||||
margin-right: 8px;
|
||||
font-size: 14px;
|
||||
}
|
||||
.calendar-icon { margin-right: 8px; font-size: 14px; }
|
||||
.date-range-text { font-size: 14px; color: #333; }
|
||||
|
||||
.date-range-text {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
/* 统计区域布局 */
|
||||
.stats-section {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.stats-card-main {
|
||||
flex: 3;
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
padding: 24px;
|
||||
min-height: 380px;
|
||||
}
|
||||
|
||||
.card-title {
|
||||
font-size: 16px;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
margin-bottom: 20px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.amount-wrap {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.currency {
|
||||
font-size: 24px;
|
||||
color: #333;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.amount-value {
|
||||
font-size: 40px;
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.chart-legend {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.legend-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
margin-right: 20px;
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
margin-right: 8px;
|
||||
|
||||
&.blue { background-color: #1890ff; }
|
||||
&.gray { background-color: #d9d9d9; }
|
||||
}
|
||||
|
||||
.chart-box {
|
||||
width: 100%;
|
||||
height: 220px;
|
||||
margin-top: 20px;
|
||||
}
|
||||
|
||||
.stats-chart {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* 侧边网格 */
|
||||
.stats-side-grid {
|
||||
flex: 2;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.side-column {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 16px;
|
||||
}
|
||||
|
||||
.side-stat-card {
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
padding: 24px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.main-val {
|
||||
font-size: 32px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 20px;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.compare-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin-bottom: 8px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.compare-row .label {
|
||||
color: #999;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.compare-row .val {
|
||||
color: #333;
|
||||
|
||||
&.down { color: #52c41a; }
|
||||
&.up { color: #f5222d; }
|
||||
}
|
||||
|
||||
.mini-chart-placeholder {
|
||||
margin-top: 20px;
|
||||
height: 2px;
|
||||
background-color: #f0f0f0;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.blue-line {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 60%;
|
||||
height: 100%;
|
||||
background-color: #1890ff;
|
||||
}
|
||||
|
||||
/* 交易概况复刻样式 */
|
||||
.overview-card {
|
||||
background: #fff;
|
||||
border-radius: 4px;
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.overview-header {
|
||||
@@ -674,72 +370,22 @@ function initCharts() {
|
||||
margin-bottom: 24px;
|
||||
}
|
||||
|
||||
.overview-header .header-left {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.header-left { display: flex; flex-direction: row; align-items: center; }
|
||||
.section-title { font-size: 16px; font-weight: bold; color: #333; }
|
||||
.info-tag {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-radius: 50%;
|
||||
background: #eee;
|
||||
color: #999;
|
||||
font-size: 11px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.overview-header .header-right {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.date-picker-inline {
|
||||
border: 1px solid #dcdfe6;
|
||||
padding: 5px 15px;
|
||||
border-radius: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.date-text {
|
||||
font-size: 13px;
|
||||
color: #606266;
|
||||
width: 16px; height: 16px; border-radius: 50%;
|
||||
background: #eee; color: #999; font-size: 11px;
|
||||
display: flex; align-items: center; justify-content: center; margin-left: 8px;
|
||||
}
|
||||
|
||||
.header-right { display: flex; flex-direction: row; gap: 12px; }
|
||||
.btn-query, .btn-export {
|
||||
padding: 0 16px;
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
font-size: 13px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
padding: 0 16px; height: 32px; line-height: 32px;
|
||||
font-size: 13px; border-radius: 4px; cursor: pointer;
|
||||
}
|
||||
.btn-query { background-color: #1890ff; color: #fff; border: none; }
|
||||
.btn-export { background: #fff; color: #1890ff; border: 1px solid #1890ff; }
|
||||
|
||||
.btn-query {
|
||||
background-color: #1890ff;
|
||||
color: #fff;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.btn-export {
|
||||
background: #fff;
|
||||
color: #1890ff;
|
||||
border: 1px solid #1890ff;
|
||||
}
|
||||
|
||||
/* 指标网格 */
|
||||
.overview-grid {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -748,36 +394,14 @@ function initCharts() {
|
||||
border-bottom: 1px dashed #f0f0f0;
|
||||
}
|
||||
|
||||
.grid-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.overview-item {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.overview-item.transparent {
|
||||
visibility: hidden;
|
||||
}
|
||||
.grid-row { display: flex; flex-direction: row; justify-content: space-between; }
|
||||
.overview-item { flex: 1; display: flex; flex-direction: row; align-items: center; }
|
||||
.overview-item.transparent { visibility: hidden; }
|
||||
|
||||
.icon-box {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.icon-box .icon {
|
||||
font-size: 20px;
|
||||
color: #fff;
|
||||
width: 44px; height: 44px; border-radius: 50%;
|
||||
display: flex; align-items: center; justify-content: center; margin-right: 12px;
|
||||
text { color: #fff; font-size: 20px; }
|
||||
}
|
||||
|
||||
.icon-box.blue { background-color: #2f54eb; }
|
||||
@@ -790,74 +414,14 @@ function initCharts() {
|
||||
.icon-box.red-purple { background-color: #eb2f96; }
|
||||
.icon-box.blue-gray { background-color: #4096ff; }
|
||||
|
||||
.item-info {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.item-info { display: flex; flex-direction: column; }
|
||||
.item-label { font-size: 13px; color: #999; margin-bottom: 4px; }
|
||||
.item-value { font-size: 22px; font-weight: bold; color: #333; margin-bottom: 4px; }
|
||||
.trend-row { display: flex; flex-direction: row; font-size: 12px; color: #999; }
|
||||
.trend-value { margin-left: 4px; }
|
||||
|
||||
.item-label {
|
||||
font-size: 13px;
|
||||
color: #999;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.item-value {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.trend-row {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.trend-value.up {
|
||||
color: #f5222d;
|
||||
margin-left: 4px;
|
||||
}
|
||||
|
||||
/* 图表区 */
|
||||
.overview-chart-section {
|
||||
padding-top: 24px;
|
||||
}
|
||||
|
||||
.overview-chart-legend {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 16px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.legend-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
}
|
||||
|
||||
.legend-dot.blue { background: #1890ff; }
|
||||
.legend-dot.green { background: #52c41a; }
|
||||
.legend-dot.gray-blue { background: #607d8b; }
|
||||
.legend-dot.red { background: #f44336; }
|
||||
.legend-dot.orange { background: #fa8c16; }
|
||||
|
||||
.legend-text {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.overview-chart-box {
|
||||
width: 100%;
|
||||
height: 400px;
|
||||
}
|
||||
|
||||
.main-trend-chart {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.overview-chart-section { padding-top: 24px; }
|
||||
.overview-chart-box { width: 100%; height: 350px; }
|
||||
.main-trend-chart { width: 100%; height: 100%; }
|
||||
.chart-loading { height: 100%; display: flex; align-items: center; justify-content: center; color: #999; }
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user