style(member): 优化申诉详情、会员等级、优惠券、礼品卡页面样式

This commit is contained in:
pikachu1995@126.com
2026-10-26 17:01:08 +08:00
parent 99e7f6ed47
commit 1f872254cc
4 changed files with 550 additions and 312 deletions

View File

@@ -1,185 +1,224 @@
<template>
<div class="add-eval">
<div class="complain-detail">
<UserCenterLayout>
<div class="title">
<p>
<span class="fontsize_16 global_color">{{ statusLabel[detail.complainStatus] }}</span>
<span class="color999 ml_20">创建时间:</span><span>{{ detail.createTime }}</span>
<span class="color999 ml_20">{{ detail.createTime }}</span>
</p>
<div class="detail-head">
<el-tag :type="statusTagType" effect="dark">{{ statusText }}</el-tag>
<span class="create-time">创建时间:{{ detail.createTime || "-" }}</span>
</div>
<el-alert class="l_title" show-icon type="warning">我的申诉信息</el-alert>
<table cellspacing="0" cellpadding='0' border="1">
<tbody>
<tr>
<td>投诉商品</td>
<td class="hover-color" @click="linkTo(`/goodsDetail?goodsId=${detail.goodsId}&skuId=${detail.skuId}`)"><img :src="detail.goodsImage" loading="lazy" decoding="async" width="60" alt=""> &nbsp;{{ detail.goodsName }}</td>
</tr>
<tr>
<td>投诉主题</td>
<td>{{ detail.complainTopic }}</td>
</tr>
<tr>
<td>投诉内容</td>
<td>{{ detail.content }}</td>
</tr>
<tr>
<td>补充内容</td>
<td>
<div style="display:flex;align-items:center;">
<template v-if="detail.images">
<div class="demo-upload-list" v-for="(img, index) in detail.images.split(',')" :key="index">
<img :src="img" loading="lazy" decoding="async">
<section class="panel">
<div class="panel-title">我的申诉信息</div>
<div class="info-row">
<span class="info-label">投诉商品</span>
<div class="info-value">
<button
v-if="detail.goodsId"
type="button"
class="goods-info"
@click="goGoodsDetail(detail.skuId, detail.goodsId)"
>
<img :src="detail.goodsImage" loading="lazy" decoding="async" alt="" />
<span class="goods-name">{{ detail.goodsName }}</span>
</button>
<span v-else class="empty-text">暂无</span>
</div>
</div>
<div class="info-row">
<span class="info-label">投诉主题</span>
<span class="info-value">{{ detail.complainTopic || "暂无" }}</span>
</div>
<div class="info-row">
<span class="info-label">投诉内容</span>
<span class="info-value">{{ detail.content || "暂无" }}</span>
</div>
<div class="info-row">
<span class="info-label">补充内容</span>
<div class="info-value">
<div v-if="images.length" class="img-list">
<div
v-for="(img, index) in images"
:key="index"
class="demo-upload-list"
@click="handleView(img)"
>
<img :src="img" loading="lazy" decoding="async" />
<div class="demo-upload-list-cover">
<el-icon @click="handleView(img)"><View /></el-icon>
<el-icon><View /></el-icon>
</div>
</div>
</template>
<div v-else>暂无</div>
</div>
<span v-else class="empty-text">暂无</span>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</section>
<el-alert class="l_title" show-icon type="warning">商家申诉信息</el-alert>
<table cellspacing="0" cellpadding='0' border="1">
<tbody>
<tr>
<td>申诉时间</td>
<td>{{ detail.appealTime || '暂无' }}</td>
</tr>
<tr>
<td>申诉内容</td>
<td>{{ detail.appealContent || '暂无' }}</td>
</tr>
<tr>
<td>申诉凭证</td>
<td>
<div style="display:flex;align-items:center;">
<template v-if="detail.appealImages">
<div class="demo-upload-list" v-for="(img, index) in detail.appealImages.split(',')" :key="index">
<img :src="img" loading="lazy" decoding="async">
<section class="panel">
<div class="panel-title">商家申诉信息</div>
<div class="info-row">
<span class="info-label">申诉时间</span>
<span class="info-value">{{ detail.appealTime || "暂无" }}</span>
</div>
<div class="info-row">
<span class="info-label">申诉内容</span>
<span class="info-value">{{ detail.appealContent || "暂无" }}</span>
</div>
<div class="info-row">
<span class="info-label">申诉凭证</span>
<div class="info-value">
<div v-if="appealImages.length" class="img-list">
<div
v-for="(img, index) in appealImages"
:key="index"
class="demo-upload-list"
@click="handleView(img)"
>
<img :src="img" loading="lazy" decoding="async" />
<div class="demo-upload-list-cover">
<el-icon @click="handleView(img)"><View /></el-icon>
<el-icon><View /></el-icon>
</div>
</div>
</template>
<div v-else>暂无</div>
</div>
<span v-else class="empty-text">暂无</span>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</section>
<el-alert class="l_title" show-icon type="warning">平台仲裁</el-alert>
<table cellspacing="0" cellpadding='0' border="1">
<tbody>
<tr>
<td>仲裁意见</td>
<td>{{ detail.arbitrationResult || '暂无' }}</td>
</tr>
</tbody>
</table>
<section class="panel">
<div class="panel-title">平台仲裁</div>
<div class="info-row">
<span class="info-label">仲裁意见</span>
<span class="info-value">{{ detail.arbitrationResult || "暂无" }}</span>
</div>
</section>
<el-alert class="l_title" show-icon type="warning">对话详情</el-alert>
<div class="speak-way" v-if="detail.orderComplaintCommunications && detail.orderComplaintCommunications.length">
<div
class="speak-msg seller"
:class="{'speak-buyer': item.owner == 'BUYER', 'speak-platform': item.owner == 'PLATFORM', 'speak-seller': item.owner == 'STORE',}"
:key="i"
v-for="(item, i) in detail.orderComplaintCommunications"
>
{{
item.owner == "PLATFORM"
? "平台"
: item.owner == "BUYER"
? "买家"
: "卖家"
}}[{{ item.createTime }}]:
<span>{{ item.content }}</span>
</div>
</div>
<div v-else>暂无对话</div>
<table cellspacing="0" cellpadding='0' border="1" v-if="detail.complainStatus!='COMPLETE'">
<tbody>
<tr>
<td>回复:</td>
<td><label>
<input type="textarea" maxlength="200" :rows="4" clearable
style="width:260px" v-model="params.content" />
</label></td>
</tr>
<tr>
<td></td>
<td>
<el-button type="primary" :loading="submitLoading" @click="handleSubmit" style="margin-left: 5px">
<section class="panel">
<div class="panel-title">对话详情</div>
<div v-if="communications.length" class="chat-list">
<div
v-for="(item, i) in communications"
:key="i"
class="chat-item"
:class="{
'is-buyer': item.owner === 'BUYER',
'is-platform': item.owner === 'PLATFORM',
}"
>
<div class="chat-meta">{{ ownerName(item.owner) }} · {{ item.createTime }}</div>
<div class="chat-bubble">{{ item.content }}</div>
</div>
</div>
<div v-else class="empty-text">暂无对话</div>
<div v-if="detail.complainStatus !== 'COMPLETE'" class="reply-box">
<el-input
v-model="params.content"
type="textarea"
:rows="3"
maxlength="200"
show-word-limit
placeholder="请输入回复内容"
/>
<el-button
type="primary"
:loading="submitLoading"
class="reply-btn"
@click="handleSubmit"
>
回复
</el-button>
</td>
</tr>
</tbody>
</table>
</div>
</section>
<el-dialog title="图片预览" v-model="visible">
<img :src="previewImage" loading="lazy" decoding="async" v-if="visible" style="width: 100%">
</el-dialog>
<el-dialog title="图片预览" v-model="visible">
<img :src="previewImage" loading="lazy" decoding="async" v-if="visible" style="width: 100%" />
</el-dialog>
</UserCenterLayout>
</div>
</template>
<script setup>
import { onMounted, reactive, ref } from "vue";
import { computed, onMounted, reactive, ref } from "vue";
import { useRoute, useRouter } from "vue-router";
import { Message } from "@/utils/message";
import { View } from '@element-plus/icons-vue';
import {getComplainDetail} from '@/api/member.js';
import {communication} from '@/api/order';
import { View } from "@element-plus/icons-vue";
import { getComplainDetail } from "@/api/member.js";
import { communication } from "@/api/order";
const route = useRoute();
const router = useRouter();
const detail = ref({}); // 评价详情
const detail = ref({}); // 投诉详情
const visible = ref(false); // 图片预览
const previewImage = ref(''); // 预览图片地址
const loading = ref(false); // 加载状态
const previewImage = ref(""); // 预览图片地址
const submitLoading = ref(false); // 回复消息loading
// 状态
const statusLabel = {
NO_APPLY: '未申请',
APPLYING: '申请中',
COMPLETE: '已完成',
EXPIRED: '已失效',
CANCEL: '已取消',
NEW: '待审核'
NO_APPLY: "未申请",
APPLYING: "申请中",
COMPLETE: "已完成",
EXPIRED: "已失效",
CANCEL: "已取消",
NEW: "待审核",
};
// 商家回复内容
const params = reactive({
content: '',
complainId: ''
content: "",
complainId: "",
});
function getDetail () { // 获取投诉详情
getComplainDetail(route.query.id).then(res => {
if (res.success) detail.value = res.result
})
const statusText = computed(
() => statusLabel[detail.value.complainStatus] || detail.value.complainStatus || ""
);
const statusTagType = computed(() => {
const map = {
COMPLETE: "success",
APPLYING: "warning",
NEW: "warning",
CANCEL: "info",
EXPIRED: "info",
NO_APPLY: "info",
};
return map[detail.value.complainStatus] || "info";
});
const images = computed(() =>
detail.value.images ? detail.value.images.split(",").filter(Boolean) : []
);
const appealImages = computed(() =>
detail.value.appealImages ? detail.value.appealImages.split(",").filter(Boolean) : []
);
const communications = computed(() =>
Array.isArray(detail.value.orderComplaintCommunications)
? detail.value.orderComplaintCommunications
: []
);
function ownerName(owner) {
return owner === "PLATFORM" ? "平台" : owner === "BUYER" ? "买家" : "卖家";
}
function goGoodsDetail (skuId, goodsId) { // 跳转商品详情
function getDetail() {
// 获取投诉详情
getComplainDetail(route.query.id).then((res) => {
if (res.success) detail.value = res.result;
});
}
function goGoodsDetail(skuId, goodsId) {
// 跳转商品详情
let routerUrl = router.resolve({
path: '/goodsDetail',
query: {skuId, goodsId}
})
window.open(routerUrl.href, '_blank')
path: "/goodsDetail",
query: { skuId, goodsId },
});
window.open(routerUrl.href, "_blank");
}
function handleView (name) { // 预览图片
function handleView(name) {
// 预览图片
previewImage.value = name;
visible.value = true;
}
// 回复消息
function handleSubmit () {
if (params.content === '') {
Message.error('请填写对话内容');
function handleSubmit() {
if (params.content === "") {
Message.error("请填写对话内容");
return;
}
submitLoading.value = true;
@@ -187,64 +226,129 @@ function handleSubmit () {
communication(params).then((res) => {
submitLoading.value = false;
if (res.success) {
Message.success('对话成功');
params.content = '';
Message.success("对话成功");
params.content = "";
getDetail();
}
});
}
onMounted(() => {
getDetail()
})
getDetail();
});
</script>
<style lang="scss" scoped>
.speak-way {
background-color: #fff;
width: 100%;
border: 1px solid #999;
.detail-head {
display: flex;
align-items: center;
gap: 12px;
padding-bottom: 4px;
}
.speak-seller {
background-color: #fff2c6;
}
.create-time {
color: #999;
font-size: 14px;
}
.speak-platform {
background-color: #ffe2d1;
}
.panel {
margin-top: 16px;
border: 1px solid #eee;
border-radius: 8px;
background: #fff;
padding: 16px 20px;
}
.speak-buyer {
background-color: #c1d6d5;
.panel-title {
color: #333;
font-size: 16px;
font-weight: 600;
line-height: 24px;
padding-bottom: 12px;
border-bottom: 1px solid #f5f5f5;
}
.info-row {
display: flex;
gap: 16px;
padding: 12px 0;
border-bottom: 1px solid #f5f5f5;
&:last-child {
border-bottom: none;
padding-bottom: 4px;
}
}
.speak-msg {
height: 40px;
line-height: 40px;
padding: 0 5px;
border-radius: 5px;
margin: 5px;
background-color: #eee;
.info-label {
flex: 0 0 80px;
color: #999;
font-size: 14px;
line-height: 22px;
}
.info-value {
flex: 1;
min-width: 0;
color: #333;
font-size: 14px;
line-height: 22px;
word-break: break-all;
}
.empty-text {
color: #999;
font-size: 14px;
}
.goods-info {
display: inline-flex;
align-items: center;
gap: 12px;
padding: 0;
border: none;
background: none;
cursor: pointer;
img {
width: 56px;
height: 56px;
border-radius: 6px;
background: #f5f5f5;
object-fit: cover;
}
.goods-name {
color: #333;
font-size: 14px;
}
&:hover .goods-name {
color: $theme_color;
}
}
.img-list {
display: flex;
flex-wrap: wrap;
gap: 10px;
}
.demo-upload-list {
display: inline-block;
width: 100px;
height: 100px;
text-align: center;
line-height: 100px;
border: 1px solid transparent;
border-radius: 4px;
border-radius: 6px;
overflow: hidden;
background: #fff;
background: #f5f5f5;
position: relative;
box-shadow: 0 1px 1px rgba(0, 0, 0, .2);
margin-right: 4px;
margin-top: 10px;
box-shadow: 0 1px 1px rgba(0, 0, 0, 0.2);
cursor: pointer;
}
.demo-upload-list img {
width: 100%;
height: 100%;
object-fit: cover;
}
.demo-upload-list-cover {
@@ -254,51 +358,70 @@ onMounted(() => {
bottom: 0;
left: 0;
right: 0;
background: rgba(0, 0, 0, .6);
background: rgba(0, 0, 0, 0.6);
align-items: center;
justify-content: center;
color: #fff;
font-size: 24px;
}
.demo-upload-list:hover .demo-upload-list-cover {
display: block;
display: flex;
}
.demo-upload-list-cover i {
color: #fff;
font-size: 30px;
cursor: pointer;
margin: 0 2px;
.chat-list {
display: flex;
flex-direction: column;
gap: 12px;
padding: 16px 0 4px;
}
.icon-upload {
width: 58px;
height: 58px;
line-height: 58px;
text-align: center;
display: inline-block;
border: 1px dashed #999;
border-radius: 4px;
margin-top: 10px;
.chat-item {
display: flex;
flex-direction: column;
align-items: flex-start;
max-width: 80%;
&:hover {
cursor: pointer;
border-color: $theme_color;
}
}
&.is-buyer {
align-self: flex-end;
align-items: flex-end;
.l_title {
margin: 10px 0;
}
table {
width: 100%;
border-color: #eee;
td {
padding: 10px;
&:nth-child(1) {
width: 120px;
color: #999;
.chat-bubble {
background: $theme_color;
color: #fff;
}
}
&.is-platform .chat-bubble {
background: #fdf3e4;
color: #8a5a00;
}
}
.chat-meta {
margin-bottom: 4px;
color: #999;
font-size: 12px;
line-height: 18px;
}
.chat-bubble {
padding: 8px 12px;
border-radius: 8px;
background: #f5f5f5;
color: #333;
font-size: 14px;
line-height: 22px;
word-break: break-all;
}
.reply-box {
margin-top: 16px;
padding-top: 16px;
border-top: 1px solid #f5f5f5;
}
.reply-btn {
margin-top: 10px;
}
</style>