|
|
@@ -0,0 +1,381 @@
|
|
|
+<!-- 编辑弹窗 -->
|
|
|
+<template>
|
|
|
+ <ele-modal
|
|
|
+ form
|
|
|
+ :width="960"
|
|
|
+ v-model="visible"
|
|
|
+ title="投诉状态及操作"
|
|
|
+ @open="handleOpen"
|
|
|
+ >
|
|
|
+ <div class="flex w-full">
|
|
|
+ <div style="flex: 1.5">
|
|
|
+ <el-tag size="large">{{ getStatusText(form.status) }}</el-tag>
|
|
|
+
|
|
|
+ <ele-text style="margin-top: 15px"
|
|
|
+ >1.请点击协商(留言)与对方协商。</ele-text
|
|
|
+ >
|
|
|
+ <ele-text>2.也可点击完结,投诉完结。</ele-text>
|
|
|
+ <el-divider />
|
|
|
+ <el-button @click="handleNegotiate">协商(留言)</el-button>
|
|
|
+ <el-button type="danger" @click="handleComplete"
|
|
|
+ >完结</el-button
|
|
|
+ >
|
|
|
+ <el-divider />
|
|
|
+
|
|
|
+ <div class="common-title">协商历史</div>
|
|
|
+ <complain-item
|
|
|
+ v-for="(item, index) in disposeLogList"
|
|
|
+ :key="index"
|
|
|
+ :item="item"
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ <div class="flex-1 complain-detail">
|
|
|
+ <div class="common-title">投诉详情</div>
|
|
|
+ <div class="detail-content mt-6">
|
|
|
+ <div class="detail-item">
|
|
|
+ <span class="label">投诉编号:</span>
|
|
|
+ <span class="value">{{ form.id }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="detail-item">
|
|
|
+ <span class="label">投诉原因:</span>
|
|
|
+ <span class="value">{{ form.reason }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="detail-item">
|
|
|
+ <span class="label">投诉说明:</span>
|
|
|
+ <span class="value">{{ form.description }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="detail-item">
|
|
|
+ <span class="label">投诉时间:</span>
|
|
|
+ <span class="value">{{ form.createTime }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="detail-item">
|
|
|
+ <span class="label">发 起 人:</span>
|
|
|
+ <span class="value">{{ form.userName }}</span>
|
|
|
+ </div>
|
|
|
+ <div class="detail-item">
|
|
|
+ <span class="label">订单编号:</span>
|
|
|
+ <el-tooltip
|
|
|
+ content="跳转到订单详情<br/>商品最多展示三个"
|
|
|
+ placement="top"
|
|
|
+ effect="light"
|
|
|
+ raw-content
|
|
|
+ >
|
|
|
+ <span class="value link" @click="goToOrder(form.orderId)">{{ form.orderId }}</span>
|
|
|
+ </el-tooltip>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <el-divider />
|
|
|
+
|
|
|
+ <!-- 商品列表 -->
|
|
|
+ <div class="product-list">
|
|
|
+ <div v-for="(item, index) in goodsList" :key="index" class="product-item">
|
|
|
+ <el-image :src="item.image" class="product-img" fit="cover" />
|
|
|
+ <div class="product-info">
|
|
|
+ <div class="product-title">{{ item.title }}</div>
|
|
|
+ <div class="product-meta">
|
|
|
+ <span class="product-spec">{{ item.spec }}</span>
|
|
|
+ <span class="product-qty" :class="{ 'text-red': item.quantity > 1 }">X{{ item.quantity }}</span>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <template #footer>
|
|
|
+ <el-button @click="handleCancel">关闭</el-button>
|
|
|
+ </template>
|
|
|
+ </ele-modal>
|
|
|
+
|
|
|
+ <!-- 协商弹窗 -->
|
|
|
+ <negotiate-dialog
|
|
|
+ v-model:visible="negotiateVisible"
|
|
|
+ :id="currentId"
|
|
|
+ @submit="submitNegotiate"
|
|
|
+ />
|
|
|
+</template>
|
|
|
+
|
|
|
+<script setup>
|
|
|
+ import { ref, reactive, nextTick, getCurrentInstance } from 'vue';
|
|
|
+ import complainItem from './complain-item.vue';
|
|
|
+ import negotiateDialog from './negotiate-dialog.vue';
|
|
|
+ import { EleMessage } from 'ele-admin-plus/es';
|
|
|
+ import { ElMessageBox } from 'element-plus';
|
|
|
+ import { useDictData } from '@/utils/use-dict-data';
|
|
|
+ import Logo from '@/assets/logo.png';
|
|
|
+
|
|
|
+ const { proxy } = getCurrentInstance();
|
|
|
+ const emit = defineEmits(['refresh']);
|
|
|
+
|
|
|
+ // 获取投诉状态字典
|
|
|
+ const [statusDicts] = useDictData(['complain_status']);
|
|
|
+
|
|
|
+ // 获取状态文本
|
|
|
+ const getStatusText = (status) => {
|
|
|
+ return (
|
|
|
+ statusDicts.value.find((d) => d.dictValue == status)?.dictLabel ||
|
|
|
+ '协商中'
|
|
|
+ );
|
|
|
+ };
|
|
|
+
|
|
|
+ /** 弹窗是否打开 */
|
|
|
+ const visible = defineModel({ type: Boolean });
|
|
|
+
|
|
|
+ /** 关闭弹窗 */
|
|
|
+ const handleCancel = () => {
|
|
|
+ visible.value = false;
|
|
|
+ };
|
|
|
+
|
|
|
+ // 当前投诉ID
|
|
|
+ const currentId = ref(null);
|
|
|
+ // 处理记录列表
|
|
|
+ const disposeLogList = ref([]);
|
|
|
+ // 商品列表
|
|
|
+ const goodsList = ref([]);
|
|
|
+
|
|
|
+ /** 弹窗打开事件 */
|
|
|
+ const handleOpen = (row) => {
|
|
|
+ if (row && row.id) {
|
|
|
+ currentId.value = row.id;
|
|
|
+ visible.value = true;
|
|
|
+ getComplaintDetail(row.id);
|
|
|
+ } else {
|
|
|
+ visible.value = true;
|
|
|
+ nextTick(() => console.log('打开'));
|
|
|
+ }
|
|
|
+ };
|
|
|
+
|
|
|
+ // 获取投诉详情
|
|
|
+ const getComplaintDetail = (id) => {
|
|
|
+ proxy.$http
|
|
|
+ .get(`/order/orderComplaintsLog/getInfo/${id}`)
|
|
|
+ .then((res) => {
|
|
|
+ if (res.data.code === 200) {
|
|
|
+ const data = res.data.data || {};
|
|
|
+ // 映射API返回的数据到表单
|
|
|
+ form.id = data.id || '';
|
|
|
+ form.userName = data.userName || '-';
|
|
|
+ form.orderId = data.orderId || '';
|
|
|
+ form.reason = data.reason || '';
|
|
|
+ form.description = data.description || '';
|
|
|
+ form.createTime = data.createTime || '';
|
|
|
+ form.status = data.status || '';
|
|
|
+
|
|
|
+ // 处理协商历史
|
|
|
+ disposeLogList.value = data.disposeLogList || [];
|
|
|
+
|
|
|
+ // 模拟商品数据 (因为后端可能没返回)
|
|
|
+ goodsList.value = [
|
|
|
+ {
|
|
|
+ image: 'https://img14.360buyimg.com/n0/jfs/t1/157997/3/36676/122176/65e975a0F98822998/5023348143493720.jpg',
|
|
|
+ title: '新版中日交流标准日本语初级上下册第二版日语零基础入门',
|
|
|
+ spec: '品相 (一般)',
|
|
|
+ quantity: 1
|
|
|
+ },
|
|
|
+ {
|
|
|
+ image: 'https://img14.360buyimg.com/n0/jfs/t1/157997/3/36676/122176/65e975a0F98822998/5023348143493720.jpg',
|
|
|
+ title: '新版中日交流标准日本语初级上下册第二版日语零基础入门',
|
|
|
+ spec: '品相 (一般)',
|
|
|
+ quantity: 2
|
|
|
+ },
|
|
|
+ {
|
|
|
+ image: 'https://img14.360buyimg.com/n0/jfs/t1/157997/3/36676/122176/65e975a0F98822998/5023348143493720.jpg',
|
|
|
+ title: '新版中日交流标准日本语初级上下册第二版日语零基础入门',
|
|
|
+ spec: '品相 (一般)',
|
|
|
+ quantity: 1
|
|
|
+ }
|
|
|
+ ];
|
|
|
+ } else {
|
|
|
+ EleMessage.error(res.data.msg || '获取投诉详情失败');
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .catch((err) => {
|
|
|
+ console.error('获取投诉详情失败', err);
|
|
|
+ EleMessage.error('获取投诉详情失败');
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ // 协商弹窗相关
|
|
|
+ const negotiateVisible = ref(false);
|
|
|
+
|
|
|
+ // 处理协商
|
|
|
+ const handleNegotiate = () => {
|
|
|
+ if (!currentId.value) {
|
|
|
+ EleMessage.warning('请先选择投诉记录');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 打开协商弹窗
|
|
|
+ negotiateVisible.value = true;
|
|
|
+ };
|
|
|
+
|
|
|
+ // 提交协商
|
|
|
+ const submitNegotiate = (formData) => {
|
|
|
+ // 调用协商API
|
|
|
+ proxy.$http
|
|
|
+ .post('/order/orderComplaintsLog/dispose', formData)
|
|
|
+ .then((res) => {
|
|
|
+ if (res.data.code === 200) {
|
|
|
+ EleMessage.success('协商成功');
|
|
|
+ negotiateVisible.value = false;
|
|
|
+
|
|
|
+ // 刷新详情,更新协商历史
|
|
|
+ getComplaintDetail(currentId.value);
|
|
|
+ } else {
|
|
|
+ EleMessage.error(res.data.msg || '协商失败');
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .catch((err) => {
|
|
|
+ console.error('协商失败', err);
|
|
|
+ EleMessage.error('协商失败');
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ // 处理完结
|
|
|
+ const handleComplete = () => {
|
|
|
+ if (!currentId.value) {
|
|
|
+ EleMessage.warning('请先选择投诉记录');
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ ElMessageBox.confirm('确认完结此投诉?', '提示', {
|
|
|
+ confirmButtonText: '确定',
|
|
|
+ cancelButtonText: '取消',
|
|
|
+ type: 'warning'
|
|
|
+ })
|
|
|
+ .then(() => {
|
|
|
+ // 调用完结API
|
|
|
+ proxy.$http
|
|
|
+ .post(`/order/orderComplaintsLog/over/${currentId.value}`)
|
|
|
+ .then((res) => {
|
|
|
+ if (res.data.code === 200) {
|
|
|
+ EleMessage.success('投诉已完结');
|
|
|
+ emit('refresh'); // 通知父组件刷新列表
|
|
|
+ handleCancel(); // 关闭弹窗
|
|
|
+ } else {
|
|
|
+ EleMessage.error(res.data.msg || '操作失败');
|
|
|
+ }
|
|
|
+ })
|
|
|
+ .catch((err) => {
|
|
|
+ console.error('完结投诉失败', err);
|
|
|
+ EleMessage.error('完结投诉失败');
|
|
|
+ });
|
|
|
+ })
|
|
|
+ .catch(() => {
|
|
|
+ // 用户取消操作
|
|
|
+ });
|
|
|
+ };
|
|
|
+
|
|
|
+ // 跳转到订单详情
|
|
|
+ const goToOrder = (orderId) => {
|
|
|
+ if (!orderId) return;
|
|
|
+ // 由于暂无订单详情页,暂时提示
|
|
|
+ EleMessage.info(`功能开发中,订单号: ${orderId}`);
|
|
|
+ // 实际开发中应该是: router.push({ path: '/mallOrder/detail', query: { id: orderId } })
|
|
|
+ };
|
|
|
+
|
|
|
+ const form = reactive({
|
|
|
+ id: '',
|
|
|
+ userName: '',
|
|
|
+ orderId: '',
|
|
|
+ reason: '',
|
|
|
+ description: '',
|
|
|
+ createTime: '',
|
|
|
+ status: ''
|
|
|
+ });
|
|
|
+
|
|
|
+ defineExpose({
|
|
|
+ handleOpen
|
|
|
+ });
|
|
|
+</script>
|
|
|
+
|
|
|
+<style lang="scss">
|
|
|
+ .complain-detail {
|
|
|
+ border-left: 1px solid #e6e6e6;
|
|
|
+ padding-left: 15px;
|
|
|
+
|
|
|
+ .detail-item {
|
|
|
+ margin-bottom: 15px;
|
|
|
+ font-size: 14px;
|
|
|
+ display: flex;
|
|
|
+
|
|
|
+ .label {
|
|
|
+ color: #666;
|
|
|
+ width: 80px;
|
|
|
+ flex-shrink: 0;
|
|
|
+ }
|
|
|
+
|
|
|
+ .value {
|
|
|
+ color: #333;
|
|
|
+ flex: 1;
|
|
|
+
|
|
|
+ &.link {
|
|
|
+ color: #409eff;
|
|
|
+ cursor: pointer;
|
|
|
+ &:hover {
|
|
|
+ text-decoration: underline;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ .product-list {
|
|
|
+ .product-item {
|
|
|
+ display: flex;
|
|
|
+ margin-bottom: 15px;
|
|
|
+ padding-bottom: 15px;
|
|
|
+ border-bottom: 1px solid #f5f5f5;
|
|
|
+
|
|
|
+ &:last-child {
|
|
|
+ border-bottom: none;
|
|
|
+ }
|
|
|
+
|
|
|
+ .product-img {
|
|
|
+ width: 80px;
|
|
|
+ height: 80px;
|
|
|
+ border-radius: 4px;
|
|
|
+ margin-right: 10px;
|
|
|
+ flex-shrink: 0;
|
|
|
+ background-color: #f5f5f5;
|
|
|
+ }
|
|
|
+
|
|
|
+ .product-info {
|
|
|
+ flex: 1;
|
|
|
+ display: flex;
|
|
|
+ flex-direction: column;
|
|
|
+ justify-content: space-between;
|
|
|
+
|
|
|
+ .product-title {
|
|
|
+ font-weight: bold;
|
|
|
+ color: #333;
|
|
|
+ font-size: 14px;
|
|
|
+ line-height: 1.4;
|
|
|
+ margin-bottom: 5px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .product-meta {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: flex-end;
|
|
|
+
|
|
|
+ .product-spec {
|
|
|
+ color: #999;
|
|
|
+ font-size: 12px;
|
|
|
+ }
|
|
|
+
|
|
|
+ .product-qty {
|
|
|
+ font-size: 14px;
|
|
|
+ font-weight: bold;
|
|
|
+ color: #333;
|
|
|
+
|
|
|
+ &.text-red {
|
|
|
+ color: #ff4d4f;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+</style>
|