refactor(shop-dashboard): 调整商城后台Dashboard布局及面板结构
- 基本信息面板改为左右两列并排显示,响应式列数调整为2列 - 今日数据概况与基本信息合并至同一行,分别占用半宽空间 - 今日数据概况面板栅格调整为2列,样式和间距优化 - 删除快捷操作面板及相关图标导入和缓存清理功能 - 快速入口功能调整为动态渲染卡片布局取代原按键列表 - 优化部分样式,如待办任务列表的内边距和字体大小调整 - 头像显示增加默认图片占位,避免空白显示
This commit is contained in:
@@ -29,3 +29,20 @@
|
||||
1. 后端:`cd /Users/gxwebsoft/JAVA/websopy-java && git push`,然后远程服务器 `git pull && mvn clean package -DskipTests && docker-compose up -d --build cms-api`(或对应重启方式)。
|
||||
2. 小程序端:`websopy-taro` 重新编译上传体验版/正式版。
|
||||
3. 联调验证:admin 点"立即续费" → 弹码 → 微信扫码 → 进小程序应显示"确认支付"页(不再误显成功)→ 点"立即支付" → 微信支付 → 显示"支付成功" → 跳转 `/user/apps/index`。
|
||||
|
||||
## 商城后台 Dashboard 布局调整
|
||||
|
||||
- 文件:`src/views/shop/dashboard/index.vue`
|
||||
- 需求:今日数据概况页,「基本信息」改为左右两列并排;删除「快捷操作」面板。
|
||||
- 改动:
|
||||
- `a-descriptions` 由 `:column="1"` 改为 `:column="{ xs: 1, sm: 2 }"`(响应式两列并排)。
|
||||
- 基本信息 `a-col` 由 `:span="12"` 改为 `:span="24"`(占满整行)。
|
||||
- 删除「快捷操作」`a-col` 整块,并清理其专属的图标导入(ShoppingCartOutlined/ShopOutlined/AppstoreOutlined/GiftOutlined/TeamOutlined/SettingOutlined/ClearOutlined)、`removeSiteInfoCache` 导入及 `handleClearCache` 函数。
|
||||
|
||||
### 今日数据概况 + 基本信息改为左右两栏布局
|
||||
|
||||
- 需求:参考「待处理事项 + 快速入口」的左右结构,将「今日数据概况」和「基本信息」也并排为左右各半。
|
||||
- 改动:
|
||||
- 原来各自独占整行的两个 panel,合并到一个 `<a-row>` 中,各占 `:md="12"`。
|
||||
- 今日数据概况栅格从 4 列改为 2 列(`grid-template-columns: repeat(2, 1fr)`),适配半宽空间。
|
||||
- 基本信息 `a-descriptions` 列数改为 `:column="1"`,半宽下单列更不拥挤。
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
<div class="welcome-banner">
|
||||
<div class="welcome-left flex flex-row gap-2 items-center">
|
||||
<a-avatar
|
||||
:src="shopLogoUrl"
|
||||
:src="shopLogoUrl || 'https://oss.wsdns.cn/20240822/0252ad4ed46449cdafe12f8d3d96c2ea.svg'"
|
||||
:size="64"
|
||||
shape="square"
|
||||
class="welcome-logo"
|
||||
@@ -25,7 +25,6 @@
|
||||
<a-button size="small" :disabled="!notifyEnabled" @click="onTestNotify">测试</a-button>
|
||||
</div>
|
||||
<a-space>
|
||||
<a-tag color="green" style="font-size:13px;padding:4px 12px">{{ siteStore.statusText || '正常' }}</a-tag>
|
||||
<a-button size="small" @click="refreshStatistics" :loading="loading">
|
||||
<template #icon><ReloadOutlined /></template>
|
||||
刷新数据
|
||||
@@ -64,11 +63,11 @@
|
||||
</div>
|
||||
<div class="todo-list">
|
||||
<div
|
||||
v-for="todo in todoItems"
|
||||
:key="todo.label"
|
||||
class="todo-item"
|
||||
:class="{ 'todo-item-urgent': todo.urgent }"
|
||||
@click="navigateTo(todo.to)"
|
||||
v-for="todo in todoItems"
|
||||
:key="todo.label"
|
||||
class="todo-item"
|
||||
:class="{ 'todo-item-urgent': todo.urgent }"
|
||||
@click="navigateTo(todo.to)"
|
||||
>
|
||||
<div class="todo-dot" :class="todo.dotColor"></div>
|
||||
<div class="todo-content">
|
||||
@@ -80,82 +79,63 @@
|
||||
</div>
|
||||
<RightOutlined class="todo-arrow" />
|
||||
</div>
|
||||
<div v-if="!loading && todoItems.every(t => t.value === 0)" class="todo-empty">
|
||||
🎉 暂无待处理事项,一切正常!
|
||||
</div>
|
||||
<!-- <div v-if="!loading && todoItems.every(t => t.value === 0)" class="todo-empty">-->
|
||||
<!-- 🎉 暂无待处理事项,一切正常!-->
|
||||
<!-- </div>-->
|
||||
</div>
|
||||
</div>
|
||||
</a-col>
|
||||
|
||||
<!-- 快速入口 -->
|
||||
<!-- 今日数据概况 -->
|
||||
<a-col :xs="24" :md="12">
|
||||
<div class="panel">
|
||||
<div class="panel-header">
|
||||
<span class="panel-title">⚡ 快速入口</span>
|
||||
<span class="panel-title">📊 今日数据概况</span>
|
||||
</div>
|
||||
<div class="quick-grid">
|
||||
<div
|
||||
v-for="item in quickLinks"
|
||||
:key="item.to"
|
||||
class="quick-card"
|
||||
@click="navigateTo(item.to)"
|
||||
>
|
||||
<div class="quick-icon" :style="{ background: item.bg }">
|
||||
{{ item.icon }}
|
||||
<span v-if="item.badge" class="quick-icon-dot"></span>
|
||||
<div class="today-stats">
|
||||
<div class="today-stat">
|
||||
<div class="today-stat-icon">💰</div>
|
||||
<div class="today-stat-info">
|
||||
<div class="today-stat-value">{{ formatMoney(todayStats.salesAmount) }}</div>
|
||||
<div class="today-stat-label">今日销售额(元)</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="quick-label">{{ item.label }}</div>
|
||||
<div class="today-stat">
|
||||
<div class="today-stat-icon">📦</div>
|
||||
<div class="today-stat-info">
|
||||
<div class="today-stat-value">{{ todayStats.orderCount || 0 }}</div>
|
||||
<div class="today-stat-label">今日订单数</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="today-stat">
|
||||
<div class="today-stat-icon">👥</div>
|
||||
<div class="today-stat-info">
|
||||
<div class="today-stat-value">{{ todayStats.newUsers || 0 }}</div>
|
||||
<div class="today-stat-label">新增用户</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="today-stat">
|
||||
<div class="today-stat-icon">🎁</div>
|
||||
<div class="today-stat-info">
|
||||
<div class="today-stat-value">{{ todayStats.couponUsed || 0 }}</div>
|
||||
<div class="today-stat-label">使用优惠券</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-col>
|
||||
</a-row>
|
||||
|
||||
<!-- 今日数据趋势 -->
|
||||
<div class="panel">
|
||||
<div class="panel-header">
|
||||
<span class="panel-title">📊 今日数据概况</span>
|
||||
</div>
|
||||
<div class="today-stats">
|
||||
<div class="today-stat">
|
||||
<div class="today-stat-icon">💰</div>
|
||||
<div class="today-stat-info">
|
||||
<div class="today-stat-value">{{ formatMoney(todayStats.salesAmount) }}</div>
|
||||
<div class="today-stat-label">今日销售额(元)</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="today-stat">
|
||||
<div class="today-stat-icon">📦</div>
|
||||
<div class="today-stat-info">
|
||||
<div class="today-stat-value">{{ todayStats.orderCount || 0 }}</div>
|
||||
<div class="today-stat-label">今日订单数</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="today-stat">
|
||||
<div class="today-stat-icon">👥</div>
|
||||
<div class="today-stat-info">
|
||||
<div class="today-stat-value">{{ todayStats.newUsers || 0 }}</div>
|
||||
<div class="today-stat-label">新增用户</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="today-stat">
|
||||
<div class="today-stat-icon">🎁</div>
|
||||
<div class="today-stat-info">
|
||||
<div class="today-stat-value">{{ todayStats.couponUsed || 0 }}</div>
|
||||
<div class="today-stat-label">使用优惠券</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<!-- 今日数据概况 + 基本信息 -->
|
||||
<a-row :gutter="[16, 16]">
|
||||
|
||||
<!-- 系统基本信息 -->
|
||||
<a-row :gutter="[16, 16]" style="margin-top: 16px">
|
||||
<a-col :span="12">
|
||||
<!-- 基本信息 -->
|
||||
<a-col :xs="24" :md="12">
|
||||
<div class="panel">
|
||||
<div class="panel-header">
|
||||
<span class="panel-title">📋 基本信息</span>
|
||||
</div>
|
||||
<div style="padding: 16px 18px;">
|
||||
<div style="padding: 23px 18px;">
|
||||
<a-descriptions :column="1" size="small">
|
||||
<a-descriptions-item label="软件名称">
|
||||
<template v-if="subscriptionLoading">
|
||||
@@ -163,16 +143,16 @@
|
||||
</template>
|
||||
<template v-else>{{ currentProduct?.productName || '-' }}</template>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="版本号">
|
||||
<a-descriptions-item label="当前版本">
|
||||
<template v-if="subscriptionLoading">
|
||||
<a-skeleton-input :active="true" size="small" style="width: 80px" />
|
||||
<a-skeleton-input :active="true" size="small" style="width: 160px" />
|
||||
</template>
|
||||
<template v-else>{{ currentProduct?.version || '-' }}</template>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="运行状态">
|
||||
<a-tag :color="productStatusText.color">{{ productStatusText.text }}</a-tag>
|
||||
<a-descriptions-item label="授权编号">
|
||||
<span>{{ currentSubscription?.subscriptionNo || '-' }}</span>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="创建时间">
|
||||
<a-descriptions-item label="开通时间">
|
||||
<template v-if="subscriptionLoading">
|
||||
<a-skeleton-input :active="true" size="small" style="width: 160px" />
|
||||
</template>
|
||||
@@ -184,7 +164,7 @@
|
||||
</template>
|
||||
<template v-else-if="currentSubscription && currentSubscription.status === 'active' && currentSubscription.expireTime">
|
||||
<span>{{ formatDateTime(currentSubscription.expireTime) }}</span>
|
||||
<a-tag :color="isExpired ? 'red' : 'green'" style="margin-left: 8px">
|
||||
<a-tag v-if="isExpired" :color="isExpired ? 'red' : 'green'" style="margin-left: 8px">
|
||||
{{ isExpired ? '已过期' : '已激活' }}
|
||||
</a-tag>
|
||||
<span v-if="daysToExpire !== null && daysToExpire >= 0 && daysToExpire <= 7" class="expire-warn">
|
||||
@@ -207,62 +187,45 @@
|
||||
<a-button type="link" size="small" @click="onSubscribe" style="padding: 0 0 0 8px">立即订阅</a-button>
|
||||
</template>
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item label="系统运行">
|
||||
<template v-if="subscriptionLoading">
|
||||
<a-skeleton-input :active="true" size="small" style="width: 60px" />
|
||||
</template>
|
||||
<template v-else>
|
||||
<a-tooltip title="自您首次开通小程序商城起">
|
||||
<span>{{ runDays }} 天</span>
|
||||
</a-tooltip>
|
||||
</template>
|
||||
</a-descriptions-item>
|
||||
<!-- <a-descriptions-item label="系统运行">-->
|
||||
<!-- <template v-if="subscriptionLoading">-->
|
||||
<!-- <a-skeleton-input :active="true" size="small" style="width: 60px" />-->
|
||||
<!-- </template>-->
|
||||
<!-- <template v-else>-->
|
||||
<!-- <a-tooltip title="自您首次开通小程序商城起">-->
|
||||
<!-- <span>{{ runDays }} 天</span>-->
|
||||
<!-- </a-tooltip>-->
|
||||
<!-- </template>-->
|
||||
<!-- </a-descriptions-item>-->
|
||||
<!-- <a-descriptions-item label="绑定小程序">-->
|
||||
<!-- {{ currentSubscription?.miniProgramId || 'wx75cbc65759eb981a' }}-->
|
||||
<!-- </a-descriptions-item>-->
|
||||
<!-- <a-descriptions-item label="售后服务">-->
|
||||
<!-- <a href="https://websoft.top/login" class="text-gray-500">提交售后工单</a>-->
|
||||
<!-- </a-descriptions-item>-->
|
||||
</a-descriptions>
|
||||
</div>
|
||||
</div>
|
||||
</a-col>
|
||||
|
||||
<!-- 快捷操作 -->
|
||||
<a-col :span="12">
|
||||
<div class="panel" style="min-height: 353px">
|
||||
<!-- 快速入口 -->
|
||||
<a-col :xs="24" :md="12">
|
||||
<div class="panel">
|
||||
<div class="panel-header">
|
||||
<span class="panel-title">⚡ 快捷操作</span>
|
||||
<span class="panel-title">⚡ 快速入口</span>
|
||||
</div>
|
||||
<div style="padding: 16px 18px;">
|
||||
<a-space direction="vertical" style="width: 100%">
|
||||
<a-button
|
||||
type="primary"
|
||||
block
|
||||
@click="navigateTo('/shop/shopOrder')"
|
||||
>
|
||||
<ShoppingCartOutlined />
|
||||
订单管理
|
||||
</a-button>
|
||||
<a-button block @click="navigateTo('/shop/shopGoods')">
|
||||
<ShopOutlined />
|
||||
商品管理
|
||||
</a-button>
|
||||
<a-button block @click="navigateTo('/shop/shopGoodsCategory')">
|
||||
<AppstoreOutlined />
|
||||
商品分类
|
||||
</a-button>
|
||||
<a-button block @click="navigateTo('/market/coupon')">
|
||||
<GiftOutlined />
|
||||
优惠券管理
|
||||
</a-button>
|
||||
<a-button block @click="navigateTo('/system/user')">
|
||||
<TeamOutlined />
|
||||
会员管理
|
||||
</a-button>
|
||||
<a-button block @click="navigateTo('/shop/shopSetting')">
|
||||
<SettingOutlined />
|
||||
商城设置
|
||||
</a-button>
|
||||
<a-button block @click="handleClearCache">
|
||||
<ClearOutlined />
|
||||
清除缓存
|
||||
</a-button>
|
||||
</a-space>
|
||||
<div class="quick-grid">
|
||||
<div
|
||||
v-for="item in quickLinks"
|
||||
:key="item.to"
|
||||
class="quick-card"
|
||||
@click="navigateTo(item.to)"
|
||||
>
|
||||
<div class="quick-icon" :style="{ background: item.bg }">
|
||||
{{ item.icon }}
|
||||
<span v-if="item.badge" class="quick-icon-dot"></span>
|
||||
</div>
|
||||
<div class="quick-label">{{ item.label }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-col>
|
||||
@@ -350,13 +313,6 @@ import { useOrderNotify } from '@/views/shop/shopOrder/useOrderNotify';
|
||||
import {
|
||||
ReloadOutlined,
|
||||
RightOutlined,
|
||||
ShoppingCartOutlined,
|
||||
ShopOutlined,
|
||||
AppstoreOutlined,
|
||||
GiftOutlined,
|
||||
TeamOutlined,
|
||||
SettingOutlined,
|
||||
ClearOutlined,
|
||||
InfoCircleOutlined,
|
||||
QrcodeOutlined
|
||||
} from '@ant-design/icons-vue';
|
||||
@@ -370,7 +326,6 @@ import { pageShopOrder } from '@/api/shop/shopOrder';
|
||||
import { getShopSettingCategoryValues } from '@/api/shop/shopSetting';
|
||||
import { getCompressedImageUrl } from '@/utils/image';
|
||||
import { storeToRefs } from 'pinia';
|
||||
import { removeSiteInfoCache } from '@/api/cms/cmsWebsite';
|
||||
import { getProductDetail } from '@/api/app/appProduct';
|
||||
import type { AppProduct } from '@/api/app/appProduct/model';
|
||||
import type { AppSubscription } from '@/api/app/appSubscription/model';
|
||||
@@ -769,17 +724,6 @@ const formatMoney = (value?: number | string) => {
|
||||
return Number.isFinite(num) ? num.toFixed(2) : '0.00';
|
||||
};
|
||||
|
||||
// 清除缓存
|
||||
const handleClearCache = () => {
|
||||
removeSiteInfoCache('SiteInfo:' + localStorage.getItem('TenantId')).then(
|
||||
(msg) => {
|
||||
if (msg) {
|
||||
message.success(msg);
|
||||
}
|
||||
}
|
||||
);
|
||||
};
|
||||
|
||||
// 新订单声音提醒(全局单例:轮询由 layout 启动,此处仅消费状态 + UI)
|
||||
const { enabled: notifyEnabled, hasNewOrder, toggle: onNotifyToggle, testNotify: onTestNotify } = useOrderNotify({
|
||||
onNewOrder: () => loadData()
|
||||
@@ -971,7 +915,7 @@ onUnmounted(() => {
|
||||
.panel-title { font-size: 14px; font-weight: 600; color: rgba(0,0,0,0.85); }
|
||||
|
||||
/* 待办 */
|
||||
.todo-list { padding: 8px 0; }
|
||||
.todo-list { padding: 2px 0; }
|
||||
.todo-item {
|
||||
display: flex; align-items: center; gap: 12px;
|
||||
padding: 12px 18px; cursor: pointer; transition: background 0.15s;
|
||||
@@ -1029,9 +973,9 @@ onUnmounted(() => {
|
||||
/* 今日数据 */
|
||||
.today-stats {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 16px;
|
||||
padding: 20px;
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
gap: 12px;
|
||||
padding: 16px;
|
||||
}
|
||||
.today-stat {
|
||||
display: flex;
|
||||
|
||||
Reference in New Issue
Block a user