|
|
@@ -1,29 +1,38 @@
|
|
|
<template>
|
|
|
<div class="app-container">
|
|
|
<el-form :model="queryParams" ref="queryForm" :inline="true" v-show="showSearch" label-width="68px">
|
|
|
- <el-form-item label="投诉用户" prop="fsUserId">
|
|
|
+ <el-form-item label="用户" prop="userId">
|
|
|
<el-input
|
|
|
- v-model="queryParams.nickName"
|
|
|
- placeholder="请输入投诉用户"
|
|
|
+ v-model="queryParams.userId"
|
|
|
+ placeholder="请输入用户id"
|
|
|
clearable
|
|
|
size="small"
|
|
|
@keyup.enter.native="handleQuery"
|
|
|
/>
|
|
|
</el-form-item>
|
|
|
- <el-form-item label="投诉标题" prop="title">
|
|
|
+ <el-form-item label="联系方式" prop="phone">
|
|
|
<el-input
|
|
|
- v-model="queryParams.title"
|
|
|
- placeholder="请输入投诉标题"
|
|
|
+ v-model="queryParams.phone"
|
|
|
+ placeholder="请输入联系方式"
|
|
|
clearable
|
|
|
size="small"
|
|
|
@keyup.enter.native="handleQuery"
|
|
|
/>
|
|
|
</el-form-item>
|
|
|
- <el-form-item label="投诉类型" prop="type">
|
|
|
- <el-select v-model="queryParams.type" placeholder="请选择投诉类型" clearable size="small">
|
|
|
- <el-option v-for="item in typeList" :label="item.value" :value="item.type" />
|
|
|
+ <el-form-item label="处理状态" prop="isHandleStore">
|
|
|
+ <el-select
|
|
|
+ v-model="queryParams.isHandleStore"
|
|
|
+ placeholder="请选择处理状态"
|
|
|
+ clearable
|
|
|
+ size="small"
|
|
|
+ >
|
|
|
+ <el-option label="未处理" value="0"></el-option>
|
|
|
+ <el-option label="已处理" value="1"></el-option>
|
|
|
</el-select>
|
|
|
</el-form-item>
|
|
|
+ <el-form-item label="投诉时间" prop="complaintTime">
|
|
|
+ <el-date-picker v-model="complaintTime" size="small" style="width: 220px" value-format="yyyy-MM-dd" type="daterange" range-separator="-" start-placeholder="开始日期" end-placeholder="结束日期" @change="changeTime"></el-date-picker>
|
|
|
+ </el-form-item>
|
|
|
<el-form-item>
|
|
|
<el-button type="primary" icon="el-icon-search" size="mini" @click="handleQuery">搜索</el-button>
|
|
|
<el-button icon="el-icon-refresh" size="mini" @click="resetQuery">重置</el-button>
|
|
|
@@ -31,101 +40,84 @@
|
|
|
</el-form>
|
|
|
|
|
|
<el-row :gutter="10" class="mb8">
|
|
|
-<!-- <el-col :span="1.5">-->
|
|
|
-<!-- <el-button-->
|
|
|
-<!-- type="primary"-->
|
|
|
-<!-- plain-->
|
|
|
-<!-- icon="el-icon-plus"-->
|
|
|
-<!-- size="mini"-->
|
|
|
-<!-- @click="handleAdd"-->
|
|
|
-<!-- v-hasPermi="['user:complaint:add']"-->
|
|
|
-<!-- >新增</el-button>-->
|
|
|
-<!-- </el-col>-->
|
|
|
-<!-- <el-col :span="1.5">-->
|
|
|
-<!-- <el-button-->
|
|
|
-<!-- type="success"-->
|
|
|
-<!-- plain-->
|
|
|
-<!-- icon="el-icon-edit"-->
|
|
|
-<!-- size="mini"-->
|
|
|
-<!-- :disabled="single"-->
|
|
|
-<!-- @click="handleUpdate"-->
|
|
|
-<!-- v-hasPermi="['user:complaint:edit']"-->
|
|
|
-<!-- >修改</el-button>-->
|
|
|
-<!-- </el-col>-->
|
|
|
<el-col :span="1.5">
|
|
|
<el-button
|
|
|
- type="danger"
|
|
|
+ type="warning"
|
|
|
plain
|
|
|
- icon="el-icon-delete"
|
|
|
+ icon="el-icon-download"
|
|
|
size="mini"
|
|
|
- :disabled="multiple"
|
|
|
- @click="handleDelete"
|
|
|
- v-hasPermi="['user:complaint:remove']"
|
|
|
- >删除</el-button>
|
|
|
+ :loading="exportLoading"
|
|
|
+ @click="handleExport"
|
|
|
+ >导出</el-button>
|
|
|
</el-col>
|
|
|
-<!-- <el-col :span="1.5">-->
|
|
|
-<!-- <el-button-->
|
|
|
-<!-- type="warning"-->
|
|
|
-<!-- plain-->
|
|
|
-<!-- icon="el-icon-download"-->
|
|
|
-<!-- size="mini"-->
|
|
|
-<!-- :loading="exportLoading"-->
|
|
|
-<!-- @click="handleExport"-->
|
|
|
-<!-- v-hasPermi="['user:complaint:export']"-->
|
|
|
-<!-- >导出</el-button>-->
|
|
|
-<!-- </el-col>-->
|
|
|
<right-toolbar :showSearch.sync="showSearch" @queryTable="getList"></right-toolbar>
|
|
|
</el-row>
|
|
|
|
|
|
<el-table border v-loading="loading" :data="complaintList" @selection-change="handleSelectionChange">
|
|
|
<el-table-column type="selection" width="55" align="center" />
|
|
|
<el-table-column label="id" align="center" prop="id" />
|
|
|
- <el-table-column label="投诉用户" align="center" prop="nickName" />
|
|
|
- <el-table-column label="投诉标题" align="center" prop="title" />
|
|
|
- <el-table-column label="投诉内容" align="center" prop="content" />
|
|
|
- <el-table-column label="商品图片" align="center" width="120">
|
|
|
+ <el-table-column label="用户" align="center" prop="nickName" />
|
|
|
+ <el-table-column label="投诉方式" align="center" prop="complaintType" >
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <template v-for="item in complaintTypeOptions">
|
|
|
+ <el-tag v-if="item.dictValue === scope.row.complaintType" :key="item.dictValue">
|
|
|
+ {{ item.dictLabel }}
|
|
|
+ </el-tag>
|
|
|
+ </template>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="投诉类型" align="center" prop="type" >
|
|
|
+ <template slot-scope="scope">
|
|
|
+ <dict-tag :options="typeOptions" :value="scope.row.type"/>
|
|
|
+ </template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column label="投诉时间" align="center" prop="createTime" />
|
|
|
+ <el-table-column
|
|
|
+ label="店铺消息是否处理"
|
|
|
+ align="center"
|
|
|
+ prop="isHandlePlatform"
|
|
|
+ :render-header="renderHandleHeader"
|
|
|
+ >
|
|
|
<template slot-scope="scope">
|
|
|
- <el-popover
|
|
|
- placement="right"
|
|
|
- title=""
|
|
|
- trigger="hover">
|
|
|
- <img slot="reference" :src="scope.row.images" width="100">
|
|
|
- <img :src="scope.row.images" style="max-width: 150px;">
|
|
|
- </el-popover>
|
|
|
+ <el-tag
|
|
|
+ :type="(scope.row.isHandleStore === 1)? 'success' : 'warning'"
|
|
|
+ disable-transitions
|
|
|
+ >
|
|
|
+ {{ formatHandleStatus(scope.row.isHandleStore) }}
|
|
|
+ </el-tag>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
- <el-table-column label="投诉类型" align="center">
|
|
|
+ <el-table-column
|
|
|
+ label="处理状态"
|
|
|
+ align="center"
|
|
|
+ prop="isHandlePlatform"
|
|
|
+ :render-header="renderHandleHeader"
|
|
|
+ >
|
|
|
<template slot-scope="scope">
|
|
|
- <span v-for="item in typeList" :key="item.type">
|
|
|
- <template v-if="item.type === scope.row.type">
|
|
|
- {{ item.value }}
|
|
|
- </template>
|
|
|
- </span>
|
|
|
+ <el-tag
|
|
|
+ :type="(scope.row.isProcessCompleted === 1)? 'success' : 'danger'"
|
|
|
+ disable-transitions
|
|
|
+ >
|
|
|
+ {{ formatHandleProcessCompleted(scope.row.isProcessCompleted) }}
|
|
|
+ </el-tag>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
- <el-table-column label="备注" align="center" prop="remark" />
|
|
|
+ <el-table-column label="备注" align="center" prop="remarks" />
|
|
|
<el-table-column label="操作" align="center" class-name="small-padding fixed-width">
|
|
|
<template slot-scope="scope">
|
|
|
<el-button
|
|
|
size="mini"
|
|
|
type="text"
|
|
|
- icon="el-icon-chat-dot-round"
|
|
|
- @click="reply(scope.row)"
|
|
|
- >回复</el-button>
|
|
|
+ icon="el-icon-check"
|
|
|
+ @click="handlecompleteComplaint(scope.row)"
|
|
|
+ v-if="scope.row.isProcessCompleted !== 1"
|
|
|
+ >完成投诉</el-button>
|
|
|
<el-button
|
|
|
size="mini"
|
|
|
type="text"
|
|
|
icon="el-icon-edit"
|
|
|
@click="handleUpdate(scope.row)"
|
|
|
- v-hasPermi="['user:complaint:edit']"
|
|
|
- >修改</el-button>
|
|
|
- <el-button
|
|
|
- size="mini"
|
|
|
- type="text"
|
|
|
- icon="el-icon-delete"
|
|
|
- @click="handleDelete(scope.row)"
|
|
|
- v-hasPermi="['user:complaint:remove']"
|
|
|
- >删除</el-button>
|
|
|
+ >投诉详情</el-button>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
</el-table>
|
|
|
@@ -138,125 +130,267 @@
|
|
|
@pagination="getList"
|
|
|
/>
|
|
|
|
|
|
- <!-- 添加或修改用户投诉对话框 -->
|
|
|
- <el-dialog :title="title" :visible.sync="open" width="500px" append-to-body>
|
|
|
- <el-form ref="form" :model="form" :rules="rules" label-width="80px">
|
|
|
- <el-form-item label="投诉用户" prop="nickName">
|
|
|
- <el-input v-model="form.nickName" placeholder="请输入投诉用户" />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="投诉标题" prop="title">
|
|
|
- <el-input v-model="form.title" placeholder="请输入投诉标题" />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="投诉内容">
|
|
|
- <el-input type="textarea" :rows="3" v-model="form.content" :min-height="192" disabled/>
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="投诉图片" prop="images">
|
|
|
- <div class="demo-image__preview">
|
|
|
- <el-image
|
|
|
- style="width: 100px; height: 100px"
|
|
|
- :src="form.images"
|
|
|
- @click="imageClick(form.images)"
|
|
|
- :preview-src-list="srcList">
|
|
|
- </el-image>
|
|
|
+ <!-- 修改用户投诉对话框 -->
|
|
|
+ <el-dialog :title="title" :visible.sync="open" width="900px" append-to-body>
|
|
|
+ <!-- 修改tab-click事件处理器名称 -->
|
|
|
+ <el-tabs v-model="activeTab" type="card" @tab-click="handleClick">
|
|
|
+ <!-- 投诉详情标签页 -->
|
|
|
+ <el-tab-pane :label="complaintType === 1?'咨询详情':'投诉详情'" name="complaint">
|
|
|
+ <el-form ref="form" :model="form" :rules="rules" label-width="80px">
|
|
|
+ <el-form-item label="投诉内容" prop="name">
|
|
|
+ <el-input v-model="form.name" :disabled="true" />
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="详细内容" v-if="form.content && form.content!=''">
|
|
|
+ <el-input type="textarea" v-model="form.content" :disabled="true"/>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="联系方式" prop="phone" v-if="form.phone && form.phone!=''">
|
|
|
+ <el-input v-model="form.phone" :disabled="true"/>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="图片" v-if="form.images && form.images!=''">
|
|
|
+ <div v-for="(url, index) in imageUrls(form.images)" :key="index" class="image-container">
|
|
|
+ <el-image :src="url" :preview-src-list="[url]" style="width: 100px; height: 100px;"></el-image>
|
|
|
+ </div>
|
|
|
+ </el-form-item>
|
|
|
+ <!-- <el-form-item label="交易截图" v-if="form.tradeImage && form.tradeImage!=''">
|
|
|
+ <div v-for="(url, index) in imageUrls(form.tradeImage)" :key="index" class="image-container">
|
|
|
+ <el-image :src="url" :preview-src-list="[url]" style="width: 100px; height: 100px;"></el-image>
|
|
|
+ </div>
|
|
|
+ </el-form-item> -->
|
|
|
+ <el-form-item label="用户" prop="nickName">
|
|
|
+ <el-input v-model="form.nickName" :disabled="true"/>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="是否处理" prop="isHandleStore">
|
|
|
+ <el-select v-model="form.isHandleStore" placeholder="请选择处理状态" :disabled="true">
|
|
|
+ <el-option label="未处理" :value="0"></el-option>
|
|
|
+ <el-option label="已处理" :value="1"></el-option>
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="备注" prop="remarks" >
|
|
|
+ <el-input type="textarea" v-model="form.remarks" :maxlength="250" :disabled="true"/>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ </el-tab-pane>
|
|
|
+
|
|
|
+ <!-- 回复详情标签页 -->
|
|
|
+ <el-tab-pane label="回复详情" name="replies">
|
|
|
+ <div class="reply-section">
|
|
|
+ <!-- 添加回复表单 -->
|
|
|
+ <el-card class="add-reply-card" shadow="never">
|
|
|
+ <div slot="header">
|
|
|
+ <span>添加回复</span>
|
|
|
+ </div>
|
|
|
+ <el-form :model="replyForm" ref="replyForm" label-width="80px">
|
|
|
+ <el-form-item label="回复内容" prop="content" :rules="[{ required: true, message: '请输入回复内容', trigger: 'blur' }]" >
|
|
|
+ <el-input
|
|
|
+ type="textarea"
|
|
|
+ v-model="replyForm.content"
|
|
|
+ placeholder="请输入回复内容"
|
|
|
+ :rows="3"
|
|
|
+ maxlength="500"
|
|
|
+ show-word-limit
|
|
|
+ :disabled="isProcessCompleted === 1"
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <!-- 添加图片上传功能 -->
|
|
|
+ <el-form-item label="上传图片" prop="images">
|
|
|
+ <!-- <div class="image-upload-container">
|
|
|
+ <el-upload
|
|
|
+ ref="imageUpload"
|
|
|
+ :action="uploadUrl"
|
|
|
+ :show-file-list="false"
|
|
|
+ :file-list="replyImageList"
|
|
|
+ :on-success="handleSuccess"
|
|
|
+ :before-upload="beforeUpload"
|
|
|
+ :limit="4"
|
|
|
+ list-type="picture-card"
|
|
|
+ accept="image/*"
|
|
|
+ >
|
|
|
+ <i class="el-icon-plus"></i>
|
|
|
+ <div slot="tip" class="el-upload__tip">
|
|
|
+ 最多上传4张图片,单张图片不超过2MB
|
|
|
+ </div>
|
|
|
+ </el-upload>
|
|
|
+ </div> -->
|
|
|
+ <ImageUpload v-model="replyForm.images" type="image" :num="4" :width="150" :height="150" v-if="isProcessCompleted !== 1"/>
|
|
|
+ <el-upload
|
|
|
+ ref="imageUpload"
|
|
|
+ :action="uploadUrl"
|
|
|
+ :show-file-list="false"
|
|
|
+ :file-list="replyImageList"
|
|
|
+ :on-success="handleSuccess"
|
|
|
+ :before-upload="beforeUpload"
|
|
|
+ :limit="4"
|
|
|
+ list-type="picture-card"
|
|
|
+ accept="image/*"
|
|
|
+ :disabled="isProcessCompleted === 1"
|
|
|
+ v-else
|
|
|
+ >
|
|
|
+ <i class="el-icon-plus"></i>
|
|
|
+ </el-upload>
|
|
|
+ </el-form-item>
|
|
|
+
|
|
|
+ <el-form-item>
|
|
|
+ <el-button type="primary" size="small" @click="submitReply" :loading="replyLoading" :disabled="isProcessCompleted === 1">
|
|
|
+ <i class="el-icon-s-promotion"></i> 发送回复
|
|
|
+ </el-button>
|
|
|
+ </el-form-item>
|
|
|
+ </el-form>
|
|
|
+ </el-card>
|
|
|
+
|
|
|
+ <!-- 回复列表 -->
|
|
|
+ <el-card class="reply-list-card" shadow="never">
|
|
|
+ <div slot="header">
|
|
|
+ <span>回复记录 ({{ replyTotal }}条)</span>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div v-loading="replyLoading" class="reply-list">
|
|
|
+ <div v-if="replyList.length === 0" class="empty-replies">
|
|
|
+ <el-empty description="暂无回复记录" :image-size="100"></el-empty>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div v-else>
|
|
|
+ <div v-for="reply in replyList" :key="reply.id" class="reply-item">
|
|
|
+ <div class="reply-header">
|
|
|
+ <div class="reply-user">
|
|
|
+ <el-avatar :size="32" :src="reply.avatar || '/default-avatar.png'">
|
|
|
+ {{ reply.userName ? reply.userName.charAt(0) : 'U' }}
|
|
|
+ </el-avatar>
|
|
|
+ <!-- <span class="user-name">{{ reply.userName || '系统管理员' }}</span> -->
|
|
|
+ <el-tag size="mini" :type="reply.sendType != 1 ? 'success' : 'info'">
|
|
|
+ {{ reply.sendType === 1 ? '用户' : reply.sendType === 2 ? '系统平台':'店铺平台' }}
|
|
|
+ </el-tag>
|
|
|
+ </div>
|
|
|
+ <div class="reply-time">
|
|
|
+ {{ reply.createTime }}
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="reply-content">
|
|
|
+ {{ reply.content }}
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 显示回复中的图片 -->
|
|
|
+ <div class="reply-images" v-if="reply.images && reply.images !== ''">
|
|
|
+ <div v-for="(url, index) in imageUrls(reply.images)" :key="index" class="reply-image-container">
|
|
|
+ <el-image
|
|
|
+ :src="url"
|
|
|
+ :preview-src-list="imageUrls(reply.images)"
|
|
|
+ style="width: 80px; height: 80px;"
|
|
|
+ fit="cover"
|
|
|
+ ></el-image>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <div class="reply-actions" v-if="reply.userType === 'admin'">
|
|
|
+ <el-button type="text" size="mini" @click="editReply(reply)">
|
|
|
+ <i class="el-icon-edit"></i> 编辑
|
|
|
+ </el-button>
|
|
|
+ <el-button type="text" size="mini" @click="deleteReply(reply)" style="color: #f56c6c;">
|
|
|
+ <i class="el-icon-delete"></i> 删除
|
|
|
+ </el-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+
|
|
|
+ <!-- 回复分页 -->
|
|
|
+ <div class="reply-pagination" v-if="replyTotal > 0">
|
|
|
+ <el-pagination
|
|
|
+ @size-change="handleReplySizeChange"
|
|
|
+ @current-change="handleReplyCurrentChange"
|
|
|
+ :current-page="replyQueryParams.pageNum"
|
|
|
+ :page-sizes="[5, 10, 20]"
|
|
|
+ :page-size="replyQueryParams.pageSize"
|
|
|
+ layout="total, sizes, prev, pager, next, jumper"
|
|
|
+ :total="replyTotal"
|
|
|
+ small
|
|
|
+ />
|
|
|
+ </div>
|
|
|
+ </el-card>
|
|
|
</div>
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="投诉类型" prop="type">
|
|
|
- <el-select v-model="form.type" placeholder="请选择投诉类型" clearable size="small">
|
|
|
- <el-option v-for="item in typeList" :label="item.value" :value="item.type" />
|
|
|
- </el-select>
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="备注" prop="remark">
|
|
|
- <el-input v-model="form.remark" placeholder="请输入备注" />
|
|
|
- </el-form-item>
|
|
|
- </el-form>
|
|
|
+ </el-tab-pane>
|
|
|
+ </el-tabs>
|
|
|
+
|
|
|
<div slot="footer" class="dialog-footer">
|
|
|
- <el-button type="primary" @click="submitForm">确 定</el-button>
|
|
|
+ <el-button type="primary" @click="submitForm" v-if="activeTab === 'complaint' && form.id==null">确 定</el-button>
|
|
|
<el-button @click="cancel">取 消</el-button>
|
|
|
</div>
|
|
|
</el-dialog>
|
|
|
|
|
|
- <el-dialog
|
|
|
- title="投诉消息回复"
|
|
|
- :visible.sync="replyVisible"
|
|
|
- width="50%"
|
|
|
- :before-close="handleClose"
|
|
|
- center>
|
|
|
- <el-form label-width="80px">
|
|
|
- <el-row>
|
|
|
- <el-col>
|
|
|
- <el-form-item label="投诉标题">
|
|
|
- <el-input v-model="complaint.title" disabled></el-input>
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- </el-row>
|
|
|
- <el-row>
|
|
|
- <el-col>
|
|
|
- <el-form-item label="投诉内容">
|
|
|
- <el-input type="textarea" v-model="complaint.content" :rows="2" disabled></el-input>
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- </el-row>
|
|
|
- <el-row>
|
|
|
- <el-col :span="12">
|
|
|
- <el-form-item label="凭证图片">
|
|
|
- <el-image
|
|
|
- style="width: 100px; height: 100px"
|
|
|
- :src="replyInfo.url"
|
|
|
- :preview-src-list="replyInfo.urlList">
|
|
|
- </el-image>
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- <el-col :span="12">
|
|
|
- <el-form-item label="投诉类型">
|
|
|
- <span v-for="item in typeList" :key="item.type">
|
|
|
- <template v-if="item.type === complaint.type">
|
|
|
- {{ item.value }}
|
|
|
- </template>
|
|
|
- </span>
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- </el-row>
|
|
|
+
|
|
|
+ <!-- 编辑回复对话框 -->
|
|
|
+ <el-dialog title="编辑回复" :visible.sync="editReplyVisible" width="500px" append-to-body>
|
|
|
+ <el-form :model="editReplyForm" ref="editReplyForm" label-width="80px">
|
|
|
+ <el-form-item label="回复内容" prop="content" :rules="[{ required: true, message: '请输入回复内容', trigger: 'blur' }]">
|
|
|
+ <el-input
|
|
|
+ type="textarea"
|
|
|
+ v-model="editReplyForm.content"
|
|
|
+ placeholder="请输入回复内容"
|
|
|
+ :rows="4"
|
|
|
+ maxlength="500"
|
|
|
+ show-word-limit
|
|
|
+ />
|
|
|
+ </el-form-item>
|
|
|
</el-form>
|
|
|
- <replyIndex v-if="isReplyOpen" ref="replyRef" :complaint-id="complaintId"></replyIndex>
|
|
|
- <span slot="footer" class="dialog-footer">
|
|
|
- <el-button @click="replyClose">关 闭</el-button>
|
|
|
- </span>
|
|
|
+ <div slot="footer" class="dialog-footer">
|
|
|
+ <el-button type="primary" @click="updateReply" :loading="replyLoading">确 定</el-button>
|
|
|
+ <el-button @click="editReplyVisible = false">取 消</el-button>
|
|
|
+ </div>
|
|
|
</el-dialog>
|
|
|
</div>
|
|
|
</template>
|
|
|
|
|
|
<script>
|
|
|
-import { listComplaint, getComplaint, delComplaint, addComplaint, updateComplaint, exportComplaint } from "@/api/user/complaint";
|
|
|
-import Index from '@/views/user/complaint/reply/index.vue'
|
|
|
+import {
|
|
|
+ listComplaint,
|
|
|
+ getComplaint,
|
|
|
+ delComplaint,
|
|
|
+ addComplaint,
|
|
|
+ updateComplaint,
|
|
|
+ exportComplaint,
|
|
|
+ completeComplaint
|
|
|
+} from '@/api/user/complaint';
|
|
|
+
|
|
|
+import {listMsg,addMsg} from '@/api/user/complaintMsg';
|
|
|
+import ImageUpload from '@/components/ImageUpload/index';
|
|
|
export default {
|
|
|
name: "Complaint",
|
|
|
+ components: {
|
|
|
+ ImageUpload
|
|
|
+ },
|
|
|
data() {
|
|
|
return {
|
|
|
- isReplyOpen:false,
|
|
|
- complaint:{
|
|
|
- title:"",
|
|
|
- content:"",
|
|
|
- type:'1',
|
|
|
- },
|
|
|
- replyInfo:{
|
|
|
- url:null,
|
|
|
- urlList:[],
|
|
|
- },
|
|
|
+ complaintType:0,
|
|
|
+ isProcessCompleted:0,
|
|
|
+ replyImageList: [],
|
|
|
+ uploadUrl:process.env.VUE_APP_BASE_API+"/common/uploadOSS",
|
|
|
complaintId:null,
|
|
|
- srcList:[],
|
|
|
- typeList:[
|
|
|
+ activeName:"0",
|
|
|
+ typeOptions:[
|
|
|
+ {
|
|
|
+ dictValue:'0',
|
|
|
+ dictLabel:'默认'
|
|
|
+ },
|
|
|
{
|
|
|
- type:1,
|
|
|
- value:'店铺'
|
|
|
- },{
|
|
|
- type:2,
|
|
|
- value:'商品'
|
|
|
- },{
|
|
|
- type:0,
|
|
|
- value:'其它'
|
|
|
+ dictValue:'1',
|
|
|
+ dictLabel:'店铺'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ dictValue:'2',
|
|
|
+ dictLabel:'商品'
|
|
|
},
|
|
|
],
|
|
|
- replyVisible:false,
|
|
|
+ complaintTypeOptions:[
|
|
|
+ {
|
|
|
+ dictValue:1,
|
|
|
+ dictLabel:'咨询'
|
|
|
+ },
|
|
|
+ {
|
|
|
+ dictValue:2,
|
|
|
+ dictLabel:'投诉/举报'
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ complaintTime:null,
|
|
|
// 遮罩层
|
|
|
loading: true,
|
|
|
// 导出遮罩层
|
|
|
@@ -277,32 +411,105 @@ export default {
|
|
|
title: "",
|
|
|
// 是否显示弹出层
|
|
|
open: false,
|
|
|
+ // 当前激活的标签页
|
|
|
+ activeTab: 'complaint',
|
|
|
// 查询参数
|
|
|
queryParams: {
|
|
|
pageNum: 1,
|
|
|
pageSize: 10,
|
|
|
- fsUserId: null,
|
|
|
- title: null,
|
|
|
+ userId: null,
|
|
|
+ userName: null,
|
|
|
+ templateId: null,
|
|
|
content: null,
|
|
|
- images: null,
|
|
|
- type:null,
|
|
|
- linkId: null,
|
|
|
- nickName: null,
|
|
|
+ phone: null,
|
|
|
+ urls: null,
|
|
|
+ account: null,
|
|
|
+ isHandle: null
|
|
|
},
|
|
|
// 表单参数
|
|
|
form: {},
|
|
|
// 表单校验
|
|
|
rules: {
|
|
|
+ userId: [
|
|
|
+ { required: true, message: "用户id不能为空", trigger: "blur" }
|
|
|
+ ],
|
|
|
+ templateId: [
|
|
|
+ { required: true, message: "投诉模板id不能为空", trigger: "blur" }
|
|
|
+ ],
|
|
|
+ },
|
|
|
+ // 回复相关数据
|
|
|
+ replyList: [],
|
|
|
+ replyTotal: 0,
|
|
|
+ replyLoading: false,
|
|
|
+ replyQueryParams: {
|
|
|
+ pageNum: 1,
|
|
|
+ pageSize: 10,
|
|
|
+ complaintId: null
|
|
|
+ },
|
|
|
+ replyForm: {
|
|
|
+ content: '',
|
|
|
+ complaintId: null
|
|
|
+ },
|
|
|
+ // 编辑回复
|
|
|
+ editReplyVisible: false,
|
|
|
+ editReplyForm: {
|
|
|
+ id: null,
|
|
|
+ content: ''
|
|
|
}
|
|
|
};
|
|
|
},
|
|
|
- components:{
|
|
|
- replyIndex: Index
|
|
|
- },
|
|
|
created() {
|
|
|
this.getList();
|
|
|
},
|
|
|
methods: {
|
|
|
+ handleClick(tab) {
|
|
|
+ if (tab.name === 'replies') {
|
|
|
+ this.getReplyList();
|
|
|
+ }
|
|
|
+ },
|
|
|
+ renderHandleHeader(h, { column }) {
|
|
|
+ if (column.label === '是否处理') {
|
|
|
+ return h('div', [
|
|
|
+ '是否处理',
|
|
|
+ h('el-tooltip', {
|
|
|
+ props: {
|
|
|
+ content: '仅需要处理投诉/举报类',
|
|
|
+ placement: 'top'
|
|
|
+ }
|
|
|
+ }, [
|
|
|
+ h('i', {
|
|
|
+ class: 'el-icon-question',
|
|
|
+ style: 'margin-left: 5px; cursor: pointer; color: #909399;'
|
|
|
+ })
|
|
|
+ ])
|
|
|
+ ]);
|
|
|
+ } else if (column.label === '店铺是否处理') {
|
|
|
+ return h('div', [
|
|
|
+ '店铺是否处理',
|
|
|
+ h('el-tooltip', {
|
|
|
+ props: {
|
|
|
+ content: '仅需要处理咨询类',
|
|
|
+ placement: 'top'
|
|
|
+ }
|
|
|
+ }, [
|
|
|
+ h('i', {
|
|
|
+ class: 'el-icon-question',
|
|
|
+ style: 'margin-left: 5px; cursor: pointer; color: #909399;'
|
|
|
+ })
|
|
|
+ ])
|
|
|
+ ]);
|
|
|
+ }
|
|
|
+ return column.label;
|
|
|
+ },
|
|
|
+ imageUrls(urls) {
|
|
|
+ return String(urls).split(",");
|
|
|
+ },
|
|
|
+ formatHandleStatus(status) {
|
|
|
+ return status === 1 ? '已回复' : '未回复';
|
|
|
+ },
|
|
|
+ formatHandleProcessCompleted(processCompleted) {
|
|
|
+ return processCompleted === 1 ? '已完成' : '处理中';
|
|
|
+ },
|
|
|
/** 查询用户投诉列表 */
|
|
|
getList() {
|
|
|
this.loading = true;
|
|
|
@@ -316,26 +523,42 @@ export default {
|
|
|
cancel() {
|
|
|
this.open = false;
|
|
|
this.reset();
|
|
|
+ this.resetReplyData();
|
|
|
},
|
|
|
// 表单重置
|
|
|
reset() {
|
|
|
this.form = {
|
|
|
id: null,
|
|
|
- fsUserId: null,
|
|
|
- title: null,
|
|
|
+ userId: null,
|
|
|
+ templateId: null,
|
|
|
content: null,
|
|
|
- images: null,
|
|
|
- type: null,
|
|
|
- linkId: null,
|
|
|
+ phone: null,
|
|
|
+ urls: null,
|
|
|
+ account: null,
|
|
|
createTime: null,
|
|
|
- createBy: null,
|
|
|
- updateTime: null,
|
|
|
- updateBy: null,
|
|
|
- remark: null,
|
|
|
- nickName: null,
|
|
|
+ isHandle: 0,
|
|
|
+ remarks:null
|
|
|
};
|
|
|
this.resetForm("form");
|
|
|
},
|
|
|
+ // 重置回复数据
|
|
|
+ resetReplyData() {
|
|
|
+ this.activeTab = 'complaint';
|
|
|
+ this.replyList = [];
|
|
|
+ this.replyTotal = 0;
|
|
|
+ this.replyQueryParams = {
|
|
|
+ pageNum: 1,
|
|
|
+ pageSize: 10,
|
|
|
+ complaintId: null
|
|
|
+ };
|
|
|
+ this.replyForm = {
|
|
|
+ content: '',
|
|
|
+ complaintId: null,
|
|
|
+ images: '' // 重置图片字段
|
|
|
+ };
|
|
|
+ this.replyImageList = [];
|
|
|
+ this.editReplyImageList = [];
|
|
|
+ },
|
|
|
/** 搜索按钮操作 */
|
|
|
handleQuery() {
|
|
|
this.queryParams.pageNum = 1;
|
|
|
@@ -343,14 +566,16 @@ export default {
|
|
|
},
|
|
|
/** 重置按钮操作 */
|
|
|
resetQuery() {
|
|
|
+ this.complaintTime = null;
|
|
|
+ this.queryParams.complaintsTime=null;
|
|
|
+ this.queryParams.complainteTime=null;
|
|
|
this.resetForm("queryForm");
|
|
|
- this.queryParams.nickName = null;
|
|
|
this.handleQuery();
|
|
|
},
|
|
|
// 多选框选中数据
|
|
|
handleSelectionChange(selection) {
|
|
|
this.ids = selection.map(item => item.id)
|
|
|
- this.single = selection.length!==1
|
|
|
+ this.single = selection.length !== 1
|
|
|
this.multiple = !selection.length
|
|
|
},
|
|
|
/** 新增按钮操作 */
|
|
|
@@ -361,12 +586,21 @@ export default {
|
|
|
},
|
|
|
/** 修改按钮操作 */
|
|
|
handleUpdate(row) {
|
|
|
+ this.complaintType = row.complaintType;
|
|
|
+ this.isProcessCompleted = row.isProcessCompleted;
|
|
|
this.reset();
|
|
|
- const id = row.id || this.ids
|
|
|
- getComplaint(id).then(response => {
|
|
|
+ this.resetReplyData();
|
|
|
+ // const id = row.id || this.ids
|
|
|
+ const complaintId = row.id || this.complaintId
|
|
|
+ this.complaintId = complaintId;
|
|
|
+ getComplaint(complaintId).then(response => {
|
|
|
this.form = response.data;
|
|
|
+ this.replyForm.complaintId = complaintId;
|
|
|
+ this.replyQueryParams.complaintId = complaintId;
|
|
|
this.open = true;
|
|
|
- this.title = "修改用户投诉";
|
|
|
+ this.title = "投诉详情";
|
|
|
+ // 加载回复列表
|
|
|
+ this.getReplyList();
|
|
|
});
|
|
|
},
|
|
|
/** 提交按钮 */
|
|
|
@@ -418,34 +652,233 @@ export default {
|
|
|
this.exportLoading = false;
|
|
|
}).catch(() => {});
|
|
|
},
|
|
|
- imageClick(url){
|
|
|
- this.srcList=[];
|
|
|
- this.srcList.push(url)
|
|
|
+ changeTime(){
|
|
|
+ if(this.complaintTime!=null){
|
|
|
+ this.queryParams.complaintsTime=this.complaintTime[0];
|
|
|
+ this.queryParams.complainteTime=this.complaintTime[1];
|
|
|
+ }else{
|
|
|
+ this.queryParams.complaintsTime=null;
|
|
|
+ this.queryParams.complainteTime=null;
|
|
|
+ }
|
|
|
},
|
|
|
- handleClose(done) {
|
|
|
- this.$confirm('确认关闭?')
|
|
|
- .then(_ => {
|
|
|
- this.isReplyOpen=false;
|
|
|
- done();
|
|
|
- })
|
|
|
- .catch(_ => {});
|
|
|
+ // 获取回复列表
|
|
|
+ getReplyList() {
|
|
|
+ this.replyLoading = true;
|
|
|
+ // 模拟API调用 - 你需要替换为实际的API
|
|
|
+ setTimeout(() => {
|
|
|
+ // 模拟数据
|
|
|
+ listMsg(this.replyQueryParams).then(response => {
|
|
|
+ this.replyList = response.rows;
|
|
|
+ this.replyTotal = response.total;
|
|
|
+ this.replyLoading = false;
|
|
|
+ });
|
|
|
+ }, 500);
|
|
|
+ },
|
|
|
+ // 提交回复
|
|
|
+ submitReply() {
|
|
|
+ this.$refs.replyForm.validate(valid => {
|
|
|
+ if (valid) {
|
|
|
+ this.replyLoading = true;
|
|
|
+ // const imageUrls = this.replyImageList.map(file => file.url || file.response?.url).filter(url => url);
|
|
|
+ // this.replyForm.images = imageUrls.join(',');
|
|
|
+ // 模拟API调用
|
|
|
+ setTimeout(() => {
|
|
|
+ const newReply = {
|
|
|
+ content: this.replyForm.content,
|
|
|
+ complaintId:this.replyForm.complaintId,
|
|
|
+ images: this.replyForm.images, // 包含图片数据
|
|
|
+ sendType: 2
|
|
|
+ };
|
|
|
+ addMsg(newReply).then(response => {
|
|
|
+ this.replyLoading = false;
|
|
|
+ this.$message.success('回复发送成功');
|
|
|
+ this.replyForm.content = '';
|
|
|
+ this.replyForm.images = ''; // 重置图片字段
|
|
|
+ this.replyImageList = []; // 重置图片列表
|
|
|
+ this.getReplyList()
|
|
|
+ });
|
|
|
+
|
|
|
+ }, 500);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
+ // 编辑回复
|
|
|
+ editReply(reply) {
|
|
|
+ this.editReplyForm = {
|
|
|
+ id: reply.id,
|
|
|
+ content: reply.content,
|
|
|
+ images: reply.images || '' // 包含图片数据
|
|
|
+ };
|
|
|
+ this.editReplyImageList = reply.images ?
|
|
|
+ reply.images.split(',').map((url, index) => ({
|
|
|
+ uid: index,
|
|
|
+ name: `image-${index}`,
|
|
|
+ url: url
|
|
|
+ })) : [];
|
|
|
+ this.editReplyVisible = true;
|
|
|
+ },
|
|
|
+
|
|
|
+ // 更新回复
|
|
|
+ updateReply() {
|
|
|
+ this.$refs.editReplyForm.validate(valid => {
|
|
|
+ if (valid) {
|
|
|
+ this.replyLoading = true;
|
|
|
+ // const imageUrls = this.editReplyImageList.map(file => file.url || file.response?.url).filter(url => url);
|
|
|
+ // this.editReplyForm.images = imageUrls.join(',');
|
|
|
+
|
|
|
+ // 模拟API调用
|
|
|
+ setTimeout(() => {
|
|
|
+ const index = this.replyList.findIndex(item => item.id === this.editReplyForm.id);
|
|
|
+ if (index !== -1) {
|
|
|
+ this.replyList[index].content = this.editReplyForm.content;
|
|
|
+ this.replyList[index].images = this.editReplyForm.images; // 更新图片
|
|
|
+ }
|
|
|
+ this.editReplyVisible = false;
|
|
|
+ this.replyLoading = false;
|
|
|
+ this.$message.success('回复更新成功');
|
|
|
+ }, 500);
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
+ // 删除回复
|
|
|
+ deleteReply(reply) {
|
|
|
+ this.$confirm('确定要删除这条回复吗?', '提示', {
|
|
|
+ confirmButtonText: '确定',
|
|
|
+ cancelButtonText: '取消',
|
|
|
+ type: 'warning'
|
|
|
+ }).then(() => {
|
|
|
+ const index = this.replyList.findIndex(item => item.id === reply.id);
|
|
|
+ if (index !== -1) {
|
|
|
+ this.replyList.splice(index, 1);
|
|
|
+ this.replyTotal--;
|
|
|
+ this.$message.success('删除成功');
|
|
|
+ }
|
|
|
+ });
|
|
|
+ },
|
|
|
+ // 回复分页大小改变
|
|
|
+ handleReplySizeChange(val) {
|
|
|
+ this.replyQueryParams.pageSize = val;
|
|
|
+ this.getReplyList();
|
|
|
},
|
|
|
- //消息回复页面
|
|
|
- reply(row){
|
|
|
- this.isReplyOpen=true;
|
|
|
- this.complaintId=row.id;
|
|
|
- this.replyInfo.url = row.images;
|
|
|
- this.replyInfo.urlList=[];
|
|
|
- this.replyInfo.urlList.push(row.images);
|
|
|
- this.complaint.title = row.title;
|
|
|
- this.complaint.content = row.content;
|
|
|
- this.complaint.type = row.type;
|
|
|
- this.replyVisible = true;
|
|
|
+ // 回复当前页改变
|
|
|
+ handleReplyCurrentChange(val) {
|
|
|
+ this.replyQueryParams.pageNum = val;
|
|
|
+ this.getReplyList();
|
|
|
},
|
|
|
- replyClose(){
|
|
|
- this.isReplyOpen=false;
|
|
|
- this.replyVisible = false;
|
|
|
+ handleSuccess(res, file) {
|
|
|
+ if(res.code==200){
|
|
|
+ this.form.licenseImages=res.url;
|
|
|
+ this.$forceUpdate()
|
|
|
+ }
|
|
|
+ else{
|
|
|
+ this.msgError(res.msg);
|
|
|
+ }
|
|
|
+ },
|
|
|
+
|
|
|
+ beforeUpload(file) {
|
|
|
+ const isLt1M = file.size / 1024 / 1024 < 1;
|
|
|
+ if (!isLt1M) {
|
|
|
+ this.$message.error('上传图片大小不能超过 1MB!');
|
|
|
+ }
|
|
|
+ return isLt1M;
|
|
|
+ },
|
|
|
+ handlecompleteComplaint(row) {
|
|
|
+ this.$confirm('是否完成当前投诉处理, 是否继续?', '提示', {
|
|
|
+ confirmButtonText: '确定',
|
|
|
+ cancelButtonText: '取消',
|
|
|
+ type: 'warning'
|
|
|
+ }).then(() => {
|
|
|
+ completeComplaint({id:row.id}).then(response => {
|
|
|
+ this.getList();
|
|
|
+ })
|
|
|
+ }).catch(() => {
|
|
|
+ this.$message({
|
|
|
+ type: 'info',
|
|
|
+ message: '已取消删除'
|
|
|
+ });
|
|
|
+ });
|
|
|
}
|
|
|
}
|
|
|
};
|
|
|
</script>
|
|
|
+
|
|
|
+<style scoped>
|
|
|
+.reply-section {
|
|
|
+ margin-top: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.add-reply-card {
|
|
|
+ margin-bottom: 20px;
|
|
|
+}
|
|
|
+
|
|
|
+.reply-list-card {
|
|
|
+ min-height: 400px;
|
|
|
+}
|
|
|
+
|
|
|
+.reply-list {
|
|
|
+ min-height: 300px;
|
|
|
+}
|
|
|
+
|
|
|
+.empty-replies {
|
|
|
+ display: flex;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+ height: 200px;
|
|
|
+}
|
|
|
+
|
|
|
+.reply-item {
|
|
|
+ border-bottom: 1px solid #f0f0f0;
|
|
|
+ padding: 15px 0;
|
|
|
+}
|
|
|
+
|
|
|
+.reply-item:last-child {
|
|
|
+ border-bottom: none;
|
|
|
+}
|
|
|
+
|
|
|
+.reply-header {
|
|
|
+ display: flex;
|
|
|
+ justify-content: space-between;
|
|
|
+ align-items: center;
|
|
|
+ margin-bottom: 10px;
|
|
|
+}
|
|
|
+
|
|
|
+.reply-user {
|
|
|
+ display: flex;
|
|
|
+ align-items: center;
|
|
|
+ gap: 8px;
|
|
|
+}
|
|
|
+
|
|
|
+.user-name {
|
|
|
+ font-weight: 500;
|
|
|
+ color: #303133;
|
|
|
+}
|
|
|
+
|
|
|
+.reply-time {
|
|
|
+ color: #909399;
|
|
|
+ font-size: 12px;
|
|
|
+}
|
|
|
+
|
|
|
+.reply-content {
|
|
|
+ background-color: #f8f9fa;
|
|
|
+ padding: 12px;
|
|
|
+ border-radius: 6px;
|
|
|
+ margin-bottom: 8px;
|
|
|
+ line-height: 1.5;
|
|
|
+ color: #606266;
|
|
|
+}
|
|
|
+
|
|
|
+.reply-actions {
|
|
|
+ text-align: right;
|
|
|
+}
|
|
|
+
|
|
|
+.reply-pagination {
|
|
|
+ margin-top: 20px;
|
|
|
+ text-align: center;
|
|
|
+}
|
|
|
+
|
|
|
+.image-container {
|
|
|
+ display: inline-block;
|
|
|
+ margin-right: 10px;
|
|
|
+ margin-bottom: 10px;
|
|
|
+}
|
|
|
+</style>
|