418 lines
11 KiB
Vue
418 lines
11 KiB
Vue
<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>
|