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`(或对应重启方式)。
|
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` 重新编译上传体验版/正式版。
|
2. 小程序端:`websopy-taro` 重新编译上传体验版/正式版。
|
||||||
3. 联调验证:admin 点"立即续费" → 弹码 → 微信扫码 → 进小程序应显示"确认支付"页(不再误显成功)→ 点"立即支付" → 微信支付 → 显示"支付成功" → 跳转 `/user/apps/index`。
|
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-banner">
|
||||||
<div class="welcome-left flex flex-row gap-2 items-center">
|
<div class="welcome-left flex flex-row gap-2 items-center">
|
||||||
<a-avatar
|
<a-avatar
|
||||||
:src="shopLogoUrl"
|
:src="shopLogoUrl || 'https://oss.wsdns.cn/20240822/0252ad4ed46449cdafe12f8d3d96c2ea.svg'"
|
||||||
:size="64"
|
:size="64"
|
||||||
shape="square"
|
shape="square"
|
||||||
class="welcome-logo"
|
class="welcome-logo"
|
||||||
@@ -25,7 +25,6 @@
|
|||||||
<a-button size="small" :disabled="!notifyEnabled" @click="onTestNotify">测试</a-button>
|
<a-button size="small" :disabled="!notifyEnabled" @click="onTestNotify">测试</a-button>
|
||||||
</div>
|
</div>
|
||||||
<a-space>
|
<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">
|
<a-button size="small" @click="refreshStatistics" :loading="loading">
|
||||||
<template #icon><ReloadOutlined /></template>
|
<template #icon><ReloadOutlined /></template>
|
||||||
刷新数据
|
刷新数据
|
||||||
@@ -64,11 +63,11 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="todo-list">
|
<div class="todo-list">
|
||||||
<div
|
<div
|
||||||
v-for="todo in todoItems"
|
v-for="todo in todoItems"
|
||||||
:key="todo.label"
|
:key="todo.label"
|
||||||
class="todo-item"
|
class="todo-item"
|
||||||
:class="{ 'todo-item-urgent': todo.urgent }"
|
:class="{ 'todo-item-urgent': todo.urgent }"
|
||||||
@click="navigateTo(todo.to)"
|
@click="navigateTo(todo.to)"
|
||||||
>
|
>
|
||||||
<div class="todo-dot" :class="todo.dotColor"></div>
|
<div class="todo-dot" :class="todo.dotColor"></div>
|
||||||
<div class="todo-content">
|
<div class="todo-content">
|
||||||
@@ -80,82 +79,63 @@
|
|||||||
</div>
|
</div>
|
||||||
<RightOutlined class="todo-arrow" />
|
<RightOutlined class="todo-arrow" />
|
||||||
</div>
|
</div>
|
||||||
<div v-if="!loading && todoItems.every(t => t.value === 0)" class="todo-empty">
|
<!-- <div v-if="!loading && todoItems.every(t => t.value === 0)" class="todo-empty">-->
|
||||||
🎉 暂无待处理事项,一切正常!
|
<!-- 🎉 暂无待处理事项,一切正常!-->
|
||||||
</div>
|
<!-- </div>-->
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a-col>
|
</a-col>
|
||||||
|
|
||||||
<!-- 快速入口 -->
|
<!-- 今日数据概况 -->
|
||||||
<a-col :xs="24" :md="12">
|
<a-col :xs="24" :md="12">
|
||||||
<div class="panel">
|
<div class="panel">
|
||||||
<div class="panel-header">
|
<div class="panel-header">
|
||||||
<span class="panel-title">⚡ 快速入口</span>
|
<span class="panel-title">📊 今日数据概况</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="quick-grid">
|
<div class="today-stats">
|
||||||
<div
|
<div class="today-stat">
|
||||||
v-for="item in quickLinks"
|
<div class="today-stat-icon">💰</div>
|
||||||
:key="item.to"
|
<div class="today-stat-info">
|
||||||
class="quick-card"
|
<div class="today-stat-value">{{ formatMoney(todayStats.salesAmount) }}</div>
|
||||||
@click="navigateTo(item.to)"
|
<div class="today-stat-label">今日销售额(元)</div>
|
||||||
>
|
</div>
|
||||||
<div class="quick-icon" :style="{ background: item.bg }">
|
|
||||||
{{ item.icon }}
|
|
||||||
<span v-if="item.badge" class="quick-icon-dot"></span>
|
|
||||||
</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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a-col>
|
</a-col>
|
||||||
</a-row>
|
</a-row>
|
||||||
|
|
||||||
<!-- 今日数据趋势 -->
|
<!-- 今日数据概况 + 基本信息 -->
|
||||||
<div class="panel">
|
<a-row :gutter="[16, 16]">
|
||||||
<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]" style="margin-top: 16px">
|
<a-col :xs="24" :md="12">
|
||||||
<a-col :span="12">
|
|
||||||
<div class="panel">
|
<div class="panel">
|
||||||
<div class="panel-header">
|
<div class="panel-header">
|
||||||
<span class="panel-title">📋 基本信息</span>
|
<span class="panel-title">📋 基本信息</span>
|
||||||
</div>
|
</div>
|
||||||
<div style="padding: 16px 18px;">
|
<div style="padding: 23px 18px;">
|
||||||
<a-descriptions :column="1" size="small">
|
<a-descriptions :column="1" size="small">
|
||||||
<a-descriptions-item label="软件名称">
|
<a-descriptions-item label="软件名称">
|
||||||
<template v-if="subscriptionLoading">
|
<template v-if="subscriptionLoading">
|
||||||
@@ -163,16 +143,16 @@
|
|||||||
</template>
|
</template>
|
||||||
<template v-else>{{ currentProduct?.productName || '-' }}</template>
|
<template v-else>{{ currentProduct?.productName || '-' }}</template>
|
||||||
</a-descriptions-item>
|
</a-descriptions-item>
|
||||||
<a-descriptions-item label="版本号">
|
<a-descriptions-item label="当前版本">
|
||||||
<template v-if="subscriptionLoading">
|
<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>
|
||||||
<template v-else>{{ currentProduct?.version || '-' }}</template>
|
<template v-else>{{ currentProduct?.version || '-' }}</template>
|
||||||
</a-descriptions-item>
|
</a-descriptions-item>
|
||||||
<a-descriptions-item label="运行状态">
|
<a-descriptions-item label="授权编号">
|
||||||
<a-tag :color="productStatusText.color">{{ productStatusText.text }}</a-tag>
|
<span>{{ currentSubscription?.subscriptionNo || '-' }}</span>
|
||||||
</a-descriptions-item>
|
</a-descriptions-item>
|
||||||
<a-descriptions-item label="创建时间">
|
<a-descriptions-item label="开通时间">
|
||||||
<template v-if="subscriptionLoading">
|
<template v-if="subscriptionLoading">
|
||||||
<a-skeleton-input :active="true" size="small" style="width: 160px" />
|
<a-skeleton-input :active="true" size="small" style="width: 160px" />
|
||||||
</template>
|
</template>
|
||||||
@@ -184,7 +164,7 @@
|
|||||||
</template>
|
</template>
|
||||||
<template v-else-if="currentSubscription && currentSubscription.status === 'active' && currentSubscription.expireTime">
|
<template v-else-if="currentSubscription && currentSubscription.status === 'active' && currentSubscription.expireTime">
|
||||||
<span>{{ formatDateTime(currentSubscription.expireTime) }}</span>
|
<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 ? '已过期' : '已激活' }}
|
{{ isExpired ? '已过期' : '已激活' }}
|
||||||
</a-tag>
|
</a-tag>
|
||||||
<span v-if="daysToExpire !== null && daysToExpire >= 0 && daysToExpire <= 7" class="expire-warn">
|
<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>
|
<a-button type="link" size="small" @click="onSubscribe" style="padding: 0 0 0 8px">立即订阅</a-button>
|
||||||
</template>
|
</template>
|
||||||
</a-descriptions-item>
|
</a-descriptions-item>
|
||||||
<a-descriptions-item label="系统运行">
|
<!-- <a-descriptions-item label="系统运行">-->
|
||||||
<template v-if="subscriptionLoading">
|
<!-- <template v-if="subscriptionLoading">-->
|
||||||
<a-skeleton-input :active="true" size="small" style="width: 60px" />
|
<!-- <a-skeleton-input :active="true" size="small" style="width: 60px" />-->
|
||||||
</template>
|
<!-- </template>-->
|
||||||
<template v-else>
|
<!-- <template v-else>-->
|
||||||
<a-tooltip title="自您首次开通小程序商城起">
|
<!-- <a-tooltip title="自您首次开通小程序商城起">-->
|
||||||
<span>{{ runDays }} 天</span>
|
<!-- <span>{{ runDays }} 天</span>-->
|
||||||
</a-tooltip>
|
<!-- </a-tooltip>-->
|
||||||
</template>
|
<!-- </template>-->
|
||||||
</a-descriptions-item>
|
<!-- </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>
|
</a-descriptions>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a-col>
|
</a-col>
|
||||||
|
<!-- 快速入口 -->
|
||||||
<!-- 快捷操作 -->
|
<a-col :xs="24" :md="12">
|
||||||
<a-col :span="12">
|
<div class="panel">
|
||||||
<div class="panel" style="min-height: 353px">
|
|
||||||
<div class="panel-header">
|
<div class="panel-header">
|
||||||
<span class="panel-title">⚡ 快捷操作</span>
|
<span class="panel-title">⚡ 快速入口</span>
|
||||||
</div>
|
</div>
|
||||||
<div style="padding: 16px 18px;">
|
<div class="quick-grid">
|
||||||
<a-space direction="vertical" style="width: 100%">
|
<div
|
||||||
<a-button
|
v-for="item in quickLinks"
|
||||||
type="primary"
|
:key="item.to"
|
||||||
block
|
class="quick-card"
|
||||||
@click="navigateTo('/shop/shopOrder')"
|
@click="navigateTo(item.to)"
|
||||||
>
|
>
|
||||||
<ShoppingCartOutlined />
|
<div class="quick-icon" :style="{ background: item.bg }">
|
||||||
订单管理
|
{{ item.icon }}
|
||||||
</a-button>
|
<span v-if="item.badge" class="quick-icon-dot"></span>
|
||||||
<a-button block @click="navigateTo('/shop/shopGoods')">
|
</div>
|
||||||
<ShopOutlined />
|
<div class="quick-label">{{ item.label }}</div>
|
||||||
商品管理
|
</div>
|
||||||
</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>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a-col>
|
</a-col>
|
||||||
@@ -350,13 +313,6 @@ import { useOrderNotify } from '@/views/shop/shopOrder/useOrderNotify';
|
|||||||
import {
|
import {
|
||||||
ReloadOutlined,
|
ReloadOutlined,
|
||||||
RightOutlined,
|
RightOutlined,
|
||||||
ShoppingCartOutlined,
|
|
||||||
ShopOutlined,
|
|
||||||
AppstoreOutlined,
|
|
||||||
GiftOutlined,
|
|
||||||
TeamOutlined,
|
|
||||||
SettingOutlined,
|
|
||||||
ClearOutlined,
|
|
||||||
InfoCircleOutlined,
|
InfoCircleOutlined,
|
||||||
QrcodeOutlined
|
QrcodeOutlined
|
||||||
} from '@ant-design/icons-vue';
|
} from '@ant-design/icons-vue';
|
||||||
@@ -370,7 +326,6 @@ import { pageShopOrder } from '@/api/shop/shopOrder';
|
|||||||
import { getShopSettingCategoryValues } from '@/api/shop/shopSetting';
|
import { getShopSettingCategoryValues } from '@/api/shop/shopSetting';
|
||||||
import { getCompressedImageUrl } from '@/utils/image';
|
import { getCompressedImageUrl } from '@/utils/image';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { removeSiteInfoCache } from '@/api/cms/cmsWebsite';
|
|
||||||
import { getProductDetail } from '@/api/app/appProduct';
|
import { getProductDetail } from '@/api/app/appProduct';
|
||||||
import type { AppProduct } from '@/api/app/appProduct/model';
|
import type { AppProduct } from '@/api/app/appProduct/model';
|
||||||
import type { AppSubscription } from '@/api/app/appSubscription/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';
|
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)
|
// 新订单声音提醒(全局单例:轮询由 layout 启动,此处仅消费状态 + UI)
|
||||||
const { enabled: notifyEnabled, hasNewOrder, toggle: onNotifyToggle, testNotify: onTestNotify } = useOrderNotify({
|
const { enabled: notifyEnabled, hasNewOrder, toggle: onNotifyToggle, testNotify: onTestNotify } = useOrderNotify({
|
||||||
onNewOrder: () => loadData()
|
onNewOrder: () => loadData()
|
||||||
@@ -971,7 +915,7 @@ onUnmounted(() => {
|
|||||||
.panel-title { font-size: 14px; font-weight: 600; color: rgba(0,0,0,0.85); }
|
.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 {
|
.todo-item {
|
||||||
display: flex; align-items: center; gap: 12px;
|
display: flex; align-items: center; gap: 12px;
|
||||||
padding: 12px 18px; cursor: pointer; transition: background 0.15s;
|
padding: 12px 18px; cursor: pointer; transition: background 0.15s;
|
||||||
@@ -1029,9 +973,9 @@ onUnmounted(() => {
|
|||||||
/* 今日数据 */
|
/* 今日数据 */
|
||||||
.today-stats {
|
.today-stats {
|
||||||
display: grid;
|
display: grid;
|
||||||
grid-template-columns: repeat(4, 1fr);
|
grid-template-columns: repeat(2, 1fr);
|
||||||
gap: 16px;
|
gap: 12px;
|
||||||
padding: 20px;
|
padding: 16px;
|
||||||
}
|
}
|
||||||
.today-stat {
|
.today-stat {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
Reference in New Issue
Block a user