Compare commits

..

26 Commits

Author SHA1 Message Date
buaixuexideshitongxue 4612d3d936 fix:加上查重 11 hours ago
wxc ca4f6bb8fa fix: 删除多余 经办人字段 6 days ago
wxc 3f873e0e76 fix: 修复 6 days ago
buaixuexideshitongxue 0931fc881b fix:删除多余代码 7 days ago
buaixuexideshitongxue 79ce29d984 feat:标签字典渲染 7 days ago
buaixuexideshitongxue 4935b72202 feat:加上投诉举报特有信息 7 days ago
wxc da30aad3ac fix: 修复无法加载问题详情的问题 7 days ago
wxc d53ab80f73 feat: 调整局长信箱来源的问题,直接展示局长信箱系统页面 1 week ago
buaixuexideshitongxue 118cba3370 feat:所有信息到negative表 1 week ago
buaixuexideshitongxue e954f4a49e fix:初核时间状态显示 1 week ago
buaixuexideshitongxue ed0739295a Merge branch 'master' into feature/tsjb-1.0 1 week ago
buaixuexideshitongxue 1b01eda9a2 fix:投诉举报重复提交问题 2 weeks ago
wxc d9f9c4ec82 fix: 修复涉诉涉诉初核办理 3 weeks ago
buaixuexideshitongxue 38e5667552 Merge branch 'master' into feature/tsjb-1.0 3 weeks ago
buaixuexideshitongxue fe01246b9d fix--展示办结情况和群众认可 3 weeks ago
wxc 019d99dafd feat: 完善涉访涉诉办理流程 4 weeks ago
buaixuexideshitongxue 35c2e9338e fix--删除不必要的代码 4 weeks ago
wxc f68ab60d18 feat: 涉访涉诉初核办理 4 weeks ago
buaixuexideshitongxue ce74335f24 feat:投诉举报修改和删除接入综合查询 4 weeks ago
buaixuexideshitongxue 904111ed80 feat:修改投诉举报同时修改问题信息 4 weeks ago
buaixuexideshitongxue 295bbbb4fb feat:修改页面显示做限制 4 weeks ago
buaixuexideshitongxue 6628bed2b3 Merge branch 'master' into feature/tsjb-1.0 4 weeks ago
buaixuexideshitongxue d749d99a34 feat:更改查询接口、查看办理详情 1 month ago
buaixuexideshitongxue f34d88606e fix--修改描述 1 month ago
buaixuexideshitongxue 65b1569dbd feat: 添加初核描述 1 month ago
buaixuexideshitongxue 6bf060fe12 feat: 添加页面ui实现 1 month ago
  1. 4
      package.json
  2. 42
      src/api/data/complaintCollection.ts
  3. 106
      src/components/data/complaint_detail.vue
  4. 360
      src/components/data/complaintdes.vue
  5. 566
      src/components/data/complaintformdialog.vue
  6. 266
      src/components/data/complaintproblemInfo.vue
  7. 47
      src/components/negative/description.vue
  8. 141
      src/components/negative/dialog.vue
  9. 53
      src/components/negative/mailbox-dialog.vue
  10. 89
      src/components/negative/verify-description.vue
  11. 2425
      src/components/negative/verify-sfss.vue
  12. 8
      src/components/negative/verify.vue
  13. 390
      src/views/data/ComplaintCollection.vue
  14. 11
      src/views/data/ComplaintCollection_12389ts.vue
  15. 11
      src/views/data/ComplaintCollection_gabxf.vue
  16. 11
      src/views/data/ComplaintCollection_gjxf.vue
  17. 11
      src/views/data/ComplaintCollection_jzxx.vue
  18. 11
      src/views/data/ComplaintCollection_ldxj.vue
  19. 11
      src/views/data/DuplicateDrawerWithDetail.vue
  20. 111
      src/views/work/Query.vue

4
package.json

@ -33,9 +33,11 @@
"@vue-office/docx": "^1.6.0",
"@vue-office/excel": "^1.7.11",
"@vueup/vue-quill": "^1.2.0",
"@vueuse/core": "^10.11.0",
"amfe-flexible": "^2.2.1",
"baidu-map-vue3": "^0.4.9",
"crypto-js": "^4.2.0",
"dayjs": "^1.11.20",
"echarts": "^5.4.3",
"element-plus": "^2.8.8",
"flv.js": "^1.6.2",
@ -63,7 +65,7 @@
"amfe-flexible": "^2.2.1",
"mitt": "^3.0.1",
"postcss-pxtorem": "^6.1.0",
"sass": "^1.69.7",
"sass": "^1.99.0",
"unplugin-auto-import": "^0.17.3",
"unplugin-vue-components": "^0.26.0",
"vite": "^5.0.8",

42
src/api/data/complaintCollection.ts

@ -4,7 +4,7 @@ import {getToken} from "../../utils/token";
export function getComplaintCollectionPage(body) {
return request.post({
url: `/data/complaintCollection/getComplaintCollectionPage`,
url: `/data/complaintCollection/getComplaintCollectionPageNew`,
body
});
}
@ -32,20 +32,6 @@ export function updateComplaintCollection(body) {
});
}
export function addComplaintCollectionBlame(body) {
return request.post({
url: `/data/complaintCollection/addComplaintCollectionBlame`,
body
});
}
export function watchDetail(body) {
return request.post({
url: `/data/complaintCollection/watchDetail`,
body
});
}
/**
*
@ -57,16 +43,6 @@ export function maileRepeatt(body) {
});
}
/**
*
*/
export function handlerData(body) {
return request.post({
url: `/data/complaintCollection/handlerData`,
body
});
}
/**
*
@ -117,14 +93,24 @@ export function saveInvolveJson(body) {
});
}
/**
* --
*
*/
export function forceTermination(body) {
export function getComplaintCollectionDetail(body) {
return request.post({
url: `/data/complaintCollection/forceTermination`,
url: `/data/complaintCollection/getComplaintCollectionDetail`,
body
});
}
/**
*
*/
export function initialReview(body) {
return request.post({
url: `/data/complaintCollection/initialReview`,
body
});
}

106
src/components/data/complaint_detail.vue

@ -1,106 +0,0 @@
<template>
<el-dialog title="详情" v-model="visible" width="60vw" :lock-scroll="false" destroy-on-close>
<div style="min-height: 50vh" v-loading="loading">
<div class="row" style="margin: 0 60px">
<div class="col col-24">
<complaint-problem-info :data="base" :dict="dict" />
</div>
<div class="col col-24">
<complaintdes :data="base" :dict="dict" />
</div>
</div>
</div>
<template #footer>
<div class="dialog-footer">
<el-button size="large" @click="visible = false">关闭</el-button>
</div>
</template>
</el-dialog>
</template>
<script setup>
import {computed, ref, watch} from "vue";
import {ElMessage} from "element-plus";
import {watchDetail} from "@/api/data/complaintCollection.ts";
import ComplaintProblemInfo from "@/components/data/complaintproblemInfo.vue";
import Complaintdes from "@/components/data/complaintdes.vue";
const props = defineProps({
modelValue: { type: Boolean, default: false },
id: { type: [String, Number], default: "" },
dict: { type: Object, default: () => ({}) },
});
const emit = defineEmits(["update:modelValue"]);
const visible = computed({
get: () => props.modelValue,
set: (v) => emit("update:modelValue", v),
});
const loading = ref(false);
const loaded = ref(false);
const detail = ref({});
/** ✅ 假设接口统一返回:detail = { ...baseFields, negative: { ...核查/涉及信息 } } */
const base = computed(() => detail.value || {});
async function fetchDetail() {
if (!props.id) return;
loading.value = true;
loaded.value = false;
try {
const body={
id:props.id
}
const res = await watchDetail(body);
detail.value = res?.data ?? res ?? {};
loaded.value = true;
} catch (e) {
detail.value = {};
loaded.value = true;
ElMessage.error(e?.message || "详情获取失败");
} finally {
loading.value = false;
}
}
/** 打开弹窗或 id 变化时自动请求 */
watch(
() => [visible.value, props.id],
([v]) => {
if (v) fetchDetail();
}
);
</script>
<style scoped>
/* ✅ 让弹窗内容纵向排列,每个模块一整行 */
.row {
display: flex;
flex-direction: column; /* 关键:不要横向排 */
gap: 14px; /* 替代 row-gap,更直观 */
margin: 0;
}
/* ✅ 强制每个 col-24 占满 100% 宽度(在 flex 下生效) */
.col.col-24,
.col-24 {
width: 100%;
flex: 0 0 100%;
max-width: 100%;
min-width: 0;
}
/* ✅ 保险:如果你的子组件根节点是 el-collapse,确保它也占满 */
:deep(.el-collapse) {
width: 100%;
}
:deep(.el-collapse-item__header) {
width: 100%;
}
</style>

360
src/components/data/complaintdes.vue

@ -1,360 +0,0 @@
<template>
<el-collapse v-model="activeNames" class="detail-collapse">
<!-- ================== 核查办理 ================== -->
<el-collapse-item name="verify">
<template #title>
<span class="collapse-title">核查情况</span>
</template>
<div>
<div class="row">
<div class="col col-12">
<label>涉及案件/警情编号</label>
<span>{{ data.caseNumber || "/" }}</span>
</div>
<div class="col col-12">
<label>涉及举报对象</label>
<span>{{ getDictLabel(dict?.accountabilityTarget, data.accountabilityTarget) }}</span>
</div>
<div class="col col-12">
<label>核查结论</label>
<span>{{ data.checkStatusName || "/" }}</span>
</div>
<div class="col col-12">
<label>涉及单位</label>
<span>{{ data.involveDepartName || "/" }}</span>
</div>
<div class="col col-12">
<label>办结情况</label>
<span>
{{
data.completionStatus === "1"
? "程序办结"
: data.completionStatus === "2"
? "已解决合理诉求"
: "/"
}}
</span>
</div>
<div class="col col-12">
<label>群众认可</label>
<span>
{{
data.publicRecognition === "1"
? "认可"
: data.publicRecognition === "2"
? "不认可"
: data.publicRecognition === "3"
? "不接电话"
: "/"
}}
</span>
</div>
</div>
<div class="col col-24">
<label>问题核查情况</label>
<span>{{ data.checkStatusDesc || "/" }}</span>
</div>
</div>
</el-collapse-item>
<!-- ================== 涉及单位 ================== -->
<template v-if="personalBlames.length">
<el-collapse-item
v-for="(item, index) in personalBlames.filter(
(item) => item.type === BlameType.DEPARTMENT
)"
:key="item.id || index"
title="涉及单位"
:name="`dept${index}`"
>
<div class="row">
<div class="col col-6">
<label>班子成员姓名</label>
<span>{{ item.blameName || "/" }}</span>
</div>
<div class="col col-6">
<label>身份证号码</label>
<span>{{ item.blameIdCode || "/" }}</span>
</div>
<div class="col col-6">
<label>人员属性</label>
<span>{{ item.ivPersonTypeName || item.ivPersonType || "/" }}</span>
</div>
</div>
<div
class="row"
v-for="(problem, index) in item.problems"
:key="index"
>
<div class="col col-24">
<label>问题类型{{ index + 1 }}</label>
<span
>{{ problem.oneLevelContent || "/" }} /
{{ problem.twoLevelContent || "/" }} /
{{ problem.threeLevelContent || "/" }}
{{ problem.threeLevelContent === '其他' && problem.threeLevelContentOther ? `(${problem.threeLevelContentOther})` : ''}}
</span
>
</div>
</div>
<div class="row">
<div class="col col-6" v-if="item.subjectiveAspectName">
<label>主观方面</label>
<span>{{ item.subjectiveAspectName }}</span>
</div>
<div class="col col-6">
<label>责任类别</label>
<span>{{ item.responsibilityTypeName || "/" }}</span>
</div>
<div class="col col-6" v-if="item.handleResultName">
<label>处置结果</label>
<span>
<span>{{ item.handleResultName }}</span>
<span v-if="item.handleResultNameOther">({{ item.handleResultNameOther }})</span>
</span>
</div>
</div>
</el-collapse-item>
</template>
<el-collapse-item v-else name="dept">
<template #title>
<span class="collapse-title">涉及单位</span>
</template>
<div class="col col-24">
<span>/</span>
</div>
</el-collapse-item>
<!-- ================== 涉及人员 ================== -->
<template v-if="personalBlames.length">
<el-collapse-item
v-for="(item, index) in personalBlames.filter(
(item) => item.type === BlameType.PERSONAL
)"
:key="item.id || index"
:title="`涉及人员${index + 1}`"
:name="`involved${index}`"
>
<div class="row">
<div class="col col-6">
<label>涉及人员姓名</label>
<span>
<span>{{ item.blameName || "/" }}</span>
<span class="text-primary ml-10">{{ item.blameEmpNo || "/" }}</span>
</span>
</div>
<div class="col col-6">
<label>身份证</label>
<span>{{ item.blameIdCode || "/" }}</span>
</div>
<div class="col col-6">
<label>人员属性</label>
<span>{{ item.ivPersonTypeName || item.ivPersonType || "/" }}</span>
</div>
</div>
<div
class="row"
v-for="(problem, index) in item.problems"
:key="index"
>
<div class="col col-24">
<label>问题类型{{ index + 1 }}</label>
<span
>{{ problem.oneLevelContent || "/" }} /
{{ problem.twoLevelContent || "/" }} /
{{ problem.threeLevelContent || "/" }}
{{ problem.threeLevelContent === '其他' && problem.threeLevelContentOther ? `(${problem.threeLevelContentOther})` : ''}}
</span
>
</div>
</div>
<div class="row">
<div class="col col-6" v-if="item.responsibilityTypeName">
<label>责任类别</label>
<span>{{ item.responsibilityTypeName }}</span>
</div>
<div class="col col-6" v-if="item.subjectiveAspectName">
<label>主观方面</label>
<span>{{ item.subjectiveAspectName }}</span>
</div>
<div class="col col-6" v-if="item.handleResultName">
<label>处置结果</label>
<span>
<span>{{ item.handleResultName }}</span>
<span v-if="item.handleResultNameOther">({{ item.handleResultNameOther }})</span>
</span>
</div>
<div class="col col-6" v-if="item.protectRightsName">
<label>维权容错</label>
<span>{{ item.protectRightsName }}</span>
</div>
<div class="col col-6" v-if="item.superviseMeasuresName">
<label>督察措施</label>
<span>{{ item.superviseMeasuresName }}</span>
</div>
</div>
<div class="row">
<div class="col col-12" v-if="item.ConfinementData">
<label>禁闭时间</label>
<span >{{item.ConfinementData.startTime+ " - " + item.ConfinementData.endTime}}</span>
</div>
<div class="col col-4" v-if="item.ConfinementData">
<label>禁闭时长</label>
<span >{{item.ConfinementData.confinementTime}}</span>
</div>
<div class="col col-24" v-if="item.ConfinementData">
<label>禁闭措施</label>
<span>{{item.ConfinementData.matter}}</span>
</div>
</div>
<div class="row" style="background: #f5f5f5">
<div class="col col-6">
<label>涉及领导姓名</label>
<span v-if="item.leadName">{{ item.leadName }} {{ item.leadEmpNo }}</span>
<span v-else>/</span>
</div>
<div class="col col-6">
<label>身份证</label>
<span>{{ item.leadIdCode || "/" }}</span>
</div>
<div class="col col-6" v-if="item.leadResponsibilityTypeName">
<label>责任类别</label>
<span>{{ item.leadResponsibilityTypeName }}</span>
</div>
<div class="col col-6" v-if="item.leadMeasuresName">
<label>督察措施</label>
<span>{{ item.leadMeasuresName }}</span>
</div>
<div class="col col-6" v-if="item.leadHandleResultName">
<label>处置结果</label>
<span>
<span>{{ item.leadHandleResultName }}</span>
<span v-if="item.leadHandleResultNameOther">({{ item.leadHandleResultNameOther }})</span>
</span>
</div>
</div>
<div class="row" style="background: #f5f5f5" v-if="item.leadConfinement">
<div class="col col-12"v-if="item.leadConfinement" >
<label>禁闭时间</label>
<span >{{item.leadConfinement.startTime+ " - " + item.leadConfinement.endTime}}</span>
</div>
<div class="col col-4" v-if="item.leadConfinement">
<label>禁闭时长</label>
<span >{{item.leadConfinement.confinementTime}}</span>
</div>
<div class="col col-24" v-if="item.leadConfinement">
<label>禁闭措施</label>
<span>{{item.leadConfinement.matter}}</span>
</div>
</div>
<div class="row" style="background: #f5f5f5">
<div class="col col-6" v-if="item.leadProtectRightsName">
<label>维权容错</label>
<span>{{ item.leadProtectRightsName }}</span>
</div>
</div>
</el-collapse-item>
</template>
<!-- ================== 办结佐证材料 ================== -->
<el-collapse-item name="attachments" v-if="fileCount > 0">
<template #title>
<div class="title-with-count">
<span class="collapse-title">办结佐证材料</span>
<span class="count">{{ fileCount }}个附件</span>
</div>
</template>
<div>
<file-list :files="data.files" />
</div>
</el-collapse-item>
</el-collapse>
</template>
<script setup>
import { computed, ref, watchEffect } from "vue";
import {BlameType} from "@/enums/dictEnums.ts";
const props = defineProps({
data: {
type: Object,
required: true,
},
dict: {
type: Object,
default: () => ({}),
},
});
function getDictLabel(list, value) {
if (!Array.isArray(list) || value === null || value === undefined || value === "") return "/";
const item = list.find((d) => d.dictValue == value);
return item?.dictLabel || "/";
}
const personalBlames = computed(() => props.data?.personalBlames || []);
const deptBlames = computed(() => personalBlames.value.filter((item) => item.type === BlameType.DEPARTMENT));
const fileCount = computed(() => props.data?.files?.length ?? 0);
/**
* 默认展开哪些面板
* - attachments 只有在存在附件时才显示/展开
*/
const activeNames = ref(["verify"]);
watchEffect(() => {
// activeNames
let newActiveNames = ["verify"];
//
for (let i = 0; i < personalBlames.value.length; i++) {
newActiveNames.push("involved" + i);
}
//
if (deptBlames.value.length > 0) {
newActiveNames.push("dept0");
}
//
if (fileCount.value > 0) {
newActiveNames.push("attachments");
}
activeNames.value = newActiveNames;
});
</script>
<style scoped>
/* 让折叠头部更接近你截图:蓝色标题 + 右侧箭头(箭头 element-plus 自带) */
.detail-collapse {
--el-collapse-header-text-color: var(--primary-color);
--el-collapse-header-font-size: 16px;
}
.collapse-title {
font-weight: 600;
}
.title-with-count {
display: flex;
align-items: center;
gap: 8px;
}
.count {
font-size: 12px;
color: #666;
}
</style>

566
src/components/data/complaintformdialog.vue

@ -2,25 +2,28 @@
<el-dialog
:title="mode === 'add' ? '添加登记来件' : '修改登记来件'"
v-model="visibleProxy"
width="80vw"
top="5vh"
width="70vw"
top="2vh"
style="margin-bottom: 0"
>
<el-scrollbar max-height="76vh">
<el-form
ref="formRef"
:model="model"
:model="formData"
:label-width="150"
class="form-layout"
style="min-height: 66vh"
:rules="rules"
>
<!-- 来源 -->
<el-row>
<!-- 投诉信息 -->
<h2>投诉信息</h2>
<div class="add-negation-container">
<!-- 来源 -->
<el-row>
<el-col :span="12">
<el-form-item label="来源" prop="sourcePath">
<el-cascader
v-model="model.sourcePath"
v-model="formData.sourcePath"
:options="dict.sourceTableAndLevel"
:props="{ emitPath: true, checkStrictly: false }"
clearable
@ -35,7 +38,7 @@
<el-col :span="12">
<el-form-item label="业务类别" prop="handleMethod">
<el-select
v-model="model.businessTypeCode"
v-model="formData.businessTypeCode"
placeholder="业务类别"
clearable
>
@ -56,14 +59,14 @@
<el-col :span="12">
<el-form-item label="编号" prop="originId">
<el-input
v-model="model.originId"
:placeholder="model.originIdSkip ? '无' : '请输入编号'"
:disabled="mode === 'edit' || model.originIdSkip"
v-model="formData.originId"
:placeholder="formData.originIdSkip ? '无' : '请输入编号'"
:disabled="mode === 'edit' || formData.originIdSkip"
>
<template #append>
<el-checkbox
v-model="model.originIdSkip"
@change="(v) => v && (model.originId = '')"
v-model="formData.originIdSkip"
@change="(v) => v && (formData.originId = '')"
:disabled="mode=== 'edit'"
>
@ -76,7 +79,7 @@
<el-col :span="12">
<el-form-item label="登记/受理时间" prop="discoveryTime">
<el-date-picker
v-model="model.discoveryTime"
v-model="formData.discoveryTime"
type="datetime"
placeholder="请选择"
value-format="YYYY-MM-DDTHH:mm:ss"
@ -92,15 +95,15 @@
<el-col :span="12">
<el-form-item label="来件人姓名" prop="responderName">
<el-input
v-model="model.responderName"
:placeholder="model.responderNameSkip ? '无' : '请输入来件人姓名'"
:disabled="model.responderNameSkip"
v-model="formData.responderName"
:placeholder="formData.responderNameSkip ? '无' : '请输入来件人姓名'"
:disabled="formData.responderNameSkip"
@blur="onAutoCheckDuplicate"
>
<template #append>
<el-checkbox
v-model="model.responderNameSkip"
@change="(v) => v && (model.responderName = '')"
v-model="formData.responderNameSkip"
@change="(v) => v && (formData.responderName = '')"
>
</el-checkbox>
@ -113,15 +116,15 @@
<div style="display: flex; align-items: flex-start; gap: 8px;">
<el-form-item label="身份证号码" prop="responderIdCode" style="flex: 1; margin-bottom: 0;">
<el-input
v-model="model.responderIdCode"
:placeholder="model.responderIdCodeSkip ? '无' : '请输入身份证号码'"
:disabled="model.responderIdCodeSkip"
v-model="formData.responderIdCode"
:placeholder="formData.responderIdCodeSkip ? '无' : '请输入身份证号码'"
:disabled="formData.responderIdCodeSkip"
@blur="onAutoCheckDuplicate"
>
<template #append>
<el-checkbox
v-model="model.responderIdCodeSkip"
@change="(v) => v && (model.responderIdCode = '')"
v-model="formData.responderIdCodeSkip"
@change="(v) => v && (formData.responderIdCode = '')"
>
</el-checkbox>
@ -156,15 +159,15 @@
<el-col :span="12">
<el-form-item label="联系电话" prop="responderPhone">
<el-input
v-model="model.responderPhone"
:placeholder="model.responderPhoneSkip ? '无' : '请输入联系电话'"
:disabled="model.responderPhoneSkip"
v-model="formData.responderPhone"
:placeholder="formData.responderPhoneSkip ? '无' : '请输入联系电话'"
:disabled="formData.responderPhoneSkip"
@blur="onAutoCheckDuplicate"
>
<template #append>
<el-checkbox
v-model="model.responderPhoneSkip"
@change="(v) => v && (model.responderPhone = '')"
v-model="formData.responderPhoneSkip"
@change="(v) => v && (formData.responderPhone = '')"
>
</el-checkbox>
@ -176,9 +179,9 @@
<el-col :span="12">
<el-form-item label="被投诉二级机构" prop="secondDepartId" >
<depart-tree-select
v-model="model.secondDepartId"
v-model="formData.secondDepartId"
:check-strictly="true"
@node-click="(row) => (model.secondDepartName = row.shortName)"
@node-click="(row) => (formData.secondDepartName = row.shortName)"
/>
</el-form-item>
</el-col>
@ -188,7 +191,7 @@
<el-row>
<el-col :span="24">
<el-form-item label="来件内容" prop="thingDesc">
<el-input v-model="model.thingDesc" type="textarea" :autosize="{ minRows: 4 }" />
<el-input v-model="formData.thingDesc" type="textarea" :autosize="{ minRows: 4 }" />
</el-form-item>
</el-col>
</el-row>
@ -197,7 +200,7 @@
<el-row>
<el-col :span="12">
<el-form-item label="涉嫌问题" prop="involveProblemIdList">
<el-select v-model="model.involveProblemIdList" multiple clearable style="width: 100%">
<el-select v-model="formData.involveProblemIdList" multiple clearable style="width: 100%">
<el-option
v-for="item in dict.suspectProblem"
:key="item.dictValue"
@ -205,7 +208,7 @@
:label="item.dictLabel"
>
<!-- 复选框展示 -->
<el-checkbox :model-value="model.involveProblemIdList.includes(item.dictValue)">
<el-checkbox :model-value="(formData.involveProblemIdList || []).includes(item.dictValue)">
{{ item.dictLabel }}
</el-checkbox>
</el-option>
@ -215,7 +218,7 @@
<el-col :span="12">
<el-form-item label="是否重复件" prop="repeatt">
<el-radio-group v-model="model.repeatt" clearable>
<el-radio-group v-model="formData.repeatt" clearable>
<el-radio v-for="item in dict.yesNo" :key="item.id" :label="item.dictValue">
{{ item.dictLabel }}
</el-radio>
@ -228,7 +231,7 @@
<el-row>
<el-col :span="18">
<el-form-item label="标签" prop="tags">
<el-checkbox-group v-model="model.tags">
<el-checkbox-group v-model="formData.tags">
<el-checkbox v-for="item in dict.sfssTags" :key="item.id" :label="item.dictValue">
{{ item.dictLabel }}
</el-checkbox>
@ -241,7 +244,7 @@
<el-row>
<el-col :span="12">
<el-form-item label="办理方式" prop="handleMethod">
<el-radio-group v-model="model.handleMethod" clearable>
<el-radio-group v-model="formData.handleMethod" clearable>
<el-radio v-for="item in dict.handleMethodType" :key="item.id" :label="item.dictValue">
{{ item.dictLabel }}
</el-radio>
@ -250,19 +253,82 @@
</el-col>
</el-row>
<el-form-item label="附件" prop="thingFiles">
<el-form-item v-if="mode === 'add'" label="附件" prop="thingFiles">
<file-upload
v-model:files="model.thingFiles"
v-model:files="formData.thingFiles"
tips="为便于“办理单位”更全面了解问题详情,请上传相关附件,如现场督察、数字督察等相关照片、视频及其他佐证材料。"
/>
</el-form-item>
</div>
<!-- 办理单位 -->
<el-divider v-if="mode === 'add'" />
<h2 v-if="mode === 'add'">办理单位</h2>
<div v-if="mode === 'add'" class="add-negation-container">
<el-form-item label="主办层级" prop="hostLevel" :rules="{ required: true, message: '请选择主办层级', trigger: ['blur'] }">
<el-select style="width: 280px" v-model="formData.hostLevel" @change="handleChangeHostLevel">
<el-option v-for="item in dict.hostLevel" :key="item.id" :label="item.dictLabel" :value="item.dictValue" />
</el-select>
<div class="tips mt-10">
<p>如主办层级 市局主办 则由<span :data-active="formData.hostLevel === HostLevel.FIRST">督察支队</span>办理</p>
<p>如主办层级为 二级机构主办则由<span :data-active="formData.hostLevel === HostLevel.SECOND">督察部门</span>办理可进一步下发</p>
<p>如主办层级为 三级机构主办则由<span :data-active="formData.hostLevel === HostLevel.THREE">所队</span>办理</p>
</div>
</el-form-item>
<el-form-item label="指定办理单位" prop="departId" :rules="{ required: true, message: '请选择办理单位', trigger: ['blur'] }">
<div class="flex gap">
<div style="width: 280px">
<template v-if="formData.hostLevel === HostLevel.THREE">
<depart-tree-select v-model="formData.departId" v-loading="departLoading" />
</template>
<el-tree-select v-else :data="departs" :props="{ label: 'shortName', value: 'id' }" node-key="id" clearable filterable v-model="formData.departId" @node-click="handleSelectDepart" check-strictly style="width: 280px" v-loading="departLoading" />
</div>
<el-button type="primary" @click="handleLinkDepart" text v-if="formData.hostLevel !== HostLevel.FIRST">关联问题涉及单位</el-button>
</div>
<div class="tips mt-10">
<p>问题涉及单位 指与该问题相关的单位</p>
<p>指定办理单位 指将问题分派给哪个单位办理</p>
</div>
</el-form-item>
</div>
<!-- 初核时限 -->
<h2 v-if="mode === 'add'">初核时限</h2>
<p v-if="mode === 'add'" style="color: #909399; font-size: 14px; line-height: 1.6; padding-left: 200px;">
该信件的初核时限需在<span style="color: var(--el-color-danger); font-weight: 600;"> 4个工作日 </span>之内完成初核
</p>
<!-- 办理时限 -->
<h2 v-if="mode === 'add'">办理时限</h2>
<div v-if="mode === 'add'" class="add-negation-container">
<el-form-item label="办理时限" prop="timeLimit" :rules="{ required: true, message: '请选择办理时限', trigger: ['blur'] }">
<time-limit-select v-model="formData.timeLimit" v-model:maxSignDuration="formData.maxSignDuration" v-model:maxHandleDuration="formData.maxHandleDuration" v-model:maxExtensionDuration="formData.maxExtensionDuration" />
</el-form-item>
</div>
<!-- 审批流程 -->
<h2 v-if="mode === 'add'">审批流程</h2>
<div v-if="mode === 'add'" class="add-negation-container">
<el-form-item label="审批流程" prop="approvalFlow" :rules="{ required: true, message: '请选择审批流程', trigger: ['blur'] }">
<el-radio-group v-model="formData.approvalFlow" v-if="userStore.user.roleCodes.includes('admin_1')">
<el-radio v-for="item in dict.approvalFlow" :key="item.dictCode" :value="item.dictValue">{{ item.dictLabel }}{{ item.remark ? `(${item.remark})` : "" }}</el-radio>
</el-radio-group>
<el-radio-group v-model="formData.approvalFlow" v-else>
<el-radio value="2">二级审批(所队一>二级机构)</el-radio>
</el-radio-group>
<div class="tips mt-10">
<p v-if="userStore.user.roleCodes.includes('admin_1')">三级审核 问题提交办结时需经过"所队>二级机构>市局"三级审核通过后方可办结</p>
<p>二级审核 问题提交办结时仅需经过"所队—>二级机构"两级审核通过后即可办结</p>
</div>
</el-form-item>
</div>
</el-form>
</el-scrollbar>
<template #footer>
<div class="dialog-footer">
<el-button @click="visibleProxy = false" size="large">取消</el-button>
<el-button type="primary" @click="onSubmit" size="large">
<el-button type="primary" @click="onSubmit" size="large" :loading="loading || submitLoading">
{{ mode === 'add' ? '添加' : '修改' }}
</el-button>
</div>
@ -273,51 +339,194 @@
v-model="duplicateDrawerVisible"
:dict="dict"
:query="{
responderIdCode: model.responderIdCode,
responderName: model.responderName,
responderPhone: model.responderPhone
responderIdCode: formData.responderIdCode,
responderName: formData.responderName,
responderPhone: formData.responderPhone
}"
:requestFn="maileRepeatt"
detailIdKey="complaintId"
detailIdKey="negativeId"
@opened-detail="handleDuplicateRowClick"
/>
<!-- 办理详情 -->
<NegativeDialog
v-model="detailDialogVisible"
:id="detailNegativeId"
@close="detailDialogVisible = false"
/>
<!-- 局长信箱办理详情 -->
<NegativeMailboxDialog
v-model="mailboxDetailVisible"
:id="detailNegativeId"
@close="mailboxDetailVisible = false"
/>
</template>
<script setup>
import {computed, ref, watch} from "vue";
import {maileRepeatt} from "@/api/data/complaintCollection.ts";
import { WarningFilled } from '@element-plus/icons-vue'
import {maileRepeatt, getComplaintCollectionDetail, updateComplaintCollection, addComplaintCollection} from "@/api/data/complaintCollection.ts";
import dayjs from "dayjs";
import feedback from "@/utils/feedback.ts";
import Complaint_detail from "@/components/data/complaint_detail.vue";
import DuplicateDrawerWithDetail from "@/views/data/DuplicateDrawerWithDetail.vue";
import useUserStore from "@/stores/modules/user.ts";
import {secondList, listByFirstHost} from "@/api/system/depart";
import useCatchStore from "@/stores/modules/catch";
const catchStore = useCatchStore();
//
const formData = ref(createEmptyForm());
function createEmptyForm() {
return {
//
sourcePath: [],
sourceTable: '',
sourceTableSubOne: '',
//
originId: '',
originIdSkip: false,
//
discoveryTime: '',
//
responderName: '',
responderNameSkip: false,
responderIdCode: '',
responderIdCodeSkip: false,
responderPhone: '',
responderPhoneSkip: false,
//
secondDepartId: '',
secondDepartName: '',
//
thingDesc: '',
involveProblemIdList: [],
repeatt: '',
handleMethod: '',
//
tags: [],
businessTypeCode: '',
businessTypeName: '',
//
thingFiles: [],
//
hostLevel: '3',
departId: '',
departName: '',
timeLimit: '',
maxSignDuration: null,
maxHandleDuration: null,
maxExtensionDuration: null,
approvalFlow: '',
// id
id: '',
negativeId: '',
};
}
//
const rules = {
sourcePath: [
{required: true, message: "请选择来源(一级/二级)", trigger: "change"},
],
discoveryTime: [{required: true, message: '请选择受理时间', trigger: 'change'}],
secondDepartId: [{required: true, message: '请选择被投诉二级机构', trigger: 'change'}],
thingDesc: [{required: true, message: '请输入来件内容', trigger: 'blur'}],
involveProblemIdList: [{required: true, message: '请选择涉嫌问题', trigger: 'change'}],
repeatt: [{required: true, message: '请选择是否重复件', trigger: 'change'}],
hostLevel: [{required: true, message: '请选择主办层级', trigger: 'change'}],
departId: [{required: true, message: '请选择办理单位', trigger: 'change'}],
timeLimit: [{required: true, message: '请选择办理时限', trigger: 'change'}],
approvalFlow: [{required: true, message: '请选择审批流程', trigger: 'change'}],
};
// 1. computed storeDict
const storeDict = computed(() =>
catchStore.getDicts([
"businessType",
"suspectProblem",
"specialSupervision",
"checkStatus",
"policeType",
"timeLimit",
"approvalFlow",
"sfssSourceTable",
"sfssTags",
]) || {}
);
// 2.
const localDict = {
yesNo: [
{id: 1, dictLabel: "是", dictValue: "1"},
{id: 2, dictLabel: "否", dictValue: "0"},
],
handleMethodType: [
{id: 1, dictLabel: "自办", dictValue: "0"},
{id: 2, dictLabel: "下发", dictValue: "1"},
],
};
// 3. sourceTableAndLevel storeDict.value
const sourceTableAndLevel = computed(() => {
const list = storeDict.value?.sfssSourceTable || [];
if (!list || list.length === 0) return [];
// remark
const parents = list.filter(d => !d.remark);
// remark
const children = list.filter(d => d.remark);
return parents.map(p => ({
label: p.dictLabel,
value: String(p.dictValue),
children: children
.filter(c => String(c.remark) === String(p.dictValue))
.map(c => ({
label: c.dictLabel,
value: String(c.dictValue),
})),
}));
});
// 4. dict
const dict = computed(() => ({
...storeDict.value,
...localDict,
sourceTableAndLevel: sourceTableAndLevel.value,
}));
const HostLevel = {
FIRST: '1',
SECOND: '2',
THREE: '3'
};
const userStore = useUserStore();
const props = defineProps({
modelValue: Boolean, //
mode: { type: String, default: "add" }, // add | edit
model: { type: Object, required: true }, // addForm / updateForm
dict: { type: Object, required: true },
rules: { type: Object, required: true },
modelValue: Boolean,
mode: { type: String, default: "add" },
id: { type: String, default: "" },
negativeId: { type: String, default: "" },
loading: { type: Boolean, default: false },
});
const emit = defineEmits(["update:modelValue", "submit"]);
const emit = defineEmits(["update:modelValue", "updateSuccess"]);
const formRef = ref();
const submitLoading = ref(false);
const visibleProxy = computed({
get: () => props.modelValue,
set: (v) => emit("update:modelValue", v),
});
watch(
() => visibleProxy.value,
(v) => {
async (v) => {
if (v) {
//
props.model.originIdSkip = false
props.model.responderNameSkip = false
props.model.responderIdCodeSkip = false
props.model.responderPhoneSkip = false
//
formData.value = createEmptyForm();
duplicateDrawerVisible.value = false
duplicateLoading.value = false
@ -326,35 +535,179 @@ watch(
duplicateCount.value = 0
duplicateCache.value = []
duplicateList.value = []
getDeparts()
if (props.mode === 'edit' && (props.id || props.negativeId)) {
try {
const res = await getComplaintCollectionDetail({ id: props.id, negativeId: props.negativeId });
const r = res?.data ?? res ?? {};
formData.value.id = r.id ?? '';
formData.value.sourceTable = r.sourceTable ?? '';
formData.value.sourceTableSubOne = r.sourceTableSubOne ?? '';
formData.value.sourcePath = [r.sourceTable, r.sourceTableSubOne].filter(Boolean);
formData.value.originId = r.originId ?? '';
formData.value.discoveryTime = r.discoveryTime ?? '';
formData.value.responderName = r.responderName ?? '';
formData.value.responderIdCode = r.responderIdCode ?? '';
formData.value.responderPhone = r.responderPhone ?? '';
formData.value.secondDepartId = r.secondDepartId ?? '';
formData.value.secondDepartName = r.secondDepartName ?? '';
formData.value.thingDesc = r.thingDesc ?? '';
formData.value.repeatt = r.repeatt ?? '';
formData.value.handleMethod = r.handleMethod ?? '';
formData.value.involveProblemIdList = splitToArray(r.involveProblemIdList ?? r.involveProblem);
formData.value.tags = splitToArray(r.tags ?? r.tag);
formData.value.businessTypeName = r.businessTypeName ?? '';
formData.value.businessTypeCode = r.businessTypeCode ?? '';
formData.value.thingFiles = normalizeThingFiles(r.thingFiles);
formData.value.hostLevel = r.hostLevel ?? '3';
formData.value.departId = r.departId ?? '';
formData.value.departName = r.departName ?? '';
formData.value.timeLimit = r.timeLimit ?? '';
formData.value.maxSignDuration = r.maxSignDuration ?? null;
formData.value.maxHandleDuration = r.maxHandleDuration ?? null;
formData.value.maxExtensionDuration = r.maxExtensionDuration ?? null;
formData.value.approvalFlow = r.approvalFlow ?? '';
formData.value.negativeId = r.negativeId ?? '';
} catch (e) {
console.error("获取详情失败", e);
feedback.notifyError("获取数据失败,请重试");
visibleProxy.value = false;
}
}
}
}
)
);
watch(
() => formData.value.hostLevel,
() => {
getDeparts();
}
);
const departs = ref([]);
const departLoading = ref(false);
async function getDeparts() {
if (!formData.value.hostLevel) return;
departLoading.value = true;
try {
if (formData.value.hostLevel === HostLevel.FIRST) {
departs.value = await listByFirstHost();
} else if (formData.value.hostLevel === HostLevel.SECOND) {
departs.value = await secondList();
} else {
departs.value = [];
}
} catch (e) {
console.error(e);
} finally {
departLoading.value = false;
}
}
function handleSelectDepart(row) {
formData.value.departName = row.shortName;
}
function handleLinkDepart() {
if (formData.value.hostLevel === HostLevel.SECOND) {
feedback.msgWarning("当前选择二级机构主办,指定办理单位请选择二级机构!");
return;
}
formData.value.departId = formData.value.secondDepartId;
formData.value.departName = formData.value.secondDepartName;
}
function handleChangeHostLevel(val) {
if (val === HostLevel.FIRST) {
formData.value.departId = "";
}
if (val === HostLevel.SECOND) {
formData.value.approvalFlow = "3";
}
}
// -> addForm watch
watch(
() => props.model.sourcePath,
() => formData.value.sourcePath,
(path) => {
if (!path || path.length === 0) return;
props.model.sourceTable = (path && path[0]) || "";
props.model.sourceTableSubOne = (path && path[1]) || "";
formData.value.sourceTable = (path && path[0]) || "";
formData.value.sourceTableSubOne = (path && path[1]) || "";
},
{ deep: true }
);
function fillFiledName() {
const code = props.model.businessTypeCode;
const hit = props.dict.businessType?.find(
const code = formData.value.businessTypeCode;
const hit = dict.value?.businessType?.find(
(d) => String(d.dictValue) === String(code)
);
props.model.businessTypeName = hit?.dictLabel || "";
formData.value.businessTypeName = hit?.dictLabel || "";
}
function splitToArray(val) {
if (!val) return [];
if (Array.isArray(val)) return val;
if (typeof val === 'string') {
return val.split(/[,,]/).map(s => s.trim()).filter(Boolean);
}
return [];
}
function normalizeThingFiles(val) {
if (!val) return [];
if (Array.isArray(val)) return val.map(f => ({...f, loading: false, percent: 100}));
if (typeof val === 'string' && val.trim().startsWith('[')) {
try {
return JSON.parse(val).map(f => ({...f, loading: false, percent: 100}));
} catch {}
}
if (typeof val === 'string') {
return val.split(/[,,]/).map(s => s.trim()).filter(Boolean).map(p => ({
filePath: p,
fileName: p.split('/').pop() || p,
loading: false,
percent: 100,
}));
}
return [];
}
async function onSubmit() {
fillFiledName();
// await formRef.value.validate();
emit("submit");
// API
if (props.mode === 'add') {
submitLoading.value = true;
try {
await addComplaintCollection({...formData.value});
feedback.msgSuccess("添加成功");
visibleProxy.value = false;
emit("updateSuccess");
} catch (e) {
console.error("添加失败", e);
} finally {
submitLoading.value = false;
}
return;
}
// API
submitLoading.value = true;
try {
await updateComplaintCollection({...formData.value});
feedback.msgSuccess("修改成功");
visibleProxy.value = false;
emit("updateSuccess");
} catch (e) {
console.error("修改失败", e);
} finally {
submitLoading.value = false;
}
}
//
@ -365,18 +718,17 @@ const onCheckDuplicate = async () => {
duplicateDrawerVisible.value = true
}
// region
const duplicateCount = ref(0) //
const duplicateCache = ref([]) //
const duplicateCount = ref(0)
const duplicateCache = ref([])
const autoDuplicateLoading = ref(false)
const duplicateHintVisible = ref(false)
const fetchDuplicate = async () => {
const body = {
responderIdCode: props.model.responderIdCode,
responderName: props.model.responderName,
responderPhone: props.model.responderPhone,
responderIdCode: formData.value.responderIdCode,
responderName: formData.value.responderName,
responderPhone: formData.value.responderPhone,
}
const res = await maileRepeatt(body)
@ -384,7 +736,7 @@ const fetchDuplicate = async () => {
}
const onAutoCheckDuplicate = async () => {
const { responderIdCode, responderName, responderPhone } = props.model
const { responderIdCode, responderName, responderPhone } = formData.value
if (!responderIdCode && !responderName && !responderPhone) {
duplicateCount.value = 0
@ -400,19 +752,32 @@ const onAutoCheckDuplicate = async () => {
duplicateCount.value = list.length
duplicateCache.value = list
} catch (e) {
//
} finally {
autoDuplicateLoading.value = false
}
}
// endregion
//
const detailDialogVisible = ref(false)
const mailboxDetailVisible = ref(false)
const detailNegativeId = ref("")
//
const handleDuplicateRowClick = (row) => {
// ComplaintCollection.vue
if (row.problemSources === '局长信箱') {
mailboxDetailVisible.value = true
detailNegativeId.value = row.originId
} else {
detailDialogVisible.value = true
detailNegativeId.value = row.negativeId
}
}
</script>
<style>
<style lang="scss" scoped>
.form-layout {
padding: 0 34px;
}
@ -421,13 +786,35 @@ const onAutoCheckDuplicate = async () => {
padding: 16px 24px 24px;
}
.add-negation-container {
padding: 0 60px;
}
.tips {
margin-top: 10px;
[data-active="true"] {
color: var(--danger-color);
}
}
:deep() {
.el-form-item__content {
flex-direction: column;
align-items: flex-start;
}
.block.el-radio-group {
display: block;
}
.block.el-radio-group .el-radio {
display: block;
}
}
/* 重复件抽屉整体 */
.duplicate-drawer {
--el-drawer-padding-primary: 20px;
}
/* 抽屉标题 */
.drawer-header {
display: flex;
align-items: center;
@ -437,17 +824,14 @@ const onAutoCheckDuplicate = async () => {
color: #d46b08;
}
/* 内容区域 */
.drawer-body {
padding-top: 8px;
}
/* 顶部提示 */
.drawer-alert {
margin-bottom: 16px;
}
/* 身份证号高亮 */
.id-highlight {
margin-bottom: 20px;
padding: 12px 16px;
@ -463,14 +847,6 @@ const onAutoCheckDuplicate = async () => {
color: #d4380d;
}
.drawer-header {
display: flex;
align-items: center;
gap: 12px;
padding: 4px 0;
}
.drawer-header.danger {
color: #0d73ee;
}
@ -492,4 +868,10 @@ const onAutoCheckDuplicate = async () => {
margin-top: 2px;
}
h2 {
margin: 16px 0 8px;
padding-left: 60px;
font-size: 16px;
font-weight: 600;
}
</style>

266
src/components/data/complaintproblemInfo.vue

@ -1,266 +0,0 @@
<template>
<div class="info-container">
<h3>问题信息</h3>
<div class="row">
<div class="col col-12">
<label>来源</label>
<span>{{ sourcePathText }}</span>
</div>
<div class="col col-12">
<label>业务类型</label>
<span>{{ getDictLabel(dict?.businessType, data.businessTypeCode) }}</span>
</div>
<div class="col col-12">
<label>编号</label>
<span>{{ data.originId || "/" }}</span>
</div>
<div class="col col-12">
<label>登记/受理时间</label>
<span>{{ formatTime(data.discoveryTime) }}</span>
</div>
<div class="col col-12">
<label>来件人姓名</label>
<span>{{ data.responderName || "/" }}</span>
</div>
<div class="col col-12">
<label>身份证号码</label>
<span>{{ data.responderIdCode || "/" }}</span>
</div>
<div class="col col-12">
<label>联系电话</label>
<span>{{ data.responderPhone || "/" }}</span>
</div>
<div class="col col-12">
<label>涉及二级机构</label>
<span>{{ data.secondDepartName || "/" }}</span>
</div>
<div class="col col-24">
<label>来件内容</label>
<span>{{ data.thingDesc || "/" }}</span>
</div>
<div class="col col-12">
<label>涉嫌问题</label>
<span>{{ suspectProblemText }}</span>
</div>
<div class="col col-12">
<label>是否重复件</label>
<div class="inline-actions">
<span>{{ getDictLabel(dict?.yesNo, data.repeatt) }}</span>
<el-button
v-if="String(data.repeatt) === '1'"
class="repeat-btn"
type="primary"
link
@click="onRepeatClick"
>
查看
</el-button>
</div>
</div>
<div class="col col-12">
<label>标签</label>
<span>{{ tagText }}</span>
</div>
<div class="col col-24">
<label>办理方式</label>
<span>{{ getDictLabel(dict?.handleMethodType, data.handleMethod) }}</span>
</div>
</div>
<div v-if="data.thingFiles?.length">
<div class="text-primary mt-10 mb-10">附件</div>
<file-list :files="data.thingFiles" />
</div>
</div>
<DuplicateDrawerWithDetail
v-model="duplicateDrawerVisible"
:dict="dict"
:query="{
responderIdCode: data.responderIdCode,
responderName: data.responderName,
responderPhone: data.responderPhone
}"
:requestFn="maileRepeatt"
detailIdKey="complaintId"
:excludeId="data.id"
/>
</template>
<script setup>
import { computed } from "vue";
import dayjs from "dayjs";
import { ElMessage } from "element-plus";
import DuplicateDrawerWithDetail from "@/views/data/DuplicateDrawerWithDetail.vue";
import {maileRepeatt} from "@/api/data/complaintCollection.ts";
const props = defineProps({
data: { type: Object, default: () => ({}) }, // base
dict: { type: Object, default: () => ({}) },
});
const data = computed(() => props.data || {});
const dict = computed(() => props.dict || {});
function formatTime(v) {
return v ? dayjs(v).format("YYYY-MM-DD HH:mm:ss") : "/";
}
/** 兼容:单值/数组/逗号串 */
function getDictLabel(list, value) {
if (!Array.isArray(list)) return "/";
if (value === null || value === undefined || value === "") return "/";
const values = Array.isArray(value)
? value
: typeof value === "string" && value.includes(",")
? value.split(",").map((v) => v.trim())
: [value];
const labels = values
.map((v) => list.find((d) => d.dictValue == v)?.dictLabel)
.filter(Boolean);
return labels.length ? labels.join(",") : "/";
}
/** 来源展示:sourceTablePath 优先,其次 sourcePath */
const sourcePathText = computed(() => {
if (data.value?.sourceTablePath) return data.value.sourceTablePath;
if (Array.isArray(data.value?.sourcePath) && data.value.sourcePath.length) {
return data.value.sourcePath.join(" / ");
}
return "/";
});
/** 涉嫌问题:involveProblemStr 优先,否则 involveProblemIdList -> dict.suspectProblem */
const suspectProblemText = computed(() => {
if (data.value?.involveProblemStr) return data.value.involveProblemStr;
const ids = data.value?.involveProblemIdList;
if (!Array.isArray(ids) || !ids.length) return "/";
const labels = ids
.map((v) => getDictLabel(dict.value?.suspectProblem, v))
.filter((x) => x && x !== "/");
return labels.length ? labels.join(",") : "/";
});
/** 标签:按你原 info-container:base.tag 逗号拆分 -> dict.sfssTags */
const tagText = computed(() => {
const v = data.value?.tag;
if (!v) return "/";
return String(v)
.split(",")
.map((x) => getDictLabel(dict.value?.sfssTags, x))
.filter((x) => x && x !== "/")
.join(",") || "/";
});
const duplicateDrawerVisible = ref(false);
const onRepeatClick = () => {
duplicateDrawerVisible.value = true;
};
</script>
<style scoped>
/* 沿用你原来的 info-container 样式 */
.info-container {
border: 1px solid #eee;
background: #fafcff;
padding: 12px 16px;
border-radius: 6px;
margin-bottom: 12px;
}
.info-container h3 {
margin-top: 0;
margin-bottom: 12px;
font-size: 16px;
font-weight: 600;
}
.row {
display: flex;
flex-wrap: wrap;
gap: 10px 0;
}
.col {
display: flex;
gap: 8px;
line-height: 22px;
}
.col-12 {
width: 50%;
}
.col-24 {
width: 100%;
}
label {
width: 120px;
color: #666;
flex: 0 0 auto;
}
span {
color: #222;
flex: 1 1 auto;
word-break: break-word;
}
.col-24 span {
text-align: justify;
text-align-last: left;
line-height: 1.6;
}
.text-primary {
color: #409eff;
font-weight: 500;
}
.mt-10 {
margin-top: 10px;
}
.mb-10 {
margin-bottom: 10px;
}
.inline-actions {
display: inline-flex;
align-items: center;
gap: 8px;
flex: 0 0 auto;
}
.repeat-btn {
padding: 0; /* link 按钮默认就很轻,这行是让它更像文字 */
}
</style>

47
src/components/negative/description.vue

@ -58,6 +58,18 @@
<label>涉及单位</label>
<span>{{ negative.involveDepartName || '/' }}</span>
</div>
<div class="col col-6" v-if="negative.sourceType === '2' && negative.currentRow?.repeatt">
<label>是否重复件</label>
<span>{{ getDictLable(dict.yesNo, negative.currentRow.repeatt) }}</span>
</div>
<div class="col col-6" v-if="negative.sourceType === '2' && negative.currentRow?.tag">
<label>标签</label>
<span>{{ getDictLabel(dict.sfssTags, negative.currentRow.tag) }}</span>
</div>
<div class="col col-6" v-if="negative.sourceType === '2' && negative.currentRow?.handleMethod">
<label>办理方式</label>
<span>{{ getDictLable(dict.handleMethodType, negative.currentRow.handleMethod) }}</span>
</div>
<div class="col col-12">
<label>涉嫌问题</label>
<span>{{ getInvolveProblem(negative.involveProblem, dict.suspectProblem) || '/' }}</span>
@ -82,14 +94,45 @@
</div>
</template>
<script setup>
import { getInvolveProblem } from "@/utils/util";
import { getInvolveProblem ,getDictLable } from "@/utils/util";
const negative = inject('negative')
import useCatchStore from "@/stores/modules/catch";
const catchSotre = useCatchStore();
const dict = catchSotre.getDicts([
"specialSupervision", "suspectProblem"
"specialSupervision", "suspectProblem", "yesNo", "sfssTags", "handleMethodType"
]);
function getDictLabel(list, value) {
if (!Array.isArray(list)) return '/'
if (value === null || value === undefined || value === '') return '/'
let values = []
// 1 value
if (Array.isArray(value)) {
values = value
}
// 2 value "1,2,3"
else if (typeof value === 'string' && value.includes(',')) {
values = value.split(',').map(v => v.trim())
}
// 3 string / number
else {
values = [value]
}
// 4
const labels = values
.map(v => {
const item = list.find(d => d.dictValue == v)
return item?.dictLabel
})
.filter(Boolean)
return labels.length ? labels.join(',') : '/'
}
console.log('negative',negative)
</script>
<style lang="scss" scoped>

141
src/components/negative/dialog.vue

@ -1,6 +1,7 @@
<template>
<el-dialog
:show-close="false"
v-model="visible"
width="84vw"
top="2vh"
class="dialog-header-nopadding"
@ -166,7 +167,14 @@
"
>
<!-- 核查办理-->
<negative-verify-sfss
v-if="isComplaintReport"
ref="componentRef"
@stage-change="handleComplaintStageChange"
@submit="handleSubmitExecute"
/>
<negative-verify
v-else
ref="componentRef"
@submit="handleSubmitExecute"
/>
@ -320,7 +328,7 @@
>保存抽检结果</el-button
>
<div v-if="!disabled">
<template v-for="item in flowActions" :key="item.actionKey">
<template v-for="item in displayFlowActions" :key="item.actionKey">
<template
v-if="
!negative.extensionApplyFlag &&
@ -365,7 +373,7 @@
</template>
</div>
</footer>
<template v-for="item in flowActions" :key="item.actionKey">
<template v-for="item in displayFlowActions" :key="item.actionKey">
<template v-if="item.actionKey.includes('apply_completion')">
<negative-apply-completion
:ref="(el) => setActionItemRef(item.actionKey, el)"
@ -398,7 +406,7 @@
/>
</template>
</template>
<template v-if="confirmationCompletionFlag && flowActions.length">
<template v-if="confirmationCompletionFlag && displayFlowActions.length">
<negative-confirmation-completion
@submit="handleSubmitExecute"
ref="confirmationCompletionRef"
@ -413,6 +421,7 @@ import {addFav, delFav} from "@/api/work/fav";
import feedback from "@/utils/feedback";
import {getComponents} from "@/utils/flow";
import {getDictLable} from "@/utils/util";
import { initialReview, saveInvolveJson } from "@/api/data/complaintCollection";
import useCatchStore from "@/stores/modules/catch";
@ -427,6 +436,7 @@ const dict = useCatchStore().getDicts([
]);
const props = defineProps({
modelValue: Boolean,
id: {
type: String,
required: true,
@ -437,7 +447,7 @@ const props = defineProps({
},
});
const emit = defineEmits(["close", "change"]);
const emit = defineEmits(["close", "change", "update:modelValue"]);
const work = inject("work", null);
@ -462,12 +472,56 @@ const remainingDuration = ref(0);
const maxDuration = ref(0);
const flowActions = ref([]);
const components = ref([]);
const COMPLAINT_REPORT_SOURCE_TYPE = "2";
const LOCAL_INITIAL_REVIEW_ACTION_KEY = "initial_review";
const STAGE_INITIAL = "0";
const STAGE_COMPLETION = "1";
type ProcessingStage = typeof STAGE_INITIAL | typeof STAGE_COMPLETION;
const currentComplaintStage = ref<ProcessingStage>(STAGE_COMPLETION);
const isComplaintReport = computed(
() => negative.value?.sourceType === COMPLAINT_REPORT_SOURCE_TYPE
);
const displayFlowActions = computed(() => {
if (!isComplaintReport.value) {
return flowActions.value;
}
if (currentComplaintStage.value === STAGE_INITIAL) {
return [
{
actionKey: 'SAVE_INITIAL_REVIEW',
buttonLabel: "保存信息",
buttonType: "primary",
plain: true,
validateForm: false,
},
{
actionKey: LOCAL_INITIAL_REVIEW_ACTION_KEY,
buttonLabel: "初核反馈",
buttonType: "primary",
plain: false,
validateForm: true,
},
];
}
return flowActions.value;
});
const visible = computed({
get: () => props.modelValue,
set: (val) => emit('update:modelValue', val)
});
watch(
() => props.id,
() => {
getDetails();
verifyEditFlag.value = false;
formData.value = {};
if (visible.value) {
getDetails();
verifyEditFlag.value = false;
formData.value = {};
currentComplaintStage.value = STAGE_COMPLETION;
}
}
);
@ -475,7 +529,12 @@ const confirmationCompletionFlag = ref(false);
function getDetails() {
loading.value = true;
getNegativeDetails(props.id, work?.value.workId).then((data) => {
negative.value = data.negative;
negative.value = {
...data.negative,
remainingDuration: data.remainingDuration,
complaintCollectionPageDTO: data.complaintCollectionPageDTO,
currentRow: data.complaintCollectionPageDTO || {},
};
console.log('negative',negative)
getConfinementData();
getSuperviseReportFun(data.negative.reportNumber)
@ -591,7 +650,7 @@ async function handleExecute(action, data) {
return;
}
if (action.validateForm) {
if (action.actionKey !== FlowActionEnum.SAVE) {
if (action.actionKey !== FlowActionEnum.SAVE && action.actionKey !== 'SAVE_INITIAL_REVIEW') {
try {
data = await componentRef.value.validate();
} catch (e) {
@ -602,6 +661,14 @@ async function handleExecute(action, data) {
data = componentRef.value.getData();
}
}
if (
isComplaintReport.value &&
action.actionKey === FlowActionEnum.APPLY_COMPLETION &&
componentRef.value?.getStage?.() === STAGE_INITIAL
) {
feedback.msgWarning("初核阶段请先保存初核内容,再切换到办结阶段提交办结");
return;
}
if (action.openDialog) {
if (
confirmationCompletionFlag.value &&
@ -625,6 +692,21 @@ async function handleExecute(action, data) {
verifyEditFlag.value = false;
}
try {
if (isComplaintReport.value && action.actionKey === 'SAVE_INITIAL_REVIEW') {
const formData = componentRef.value.getData();
await submitSaveInvolveJson(formData);
feedback.msgSuccess("操作成功");
getDetails();
return;
}
if (isComplaintReport.value && action.actionKey === LOCAL_INITIAL_REVIEW_ACTION_KEY) {
await submitComplaintInitialReview(data);
currentComplaintStage.value = STAGE_COMPLETION;
componentRef.value?.setStage?.(STAGE_COMPLETION);
feedback.msgSuccess("操作成功");
getDetails();
return;
}
await negativeExecute(props.id, {
workId: work?.value.workId,
actionKey: action.actionKey,
@ -649,6 +731,10 @@ async function handleSubmitExecute(data) {
handleExecute(activeAction.value, data);
}
function handleComplaintStageChange(stage: ProcessingStage) {
currentComplaintStage.value = stage;
}
function handleFav() {
if (isFav.value) {
delFav(props.id);
@ -660,10 +746,6 @@ function handleFav() {
const spotCheckEditFlag = ref(false);
function spotCheck() {
spotCheckEditFlag.value = true;
}
@ -699,6 +781,24 @@ async function handleUpdateVerify() {
verifyEditFlag.value = !verifyEditFlag.value;
}
async function submitComplaintInitialReview(data) {
await initialReview({
complaintId: data?.complaintId,
initWorkDes: data?.initWorkDes,
initProblemPlan: data?.initProblemPlan,
initVerdict: data?.initVerdict,
});
}
async function submitSaveInvolveJson(data) {
await saveInvolveJson({
complaintId: data?.complaintId,
initWorkDes: data?.initWorkDes,
initProblemPlan: data?.initProblemPlan,
initVerdict: data?.initVerdict,
});
}
defineExpose({
spotCheck,
});
@ -720,19 +820,14 @@ watch(
watch(loading, (val) => {
if (!val) {
nextTick(() => {
actionHistoryRef.value.style.height = `${
leftContainerRef.value.offsetHeight -
(countdownContainerRef.value?.offsetHeight || 0) -
leftRowRef.value.offsetHeight
}px`;
console.log(
`${
if (actionHistoryRef.value) {
actionHistoryRef.value.style.height = `${
leftContainerRef.value.offsetHeight -
(countdownContainerRef.value?.offsetHeight || 0) -
leftRowRef.value.offsetHeight
}px`
);
}px`;
}
});
}
});

53
src/components/negative/mailbox-dialog.vue

@ -0,0 +1,53 @@
<template>
<el-dialog
:show-close="false"
width="90vw"
top="2vh"
class="dialog-header-nopadding dialog-body-nopadding"
style="--el-dialog-padding-primary: 10px; margin-bottom: 0"
:lock-scroll="false"
:close-on-press-escape="false"
ref="dialogRef"
>
<template #header="{ close }">
<header class="dialog-header">
<el-button link circle size="large" @click="close" class="close-btn">
<template #icon>
<icon name="el-icon-close" :size="26" :color="'#fff'" />
</template>
</el-button>
</header>
</template>
<main>
<iframe :src="`http://65.47.60.145/#/mail-detail?mailId=` + id"></iframe>
</main>
</el-dialog>
</template>
<script lang="ts" setup>
const props = defineProps({
id: {
type: String,
required: true,
},
});
</script>
<style lang="scss" scoped>
iframe {
width: 100%;
height: 100%;
border: none;
}
main {
height: 90vh;
}
.dialog-header {
position: absolute;
top: 10px;
right: 10px;
}
</style>
<style lang="scss">
.dialog-body-nopadding .el-dialog__body {
padding: 0;
}
</style>

89
src/components/negative/verify-description.vue

@ -48,14 +48,43 @@
</div>
</div>
</el-collapse-item>
<el-collapse-item
title="初核信息"
name="initialReview"
v-if="showInitialReviewInfo"
>
<div class="row">
<div class="col col-12">
<label>初核时间</label>
<span>{{ initialReviewInfo.gwf2 || "/" }}</span>
</div>
<div class="col col-12">
<label>初核状态</label>
<span>{{ getInitialReviewStatusLabel(initialReviewInfo.gwf3) }}</span>
</div>
<div class="col col-24">
<label style="text-align: left; line-height: 1.5; font-size: 12px">初核工作开展情况</label>
<span>{{ initialReviewInfo.initWorkDes || "/" }}</span>
</div>
<div class="col col-24">
<label style="text-align: left; line-height: 1.5; font-size: 12px">初核发现的问题及下一步工作计划</label>
<span>{{ initialReviewInfo.initProblemPlan || "/" }}</span>
</div>
<div class="col col-24">
<label>初核结论</label>
<span>{{ getDictLable(dict.checkStatus, initialReviewInfo.initVerdict) || initialReviewInfo.initVerdict || "/" }}</span>
</div>
</div>
</el-collapse-item>
<el-collapse-item
title="信访情况"
name="mail"
v-if="
negative.problemSourcesCode === ProblemSources.GJXFPT ||
showInitialReviewInfo ||
(negative.problemSourcesCode === ProblemSources.GJXFPT ||
negative.problemSourcesCode === ProblemSources.GABXF ||
negative.problemSourcesCode === ProblemSources.JZXX ||
negative.problemSourcesCode === ProblemSources.XF12337
negative.problemSourcesCode === ProblemSources.XF12337)
"
>
<div class="row" >
@ -96,6 +125,20 @@
<label>处分处理情况</label>
<span>{{ negative.disciplinaryActionDesc }}</span>
</div>
<div class="col col-6" v-if="initialReviewInfo.completionStatus">
<label>办结情况</label>
<span>{{ getDictLable(
dict.completionStatus,
initialReviewInfo.completionStatus
) }}</span>
</div>
<div class="col col-6" v-if="initialReviewInfo.publicRecognition">
<label>群众认可</label>
<span>{{ getDictLable(
dict.publicRecognition,
initialReviewInfo.publicRecognition
) }}</span>
</div>
</div>
</el-collapse-item>
<el-collapse-item
@ -338,8 +381,11 @@ import {getConfinementListAll} from "@/api/work/confinement";
const dict = useCatchStore().getDicts([
"accountabilityTarget",
"personType",
"checkStatus",
"verifySituation",
"verifyFileSituation",
"completionStatus",
"publicRecognition"
]);
const activeNames = ref([
@ -347,9 +393,46 @@ const activeNames = ref([
"involved_department",
"completionAttachment",
"completed",
"mail"
"mail",
"initialReview"
]);
let negative = inject("negative");
const COMPLAINT_REPORT_SOURCE_TYPE = "2";
const initialReviewInfo = computed(() => {
debugger
const currentRow = negative.value?.currentRow || {};
return {
gwf2: currentRow?.gwf2 || negative.value?.gwf2 || "",
gwf3: currentRow?.gwf3 || negative.value?.gwf3 || "",
initWorkDes: currentRow?.initWorkDes || negative.value?.initWorkDes || "",
initProblemPlan: currentRow?.initProblemPlan || negative.value?.initProblemPlan || "",
initVerdict: currentRow?.initVerdict || negative.value?.initVerdict || "",
completionStatus: currentRow?.completionStatus,
publicRecognition: currentRow?.publicRecognition
};
});
const showInitialReviewInfo = computed(() => {
if (String(negative.value?.sourceType || "") !== COMPLAINT_REPORT_SOURCE_TYPE) {
return false;
}
return Boolean(
initialReviewInfo.value.gwf2 ||
initialReviewInfo.value.gwf3 ||
initialReviewInfo.value.initWorkDes ||
initialReviewInfo.value.initProblemPlan ||
initialReviewInfo.value.initVerdict
);
});
function getInitialReviewStatusLabel(status) {
const code = String(status || "");
if (code === "0") return "未初核";
if (code === "1") return "已初核";
if (code === "2") return "超时上传";
return "/";
}
console.log('negative.value',negative.value)

2425
src/components/negative/verify-sfss.vue

File diff suppressed because it is too large Load Diff

8
src/components/negative/verify.vue

@ -101,7 +101,7 @@
v-if="
problemIsTrue &&
form.isRectifyCode === IsRectify.NOT &&
(form.checkStatusCode === '1' || form.checkStatusCode === '2' || form.checkStatusCode === '3')
problemIsTrue
"
>
<el-form-item
@ -143,7 +143,7 @@
message: '请输入未整改原因',
trigger: ['blur'],
}"
v-if="
v-if="problemIsTrue &&
(form.isRectifyCode === '0' || form.isRectifyCode === '2')
"
>
@ -1663,10 +1663,6 @@ const problemIsTrue = computed(() => {
getFormData();
watch(negative, () => {
getFormData();
});
async function getFormData() {
form.value = {
involveDepartId: negative.value.involveDepartId,

390
src/views/data/ComplaintCollection.vue

@ -77,13 +77,18 @@
<el-col :span="6">
<el-form-item label="办理状态">
<el-select
v-model="query.status"
v-model="query.processingStatus"
clearable
multiple
collapse-tags
placeholder="办理状态"
>
<el-option value="0" label="未办结"/>
<el-option value="1" label="已办结"/>
<el-option value="2" label="强制终结"/>
<el-option
v-for="item in dict.processingStatus"
:key="item.id"
:label="item.dictLabel"
:value="item.dictValue"
/>
</el-select>
</el-form-item>
</el-col>
@ -182,6 +187,7 @@
/>
</el-form-item>
</el-col>
<!-- 局长信箱专属搜索框 - 注释 by Claude
<el-col :span="6"
v-if=" query.sourceTableList?.length === 1 && String(query.sourceTableList[0]) === '23'">
<el-form-item label="信件状态">
@ -198,6 +204,7 @@
</el-select>
</el-form-item>
</el-col>
-->
</el-row>
</el-form>
<div class="mb-25 flex between">
@ -329,38 +336,43 @@
<el-table-column label="业务类别Code" width="100" prop="businessTypeCode" v-if="false"/>
<el-table-column label="核查结论" width="140" prop="checkStatus" show-overflow-tooltip>
<template #default="{ row }">
{{ getDictLabel(dict.checkStatus, row.checkStatus) }}
</template>
</el-table-column>
<el-table-column label="状态" prop="status" width="100">
<template #default="{ row }">
<span :style="row.status === '0' ? 'color: red' : ''">
{{
row.status === '0' ? '未办结' :
row.status === '1' ? '已办结' :
row.status === '2' ? '强制终结' :
'-'
}}
</span>
{{ getDictLabel(dict.checkStatus, row.checkStatusCode) }}
</template>
</el-table-column>
<el-table-column label="信件状态(局)" width="120" prop="processingStatus">
<el-table-column label="办理状态" prop="processingStatus" width="100">
<template #default="{ row }">
{{
row.processingStatus === 'completion' ? '已办结' :
row.processingStatus === 'processing' ? '办理中' :
row.processingStatus === 'delayed' ? '已延期' :
row.processingStatus === 'terminated' ? '已终止' :
'/'
}}
<el-tag
:type="row.processingStatus === 'completed' ? 'success' : 'primary'"
v-if="row.processingStatus"
>
{{ getDictLabel(dict.processingStatus, row.processingStatus) }}
</el-tag>
<span v-else>-</span>
</template>
</el-table-column>
<!-- <el-table-column label="信件状态(局)" width="120" prop="processingStatus">-->
<!-- <template #default="{ row }">-->
<!-- <el-tag-->
<!-- :type="row.processingStatus === 'completed' ? 'success' : 'primary'"-->
<!-- v-if="row.processingStatus"-->
<!-- >-->
<!-- {{ getDictLabel(dict.processingStatus, row.processingStatus) }}-->
<!-- </el-tag>-->
<!-- <span v-else>/</span>-->
<!-- </template>-->
<!-- </el-table-column>-->
<el-table-column label="操作" width="240" fixed="right">
<template #default="{ row }">
<el-button type="primary" v-if="row.status === '0'" link @click="handleAction(row)">办理</el-button>
<el-button type="primary" v-if="row.status === '0'" link @click="handleUpdate(row)">修改</el-button>
<el-button type="danger" v-if="row.status === '0'" link @click="handleDel(row)">删除</el-button>
<el-button type="primary" v-if="row.status === '1' || row.status === '2'" link @click="handleWatchDetail(row)">查看详情</el-button>
<!-- <el-button type="primary" v-if="row.status === '0'" link @click="handleAction(row)">办理</el-button>-->
<el-button
type="primary"
link
@click="handleActionDetail(row)"
>办理详情</el-button
>
<el-button type="primary" v-if="row.processingStatus !== 'completed'" link @click="handleUpdate(row)">修改</el-button>
<el-button type="danger" v-if="row.processingStatus !== 'completed'" link @click="handleDel(row)">删除</el-button>
<!-- <el-button type="primary" v-if="row.status === '1' || row.status === '2'" link @click="handleWatchDetail(row)">查看详情</el-button>-->
</template>
</el-table-column>
</el-table>
@ -384,51 +396,31 @@
<data-complaintformdialog
v-model="addShow"
mode="add"
:model="addForm"
:dict="dict"
:rules="addRules"
@submit="submitAdd"
@updateSuccess="getList"
/>
<!-- 修改 -->
<data-complaintformdialog
v-model="updateShow"
mode="edit"
:model="updateForm"
:dict="dict"
:rules="addRules"
@submit="submitUpdate"
:id="updateId"
:negativeId="activeNegativeId"
:loading="updateLoading"
@updateSuccess="getList"
/>
<!-- 办理 -->
<el-dialog
v-model="negativeVerifySfssDailog"
title="办理情况"
width="80vw"
top="5vh"
destroy-on-close
>
<div v-loading="submitLoading" element-loading-text="请稍等...">
<negative-verify-sfss ref="negativeVerifySfssRef" :extraDict="dict"/>
</div>
<template #footer>
<div class="dialog-footer">
<el-button @click="negativeVerifySfssDailog = false">取消</el-button>
<el-button type="primary" @click="handleSubmit" :loading="submitLoading" :disabled="submitLoading">办结
</el-button>
<el-button type="primary" @click="handleSaveInvolve" :loading="saveLoading">临时保存</el-button>
<el-button type="danger" @click="forceTerminationFun" :loading="forceTerminationLoading">强制终结</el-button>
</div>
</template>
</el-dialog>
<negative-dialog
v-model="show"
:id="activeNegativeId"
@close="show = false"
ref="negativeDialogRef"
/>
<!-- 查看详情 -->
<complaint_detail
width="80vw"
v-model="detailShow"
:id="activeId"
:dict="dict"/>
<negative-mailbox-dialog
v-model="mailboxShow"
:id="activeNegativeId"
@close="mailboxShow = false"
/>
</template>
@ -438,12 +430,11 @@ import {timeFormat} from "@/utils/util";
import feedback from "@/utils/feedback";
import useCatchStore from "@/stores/modules/catch";
import {
addComplaintCollection, addComplaintCollectionBlame,
delComplaintCollection, exportData, forceTermination,
getComplaintCollectionPage, handlerData, saveInvolveJson,
addComplaintCollection,
delComplaintCollection, exportData,
getComplaintCollectionPage, saveInvolveJson,
updateComplaintCollection
} from "@/api/data/complaintCollection.ts";
import Complaint_detail from "@/components/data/complaint_detail.vue";
import useUserStore from "@/stores/modules/user.ts";
const route = useRoute()
@ -497,8 +488,8 @@ const getList = async () => {
delete params.sourcePath
let res = await getComplaintCollectionPage(params);
console.log(res.complaintCollectionPageDTOS);
list.value = res.complaintCollectionPageDTOS;
debugger
list.value = res.records;
total.value = res.total;
loading.value = false;
}
@ -528,210 +519,33 @@ onMounted(() => {
// endregion
// region
// region
const addShow = ref(false);
const add = () => {
addShow.value = true;
};
//
//
function hasPermission() {
console.log("=================userStore===================")
console.log(userStore.user)
const roles = userStore.user?.roleCodes || []
const allow = new Set(['admin_1', 'admin_1_12337'])
return roles.some(r => allow.has(r))
}
const canAdd = computed(() => hasPermission())
const createEmptyAddForm = () => ({
//
sourcePath: [],
// watch
sourceTable: '',
sourceTableSubOne: '',
originId: '',
discoveryTime: '',
responderName: '',
responderIdCode: '',
responderPhone: '',
secondDepartId: '',
secondDepartName: '',
thingDesc: '',
involveProblemIdList: [],
repeatt: '',
leadApproval: '',
tags: [],
handleMethod: '',
thingFiles: [],
});
const addShow = ref(false);
const addForm = ref(createEmptyAddForm());
//
const add = async () => {
addForm.value = createEmptyAddForm()
addShow.value = true
}
//
const submitAdd = async () => {
try {
const body = {...addForm.value}
let res = await addComplaintCollection(body);
if (res === true) {
feedback.msgSuccess("操作成功");
} else {
feedback.notifyError("操作失败");
}
addShow.value = false;
addForm.value = createEmptyAddForm();
} catch (e) {
console.error(e);
feedback.notifyError("操作失败");
} finally {
getList()
}
}
//
const addRules = {
sourcePath: [
{required: true, message: "请选择来源(一级/二级)", trigger: "change"},
],
// originId: [{required: true, message: '', trigger: 'blur'}],
discoveryTime: [{required: true, message: '请选择受理时间', trigger: 'change'}],
// responderName: [{required: true, message: '', trigger: 'blur'}],
// responderIdCode: [{required: true, message: '', trigger: 'blur'}],
// responderPhone: [{required: true, message: '', trigger: 'blur'}],
secondDepartId: [{required: true, message: '请选择被投诉二级机构', trigger: 'change'}],
thingDesc: [{required: true, message: '请输入来件内容', trigger: 'blur'}],
involveProblemIdList: [{required: true, message: '请选择涉嫌问题', trigger: 'change'}],
repeatt: [{required: true, message: '请选择是否重复件', trigger: 'change'}],
// leadApproval: [{required: true, message: '', trigger: 'change'}],
// tags: [{required: true, message: '', trigger: 'change'}],
// handleMethod: [{required: true, message: '', trigger: 'change'}],
}
watch(
() => addForm.value.sourcePath,
(path) => {
addForm.value.sourceTable = path?.[0] ?? "";
addForm.value.sourceTableSubOne = path?.[1] ?? "";
},
{deep: true}
);
// endregion
// region
const updateShow = ref(false);
const updateForm = ref({
// 3
sourcePath: [],
sourceTable: "",
sourceTableSubOne: "",
// v-model undefined
id: "",
originId: "",
discoveryTime: "",
responderName: "",
responderIdCode: "",
responderPhone: "",
secondDepartId: "",
secondDepartName: "",
thingDesc: "",
repeatt: "",
leadApproval: "",
handleMethod: "",
involveProblemIdList: [],
tags: [],
thingFiles: [],
})
function normalizeThingFiles(val) {
if (!val) return []
if (Array.isArray(val)) return val
// JSON
if (typeof val === 'string' && val.trim().startsWith('[')) {
try {
return JSON.parse(val)
} catch { /* ignore */
}
}
// "path1,path2"
if (typeof val === 'string') {
return val.split(/[,,]/).map(s => s.trim()).filter(Boolean).map(p => ({
filePath: p,
fileName: p.split('/').pop() || p,
loading: false,
percent: 100,
}))
}
return []
}
const updateId = ref("");
const updateLoading = ref(false);
//
const handleUpdate = async (row) => {
const r = JSON.parse(JSON.stringify(row || {}))
console.log(r)
updateForm.value = {
// id
id: r.id ?? '',
// cascader sourcePath
sourceTable: r.sourceTable ?? '',
sourceTableSubOne: r.sourceTableSubOne ?? '',
sourcePath: [r.sourceTable, r.sourceTableSubOne].filter(Boolean),
//
originId: r.originId ?? '',
discoveryTime: r.discoveryTime ?? '',
responderName: r.responderName ?? '',
responderIdCode: r.responderIdCode ?? '',
responderPhone: r.responderPhone ?? '',
secondDepartId: r.secondDepartId ?? '',
secondDepartName: r.secondDepartName ?? '',
thingDesc: r.thingDesc ?? '',
repeatt: r.repeatt ?? '',
leadApproval: r.leadApproval ?? '',
handleMethod: r.handleMethod ?? '',
//
involveProblemIdList: splitToArray(r.involveProblemIdList ?? r.involveProblem),
tags: splitToArray(r.tags ?? r.tag),
businessTypeName: r.businessTypeName ?? '',
businessTypeCode: r.businessTypeCode ?? '',
thingFiles: normalizeThingFiles(r.thingFiles).map(f => ({
...f,
loading: false,
percent: 100,
uid: f.uid || `${f.filePath}-${Math.random()}`,
})),
};
updateShow.value = true
}
const submitUpdate = async () => {
const body = {
...updateForm.value,
}
let res = await updateComplaintCollection(body)
if (res === true) {
feedback.msgSuccess("操作成功");
updateShow.value = false
getList()
}
}
function splitToArray(val) {
if (!val) return []
if (Array.isArray(val)) return val
if (typeof val === 'string') {
return val
.split(/[,,]/)
.map(s => s.trim())
.filter(Boolean)
}
return []
const handleUpdate = (row) => {
updateId.value = row.id || '';
activeNegativeId.value = row.negativeId || '';
updateShow.value = true;
}
// endregion
// region
@ -760,7 +574,10 @@ const storeDict = computed(() =>
"checkStatus",
"sfssSourceTable",
"sfssTags",
"accountabilityTarget"
"accountabilityTarget",
"processingStatus",
"handleMethodType",
"yesNo"
]) || {}
);
@ -858,40 +675,6 @@ function getDictLabel(list, value) {
const negativeVerifySfssDailog = ref(false)
const submitLoading = ref(false)
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
//
const handleAction = async (row) => {
submitLoading.value = true
try {
// 1
negativeSfss.value.currentRow = JSON.parse(JSON.stringify(row))
// 2
const res = await handlerData({ id: row.id })
const data = res?.data ?? res ?? {}
// 3 involveJson
let involveObj = {}
if (data.involveJson) {
try {
involveObj = JSON.parse(data.involveJson)
} catch (e) {
console.error("involveJson 解析失败", e)
}
}
// 4 negative🔥
Object.assign(negativeSfss.value, {
...data,
...involveObj, // blames / blameLeaders / handlePolices
})
negativeVerifySfssDailog.value = true
} catch (e) {
console.error("加载办理数据失败", e)
} finally {
submitLoading.value = false
}
}
const negativeVerifySfssRef = ref();
//
@ -1025,6 +808,23 @@ const handleWatchDetail = async (row) => {
}
// endregion
const mailboxShow = ref(false)
// region
// const show = ref(false);
const activeNegativeId = ref("");
function handleActionDetail(row) {
console.log('办理详情', row)
if (row.problemSources === '局长信箱') {
mailboxShow.value = true;
activeNegativeId.value = row.originId;
} else {
show.value = true;
activeNegativeId.value = row.negativeId;
}
}
// endregion
// region
const onHeaderDblClick = () => {
const rows = JSON.parse(JSON.stringify(toRaw(list.value || [])))

11
src/views/data/ComplaintCollection_12389ts.vue

@ -298,12 +298,6 @@
</div>
</div>
<!-- 查看详情 -->
<complaint_detail
v-model="detailShow"
:id="activeId"
:dict="dict"/>
</template>
@ -315,10 +309,7 @@ import {
exportData,
getComplaintCollectionPage
} from "@/api/data/complaintCollection.ts";
import Complaint_detail from "@/components/data/complaint_detail.vue";
const catchStore = useCatchStore();
import useCatchStore = useCatchStore();
const query = ref({
size: 10,

11
src/views/data/ComplaintCollection_gabxf.vue

@ -297,12 +297,6 @@
</div>
</div>
<!-- 查看详情 -->
<complaint_detail
v-model="detailShow"
:id="activeId"
:dict="dict"/>
</template>
@ -315,10 +309,7 @@ import {
exportData,
getComplaintCollectionPage
} from "@/api/data/complaintCollection.ts";
import Complaint_detail from "@/components/data/complaint_detail.vue";
const catchStore = useCatchStore();
import useCatchStore = useCatchStore();
const query = ref({
size: 10,

11
src/views/data/ComplaintCollection_gjxf.vue

@ -297,12 +297,6 @@
</div>
</div>
<!-- 查看详情 -->
<complaint_detail
v-model="detailShow"
:id="activeId"
:dict="dict"/>
</template>
@ -311,10 +305,7 @@ import {computed, onMounted, ref} from 'vue';
import {timeFormat} from "@/utils/util";
import useCatchStore from "@/stores/modules/catch";
import {exportData, getComplaintCollectionPage} from "@/api/data/complaintCollection.ts";
import Complaint_detail from "@/components/data/complaint_detail.vue";
const catchStore = useCatchStore();
import useCatchStore = useCatchStore();
const query = ref({
size: 10,

11
src/views/data/ComplaintCollection_jzxx.vue

@ -324,12 +324,6 @@
</div>
</div>
<!-- 查看详情 -->
<complaint_detail
v-model="detailShow"
:id="activeId"
:dict="dict"/>
</template>
@ -341,10 +335,7 @@ import {
exportData,
getComplaintCollectionPage
} from "@/api/data/complaintCollection.ts";
import Complaint_detail from "@/components/data/complaint_detail.vue";
const catchStore = useCatchStore();
import useCatchStore = useCatchStore();
const query = ref({
size: 10,

11
src/views/data/ComplaintCollection_ldxj.vue

@ -297,12 +297,6 @@
</div>
</div>
<!-- 查看详情 -->
<complaint_detail
v-model="detailShow"
:id="activeId"
:dict="dict"/>
</template>
@ -314,10 +308,7 @@ import {
exportData,
getComplaintCollectionPage
} from "@/api/data/complaintCollection.ts";
import Complaint_detail from "@/components/data/complaint_detail.vue";
const catchStore = useCatchStore();
import useCatchStore = useCatchStore();
const query = ref({
size: 10,

11
src/views/data/DuplicateDrawerWithDetail.vue

@ -65,25 +65,17 @@
</div>
</el-drawer>
<!-- 详情弹窗封装在组件内部 -->
<complaint_detail
width="80vw"
v-model="detailShow"
:id="activeId"
:dict="dict"
/>
</template>
<script setup>
import { computed, ref, watch } from "vue";
import dayjs from "dayjs";
import { WarningFilled } from "@element-plus/icons-vue";
import Complaint_detail from "@/components/data/complaint_detail.vue";
/**
* 目标组件内部封装
* 1) 抽屉展示重复件列表
* 2) 点击行 -> 打开 complaint_detail
* 2) 点击行 -> 查看详情
*
* props:
* - modelValue: 抽屉开关
@ -156,6 +148,7 @@ watch(
);
const handleRowClick = (row) => {
debugger
const id = row?.[props.detailIdKey];
if (!id) return; // ElMessage.warning("ID")
activeId.value = id;

111
src/views/work/Query.vue

@ -43,9 +43,7 @@
style="width: 200px"
placeholder="专项督察"
clearable
v-model="query.specialSupervisions"
multiple
collapse-tags
v-model="query.specialSupervision"
>
<el-option
v-for="item in dict.specialSupervision"
@ -254,9 +252,7 @@
style="width: 146px"
placeholder="专项督察"
clearable
v-model="query.specialSupervisions"
multiple
collapse-tags
v-model="query.specialSupervision"
>
<el-option
v-for="item in dict.specialSupervision"
@ -595,12 +591,12 @@
width="150"
sortable="custom"
/>
<!-- <el-table-column
label="问题发现时间"
prop="discoveryTime"
width="150"
sortable="custom"
/> -->
<el-table-column
label="问题发现时间"
prop="discoveryTime"
width="150"
sortable="custom"
/>
<el-table-column
label="问题来源"
prop="problemSources"
@ -816,6 +812,12 @@
ref="negativeDialogRef"
/>
<negative-mailbox-dialog
v-model="mailboxShow"
:id="activeNegativeId"
@close="mailboxShow = false"
/>
<el-dialog v-model="editShow" title="问题编辑" width="900px">
<el-form label-width="148" :model="formData" ref="formRef">
<div style="margin-bottom: 80px">
@ -874,17 +876,22 @@
<el-row>
<el-col
:span="12"
v-if="
formData.problemSourcesCode === ProblemSources.ZXDC
"
>
<el-form-item
label="专项督察"
prop="specialSupervision"
:rules="{
required: true,
message: '请选择专项督察',
trigger: ['blur'],
}"
>
<el-select
v-model="formData.specialSupervision"
multiple
clearable
collapse-tags
placeholder="请选择专项督察"
>
<el-option
v-for="item in dict.specialSupervision"
@ -1105,6 +1112,17 @@
</footer>
</el-dialog>
<data-complaintformdialog
v-model="complaintEditShow"
mode="edit"
:negativeId="complaintEditId"
:model="complaintModel"
:dict="dict"
:rules="complaintRules"
@updateSuccess="getList"
/>
</template>
<script setup>
import moment from "moment";
@ -1127,6 +1145,7 @@ import {computed, watch} from "vue";
import {getListData} from "@/api/superviseReport/superviseReport";
import {ArrowDown, ArrowUp} from "@element-plus/icons-vue";
import useUserStore from "@/stores/modules/user.ts";
import {delComplaintCollection} from "@/api/data/complaintCollection.ts";
const userStore = useUserStore();
const catchStore = useCatchStore();
@ -1222,8 +1241,6 @@ async function updateQuery() {
await nextTick()
}
if (route.query.extensionFlag === "true") {
query.value.extensionFlag = true;
query.value.processingStatus = ['signing', 'processing', 'approval'];
@ -1237,10 +1254,16 @@ onMounted(() => {
const show = ref(false);
const activeNegativeId = ref("");
const mailboxShow = ref(false);
function handleAction(row) {
show.value = true;
activeNegativeId.value = row.id;
if (row.problemSources === '局长信箱') {
mailboxShow.value = true;
console.log(row)
activeNegativeId.value = row.originId;
} else {
show.value = true;
activeNegativeId.value = row.id;
}
}
const router = useRouter();
@ -1287,8 +1310,15 @@ async function handleReportGenerate() {
async function handleDel(row) {
await feedback.confirm(`确定删除该数据?`);
await feedback.confirm(`确定删除该数据?`);
if (row.sourceType == '2') {
const body = {
negativeId: row.id
}
await delComplaintCollection(body)
}else {
await delNegative(row.id);
}
feedback.msgSuccess("操作成功");
getList();
}
@ -1298,29 +1328,27 @@ const formData = ref({});
const formRef = ref(null);
function handleEdit(row) {
const form = { ...row };
debugger
console.log(row.sourceType);
if (row.sourceType == '2') {
complaintEditShow.value = true;
complaintEditId.value = row.id;
complaintModel.value = { ...row };
}else {
editShow.value = true;
formData.value = { ...row };
if (row.involveProblem) {
form.involveProblem = row.involveProblem.split(",");
formData.value.involveProblem =
formData.value.involveProblem.split(",");
} else {
form.involveProblem = [];
formData.value.involveProblem = [];
}
if (row.specialSupervision) {
form.specialSupervision = row.specialSupervision.split(",");
} else {
form.specialSupervision = [];
}
formData.value = form;
editShow.value = true;
}
}
async function handleSumbit() {
await formRef.value.validate();
//
const submitData = JSON.parse(JSON.stringify(formData.value));
if (Array.isArray(submitData.specialSupervision)) {
submitData.specialSupervision = submitData.specialSupervision.filter(v => v).join(',');
}
await updateNegative(submitData);
await updateNegative(formData.value);
editShow.value = false;
feedback.msgSuccess("操作成功");
getList();
@ -1342,9 +1370,6 @@ function handleSpotCheck(row) {
const remainingTimeFlag = ref(true);
const reportData = ref(null)
watch(()=>reportData.value,(val)=>{
if(val){
@ -1407,6 +1432,12 @@ function handleChangeProblem(node, problem) {
problem.twoLevelContent = node.parent.label;
}
}
//
const complaintEditShow = ref(false);
const complaintEditId = ref("");
const complaintModel = ref({});
const complaintRules = ref({}); //
</script>
<style lang="scss" scoped>
:deep() {

Loading…
Cancel
Save