1
This commit is contained in:
417
src/pages/client/order/remark.vue
Normal file
417
src/pages/client/order/remark.vue
Normal file
@ -0,0 +1,417 @@
|
||||
<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>
|
||||
Reference in New Issue
Block a user