Bladeren bron

举报审核调整

sgc 3 weken geleden
bovenliggende
commit
73cadd7f28

+ 130 - 0
src/views/appeal/detailDialog.vue

@@ -0,0 +1,130 @@
+<template>
+  <el-dialog v-model="dialogShow" title="举报详情" width="760px" @close="handleClose">
+    <el-descriptions :column="2" border class="detail-descriptions">
+      <el-descriptions-item label="律师姓名">
+        {{ detailData.reportedUserName || "--" }}
+      </el-descriptions-item>
+      <el-descriptions-item label="订单编号">
+        {{ detailData.orderNumber || "--" }}
+      </el-descriptions-item>
+      <el-descriptions-item label="举报类型">
+        {{ violationDisplay }}
+      </el-descriptions-item>
+      <el-descriptions-item label="举报人姓名">
+        {{ detailData.nickname || "--" }}
+      </el-descriptions-item>
+      <el-descriptions-item label="举报时间">
+        {{ detailData.createdTime || "--" }}
+      </el-descriptions-item>
+      <el-descriptions-item v-if="detailData.processingTime" label="处理时间">
+        {{ detailData.processingTime || "--" }}
+      </el-descriptions-item>
+      <el-descriptions-item label="当前状态">
+        <el-tag v-if="currentStatus" :type="currentStatus.tagType">
+          {{ currentStatus.label }}
+        </el-tag>
+        <span v-else> -- </span>
+      </el-descriptions-item>
+      <el-descriptions-item label="处理说明">
+        {{ detailData.reportResult || "--" }}
+      </el-descriptions-item>
+    </el-descriptions>
+
+    <div class="evidence-block" v-if="evidenceList.length">
+      <div class="block-title">举报凭证</div>
+      <div class="image-grid">
+        <el-image
+          v-for="(img, index) in evidenceList"
+          :key="`${detailData.id}-${index}`"
+          :src="img"
+          :preview-src-list="evidenceList"
+          fit="cover"
+        />
+      </div>
+    </div>
+
+    <template #footer>
+      <span class="dialog-footer">
+        <el-button type="primary" @click="handleClose">关闭</el-button>
+      </span>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup lang="ts">
+import { computed, ref } from "vue";
+
+const dialogShow = ref(false);
+const detailData = ref<Record<string, any>>({});
+
+const statusMeta = [
+  { value: 0, label: "未处理", tagType: "info" },
+  { value: 1, label: "违规", tagType: "danger" },
+  { value: 2, label: "未违规", tagType: "success" }
+] as const;
+
+const violationTypeMeta = [
+  { value: 1, label: "服务态度差" },
+  { value: 2, label: "专业能力差" },
+  { value: 3, label: "响应时间超过24小时" },
+  { value: 4, label: "其他原因" }
+] as const;
+
+const getStatusMeta = (status?: number) => statusMeta.find(item => item.value == status);
+const getViolationMeta = (type?: number | string) => {
+  const value = typeof type === "string" ? Number(type) : type;
+  return violationTypeMeta.find(item => item.value === value);
+};
+
+const parseEvidenceImages = (value: string | string[] | null | undefined) => {
+  if (!value) return [];
+  if (Array.isArray(value)) return value.filter(Boolean);
+  return value
+    .split(",")
+    .map(url => url.trim())
+    .filter(Boolean);
+};
+
+const evidenceList = computed(() => parseEvidenceImages(detailData.value.reportEvidenceImg));
+const currentStatus = computed(() => getStatusMeta(detailData.value.processingStatus));
+const violationDisplay = computed(() => {
+  if (detailData.value.violationReason == 4) return detailData.value.otherReasonContent || "其他原因";
+  return getViolationMeta(detailData.value.violationReason)?.label || "--";
+});
+
+const open = (data: any) => {
+  detailData.value = { ...data };
+  dialogShow.value = true;
+};
+
+const handleClose = () => {
+  dialogShow.value = false;
+};
+
+defineExpose({
+  open,
+  close: handleClose
+});
+</script>
+
+<style scoped lang="scss">
+.detail-descriptions {
+  margin-bottom: 16px;
+}
+.block-title {
+  margin-bottom: 8px;
+  font-weight: 600;
+  color: #303133;
+}
+.image-grid {
+  display: grid;
+  grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
+  gap: 8px;
+}
+.image-grid :deep(.el-image) {
+  width: 100%;
+  height: 100%;
+  overflow: hidden;
+  border-radius: 8px;
+}
+</style>

+ 86 - 149
src/views/appeal/index.vue

@@ -1,26 +1,22 @@
 <template>
   <div class="table-box">
     <ProTable ref="proTable" :columns="columns" :request-api="getTableList" :data-callback="dataCallback">
-      <template #endFundsButton="scope">
-        <el-tag v-if="scope.row.endFundsButton == 1" type="success"> 审核中 </el-tag>
-        <el-tag v-if="scope.row.endFundsButton == 2" type="success"> 已通过 </el-tag>
-        <el-tag v-if="scope.row.endFundsButton == 3" type="primary"> 已驳回 </el-tag>
-      </template>
-      <template #promoteType="scope">
-        <el-tag v-for="(item, index) in getPromoteTypes(scope.row.promoteType)" :key="index">
-          {{ item }}
+      <template #processingStatus="scope">
+        <el-tag v-if="getStatusMeta(scope.row.processingStatus)" :type="getStatusMeta(scope.row.processingStatus)?.tagType">
+          {{ getStatusMeta(scope.row.processingStatus)?.label }}
         </el-tag>
+        <span v-else> -- </span>
       </template>
 
       <template #operation="scope">
         <el-button type="primary" :icon="Search" link @click="handleDetail(scope.row)"> 查看详情 </el-button>
-        <el-button type="primary" :icon="Setting" v-if="scope.row.endFundsButton == 1" link @click="handleReview(scope.row)">
+        <el-button type="primary" :icon="Setting" v-if="scope.row.processingStatus == 0" link @click="handleReview(scope.row)">
           审核
         </el-button>
       </template>
     </ProTable>
 
-    <ReviewDialog ref="reviewDialog" @approve="handleApprove" @reject="handleReject" />
+    <ReviewDialog ref="reviewDialog" @submit="handleReviewSubmit" />
     <DetailDialog ref="detailDialog" />
   </div>
 </template>
@@ -29,18 +25,41 @@
 import ReviewDialog from "./reviewDialog.vue";
 import DetailDialog from "./detailDialog.vue";
 import { ref, reactive, onActivated } from "vue";
-import type { Course } from "@/api/interface";
 import { ElMessage } from "element-plus";
 import ProTable from "@/components/ProTable/index.vue";
 import type { ProTableInstance, ColumnProps } from "@/components/ProTable/interface";
 import { Search, Setting } from "@element-plus/icons-vue";
+import { getViolationPage, violationApprove } from "@/api/modules/lawyer";
 
 const proTable = ref<ProTableInstance>();
 
 const reviewDialog = ref<any>(null);
 const detailDialog = ref<any>(null);
+const currentRow = ref<any>();
+
+const statusMeta = [
+  { value: 0, label: "未处理", tagType: "info" },
+  { value: 1, label: "违规", tagType: "danger" },
+  { value: 2, label: "未违规", tagType: "success" }
+] as const;
+
+const violationTypeMeta = [
+  { value: 1, label: "服务态度差" },
+  { value: 2, label: "专业能力差" },
+  { value: 3, label: "响应时间超过24小时" },
+  { value: 4, label: "其他原因" }
+] as const;
+
+const getStatusMeta = (status?: number) => statusMeta.find(item => item.value == status);
+const getViolationMeta = (type?: number | string) => violationTypeMeta.find(item => item.value == type);
+
+const getViolationDisplay = (row: any) => {
+  if (!row) return "--";
+  if (row.violationReason == 4) return row.otherReasonContent || "其他原因";
+  return getViolationMeta(row.violationReason)?.label || "--";
+};
 
-const columns = reactive<ColumnProps<Course.ReqCourseParams>[]>([
+const columns = reactive<ColumnProps<any>[]>([
   {
     label: "序号",
     type: "index",
@@ -49,79 +68,64 @@ const columns = reactive<ColumnProps<Course.ReqCourseParams>[]>([
     fixed: "left" // 固定在左侧
   },
   {
-    label: "订单编号",
-    prop: "orderNo",
-    width: 240,
-    search: { el: "input", tooltip: "请输入订单编号" },
+    label: "律师姓名",
+    prop: "reportedUserName",
+    width: 200,
+    search: { el: "input", tooltip: "请输入律师姓名" },
     fixed: "left", // 固定在左侧
     showOverflowTooltip: true
   },
   {
-    label: "姓名",
-    prop: "realName",
-    width: 160,
-    search: { el: "input", tooltip: "请输入姓名" },
+    label: "订单编号",
+    prop: "orderNumber",
+    width: 220,
+    fixed: "left", // 固定在左侧
     showOverflowTooltip: true
   },
   {
-    label: "订单金额(元)",
-    prop: "orderMoney",
-    width: 200,
-    align: "right"
-  },
-  {
-    label: "状态",
-    prop: "endFundsButton",
-    width: 100,
+    label: "举报类型",
+    prop: "violationReason",
+    showOverflowTooltip: true,
     search: {
       el: "select",
-      tooltip: "请选择状态",
       props: { clearable: true }
     },
-    enum: [
-      { value: 1, label: "审核中" },
-      { value: 2, label: "已通过" },
-      { value: 3, label: "已驳回" }
-    ],
-    fieldNames: { label: "label", value: "value" }
+    enum: violationTypeMeta as any,
+    render: scope => getViolationDisplay(scope.row)
   },
   {
-    label: "下单时间",
-    prop: "orderTime"
+    label: "处理说明",
+    prop: "reportResult",
+    width: 200,
+    align: "center"
   },
   {
-    label: "申请时间",
-    prop: "endFundsTime"
+    label: "举报人姓名",
+    prop: "nickname",
+    width: 200,
+    align: "center"
   },
   {
-    label: "下单时间",
-    prop: "time1",
-    isShow: false, // 关键:不在表格中显示
-    search: {
-      el: "date-picker",
-      props: {
-        type: "datetimerange",
-        valueFormat: "YYYY-MM-DD HH:mm:ss",
-        rangeSeparator: "至",
-        startPlaceholder: "开始时间",
-        endPlaceholder: "结束时间"
-      }
-    }
+    label: "举报时间",
+    prop: "createdTime",
+    width: 200,
+    align: "center"
   },
   {
-    label: "申请时间",
-    prop: "time4",
-    isShow: false, // 关键:不在表格中显示
+    label: "当前状态",
+    prop: "processingStatus",
+    width: 100,
     search: {
-      el: "date-picker",
-      props: {
-        type: "datetimerange",
-        valueFormat: "YYYY-MM-DD HH:mm:ss",
-        rangeSeparator: "至",
-        startPlaceholder: "开始时间",
-        endPlaceholder: "结束时间"
-      }
-    }
+      el: "select",
+      tooltip: "请选择状态",
+      props: { clearable: true }
+    },
+    enum: [
+      { value: 0, label: "未处理" },
+      { value: 1, label: "违规" },
+      { value: 2, label: "未违规" }
+    ],
+    fieldNames: { label: "label", value: "value" }
   },
   {
     label: "操作",
@@ -132,35 +136,14 @@ const columns = reactive<ColumnProps<Course.ReqCourseParams>[]>([
 ]);
 
 const getTableList = async (params: any) => {
-  let tempParams = JSON.parse(JSON.stringify(params));
-  delete tempParams.time1;
-  delete tempParams.time2;
-  delete tempParams.time3;
-  delete tempParams.time4;
-  // 深拷贝原始参数
-  let newParams = JSON.parse(JSON.stringify(tempParams));
-  newParams.page = newParams.pageNum;
-  newParams.size = newParams.pageSize;
+  const newParams = {
+    ...params,
+    page: params.pageNum,
+    size: params.pageSize
+  };
   delete newParams.pageNum;
   delete newParams.pageSize;
-  if (params.time1) {
-    newParams.orderCreatedTime = params.time1[0];
-    newParams.orderEndTime = params.time1[1];
-  }
-  // todo  后端 现在下单时间和支付时间是一个字段,后续调整 在改动
-  if (params.time2) {
-    newParams.payCreatedTime = params.time2[0];
-    newParams.payEndTime = params.time2[1];
-  }
-  if (params.time3) {
-    newParams.firstCompleteTime = params.time3[0];
-    newParams.endCompleteTime = params.time3[1];
-  }
-  if (params.time4) {
-    newParams.createdEndPaymentTime = params.time4[0];
-    newParams.endEndPaymentTime = params.time4[1];
-  }
-  const res = await getFinalPaymentList(newParams);
+  const res = await getViolationPage(newParams);
   return res;
 };
 
@@ -171,12 +154,6 @@ const dataCallback = (data: any) => {
   };
 };
 
-// 处理推广板块字符串
-const getPromoteTypes = (promoteType: string) => {
-  if (!promoteType) return [];
-  return promoteType.split(",");
-};
-
 //详情
 const handleDetail = row => {
   detailDialog.value?.open(row);
@@ -184,64 +161,24 @@ const handleDetail = row => {
 
 // 审核
 const handleReview = (row: any) => {
+  currentRow.value = row;
   reviewDialog.value?.open(row);
 };
 
-// 处理同意操作 endFundsButton: 2
-const handleApprove = async (payload: {
-  id: number;
-  name: string;
-  orderMoney: number;
-  orderNo: string;
-  storeTel: string;
-  userPhone: string;
-  endPaymentRefusal: string;
-  endPayment: number | string;
-}) => {
+const handleReviewSubmit = async (payload: { processingStatus: number; reportResult: string }) => {
+  if (!currentRow.value) return;
   try {
-    const res: any = await getFinalPaymentReview({
-      ...payload,
-      endFundsButton: 2
+    await violationApprove({
+      id: currentRow.value.id,
+      processingStatus: payload.processingStatus,
+      reportResult: payload.reportResult
     });
-    if (res.data.status == 0) {
-      ElMessage.success(res.msg);
-    }
-    if (res.data.status == 1) {
-      ElMessage.error(res.data.codeMsg);
-    }
-    proTable.value?.getTableList();
-  } catch (error) {
-    console.error("审核通过失败:", error);
-    ElMessage.error("审核通过失败");
-  }
-};
-
-// 处理驳回操作 endFundsButton: 3
-const handleReject = async (payload: {
-  id: number;
-  name: string;
-  orderMoney: number;
-  orderNo: string;
-  storeTel: string;
-  userPhone: string;
-  endPaymentRefusal: string;
-  endPayment: number | string;
-}) => {
-  try {
-    const res: any = await getFinalPaymentReview({
-      ...payload,
-      endFundsButton: 3
-    });
-    if (res.data.status == 0) {
-      ElMessage.success(res.msg);
-    }
-    if (res.data.status == 1) {
-      ElMessage.error(res.data.codeMsg);
-    }
+    ElMessage.success("审核结果已提交");
+    reviewDialog.value?.close?.();
     proTable.value?.getTableList();
   } catch (error) {
-    console.error("审核驳回失败:", error);
-    ElMessage.error("审核驳回失败");
+    console.error("审核失败:", error);
+    ElMessage.error("审核失败,请稍后重试");
   }
 };
 

+ 192 - 0
src/views/appeal/reviewDialog.vue

@@ -0,0 +1,192 @@
+<template>
+  <el-dialog v-model="dialogShow" title="举报审核" width="780px" @close="handleClose">
+    <el-descriptions :column="2" border class="review-descriptions">
+      <el-descriptions-item label="律师姓名">
+        {{ detailData.reportedUserName || "--" }}
+      </el-descriptions-item>
+      <el-descriptions-item label="订单编号">
+        {{ detailData.orderNumber || "--" }}
+      </el-descriptions-item>
+      <el-descriptions-item label="举报人姓名">
+        {{ detailData.nickname || "--" }}
+      </el-descriptions-item>
+      <el-descriptions-item label="举报类型">
+        {{ violationDisplay }}
+      </el-descriptions-item>
+      <el-descriptions-item label="举报时间">
+        {{ detailData.createdTime || "--" }}
+      </el-descriptions-item>
+      <el-descriptions-item v-if="detailData.processingTime" label="处理时间">
+        {{ detailData.processingTime || "--" }}
+      </el-descriptions-item>
+      <el-descriptions-item label="当前状态">
+        <el-tag v-if="currentStatus" :type="currentStatus.tagType">
+          {{ currentStatus.label }}
+        </el-tag>
+        <span v-else> -- </span>
+      </el-descriptions-item>
+      <el-descriptions-item label="举报描述" :span="2">
+        {{ detailData.otherReasonContent || "暂无" }}
+      </el-descriptions-item>
+    </el-descriptions>
+
+    <div class="evidence-block" v-if="evidenceList.length">
+      <div class="block-title">举报凭证</div>
+      <div class="image-grid">
+        <el-image
+          v-for="(img, index) in evidenceList"
+          :key="`${detailData.id}-${index}`"
+          :src="img"
+          :preview-src-list="evidenceList"
+          fit="cover"
+        />
+      </div>
+    </div>
+
+    <el-form ref="formRef" :model="reviewForm" :rules="rules" label-width="90px" class="review-form">
+      <el-form-item label="处理结果" prop="processingStatus">
+        <el-radio-group v-model="reviewForm.processingStatus">
+          <el-radio :value="1"> 违规 </el-radio>
+          <el-radio :value="2"> 未违规 </el-radio>
+        </el-radio-group>
+      </el-form-item>
+      <el-form-item label="处理说明" prop="reportResult">
+        <el-input
+          v-model="reviewForm.reportResult"
+          type="textarea"
+          :rows="3"
+          maxlength="200"
+          show-word-limit
+          placeholder="请输入处理说明"
+        />
+      </el-form-item>
+    </el-form>
+
+    <template #footer>
+      <span class="dialog-footer">
+        <el-button @click="handleClose">取消</el-button>
+        <el-button type="primary" @click="handleSubmit">提交</el-button>
+      </span>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup lang="ts">
+import { computed, reactive, ref } from "vue";
+import type { FormInstance, FormRules } from "element-plus";
+
+const dialogShow = ref(false);
+const detailData = ref<Record<string, any>>({});
+const formRef = ref<FormInstance>();
+
+const statusMeta = [
+  { value: 0, label: "未处理", tagType: "info" },
+  { value: 1, label: "违规", tagType: "danger" },
+  { value: 2, label: "未违规", tagType: "success" }
+] as const;
+
+const violationTypeMeta = [
+  { value: 1, label: "服务态度差" },
+  { value: 2, label: "专业能力差" },
+  { value: 3, label: "响应时间超过24小时" },
+  { value: 4, label: "其他原因" }
+] as const;
+
+const getStatusMeta = (status?: number) => statusMeta.find(item => item.value == status);
+const getViolationMeta = (type?: number | string) => {
+  const value = typeof type === "string" ? Number(type) : type;
+  return violationTypeMeta.find(item => item.value === value);
+};
+
+const parseEvidenceImages = (value: string | string[] | null | undefined) => {
+  if (!value) return [];
+  if (Array.isArray(value)) return value.filter(Boolean);
+  return value
+    .split(",")
+    .map(url => url.trim())
+    .filter(Boolean);
+};
+
+const evidenceList = computed(() => parseEvidenceImages(detailData.value.reportEvidenceImg));
+const currentStatus = computed(() => getStatusMeta(detailData.value.processingStatus));
+const violationDisplay = computed(() => {
+  if (detailData.value.violationReason == 4) return detailData.value.otherReasonContent || "其他原因";
+  return getViolationMeta(detailData.value.violationReason)?.label || "--";
+});
+
+const reviewForm = reactive({
+  processingStatus: 1,
+  reportResult: ""
+});
+
+const rules: FormRules = {
+  processingStatus: [{ required: true, message: "请选择处理结果", trigger: "change" }],
+  reportResult: [{ required: true, message: "请输入处理说明", trigger: "blur" }]
+};
+
+const emit = defineEmits<{
+  submit: [{ processingStatus: number; reportResult: string }];
+  close: [];
+}>();
+
+const resetForm = () => {
+  reviewForm.processingStatus = 1;
+  reviewForm.reportResult = "";
+  formRef.value?.clearValidate();
+};
+
+const open = (data: any) => {
+  detailData.value = { ...data };
+  reviewForm.processingStatus = data?.processingStatus ?? 1;
+  reviewForm.reportResult = data?.reportResult || "";
+  dialogShow.value = true;
+};
+
+const handleSubmit = async () => {
+  if (!formRef.value) return;
+  await formRef.value.validate(async valid => {
+    if (!valid) return;
+    emit("submit", {
+      processingStatus: reviewForm.processingStatus,
+      reportResult: reviewForm.reportResult.trim()
+    });
+  });
+};
+
+const handleClose = () => {
+  dialogShow.value = false;
+  emit("close");
+  resetForm();
+};
+
+defineExpose({
+  open,
+  close: handleClose
+});
+</script>
+
+<style scoped lang="scss">
+.review-descriptions {
+  margin-bottom: 16px;
+}
+.block-title {
+  margin-bottom: 8px;
+  font-weight: 600;
+  color: #303133;
+}
+.image-grid {
+  display: grid;
+  grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
+  gap: 8px;
+  margin-bottom: 20px;
+}
+.image-grid :deep(.el-image) {
+  width: 100%;
+  height: 100%;
+  overflow: hidden;
+  border-radius: 8px;
+}
+.review-form {
+  padding-top: 8px;
+}
+</style>

+ 74 - 43
src/views/refund/detailDialog.vue

@@ -1,48 +1,60 @@
 <template>
   <el-dialog v-model="dialogShow" title="举报详情" width="760px" @close="handleClose">
+    <div v-if="detailData.imageList.length > 0" class="image-gallery">
+      <div class="block-title">举报凭证</div>
+      <div class="image-grid">
+        <div v-for="(url, index) in detailData.imageList" :key="index" class="image-item">
+          <el-image
+            :src="url"
+            :preview-src-list="detailData.imageList"
+            fit="cover"
+            class="gallery-image"
+            :hide-on-click-modal="false"
+            :preview-teleported="true"
+          />
+        </div>
+      </div>
+    </div>
+
     <el-descriptions :column="2" border class="detail-descriptions">
-      <el-descriptions-item label="律师姓名">
+      <el-descriptions-item label="举报理由">
         {{ detailData.reportedUserName || "--" }}
       </el-descriptions-item>
       <el-descriptions-item label="订单编号">
         {{ detailData.orderNumber || "--" }}
       </el-descriptions-item>
-      <el-descriptions-item label="举报类型">
-        {{ violationDisplay }}
-      </el-descriptions-item>
-      <el-descriptions-item label="举报人姓名">
+      <el-descriptions-item label="用户昵称">
         {{ detailData.nickname || "--" }}
       </el-descriptions-item>
-      <el-descriptions-item label="举报时间">
-        {{ detailData.createdTime || "--" }}
+      <el-descriptions-item label="用户电话">
+        {{ detailData.reportedUserName || "--" }}
       </el-descriptions-item>
-      <el-descriptions-item v-if="detailData.processingTime" label="处理时间">
-        {{ detailData.processingTime || "--" }}
+      <el-descriptions-item label="律师姓名">
+        {{ detailData.reportedUserName || "--" }}
+      </el-descriptions-item>
+      <el-descriptions-item label="律师电话">
+        {{ detailData.reportedUserName || "--" }}
       </el-descriptions-item>
-      <el-descriptions-item label="当前状态">
+      <el-descriptions-item label="举报类型">
+        {{ violationDisplay }}
+      </el-descriptions-item>
+      <el-descriptions-item label="状态">
         <el-tag v-if="currentStatus" :type="currentStatus.tagType">
           {{ currentStatus.label }}
         </el-tag>
         <span v-else> -- </span>
       </el-descriptions-item>
+      <el-descriptions-item label="举报时间">
+        {{ detailData.createdTime || "--" }}
+      </el-descriptions-item>
+      <el-descriptions-item v-if="detailData.processingTime" label="审核时间">
+        {{ detailData.processingTime || "--" }}
+      </el-descriptions-item>
       <el-descriptions-item label="处理说明">
         {{ detailData.reportResult || "--" }}
       </el-descriptions-item>
     </el-descriptions>
 
-    <div class="evidence-block" v-if="evidenceList.length">
-      <div class="block-title">举报凭证</div>
-      <div class="image-grid">
-        <el-image
-          v-for="(img, index) in evidenceList"
-          :key="`${detailData.id}-${index}`"
-          :src="img"
-          :preview-src-list="evidenceList"
-          fit="cover"
-        />
-      </div>
-    </div>
-
     <template #footer>
       <span class="dialog-footer">
         <el-button type="primary" @click="handleClose">关闭</el-button>
@@ -76,16 +88,6 @@ const getViolationMeta = (type?: number | string) => {
   return violationTypeMeta.find(item => item.value === value);
 };
 
-const parseEvidenceImages = (value: string | string[] | null | undefined) => {
-  if (!value) return [];
-  if (Array.isArray(value)) return value.filter(Boolean);
-  return value
-    .split(",")
-    .map(url => url.trim())
-    .filter(Boolean);
-};
-
-const evidenceList = computed(() => parseEvidenceImages(detailData.value.reportEvidenceImg));
 const currentStatus = computed(() => getStatusMeta(detailData.value.processingStatus));
 const violationDisplay = computed(() => {
   if (detailData.value.violationReason == 4) return detailData.value.otherReasonContent || "其他原因";
@@ -116,15 +118,44 @@ defineExpose({
   font-weight: 600;
   color: #303133;
 }
-.image-grid {
-  display: grid;
-  grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
-  gap: 8px;
-}
-.image-grid :deep(.el-image) {
-  width: 100%;
-  height: 100%;
-  overflow: hidden;
-  border-radius: 8px;
+.image-gallery {
+  padding: 10px;
+  margin-bottom: 20px;
+  background-color: #f5f7fa;
+  border-radius: 4px;
+  .image-grid {
+    display: flex;
+    flex-wrap: wrap;
+    gap: 10px;
+    .image-item {
+      width: 100px;
+      height: 100px;
+      cursor: pointer;
+      transition: all 0.3s;
+      &:hover {
+        box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%);
+        transform: scale(1.05);
+      }
+      .gallery-image {
+        width: 100%;
+        height: 100%;
+        overflow: hidden;
+        border-radius: 4px;
+        :deep(.el-image__inner) {
+          width: 100%;
+          height: 100%;
+          object-fit: cover;
+        }
+        :deep(.el-image__error) {
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          font-size: 12px;
+          color: #909399;
+          background-color: #f0f2f5;
+        }
+      }
+    }
+  }
 }
 </style>

+ 48 - 12
src/views/refund/index.vue

@@ -68,19 +68,39 @@ const columns = reactive<ColumnProps<any>[]>([
     fixed: "left" // 固定在左侧
   },
   {
+    label: "订单编号",
+    prop: "orderNumber",
+    width: 220,
+    fixed: "left", // 固定在左侧
+    search: { el: "input", tooltip: "请输入订单编号" },
+    showOverflowTooltip: true
+  },
+  {
     label: "律师姓名",
     prop: "reportedUserName",
-    width: 200,
+    width: 140,
     search: { el: "input", tooltip: "请输入律师姓名" },
     fixed: "left", // 固定在左侧
     showOverflowTooltip: true
   },
   {
-    label: "订单编号",
-    prop: "orderNumber",
-    width: 220,
-    fixed: "left", // 固定在左侧
-    showOverflowTooltip: true
+    label: "律师电话",
+    prop: "reportedPhone",
+    search: { el: "input", tooltip: "请输入律师电话" },
+    align: "center"
+  },
+  {
+    label: "用户姓名",
+    prop: "nickname",
+    search: { el: "input", tooltip: "请输入用户姓名" },
+    width: 140,
+    align: "center"
+  },
+  {
+    label: "用户电话",
+    prop: "phone",
+    search: { el: "input", tooltip: "请输入用户电话" },
+    align: "center"
   },
   {
     label: "举报类型",
@@ -99,12 +119,7 @@ const columns = reactive<ColumnProps<any>[]>([
     width: 200,
     align: "center"
   },
-  {
-    label: "举报人姓名",
-    prop: "nickname",
-    width: 200,
-    align: "center"
-  },
+
   {
     label: "举报时间",
     prop: "createdTime",
@@ -128,6 +143,21 @@ const columns = reactive<ColumnProps<any>[]>([
     fieldNames: { label: "label", value: "value" }
   },
   {
+    label: "举报时间",
+    prop: "time",
+    isShow: false, // 关键:不在表格中显示
+    search: {
+      el: "date-picker",
+      props: {
+        type: "datetimerange",
+        valueFormat: "YYYY-MM-DD",
+        rangeSeparator: "至",
+        startPlaceholder: "开始时间",
+        endPlaceholder: "结束时间"
+      }
+    }
+  },
+  {
     label: "操作",
     prop: "operation",
     width: 200,
@@ -141,6 +171,12 @@ const getTableList = async (params: any) => {
     page: params.pageNum,
     size: params.pageSize
   };
+
+  if (newParams.time) {
+    newParams.startTime = newParams.time[0] + " 00:00:00";
+    newParams.endTime = newParams.time[1] + " 23:59:59";
+  }
+  delete newParams.time;
   delete newParams.pageNum;
   delete newParams.pageSize;
   const res = await getViolationPage(newParams);

+ 108 - 86
src/views/refund/reviewDialog.vue

@@ -1,23 +1,42 @@
 <template>
   <el-dialog v-model="dialogShow" title="举报审核" width="780px" @close="handleClose">
+    <div v-if="detailData.imageList.length > 0" class="image-gallery">
+      <div class="block-title">举报凭证</div>
+      <div class="image-grid">
+        <div v-for="(url, index) in detailData.imageList" :key="index" class="image-item">
+          <el-image
+            :src="url"
+            :preview-src-list="detailData.imageList"
+            fit="cover"
+            class="gallery-image"
+            :hide-on-click-modal="false"
+            :preview-teleported="true"
+          />
+        </div>
+      </div>
+    </div>
+
     <el-descriptions :column="2" border class="review-descriptions">
-      <el-descriptions-item label="律师姓名">
-        {{ detailData.reportedUserName || "--" }}
-      </el-descriptions-item>
       <el-descriptions-item label="订单编号">
         {{ detailData.orderNumber || "--" }}
       </el-descriptions-item>
-      <el-descriptions-item label="举报人姓名">
+      <el-descriptions-item label="举报时间">
+        {{ detailData.createdTime || "--" }}
+      </el-descriptions-item>
+      <el-descriptions-item label="用户昵称">
         {{ detailData.nickname || "--" }}
       </el-descriptions-item>
-      <el-descriptions-item label="举报类型">
-        {{ violationDisplay }}
+      <el-descriptions-item label="用户电话">
+        {{ detailData.phone || "--" }}
       </el-descriptions-item>
-      <el-descriptions-item label="举报时间">
-        {{ detailData.createdTime || "--" }}
+      <el-descriptions-item label="律师姓名">
+        {{ detailData.reportedUserName || "--" }}
       </el-descriptions-item>
-      <el-descriptions-item v-if="detailData.processingTime" label="处理时间">
-        {{ detailData.processingTime || "--" }}
+      <el-descriptions-item label="律师电话">
+        {{ detailData.reportedPhone || "--" }}
+      </el-descriptions-item>
+      <el-descriptions-item label="举报类型">
+        {{ violationDisplay }}
       </el-descriptions-item>
       <el-descriptions-item label="当前状态">
         <el-tag v-if="currentStatus" :type="currentStatus.tagType">
@@ -25,59 +44,24 @@
         </el-tag>
         <span v-else> -- </span>
       </el-descriptions-item>
-      <el-descriptions-item label="举报描述" :span="2">
-        {{ detailData.otherReasonContent || "暂无" }}
-      </el-descriptions-item>
     </el-descriptions>
 
-    <div class="evidence-block" v-if="evidenceList.length">
-      <div class="block-title">举报凭证</div>
-      <div class="image-grid">
-        <el-image
-          v-for="(img, index) in evidenceList"
-          :key="`${detailData.id}-${index}`"
-          :src="img"
-          :preview-src-list="evidenceList"
-          fit="cover"
-        />
-      </div>
-    </div>
-
-    <el-form ref="formRef" :model="reviewForm" :rules="rules" label-width="90px" class="review-form">
-      <el-form-item label="处理结果" prop="processingStatus">
-        <el-radio-group v-model="reviewForm.processingStatus">
-          <el-radio :value="1"> 违规 </el-radio>
-          <el-radio :value="2"> 未违规 </el-radio>
-        </el-radio-group>
-      </el-form-item>
-      <el-form-item label="处理说明" prop="reportResult">
-        <el-input
-          v-model="reviewForm.reportResult"
-          type="textarea"
-          :rows="3"
-          maxlength="200"
-          show-word-limit
-          placeholder="请输入处理说明"
-        />
-      </el-form-item>
-    </el-form>
-
     <template #footer>
       <span class="dialog-footer">
         <el-button @click="handleClose">取消</el-button>
-        <el-button type="primary" @click="handleSubmit">提交</el-button>
+        <el-button type="danger" @click="handleReject"> 驳回 </el-button>
+        <el-button type="primary" @click="handleApprove"> 同意 </el-button>
       </span>
     </template>
   </el-dialog>
 </template>
 
 <script setup lang="ts">
-import { computed, reactive, ref } from "vue";
-import type { FormInstance, FormRules } from "element-plus";
+import { computed, ref } from "vue";
+import { ElMessageBox } from "element-plus";
 
 const dialogShow = ref(false);
 const detailData = ref<Record<string, any>>({});
-const formRef = ref<FormInstance>();
 
 const statusMeta = [
   { value: 0, label: "未处理", tagType: "info" },
@@ -114,49 +98,59 @@ const violationDisplay = computed(() => {
   return getViolationMeta(detailData.value.violationReason)?.label || "--";
 });
 
-const reviewForm = reactive({
-  processingStatus: 1,
-  reportResult: ""
-});
-
-const rules: FormRules = {
-  processingStatus: [{ required: true, message: "请选择处理结果", trigger: "change" }],
-  reportResult: [{ required: true, message: "请输入处理说明", trigger: "blur" }]
-};
-
 const emit = defineEmits<{
   submit: [{ processingStatus: number; reportResult: string }];
   close: [];
 }>();
 
-const resetForm = () => {
-  reviewForm.processingStatus = 1;
-  reviewForm.reportResult = "";
-  formRef.value?.clearValidate();
-};
-
 const open = (data: any) => {
   detailData.value = { ...data };
-  reviewForm.processingStatus = data?.processingStatus ?? 1;
-  reviewForm.reportResult = data?.reportResult || "";
   dialogShow.value = true;
 };
 
-const handleSubmit = async () => {
-  if (!formRef.value) return;
-  await formRef.value.validate(async valid => {
-    if (!valid) return;
-    emit("submit", {
-      processingStatus: reviewForm.processingStatus,
-      reportResult: reviewForm.reportResult.trim()
-    });
+const submitResult = (processingStatus: number, reportResult: string) => {
+  emit("submit", {
+    processingStatus,
+    reportResult
   });
 };
 
+const handleApprove = async () => {
+  try {
+    await ElMessageBox.confirm("确认同意本次举报处理结果吗?", "确认操作", {
+      confirmButtonText: "确认同意",
+      cancelButtonText: "取消",
+      type: "warning"
+    });
+    submitResult(1, "同意");
+  } catch (error) {
+    // 用户取消操作
+  }
+};
+
+const handleReject = async () => {
+  try {
+    const { value } = await ElMessageBox.prompt("", "驳回确认", {
+      confirmButtonText: "确定驳回",
+      cancelButtonText: "取消",
+      inputType: "textarea",
+      inputPlaceholder: "请输入驳回原因",
+      inputValidator: value => {
+        if (!value || !value.trim()) {
+          return "请输入驳回原因";
+        }
+        return true;
+      }
+    });
+    submitResult(2, value.trim());
+  } catch (error) {
+    // 用户取消输入
+  }
+};
+
 const handleClose = () => {
   dialogShow.value = false;
   emit("close");
-  resetForm();
 };
 
 defineExpose({
@@ -174,17 +168,45 @@ defineExpose({
   font-weight: 600;
   color: #303133;
 }
-.image-grid {
-  display: grid;
-  grid-template-columns: repeat(auto-fill, minmax(100px, 1fr));
-  gap: 8px;
+.image-gallery {
+  padding: 10px;
   margin-bottom: 20px;
-}
-.image-grid :deep(.el-image) {
-  width: 100%;
-  height: 100%;
-  overflow: hidden;
-  border-radius: 8px;
+  background-color: #f5f7fa;
+  border-radius: 4px;
+  .image-grid {
+    display: flex;
+    flex-wrap: wrap;
+    gap: 10px;
+    .image-item {
+      width: 100px;
+      height: 100px;
+      cursor: pointer;
+      transition: all 0.3s;
+      &:hover {
+        box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%);
+        transform: scale(1.05);
+      }
+      .gallery-image {
+        width: 100%;
+        height: 100%;
+        overflow: hidden;
+        border-radius: 4px;
+        :deep(.el-image__inner) {
+          width: 100%;
+          height: 100%;
+          object-fit: cover;
+        }
+        :deep(.el-image__error) {
+          display: flex;
+          align-items: center;
+          justify-content: center;
+          font-size: 12px;
+          color: #909399;
+          background-color: #f0f2f5;
+        }
+      }
+    }
+  }
 }
 .review-form {
   padding-top: 8px;