feat: 积分商城-完成
This commit is contained in:
@@ -0,0 +1,767 @@
|
||||
<template>
|
||||
<main class="after-sale-detail-page">
|
||||
<van-nav-bar title="售后详情" left-arrow @click-left="$router.replace('/joyful-selection/merchant')" />
|
||||
|
||||
<section v-if="loading" class="skeleton-card">
|
||||
<van-skeleton title :row="3" />
|
||||
<van-skeleton title :row="5" />
|
||||
<van-skeleton title :row="3" />
|
||||
</section>
|
||||
|
||||
<section v-show="!loading" class="status-card" :class="statusCardClass">
|
||||
<div class="status-icon">
|
||||
<van-icon :name="statusIcon" />
|
||||
</div>
|
||||
<div>
|
||||
<h2>{{ statusTitle }}</h2>
|
||||
<p>{{ statusDesc }}</p>
|
||||
<button v-if="afterSale.auditRemark" type="button" @click="showAuditRemark">查看审核意见</button>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section v-show="!loading" class="detail-card progress-card">
|
||||
<h3>售后进度</h3>
|
||||
<div class="timeline">
|
||||
<div v-for="step in progressSteps" :key="step.key" class="timeline-item" :class="step.state">
|
||||
<span class="timeline-dot">
|
||||
<van-icon v-if="step.state === 'done'" name="success" />
|
||||
</span>
|
||||
<div class="timeline-main">
|
||||
<b>{{ step.title }}</b>
|
||||
<em>{{ step.time || step.subTitle }}</em>
|
||||
</div>
|
||||
<p>{{ step.desc }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<section v-show="!loading" class="detail-card product-card">
|
||||
<h3>售后商品</h3>
|
||||
<article v-for="item in products" :key="item.id" class="product-row">
|
||||
<van-image class="product-thumb" :src="item.image" fit="cover" />
|
||||
<div class="product-info">
|
||||
<h4>{{ item.name || '-' }}</h4>
|
||||
<p>{{ skuText(item) }}</p>
|
||||
<b>¥{{ item.price || afterSale.amount || '0.00' }}</b>
|
||||
</div>
|
||||
<span>x{{ item.number || 1 }}</span>
|
||||
</article>
|
||||
</section>
|
||||
|
||||
<section v-if="!loading && showReturnInfo" class="detail-card address-card">
|
||||
<h3>{{ returnInfoTitle }}</h3>
|
||||
<p><span>联系人</span><b>{{ afterSale.pickupName || '-' }}</b></p>
|
||||
<p><span>联系电话</span><b>{{ maskedMobile(afterSale.pickupMobile) }}</b></p>
|
||||
<p><span>地址</span><b>{{ afterSale.pickupAddress || '-' }}</b></p>
|
||||
<p v-if="afterSale.userLogisticsNo" class="logistics-row">
|
||||
<span>寄回单号</span><b>{{ afterSale.userLogisticsCompany || '快递' }} {{ afterSale.userLogisticsNo }}</b>
|
||||
</p>
|
||||
<p v-if="afterSale.merchantLogisticsNo" class="logistics-row">
|
||||
<span>换货单号</span><b>{{ afterSale.merchantLogisticsCompany || '快递' }} {{ afterSale.merchantLogisticsNo }}</b>
|
||||
</p>
|
||||
</section>
|
||||
|
||||
<section v-show="!loading" class="detail-card info-card">
|
||||
<h3>售后信息</h3>
|
||||
<p class="service-no-row"><span>售后单号</span><b class="scrollable-no">{{ afterSale.serviceOrderId || '-' }}</b><button type="button" @click="copyServiceNo">复制</button></p>
|
||||
<p><span>子订单号</span><b>{{ afterSale.orderNumberId || afterSale.subOrderId || '-' }}</b></p>
|
||||
<p><span>售后类型</span><b>{{ afterSale.serviceTypeText || '-' }}</b></p>
|
||||
<p><span>售后原因</span><b>{{ afterSale.reason || '-' }}</b></p>
|
||||
<p><span>问题描述</span><b>{{ afterSale.problemDescription || '-' }}</b></p>
|
||||
<p><span>申请时间</span><b>{{ afterSale.applyTime || '-' }}</b></p>
|
||||
<p v-if="!isUnsignedAfterSale"><span>取件方式</span><b>{{ pickupMethodText }}</b></p>
|
||||
<p v-if="!isUnsignedAfterSale"><span>取件地址</span><b>{{ pickupAddressText }}</b></p>
|
||||
<div v-if="images.length" class="voucher-block">
|
||||
<h4>上传凭证</h4>
|
||||
<div>
|
||||
<van-image v-for="image in images" :key="image.url" :src="image.url" fit="cover" />
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<footer v-show="!loading && merchantActions.length" class="bottom-actions">
|
||||
<button
|
||||
v-for="action in merchantActions"
|
||||
:key="action.key"
|
||||
type="button"
|
||||
:class="{ primary: action.primary }"
|
||||
@click="handleAction(action.key)"
|
||||
>
|
||||
{{ action.text }}
|
||||
</button>
|
||||
</footer>
|
||||
|
||||
<van-popup v-model="showExchangeShipPopup" round position="bottom">
|
||||
<section class="action-popup">
|
||||
<h3>填写换货发货信息</h3>
|
||||
<van-field v-model="exchangeShipForm.logisticsCompany" label="快递公司" placeholder="请输入快递公司" />
|
||||
<van-field v-model="exchangeShipForm.logisticsNo" label="快递单号" placeholder="请输入快递单号" />
|
||||
<button type="button" @click="submitExchangeShip">确认发货</button>
|
||||
</section>
|
||||
</van-popup>
|
||||
</main>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { mapActions, mapState } from 'pinia'
|
||||
import { Dialog, Toast } from 'vant'
|
||||
import { postFunction } from '@/api/supplied-service'
|
||||
import { useUserStore } from '@/store/user'
|
||||
|
||||
const SERVICE_TYPE = {
|
||||
RETURN: 4,
|
||||
EXCHANGE: 5
|
||||
}
|
||||
|
||||
const SERVICE_STATE = {
|
||||
PENDING_AUDIT: 0,
|
||||
APPROVED: 1,
|
||||
REJECTED: 2,
|
||||
USER_SHIPPED: 3,
|
||||
RECEIVED: 4,
|
||||
FINISHED: 5,
|
||||
CANCELLED: 6
|
||||
}
|
||||
|
||||
const LOGISTICS_STATE = {
|
||||
NONE: 0,
|
||||
WAIT_USER_SHIP: 1,
|
||||
USER_SHIPPED: 2,
|
||||
MERCHANT_RECEIVED: 3,
|
||||
MERCHANT_SHIPPED: 4,
|
||||
USER_RECEIVED: 5
|
||||
}
|
||||
|
||||
const SKU_COLOR_MAP = {
|
||||
cream: '奶油白',
|
||||
blue: '雾霾蓝',
|
||||
orange: '暖橙色'
|
||||
}
|
||||
|
||||
const SKU_SIZE_MAP = {
|
||||
standard: '标准款',
|
||||
plus: '升级款'
|
||||
}
|
||||
|
||||
export default {
|
||||
name: 'JoyfulSelectionMerchantAfterSaleDetail',
|
||||
data: () => ({
|
||||
afterSale: {},
|
||||
loading: false,
|
||||
showExchangeShipPopup: false,
|
||||
exchangeShipForm: {
|
||||
logisticsCompany: '',
|
||||
logisticsNo: ''
|
||||
}
|
||||
}),
|
||||
computed: {
|
||||
...mapState(useUserStore, ['supplierId']),
|
||||
products() {
|
||||
return this.afterSale.products || []
|
||||
},
|
||||
images() {
|
||||
return Array.isArray(this.afterSale.images)
|
||||
? this.afterSale.images.map(item => ({
|
||||
url: item.url || item.previewUrl || item
|
||||
})).filter(item => item.url)
|
||||
: []
|
||||
},
|
||||
isExchange() {
|
||||
return Number(this.afterSale.serviceType) === SERVICE_TYPE.EXCHANGE
|
||||
},
|
||||
isUnsignedAfterSale() {
|
||||
if (this.afterSale.isSigned === false || this.afterSale.isSigned === 0 || this.afterSale.isSigned === 'false') {
|
||||
return true
|
||||
}
|
||||
|
||||
return this.afterSale.isSigned === undefined &&
|
||||
Number(this.afterSale.logisticsState) === LOGISTICS_STATE.NONE &&
|
||||
!this.afterSale.pickupMethod &&
|
||||
!this.afterSale.pickupName &&
|
||||
!this.afterSale.pickupMobile &&
|
||||
!this.afterSale.pickupAddress
|
||||
},
|
||||
statusCardClass() {
|
||||
if (Number(this.afterSale.serviceState) === SERVICE_STATE.REJECTED) return 'rejected'
|
||||
if (Number(this.afterSale.serviceState) === SERVICE_STATE.APPROVED) return 'approved'
|
||||
if (Number(this.afterSale.serviceState) === SERVICE_STATE.FINISHED) return 'finished'
|
||||
return 'pending'
|
||||
},
|
||||
statusIcon() {
|
||||
if (this.statusCardClass === 'approved' || this.statusCardClass === 'finished') return 'success'
|
||||
if (this.statusCardClass === 'rejected') return 'cross'
|
||||
return 'clock-o'
|
||||
},
|
||||
statusTitle() {
|
||||
const serviceState = Number(this.afterSale.serviceState)
|
||||
const logisticsState = Number(this.afterSale.logisticsState)
|
||||
if (serviceState === SERVICE_STATE.REJECTED) return '审核未通过'
|
||||
if (serviceState === SERVICE_STATE.FINISHED) return '售后完成'
|
||||
if (logisticsState === LOGISTICS_STATE.MERCHANT_SHIPPED) return '商家已发货'
|
||||
if (logisticsState === LOGISTICS_STATE.MERCHANT_RECEIVED && this.isExchange) return '待商家发货'
|
||||
if (logisticsState === LOGISTICS_STATE.USER_SHIPPED) return '用户已寄回'
|
||||
if (serviceState === SERVICE_STATE.APPROVED) return '审核通过'
|
||||
return '待审核'
|
||||
},
|
||||
statusDesc() {
|
||||
const serviceState = Number(this.afterSale.serviceState)
|
||||
const logisticsState = Number(this.afterSale.logisticsState)
|
||||
if (serviceState === SERVICE_STATE.REJECTED) return '商家已拒绝该售后申请'
|
||||
if (serviceState === SERVICE_STATE.FINISHED) return '售后流程已完成'
|
||||
if (logisticsState === LOGISTICS_STATE.MERCHANT_SHIPPED) return '换货商品已发出,等待用户收货'
|
||||
if (logisticsState === LOGISTICS_STATE.MERCHANT_RECEIVED && this.isExchange) return '已收到寄回商品,请发出换货商品'
|
||||
if (logisticsState === LOGISTICS_STATE.USER_SHIPPED) return '用户已寄回商品,请确认收货'
|
||||
if (this.isExchange && this.isUnsignedAfterSale && serviceState === SERVICE_STATE.APPROVED) return '未签收换货申请已通过,请发出换货商品'
|
||||
if (serviceState === SERVICE_STATE.APPROVED) return '已通过售后申请,等待用户寄回商品'
|
||||
return '请审核用户提交的售后申请'
|
||||
},
|
||||
progressSteps() {
|
||||
const serviceState = Number(this.afterSale.serviceState)
|
||||
const logisticsState = Number(this.afterSale.logisticsState)
|
||||
const steps = [
|
||||
{
|
||||
key: 'apply',
|
||||
title: '提交申请',
|
||||
time: this.afterSale.applyTime,
|
||||
desc: '用户已提交售后申请',
|
||||
state: 'done'
|
||||
},
|
||||
{
|
||||
key: 'audit',
|
||||
title: '商家审核',
|
||||
time: this.afterSale.auditTime,
|
||||
desc: serviceState === SERVICE_STATE.REJECTED ? '商家审核未通过' : serviceState >= SERVICE_STATE.APPROVED ? '商家已审核通过' : '待商家审核',
|
||||
state: serviceState >= SERVICE_STATE.APPROVED || serviceState === SERVICE_STATE.REJECTED ? 'done' : 'current'
|
||||
},
|
||||
{
|
||||
key: 'userShip',
|
||||
title: '寄回商品',
|
||||
time: this.afterSale.userShipTime,
|
||||
desc: logisticsState >= LOGISTICS_STATE.USER_SHIPPED ? '用户已寄回商品' : '等待用户寄回商品',
|
||||
state: logisticsState >= LOGISTICS_STATE.USER_SHIPPED ? 'done' : logisticsState === LOGISTICS_STATE.WAIT_USER_SHIP ? 'current' : 'todo'
|
||||
},
|
||||
{
|
||||
key: 'merchantReceive',
|
||||
title: '商家收货',
|
||||
time: this.afterSale.merchantReceiveTime,
|
||||
desc: logisticsState >= LOGISTICS_STATE.MERCHANT_RECEIVED ? '商家已收到商品' : '商家尚未收到商品',
|
||||
state: logisticsState >= LOGISTICS_STATE.MERCHANT_RECEIVED ? 'done' : logisticsState === LOGISTICS_STATE.USER_SHIPPED ? 'current' : 'todo'
|
||||
}
|
||||
]
|
||||
|
||||
if (this.isUnsignedAfterSale && !this.isExchange) {
|
||||
steps.splice(2)
|
||||
steps.push({
|
||||
key: 'finish',
|
||||
title: '售后完成',
|
||||
time: this.afterSale.finishTime,
|
||||
desc: serviceState === SERVICE_STATE.FINISHED ? '售后已完成' : '审核通过后售后完成',
|
||||
state: serviceState === SERVICE_STATE.FINISHED ? 'done' : serviceState >= SERVICE_STATE.APPROVED ? 'current' : 'todo'
|
||||
})
|
||||
|
||||
return this.normalizeSteps(steps)
|
||||
}
|
||||
|
||||
if (this.isExchange) {
|
||||
if (this.isUnsignedAfterSale) {
|
||||
steps.splice(2)
|
||||
}
|
||||
|
||||
steps.push(
|
||||
{
|
||||
key: 'merchantShip',
|
||||
title: '商家发货',
|
||||
time: this.afterSale.merchantShipTime,
|
||||
desc: logisticsState >= LOGISTICS_STATE.MERCHANT_SHIPPED ? '商家已发出换货商品' : '等待商家发出换货商品',
|
||||
state: logisticsState >= LOGISTICS_STATE.MERCHANT_SHIPPED ? 'done' : (this.isUnsignedAfterSale ? serviceState >= SERVICE_STATE.APPROVED : logisticsState === LOGISTICS_STATE.MERCHANT_RECEIVED) ? 'current' : 'todo'
|
||||
},
|
||||
{
|
||||
key: 'userReceive',
|
||||
title: '用户收货',
|
||||
time: this.afterSale.finishTime,
|
||||
desc: logisticsState >= LOGISTICS_STATE.USER_RECEIVED ? '用户已收到换货商品' : '等待用户收货',
|
||||
state: logisticsState >= LOGISTICS_STATE.USER_RECEIVED ? 'done' : logisticsState === LOGISTICS_STATE.MERCHANT_SHIPPED ? 'current' : 'todo'
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
steps.push({
|
||||
key: 'finish',
|
||||
title: '售后完成',
|
||||
time: this.afterSale.finishTime,
|
||||
desc: serviceState === SERVICE_STATE.FINISHED ? '售后流程已完成' : '售后流程未完成',
|
||||
state: serviceState === SERVICE_STATE.FINISHED ? 'done' : !this.isExchange && logisticsState === LOGISTICS_STATE.MERCHANT_RECEIVED ? 'current' : 'todo'
|
||||
})
|
||||
|
||||
return this.normalizeSteps(steps)
|
||||
},
|
||||
showReturnInfo() {
|
||||
return Number(this.afterSale.serviceState) >= SERVICE_STATE.APPROVED && !this.isUnsignedAfterSale
|
||||
},
|
||||
returnInfoTitle() {
|
||||
return Number(this.afterSale.logisticsState) >= LOGISTICS_STATE.WAIT_USER_SHIP ? '退货地址' : '取件地址'
|
||||
},
|
||||
pickupMethodText() {
|
||||
return this.afterSale.pickupMethod === 'mail' ? '自行寄回' : '上门取件'
|
||||
},
|
||||
pickupAddressText() {
|
||||
return this.afterSale.pickupAddress || '-'
|
||||
},
|
||||
merchantActions() {
|
||||
const serviceState = Number(this.afterSale.serviceState)
|
||||
if (serviceState === SERVICE_STATE.PENDING_AUDIT) {
|
||||
return [
|
||||
{ key: 'reject', text: '拒绝' },
|
||||
{ key: 'approve', text: '通过', primary: true }
|
||||
]
|
||||
}
|
||||
|
||||
if (Number(this.afterSale.serviceState) === SERVICE_STATE.USER_SHIPPED) {
|
||||
return [{ key: 'receive', text: '确认收货', primary: true }]
|
||||
}
|
||||
|
||||
if (this.canShipExchange) {
|
||||
return [{ key: 'shipExchange', text: '换货发货', primary: true }]
|
||||
}
|
||||
|
||||
return []
|
||||
},
|
||||
canShipExchange() {
|
||||
return this.isExchange &&
|
||||
(Number(this.afterSale.serviceState) === SERVICE_STATE.RECEIVED ||
|
||||
(this.afterSale.isSigned === false && Number(this.afterSale.serviceState) === SERVICE_STATE.APPROVED))
|
||||
}
|
||||
},
|
||||
created() {
|
||||
this.loadDetail()
|
||||
},
|
||||
methods: {
|
||||
...mapActions(useUserStore, ['fetchBridgeToken']),
|
||||
normalizeSteps(steps) {
|
||||
const serviceState = Number(this.afterSale.serviceState)
|
||||
return steps.map((step, index) => ({
|
||||
...step,
|
||||
state: serviceState === SERVICE_STATE.REJECTED && index > 1 ? 'todo' : step.state,
|
||||
subTitle: step.state === 'done' ? '' : '-'
|
||||
}))
|
||||
},
|
||||
skuText(item = {}) {
|
||||
const sku = String(item.sku || item.skuText || '').trim()
|
||||
const parts = sku.split('-').slice(-2).filter(Boolean)
|
||||
const color = SKU_COLOR_MAP[parts[0]] || parts[0]
|
||||
const size = SKU_SIZE_MAP[parts[1]] || parts[1]
|
||||
|
||||
return color || size ? [color, size].filter(Boolean).join(' | ') : '-'
|
||||
},
|
||||
maskedMobile(mobile = '') {
|
||||
return String(mobile).replace(/^(\d{3})\d{4}(\d{4})$/, '$1****$2') || '-'
|
||||
},
|
||||
async loadDetail() {
|
||||
try {
|
||||
this.loading = true
|
||||
await this.fetchBridgeToken()
|
||||
const data = await postFunction('merchant-after-sale-detail', {
|
||||
supplierId: this.supplierId,
|
||||
serviceOrderId: String(this.$route.query.serviceOrderId || '')
|
||||
})
|
||||
|
||||
this.afterSale = data?.afterSale || {}
|
||||
} catch (error) {
|
||||
Toast(error.message || '查询售后详情失败,请稍后再试')
|
||||
} finally {
|
||||
this.loading = false
|
||||
}
|
||||
},
|
||||
copyServiceNo() {
|
||||
if (navigator.clipboard && this.afterSale.serviceOrderId) {
|
||||
navigator.clipboard.writeText(this.afterSale.serviceOrderId)
|
||||
}
|
||||
Toast('已复制')
|
||||
},
|
||||
showAuditRemark() {
|
||||
Dialog.alert({
|
||||
title: '审核意见',
|
||||
message: this.afterSale.auditRemark
|
||||
})
|
||||
},
|
||||
handleAction(key) {
|
||||
if (key === 'approve') {
|
||||
this.auditAfterSale(1)
|
||||
return
|
||||
}
|
||||
if (key === 'reject') {
|
||||
this.auditAfterSale(2)
|
||||
return
|
||||
}
|
||||
if (key === 'receive') {
|
||||
this.receiveAfterSale()
|
||||
return
|
||||
}
|
||||
if (key === 'shipExchange') {
|
||||
this.openExchangeShipPopup()
|
||||
}
|
||||
},
|
||||
async auditAfterSale(auditStatus) {
|
||||
await Dialog.confirm({
|
||||
title: auditStatus === 1 ? '确认通过售后申请?' : '确认拒绝售后申请?',
|
||||
message: auditStatus === 1 ? '通过后用户可寄回商品。' : '拒绝后该售后申请将结束。'
|
||||
})
|
||||
await postFunction('merchant-after-sale-audit', {
|
||||
supplierId: this.supplierId,
|
||||
serviceOrderId: this.afterSale.serviceOrderId,
|
||||
auditStatus,
|
||||
auditRemark: auditStatus === 1 ? '审核通过' : '商家拒绝'
|
||||
})
|
||||
Toast('处理成功')
|
||||
await this.loadDetail()
|
||||
},
|
||||
async receiveAfterSale() {
|
||||
await Dialog.confirm({
|
||||
title: '确认已收到寄回商品?',
|
||||
message: '确认后退货售后会进入完成,换货售后会进入待商家寄货。'
|
||||
})
|
||||
await postFunction('merchant-after-sale-receive', {
|
||||
supplierId: this.supplierId,
|
||||
serviceOrderId: this.afterSale.serviceOrderId
|
||||
})
|
||||
Toast('已确认收货')
|
||||
await this.loadDetail()
|
||||
},
|
||||
openExchangeShipPopup() {
|
||||
this.exchangeShipForm = {
|
||||
logisticsCompany: '',
|
||||
logisticsNo: ''
|
||||
}
|
||||
this.showExchangeShipPopup = true
|
||||
},
|
||||
async submitExchangeShip() {
|
||||
if (!this.exchangeShipForm.logisticsCompany || !this.exchangeShipForm.logisticsNo) {
|
||||
Toast('请填写完整发货信息')
|
||||
return
|
||||
}
|
||||
|
||||
await postFunction('merchant-after-sale-ship', {
|
||||
supplierId: this.supplierId,
|
||||
serviceOrderId: this.afterSale.serviceOrderId,
|
||||
logisticsCompany: this.exchangeShipForm.logisticsCompany,
|
||||
logisticsNo: this.exchangeShipForm.logisticsNo
|
||||
})
|
||||
Toast('换货发货成功')
|
||||
this.showExchangeShipPopup = false
|
||||
await this.loadDetail()
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="less">
|
||||
.after-sale-detail-page {
|
||||
min-height: 100vh;
|
||||
padding: 56px 10px 78px;
|
||||
background: #f7f8fa;
|
||||
}
|
||||
|
||||
.after-sale-detail-page ::v-deep .van-nav-bar {
|
||||
position: fixed;
|
||||
z-index: 20;
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
}
|
||||
|
||||
.status-card,
|
||||
.skeleton-card,
|
||||
.detail-card {
|
||||
margin-bottom: 10px;
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.skeleton-card {
|
||||
padding: 18px 14px;
|
||||
}
|
||||
|
||||
.status-card {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
padding: 18px 14px;
|
||||
}
|
||||
|
||||
.status-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex: none;
|
||||
width: 42px;
|
||||
height: 42px;
|
||||
border-radius: 50%;
|
||||
color: #fff;
|
||||
font-size: 24px;
|
||||
background: #ff9f1a;
|
||||
}
|
||||
|
||||
.status-card.approved .status-icon,
|
||||
.status-card.finished .status-icon {
|
||||
background: #20b879;
|
||||
}
|
||||
|
||||
.status-card.rejected .status-icon {
|
||||
background: #ee3f4d;
|
||||
}
|
||||
|
||||
.status-card h2 {
|
||||
margin: 0 0 6px;
|
||||
color: #222;
|
||||
font-size: 18px;
|
||||
}
|
||||
|
||||
.status-card p {
|
||||
margin: 0;
|
||||
color: #666;
|
||||
font-size: 13px;
|
||||
line-height: 20px;
|
||||
}
|
||||
|
||||
.status-card button {
|
||||
margin-top: 8px;
|
||||
border: 0;
|
||||
color: #ff5b20;
|
||||
font-size: 12px;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.info-card button {
|
||||
flex: none;
|
||||
align-self: center;
|
||||
height: 24px;
|
||||
margin-left: 8px;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
color: #ff5b20;
|
||||
font-size: 12px;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
.detail-card {
|
||||
padding: 14px 12px;
|
||||
}
|
||||
|
||||
.detail-card h3 {
|
||||
margin: 0 0 12px;
|
||||
color: #222;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.timeline-item {
|
||||
position: relative;
|
||||
padding-left: 24px;
|
||||
padding-bottom: 16px;
|
||||
}
|
||||
|
||||
.timeline-item::before {
|
||||
content: '';
|
||||
position: absolute;
|
||||
top: 12px;
|
||||
bottom: -2px;
|
||||
left: 6px;
|
||||
width: 1px;
|
||||
background: #e5e5e5;
|
||||
}
|
||||
|
||||
.timeline-item:last-child {
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.timeline-item:last-child::before {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.timeline-dot {
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
left: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 13px;
|
||||
height: 13px;
|
||||
border-radius: 50%;
|
||||
color: #fff;
|
||||
font-size: 10px;
|
||||
background: #d8d8d8;
|
||||
}
|
||||
|
||||
.timeline-item.done .timeline-dot {
|
||||
background: #20b879;
|
||||
}
|
||||
|
||||
.timeline-item.current .timeline-dot {
|
||||
background: #ff5b20;
|
||||
}
|
||||
|
||||
.timeline-main {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
.timeline-main b {
|
||||
color: #222;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.timeline-main em,
|
||||
.timeline-item p {
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
.timeline-item p {
|
||||
margin: 6px 0 0;
|
||||
}
|
||||
|
||||
.product-row {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.product-thumb {
|
||||
flex: none;
|
||||
width: 72px;
|
||||
height: 72px;
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.product-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.product-info h4 {
|
||||
margin: 0 0 6px;
|
||||
color: #222;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.product-info p {
|
||||
margin: 0 0 8px;
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.product-info b {
|
||||
color: #ff5b20;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.product-row > span {
|
||||
color: #999;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.address-card p,
|
||||
.info-card p {
|
||||
display: flex;
|
||||
align-items: flex-start;
|
||||
justify-content: space-between;
|
||||
gap: 12px;
|
||||
margin: 10px 0 0;
|
||||
color: #999;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.address-card b,
|
||||
.info-card b {
|
||||
flex: 1;
|
||||
color: #333;
|
||||
font-weight: 500;
|
||||
text-align: right;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.info-card .service-no-row {
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.info-card .scrollable-no {
|
||||
min-width: 0;
|
||||
overflow-x: auto;
|
||||
scrollbar-width: none;
|
||||
white-space: nowrap;
|
||||
word-break: normal;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
.info-card .scrollable-no::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.voucher-block h4 {
|
||||
margin: 14px 0 8px;
|
||||
color: #333;
|
||||
font-size: 13px;
|
||||
}
|
||||
|
||||
.voucher-block div {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.voucher-block .van-image {
|
||||
width: 72px;
|
||||
height: 72px;
|
||||
border-radius: 6px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.bottom-actions {
|
||||
position: fixed;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
z-index: 10;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 10px;
|
||||
padding: 10px 12px calc(10px + env(safe-area-inset-bottom));
|
||||
background: #fff;
|
||||
box-shadow: 0 -4px 14px rgba(0, 0, 0, 0.06);
|
||||
}
|
||||
|
||||
.bottom-actions button,
|
||||
.action-popup button {
|
||||
min-width: 96px;
|
||||
height: 36px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 18px;
|
||||
color: #333;
|
||||
font-size: 13px;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.bottom-actions button.primary,
|
||||
.action-popup button {
|
||||
border-color: #ff5b20;
|
||||
color: #fff;
|
||||
background: #ff5b20;
|
||||
}
|
||||
|
||||
.action-popup {
|
||||
padding: 18px 14px 22px;
|
||||
}
|
||||
|
||||
.action-popup h3 {
|
||||
margin: 0 0 12px;
|
||||
color: #222;
|
||||
font-size: 16px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.action-popup button {
|
||||
width: 100%;
|
||||
margin-top: 12px;
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user