From 9e65007e6541bb9f8b73974041d3404d6c62a3fe Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=B5=B5=E5=BF=A0=E6=9E=97?= <170083662@qq.com> Date: Wed, 15 Jul 2026 12:49:05 +0800 Subject: [PATCH] =?UTF-8?q?feat(appSubscription):=20=E6=96=B0=E5=A2=9E?= =?UTF-8?q?=E5=BA=94=E7=94=A8=E8=AE=A2=E9=98=85=E6=A8=A1=E5=9D=97=E5=8F=8A?= =?UTF-8?q?=E5=95=86=E5=9F=8E=E6=AC=A2=E8=BF=8E=E6=A8=AA=E5=B9=85Logo?= =?UTF-8?q?=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增 useAppSubscriptionStore,封装 websopy 特殊接口的应用订阅功能 - 实现订阅列表分页查询、详情获取、支付状态查询、订阅管理等接口调用 - 适配不同支付方式,支持余额支付、微信支付及小程序支付预支付流程 - 在商城 Dashboard 欢迎横幅左侧新增商城 Logo 显示,读取商城设置的 shopLogo 字段 - 使用图像压缩接口处理 Logo 大小和质量,优化加载体验 - 优化 Dashboard 待处理事项展示,恢复退款和优惠券使用项 - 统一订阅接口调用地址为独立域名,复用请求工具共享认证和错误处理 --- .workbuddy/memory/2026-07-15.md | 16 ++ src/api/app/appSubscription/index.ts | 269 ++++++++++++++++++++ src/api/app/appSubscription/model.ts | 219 ++++++++++++++++ src/store/modules/appSubscription.ts | 368 +++++++++++++++++++++++++++ src/views/shop/dashboard/index.vue | 43 +++- 5 files changed, 909 insertions(+), 6 deletions(-) create mode 100644 src/api/app/appSubscription/index.ts create mode 100644 src/api/app/appSubscription/model.ts create mode 100644 src/store/modules/appSubscription.ts diff --git a/.workbuddy/memory/2026-07-15.md b/.workbuddy/memory/2026-07-15.md index 96b1c5a..15f2031 100644 --- a/.workbuddy/memory/2026-07-15.md +++ b/.workbuddy/memory/2026-07-15.md @@ -25,3 +25,19 @@ - 基本信息"创建时间"改为读取 `tenantStore.company?.createTime`。 - 运行天数也改为从 `tenantStore.company?.createTime` 计算,移除独立的 `tenantCreateTime` ref。 - 文件:`src/views/shop/dashboard/index.vue` + +## Dashboard 欢迎横幅新增商城Logo + +- 通过 `getShopSettingCategoryValues('basic')` 获取商城设置,读取 `shopLogo` 字段。 +- 使用 `getCompressedImageUrl(shopLogo, { width: 200, quality: 90 })` 压缩图片。 +- 在 welcome-banner 左侧用 `a-avatar` (64px, square) 展示,无 Logo 时不显示。 +- 文件:`src/views/shop/dashboard/index.vue` + +## 新增 useAppSubscriptionStore(websopy 应用订阅特殊接口) + +- 新增 `src/api/app/appSubscription/model.ts`:AppSubscription 实体及参数/返回类型,对照后端 `com.gxwebsoft.app.entity.AppSubscription`。 +- 新增 `src/api/app/appSubscription/index.ts`:封装 `AppSubscriptionController` 全部 14 个接口。 + - 特殊接口域名 `https://websopy-api.websoft.top/api`(与项目主 API 域名不同),复用 `@/utils/request`(共享登录态 token、401 处理),传绝对 URL。 + - 后端无 context-path;baseURL=`https://websopy-api.websoft.top/api`,接口路径用 `/app/subscription/xxx`。 + - 后端 `BaseController` 成功 code=0;`success(IPage)` 转成 `PageResult{list,count}`,与项目 `@/api` 的 PageResult 结构一致。 +- 新增 `src/store/modules/appSubscription.ts`:`useAppSubscriptionStore`(Pinia Options API 风格,对齐 site.ts/statistics.ts),聚合列表/详情/支付状态/订阅管理方法。列表/详情缓存5分钟;`checkStatus`/`checkPurchased` 实时请求;订阅/支付/管理类操作成功后调 `invalidateCache()` 失效缓存。 diff --git a/src/api/app/appSubscription/index.ts b/src/api/app/appSubscription/index.ts new file mode 100644 index 0000000..a420181 --- /dev/null +++ b/src/api/app/appSubscription/index.ts @@ -0,0 +1,269 @@ +import request from '@/utils/request'; +import type { ApiResult, PageResult } from '@/api'; +import type { + AppSubscription, + AppSubscriptionQueryParam, + SubscribeParam, + SubscribeResult, + GeneratePayQrcodeParam, + GeneratePayQrcodeResult, + CheckStatusResult, + PayResult, + WechatNativePayResult, + MpPrepayParam, + MpPrepayResult, + MpConfirmParam, + BalanceResult +} from './model'; + +/** + * websopy 特殊接口域名(与项目主 API 域名不同) + * 后端 Controller: com.gxwebsoft.app.controller.AppSubscriptionController + * @RequestMapping("/api/app/subscription"),后端无 context-path + * 因此完整 URL = https://websopy-api.websoft.top + /api/app/subscription/xxx + */ +const WEBSOPY_API_BASE = 'https://websopy-api.websoft.top/api'; +const BASE = `${WEBSOPY_API_BASE}/app/subscription`; + +/** + * 我的订阅列表(分页) + * GET /app/subscription/my/page + */ +export async function pageMySubscriptions( + params: AppSubscriptionQueryParam +): Promise> { + const res = await request.get>>( + `${BASE}/my/page`, + { params } + ); + if (res.data.code === 0) { + return ( + res.data.data || { list: [], count: 0 } + ); + } + return Promise.reject(new Error(res.data.message)); +} + +/** + * 订阅详情(按ID) + * GET /app/subscription/detail/{id} + */ +export async function getSubscriptionDetail( + id: number +): Promise { + const res = await request.get>( + `${BASE}/detail/${id}` + ); + if (res.data.code === 0) { + return res.data.data as AppSubscription; + } + return Promise.reject(new Error(res.data.message)); +} + +/** + * 根据订阅编号查询详情(小程序入口用) + * GET /app/subscription/detail-by-no/{subscriptionNo} + */ +export async function getSubscriptionDetailByNo( + subscriptionNo: string +): Promise { + const res = await request.get>( + `${BASE}/detail-by-no/${subscriptionNo}` + ); + if (res.data.code === 0) { + return res.data.data as AppSubscription; + } + return Promise.reject(new Error(res.data.message)); +} + +/** + * 查询支付状态(前端轮询用) + * GET /app/subscription/check-status/{subscriptionNo} + */ +export async function checkSubscriptionStatus( + subscriptionNo: string +): Promise { + const res = await request.get>( + `${BASE}/check-status/${subscriptionNo}` + ); + if (res.data.code === 0) { + return res.data.data as CheckStatusResult; + } + return Promise.reject(new Error(res.data.message)); +} + +/** + * 检查是否已购买某应用 + * GET /app/subscription/check-purchased/{productId} + * 返回 boolean + */ +export async function checkPurchased( + productId: number +): Promise { + const res = await request.get>( + `${BASE}/check-purchased/${productId}` + ); + if (res.data.code === 0) { + return res.data.data === true; + } + return Promise.reject(new Error(res.data.message)); +} + +/** + * 获取当前用户余额 + * GET /app/subscription/balance + */ +export async function getBalance(): Promise { + const res = await request.get>(`${BASE}/balance`); + if (res.data.code === 0) { + return ( + res.data.data || { balance: 0 } + ); + } + return Promise.reject(new Error(res.data.message)); +} + +/** + * 创建订阅 + * POST /app/subscription/subscribe + * 免费应用直接激活,付费应用创建待支付记录 + */ +export async function subscribe( + data: SubscribeParam +): Promise { + const res = await request.post>( + `${BASE}/subscribe`, + data + ); + if (res.data.code === 0) { + return res.data.data as SubscribeResult; + } + return Promise.reject(new Error(res.data.message)); +} + +/** + * 生成支付小程序码(同时创建订阅记录) + * POST /app/subscription/generate-pay-qrcode + */ +export async function generatePayQrcode( + data: GeneratePayQrcodeParam +): Promise { + const res = await request.post>( + `${BASE}/generate-pay-qrcode`, + data + ); + if (res.data.code === 0) { + return res.data.data as GeneratePayQrcodeResult; + } + return Promise.reject(new Error(res.data.message)); +} + +/** + * 发起支付 + * POST /app/subscription/pay/{id}?method=balance|wechat&envVersion=xxx + * - method=balance:余额支付,返回 PayResult + * - method=wechat:微信 Native 支付,返回小程序码 WechatNativePayResult + */ +export async function paySubscription( + id: number, + method: 'balance' | 'wechat' = 'wechat', + envVersion?: string +): Promise { + const res = await request.post>( + `${BASE}/pay/${id}`, + undefined, + { params: { method, envVersion } } + ); + if (res.data.code === 0) { + return res.data.data as PayResult | WechatNativePayResult; + } + return Promise.reject(new Error(res.data.message)); +} + +/** + * 小程序 JSAPI 预支付下单 + * POST /app/subscription/mp-prepay/{id} + * Body: { openid } + */ +export async function mpPrepay( + id: number, + data: MpPrepayParam +): Promise { + const res = await request.post>( + `${BASE}/mp-prepay/${id}`, + data + ); + if (res.data.code === 0) { + return res.data.data as MpPrepayResult; + } + return Promise.reject(new Error(res.data.message)); +} + +/** + * 小程序支付成功确认 + * POST /app/subscription/mp-confirm/{subscriptionNo} + * Body: { transactionId? } + */ +export async function mpConfirm( + subscriptionNo: string, + data?: MpConfirmParam +): Promise { + const res = await request.post>( + `${BASE}/mp-confirm/${subscriptionNo}`, + data + ); + if (res.data.code === 0) { + return ( + res.data.data || { paid: true, subscriptionNo } + ); + } + return Promise.reject(new Error(res.data.message)); +} + +/** + * 续费 + * POST /app/subscription/renew/{id}?period=month|year + */ +export async function renewSubscription( + id: number, + period: 'month' | 'year' = 'month' +): Promise { + const res = await request.post>(`${BASE}/renew/${id}`, undefined, { + params: { period } + }); + if (res.data.code === 0) { + return res.data.message || '续费成功'; + } + return Promise.reject(new Error(res.data.message)); +} + +/** + * 退订/取消 + * POST /app/subscription/cancel/{id} + */ +export async function cancelSubscription(id: number): Promise { + const res = await request.post>(`${BASE}/cancel/${id}`); + if (res.data.code === 0) { + return res.data.message || '退订成功'; + } + return Promise.reject(new Error(res.data.message)); +} + +/** + * 启用/禁用 + * POST /app/subscription/toggle-enable/{id}?enabled=true|false + */ +export async function toggleEnable( + id: number, + enabled: boolean +): Promise { + const res = await request.post>( + `${BASE}/toggle-enable/${id}`, + undefined, + { params: { enabled } } + ); + if (res.data.code === 0) { + return res.data.message || (enabled ? '已启用' : '已禁用'); + } + return Promise.reject(new Error(res.data.message)); +} diff --git a/src/api/app/appSubscription/model.ts b/src/api/app/appSubscription/model.ts new file mode 100644 index 0000000..d393083 --- /dev/null +++ b/src/api/app/appSubscription/model.ts @@ -0,0 +1,219 @@ +import type { PageParam } from '@/api'; + +/** + * 订阅状态: pending-待支付 active-已激活 expired-已过期 cancelled-已取消 + */ +export type SubscriptionStatus = + | 'pending' + | 'active' + | 'expired' + | 'cancelled'; + +/** + * 价格类型: free-免费 one_time-买断 subscription-订阅 + */ +export type PriceType = 'free' | 'one_time' | 'subscription'; + +/** + * 订阅周期: month-月 quarter-季 year-年 + */ +export type SubscriptionPeriod = 'month' | 'quarter' | 'year'; + +/** + * 支付方式: 0-余额 1-微信 2-支付宝 12-免费 + */ +export type PayType = 0 | 1 | 2 | 12; + +/** + * 应用订阅实体(对应表 app_subscription) + * 字段对照后端 com.gxwebsoft.app.entity.AppSubscription + */ +export interface AppSubscription { + // 主键ID + id?: number; + // 订阅编号(业务唯一) + subscriptionNo?: string; + // 购买用户ID + userId?: number; + // 应用产品ID + productId?: number; + // 租户ID + tenantId?: number; + // 订阅状态 + status?: SubscriptionStatus; + // 价格类型 + priceType?: PriceType; + // 原价(单位:元) + originalPrice?: number; + // 实付金额(单位:元) + payPrice?: number; + // 支付方式 + payType?: PayType; + // 支付状态: 0-未支付 1-已支付 + payStatus?: number; + // 支付时间 + payTime?: string; + // 第三方交易号 + transactionId?: string; + // 订阅周期 + subscriptionPeriod?: SubscriptionPeriod; + // 生效时间 + startTime?: string; + // 到期时间(订阅型) + expireTime?: string; + // 是否自动续费 0-否 1-是 + autoRenew?: number; + // 分配的域名 + instanceDomain?: string; + // 实例管理后台URL + instanceAdminUrl?: string; + // 实例配置(JSON) + instanceConfig?: string; + // 关联的支付订单号 + orderNo?: string; + // 关联的支付订单ID + orderId?: number; + // 备注 + remark?: string; + // 排序 + sortNumber?: number; + // 创建时间 + createTime?: string; + // 更新时间 + updateTime?: string; + // ===== 关联查询字段(非数据库字段) ===== + productName?: string; + productIcon?: string; + productLogo?: string; + productAppType?: number; + productDescription?: string; + developerName?: string; +} + +/** + * 我的订阅分页查询参数 + */ +export interface AppSubscriptionQueryParam extends PageParam { + // 订阅状态过滤 + status?: SubscriptionStatus; +} + +/** + * 创建订阅参数 + */ +export interface SubscribeParam { + // 应用产品ID + productId: number; + // 订阅周期,默认 month + subscriptionPeriod?: SubscriptionPeriod; +} + +/** + * 生成支付小程序码参数 + */ +export interface GeneratePayQrcodeParam { + // 应用产品ID + productId: number; + // 订阅周期,默认 month + subscriptionPeriod?: SubscriptionPeriod; + // 小程序版本:develop / trial / release,默认 trial + envVersion?: string; +} + +/** + * 创建订阅返回结果 + */ +export interface SubscribeResult { + subscriptionId: number; + subscriptionNo: string; + status: SubscriptionStatus; + message: string; + payPrice?: number; + orderNo?: string; +} + +/** + * 生成支付小程序码返回结果 + */ +export interface GeneratePayQrcodeResult { + subscriptionNo: string; + subscriptionId: number; + qrcodeBase64: string; + productName: string; + payPrice: number; +} + +/** + * 查询支付状态返回结果 + */ +export interface CheckStatusResult { + paid: boolean; + payStatus: number; + status: SubscriptionStatus; + payTime?: string; + transactionId?: string; + id: number; + subscriptionNo: string; + productId: number; + productName?: string; + productLogo?: string; + priceType?: PriceType; + payPrice?: number; + subscriptionPeriod?: SubscriptionPeriod; +} + +/** + * 余额支付返回结果 + */ +export interface PayResult { + paid: boolean; + balance?: number; + subscriptionNo: string; +} + +/** + * 微信 Native 支付(Web 端)返回结果 + */ +export interface WechatNativePayResult { + subscriptionId: number; + subscriptionNo: string; + miniappQrcode: string; + miniappPagePath: string; + payPrice: number; +} + +/** + * 小程序 JSAPI 预支付返回结果 + */ +export interface MpPrepayResult { + subscriptionId: number; + subscriptionNo: string; + outTradeNo: string; + timeStamp: string; + nonceStr: string; + package: string; + signType: string; + paySign: string; + payPrice: number; +} + +/** + * 获取余额返回结果 + */ +export interface BalanceResult { + balance: number; +} + +/** + * 小程序预支付参数 + */ +export interface MpPrepayParam { + openid: string; +} + +/** + * 小程序支付确认参数 + */ +export interface MpConfirmParam { + transactionId?: string; +} diff --git a/src/store/modules/appSubscription.ts b/src/store/modules/appSubscription.ts new file mode 100644 index 0000000..0fc8288 --- /dev/null +++ b/src/store/modules/appSubscription.ts @@ -0,0 +1,368 @@ +/** + * 应用订阅 store + * 数据来源:websopy 特殊接口(db_websopy.app_subscription 表) + * 接口域名:https://websopy-api.websoft.top/api + * 后端:com.gxwebsoft.app.controller.AppSubscriptionController + */ +import { defineStore } from 'pinia'; +import { + pageMySubscriptions, + getSubscriptionDetail, + getSubscriptionDetailByNo, + checkSubscriptionStatus, + checkPurchased, + getBalance, + subscribe, + generatePayQrcode, + paySubscription, + mpPrepay, + mpConfirm, + renewSubscription, + cancelSubscription, + toggleEnable +} from '@/api/app/appSubscription'; +import type { + AppSubscription, + AppSubscriptionQueryParam, + SubscribeParam, + SubscribeResult, + GeneratePayQrcodeParam, + GeneratePayQrcodeResult, + CheckStatusResult, + PayResult, + WechatNativePayResult, + MpPrepayResult +} from '@/api/app/appSubscription/model'; +import type { PageResult } from '@/api'; + +export interface AppSubscriptionState { + // 我的订阅列表 + subscriptionList: AppSubscription[]; + // 总数量 + total: number; + // 当前查看的订阅详情 + currentSubscription: AppSubscription | null; + // 用户余额 + balance: number; + // 加载状态 + loading: boolean; + // 最后更新时间 + lastUpdateTime: number | null; + // 缓存有效期(毫秒) + cacheExpiry: number; +} + +export const useAppSubscriptionStore = defineStore('appSubscription', { + state: (): AppSubscriptionState => ({ + subscriptionList: [], + total: 0, + currentSubscription: null, + balance: 0, + loading: false, + lastUpdateTime: null, + // 默认缓存5分钟(订阅涉及支付状态,不宜过长) + cacheExpiry: 5 * 60 * 1000 + }), + + getters: { + /** + * 已激活订阅 + */ + activeSubscriptions: (state): AppSubscription[] => { + return state.subscriptionList.filter((s) => s.status === 'active'); + }, + + /** + * 待支付订阅 + */ + pendingSubscriptions: (state): AppSubscription[] => { + return state.subscriptionList.filter((s) => s.status === 'pending'); + }, + + /** + * 已过期订阅 + */ + expiredSubscriptions: (state): AppSubscription[] => { + return state.subscriptionList.filter((s) => s.status === 'expired'); + }, + + /** + * 检查缓存是否有效 + */ + isCacheValid: (state): boolean => { + if (!state.lastUpdateTime) return false; + const now = Date.now(); + return now - state.lastUpdateTime < state.cacheExpiry; + } + }, + + actions: { + // ============================================================ + // 查询类 + // ============================================================ + + /** + * 获取我的订阅列表(分页,带缓存) + * @param params 查询参数(page/limit/status) + * @param forceRefresh 是否强制刷新(切换 status 过滤时建议传 true) + */ + async fetchMySubscriptions( + params: AppSubscriptionQueryParam = { page: 1, limit: 10 }, + forceRefresh = false + ): Promise> { + // 缓存有效且不强制刷新,直接返回缓存列表 + if (!forceRefresh && this.isCacheValid && this.subscriptionList.length > 0) { + return { list: this.subscriptionList, count: this.total }; + } + + this.loading = true; + try { + const data = await pageMySubscriptions(params); + this.subscriptionList = data.list || []; + this.total = data.count || 0; + this.lastUpdateTime = Date.now(); + return data; + } catch (error) { + console.error('获取我的订阅列表失败:', error); + throw error; + } finally { + this.loading = false; + } + }, + + /** + * 获取订阅详情(按ID) + */ + async fetchDetail(id: number): Promise { + try { + const data = await getSubscriptionDetail(id); + this.currentSubscription = data; + return data; + } catch (error) { + console.error('获取订阅详情失败:', error); + throw error; + } + }, + + /** + * 根据订阅编号查询详情(小程序入口用) + */ + async fetchDetailByNo(subscriptionNo: string): Promise { + try { + const data = await getSubscriptionDetailByNo(subscriptionNo); + this.currentSubscription = data; + return data; + } catch (error) { + console.error('根据订阅编号查询详情失败:', error); + throw error; + } + }, + + /** + * 查询支付状态(前端轮询用,强制实时请求,不走缓存) + */ + async checkStatus(subscriptionNo: string): Promise { + return await checkSubscriptionStatus(subscriptionNo); + }, + + /** + * 检查是否已购买某应用(强制实时请求,不走缓存) + */ + async checkPurchased(productId: number): Promise { + return await checkPurchased(productId); + }, + + /** + * 获取当前用户余额 + */ + async fetchBalance(): Promise { + try { + const data = await getBalance(); + this.balance = data.balance; + return data.balance; + } catch (error) { + console.error('获取用户余额失败:', error); + throw error; + } + }, + + // ============================================================ + // 订阅与支付操作 + // ============================================================ + + /** + * 创建订阅 + * 免费应用直接激活,付费应用创建待支付记录 + */ + async subscribe(data: SubscribeParam): Promise { + try { + const result = await subscribe(data); + // 订阅状态变化,失效缓存 + this.invalidateCache(); + return result; + } catch (error) { + console.error('创建订阅失败:', error); + throw error; + } + }, + + /** + * 生成支付小程序码(同时创建订阅记录) + */ + async generatePayQrcode( + data: GeneratePayQrcodeParam + ): Promise { + try { + const result = await generatePayQrcode(data); + // 已创建订阅记录,失效缓存 + this.invalidateCache(); + return result; + } catch (error) { + console.error('生成支付小程序码失败:', error); + throw error; + } + }, + + /** + * 发起支付 + * @param id 订阅ID + * @param method 支付方式:balance-余额支付 / wechat-微信支付 + * @param envVersion 小程序版本(微信支付时使用):develop/trial/release + */ + async pay( + id: number, + method: 'balance' | 'wechat' = 'wechat', + envVersion?: string + ): Promise { + try { + const result = await paySubscription(id, method, envVersion); + // 支付成功后余额/订阅状态变化,失效缓存并刷新余额 + this.invalidateCache(); + if (method === 'balance') { + const payResult = result as PayResult; + if (typeof payResult.balance === 'number') { + this.balance = payResult.balance; + } + } + return result; + } catch (error) { + console.error('发起支付失败:', error); + throw error; + } + }, + + /** + * 小程序 JSAPI 预支付下单 + */ + async mpPrepay(id: number, openid: string): Promise { + try { + return await mpPrepay(id, { openid }); + } catch (error) { + console.error('小程序预支付失败:', error); + throw error; + } + }, + + /** + * 小程序支付成功确认 + */ + async mpConfirm( + subscriptionNo: string, + transactionId?: string + ): Promise { + try { + const result = await mpConfirm(subscriptionNo, { transactionId }); + // 支付确认后订阅状态变化,失效缓存 + this.invalidateCache(); + return result; + } catch (error) { + console.error('小程序支付确认失败:', error); + throw error; + } + }, + + // ============================================================ + // 订阅管理 + // ============================================================ + + /** + * 续费 + * @param id 订阅ID + * @param period 周期:month/year + */ + async renew(id: number, period: 'month' | 'year' = 'month'): Promise { + try { + const msg = await renewSubscription(id, period); + this.invalidateCache(); + return msg; + } catch (error) { + console.error('续费失败:', error); + throw error; + } + }, + + /** + * 退订/取消 + */ + async cancel(id: number): Promise { + try { + const msg = await cancelSubscription(id); + this.invalidateCache(); + return msg; + } catch (error) { + console.error('退订失败:', error); + throw error; + } + }, + + /** + * 启用/禁用 + */ + async toggleEnable(id: number, enabled: boolean): Promise { + try { + const msg = await toggleEnable(id, enabled); + this.invalidateCache(); + return msg; + } catch (error) { + console.error('启用/禁用失败:', error); + throw error; + } + }, + + // ============================================================ + // 缓存控制 + // ============================================================ + + /** + * 失效缓存(仅重置时间标记,保留已有数据,下次拉取时更新) + * 用于订阅/支付/管理操作后,确保下次查询获取最新数据 + */ + invalidateCache() { + this.lastUpdateTime = null; + }, + + /** + * 清除缓存(清空数据并重置时间标记) + */ + clearCache() { + this.subscriptionList = []; + this.total = 0; + this.currentSubscription = null; + this.lastUpdateTime = null; + }, + + /** + * 强制刷新订阅列表 + */ + async refresh() { + return await this.fetchMySubscriptions({ page: 1, limit: 10 }, true); + }, + + /** + * 设置缓存有效期 + */ + setCacheExpiry(expiry: number) { + this.cacheExpiry = expiry; + } + } +}); diff --git a/src/views/shop/dashboard/index.vue b/src/views/shop/dashboard/index.vue index 79ca4da..cc642e3 100644 --- a/src/views/shop/dashboard/index.vue +++ b/src/views/shop/dashboard/index.vue @@ -2,9 +2,18 @@
-
-

🏸 {{ userStore.info?.tenantName }}

-

欢迎回来,管理员,今日数据已更新

+
+
@@ -159,7 +168,7 @@ {{ tenantStore.company?.createTime || '-' }} - {{ siteInfo?.expirationTime }} + {{ siteInfo?.expirationTime || '-' }} {{ runDays }} 天 @@ -239,6 +248,8 @@ import { useStatisticsStore } from '@/store/modules/statistics'; import { useUserStore } from '@/store/modules/user'; import { useTenantStore } from '@/store/modules/tenant'; 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'; @@ -294,12 +305,18 @@ const pendingShipmentCount = ref(0); const pendingRefundCount = ref(0); const couponUsedCount = computed(() => statisticsStore.safeCouponUsedCount); +// 商城Logo +const shopLogo = ref(''); +const shopLogoUrl = computed(() => + shopLogo.value ? getCompressedImageUrl(shopLogo.value, { width: 200, quality: 90 }) : '' +); + // 待处理事项(使用computed确保响应式更新) const todoItems = computed(() => [ { label: '待付款订单', value: pendingPaymentCount.value, to: '/shop/shopOrder?tab=unpaid', tagColor: 'gold', dotColor: 'dot-gold', urgent: pendingPaymentCount.value > 0 }, { label: '待发货订单', value: pendingShipmentCount.value, to: '/shop/shopOrder?tab=undelivered', tagColor: 'blue', dotColor: 'dot-blue', urgent: pendingShipmentCount.value > 0 }, - // { label: '退款申请', value: pendingRefundCount.value, to: '/shop/shopOrder?tab=refunded', tagColor: 'orange', dotColor: 'dot-orange', urgent: pendingRefundCount.value > 0 }, - // { label: '优惠券使用', value: couponUsedCount.value, to: '/market/coupon', tagColor: 'cyan', dotColor: 'dot-cyan', urgent: false }, + { label: '退款申请', value: pendingRefundCount.value, to: '/shop/shopOrder?tab=refunded', tagColor: 'orange', dotColor: 'dot-orange', urgent: pendingRefundCount.value > 0 }, + { label: '优惠券使用', value: couponUsedCount.value, to: '/market/coupon', tagColor: 'cyan', dotColor: 'dot-cyan', urgent: false }, ]); // 今日统计(使用computed确保响应式更新) @@ -367,6 +384,15 @@ const loadData = async () => { console.warn('获取租户信息失败:', e); }); + // 独立请求商城Logo,不受其他请求失败影响 + getShopSettingCategoryValues('basic') + .then((values) => { + shopLogo.value = values?.shopLogo || ''; + }) + .catch((e) => { + console.warn('获取商城设置失败:', e); + }); + try { await Promise.all([ siteStore.fetchSiteInfo(), @@ -458,6 +484,11 @@ onUnmounted(() => { flex-wrap: wrap; gap: 12px; } +.welcome-logo { + flex-shrink: 0; + border: 2px solid rgba(255,255,255,0.2); + background: rgba(255,255,255,0.1); +} .welcome-title { font-size: 20px; font-weight: 700; color: #fff; margin: 0 0 6px; } .welcome-sub { font-size: 14px; color: rgba(255,255,255,0.7); margin: 0; }