Files
wagoo-douy3/src/pages/client/order/remark.vue
2026-03-06 13:41:22 +08:00

418 lines
11 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="remark-page">
<scroll-view class="remark-scroll" scroll-y>
<!-- 文字评价 -->
<view class="section text-review-section">
<view class="section-title">文字评价</view>
<view class="textarea-wrap">
<textarea class="remark-textarea" :value="remark" :maxlength="remarkMaxLen" placeholder="请输入评价"
placeholder-class="textarea-placeholder" :auto-height="true" @input="onRemarkChange" />
<view class="char-count">{{ remark.length }}/{{ remarkMaxLen }}</view>
</view>
</view>
<!-- 上传图片 -->
<view class="section upload-section">
<view class="section-title">上传图片 <text class="tips">(最多可上传3张)</text></view>
<view class="image-upload-grid">
<view v-for="(item, index) in imgList" :key="index" class="image-item">
<image class="uploaded-image" :src="item.fullUrl || item.url" mode="aspectFill" @click="previewImage(item.fullUrl || item.url, imgList.map(i => i.fullUrl || i.url))" />
<view class="delete-btn" @click="deleteImage(index)">
<text class="delete-icon">×</text>
</view>
</view>
<view v-if="imgList.length < 3" class="image-item upload-placeholder" @click="chooseImage">
<text class="add-icon">+</text>
</view>
</view>
</view>
<!-- 星级评分 -->
<view class="section rating-section">
<view class="rating-row">
<text class="rating-label">描述相符</text>
<view class="star-row">
<image v-for="v in 5" :key="v" class="star-icon"
:src="starDesc >= v ? require('@/static/images/star.png') : require('@/static/images/star_dark.png')"
@click="starDesc = v" />
</view>
</view>
<view class="rating-row" style="margin-top: 40rpx;">
<text class="rating-label">服务态度</text>
<view class="star-row">
<image v-for="v in 5" :key="v" class="star-icon"
:src="starService >= v ? require('@/static/images/star.png') : require('@/static/images/star_dark.png')"
@click="starService = v" />
</view>
</view>
</view>
<!-- 洗护对比图有数据时展示 -->
<!-- <view v-if="showWashImg" class="section wash-compare-section" @click="showModal = true">
<view class="wash-compare-row">
<text class="wash-compare-label">洗护对比图</text>
<image class="arrow-icon" src="@/static/images/arrow_right_black.png" mode="aspectFit" />
</view>
</view> -->
<view class="bottom-placeholder" />
</scroll-view>
<view class="submit-footer">
<view class="submit-btn" @click="submit">提交</view>
</view>
<compare-modal v-if="showModal" :before-imgs="washBeforeImgs" :after-imgs="washAfterImgs" :select-imgs="selectImgs"
:is-can-remark="true" @close="showModal = false" @add="selectImgsChange" />
</view>
</template>
<script>
import { createRemark } from "@/api/shop";
import CompareModal from "@/components/CompareModal.vue";
import { ORDER_TYPE_GOODS } from "@/constants/app.business";
import { uploadImageToOSS_PUT } from "@/utils/oss";
export default {
components: { CompareModal },
data() {
return {
orderId: "",
remark: "",
remarkMaxLen: 300,
starDesc: 5,
starService: 5,
imgList: [],
washBeforeImgs: [],
washAfterImgs: [],
selectImgs: [],
showModal: false,
orderType: ORDER_TYPE_GOODS,
};
},
computed: {
showWashImg() {
return [...this.washBeforeImgs, ...this.washAfterImgs].length > 0;
},
},
onLoad(options) {
console.log(options, '--')
this.orderId = options.orderId || "";
if (options.orderType) this.orderType = options.orderType;
const eventChannel = this.getOpenerEventChannel();
if (eventChannel && typeof eventChannel.on === "function") {
eventChannel.on("remarkInfo", (data) => {
this.washAfterImgs = data.afterImages || [];
this.washBeforeImgs = data.beforeImages || [];
this.selectImgs = data.remarkImages || [];
});
}
},
beforeDestroy() {
const ec = this.getOpenerEventChannel();
if (ec && typeof ec.emit === "function") ec.emit("refreshData");
},
methods: {
selectImgsChange(imgs) {
this.selectImgs = imgs;
this.showModal = false;
},
onRemarkChange(e) {
const val = (e && e.detail && e.detail.value) || (e && e.mp && e.mp.detail && e.mp.detail.value) || "";
this.remark = String(val).slice(0, this.remarkMaxLen);
},
chooseImage() {
const remaining = 3 - this.imgList.length;
if (remaining <= 0) {
uni.showToast({ title: "最多只能上传3张图片", icon: "none" });
return;
}
uni.chooseImage({
count: remaining,
sizeType: ['compressed'],
sourceType: ['album', 'camera'],
success: (res) => {
const tempFilePaths = res.tempFilePaths || [];
if (tempFilePaths.length === 0) return;
this.uploadImages(tempFilePaths);
},
fail: (err) => {
console.error('选择图片失败', err);
}
});
},
async uploadImages(filePaths) {
uni.showLoading({ title: "图片上传中...", icon: "none", mask: true });
try {
const uploadTasks = filePaths.map(filePath => this.uploadImageToOSS(filePath));
await Promise.all(uploadTasks);
uni.hideLoading();
uni.showToast({ title: "上传成功", icon: "success" });
} catch (error) {
uni.hideLoading();
uni.showToast({ title: error?.message || "上传失败", icon: "none" });
}
},
async uploadImageToOSS(filePath) {
try {
const { url, objectKey } = await uploadImageToOSS_PUT(filePath);
this.imgList.push({
url: objectKey,
fullUrl: url,
});
this.$forceUpdate();
} catch (error) {
console.error('图片上传失败:', error);
throw error;
}
},
deleteImage(index) {
this.imgList.splice(index, 1);
this.$forceUpdate();
},
previewImage(current, urls) {
uni.previewImage({
current: current,
urls: urls || [current]
});
},
submit() {
if (!this.remark || !this.remark.trim()) {
uni.showToast({ title: "请输入评价内容", icon: "none" });
return;
}
const pics = [...this.selectImgs, ...this.imgList].map((v) => (v.url || v.objectKey || v.filename || v.fullUrl)).filter(Boolean);
if (!pics.length) {
uni.showToast({ title: "请上传图片", icon: "none" });
return;
}
uni.showLoading({ title: "提交中...", icon: "none" });
createRemark({
order_id: +this.orderId,
type: this.orderType,
imgs: pics,
content: this.remark.trim(),
description: this.starDesc || 0,
attitude: this.starService || 0,
})
.then(() => {
uni.hideLoading();
uni.$emit("createRemarkSuccess");
uni.navigateBack();
})
.catch((err) => {
uni.hideLoading();
uni.showToast({ title: err?.message || err || "提交失败", icon: "none" });
});
},
},
};
</script>
<style lang="scss" scoped>
.remark-page {
min-height: 100vh;
background: #f5f5f5;
box-sizing: border-box;
padding-bottom: constant(safe-area-inset-bottom);
padding-bottom: env(safe-area-inset-bottom);
}
.remark-scroll {
height: 100vh;
box-sizing: border-box;
}
.section {
margin: 20rpx;
background: #fff;
border-radius: 16rpx;
overflow: hidden;
box-sizing: border-box;
padding: 20rpx;
}
.section-title {
font-size: 24rpx;
font-weight: 500;
color: #272427;
margin-bottom: 20rpx;
.tips {
font-size: 20rpx;
color: #ACACAC;
}
}
/* 文字评价 */
.text-review-section {
.textarea-wrap {
position: relative;
padding: 20rpx;
background: #f5f5f5;
border-radius: 16rpx;
min-height: 240rpx;
}
.remark-textarea {
width: 100%;
min-height: 200rpx;
font-size: 28rpx;
color: #272427;
line-height: 1.5;
box-sizing: border-box;
}
.textarea-placeholder {
color: #acacac;
font-size: 28rpx;
}
.char-count {
position: absolute;
right: 32rpx;
bottom: 24rpx;
font-size: 24rpx;
color: #9b939a;
}
}
/* 上传图片 */
.upload-section {
.image-upload-grid {
display: flex;
flex-wrap: wrap;
gap: 24rpx;
}
.image-item {
position: relative;
width: 200rpx;
height: 200rpx;
border-radius: 16rpx;
overflow: hidden;
flex-shrink: 0;
}
.uploaded-image {
width: 100%;
height: 100%;
border-radius: 16rpx;
}
.delete-btn {
position: absolute;
top: 8rpx;
right: 8rpx;
width: 40rpx;
height: 40rpx;
background: rgba(0, 0, 0, 0.5);
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
}
.delete-icon {
color: #fff;
font-size: 32rpx;
font-weight: bold;
line-height: 1;
}
.upload-placeholder {
background: #f5f5f5;
display: flex;
align-items: center;
justify-content: center;
border: 1rpx dashed #d0d0d0;
}
.add-icon {
font-size: 64rpx;
color: #999;
font-weight: 300;
line-height: 1;
}
}
/* 星级 */
.rating-section {
margin-top: 24rpx;
padding: 20rpx;
.rating-row {
display: flex;
align-items: center;
justify-content: space-between;
}
.rating-label {
font-size: 24rpx;
color: #3D3D3D;
}
.star-row {
display: flex;
align-items: center;
gap: 12rpx;
}
.star-icon {
width: 40rpx;
height: 40rpx;
}
}
/* 洗护对比图 */
.wash-compare-section {
.wash-compare-row {
display: flex;
align-items: center;
justify-content: space-between;
padding: 24rpx 32rpx;
background: #f5f5f5;
margin: 0 24rpx 24rpx;
border-radius: 12rpx;
}
.wash-compare-label {
font-size: 28rpx;
color: #272427;
}
.arrow-icon {
width: 24rpx;
height: 24rpx;
}
}
.bottom-placeholder {
height: 180rpx;
}
.submit-footer {
position: fixed;
left: 0;
right: 0;
bottom: 0;
padding: 24rpx 32rpx;
padding-bottom: calc(24rpx + constant(safe-area-inset-bottom));
padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
background: #fff;
box-sizing: border-box;
}
.submit-btn {
width: 100%;
height: 88rpx;
line-height: 88rpx;
text-align: center;
font-size: 32rpx;
font-weight: 500;
color: #fff;
background: #fe019b;
border-radius: 44rpx;
}
</style>