Browse Source

feat(complaint): 新增投诉管理模块

yz 1 month ago
parent
commit
abc0bb120d
3 changed files with 1022 additions and 0 deletions
  1. 205 0
      src/api/complaint/index.js
  2. 20 0
      src/router/views/index.js
  3. 797 0
      src/views/complaint/index.vue

+ 205 - 0
src/api/complaint/index.js

@@ -0,0 +1,205 @@
+import request from '@/router/axios';
+
+/**
+ * 投诉查询参数类型定义
+ * @typedef {Object} ComplaintQueryParams
+ * @property {string} [complaintNo] - 投诉单号
+ * @property {string} [title] - 投诉标题
+ * @property {number} [complainantType] - 投诉人类型 1-消费者 2-经销商 3-分销商
+ * @property {string} [customerCode] - 客户编码
+ * @property {string} [customerName] - 客户名称
+ * @property {string} [contactName] - 联系人姓名
+ * @property {string} [contactPhone] - 联系人电话
+ * @property {string} [complaintType] - 投诉类型
+ * @property {number} [status] - 投诉状态 1-待处理 2-处理中 3-已完成 4-已关闭
+ * @property {number} [replyStatus] - 回复状态 0-未回复 1-已回复
+ * @property {string} [submitTimeStart] - 提交时间开始
+ * @property {string} [submitTimeEnd] - 提交时间结束
+ * @property {number} [current] - 当前页码
+ * @property {number} [size] - 每页大小
+ */
+
+/**
+ * 投诉表单数据类型定义
+ * @typedef {Object} ComplaintForm
+ * @property {string} [id] - 投诉ID(修改时必填)
+ * @property {string} [complaintNo] - 投诉单号(系统自动生成)
+ * @property {number} complainantType - 投诉人类型 1-消费者 2-经销商 3-分销商
+ * @property {string|number} [customerId] - 客户ID
+ * @property {string} [customerCode] - 客户编码
+ * @property {string} [customerName] - 客户名称
+ * @property {string} contactName - 联系人姓名
+ * @property {string} contactPhone - 联系人电话
+ * @property {string} title - 投诉标题
+ * @property {string} content - 投诉内容
+ * @property {string} complaintType - 投诉类型
+ * @property {number} [visibleScope] - 可见范围
+ * @property {number} [status] - 投诉状态 1-待处理 2-处理中 3-已完成 4-已关闭
+ * @property {number} [replyStatus] - 回复状态 0-未回复 1-已回复
+ * @property {string} [closeReason] - 关闭原因
+ * @property {string} [submitTime] - 提交时间
+ */
+
+/**
+ * 投诉列表项类型定义
+ * @typedef {Object} ComplaintRecord
+ * @property {string} id - 投诉ID
+ * @property {string} createUser - 创建用户ID
+ * @property {string} createDept - 创建部门ID
+ * @property {string} createTime - 创建时间
+ * @property {string} updateUser - 更新用户ID
+ * @property {string} updateTime - 更新时间
+ * @property {number} status - 投诉状态 1-待处理 2-处理中 3-已完成 4-已关闭
+ * @property {number} isDeleted - 是否删除 0-未删除 1-已删除
+ * @property {string} complaintNo - 投诉单号
+ * @property {number} complainantType - 投诉人类型 1-消费者 2-经销商 3-分销商
+ * @property {string|number|null} customerId - 客户ID
+ * @property {string|null} customerCode - 客户编码
+ * @property {string|null} customerName - 客户名称
+ * @property {string} contactName - 联系人姓名
+ * @property {string} contactPhone - 联系人电话
+ * @property {string} title - 投诉标题
+ * @property {string} content - 投诉内容
+ * @property {string} complaintType - 投诉类型
+ * @property {number} visibleScope - 可见范围
+ * @property {number} replyStatus - 回复状态 0-未回复 1-已回复
+ * @property {string|null} closeReason - 关闭原因
+ * @property {string} submitTime - 提交时间
+ */
+
+/**
+ * API响应类型定义
+ * @template T
+ * @typedef {Object} ApiResponse
+ * @property {number} code - 响应码
+ * @property {boolean} success - 是否成功
+ * @property {T} data - 响应数据
+ * @property {string} msg - 响应消息
+ */
+
+/**
+ * 分页结果类型定义
+ * @template T
+ * @typedef {Object} PageResult
+ * @property {T[]} records - 数据记录
+ * @property {number} total - 总记录数
+ * @property {number} size - 每页大小
+ * @property {number} current - 当前页码
+ * @property {Array} orders - 排序信息
+ * @property {boolean} optimizeCountSql - 是否优化count查询
+ * @property {boolean} hitCount - 是否命中count缓存
+ * @property {string|null} countId - count查询ID
+ * @property {string|null} maxLimit - 最大限制
+ * @property {boolean} searchCount - 是否查询count
+ * @property {number} pages - 总页数
+ */
+
+/**
+ * 投诉分页查询
+ * @param {number} current - 当前页码
+ * @param {number} size - 每页大小
+ * @param {ComplaintQueryParams} params - 查询参数
+ * @returns {Promise<ApiResponse<PageResult<ComplaintRecord>>>} 分页查询结果
+ */
+export const getList = (current, size, params) => {
+  return request({
+    url: '/api/blade-factory/api/factory/complaint',
+    method: 'get',
+    params: {
+      ...params,
+      current,
+      size
+    }
+  })
+}
+
+/**
+ * 新增投诉
+ * @param {ComplaintForm} row - 投诉表单数据
+ * @returns {Promise<ApiResponse<null>>} 操作结果
+ */
+export const add = (row) => {
+  return request({
+    url: '/api/blade-factory/api/factory/complaint',
+    method: 'post',
+    data: row
+  })
+}
+
+/**
+ * 修改投诉
+ * @param {ComplaintForm} row - 投诉表单数据
+ * @returns {Promise<ApiResponse<null>>} 操作结果
+ */
+export const update = (row) => {
+  return request({
+    url: '/api/blade-factory/api/factory/complaint',
+    method: 'put',
+    data: row
+  })
+}
+
+/**
+ * 删除投诉
+ * @param {string} ids - 投诉ID列表,多个用逗号分隔
+ * @returns {Promise<ApiResponse<null>>} 操作结果
+ */
+export const remove = (ids) => {
+  return request({
+    url: '/api/blade-factory/api/factory/complaint',
+    method: 'delete',
+    params: {
+      ids
+    }
+  })
+}
+
+/**
+ * 获取投诉详情
+ * @param {string} complaintId - 投诉ID
+ * @returns {Promise<ApiResponse<ComplaintRecord>>} 投诉详情
+ */
+export const getDetail = (complaintId) => {
+  return request({
+    url: `/api/blade-factory/api/factory/complaint/${complaintId}`,
+    method: 'get'
+  })
+}
+
+/**
+ * 更新投诉状态
+ * @param {string} id - 投诉ID
+ * @param {number} status - 新状态
+ * @param {string} [closeReason] - 关闭原因(状态为已关闭时必填)
+ * @returns {Promise<ApiResponse<null>>} 操作结果
+ */
+export const updateStatus = (id, status, closeReason) => {
+  return request({
+    url: '/api/blade-factory/api/factory/complaint/status',
+    method: 'put',
+    data: {
+      id,
+      status,
+      closeReason
+    }
+  })
+}
+
+/**
+ * 批量更新投诉状态
+ * @param {string[]} ids - 投诉ID数组
+ * @param {number} status - 新状态
+ * @param {string} [closeReason] - 关闭原因(状态为已关闭时必填)
+ * @returns {Promise<ApiResponse<null>>} 操作结果
+ */
+export const batchUpdateStatus = (ids, status, closeReason) => {
+  return request({
+    url: '/api/blade-factory/api/factory/complaint/batch-status',
+    method: 'put',
+    data: {
+      ids,
+      status,
+      closeReason
+    }
+  })
+}

+ 20 - 0
src/router/views/index.js

@@ -169,6 +169,26 @@ export default [
         ]
     },
     {
+        path: '/complaint',
+        component: Layout,
+        redirect: '/complaint/index',
+        meta: {
+            title: '投诉管理',
+            icon: 'icon-complaint'
+        },
+        children: [
+            {
+                path: 'index',
+                name: 'ComplaintIndex',
+                component: () => import('@/views/complaint/index'),
+                meta: {
+                    title: '投诉管理',
+                    keepAlive: true
+                }
+            }
+        ]
+    },
+    {
         path: "/forecast",
         component: Layout,
         redirect: "/forecast/index",

+ 797 - 0
src/views/complaint/index.vue

@@ -0,0 +1,797 @@
+<template>
+  <basic-container>
+    <avue-crud
+      :option="option"
+      :table-loading="loading"
+      :data="data"
+      :page="page"
+      ref="crud"
+      v-model="form"
+      :permission="permissionList"
+      @row-update="rowUpdate"
+      @row-save="rowSave"
+      @row-del="rowDel"
+      @search-change="searchChange"
+      @search-reset="searchReset"
+      @selection-change="selectionChange"
+      @current-change="currentChange"
+      @size-change="sizeChange"
+      @refresh-change="refreshChange"
+      @on-load="onLoad"
+    >
+      <template slot="menuLeft">
+        <el-button
+          type="primary"
+          size="small"
+          icon="el-icon-plus"
+          v-if="permission.complaint_add"
+          @click="handleAdd"
+        >
+          新增投诉
+        </el-button>
+        <el-button
+          type="warning"
+          size="small"
+          icon="el-icon-edit"
+          :disabled="selectionList.length !== 1"
+          v-if="permission.complaint_edit"
+          @click="handleBatchStatus"
+        >
+          批量处理
+        </el-button>
+      </template>
+      
+      <template slot="status" slot-scope="{row}">
+        <el-tag
+          :type="getStatusType(row.status)"
+          size="small"
+        >
+          {{ getStatusText(row.status) }}
+        </el-tag>
+      </template>
+      
+      <template slot="replyStatus" slot-scope="{row}">
+        <el-tag
+          :type="row.replyStatus === 1 ? 'success' : 'info'"
+          size="small"
+        >
+          {{ row.replyStatus === 1 ? '已回复' : '未回复' }}
+        </el-tag>
+      </template>
+      
+      <template slot="complainantType" slot-scope="{row}">
+        <span>{{ getComplainantTypeText(row.complainantType) }}</span>
+      </template>
+      
+      <template slot="menu" slot-scope="{row}">
+        <el-button
+          type="text"
+          size="small"
+          icon="el-icon-view"
+          @click="handleDetail(row)"
+        >
+          详情
+        </el-button>
+        <el-button
+          type="text"
+          size="small"
+          icon="el-icon-edit"
+          v-if="permission.complaint_edit && row.status !== 4"
+          @click="handleEdit(row)"
+        >
+          编辑
+        </el-button>
+        <el-button
+          type="text"
+          size="small"
+          icon="el-icon-check"
+          v-if="permission.complaint_edit && row.status === 1"
+          @click="handleProcess(row)"
+        >
+          处理
+        </el-button>
+        <el-button
+          type="text"
+          size="small"
+          icon="el-icon-close"
+          v-if="permission.complaint_edit && row.status !== 4"
+          @click="handleClose(row)"
+        >
+          关闭
+        </el-button>
+      </template>
+    </avue-crud>
+    
+    <!-- 投诉详情对话框 -->
+    <el-dialog
+      title="投诉详情"
+      :visible.sync="detailVisible"
+      width="800px"
+      :close-on-click-modal="false"
+      :modal-append-to-body="false"
+      :append-to-body="true"
+      :destroy-on-close="true"
+      class="complaint-detail-dialog"
+    >
+      <div v-if="detailData" class="complaint-detail">
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <div class="detail-item">
+              <label>投诉单号:</label>
+              <span>{{ detailData.complaintNo }}</span>
+            </div>
+          </el-col>
+          <el-col :span="12">
+            <div class="detail-item">
+              <label>投诉标题:</label>
+              <span>{{ detailData.title }}</span>
+            </div>
+          </el-col>
+        </el-row>
+        
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <div class="detail-item">
+              <label>投诉人类型:</label>
+              <span>{{ getComplainantTypeText(detailData.complainantType) }}</span>
+            </div>
+          </el-col>
+          <el-col :span="12">
+            <div class="detail-item">
+              <label>投诉类型:</label>
+              <span>{{ detailData.complaintType }}</span>
+            </div>
+          </el-col>
+        </el-row>
+        
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <div class="detail-item">
+              <label>联系人:</label>
+              <span>{{ detailData.contactName }}</span>
+            </div>
+          </el-col>
+          <el-col :span="12">
+            <div class="detail-item">
+              <label>联系电话:</label>
+              <span>{{ detailData.contactPhone }}</span>
+            </div>
+          </el-col>
+        </el-row>
+        
+        <el-row :gutter="20" v-if="detailData.customerName">
+          <el-col :span="12">
+            <div class="detail-item">
+              <label>客户名称:</label>
+              <span>{{ detailData.customerName }}</span>
+            </div>
+          </el-col>
+          <el-col :span="12">
+            <div class="detail-item">
+              <label>客户编码:</label>
+              <span>{{ detailData.customerCode }}</span>
+            </div>
+          </el-col>
+        </el-row>
+        
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <div class="detail-item">
+              <label>投诉状态:</label>
+              <el-tag :type="getStatusType(detailData.status)" size="small">
+                {{ getStatusText(detailData.status) }}
+              </el-tag>
+            </div>
+          </el-col>
+          <el-col :span="12">
+            <div class="detail-item">
+              <label>回复状态:</label>
+              <el-tag :type="detailData.replyStatus === 1 ? 'success' : 'info'" size="small">
+                {{ detailData.replyStatus === 1 ? '已回复' : '未回复' }}
+              </el-tag>
+            </div>
+          </el-col>
+        </el-row>
+        
+        <el-row :gutter="20">
+          <el-col :span="24">
+            <div class="detail-item">
+              <label>投诉内容:</label>
+              <div class="content-text">{{ detailData.content }}</div>
+            </div>
+          </el-col>
+        </el-row>
+        
+        <el-row :gutter="20" v-if="detailData.closeReason">
+          <el-col :span="24">
+            <div class="detail-item">
+              <label>关闭原因:</label>
+              <div class="content-text">{{ detailData.closeReason }}</div>
+            </div>
+          </el-col>
+        </el-row>
+        
+        <el-row :gutter="20">
+          <el-col :span="12">
+            <div class="detail-item">
+              <label>提交时间:</label>
+              <span>{{ detailData.submitTime }}</span>
+            </div>
+          </el-col>
+          <el-col :span="12">
+            <div class="detail-item">
+              <label>更新时间:</label>
+              <span>{{ detailData.updateTime }}</span>
+            </div>
+          </el-col>
+        </el-row>
+      </div>
+      
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="detailVisible = false">关闭</el-button>
+      </div>
+    </el-dialog>
+    
+    <!-- 状态处理对话框 -->
+    <el-dialog
+      :title="statusDialogTitle"
+      :visible.sync="statusVisible"
+      width="500px"
+      :close-on-click-modal="false"
+    >
+      <el-form :model="statusForm" :rules="statusRules" ref="statusForm" label-width="100px">
+        <el-form-item label="新状态" prop="status">
+          <el-select v-model="statusForm.status" placeholder="请选择状态" style="width: 100%">
+            <el-option label="待处理" :value="1"></el-option>
+            <el-option label="处理中" :value="2"></el-option>
+            <el-option label="已完成" :value="3"></el-option>
+            <el-option label="已关闭" :value="4"></el-option>
+          </el-select>
+        </el-form-item>
+        <el-form-item label="关闭原因" prop="closeReason" v-if="statusForm.status === 4">
+          <el-input
+            type="textarea"
+            :rows="3"
+            v-model="statusForm.closeReason"
+            placeholder="请输入关闭原因"
+          ></el-input>
+        </el-form-item>
+      </el-form>
+      
+      <div slot="footer" class="dialog-footer">
+        <el-button @click="statusVisible = false">取消</el-button>
+        <el-button type="primary" @click="confirmStatusUpdate" :loading="statusLoading">确定</el-button>
+      </div>
+    </el-dialog>
+  </basic-container>
+</template>
+
+<script>
+import { getList, add, update, remove, getDetail, updateStatus, batchUpdateStatus } from '@/api/complaint'
+import { mapGetters } from 'vuex'
+
+/**
+ * 投诉查询参数类型定义
+ * @typedef {Object} ComplaintQueryParams
+ * @property {string} [complaintNo] - 投诉单号
+ * @property {string} [title] - 投诉标题
+ * @property {number} [complainantType] - 投诉人类型
+ * @property {string} [customerCode] - 客户编码
+ * @property {string} [customerName] - 客户名称
+ * @property {string} [contactName] - 联系人姓名
+ * @property {string} [contactPhone] - 联系人电话
+ * @property {string} [complaintType] - 投诉类型
+ * @property {number} [status] - 投诉状态
+ * @property {number} [replyStatus] - 回复状态
+ * @property {string} [submitTimeStart] - 提交时间开始
+ * @property {string} [submitTimeEnd] - 提交时间结束
+ */
+
+/**
+ * 状态更新表单类型定义
+ * @typedef {Object} StatusForm
+ * @property {number} status - 新状态
+ * @property {string} [closeReason] - 关闭原因
+ */
+
+export default {
+  data() {
+    return {
+      /** @type {ComplaintRecord[]} */
+      data: [],
+      /** @type {ComplaintQueryParams} */
+      query: {},
+      loading: true,
+      /** @type {ComplaintForm} */
+      form: {},
+      /** @type {ComplaintRecord[]} */
+      selectionList: [],
+      /** @type {Object} */
+      page: {
+        pageSize: 10,
+        currentPage: 1,
+        total: 0
+      },
+      /** @type {ComplaintRecord|null} */
+      detailData: null,
+      detailVisible: false,
+      statusVisible: false,
+      statusDialogTitle: '',
+      /** @type {StatusForm} */
+      statusForm: {
+        status: 1,
+        closeReason: ''
+      },
+      statusLoading: false,
+      /** @type {string[]} */
+      currentIds: [],
+      statusRules: {
+        status: [
+          { required: true, message: '请选择状态', trigger: 'change' }
+        ],
+        closeReason: [
+          { required: true, message: '请输入关闭原因', trigger: 'blur' }
+        ]
+      },
+      option: {
+        height: 'auto',
+        calcHeight: 30,
+        tip: false,
+        searchShow: true,
+        searchMenuSpan: 6,
+        border: true,
+        index: true,
+        viewBtn: true,
+        selection: true,
+        dialogClickModal: false,
+        column: [
+          {
+            label: '投诉单号',
+            prop: 'complaintNo',
+            minWidth: 150,
+            search: true
+          },
+          {
+            label: '投诉标题',
+            prop: 'title',
+            minWidth: 200,
+            search: true,
+            overHidden: true
+          },
+          {
+            label: '投诉人类型',
+            prop: 'complainantType',
+            minWidth: 100,
+            slot: true,
+            search: true,
+            type: 'select',
+            dicData: [
+              { label: '消费者', value: 1 },
+              { label: '经销商', value: 2 },
+              { label: '分销商', value: 3 }
+            ]
+          },
+          {
+            label: '客户名称',
+            prop: 'customerName',
+            minWidth: 150,
+            search: true,
+            overHidden: true
+          },
+          {
+            label: '联系人',
+            prop: 'contactName',
+            minWidth: 100,
+            search: true
+          },
+          {
+            label: '联系电话',
+            prop: 'contactPhone',
+            minWidth: 120,
+            search: true
+          },
+          {
+            label: '投诉类型',
+            prop: 'complaintType',
+            minWidth: 100,
+            search: true,
+            type: 'select',
+            dicData: [
+              { label: '质量', value: '质量' },
+              { label: '物流', value: '物流' },
+              { label: '服务', value: '服务' },
+              { label: '价格', value: '价格' },
+              { label: '其他', value: '其他' }
+            ]
+          },
+          {
+            label: '投诉状态',
+            prop: 'status',
+            minWidth: 100,
+            slot: true,
+            search: true,
+            type: 'select',
+            dicData: [
+              { label: '待处理', value: 1 },
+              { label: '处理中', value: 2 },
+              { label: '已完成', value: 3 },
+              { label: '已关闭', value: 4 }
+            ]
+          },
+          {
+            label: '回复状态',
+            prop: 'replyStatus',
+            minWidth: 100,
+            slot: true,
+            search: true,
+            type: 'select',
+            dicData: [
+              { label: '未回复', value: 0 },
+              { label: '已回复', value: 1 }
+            ]
+          },
+          {
+            label: '提交时间',
+            prop: 'submitTime',
+            minWidth: 150,
+            type: 'datetime',
+            format: 'YYYY-MM-DD HH:mm:ss',
+            valueFormat: 'YYYY-MM-DD HH:mm:ss'
+          },
+          {
+            label: '更新时间',
+            prop: 'updateTime',
+            minWidth: 150,
+            type: 'datetime',
+            format: 'YYYY-MM-DD HH:mm:ss',
+            valueFormat: 'YYYY-MM-DD HH:mm:ss'
+          }
+        ]
+      }
+    }
+  },
+  computed: {
+    ...mapGetters(['permission']),
+    permissionList() {
+      return {
+        addBtn: this.vaildData(this.permission.complaint_add, false),
+        viewBtn: this.vaildData(this.permission.complaint_view, false),
+        delBtn: this.vaildData(this.permission.complaint_delete, false),
+        editBtn: this.vaildData(this.permission.complaint_edit, false)
+      }
+    },
+    ids() {
+      /** @type {string[]} */
+      const ids = []
+      this.selectionList.forEach(ele => {
+        ids.push(ele.id)
+      })
+      return ids.join(',')
+    }
+  },
+  methods: {
+    /**
+     * 获取投诉状态文本
+     * @param {number} status - 状态值
+     * @returns {string} 状态文本
+     */
+    getStatusText(status) {
+      const statusMap = {
+        1: '待处理',
+        2: '处理中',
+        3: '已完成',
+        4: '已关闭'
+      }
+      return statusMap[status] || '未知'
+    },
+    
+    /**
+     * 获取投诉状态标签类型
+     * @param {number} status - 状态值
+     * @returns {string} 标签类型
+     */
+    getStatusType(status) {
+      const typeMap = {
+        1: 'warning',
+        2: 'primary',
+        3: 'success',
+        4: 'info'
+      }
+      return typeMap[status] || 'info'
+    },
+    
+    /**
+     * 获取投诉人类型文本
+     * @param {number} type - 类型值
+     * @returns {string} 类型文本
+     */
+    getComplainantTypeText(type) {
+      const typeMap = {
+        1: '消费者',
+        2: '经销商',
+        3: '分销商'
+      }
+      return typeMap[type] || '未知'
+    },
+    
+    /**
+     * 行保存
+     * @param {ComplaintForm} row - 表单数据
+     * @param {boolean} done - 完成回调
+     * @param {boolean} loading - 加载状态回调
+     */
+    async rowSave(row, done, loading) {
+      try {
+        loading()
+        const res = await add(row)
+        if (res.data.success) {
+          this.$message.success('操作成功')
+          done()
+          this.onLoad(this.page)
+        } else {
+          this.$message.error(res.data.msg || '操作失败')
+          loading()
+        }
+      } catch (error) {
+        console.error('新增投诉失败:', error)
+        this.$message.error('操作失败')
+        loading()
+      }
+    },
+    
+    /**
+     * 行更新
+     * @param {ComplaintForm} row - 表单数据
+     * @param {number} index - 行索引
+     * @param {boolean} done - 完成回调
+     * @param {boolean} loading - 加载状态回调
+     */
+    async rowUpdate(row, index, done, loading) {
+      try {
+        loading()
+        const res = await update(row)
+        if (res.data.success) {
+          this.$message.success('操作成功')
+          done()
+          this.onLoad(this.page)
+        } else {
+          this.$message.error(res.data.msg || '操作失败')
+          loading()
+        }
+      } catch (error) {
+        console.error('修改投诉失败:', error)
+        this.$message.error('操作失败')
+        loading()
+      }
+    },
+    
+    /**
+     * 行删除
+     * @param {ComplaintRecord} row - 行数据
+     * @param {number} index - 行索引
+     */
+    async rowDel(row, index) {
+      try {
+        const res = await remove(row.id)
+        if (res.data.success) {
+          this.$message.success('操作成功')
+          this.onLoad(this.page)
+        } else {
+          this.$message.error(res.data.msg || '操作失败')
+        }
+      } catch (error) {
+        console.error('删除投诉失败:', error)
+        this.$message.error('操作失败')
+      }
+    },
+    
+    /**
+     * 搜索变化
+     * @param {ComplaintQueryParams} params - 搜索参数
+     * @param {boolean} done - 完成回调
+     */
+    searchChange(params, done) {
+      this.query = params
+      this.onLoad(this.page, params)
+      done()
+    },
+    
+    /**
+     * 搜索重置
+     */
+    searchReset() {
+      this.query = {}
+      this.onLoad(this.page)
+    },
+    
+    /**
+     * 选择变化
+     * @param {ComplaintRecord[]} val - 选中的行
+     */
+    selectionChange(val) {
+      this.selectionList = val
+    },
+    
+    /**
+     * 当前页变化
+     * @param {number} currentPage - 当前页
+     */
+    currentChange(currentPage) {
+      this.page.currentPage = currentPage
+    },
+    
+    /**
+     * 页大小变化
+     * @param {number} pageSize - 页大小
+     */
+    sizeChange(pageSize) {
+      this.page.pageSize = pageSize
+    },
+    
+    /**
+     * 刷新变化
+     */
+    refreshChange() {
+      this.onLoad(this.page, this.query)
+    },
+    
+    /**
+     * 加载数据
+     * @param {Object} page - 分页信息
+     * @param {ComplaintQueryParams} [params={}] - 查询参数
+     */
+    async onLoad(page, params = {}) {
+      this.loading = true
+      try {
+        const res = await getList(page.currentPage, page.pageSize, Object.assign(params, this.query))
+        if (res.data.success) {
+          const data = res.data.data
+          this.data = data.records
+          this.page.total = data.total
+        } else {
+          this.$message.error(res.data.msg || '查询失败')
+        }
+      } catch (error) {
+        console.error('查询投诉列表失败:', error)
+        this.$message.error('查询失败')
+      } finally {
+        this.loading = false
+      }
+    },
+    
+    /**
+     * 新增投诉
+     */
+    handleAdd() {
+      this.$refs.crud.rowAdd()
+    },
+    
+    /**
+     * 编辑投诉
+     * @param {ComplaintRecord} row - 行数据
+     */
+    handleEdit(row) {
+      this.$refs.crud.rowEdit(row, this.data.indexOf(row))
+    },
+    
+    /**
+     * 查看详情
+     * @param {ComplaintRecord} row - 行数据
+     */
+    async handleDetail(row) {
+      try {
+        const res = await getDetail(row.id)
+        if (res.data.success) {
+          this.detailData = res.data.data
+          this.detailVisible = true
+        } else {
+          this.$message.error(res.data.msg || '获取详情失败')
+        }
+      } catch (error) {
+        console.error('获取投诉详情失败:', error)
+        this.$message.error('获取详情失败')
+      }
+    },
+    
+    /**
+     * 处理投诉
+     * @param {ComplaintRecord} row - 行数据
+     */
+    handleProcess(row) {
+      this.statusDialogTitle = '处理投诉'
+      this.statusForm = {
+        status: 2,
+        closeReason: ''
+      }
+      this.currentIds = [row.id]
+      this.statusVisible = true
+    },
+    
+    /**
+     * 关闭投诉
+     * @param {ComplaintRecord} row - 行数据
+     */
+    handleClose(row) {
+      this.statusDialogTitle = '关闭投诉'
+      this.statusForm = {
+        status: 4,
+        closeReason: ''
+      }
+      this.currentIds = [row.id]
+      this.statusVisible = true
+    },
+    
+    /**
+     * 批量状态处理
+     */
+    handleBatchStatus() {
+      if (this.selectionList.length === 0) {
+        this.$message.warning('请选择要处理的投诉')
+        return
+      }
+      this.statusDialogTitle = '批量处理投诉'
+      this.statusForm = {
+        status: 2,
+        closeReason: ''
+      }
+      this.currentIds = this.selectionList.map(item => item.id)
+      this.statusVisible = true
+    },
+    
+    /**
+     * 确认状态更新
+     */
+    async confirmStatusUpdate() {
+      try {
+        await this.$refs.statusForm.validate()
+        this.statusLoading = true
+        
+        let res
+        if (this.currentIds.length === 1) {
+          res = await updateStatus(this.currentIds[0], this.statusForm.status, this.statusForm.closeReason)
+        } else {
+          res = await batchUpdateStatus(this.currentIds, this.statusForm.status, this.statusForm.closeReason)
+        }
+        
+        if (res.data.success) {
+          this.$message.success('操作成功')
+          this.statusVisible = false
+          this.onLoad(this.page, this.query)
+        } else {
+          this.$message.error(res.data.msg || '操作失败')
+        }
+      } catch (error) {
+        if (error.message) {
+          console.error('状态更新失败:', error)
+          this.$message.error('操作失败')
+        }
+      } finally {
+        this.statusLoading = false
+      }
+    }
+  }
+}
+</script>
+
+<style lang="scss" scoped>
+.complaint-detail {
+  .detail-item {
+    margin-bottom: 15px;
+    
+    label {
+      font-weight: bold;
+      color: #606266;
+      margin-right: 10px;
+    }
+    
+    .content-text {
+      margin-top: 5px;
+      padding: 10px;
+      background-color: #f5f7fa;
+      border-radius: 4px;
+      line-height: 1.6;
+      white-space: pre-wrap;
+    }
+  }
+}
+</style>