修改页面结构
This commit is contained in:
@@ -1,531 +1,24 @@
|
||||
<template>
|
||||
<AdminLayout currentPage="home">
|
||||
<view class="dashboard-page">
|
||||
<!-- 第一行:4 个 KPI 卡片 -->
|
||||
<view class="kpi-cards-row">
|
||||
<KpiMiniCard
|
||||
title="销售额"
|
||||
tagText="今日"
|
||||
valuePrefix="¥"
|
||||
:valueText="String(formatNumber(kpiData.sales.today))"
|
||||
:metaLeft="`昨日 ${formatNumber(kpiData.sales.yesterday)}`"
|
||||
:metaRight="`日环比 ${Math.abs(kpiData.sales.change)}%`"
|
||||
:trend="kpiData.sales.change > 0 ? 'up' : (kpiData.sales.change < 0 ? 'down' : 'flat')"
|
||||
:footerLeftText="'本月销售额'"
|
||||
:footerRightText="`¥${formatNumber(kpiData.sales.monthTotal)}`"
|
||||
/>
|
||||
|
||||
<KpiMiniCard
|
||||
title="用户访问量"
|
||||
tagText="今日"
|
||||
:valueText="String(formatNumber(kpiData.visits.today))"
|
||||
:metaLeft="`昨日 ${formatNumber(kpiData.visits.yesterday)}`"
|
||||
:metaRight="`日环比 ${Math.abs(kpiData.visits.change)}%`"
|
||||
:trend="kpiData.visits.change > 0 ? 'up' : (kpiData.visits.change < 0 ? 'down' : 'flat')"
|
||||
footerLeftText="本月访问量"
|
||||
:footerRightText="`${formatNumber(kpiData.visits.monthTotal)}Pv`"
|
||||
/>
|
||||
|
||||
<KpiMiniCard
|
||||
title="订单量"
|
||||
tagText="今日"
|
||||
:valueText="String(formatNumber(kpiData.orders.today))"
|
||||
:metaLeft="`昨日 ${formatNumber(kpiData.orders.yesterday)}`"
|
||||
:metaRight="`日环比 ${Math.abs(kpiData.orders.change)}%`"
|
||||
:trend="kpiData.orders.change > 0 ? 'up' : (kpiData.orders.change < 0 ? 'down' : 'flat')"
|
||||
footerLeftText="本月订单量"
|
||||
:footerRightText="`${formatNumber(kpiData.orders.monthTotal)}单`"
|
||||
/>
|
||||
|
||||
<KpiMiniCard
|
||||
title="新增用户"
|
||||
tagText="今日"
|
||||
:valueText="String(formatNumber(kpiData.users.today))"
|
||||
:metaLeft="`昨日 ${formatNumber(kpiData.users.yesterday)}`"
|
||||
:metaRight="`日环比 ${Math.abs(kpiData.users.change)}%`"
|
||||
:trend="kpiData.users.change > 0 ? 'up' : (kpiData.users.change < 0 ? 'down' : 'flat')"
|
||||
footerLeftText="本月新增用户"
|
||||
:footerRightText="`${formatNumber(kpiData.users.monthTotal)}人`"
|
||||
/>
|
||||
</view>
|
||||
|
||||
|
||||
<!-- 第二行:订单统计图表 -->
|
||||
<view class="chart-section">
|
||||
<view class="admin-card">
|
||||
<view class="admin-card-header">
|
||||
<view class="header-left">
|
||||
<view class="title-icon">
|
||||
<!-- 不用 emoji,纯样式画一个“图表感”的小方块 -->
|
||||
<view class="title-icon-mark"></view>
|
||||
</view>
|
||||
<text class="admin-card-title">订单</text>
|
||||
</view>
|
||||
|
||||
<view class="chart-controls">
|
||||
<view
|
||||
v-for="p in chartPeriods"
|
||||
:key="p.value"
|
||||
class="seg-btn"
|
||||
:class="{ active: selectedPeriod === p.value }"
|
||||
@click="changePeriod(p.value)"
|
||||
>
|
||||
<text class="seg-btn-text">{{ p.label }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<view class="admin-card-body">
|
||||
<!-- 图表容器:你后面接 ECharts / uCharts 都挂这里 -->
|
||||
<view class="echarts-container">
|
||||
<!-- 先空着也行;不要放 emoji 占位符 -->
|
||||
111
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 第三行:用户统计图表 -->
|
||||
<view class="charts-row">
|
||||
<!-- 用户趋势折线图 -->
|
||||
<view class="chart-col">
|
||||
<view class="admin-card">
|
||||
<view class="admin-card-header">
|
||||
<text class="admin-card-title">用户趋势</text>
|
||||
</view>
|
||||
<view class="admin-card-body">
|
||||
<view class="echarts-container">
|
||||
<text class="chart-placeholder">📈 ECharts 折线图:用户增长趋势</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 用户构成饼图 -->
|
||||
<view class="chart-col">
|
||||
<view class="admin-card">
|
||||
<view class="admin-card-header">
|
||||
<text class="admin-card-title">用户构成</text>
|
||||
</view>
|
||||
<view class="admin-card-body">
|
||||
<view class="echarts-container">
|
||||
<text class="chart-placeholder">🥧 ECharts 饼图:用户来源分布</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</AdminLayout>
|
||||
<!-- 管理后台入口:直接加载 AdminLayout,使用 CRMEB 内部路由系统 -->
|
||||
<AdminLayout />
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
import { ref } from 'vue'
|
||||
/**
|
||||
* 管理后台入口页面
|
||||
*
|
||||
* 架构说明:
|
||||
* 1. 此页面是 pages.json 中配置的主入口
|
||||
* 2. 直接加载 AdminLayout 组件作为容器
|
||||
* 3. AdminLayout 内部使用 CRMEB 路由系统管理所有子页面
|
||||
* 4. 不需要额外的业务逻辑,保持简洁
|
||||
*
|
||||
* 路由流程:
|
||||
* pages.json → homePage/index.uvue → AdminLayout → 内部路由切换
|
||||
*/
|
||||
import AdminLayout from '@/layouts/admin/AdminLayout.uvue'
|
||||
import KpiMiniCard from './components/KpiMiniCard.uvue'
|
||||
|
||||
// KPI 数据
|
||||
const kpiData = ref({
|
||||
sales: {
|
||||
today: 125680.50,
|
||||
yesterday: 118920.30,
|
||||
monthTotal: 2857808.90,
|
||||
change: 5.7
|
||||
},
|
||||
visits: {
|
||||
today: 15420,
|
||||
yesterday: 14890,
|
||||
monthTotal: 342680,
|
||||
change: 3.4
|
||||
},
|
||||
orders: {
|
||||
today: 342,
|
||||
yesterday: 318,
|
||||
monthTotal: 8956,
|
||||
change: 7.5
|
||||
},
|
||||
users: {
|
||||
today: 156,
|
||||
yesterday: 142,
|
||||
monthTotal: 3245,
|
||||
change: 9.9
|
||||
}
|
||||
})
|
||||
|
||||
// 图表配置
|
||||
const selectedPeriod = ref('30days')
|
||||
const selectedPeriodLabel = computed((): string => {
|
||||
const hit = chartPeriods.value.find((x) => x.value === selectedPeriod.value)
|
||||
return hit ? hit.label : ""
|
||||
})
|
||||
|
||||
const chartPeriods = [
|
||||
{ label: '30天', value: '30days' },
|
||||
{ label: '周', value: 'week' },
|
||||
{ label: '月', value: 'month' },
|
||||
{ label: '年', value: 'year' }
|
||||
]
|
||||
|
||||
type PeriodItem = {
|
||||
label: string
|
||||
value: string
|
||||
}
|
||||
|
||||
|
||||
// 方法
|
||||
const formatNumber = (num: number) => {
|
||||
if (num >= 10000) {
|
||||
return (num / 10000).toFixed(1) + '万'
|
||||
} else if (num >= 1000) {
|
||||
return (num / 1000).toFixed(1) + 'k'
|
||||
}
|
||||
return num.toString()
|
||||
}
|
||||
|
||||
const changePeriod = (period: string) => {
|
||||
selectedPeriod.value = period
|
||||
const periodMap: Record<string, string> = {
|
||||
'30days': '30天',
|
||||
'week': '周',
|
||||
'month': '月',
|
||||
'year': '年'
|
||||
}
|
||||
selectedPeriodLabel.value = periodMap[period] || '30天'
|
||||
|
||||
// TODO: 重新加载图表数据
|
||||
console.log('切换时间粒度:', period)
|
||||
}
|
||||
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
/* ===== Dashboard 页面样式 ===== */
|
||||
.dashboard-page {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
/* ===== KPI 卡片行 ===== */
|
||||
/* 第一行:4 个 KPI 卡片一行 */
|
||||
.kpi-cards-row{
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, minmax(0, 1fr)); /* 一行 4 列等分 */
|
||||
gap: 16px;
|
||||
align-items: stretch;
|
||||
}
|
||||
|
||||
/* 卡片本体:不要写死宽高 */
|
||||
.kpi-card{
|
||||
|
||||
background-color: #ffffff;
|
||||
border: 1px solid #e8e8e8;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.06);
|
||||
position: relative;
|
||||
overflow: hidden;
|
||||
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
width: 100%;
|
||||
height: 200px; /* 你可以改成 140/160,别写死 200px */
|
||||
box-sizing: border-box;
|
||||
margin-bottom: 20rpx;
|
||||
min-width: 200rpx;
|
||||
}
|
||||
|
||||
/* 响应式:宽度不够时变 2 列 / 1 列(可选) */
|
||||
@media (max-width: 1200px){
|
||||
.kpi-cards-row{ grid-template-columns: repeat(2, minmax(0, 1fr)); }
|
||||
}
|
||||
@media (max-width: 768px){
|
||||
.kpi-cards-row{ grid-template-columns: 1fr; }
|
||||
}
|
||||
|
||||
|
||||
.kpi-card-content {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.kpi-card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 12px;
|
||||
}
|
||||
|
||||
.kpi-card-title {
|
||||
position: absolute;
|
||||
|
||||
top: 10rpx;
|
||||
left: 5rpx;
|
||||
font-size: 16px;
|
||||
color: #666666;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.kpi-card-tag {
|
||||
background-color: #1890ff;
|
||||
padding: 2px 8px;
|
||||
border-radius: 12px;
|
||||
}
|
||||
|
||||
.kpi-tag-text {
|
||||
font-size: 12px;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.kpi-card-value {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.kpi-value-number {
|
||||
font-size: 32px;
|
||||
font-weight: 600;
|
||||
color: #262626;
|
||||
margin-right: 16px;
|
||||
}
|
||||
|
||||
.kpi-value-trend {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 14px;
|
||||
border-radius: 12px;
|
||||
padding: 4px 8px;
|
||||
}
|
||||
|
||||
.kpi-value-trend.up {
|
||||
background-color: #f6ffed;
|
||||
color: #52c41a;
|
||||
}
|
||||
|
||||
.kpi-value-trend.down {
|
||||
background-color: #fff2f0;
|
||||
color: #ff4d4f;
|
||||
}
|
||||
|
||||
.kpi-trend-text {
|
||||
margin-left: 4px;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.kpi-card-footer {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.kpi-footer-text {
|
||||
font-size: 14px;
|
||||
color: #999999;
|
||||
}
|
||||
|
||||
.kpi-card-icon {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
background: linear-gradient(135deg, #1890ff 0%, #36cfc9 100%);
|
||||
border-radius: 50%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #ffffff;
|
||||
font-size: 32px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
/* ===== 图表区域 ===== */
|
||||
|
||||
|
||||
/* 卡片外观 */
|
||||
.admin-card {
|
||||
background: #ffffff;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
/* 头部:左标题 + 右分段按钮(不换行) */
|
||||
.admin-card-header {
|
||||
padding: 16px 24px 12px 24px;
|
||||
display: flex;
|
||||
flex-direction: row;;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
flex-wrap: nowrap; /* 防止被挤下去 */
|
||||
}
|
||||
|
||||
.header-left {
|
||||
display: flex;
|
||||
flex-direction: row;;
|
||||
align-items: center;
|
||||
gap: 10px;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.title-icon {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
border-radius: 14px;
|
||||
background: #e6f4ff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.title-icon-mark {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
border-radius: 4px;
|
||||
background: #1677ff;
|
||||
}
|
||||
|
||||
.admin-card-title {
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
color: #262626;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* 分段控件:一整条外框 + 内部分段(完全贴近你第二张图右上角) */
|
||||
.chart-controls {
|
||||
display: flex;
|
||||
flex-direction: row;;
|
||||
align-items: center;
|
||||
justify-content: center;;
|
||||
border: 1px solid #d9d9d9;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
background: #ffffff;
|
||||
flex-shrink: 0; /* 防止被压缩换行 */
|
||||
}
|
||||
|
||||
.seg-btn {
|
||||
height: 32px;
|
||||
min-width: 44px;
|
||||
padding: 0 14px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-left: 1px solid #d9d9d9;
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.seg-btn:first-child {
|
||||
border-left: 0;
|
||||
}
|
||||
|
||||
.seg-btn-text {
|
||||
font-size: 14px;
|
||||
color: #262626;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.seg-btn.active {
|
||||
background: #1677ff;
|
||||
}
|
||||
|
||||
.seg-btn.active .seg-btn-text {
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
/* ✅ 注意:body 是 header 的兄弟,不要写进 header 嵌套里 */
|
||||
.admin-card-body {
|
||||
padding: 0 24px 16px 24px;
|
||||
}
|
||||
|
||||
.echarts-container {
|
||||
width: 100%;
|
||||
height: 300px; /* 贴近截图比例 */
|
||||
}
|
||||
|
||||
.charts-row{
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
gap: 16px;
|
||||
margin-top: 16px;
|
||||
}
|
||||
/* 每个图表列容器 */
|
||||
.chart-col{
|
||||
min-width: 0; /* 防止 ECharts/SVG 内容把列撑爆 */
|
||||
}
|
||||
|
||||
/* ===== 响应式设计 ===== */
|
||||
@media (max-width: 1200px) {
|
||||
|
||||
.kpi-card {
|
||||
min-width: 45%;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
|
||||
.charts-row{
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.kpi-cards-row {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.kpi-card {
|
||||
min-width: auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
.dashboard-page {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.kpi-cards-row,
|
||||
.chart-section,
|
||||
.charts-row {
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
.kpi-card {
|
||||
padding: 16px;
|
||||
}
|
||||
|
||||
.admin-card-header,
|
||||
.admin-card-body {
|
||||
padding-left: 16px;
|
||||
padding-right: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== 图标字体 ===== */
|
||||
.iconfont {
|
||||
font-family: 'iconfont';
|
||||
font-size: 16px;
|
||||
font-style: normal;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icon-up:before {
|
||||
content: '↑';
|
||||
}
|
||||
|
||||
.icon-down:before {
|
||||
content: '↓';
|
||||
}
|
||||
|
||||
.icon-sales:before {
|
||||
content: '💰';
|
||||
}
|
||||
|
||||
.icon-visits:before {
|
||||
content: '👁️';
|
||||
}
|
||||
|
||||
.icon-orders:before {
|
||||
content: '📦';
|
||||
}
|
||||
|
||||
.icon-users:before {
|
||||
content: '👥';
|
||||
}
|
||||
</style>
|
||||
<style scoped>
|
||||
/* 无需额外样式,完全由 AdminLayout 控制布局 */
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user