|
@@ -1,6 +1,6 @@
|
|
|
- <!--
|
|
|
|
|
|
|
+<!--
|
|
|
商城订单申请售后页
|
|
商城订单申请售后页
|
|
|
- 供订单详情「申请售后」进入;顾客勾选商品/金额后提交待审核申请,商家在 hdApp 审核
|
|
|
|
|
|
|
+ 供订单详情「申请售后」进入;顾客勾选商品/金额、可选上传照片后提交待审核申请,商家在 hdApp 审核可见图片
|
|
|
仅当存在待审核申请时锁进度页不可再提交;无待审核则直接进表单(含已取消/通过/驳回后再申)
|
|
仅当存在待审核申请时锁进度页不可再提交;无待审核则直接进表单(含已取消/通过/驳回后再申)
|
|
|
退货并退款金额:普通商品按勾选原价分摊会员折/满减;秒杀、团购按活动单价退,不再叠整单优惠
|
|
退货并退款金额:普通商品按勾选原价分摊会员折/满减;秒杀、团购按活动单价退,不再叠整单优惠
|
|
|
红包全额抵扣导致实付为 0 时仍可申请:提交 0 元,审核通过后退回红包
|
|
红包全额抵扣导致实付为 0 时仍可申请:提交 0 元,审核通过后退回红包
|
|
@@ -27,10 +27,24 @@
|
|
|
<text class="label">驳回原因</text>
|
|
<text class="label">驳回原因</text>
|
|
|
<text class="value reject">{{ apply.rejectReason }}</text>
|
|
<text class="value reject">{{ apply.rejectReason }}</text>
|
|
|
</view>
|
|
</view>
|
|
|
- <view class="status-row">
|
|
|
|
|
|
|
+ <view class="status-row" v-if="apply.addTime">
|
|
|
<text class="label">申请时间</text>
|
|
<text class="label">申请时间</text>
|
|
|
<text class="value">{{ apply.addTime || '' }}</text>
|
|
<text class="value">{{ apply.addTime || '' }}</text>
|
|
|
</view>
|
|
</view>
|
|
|
|
|
+ <!-- 已提交的售后图,便于顾客确认 -->
|
|
|
|
|
+ <view class="apply-imgs" v-if="apply.smallImgList && apply.smallImgList.length">
|
|
|
|
|
+ <text class="label">售后图片</text>
|
|
|
|
|
+ <view class="apply-img-grid">
|
|
|
|
|
+ <image
|
|
|
|
|
+ v-for="(img, idx) in apply.smallImgList"
|
|
|
|
|
+ :key="idx"
|
|
|
|
|
+ class="apply-img"
|
|
|
|
|
+ :src="img"
|
|
|
|
|
+ mode="aspectFill"
|
|
|
|
|
+ @tap="previewApplyImg(idx)"
|
|
|
|
|
+ />
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
<view class="bottom-actions">
|
|
<view class="bottom-actions">
|
|
@@ -194,6 +208,40 @@
|
|
|
</view>
|
|
</view>
|
|
|
</view>
|
|
</view>
|
|
|
|
|
|
|
|
|
|
+ <!-- 售后图片:选填,提交后商家审核可见 -->
|
|
|
|
|
+ <view class="card-section">
|
|
|
|
|
+ <view class="section-title">
|
|
|
|
|
+ <text class="title-text">售后图片</text>
|
|
|
|
|
+ <text class="title-sub">选填,最多9张</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view class="upload-body">
|
|
|
|
|
+ <view class="upload-grid">
|
|
|
|
|
+ <view
|
|
|
|
|
+ v-for="(img, idx) in refundImgShow"
|
|
|
|
|
+ :key="idx"
|
|
|
|
|
+ class="upload-item"
|
|
|
|
|
+ >
|
|
|
|
|
+ <image
|
|
|
|
|
+ class="upload-item__img"
|
|
|
|
|
+ :src="img"
|
|
|
|
|
+ mode="aspectFill"
|
|
|
|
|
+ @tap="previewUploadImg(idx)"
|
|
|
|
|
+ />
|
|
|
|
|
+ <view class="upload-item__del" @tap.stop="removeUploadImg(idx)">×</view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <view
|
|
|
|
|
+ v-if="uploadRefundImg.length < 9"
|
|
|
|
|
+ class="upload-item upload-item--add"
|
|
|
|
|
+ @tap="chooseRefundImg"
|
|
|
|
|
+ >
|
|
|
|
|
+ <text class="upload-add-icon">+</text>
|
|
|
|
|
+ <text class="upload-add-text">上传</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ <text class="upload-tip">建议拍摄问题商品照片,便于商家更快审核</text>
|
|
|
|
|
+ </view>
|
|
|
|
|
+ </view>
|
|
|
|
|
+
|
|
|
<view class="bottom-actions">
|
|
<view class="bottom-actions">
|
|
|
<button class="action-btn cancel-btn" @tap="onFormCancel">取消</button>
|
|
<button class="action-btn cancel-btn" @tap="onFormCancel">取消</button>
|
|
|
<button class="action-btn confirm-btn" @tap="askSubmit">提交申请</button>
|
|
<button class="action-btn confirm-btn" @tap="askSubmit">提交申请</button>
|
|
@@ -204,6 +252,8 @@
|
|
|
|
|
|
|
|
<script>
|
|
<script>
|
|
|
import { getRefundData, createRefund, cancelRefund } from '@/api/refund'
|
|
import { getRefundData, createRefund, cancelRefund } from '@/api/refund'
|
|
|
|
|
+import constant from '@/constant/index'
|
|
|
|
|
+import { TOKEN_STORAGE_KEY } from '@/constant/storageKeys'
|
|
|
|
|
|
|
|
export default {
|
|
export default {
|
|
|
name: 'orderRefund',
|
|
name: 'orderRefund',
|
|
@@ -218,7 +268,12 @@ export default {
|
|
|
apply: null,
|
|
apply: null,
|
|
|
canApply: 0,
|
|
canApply: 0,
|
|
|
refundDeadlineTip: '',
|
|
refundDeadlineTip: '',
|
|
|
- submitting: false
|
|
|
|
|
|
|
+ submitting: false,
|
|
|
|
|
+ /** 展示用完整 URL */
|
|
|
|
|
+ refundImgShow: [],
|
|
|
|
|
+ /** 提交用短路径 */
|
|
|
|
|
+ uploadRefundImg: [],
|
|
|
|
|
+ uploadingImg: false
|
|
|
}
|
|
}
|
|
|
},
|
|
},
|
|
|
computed: {
|
|
computed: {
|
|
@@ -564,13 +619,17 @@ export default {
|
|
|
price: this.refundMoney,
|
|
price: this.refundMoney,
|
|
|
remark: this.remark,
|
|
remark: this.remark,
|
|
|
refundType: this.refundType,
|
|
refundType: this.refundType,
|
|
|
- product: JSON.stringify(selectProduct || [])
|
|
|
|
|
|
|
+ product: JSON.stringify(selectProduct || []),
|
|
|
|
|
+ // 短路径 JSON,后端写入 xhHdRefund.imgList
|
|
|
|
|
+ imgList: JSON.stringify(this.uploadRefundImg || [])
|
|
|
}
|
|
}
|
|
|
createRefund(params).then(res => {
|
|
createRefund(params).then(res => {
|
|
|
uni.hideLoading()
|
|
uni.hideLoading()
|
|
|
this.submitting = false
|
|
this.submitting = false
|
|
|
if (res.code == 1) {
|
|
if (res.code == 1) {
|
|
|
this.$msg(res.msg || '提交成功')
|
|
this.$msg(res.msg || '提交成功')
|
|
|
|
|
+ this.refundImgShow = []
|
|
|
|
|
+ this.uploadRefundImg = []
|
|
|
this.init()
|
|
this.init()
|
|
|
}
|
|
}
|
|
|
}).catch(() => {
|
|
}).catch(() => {
|
|
@@ -578,6 +637,99 @@ export default {
|
|
|
this.submitting = false
|
|
this.submitting = false
|
|
|
})
|
|
})
|
|
|
},
|
|
},
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 选择并上传售后图:走 /upload/save-file,短路径入库、展示用 smallUrl
|
|
|
|
|
+ */
|
|
|
|
|
+ chooseRefundImg() {
|
|
|
|
|
+ if (this.uploadingImg) {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ const remain = 9 - this.uploadRefundImg.length
|
|
|
|
|
+ if (remain <= 0) {
|
|
|
|
|
+ this.$msg('最多上传9张')
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ uni.chooseImage({
|
|
|
|
|
+ count: remain,
|
|
|
|
|
+ sizeType: ['compressed'],
|
|
|
|
|
+ sourceType: ['album', 'camera'],
|
|
|
|
|
+ success: (res) => {
|
|
|
|
|
+ const paths = res.tempFilePaths || []
|
|
|
|
|
+ this.uploadImageFiles(paths)
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ },
|
|
|
|
|
+ /** 串行上传,避免并发打满接口 */
|
|
|
|
|
+ uploadImageFiles(paths) {
|
|
|
|
|
+ if (!paths.length) {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ this.uploadingImg = true
|
|
|
|
|
+ uni.showLoading({ title: '上传中', mask: true })
|
|
|
|
|
+ const token = uni.getStorageSync(TOKEN_STORAGE_KEY) || uni.getStorageSync('token') || ''
|
|
|
|
|
+ const uploadOne = (filePath) => new Promise((resolve, reject) => {
|
|
|
|
|
+ uni.uploadFile({
|
|
|
|
|
+ url: constant.hostUrl + '/upload/save-file',
|
|
|
|
|
+ filePath,
|
|
|
|
|
+ name: 'file',
|
|
|
|
|
+ header: { token },
|
|
|
|
|
+ success: (uploadRes) => {
|
|
|
|
|
+ try {
|
|
|
|
|
+ const body = typeof uploadRes.data === 'string'
|
|
|
|
|
+ ? JSON.parse(uploadRes.data)
|
|
|
|
|
+ : uploadRes.data
|
|
|
|
|
+ if (body && body.code == 1 && body.data) {
|
|
|
|
|
+ const shortUrl = body.data.shortUrl || ''
|
|
|
|
|
+ const showUrl = body.data.smallUrl || body.data.url || ''
|
|
|
|
|
+ if (shortUrl) {
|
|
|
|
|
+ this.uploadRefundImg.push(shortUrl)
|
|
|
|
|
+ this.refundImgShow.push(showUrl || shortUrl)
|
|
|
|
|
+ }
|
|
|
|
|
+ resolve()
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ reject(new Error((body && body.msg) || '上传失败'))
|
|
|
|
|
+ } catch (e) {
|
|
|
|
|
+ reject(e)
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ fail: (err) => reject(err)
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+ paths.reduce((chain, p) => chain.then(() => uploadOne(p)), Promise.resolve())
|
|
|
|
|
+ .catch(() => {
|
|
|
|
|
+ this.$msg('图片上传失败')
|
|
|
|
|
+ })
|
|
|
|
|
+ .finally(() => {
|
|
|
|
|
+ this.uploadingImg = false
|
|
|
|
|
+ uni.hideLoading()
|
|
|
|
|
+ })
|
|
|
|
|
+ },
|
|
|
|
|
+ removeUploadImg(index) {
|
|
|
|
|
+ this.refundImgShow.splice(index, 1)
|
|
|
|
|
+ this.uploadRefundImg.splice(index, 1)
|
|
|
|
|
+ },
|
|
|
|
|
+ previewUploadImg(index) {
|
|
|
|
|
+ if (!this.refundImgShow.length) {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ uni.previewImage({
|
|
|
|
|
+ urls: this.refundImgShow,
|
|
|
|
|
+ current: this.refundImgShow[index]
|
|
|
|
|
+ })
|
|
|
|
|
+ },
|
|
|
|
|
+ /** 进度页预览已提交图片 */
|
|
|
|
|
+ previewApplyImg(index) {
|
|
|
|
|
+ const list = (this.apply && this.apply.smallImgList) || []
|
|
|
|
|
+ if (!list.length) {
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ const urls = list.map(url => String(url).split('?')[0] + '?x-oss-process=image/resize,w_1200')
|
|
|
|
|
+ uni.previewImage({
|
|
|
|
|
+ urls,
|
|
|
|
|
+ current: urls[index] || urls[0]
|
|
|
|
|
+ })
|
|
|
|
|
+ },
|
|
|
doCancel() {
|
|
doCancel() {
|
|
|
if (!this.apply || !this.apply.id) {
|
|
if (!this.apply || !this.apply.id) {
|
|
|
return
|
|
return
|
|
@@ -636,11 +788,19 @@ export default {
|
|
|
.section-title {
|
|
.section-title {
|
|
|
padding: 30upx 30upx 20upx;
|
|
padding: 30upx 30upx 20upx;
|
|
|
border-bottom: 1upx solid #f0f0f0;
|
|
border-bottom: 1upx solid #f0f0f0;
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ align-items: center;
|
|
|
.title-text {
|
|
.title-text {
|
|
|
font-size: 32upx;
|
|
font-size: 32upx;
|
|
|
font-weight: 600;
|
|
font-weight: 600;
|
|
|
color: #333;
|
|
color: #333;
|
|
|
}
|
|
}
|
|
|
|
|
+ .title-sub {
|
|
|
|
|
+ margin-left: 16upx;
|
|
|
|
|
+ font-size: 24upx;
|
|
|
|
|
+ color: #999;
|
|
|
|
|
+ font-weight: 400;
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
.refund-type-buttons {
|
|
.refund-type-buttons {
|
|
@@ -815,6 +975,86 @@ export default {
|
|
|
box-sizing: border-box;
|
|
box-sizing: border-box;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+.upload-body {
|
|
|
|
|
+ padding: 20upx 30upx 30upx;
|
|
|
|
|
+}
|
|
|
|
|
+.upload-grid {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: row;
|
|
|
|
|
+ flex-wrap: wrap;
|
|
|
|
|
+}
|
|
|
|
|
+.upload-item {
|
|
|
|
|
+ position: relative;
|
|
|
|
|
+ width: 160upx;
|
|
|
|
|
+ height: 160upx;
|
|
|
|
|
+ margin-right: 16upx;
|
|
|
|
|
+ margin-bottom: 16upx;
|
|
|
|
|
+ border-radius: 12upx;
|
|
|
|
|
+ overflow: hidden;
|
|
|
|
|
+ background: #f5f6f8;
|
|
|
|
|
+ box-sizing: border-box;
|
|
|
|
|
+}
|
|
|
|
|
+.upload-item__img {
|
|
|
|
|
+ width: 160upx;
|
|
|
|
|
+ height: 160upx;
|
|
|
|
|
+}
|
|
|
|
|
+.upload-item__del {
|
|
|
|
|
+ position: absolute;
|
|
|
|
|
+ top: 0;
|
|
|
|
|
+ right: 0;
|
|
|
|
|
+ width: 40upx;
|
|
|
|
|
+ height: 40upx;
|
|
|
|
|
+ line-height: 36upx;
|
|
|
|
|
+ text-align: center;
|
|
|
|
|
+ background: rgba(0, 0, 0, 0.55);
|
|
|
|
|
+ color: #fff;
|
|
|
|
|
+ font-size: 28upx;
|
|
|
|
|
+ border-radius: 0 0 0 12upx;
|
|
|
|
|
+}
|
|
|
|
|
+.upload-item--add {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: column;
|
|
|
|
|
+ align-items: center;
|
|
|
|
|
+ justify-content: center;
|
|
|
|
|
+ border: 2upx dashed #d0d3d8;
|
|
|
|
|
+ background: #fafbfc;
|
|
|
|
|
+}
|
|
|
|
|
+.upload-add-icon {
|
|
|
|
|
+ font-size: 48upx;
|
|
|
|
|
+ color: #999;
|
|
|
|
|
+ line-height: 1;
|
|
|
|
|
+}
|
|
|
|
|
+.upload-add-text {
|
|
|
|
|
+ margin-top: 8upx;
|
|
|
|
|
+ font-size: 22upx;
|
|
|
|
|
+ color: #999;
|
|
|
|
|
+}
|
|
|
|
|
+.upload-tip {
|
|
|
|
|
+ display: block;
|
|
|
|
|
+ margin-top: 8upx;
|
|
|
|
|
+ font-size: 24upx;
|
|
|
|
|
+ color: #999;
|
|
|
|
|
+ line-height: 1.4;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+.apply-imgs {
|
|
|
|
|
+ margin-top: 20upx;
|
|
|
|
|
+}
|
|
|
|
|
+.apply-img-grid {
|
|
|
|
|
+ display: flex;
|
|
|
|
|
+ flex-direction: row;
|
|
|
|
|
+ flex-wrap: wrap;
|
|
|
|
|
+ margin-top: 12upx;
|
|
|
|
|
+}
|
|
|
|
|
+.apply-img {
|
|
|
|
|
+ width: 140upx;
|
|
|
|
|
+ height: 140upx;
|
|
|
|
|
+ margin-right: 12upx;
|
|
|
|
|
+ margin-bottom: 12upx;
|
|
|
|
|
+ border-radius: 10upx;
|
|
|
|
|
+ background: #f0f2f5;
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
.status-block {
|
|
.status-block {
|
|
|
padding: 30upx;
|
|
padding: 30upx;
|
|
|
.status-tag {
|
|
.status-tag {
|