feat(shopOrder): 支持订单商品图片OSS压缩及新增支付方式

- 新增utils/image.ts,提供图片URL补全及OSS压缩参数拼接功能
- 订单列表商品图片列改为使用压缩后图片URL,提高加载性能
- shopOrder相关支付类型注释更新,新增银联支付、POS机支付等多种方式
- 更新支付组件及订单信息界面,新增并显示银联支付、POS机支付、免费、积分支付、货到付款等支付标签
- 订单支付状态标签支持区分“已付款”和“待收货付款”状态
- shopOrder数据model中支付类型及代付类型描述同步更新
- 修复shop utils中payType标签获取的边界条件错误
- 注释掉dashboard待办事项中优惠券使用项,暂停显示该统计数据
This commit is contained in:
2026-07-14 19:47:17 +08:00
parent ad1a9f9839
commit 8918daa6a1
8 changed files with 140 additions and 22 deletions

View File

@@ -34,3 +34,16 @@
### 订单新订单语音提醒同步刷新
- `src/views/shop/shopOrder/useOrderNotify.ts` 增加 `onNewOrder` 回调参数,检测到新订单触发提醒(叮声+语音)时同时调用回调。
- `src/views/shop/shopOrder/index.vue` 调用 `useOrderNotify({ onNewOrder: () => reload() })`,使新订单提醒时自动刷新表格数据,保持数据同步。
## 2026-07-14 订单列表商品图片接入 OSS 压缩
### 改动内容
- 参考 `/Users/gxwebsoft/VUE/xinlong-shop-taro/src/utils/image.ts`,在管理后台新增 `src/utils/image.ts`
- `ensureFullUrl(url)`:补全 OSS 域名为完整 URL
- `getCompressedImageUrl(url, options?)`:拼接 `x-oss-process=image/resize,w_${width}/quality,Q_${quality}` 压缩参数
- `src/views/shop/shopOrder/index.vue`
- 引入 `getCompressedImageUrl`
- 订单列表商品图片列 `<a-avatar :src="item.image" />` 改为 `<a-avatar :src="getCompressedImageUrl(item.image)" />`
### 待办
- 用户提到后端也需要此方法,但当前工作区只有前端代码,后端实现需在后端项目补充。

View File

@@ -85,9 +85,9 @@ export interface ShopOrder {
coachId?: number;
// 支付的用户id
payUserId?: number;
// 0余额支付, 1微信支付102微信Native2会员卡支付3支付宝4现金5POS机6VIP月卡7VIP年卡8VIP次卡9IC月卡10IC年卡11IC次卡12免费13VIP充值卡14IC充值卡15积分支付16VIP季卡17IC季卡18代付
// 0余额支付, 1微信支付, 2支付宝, 3银联支付, 4现金支付, 5POS机支付, 6免费, 7积分支付, 8货到付款, 9~18 已废弃
payType?: number;
// 代付支付方式,0余额支付, 1微信支付102微信Native2会员卡支付3支付宝4现金5POS机6VIP月卡7VIP年卡8VIP次卡9IC月卡10IC年卡11IC次卡12免费13VIP充值卡14IC充值卡15积分支付16VIP季卡17IC季卡18代付
// 代付支付方式, 0余额支付, 1微信支付, 2支付宝, 3银联支付, 4现金支付, 5POS机支付, 6免费, 7积分支付, 8货到付款, 9~18 已废弃
friendPayType?: number;
// 0未付款1已付款
payStatus?: number;

View File

@@ -45,21 +45,45 @@
{ value: 1, label: '微信支付', key: 'wxPay', icon: 'WechatOutlined' },
{
value: 2,
label: '会员卡支付',
key: 'userCardPay',
icon: 'IdcardOutlined'
label: '支付',
key: 'aliPay',
icon: 'AlipayCircleOutlined'
},
{
value: 3,
label: '支付宝支付',
key: 'aliPay',
icon: 'AlipayCircleOutlined'
label: '银联支付',
key: 'unionPay',
icon: 'IdcardOutlined'
},
{
value: 4,
label: '现金支付',
key: 'cashPayment',
icon: 'PayCircleOutlined'
},
{
value: 5,
label: 'POS机支付',
key: 'posPay',
icon: 'IdcardOutlined'
},
{
value: 6,
label: '免费',
key: 'freePay',
icon: 'IdcardOutlined'
},
{
value: 7,
label: '积分支付',
key: 'pointsPay',
icon: 'IdcardOutlined'
},
{
value: 8,
label: '货到付款',
key: 'codPay',
icon: 'IdcardOutlined'
}
]);

76
src/utils/image.ts Normal file
View File

@@ -0,0 +1,76 @@
/**
* 图片压缩参数配置
*/
export interface ImageCompressOptions {
/** 图片宽度px默认 300 */
width?: number;
/** 图片质量 0-100默认 90 */
quality?: number;
/** 是否启用压缩,默认 true */
enabled?: boolean;
}
/**
* 默认压缩参数
*/
const DEFAULT_OPTIONS: Required<ImageCompressOptions> = {
width: 240,
quality: 90,
enabled: true
};
/**
* OSS 域名(文件上传接口同域)
*/
const OSS_BASE_URL = 'https://oss.wsdns.cn';
/**
* 确保图片 URL 为完整路径
*
* 后端可能返回相对路径(如 /uploads/xxx.jpg需要补全 OSS 域名。
*
* @param url 原始图片 URL
* @returns 完整图片 URL
*/
export function ensureFullUrl(url: string): string {
if (!url) return '';
if (url.startsWith('http://') || url.startsWith('https://')) {
return url;
}
// 相对路径:补全 OSS 域名
return `${OSS_BASE_URL}${url.startsWith('/') ? '' : '/'}${url}`;
}
/**
* 获取 OSS 压缩后的图片 URL
*
* 默认给图片 URL 拼接阿里云 OSS 图片处理参数,实现等比缩放 + 质量压缩。
* 使用场景:商品列表、商品卡片等缩略图展示。
*
* 注意:预览原图时不需要调用此函数,直接用原始 URL 即可。
*
* @param url 原始图片 URL
* @param options 压缩选项
* @returns 处理后的图片 URL
*/
export function getCompressedImageUrl(
url: string,
options?: ImageCompressOptions
): string {
// 先补全为完整 URL
const fullUrl = ensureFullUrl(url);
if (!fullUrl) return '';
const { width, quality, enabled } = { ...DEFAULT_OPTIONS, ...options };
// 未启用压缩,原样返回
if (!enabled) return fullUrl;
// 已包含 OSS 处理参数,避免重复拼接
if (fullUrl.includes('x-oss-process')) return fullUrl;
// 已有其他 query 参数用 & 拼接,否则用 ?
const separator = fullUrl.includes('?') ? '&' : '?';
return `${fullUrl}${separator}x-oss-process=image/resize,w_${width}/quality,Q_${quality}`;
}

View File

@@ -23,7 +23,7 @@ export function getPayType(index?: number): any {
},
{
value: 5,
label: 'POS机'
label: 'POS机支付'
},
{
value: 6,
@@ -38,7 +38,7 @@ export function getPayType(index?: number): any {
label: '货到付款'
}
];
if (index) {
if (index != null) {
return payType[index]?.label || '';
}
return payType;

View File

@@ -273,7 +273,7 @@ const couponUsedCount = ref(0);
const todoItems = computed(() => [
{ label: '待发货订单', value: pendingShipmentCount.value, to: '/shop/shopOrder', tagColor: 'blue', dotColor: 'dot-blue', urgent: pendingShipmentCount.value > 0 },
{ label: '退款申请', value: pendingRefundCount.value, to: '/shop/shopOrder', 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: couponUsedCount.value, to: '/market/coupon', tagColor: 'cyan', dotColor: 'dot-cyan', urgent: false },
]);
// 今日统计使用computed确保响应式更新

View File

@@ -71,7 +71,8 @@
label="支付状态"
:labelStyle="{ width: '90px', color: '#808080' }"
>
<a-tag v-if="form.payStatus == 1" color="green">已付款</a-tag>
<a-tag v-if="form.payStatus == 1 && form.payType !== 8" color="green">已付款</a-tag>
<a-tag v-if="form.payStatus == 1 && form.payType === 8" color="blue">待收货付款</a-tag>
<a-tag v-if="form.payStatus == 0">未付款</a-tag>
<a-tag v-if="form.payStatus == 3">未付款,占场中</a-tag>
</a-descriptions-item>
@@ -116,30 +117,33 @@
<WechatOutlined class="tag-icon" />
微信支付
</a-tag>
<a-tag v-if="form.payType == 2">积分</a-tag>
<a-tag v-if="form.payType == 3">
<a-tag v-if="form.payType == 2">
<AlipayCircleOutlined class="tag-icon" />
支付宝
</a-tag>
<a-tag v-if="form.payType == 3">
<IdcardOutlined class="tag-icon" />
银联支付
</a-tag>
<a-tag v-if="form.payType == 4">
<IdcardOutlined class="tag-icon" />
现金
现金支付
</a-tag>
<a-tag v-if="form.payType == 5">
<IdcardOutlined class="tag-icon" />
POS机
POS机支付
</a-tag>
<a-tag v-if="form.payType == 6">
<IdcardOutlined class="tag-icon" />
VIP月卡
免费
</a-tag>
<a-tag v-if="form.payType == 7">
<IdcardOutlined class="tag-icon" />
VIP年卡
积分支付
</a-tag>
<a-tag v-if="form.payType == 8">
<IdcardOutlined class="tag-icon" />
VIP次卡
货到付款
</a-tag>
<a-tag v-if="form.payType == 9">
<IdcardOutlined class="tag-icon" />
@@ -541,9 +545,9 @@
coachId: undefined,
// 支付的用户id
payUserId: undefined,
// 0余额支付, 1微信支付102微信Native2会员卡支付3支付宝4现金5POS机6VIP月卡7VIP年卡8VIP次卡9IC月卡10IC年卡11IC次卡12免费13VIP充值卡14IC充值卡15积分支付16VIP季卡17IC季卡18代付
// 0余额支付, 1微信支付, 2支付宝, 3银联支付, 4现金支付, 5POS机支付, 6免费, 7积分支付, 8货到付款, 9~18 已废弃
payType: undefined,
// 代付支付方式,0余额支付, 1微信支付102微信Native2会员卡支付3支付宝4现金5POS机6VIP月卡7VIP年卡8VIP次卡9IC月卡10IC年卡11IC次卡12免费13VIP充值卡14IC充值卡15积分支付16VIP季卡17IC季卡18代付
// 代付支付方式, 0余额支付, 1微信支付, 2支付宝, 3银联支付, 4现金支付, 5POS机支付, 6免费, 7积分支付, 8货到付款, 9~18 已废弃
friendPayType: undefined,
// 0未付款1已付款
payStatus: undefined,

View File

@@ -175,7 +175,7 @@
<template v-for="(item, index) in record.orderGoods" :key="index">
<div class="item py-1">
<a-space :id="`g-${index}`">
<a-avatar :src="item.image" shape="square" :size="80" />
<a-avatar :src="getCompressedImageUrl(item.image)" shape="square" :size="80" />
<span>{{ item.goodsName }}</span>
</a-space>
</div>
@@ -376,6 +376,7 @@
} from '@/api/shop/shopOrder';
import { updateUser } from '@/api/system/user';
import { getPayType } from '@/utils/shop';
import { getCompressedImageUrl } from '@/utils/image';
import { message, Modal } from 'ant-design-vue';
import { useOrderNotify } from './useOrderNotify';