fix: review/write 接口方法增加

This commit is contained in:
jzp 2025-11-27 09:38:08 +08:00
parent cfb0838170
commit 9417528707
14 changed files with 495 additions and 293 deletions

1
components.d.ts vendored
View File

@ -17,6 +17,7 @@ declare module 'vue' {
VanButton: typeof import('vant/es')['Button']
VanCell: typeof import('vant/es')['Cell']
VanCellGroup: typeof import('vant/es')['CellGroup']
VanCheckbox: typeof import('vant/es')['Checkbox']
VanEmpty: typeof import('vant/es')['Empty']
VanField: typeof import('vant/es')['Field']
VanForm: typeof import('vant/es')['Form']

View File

@ -19,6 +19,7 @@
"@vant/area-data": "^2.1.0",
"axios": "^1.11.0",
"capture-request-log": "^0.0.4",
"es-toolkit": "^1.41.0",
"js-base64": "^3.7.8",
"lodash": "^4.17.21",
"lodash-es": "^4.17.21",

8
pnpm-lock.yaml generated
View File

@ -17,6 +17,9 @@ importers:
capture-request-log:
specifier: ^0.0.4
version: 0.0.4
es-toolkit:
specifier: ^1.41.0
version: 1.41.0
js-base64:
specifier: ^3.7.8
version: 3.7.8
@ -1281,6 +1284,9 @@ packages:
resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==}
engines: {node: '>= 0.4'}
es-toolkit@1.41.0:
resolution: {integrity: sha512-bDd3oRmbVgqZCJS6WmeQieOrzpl3URcWBUVDXxOELlUW2FuW+0glPOz1n0KnRie+PdyvUZcXz2sOn00c6pPRIA==}
esbuild@0.25.12:
resolution: {integrity: sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==}
engines: {node: '>=18'}
@ -3445,6 +3451,8 @@ snapshots:
has-tostringtag: 1.0.2
hasown: 2.0.2
es-toolkit@1.41.0: {}
esbuild@0.25.12:
optionalDependencies:
'@esbuild/aix-ppc64': 0.25.12

View File

@ -2,9 +2,27 @@
</script>
<template>
<RouterView />
<div>
<RouterView />
</div>
</template>
<style scoped>
<style lang="scss">
html {
width: 100%;
height:100%;
}
body{
width: 100%;
height:100%;
background-color: var(--app-html-bg-color);
}
#app {
width: 100%;
height: 100%;
}
</style>
<style lang="sass">
</style>

14
src/api/files.ts Normal file
View File

@ -0,0 +1,14 @@
// 商品维度接口
const file = {
// 上传图片
addUploadfile: [
'https://api.1024api.com/api-interface/mm/upload/file',
{
headers: {
'Content-Type': 'multipart/form-data',
},
},
],
}
export default file

View File

@ -2,8 +2,10 @@ import axios from '@/utils/axios'
import user from './user'
import shop from './shop'
import common from './common'
import files from './files'
import order from './order'
const totalApiConfig = { user, shop, common }
const totalApiConfig = { user, shop, common, files, order }
const handleAddArrayPrototype = (obj: Recordable<[string, Recordable<any>]>) => {
Object.values(obj).forEach((arr) => {

6
src/api/order.ts Normal file
View File

@ -0,0 +1,6 @@
const order = {
addCommentAdd: ["/comment/add"]
}
export default order

4
src/api/types/files.ts Normal file
View File

@ -0,0 +1,4 @@
export type addUploadfileRepType = {
srcFileName:string
url: string
}

188
src/api/types/order.ts Normal file
View File

@ -0,0 +1,188 @@
export type OrderStatus = 'create' | 'wait_pay' | 'wait_shipping' | 'apply_cancel' | 'close' | 'shipping' | 'shipped' | 'delivered' | 'refund' | 'all_refund' | 'part_refund' | 'unknown'
export type ReverseStatus = 'all_refund' | 'part_refund' | 'close' | 'cancel' | 'refunded' | string
export type OrderSkuInfoItem = {
propertyName: string
propertyValue: string
}
export type PackageLogisticsData = {
areaCode?: string
areaName?: string
context?: string
status?: string
time?: string
ftime?: string
}
export type PackageLogisticsInfo = {
com?: string
nu?: string
state?: string
status?: string
condition?: string
ischeck?: string
data?: PackageLogisticsData[]
routeInfo?: Record<string, any>
message?: string
}
export type OrderPackageEntity = {
id: number
isDelete?: number
logisticsCompany?: string
trackNumber?: string
packageImageUrl?: string
packageLogisticsInfo?: string | PackageLogisticsInfo
shippingAmount?: number
shippingFrom?: string
shippingTo?: string
com?: string
status?: string
state?: string
ischeck?: string
shippingType?: string
gmtSignReceipt?: number
gmtShipping?: number
gmtSendMqDelivered?: number
gmtDelivered?: number
deliveredBy?: string
}
export type TradeOrderLineEntity = {
id: number
tradeOrderId?: number
tradeOrderLineIdList?: number[]
productId?: number
productName?: string
productMainImageUrl?: string
skuId?: number
skuImageUrl?: string
skuInfo?: string | OrderSkuInfoItem[]
salePrice?: number
promotionPrice?: number
num?: number
trackNumber?: string
logisticsCompany?: string
logisticsDesc?: string
status?: OrderStatus | string
payAmount?: number
shippingAmount?: number
refundAmount?: number
refundCount?: number
reverseStatus?: ReverseStatus
activityAwardCount?: number
batchNum?: number
buyerId?: number
province?: string
city?: string
district?: string
shippingFrom?: string
shippingTo?: string
createTime?: number
createTimestamp?: number
modifyTime?: number
modifyTimestamp?: number
gmtPrePay?: number
gmtPay?: number
gmtShipped?: number
gmtDelivered?: number
gmtCancel?: number
gmtClose?: number
gmtToShipping?: number
payType?: string
prepayId?: string
transactionId?: string
deliveredUser?: string
cancelUser?: string
skuDesc?: string
[key: string]: any
}
export type TradeOrderEntity = {
id: number
isDelete?: number
buyerId?: number
buyerPhone?: string
buyerName?: string
buyerWeixin?: string
buyerDetailAddress?: string
buyerAddressId?: number
province?: string
city?: string
district?: string
contry?: string
appName?: string
channel?: string
promoterId?: string
gmtDownOrder?: number
gmtPay?: number
transactionId?: string
tradeInfo?: string
allPrice?: number
refundAmount?: number
refundCount?: number
num?: number
createTime?: number
modifyTime?: number
createTimestamp?: number
modifyTimestamp?: number
}
export type OrderListItem = {
tradeOrderEntity: TradeOrderEntity
appTradeOrderLineDTOList: TradeOrderLineEntity[]
vvPackageEntity?: OrderPackageEntity
[key: string]: any
}
export type OrderListQuery = {
tradeOrderIds?: Array<number | string>
productName?: string
minCreateTimestamp?: number
maxCreateTimestamp?: number
createTimestampSort?: 'ASC' | 'DESC'
status?: string | number
keyword?: string
pageNum?: number
pageSize?: number
[key: string]: any
}
export type OrderListResponseMeta = {
code?: string | number
msg?: string
traceId?: string
}
export type OrderListResponsePayload = {
rows?: OrderListItem[]
list?: OrderListItem[]
data?: OrderListItem[]
[key: string]: any
}
export type OrderListResponseData = OrderListItem[] | OrderListResponsePayload | null | undefined
export type OrderListResponse = OrderListResponseMeta & { data?: OrderListResponseData }
// -----------评论------------------
export type CommentMediaItem = {
commentUrl: string
type: 'image' | 'video'
}
export type CommentAddReqType = {
buyerId: number
skuId: number
tradeOrderId: number
trackNumber?: string
descMatch: number
logisticsService: number
sellerService: number
productComment: string
serviceComment: string
commentDetailList?: CommentMediaItem[]
}

View File

@ -71,174 +71,3 @@ export type ProudictProductPropertyType = {
vvProductPropertyValueList?: ProudictProductPropertyType[]
}
/** ----------------------------- Orders ------------------------------ */
export type OrderStatus =
| 'wait_pay'
| 'wait_shipping'
| 'shipping'
| 'delivered'
| 'all_refund'
| 'part_refund'
| 'close'
| 'unknown'
export type ReverseStatus = 'all_refund' | 'part_refund' | 'close' | 'cancel' | string
export type OrderSkuInfoItem = {
propertyName: string
propertyValue: string
}
export type PackageLogisticsData = {
areaCode?: string
areaName?: string
context?: string
status?: string
time?: string
ftime?: string
}
export type PackageLogisticsInfo = {
com?: string
nu?: string
state?: string
status?: string
condition?: string
ischeck?: string
data?: PackageLogisticsData[]
routeInfo?: Record<string, any>
message?: string
}
export type OrderPackageEntity = {
id: number
logisticsCompany?: string
trackNumber?: string
packageImageUrl?: string
packageLogisticsInfo?: string | PackageLogisticsInfo
shippingAmount?: number
shippingFrom?: string
shippingTo?: string
com?: string
status?: string
state?: string
shippingType?: string
}
export type TradeOrderLineEntity = {
id: number
isDelete?: number
tradeOrderId?: number
orderNo?: string
productId?: number
productName?: string
productMainImageUrl?: string
skuId?: number
skuImageUrl?: string
skuInfo?: string | OrderSkuInfoItem[]
singlePrice?: number
num?: number
allPrice?: number
trackNumber?: string
logisticsCompany?: string
logisticsDesc?: string
status?: string
payAmount?: number
shippingFrom?: string
shippingTo?: string
createTime?: number
modifyTime?: number
[key: string]: any
}
export type TradeOrderEntity = {
id: number
buyerId?: number
buyerDetailAddress?: string
province?: string
city?: string
district?: string
contry?: string
gmtDownOrder?: number
transactionId?: string
tradeInfo?: string
allPrice?: number
num?: number
createTime?: number
modifyTime?: number
}
export type OrderListItem = {
id: number
tradeOrderId?: number
orderNo?: string
tradeOrderEntity?: TradeOrderEntity
vvPackageEntity?: OrderPackageEntity
vvTradeOrderLineEntityList?: TradeOrderLineEntity[]
productId: number
productName: string
productMainImageUrl?: string
skuId?: number
skuInfo?: string | OrderSkuInfoItem[]
num: number
status: OrderStatus | string
reverseStatus?: ReverseStatus
promotionPrice?: number
originPrice?: number
discountAmount?: number
shippingAmount?: number
freightFee?: number
payAmount?: number
profitAmount?: number
activityAwardCount?: number
batchNum?: number
buyerId?: number
buyerDetailAddress?: string
city?: string
province?: string
district?: string
createTime?: number
createTimestamp?: number
modifyTime?: number
modifyTimestamp?: number
gmtPay?: number
gmtPrePay?: number
gmtShipped?: number
gmtDelivered?: number
gmtCancel?: number
gmtClose?: number
gmtToShipping?: number
payType?: string
prepayId?: string
transactionId?: string
trackNumber?: string
shippingFrom?: string
shippingTo?: string
skuDesc?: string
[key: string]: any
}
export type OrderListQuery = {
keyword?: string
status?: string | number
pageNum?: number
pageSize?: number
[key: string]: any
}
export type OrderListResponseMeta = {
code?: string | number
msg?: string
traceId?: string
}
export type OrderListResponsePayload = {
rows?: OrderListItem[]
list?: OrderListItem[]
[key: string]: any
}
export type OrderListResponseData = OrderListItem[] | OrderListResponsePayload | null | undefined
export type OrderListResponse = OrderListResponseMeta & { data?: OrderListResponseData }

View File

@ -1 +1 @@
@import './var.scss'
@use './var.scss'

View File

@ -1,4 +1,5 @@
:root{
--app-footer-bg-height: 40px;
--app-footer-bg-height: 0.4rem;
--app-footer-bg-color: #fff;
--app-html-bg-color: #f2f2f2;
}

View File

@ -26,14 +26,6 @@
</section>
<div :class="`${prefixCls}__scroll-box`">
<!-- <section v-if="summaryMetrics.length" :class="`${prefixCls}__summary`">
<div v-for="metric in summaryMetrics" :key="metric.label" :class="`${prefixCls}__summary-card`">
<p class="summary-value">{{ metric.value }}</p>
<p class="summary-label">{{ metric.label }}</p>
<p class="summary-desc">{{ metric.desc }}</p>
</div>
</section> -->
<div :class="`${prefixCls}__list`">
<van-skeleton v-if="isListLoading && !orders.length" :row="4" title />
<template v-else>
@ -45,7 +37,7 @@
</p>
<div class="status" :class="getStatusMeta(order.displayStatus).className">
<span class="status-label">{{ getStatusMeta(order.displayStatus).label }}</span>
<p class="status-desc">{{ getStatusDescription(order) }}</p>
<!-- <p class="status-desc">{{ getStatusDescription(order) }}</p> -->
</div>
</div>
@ -58,12 +50,12 @@
<div v-for="goods in order.goods" :key="goods.id || goods.productId" class="goods-item" @click="goDetail(order.id)">
<img :src="goods.productMainImageUrl || defaultCover" :alt="goods.productName" />
<div class="info">
<p class="name">{{ goods.productName || order.productName }}</p>
<p class="name">{{ goods.productName || '商品' }}</p>
<p class="spec" v-if="goods.skuDesc">{{ goods.skuDesc }}</p>
</div>
<div class="price">
<p>¥{{ formatAmount(goods.payAmount ?? order.payAmount ?? order.promotionPrice) }}</p>
<span>x{{ goods.num || order.num || 1 }}</span>
<p>¥{{ formatAmount(goods.payAmount) }}</p>
<span>x{{ goods.num || 1 }}</span>
</div>
</div>
</div>
@ -72,7 +64,7 @@
<van-icon :name="order.logisticsInfo.icon" size=".14rem" />
<div class="logistics-text">
<p class="title">{{ order.logisticsInfo.title }}</p>
<p>{{ order.logisticsInfo.desc }}</p>
<p class="desc">{{ order.logisticsInfo.desc }}</p>
<p v-if="order.logisticsInfo.sub" class="sub">{{ order.logisticsInfo.sub }}</p>
</div>
<van-button v-if="order.logisticsInfo.trackNumber" size="mini" type="primary" plain @click.stop="handleViewLogistics(order)">查物流</van-button>
@ -80,16 +72,16 @@
<ul :class="`${prefixCls}__amount`">
<li>
<span>商品金额</span><b>¥{{ formatAmount(order.originPrice ?? order.payAmount) }}</b>
<span>商品金额</span><b>¥{{ formatAmount(order.amountSummary.goodsAmount) }}</b>
</li>
<li>
<span>优惠</span><b>-¥{{ formatAmount(order.discountAmount) }}</b>
<span>优惠</span><b>-¥{{ formatAmount(order.amountSummary.discountAmount) }}</b>
</li>
<li>
<span>运费</span><b>¥{{ formatAmount(order.freightFee ?? order.shippingAmount) }}</b>
<span>运费</span><b>¥{{ formatAmount(order.amountSummary.shippingAmount) }}</b>
</li>
<li class="total">
<span>实付</span><b>¥{{ formatAmount(order.payAmount ?? order.promotionPrice) }}</b>
<span>实付</span><b>¥{{ formatAmount(order.amountSummary.payAmount) }}</b>
</li>
</ul>
@ -113,7 +105,7 @@
</template>
</van-popover>
<div :class="`${prefixCls}__action-buttons`">
<template v-for="buttonKeys in [getButtonActionKeys(order)]" :key="`buttons-${order.id}`">
<template v-for="(buttonKeys, i) in [getButtonActionKeys(order)]" :key="`buttons-${order.id} + '' +${i}`">
<van-button
v-for="(actionKey, index) in buttonKeys"
:key="actionKey"
@ -121,7 +113,7 @@
:plain="index !== buttonKeys.length - 1"
:type="index === buttonKeys.length - 1 ? ORDER_ACTION_CONFIG[actionKey]?.type : undefined"
:class="[index === buttonKeys.length - 1 ? ORDER_ACTION_CONFIG[actionKey]?.className : '', index === buttonKeys.length - 1 ? 'is-main-button' : 'is-sub-button']"
@click.stop="triggerAction(actionKey, order)"
@click.stop="triggerAction(actionKey as OrderActionKey, order)"
>
{{ ORDER_ACTION_CONFIG[actionKey]?.label }}
</van-button>
@ -143,7 +135,7 @@ import { useRouter } from 'vue-router'
import api from '@/api'
import { showToast } from 'vant'
import { useDesign } from '@/hooks/web/useDesign.ts'
import type { OrderListItem, OrderStatus, PackageLogisticsInfo, TradeOrderLineEntity } from '@/api/types/shop'
import type { OrderListItem, OrderPackageEntity, OrderStatus, PackageLogisticsInfo, TradeOrderEntity, TradeOrderLineEntity } from '@/api/types/order'
type OrderTabValue = 'all' | 'wait_pay' | 'wait_shipping' | 'shipping' | 'after_sale'
@ -165,6 +157,13 @@ interface GoodsItem {
payAmount?: number
}
interface AmountSummary {
goodsAmount: number
shippingAmount: number
discountAmount: number
payAmount: number
}
interface LogisticsInfo {
title: string
desc: string
@ -173,11 +172,19 @@ interface LogisticsInfo {
icon: string
}
interface EnhancedOrderItem extends OrderListItem {
interface EnhancedOrderItem {
id: number
orderNo?: string | number
storeName: string
displayStatus: OrderStatus
formattedCreateTime: string
goods: GoodsItem[]
logisticsInfo: LogisticsInfo | null
amountSummary: AmountSummary
tradeOrderEntity: TradeOrderEntity
lines: TradeOrderLineEntity[]
vvPackageEntity?: OrderPackageEntity
trackNumber?: string
}
interface StatusActionConfig {
@ -195,10 +202,14 @@ const tabs: OrderTab[] = [
]
const ORDER_STATUS_META: Record<OrderStatus, { label: string; desc: string; className: string }> = {
create: { label: '待确认', desc: '订单创建成功', className: 'is-wait-pay' },
wait_pay: { label: '待付款', desc: '请尽快完成支付', className: 'is-wait-pay' },
wait_shipping: { label: '待发货', desc: '商家正在备货', className: 'is-wait-ship' },
shipping: { label: '运输中', desc: '包裹已发出', className: 'is-shipping' },
shipped: { label: '待取件', desc: '包裹已投递', className: 'is-shipping' },
delivered: { label: '已收货', desc: '欢迎评价本次购物', className: 'is-delivered' },
apply_cancel: { label: '取消处理中', desc: '正在处理取消请求', className: 'is-close' },
refund: { label: '退款中', desc: '退款审核中', className: 'is-refund' },
all_refund: { label: '退款完成', desc: '已原路退回', className: 'is-refund' },
part_refund: { label: '部分退款', desc: '退款处理中', className: 'is-refund' },
close: { label: '交易关闭', desc: '订单已关闭', className: 'is-close' },
@ -206,10 +217,14 @@ const ORDER_STATUS_META: Record<OrderStatus, { label: string; desc: string; clas
}
const ORDER_STATUS_ACTIONS: Record<OrderStatus, StatusActionConfig> = {
create: { text: ['cancel'], buttons: ['pay'], primary: 'pay' },
wait_pay: { text: ['cancel'], buttons: ['pay'], primary: 'pay' },
wait_shipping: { text: ['remind', 'contact'], buttons: ['buyAgain', 'applyRefund'], primary: 'applyRefund' },
shipping: { text: ['extend'], buttons: ['viewLogistics', 'buyAgain', 'confirm'], primary: 'confirm' },
shipped: { text: ['extend'], buttons: ['viewLogistics', 'buyAgain', 'confirm'], primary: 'confirm' },
delivered: { text: [], buttons: ['viewLogistics', 'buyAgain', 'review'], primary: 'review' },
apply_cancel: { text: ['contact'], buttons: ['buyAgain'], primary: 'buyAgain' },
refund: { text: ['contact'], buttons: ['buyAgain'], primary: 'buyAgain' },
all_refund: { text: ['contact'], buttons: ['buyAgain'], primary: 'buyAgain' },
part_refund: { text: ['contact'], buttons: ['buyAgain'], primary: 'buyAgain' },
close: { text: ['delete'], buttons: ['buyAgain'], primary: 'buyAgain' },
@ -253,30 +268,30 @@ const queryParams = reactive({
keyword: '',
})
const summaryMetrics = computed(() => {
if (!orders.value.length) return []
const stats = orders.value.reduce(
(acc, order) => {
acc.total += 1
if (order.displayStatus === 'wait_pay') acc.waitPay += 1
if (order.displayStatus === 'wait_shipping') acc.waitShip += 1
if (order.displayStatus === 'shipping') acc.waitReceive += 1
if (order.displayStatus === 'delivered') acc.received += 1
if (order.displayStatus === 'all_refund' || order.displayStatus === 'part_refund') acc.afterSale += 1
return acc
},
{ total: 0, waitPay: 0, waitShip: 0, waitReceive: 0, received: 0, afterSale: 0 },
)
// const summaryMetrics = computed(() => {
// if (!orders.value.length) return []
// const stats = orders.value.reduce(
// (acc, order) => {
// acc.total += 1
// if (order.displayStatus === 'wait_pay') acc.waitPay += 1
// if (order.displayStatus === 'wait_shipping') acc.waitShip += 1
// if (order.displayStatus === 'shipping') acc.waitReceive += 1
// if (order.displayStatus === 'delivered') acc.received += 1
// if (order.displayStatus === 'all_refund' || order.displayStatus === 'part_refund') acc.afterSale += 1
// return acc
// },
// { total: 0, waitPay: 0, waitShip: 0, waitReceive: 0, received: 0, afterSale: 0 },
// )
return [
{ label: '总订单', value: stats.total, desc: '含全部交易记录' },
{ label: '待付款', value: stats.waitPay, desc: '记得及时支付' },
{ label: '待发货', value: stats.waitShip, desc: '商家备货中' },
{ label: '待收货', value: stats.waitReceive, desc: '关注物流动态' },
{ label: '已收货', value: stats.received, desc: '欢迎前往评价' },
{ label: '售后/退款', value: stats.afterSale, desc: '售后处理中' },
]
})
// return [
// { label: '', value: stats.total, desc: '' },
// { label: '', value: stats.waitPay, desc: '' },
// { label: '', value: stats.waitShip, desc: '' },
// { label: '', value: stats.waitReceive, desc: '' },
// { label: '', value: stats.received, desc: '' },
// { label: '/退', value: stats.afterSale, desc: '' },
// ]
// })
const parseJSON = <T,>(value?: string | T): T | null => {
if (!value) return null
@ -318,89 +333,113 @@ const formatAmount = (value?: number | string) => {
return Number.isFinite(num) ? num.toFixed(2) : '0.00'
}
const hasTrackNumber = (order: OrderListItem) => Boolean(order.trackNumber || order.vvPackageEntity?.trackNumber || order.vvTradeOrderLineEntityList?.some((line) => Boolean(line.trackNumber)))
const safeNumber = (value?: number | string, fallback = 0) => {
const num = Number(value ?? fallback)
return Number.isFinite(num) ? num : fallback
}
const deriveStatus = (order: OrderListItem): OrderStatus => {
const reversed = order.reverseStatus?.toLowerCase?.()
if (reversed === 'all_refund') return 'all_refund'
if (reversed === 'part_refund') return 'part_refund'
const hasTrackNumber = (pkg?: OrderPackageEntity, lines?: TradeOrderLineEntity[]) => Boolean(pkg?.trackNumber || lines?.some((line) => Boolean(line.trackNumber)))
const raw = String(order.status || '').toLowerCase()
if (raw.includes('wait_pay')) return 'wait_pay'
if (raw.includes('wait_shipping')) return 'wait_shipping'
if (raw.includes('shipping')) return 'shipping'
if (raw.includes('delivered')) return 'delivered'
if (raw.includes('all_refund')) return 'all_refund'
if (raw.includes('part_refund')) return 'part_refund'
if (raw.includes('close')) return 'close'
if (hasTrackNumber(order)) return 'shipping'
const deriveStatus = (lines: TradeOrderLineEntity[], pkg?: OrderPackageEntity): OrderStatus => {
const [firstLine] = lines
const reversed = String(firstLine?.reverseStatus || '').toLowerCase()
if (reversed.includes('all')) return 'all_refund'
if (reversed.includes('part')) return 'part_refund'
if (reversed.includes('refund')) return 'refund'
const raw = String(firstLine?.status || '').toLowerCase()
if (raw === 'create') return 'create'
if (raw === 'wait_pay') return 'wait_pay'
if (raw === 'wait_shipping') return 'wait_shipping'
if (raw === 'apply_cancel') return 'apply_cancel'
if (raw === 'shipping') return 'shipping'
if (raw === 'shipped') return 'shipped'
if (raw === 'delivered') return 'delivered'
if (raw === 'close') return 'close'
if (raw === 'all_refund') return 'all_refund'
if (raw === 'part_refund') return 'part_refund'
if (raw === 'refund') return 'refund'
if (hasTrackNumber(pkg, lines)) return 'shipping'
return 'unknown'
}
const formatGoods = (order: OrderListItem): GoodsItem[] => {
const lines = order.vvTradeOrderLineEntityList
const formatGoods = (lines: TradeOrderLineEntity[], trade?: TradeOrderEntity): GoodsItem[] => {
if (Array.isArray(lines) && lines.length) {
return lines.map((line) => ({
id: line.id,
productId: line.productId ?? order.productId,
productName: line.productName ?? order.productName,
productMainImageUrl: line.productMainImageUrl ?? order.productMainImageUrl,
productId: line.productId,
productName: line.productName,
productMainImageUrl: line.skuImageUrl || line.productMainImageUrl,
skuDesc: parseSkuInfo(line.skuInfo),
num: line.num ?? order.num,
payAmount: line.singlePrice ?? line.payAmount ?? order.payAmount,
num: line.num ?? trade?.num ?? 1,
payAmount: line.promotionPrice ?? line.salePrice ?? line.payAmount ?? trade?.allPrice,
}))
}
return [
{
id: order.id,
productId: order.productId,
productName: order.productName,
productMainImageUrl: order.productMainImageUrl,
skuDesc: parseSkuInfo(order.skuInfo),
num: order.num,
payAmount: order.payAmount ?? order.promotionPrice,
id: trade?.id,
productId: undefined,
productName: '未知商品',
productMainImageUrl: '',
skuDesc: '',
num: trade?.num ?? 1,
payAmount: trade?.allPrice,
},
]
}
const buildLogisticsInfo = (order: OrderListItem): LogisticsInfo | null => {
const pkg = order.vvPackageEntity
const trackNumber = order.trackNumber || pkg?.trackNumber
if (trackNumber) {
const info = parseLogisticsInfo(pkg?.packageLogisticsInfo)
const latest = info?.data?.[0]
return {
title: pkg?.logisticsCompany || info?.com || '承运商处理中',
desc: latest?.context || '包裹运输中,请耐心等待',
sub: latest?.areaName || pkg?.shippingTo,
trackNumber,
icon: 'logistics',
}
const calcAmountSummary = (trade?: TradeOrderEntity, lines: TradeOrderLineEntity[] = [], pkg?: OrderPackageEntity): AmountSummary => {
const goodsAmount = lines.reduce((total, line) => {
const unitPrice = safeNumber(line.promotionPrice ?? line.salePrice ?? line.payAmount)
const quantity = safeNumber(line.num, 1)
return total + unitPrice * quantity
}, 0)
const shippingAmount = lines.reduce((total, line) => total + safeNumber(line.shippingAmount), 0) + safeNumber(pkg?.shippingAmount)
const payAmount = safeNumber(trade?.allPrice ?? goodsAmount + shippingAmount)
const normalizedGoodsAmount = goodsAmount || Math.max(payAmount - shippingAmount, 0)
const discountAmount = Math.max(normalizedGoodsAmount + shippingAmount - payAmount, 0)
return {
goodsAmount: normalizedGoodsAmount,
shippingAmount,
discountAmount,
payAmount,
}
}
const firstLine: TradeOrderLineEntity | undefined = order.vvTradeOrderLineEntityList?.[0]
if (firstLine) {
return {
title: '商家备货中',
desc: firstLine.logisticsDesc || '商家正在为您准备商品',
sub: order.tradeOrderEntity?.buyerDetailAddress || [order.province, order.city, order.district].filter(Boolean).join(' '),
trackNumber: firstLine.trackNumber,
icon: 'shop-o',
}
const buildLogisticsInfo = (pkg?: OrderPackageEntity): LogisticsInfo | null => {
if (!pkg?.trackNumber) return null
const info = parseLogisticsInfo(pkg.packageLogisticsInfo)
const latest = info?.data?.[0]
return {
title: pkg.logisticsCompany || info?.com || '承运商处理中',
desc: latest?.context || '包裹运输中,请耐心等待',
sub: latest?.areaName || pkg.shippingTo,
trackNumber: pkg.trackNumber,
icon: 'logistics',
}
return null
}
const normalizeOrder = (item: OrderListItem): EnhancedOrderItem => {
const displayStatus = deriveStatus(item)
const trade = item.tradeOrderEntity ?? ({} as TradeOrderEntity)
const lines = Array.isArray(item.appTradeOrderLineDTOList) ? item.appTradeOrderLineDTOList : []
const pkg = item.vvPackageEntity
const goods = formatGoods(lines, trade)
const displayStatus = deriveStatus(lines, pkg)
const orderId = trade.id ?? (lines[0]?.tradeOrderId as number) ?? 0
return {
...item,
id: orderId,
orderNo: trade.transactionId || trade.id,
storeName: trade.channel || trade.appName || '商家店铺',
displayStatus,
formattedCreateTime: formatTimestamp(item.createTime ?? item.tradeOrderEntity?.gmtDownOrder),
goods: formatGoods(item),
logisticsInfo: buildLogisticsInfo(item),
formattedCreateTime: formatTimestamp(trade.gmtDownOrder || trade.createTime),
goods,
logisticsInfo: buildLogisticsInfo(pkg),
amountSummary: calcAmountSummary(trade, lines, pkg),
tradeOrderEntity: trade,
lines,
vvPackageEntity: pkg,
trackNumber: pkg?.trackNumber || lines[0]?.trackNumber,
}
}
@ -448,12 +487,18 @@ const onCancel = () => {
handleGetList()
}
const getPrimaryGoods = (order: EnhancedOrderItem) => order.goods?.[0]
const handleAddToCart = (order: EnhancedOrderItem) => {
showToast(`已将「${order.productName || '该商品'}」加入购物车`)
const goodsName = getPrimaryGoods(order)?.productName || '该商品'
showToast(`已将「${goodsName}」加入购物车`)
}
const handleBuyAgain = (order: EnhancedOrderItem | number | string) => {
const id = typeof order === 'object' ? order.productId || order.id : order
const id =
typeof order === 'object'
? getPrimaryGoods(order)?.productId || order.id
: order
router.push({ name: 'commodity-detail', query: { id } })
}
@ -478,7 +523,8 @@ const handleApplyRefund = (order: EnhancedOrderItem) => {
}
const handleReview = (order: EnhancedOrderItem) => {
router.push({ name: 'review/write', query: { id: order.productId } })
const productId = getPrimaryGoods(order)?.productId || order.id
router.push({ name: 'review/write', query: { id: productId } })
}
const handleDeleteOrder = (order: EnhancedOrderItem) => {
@ -572,7 +618,8 @@ const handleGetList = () => {
const list = extractOrderRows(res?.data)
const normalized = list.map(normalizeOrder)
if (activeTab.value === 'after_sale') {
orders.value = normalized.filter((item) => item.displayStatus === 'all_refund' || item.displayStatus === 'part_refund')
const afterSaleStatuses: OrderStatus[] = ['all_refund', 'part_refund', 'refund']
orders.value = normalized.filter((item) => afterSaleStatuses.includes(item.displayStatus))
return
}
orders.value = normalized
@ -771,6 +818,7 @@ $prefix-cls: #{$namespace}-order-page;
font-size: 0.14rem;
color: #333;
display: -webkit-box;
line-clamp: 2;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
@ -818,6 +866,17 @@ $prefix-cls: #{$namespace}-order-page;
font-weight: 600;
color: #111;
}
.desc {
margin-top: 0.02rem;
font-size: 0.12rem;
color: #555;
display: -webkit-box;
line-clamp: 2;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
overflow: hidden;
line-height: 1.4;
}
.sub {
color: #999;
}

View File

@ -1,5 +1,5 @@
<template>
<div id="review-write" class="bg-[#f5f5f5] min-h-screen">
<div :class="`${prefixCls}`" class="bg-[#f5f5f5] min-h-screen">
<h3 class="pl-3 py-3 text-[#666]">您的评价将帮助其他用户</h3>
<van-cell-group>
<van-cell v-for="item in stars" :title="item.name" :key="item.name">
@ -13,20 +13,40 @@
</van-cell-group>
<van-field class="mt-2" left-icon="edit" v-model="message" rows="3" autosize label="" type="textarea" maxlength="200" placeholder="请输入文字" show-word-limit />
<div class="bg-white pl-3">
<van-uploader v-model="fileList" multiple />
<van-uploader v-model="fileList" multiple max-count="5" :deletable="true" :after-read="handleAfterRead" />
<van-checkbox class="py-[10px]" :icon-size="10" >公开</van-checkbox>
</div>
<footer class="mx-3 mt-3">
<van-button type="primary" size="large" class="h-10! text-sm! theme-bg-color theme-border-color">发布</van-button>
<van-button type="primary" size="large" class="h-10! text-sm! theme-bg-color theme-border-color" @click="handleSubmit" :disabled="buttonDisable">发布</van-button>
</footer>
</div>
</template>
<script setup lang="ts">
import { useDesign } from '@/hooks/web/useDesign'
// import { load } from 'vant'
import api from '@/api'
import { set } from 'es-toolkit/compat';
import type { UploaderFileListItem } from 'vant'
import type { CommentAddReqType } from '@/api/types/order'
import type { addUploadfileRepType } from '@/api/types/files'
const { getPrefixCls } = useDesign()
const prefixCls = getPrefixCls('__review-writer')
const message = ref<string>('')
const fileList = ref<addUploadfileRepType[]>([])
const buttonDisable = computed(() => {
return stars.some(v => v.starMap == '')
})
const stars = reactive([
{ name: '商品评价', value: 0, starMap: '' },
{ name: '快递包装', value: 0, starMap: '' },
{ name: '发货速度', value: 0, starMap: '' },
{ name: '售后服务', value: 0, starMap: '' },
{ name: '描述相符', value: 0, starMap: '', key: 'descMatch' },
{ name: '卖家服务', value: 0, starMap: '', key: 'sellerService' },
{ name: '流服务', value: 0, starMap: '', key: 'logisticsService' },
// { name: '', value: 0, starMap: '' },
])
const onChangeStar = (item: any, v: any) => {
@ -36,11 +56,62 @@ const onChangeStar = (item: any, v: any) => {
else item.starMap = '差'
}
const message = ref('')
const fileList = ref([])
const handleAfterRead = (file: UploaderFileListItem[]) => {
console.log(file)
const form = new FormData()
const files = Array.isArray(file) ? file : [file]
for (let i = 0; i < files.length; i++) {
console.log(files[i].file)
form.append('files', files[i].file)
}
api.files.addUploadfile
.post<addUploadfileRepType[]>(form)
.then((res) => {
res.data.forEach((v) => {
fileList.value.push(v)
})
console.log(res)
})
.catch((e) => {
console.log('catch', e)
})
fileList.value = []
}
const handleSubmit = () => {
const data: CommentAddReqType = {
buyerId: 0,
commentDetailList: fileList.value.map((v) => {
return {
commentUrl: v.url,
type: 'image',
}
}),
descMatch: 0,
logisticsService: 0,
sellerService: 0,
productComment: "message",
serviceComment:"",
skuId: 0,
tradeOrderId: 0
}
stars.forEach(v => {
set(data, v.key, v.starMap)
})
api.files.addUploadfile.post(data).then((res) => {
console.log(res)
})
}
</script>
<style scoped lang="scss">
#review-write {
<style lang="scss">
$prefix-cls: #{$namespace}__review-write;
.#{$prefix-cls} {
height: auto;
}
</style>