Compare commits
10
Commits
2b939ed260
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9b76e1ebde | ||
|
|
294e683a47 | ||
|
|
0aa00c4587 | ||
|
|
e292b6aa07 | ||
|
|
ec79c4d6e7 | ||
|
|
1d1846dcb6 | ||
|
|
226a418ca6 | ||
|
|
44e12e2240 | ||
|
|
03d4dfebf0 | ||
|
|
f6005b2799 |
@@ -293,3 +293,20 @@ html[data-theme='dark'] {
|
|||||||
margin-inline: 0;
|
margin-inline: 0;
|
||||||
}
|
}
|
||||||
// update-end--author:wangshuai---date:20240611---for:【TV360X-1070】一对多内嵌,为什么多这一块,不从头对齐
|
// update-end--author:wangshuai---date:20240611---for:【TV360X-1070】一对多内嵌,为什么多这一块,不从头对齐
|
||||||
|
|
||||||
|
/* 反馈表格整行按状态着色(与台账颜色统一:未开始黄/进行中红/已完成绿) */
|
||||||
|
.feedback-status-row-warning td {
|
||||||
|
background: #fffbe6 !important;
|
||||||
|
}
|
||||||
|
.feedback-status-row-error td {
|
||||||
|
background: #fff1f0 !important;
|
||||||
|
}
|
||||||
|
.feedback-status-row-success td {
|
||||||
|
background: #f6ffed !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 反馈表格列名称(表头)居中 */
|
||||||
|
.feedback-table .ant-table-thead > tr > th,
|
||||||
|
.feedback-inner-table .ant-table-thead > tr > th {
|
||||||
|
text-align: center !important;
|
||||||
|
}
|
||||||
|
|||||||
@@ -68,15 +68,7 @@
|
|||||||
<template #tableTitle>
|
<template #tableTitle>
|
||||||
<a-button type="primary" v-auth="actionAuth.add" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增</a-button>
|
<a-button type="primary" v-auth="actionAuth.add" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增</a-button>
|
||||||
<a-button type="primary" v-auth="actionAuth.exportXls" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
|
<a-button type="primary" v-auth="actionAuth.exportXls" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
|
||||||
<a-button type="primary" v-auth="actionAuth.exportXls" preIcon="ant-design:download-outlined" @click="handleDownloadTemplate"
|
<a-button type="primary" v-auth="actionAuth.importExcel" preIcon="ant-design:import-outlined" @click="openImportModal">导入</a-button>
|
||||||
>下载模板</a-button
|
|
||||||
>
|
|
||||||
<j-upload-button type="primary" v-auth="actionAuth.importExcel" preIcon="ant-design:check-outlined" @click="handleCheckExcel"
|
|
||||||
>模板校验</j-upload-button
|
|
||||||
>
|
|
||||||
<j-upload-button type="primary" v-auth="actionAuth.importExcel" preIcon="ant-design:import-outlined" @click="handleImportByTemplate"
|
|
||||||
>模板导入</j-upload-button
|
|
||||||
>
|
|
||||||
<a-button
|
<a-button
|
||||||
v-if="selectedRowKeys.length > 0"
|
v-if="selectedRowKeys.length > 0"
|
||||||
v-auth="actionAuth.deleteBatch"
|
v-auth="actionAuth.deleteBatch"
|
||||||
@@ -140,6 +132,7 @@
|
|||||||
:loading="getFeedbackTableLoading(record)"
|
:loading="getFeedbackTableLoading(record)"
|
||||||
:pagination="false"
|
:pagination="false"
|
||||||
:scroll="{ x: 1400 }"
|
:scroll="{ x: 1400 }"
|
||||||
|
:rowClassName="feedbackStatusRowClassName"
|
||||||
>
|
>
|
||||||
<template #feedbackAttachments="{ record: feedbackRecord }">
|
<template #feedbackAttachments="{ record: feedbackRecord }">
|
||||||
<SUploadFile
|
<SUploadFile
|
||||||
@@ -164,6 +157,33 @@
|
|||||||
<BpmPictureModal @register="registerBpmModal" />
|
<BpmPictureModal @register="registerBpmModal" />
|
||||||
<FlowScheduleStartModalForBG ref="flowScheduleModalRef" @confirm="handleFlowScheduleConfirm" />
|
<FlowScheduleStartModalForBG ref="flowScheduleModalRef" @confirm="handleFlowScheduleConfirm" />
|
||||||
<BusinessProcessListModal ref="businessProcessListModalRef" />
|
<BusinessProcessListModal ref="businessProcessListModalRef" />
|
||||||
|
<!-- 导入弹窗:下载模板 / 模板校验 / 模板导入 -->
|
||||||
|
<a-modal v-model:open="importModalVisible" title="导入数据" :footer="null" :width="460" centered destroyOnClose>
|
||||||
|
<div class="import-action-list">
|
||||||
|
<div v-auth="actionAuth.exportXls" class="import-action-card" @click="handleDownloadTemplateFromModal">
|
||||||
|
<Icon icon="ant-design:download-outlined" class="import-action-icon" />
|
||||||
|
<div class="import-action-body">
|
||||||
|
<div class="import-action-title">下载模板</div>
|
||||||
|
<div class="import-action-desc">下载标准导入模板,按模板格式填写数据</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-auth="actionAuth.importExcel" class="import-action-card" @click="chooseFile('check')">
|
||||||
|
<Icon icon="ant-design:check-outlined" class="import-action-icon" />
|
||||||
|
<div class="import-action-body">
|
||||||
|
<div class="import-action-title">模板校验</div>
|
||||||
|
<div class="import-action-desc">校验所选文件是否符合导入规范,提前发现错误</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-auth="actionAuth.importExcel" class="import-action-card" @click="chooseFile('import')">
|
||||||
|
<Icon icon="ant-design:import-outlined" class="import-action-icon" />
|
||||||
|
<div class="import-action-body">
|
||||||
|
<div class="import-action-title">模板导入</div>
|
||||||
|
<div class="import-action-desc">导入校验通过的模板数据到当前台账</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<input ref="fileInputRef" type="file" accept=".xls,.xlsx" style="display: none" @change="onFileSelected" />
|
||||||
|
</a-modal>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -188,6 +208,7 @@
|
|||||||
import SUploadFile from '/@/components/semri/fileComponent/SUploadFile.vue';
|
import SUploadFile from '/@/components/semri/fileComponent/SUploadFile.vue';
|
||||||
import { columns, superQuerySchema, bgPartymatterFeedbackColumns } from './BgPartymatter.data';
|
import { columns, superQuerySchema, bgPartymatterFeedbackColumns } from './BgPartymatter.data';
|
||||||
import { list, statusStats, deleteOne, batchDelete, getImportUrl, getExportXlsByEasyExcelUrl, getImportExcelByEasyExcelUrl, getCheckExcelByEasyExcelUrl, getExportXlsHeadersUrl, bgPartymatterFeedbackList, saveOrUpdate } from './BgPartymatter.api';
|
import { list, statusStats, deleteOne, batchDelete, getImportUrl, getExportXlsByEasyExcelUrl, getImportExcelByEasyExcelUrl, getCheckExcelByEasyExcelUrl, getExportXlsHeadersUrl, bgPartymatterFeedbackList, saveOrUpdate } from './BgPartymatter.api';
|
||||||
|
import { feedbackStatusRowClassName } from '/@/views/bg/utils/feedbackStatusRender';
|
||||||
import { defHttp } from '/@/utils/http/axios';
|
import { defHttp } from '/@/utils/http/axios';
|
||||||
import { showImportValidationErrors } from '/@/utils/helper/importError';
|
import { showImportValidationErrors } from '/@/utils/helper/importError';
|
||||||
import { useMessage } from '/@/hooks/web/useMessage';
|
import { useMessage } from '/@/hooks/web/useMessage';
|
||||||
@@ -723,6 +744,43 @@
|
|||||||
sm: 20,
|
sm: 20,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ================= 导入弹窗(下载模板 / 模板校验 / 模板导入) =================
|
||||||
|
const importModalVisible = ref(false);
|
||||||
|
const fileInputRef = ref();
|
||||||
|
const pendingAction = ref<'check' | 'import' | null>(null);
|
||||||
|
|
||||||
|
function openImportModal() {
|
||||||
|
importModalVisible.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDownloadTemplateFromModal() {
|
||||||
|
importModalVisible.value = false;
|
||||||
|
handleDownloadTemplate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 触发隐藏文件选择框,记录当前动作(校验 or 导入) */
|
||||||
|
function chooseFile(action: 'check' | 'import') {
|
||||||
|
pendingAction.value = action;
|
||||||
|
fileInputRef.value?.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 文件选择后按动作分发到原校验/导入逻辑 */
|
||||||
|
function onFileSelected(e) {
|
||||||
|
const file = e.target.files?.[0];
|
||||||
|
e.target.value = '';
|
||||||
|
if (!file) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const action = pendingAction.value;
|
||||||
|
pendingAction.value = null;
|
||||||
|
importModalVisible.value = false;
|
||||||
|
if (action === 'check') {
|
||||||
|
void handleCheckExcel({ file });
|
||||||
|
} else if (action === 'import') {
|
||||||
|
void handleImportByTemplate({ file });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function handleDownloadTemplate() {
|
async function handleDownloadTemplate() {
|
||||||
const data = await defHttp.get({ url: getExportXlsHeadersUrl, params: { meetingType: props.meetingType }, responseType: 'blob', timeout: 60000 }, { isTransformResponse: false });
|
const data = await defHttp.get({ url: getExportXlsHeadersUrl, params: { meetingType: props.meetingType }, responseType: 'blob', timeout: 60000 }, { isTransformResponse: false });
|
||||||
if (!data) {
|
if (!data) {
|
||||||
@@ -961,4 +1019,48 @@
|
|||||||
.status-row-3 td {
|
.status-row-3 td {
|
||||||
background: #f6ffed !important;
|
background: #f6ffed !important;
|
||||||
}
|
}
|
||||||
|
/* 导入弹窗操作卡片 */
|
||||||
|
.import-action-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 4px 8px 8px;
|
||||||
|
}
|
||||||
|
.import-action-card {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 14px;
|
||||||
|
padding: 14px 16px;
|
||||||
|
border: 1px solid #e5e6eb;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #fff;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
.import-action-card:hover {
|
||||||
|
border-color: #1677ff;
|
||||||
|
background: #f0f7ff;
|
||||||
|
box-shadow: 0 2px 8px rgba(22, 119, 255, 0.15);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
.import-action-icon {
|
||||||
|
font-size: 26px;
|
||||||
|
color: #1677ff;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.import-action-body {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.import-action-title {
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1f2329;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
.import-action-desc {
|
||||||
|
margin-top: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #8a919f;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -198,6 +198,7 @@
|
|||||||
</a-space>
|
</a-space>
|
||||||
</div>
|
</div>
|
||||||
<a-table
|
<a-table
|
||||||
|
class="feedback-table"
|
||||||
v-if="showFeedbackDetail"
|
v-if="showFeedbackDetail"
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
size="small"
|
size="small"
|
||||||
@@ -206,6 +207,7 @@
|
|||||||
:data-source="feedbackList"
|
:data-source="feedbackList"
|
||||||
:pagination="false"
|
:pagination="false"
|
||||||
:scroll="{ x: feedbackTableScrollX }"
|
:scroll="{ x: feedbackTableScrollX }"
|
||||||
|
:rowClassName="feedbackStatusRowClassName"
|
||||||
>
|
>
|
||||||
<template #bodyCell="{ column, record }">
|
<template #bodyCell="{ column, record }">
|
||||||
<template v-if="column.key === 'attachments'">
|
<template v-if="column.key === 'attachments'">
|
||||||
@@ -293,6 +295,7 @@
|
|||||||
import JSelectDept from '/@/components/Form/src/jeecg/components/JSelectDept.vue';
|
import JSelectDept from '/@/components/Form/src/jeecg/components/JSelectDept.vue';
|
||||||
import Icon from '/@/components/Icon/index';
|
import Icon from '/@/components/Icon/index';
|
||||||
import { bgPartymatterFeedbackDelete, bgPartymatterFeedbackList, saveBpmForm, setBpmVariableLocal } from '../BgPartymatter.api';
|
import { bgPartymatterFeedbackDelete, bgPartymatterFeedbackList, saveBpmForm, setBpmVariableLocal } from '../BgPartymatter.api';
|
||||||
|
import { feedbackStatusRowClassName } from '/@/views/bg/utils/feedbackStatusRender';
|
||||||
import { usePermission } from '/@/hooks/web/usePermission';
|
import { usePermission } from '/@/hooks/web/usePermission';
|
||||||
import TaskHandleInnerContent from '/@/views/super/bpm/process/personalOffice/myHandleTask/content/TaskHandleInnerContent.vue';
|
import TaskHandleInnerContent from '/@/views/super/bpm/process/personalOffice/myHandleTask/content/TaskHandleInnerContent.vue';
|
||||||
import JDictSelectTag from '../../../../components/Form/src/jeecg/components/JDictSelectTag.vue';
|
import JDictSelectTag from '../../../../components/Form/src/jeecg/components/JDictSelectTag.vue';
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<a-modal
|
<a-modal
|
||||||
v-model:open="visible"
|
v-model:open="visible"
|
||||||
:title="title"
|
|
||||||
:width="width"
|
:width="width"
|
||||||
centered
|
:top="80"
|
||||||
:footer="null"
|
:footer="null"
|
||||||
:maskClosable="true"
|
:maskClosable="true"
|
||||||
:bodyStyle="{ maxHeight: '70vh', overflowY: 'auto', padding: '16px 24px 20px' }"
|
:bodyStyle="{ maxHeight: '70vh', overflowY: 'auto', padding: '4px 24px 20px' }"
|
||||||
wrapClassName="partymatter-detail-modal"
|
wrapClassName="partymatter-detail-modal"
|
||||||
destroyOnClose
|
destroyOnClose
|
||||||
>
|
>
|
||||||
@@ -100,6 +99,7 @@
|
|||||||
<div class="feedback-summary-desc">当前事项的责任部门办理情况记录</div>
|
<div class="feedback-summary-desc">当前事项的责任部门办理情况记录</div>
|
||||||
</div>
|
</div>
|
||||||
<a-table
|
<a-table
|
||||||
|
class="feedback-table"
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
size="small"
|
size="small"
|
||||||
bordered
|
bordered
|
||||||
@@ -108,6 +108,7 @@
|
|||||||
:pagination="false"
|
:pagination="false"
|
||||||
:scroll="{ x: 1600 }"
|
:scroll="{ x: 1600 }"
|
||||||
:loading="feedbackLoading"
|
:loading="feedbackLoading"
|
||||||
|
:rowClassName="feedbackStatusRowClassName"
|
||||||
>
|
>
|
||||||
<template #bodyCell="{ column, record: feedbackRecord }">
|
<template #bodyCell="{ column, record: feedbackRecord }">
|
||||||
<template v-if="column.key === 'attachments'">
|
<template v-if="column.key === 'attachments'">
|
||||||
@@ -132,6 +133,7 @@
|
|||||||
import Icon from '/@/components/Icon/index';
|
import Icon from '/@/components/Icon/index';
|
||||||
import SUploadFile from '/@/components/semri/fileComponent/SUploadFile.vue';
|
import SUploadFile from '/@/components/semri/fileComponent/SUploadFile.vue';
|
||||||
import { bgPartymatterFeedbackList } from '../BgPartymatter.api';
|
import { bgPartymatterFeedbackList } from '../BgPartymatter.api';
|
||||||
|
import { feedbackStatusRowClassName } from '/@/views/bg/utils/feedbackStatusRender';
|
||||||
import { bgPartymatterFeedbackColumns } from '../BgPartymatter.data';
|
import { bgPartymatterFeedbackColumns } from '../BgPartymatter.data';
|
||||||
import type { BasicColumn } from '/@/components/Table';
|
import type { BasicColumn } from '/@/components/Table';
|
||||||
import { countFeedbackStatus } from '/@/views/bg/utils/feedbackStatusCount';
|
import { countFeedbackStatus } from '/@/views/bg/utils/feedbackStatusCount';
|
||||||
@@ -146,7 +148,6 @@
|
|||||||
);
|
);
|
||||||
|
|
||||||
const visible = ref(false);
|
const visible = ref(false);
|
||||||
const title = ref('详情');
|
|
||||||
const width = 1100;
|
const width = 1100;
|
||||||
const activeKey = ref('item');
|
const activeKey = ref('item');
|
||||||
const record = ref<Recordable>({});
|
const record = ref<Recordable>({});
|
||||||
@@ -274,7 +275,6 @@
|
|||||||
|
|
||||||
function open(currentRecord: Recordable) {
|
function open(currentRecord: Recordable) {
|
||||||
record.value = currentRecord || {};
|
record.value = currentRecord || {};
|
||||||
title.value = meetingPresentation.value.detailTitle;
|
|
||||||
activeKey.value = 'item';
|
activeKey.value = 'item';
|
||||||
visible.value = true;
|
visible.value = true;
|
||||||
if (currentRecord?.id !== undefined && currentRecord?.id !== null && currentRecord?.id !== '') {
|
if (currentRecord?.id !== undefined && currentRecord?.id !== null && currentRecord?.id !== '') {
|
||||||
|
|||||||
@@ -103,19 +103,7 @@
|
|||||||
<a-button type="primary" v-auth="'bqtakepulse:bg_takepulse:exportXls'" preIcon="ant-design:export-outlined" @click="onExportXls">
|
<a-button type="primary" v-auth="'bqtakepulse:bg_takepulse:exportXls'" preIcon="ant-design:export-outlined" @click="onExportXls">
|
||||||
导出</a-button
|
导出</a-button
|
||||||
>
|
>
|
||||||
<a-button type="primary" v-auth="'bqtakepulse:bg_takepulse:exportXls'" preIcon="ant-design:download-outlined" @click="handleDownloadTemplate"
|
<a-button type="primary" v-auth="'bqtakepulse:bg_takepulse:importExcel'" preIcon="ant-design:import-outlined" @click="openImportModal">导入</a-button>
|
||||||
>下载模板</a-button
|
|
||||||
>
|
|
||||||
<j-upload-button type="primary" v-auth="'bqtakepulse:bg_takepulse:importExcel'" preIcon="ant-design:check-outlined" @click="handleCheckExcel"
|
|
||||||
>模板校验</j-upload-button
|
|
||||||
>
|
|
||||||
<j-upload-button
|
|
||||||
type="primary"
|
|
||||||
v-auth="'bqtakepulse:bg_takepulse:importExcel'"
|
|
||||||
preIcon="ant-design:import-outlined"
|
|
||||||
@click="handleImportByTemplate"
|
|
||||||
>模板导入</j-upload-button
|
|
||||||
>
|
|
||||||
<a-button v-if="selectedRowKeys.length > 0" v-auth="'bqtakepulse:bg_takepulse:deleteBatch'" danger @click="batchHandleDelete">
|
<a-button v-if="selectedRowKeys.length > 0" v-auth="'bqtakepulse:bg_takepulse:deleteBatch'" danger @click="batchHandleDelete">
|
||||||
<Icon icon="ant-design:delete-outlined"></Icon>
|
<Icon icon="ant-design:delete-outlined"></Icon>
|
||||||
删除
|
删除
|
||||||
@@ -178,6 +166,7 @@
|
|||||||
:loading="getFeedbackTableLoading(record)"
|
:loading="getFeedbackTableLoading(record)"
|
||||||
:pagination="false"
|
:pagination="false"
|
||||||
:scroll="{ x: 1780 }"
|
:scroll="{ x: 1780 }"
|
||||||
|
:rowClassName="feedbackStatusRowClassName"
|
||||||
>
|
>
|
||||||
<template #feedbackAttachments="{ record: feedbackRecord }">
|
<template #feedbackAttachments="{ record: feedbackRecord }">
|
||||||
<SUploadFile
|
<SUploadFile
|
||||||
@@ -201,6 +190,33 @@
|
|||||||
<BpmPictureModal @register="registerBpmModal" />
|
<BpmPictureModal @register="registerBpmModal" />
|
||||||
<FlowScheduleStartModalForBG ref="flowScheduleModalRef" @confirm="handleFlowScheduleConfirm" />
|
<FlowScheduleStartModalForBG ref="flowScheduleModalRef" @confirm="handleFlowScheduleConfirm" />
|
||||||
<BusinessProcessListModal ref="businessProcessListModalRef" />
|
<BusinessProcessListModal ref="businessProcessListModalRef" />
|
||||||
|
<!-- 导入弹窗:下载模板 / 模板校验 / 模板导入 -->
|
||||||
|
<a-modal v-model:open="importModalVisible" title="导入数据" :footer="null" :width="460" centered destroyOnClose>
|
||||||
|
<div class="import-action-list">
|
||||||
|
<div v-auth="'bqtakepulse:bg_takepulse:exportXls'" class="import-action-card" @click="handleDownloadTemplateFromModal">
|
||||||
|
<Icon icon="ant-design:download-outlined" class="import-action-icon" />
|
||||||
|
<div class="import-action-body">
|
||||||
|
<div class="import-action-title">下载模板</div>
|
||||||
|
<div class="import-action-desc">下载标准导入模板,按模板格式填写数据</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-auth="'bqtakepulse:bg_takepulse:importExcel'" class="import-action-card" @click="chooseFile('check')">
|
||||||
|
<Icon icon="ant-design:check-outlined" class="import-action-icon" />
|
||||||
|
<div class="import-action-body">
|
||||||
|
<div class="import-action-title">模板校验</div>
|
||||||
|
<div class="import-action-desc">校验所选文件是否符合导入规范,提前发现错误</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-auth="'bqtakepulse:bg_takepulse:importExcel'" class="import-action-card" @click="chooseFile('import')">
|
||||||
|
<Icon icon="ant-design:import-outlined" class="import-action-icon" />
|
||||||
|
<div class="import-action-body">
|
||||||
|
<div class="import-action-title">模板导入</div>
|
||||||
|
<div class="import-action-desc">导入校验通过的模板数据到当前台账</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<input ref="fileInputRef" type="file" accept=".xls,.xlsx" style="display: none" @change="onFileSelected" />
|
||||||
|
</a-modal>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -229,6 +245,7 @@
|
|||||||
import { showImportValidationErrors } from '/@/utils/helper/importError';
|
import { showImportValidationErrors } from '/@/utils/helper/importError';
|
||||||
import { useMessage } from '/@/hooks/web/useMessage';
|
import { useMessage } from '/@/hooks/web/useMessage';
|
||||||
import { countFeedbackStatus } from '/@/views/bg/utils/feedbackStatusCount';
|
import { countFeedbackStatus } from '/@/views/bg/utils/feedbackStatusCount';
|
||||||
|
import { feedbackStatusRowClassName } from '/@/views/bg/utils/feedbackStatusRender';
|
||||||
import { useDebounceFn } from '@vueuse/core';
|
import { useDebounceFn } from '@vueuse/core';
|
||||||
import SzSelectDept from '/@/components/Form/src/jeecg/components/SzSelectDept.vue';
|
import SzSelectDept from '/@/components/Form/src/jeecg/components/SzSelectDept.vue';
|
||||||
import JInput from '/@/components/Form/src/jeecg/components/JInput.vue';
|
import JInput from '/@/components/Form/src/jeecg/components/JInput.vue';
|
||||||
@@ -732,6 +749,43 @@
|
|||||||
/**
|
/**
|
||||||
* 重置
|
* 重置
|
||||||
*/
|
*/
|
||||||
|
// ================= 导入弹窗(下载模板 / 模板校验 / 模板导入) =================
|
||||||
|
const importModalVisible = ref(false);
|
||||||
|
const fileInputRef = ref();
|
||||||
|
const pendingAction = ref<'check' | 'import' | null>(null);
|
||||||
|
|
||||||
|
function openImportModal() {
|
||||||
|
importModalVisible.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDownloadTemplateFromModal() {
|
||||||
|
importModalVisible.value = false;
|
||||||
|
handleDownloadTemplate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 触发隐藏文件选择框,记录当前动作(校验 or 导入) */
|
||||||
|
function chooseFile(action: 'check' | 'import') {
|
||||||
|
pendingAction.value = action;
|
||||||
|
fileInputRef.value?.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 文件选择后按动作分发到原校验/导入逻辑 */
|
||||||
|
function onFileSelected(e) {
|
||||||
|
const file = e.target.files?.[0];
|
||||||
|
e.target.value = '';
|
||||||
|
if (!file) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const action = pendingAction.value;
|
||||||
|
pendingAction.value = null;
|
||||||
|
importModalVisible.value = false;
|
||||||
|
if (action === 'check') {
|
||||||
|
void handleCheckExcel({ file });
|
||||||
|
} else if (action === 'import') {
|
||||||
|
void handleImportByTemplate({ file });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function handleDownloadTemplate() {
|
async function handleDownloadTemplate() {
|
||||||
const data = await defHttp.get({ url: getExportXlsHeadersUrl, params: {}, responseType: 'blob', timeout: 60000 }, { isTransformResponse: false });
|
const data = await defHttp.get({ url: getExportXlsHeadersUrl, params: {}, responseType: 'blob', timeout: 60000 }, { isTransformResponse: false });
|
||||||
if (!data) {
|
if (!data) {
|
||||||
@@ -979,4 +1033,48 @@
|
|||||||
.status-row-3 td {
|
.status-row-3 td {
|
||||||
background: #f6ffed !important;
|
background: #f6ffed !important;
|
||||||
}
|
}
|
||||||
|
/* 导入弹窗操作卡片 */
|
||||||
|
.import-action-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 4px 8px 8px;
|
||||||
|
}
|
||||||
|
.import-action-card {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 14px;
|
||||||
|
padding: 14px 16px;
|
||||||
|
border: 1px solid #e5e6eb;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #fff;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
.import-action-card:hover {
|
||||||
|
border-color: #1677ff;
|
||||||
|
background: #f0f7ff;
|
||||||
|
box-shadow: 0 2px 8px rgba(22, 119, 255, 0.15);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
.import-action-icon {
|
||||||
|
font-size: 26px;
|
||||||
|
color: #1677ff;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.import-action-body {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.import-action-title {
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1f2329;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
.import-action-desc {
|
||||||
|
margin-top: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #8a919f;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -223,6 +223,7 @@
|
|||||||
</a-space>
|
</a-space>
|
||||||
</div>
|
</div>
|
||||||
<a-table
|
<a-table
|
||||||
|
class="feedback-table"
|
||||||
v-if="showFeedbackDetail"
|
v-if="showFeedbackDetail"
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
size="small"
|
size="small"
|
||||||
@@ -231,6 +232,7 @@
|
|||||||
:data-source="feedbackList"
|
:data-source="feedbackList"
|
||||||
:pagination="false"
|
:pagination="false"
|
||||||
:scroll="{ x: feedbackTableScrollX }"
|
:scroll="{ x: feedbackTableScrollX }"
|
||||||
|
:rowClassName="feedbackStatusRowClassName"
|
||||||
>
|
>
|
||||||
<template #bodyCell="{ column, record }">
|
<template #bodyCell="{ column, record }">
|
||||||
<template v-if="column.key === 'attachments'">
|
<template v-if="column.key === 'attachments'">
|
||||||
@@ -316,6 +318,7 @@
|
|||||||
import JSwitch from '/@/components/Form/src/jeecg/components/JSwitch.vue';
|
import JSwitch from '/@/components/Form/src/jeecg/components/JSwitch.vue';
|
||||||
import Icon from '/@/components/Icon/index';
|
import Icon from '/@/components/Icon/index';
|
||||||
import { bgTakepulseFeedbackDelete, bgTakepulseFeedbackList, saveBpmForm, setBpmVariableLocal } from '../BgTakepulse.api';
|
import { bgTakepulseFeedbackDelete, bgTakepulseFeedbackList, saveBpmForm, setBpmVariableLocal } from '../BgTakepulse.api';
|
||||||
|
import { feedbackStatusRowClassName } from '/@/views/bg/utils/feedbackStatusRender';
|
||||||
import { usePermission } from '/@/hooks/web/usePermission';
|
import { usePermission } from '/@/hooks/web/usePermission';
|
||||||
import TaskHandleInnerContent from '/@/views/super/bpm/process/personalOffice/myHandleTask/content/TaskHandleInnerContent.vue';
|
import TaskHandleInnerContent from '/@/views/super/bpm/process/personalOffice/myHandleTask/content/TaskHandleInnerContent.vue';
|
||||||
import BgTakepulseBPMFeedbackModal from './BgTakepulseBPMFeedbackModal.vue';
|
import BgTakepulseBPMFeedbackModal from './BgTakepulseBPMFeedbackModal.vue';
|
||||||
|
|||||||
@@ -1,12 +1,11 @@
|
|||||||
<template>
|
<template>
|
||||||
<a-modal
|
<a-modal
|
||||||
v-model:open="visible"
|
v-model:open="visible"
|
||||||
:title="title"
|
|
||||||
:width="width"
|
:width="width"
|
||||||
centered
|
:top="80"
|
||||||
:footer="null"
|
:footer="null"
|
||||||
:maskClosable="true"
|
:maskClosable="true"
|
||||||
:bodyStyle="{ maxHeight: '70vh', overflowY: 'auto', padding: '16px 24px 20px' }"
|
:bodyStyle="{ maxHeight: '70vh', overflowY: 'auto', padding: '4px 24px 20px' }"
|
||||||
wrapClassName="takepulse-detail-modal"
|
wrapClassName="takepulse-detail-modal"
|
||||||
destroyOnClose
|
destroyOnClose
|
||||||
>
|
>
|
||||||
@@ -115,6 +114,7 @@
|
|||||||
<div class="feedback-summary-desc">当前事项的责任部门办理情况记录</div>
|
<div class="feedback-summary-desc">当前事项的责任部门办理情况记录</div>
|
||||||
</div>
|
</div>
|
||||||
<a-table
|
<a-table
|
||||||
|
class="feedback-table"
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
size="small"
|
size="small"
|
||||||
bordered
|
bordered
|
||||||
@@ -123,6 +123,7 @@
|
|||||||
:pagination="false"
|
:pagination="false"
|
||||||
:scroll="{ x: 1800 }"
|
:scroll="{ x: 1800 }"
|
||||||
:loading="feedbackLoading"
|
:loading="feedbackLoading"
|
||||||
|
:rowClassName="feedbackStatusRowClassName"
|
||||||
>
|
>
|
||||||
<template #bodyCell="{ column, record: feedbackRecord }">
|
<template #bodyCell="{ column, record: feedbackRecord }">
|
||||||
<template v-if="column.key === 'attachments'">
|
<template v-if="column.key === 'attachments'">
|
||||||
@@ -149,9 +150,9 @@
|
|||||||
import { bgTakepulseFeedbackList } from '../BgTakepulse.api';
|
import { bgTakepulseFeedbackList } from '../BgTakepulse.api';
|
||||||
import type { BasicColumn } from '/@/components/Table';
|
import type { BasicColumn } from '/@/components/Table';
|
||||||
import { countFeedbackStatus } from '/@/views/bg/utils/feedbackStatusCount';
|
import { countFeedbackStatus } from '/@/views/bg/utils/feedbackStatusCount';
|
||||||
|
import { feedbackStatusRowClassName } from '/@/views/bg/utils/feedbackStatusRender';
|
||||||
|
|
||||||
const visible = ref(false);
|
const visible = ref(false);
|
||||||
const title = ref('详情');
|
|
||||||
const width = 1100;
|
const width = 1100;
|
||||||
const activeKey = ref('item');
|
const activeKey = ref('item');
|
||||||
const record = ref<Recordable>({});
|
const record = ref<Recordable>({});
|
||||||
@@ -300,7 +301,6 @@
|
|||||||
|
|
||||||
function open(currentRecord: Recordable) {
|
function open(currentRecord: Recordable) {
|
||||||
record.value = currentRecord || {};
|
record.value = currentRecord || {};
|
||||||
title.value = '把把脉详情';
|
|
||||||
activeKey.value = 'item';
|
activeKey.value = 'item';
|
||||||
visible.value = true;
|
visible.value = true;
|
||||||
if (currentRecord?.id !== undefined && currentRecord?.id !== null && currentRecord?.id !== '') {
|
if (currentRecord?.id !== undefined && currentRecord?.id !== null && currentRecord?.id !== '') {
|
||||||
|
|||||||
@@ -99,9 +99,7 @@
|
|||||||
<template #tableTitle>
|
<template #tableTitle>
|
||||||
<a-button type="primary" v-auth="actionAuth.add" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增</a-button>
|
<a-button type="primary" v-auth="actionAuth.add" @click="handleAdd" preIcon="ant-design:plus-outlined"> 新增</a-button>
|
||||||
<a-button type="primary" v-auth="actionAuth.exportXls" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
|
<a-button type="primary" v-auth="actionAuth.exportXls" preIcon="ant-design:export-outlined" @click="onExportXls"> 导出</a-button>
|
||||||
<a-button type="primary" v-auth="actionAuth.exportXls" preIcon="ant-design:download-outlined" @click="handleDownloadTemplate">下载模板</a-button>
|
<a-button type="primary" v-auth="actionAuth.importExcel" preIcon="ant-design:import-outlined" @click="openImportModal">导入</a-button>
|
||||||
<j-upload-button type="primary" v-auth="actionAuth.importExcel" preIcon="ant-design:check-outlined" @click="handleCheckExcel">模板校验</j-upload-button>
|
|
||||||
<j-upload-button type="primary" v-auth="actionAuth.importExcel" preIcon="ant-design:import-outlined" @click="handleImportByTemplate">模板导入</j-upload-button>
|
|
||||||
<a-button
|
<a-button
|
||||||
v-if="selectedRowKeys.length > 0"
|
v-if="selectedRowKeys.length > 0"
|
||||||
v-auth="actionAuth.deleteBatch"
|
v-auth="actionAuth.deleteBatch"
|
||||||
@@ -174,6 +172,33 @@
|
|||||||
<FixedContact20260730Modal ref="registerModal" @success="handleSuccess" />
|
<FixedContact20260730Modal ref="registerModal" @success="handleSuccess" />
|
||||||
<FlowScheduleStartModalForBG ref="flowScheduleModalRef" @confirm="handleFlowScheduleConfirm" />
|
<FlowScheduleStartModalForBG ref="flowScheduleModalRef" @confirm="handleFlowScheduleConfirm" />
|
||||||
<BusinessProcessListModal ref="businessProcessListModalRef" />
|
<BusinessProcessListModal ref="businessProcessListModalRef" />
|
||||||
|
<!-- 导入弹窗:下载模板 / 模板校验 / 模板导入 -->
|
||||||
|
<a-modal v-model:open="importModalVisible" title="导入数据" :footer="null" :width="460" centered destroyOnClose>
|
||||||
|
<div class="import-action-list">
|
||||||
|
<div v-auth="actionAuth.exportXls" class="import-action-card" @click="handleDownloadTemplateFromModal">
|
||||||
|
<Icon icon="ant-design:download-outlined" class="import-action-icon" />
|
||||||
|
<div class="import-action-body">
|
||||||
|
<div class="import-action-title">下载模板</div>
|
||||||
|
<div class="import-action-desc">下载标准导入模板,按模板格式填写数据</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-auth="actionAuth.importExcel" class="import-action-card" @click="chooseFile('check')">
|
||||||
|
<Icon icon="ant-design:check-outlined" class="import-action-icon" />
|
||||||
|
<div class="import-action-body">
|
||||||
|
<div class="import-action-title">模板校验</div>
|
||||||
|
<div class="import-action-desc">校验所选文件是否符合导入规范,提前发现错误</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-auth="actionAuth.importExcel" class="import-action-card" @click="chooseFile('import')">
|
||||||
|
<Icon icon="ant-design:import-outlined" class="import-action-icon" />
|
||||||
|
<div class="import-action-body">
|
||||||
|
<div class="import-action-title">模板导入</div>
|
||||||
|
<div class="import-action-desc">导入校验通过的模板数据到当前台账</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<input ref="fileInputRef" type="file" accept=".xls,.xlsx" style="display: none" @change="onFileSelected" />
|
||||||
|
</a-modal>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -635,6 +660,43 @@ import { showImportValidationErrors } from '/@/utils/helper/importError';
|
|||||||
sm: { span: 14 },
|
sm: { span: 14 },
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ================= 导入弹窗(下载模板 / 模板校验 / 模板导入) =================
|
||||||
|
const importModalVisible = ref(false);
|
||||||
|
const fileInputRef = ref();
|
||||||
|
const pendingAction = ref<'check' | 'import' | null>(null);
|
||||||
|
|
||||||
|
function openImportModal() {
|
||||||
|
importModalVisible.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDownloadTemplateFromModal() {
|
||||||
|
importModalVisible.value = false;
|
||||||
|
handleDownloadTemplate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 触发隐藏文件选择框,记录当前动作(校验 or 导入) */
|
||||||
|
function chooseFile(action: 'check' | 'import') {
|
||||||
|
pendingAction.value = action;
|
||||||
|
fileInputRef.value?.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 文件选择后按动作分发到原校验/导入逻辑 */
|
||||||
|
function onFileSelected(e) {
|
||||||
|
const file = e.target.files?.[0];
|
||||||
|
e.target.value = '';
|
||||||
|
if (!file) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const action = pendingAction.value;
|
||||||
|
pendingAction.value = null;
|
||||||
|
importModalVisible.value = false;
|
||||||
|
if (action === 'check') {
|
||||||
|
void handleCheckExcel({ file });
|
||||||
|
} else if (action === 'import') {
|
||||||
|
void handleImportByTemplate({ file });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function handleDownloadTemplate() {
|
async function handleDownloadTemplate() {
|
||||||
let data;
|
let data;
|
||||||
try {
|
try {
|
||||||
@@ -841,3 +903,50 @@ import { showImportValidationErrors } from '/@/utils/helper/importError';
|
|||||||
background: #f6ffed !important;
|
background: #f6ffed !important;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
/* 导入弹窗操作卡片 */
|
||||||
|
.import-action-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 4px 8px 8px;
|
||||||
|
}
|
||||||
|
.import-action-card {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 14px;
|
||||||
|
padding: 14px 16px;
|
||||||
|
border: 1px solid #e5e6eb;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #fff;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
.import-action-card:hover {
|
||||||
|
border-color: #1677ff;
|
||||||
|
background: #f0f7ff;
|
||||||
|
box-shadow: 0 2px 8px rgba(22, 119, 255, 0.15);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
.import-action-icon {
|
||||||
|
font-size: 26px;
|
||||||
|
color: #1677ff;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.import-action-body {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.import-action-title {
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1f2329;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
.import-action-desc {
|
||||||
|
margin-top: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #8a919f;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -0,0 +1,37 @@
|
|||||||
|
/**
|
||||||
|
* 反馈状态颜色工具:与台账颜色统一
|
||||||
|
* 未开始 -> warning(黄) / 进行中·督办中·整改中 -> error(红) / 已完成 -> success(绿)
|
||||||
|
*
|
||||||
|
* 说明:反馈字典(feedback_status)的码值与台账字典(super_status)不一致
|
||||||
|
* (反馈"已完成"码值为 '2',台账为 '3'),因此按显示的字典文本判定状态,
|
||||||
|
* 不依赖状态码,避免码值差异导致颜色失效。
|
||||||
|
*/
|
||||||
|
|
||||||
|
/** 根据字典文本返回状态颜色(warning/error/success) */
|
||||||
|
export function feedbackStatusColorByText(text: string | number | undefined | null): string | undefined {
|
||||||
|
const t = String(text ?? '');
|
||||||
|
if (t.includes('未开始') || t.includes('未完成')) return 'warning';
|
||||||
|
if (t.includes('进行中') || t.includes('督办中') || t.includes('整改中') || t.includes('处理中')) return 'error';
|
||||||
|
if (t.includes('已完成') || t === '完成') return 'success';
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 反馈表格整行背景色 class(表格 :rowClassName 使用)
|
||||||
|
* 自动兼容 bpmStatus / completionStatus 两套字段名,按字典文本判定状态。
|
||||||
|
* 对应全局样式:feedback-status-row-warning/error/success(定义于 /@/design/index.less)
|
||||||
|
*/
|
||||||
|
export function feedbackStatusRowClassName(record: Recordable | undefined | null): string {
|
||||||
|
const text = String(
|
||||||
|
record?.['bpmStatus_dictText'] ??
|
||||||
|
record?.['completionStatus_dictText'] ??
|
||||||
|
record?.['bpmStatus'] ??
|
||||||
|
record?.['completionStatus'] ??
|
||||||
|
'',
|
||||||
|
);
|
||||||
|
const color = feedbackStatusColorByText(text);
|
||||||
|
if (color === 'warning') return 'feedback-status-row-warning';
|
||||||
|
if (color === 'error') return 'feedback-status-row-error';
|
||||||
|
if (color === 'success') return 'feedback-status-row-success';
|
||||||
|
return '';
|
||||||
|
}
|
||||||
@@ -72,15 +72,7 @@
|
|||||||
<!-- <j-upload-button type="primary" v-auth="'bg.xispeak:bg_xi_speak:importExcel'" preIcon="ant-design:import-outlined" @click="onImportXls"-->
|
<!-- <j-upload-button type="primary" v-auth="'bg.xispeak:bg_xi_speak:importExcel'" preIcon="ant-design:import-outlined" @click="onImportXls"-->
|
||||||
<!-- >导入</j-upload-button-->
|
<!-- >导入</j-upload-button-->
|
||||||
<!-- >-->
|
<!-- >-->
|
||||||
<a-button type="primary" v-auth="'bg.xispeak:bg_xi_speak:exportXls'" preIcon="ant-design:download-outlined" @click="handleDownloadTemplate"
|
<a-button type="primary" v-auth="'bg.xispeak:bg_xi_speak:importExcel'" preIcon="ant-design:import-outlined" @click="openImportModal">导入</a-button>
|
||||||
>下载模板</a-button
|
|
||||||
>
|
|
||||||
<j-upload-button type="primary" v-auth="'bg.xispeak:bg_xi_speak:importExcel'" preIcon="ant-design:check-outlined" @click="handleCheckExcel"
|
|
||||||
>模板校验</j-upload-button
|
|
||||||
>
|
|
||||||
<j-upload-button type="primary" v-auth="'bg.xispeak:bg_xi_speak:importExcel'" preIcon="ant-design:import-outlined" @click="handleImportByTemplate"
|
|
||||||
>模板导入</j-upload-button
|
|
||||||
>
|
|
||||||
<a-button
|
<a-button
|
||||||
v-if="selectedRowKeys.length > 0"
|
v-if="selectedRowKeys.length > 0"
|
||||||
v-auth="'bg.xispeak:bg_xi_speak:deleteBatch'"
|
v-auth="'bg.xispeak:bg_xi_speak:deleteBatch'"
|
||||||
@@ -127,6 +119,33 @@
|
|||||||
<FlowScheduleStartModal ref="flowScheduleModalRef" @confirm="handleFlowScheduleConfirm" />
|
<FlowScheduleStartModal ref="flowScheduleModalRef" @confirm="handleFlowScheduleConfirm" />
|
||||||
<FeedbackRightNowModal ref="feedbackRightNowModalRef" @confirm="handleFeedbackRightNowConfirm" />
|
<FeedbackRightNowModal ref="feedbackRightNowModalRef" @confirm="handleFeedbackRightNowConfirm" />
|
||||||
<BusinessProcessListModal ref="businessProcessListModalRef" />
|
<BusinessProcessListModal ref="businessProcessListModalRef" />
|
||||||
|
<!-- 导入弹窗:下载模板 / 模板校验 / 模板导入 -->
|
||||||
|
<a-modal v-model:open="importModalVisible" title="导入数据" :footer="null" :width="460" centered destroyOnClose>
|
||||||
|
<div class="import-action-list">
|
||||||
|
<div v-auth="'bg.xispeak:bg_xi_speak:exportXls'" class="import-action-card" @click="handleDownloadTemplateFromModal">
|
||||||
|
<Icon icon="ant-design:download-outlined" class="import-action-icon" />
|
||||||
|
<div class="import-action-body">
|
||||||
|
<div class="import-action-title">下载模板</div>
|
||||||
|
<div class="import-action-desc">下载标准导入模板,按模板格式填写数据</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-auth="'bg.xispeak:bg_xi_speak:importExcel'" class="import-action-card" @click="chooseFile('check')">
|
||||||
|
<Icon icon="ant-design:check-outlined" class="import-action-icon" />
|
||||||
|
<div class="import-action-body">
|
||||||
|
<div class="import-action-title">模板校验</div>
|
||||||
|
<div class="import-action-desc">校验所选文件是否符合导入规范,提前发现错误</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-auth="'bg.xispeak:bg_xi_speak:importExcel'" class="import-action-card" @click="chooseFile('import')">
|
||||||
|
<Icon icon="ant-design:import-outlined" class="import-action-icon" />
|
||||||
|
<div class="import-action-body">
|
||||||
|
<div class="import-action-title">模板导入</div>
|
||||||
|
<div class="import-action-desc">导入校验通过的模板数据到当前台账</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<input ref="fileInputRef" type="file" accept=".xls,.xlsx" style="display: none" @change="onFileSelected" />
|
||||||
|
</a-modal>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -832,6 +851,43 @@ import { PARTY_COMMITTEE_DEPT_ID, SDW_LEADER_ROLE_ID } from '/@/constant/supervi
|
|||||||
feedbackViewModalRef.value.open(record);
|
feedbackViewModalRef.value.open(record);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ================= 导入弹窗(下载模板 / 模板校验 / 模板导入) =================
|
||||||
|
const importModalVisible = ref(false);
|
||||||
|
const fileInputRef = ref();
|
||||||
|
const pendingAction = ref<'check' | 'import' | null>(null);
|
||||||
|
|
||||||
|
function openImportModal() {
|
||||||
|
importModalVisible.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDownloadTemplateFromModal() {
|
||||||
|
importModalVisible.value = false;
|
||||||
|
handleDownloadTemplate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 触发隐藏文件选择框,记录当前动作(校验 or 导入) */
|
||||||
|
function chooseFile(action: 'check' | 'import') {
|
||||||
|
pendingAction.value = action;
|
||||||
|
fileInputRef.value?.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 文件选择后按动作分发到原校验/导入逻辑 */
|
||||||
|
function onFileSelected(e) {
|
||||||
|
const file = e.target.files?.[0];
|
||||||
|
e.target.value = '';
|
||||||
|
if (!file) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const action = pendingAction.value;
|
||||||
|
pendingAction.value = null;
|
||||||
|
importModalVisible.value = false;
|
||||||
|
if (action === 'check') {
|
||||||
|
void handleCheckExcel({ file });
|
||||||
|
} else if (action === 'import') {
|
||||||
|
void handleImportByTemplate({ file });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function handleDownloadTemplate() {
|
async function handleDownloadTemplate() {
|
||||||
const data = await defHttp.get(
|
const data = await defHttp.get(
|
||||||
{ url: getExportXlsHeadersUrl, params: {}, responseType: 'blob', timeout: 60000 },
|
{ url: getExportXlsHeadersUrl, params: {}, responseType: 'blob', timeout: 60000 },
|
||||||
@@ -994,3 +1050,50 @@ import { PARTY_COMMITTEE_DEPT_ID, SDW_LEADER_ROLE_ID } from '/@/constant/supervi
|
|||||||
background: #f6ffed !important;
|
background: #f6ffed !important;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
/* 导入弹窗操作卡片 */
|
||||||
|
.import-action-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 4px 8px 8px;
|
||||||
|
}
|
||||||
|
.import-action-card {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 14px;
|
||||||
|
padding: 14px 16px;
|
||||||
|
border: 1px solid #e5e6eb;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #fff;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
.import-action-card:hover {
|
||||||
|
border-color: #1677ff;
|
||||||
|
background: #f0f7ff;
|
||||||
|
box-shadow: 0 2px 8px rgba(22, 119, 255, 0.15);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
.import-action-icon {
|
||||||
|
font-size: 26px;
|
||||||
|
color: #1677ff;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.import-action-body {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.import-action-title {
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1f2329;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
.import-action-desc {
|
||||||
|
margin-top: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #8a919f;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -197,6 +197,7 @@
|
|||||||
</a-space>
|
</a-space>
|
||||||
</div>
|
</div>
|
||||||
<a-table
|
<a-table
|
||||||
|
class="feedback-table"
|
||||||
v-if="showFeedbackInfo && showFeedbackDetail"
|
v-if="showFeedbackInfo && showFeedbackDetail"
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
size="small"
|
size="small"
|
||||||
@@ -205,6 +206,7 @@
|
|||||||
:data-source="feedbackList"
|
:data-source="feedbackList"
|
||||||
:pagination="false"
|
:pagination="false"
|
||||||
:scroll="{ x: 1980 }"
|
:scroll="{ x: 1980 }"
|
||||||
|
:rowClassName="feedbackStatusRowClassName"
|
||||||
>
|
>
|
||||||
<template #bodyCell="{ column, record, index }">
|
<template #bodyCell="{ column, record, index }">
|
||||||
<template v-if="column.key === 'seq'">{{ index + 1 }}</template>
|
<template v-if="column.key === 'seq'">{{ index + 1 }}</template>
|
||||||
@@ -286,6 +288,7 @@
|
|||||||
import JDictSelectTag from '/@/components/Form/src/jeecg/components/JDictSelectTag.vue';
|
import JDictSelectTag from '/@/components/Form/src/jeecg/components/JDictSelectTag.vue';
|
||||||
import Icon from '/@/components/Icon/index';
|
import Icon from '/@/components/Icon/index';
|
||||||
import { bgXiSpeakFeedbackList, bgXiSpeakFeedbackDelete, saveBpmForm, saveSubApprove, getProcessVariable } from '../BgXiSpeak.api';
|
import { bgXiSpeakFeedbackList, bgXiSpeakFeedbackDelete, saveBpmForm, saveSubApprove, getProcessVariable } from '../BgXiSpeak.api';
|
||||||
|
import { feedbackStatusRowClassName } from '/@/views/bg/utils/feedbackStatusRender';
|
||||||
import { useSubApproveUserResolver } from '/@/composables/useSubApproveUserResolver';
|
import { useSubApproveUserResolver } from '/@/composables/useSubApproveUserResolver';
|
||||||
import { usePermission } from '/@/hooks/web/usePermission';
|
import { usePermission } from '/@/hooks/web/usePermission';
|
||||||
import TaskHandleInnerContent from '/@/views/super/bpm/process/personalOffice/myHandleTask/content/TaskHandleInnerContent.vue';
|
import TaskHandleInnerContent from '/@/views/super/bpm/process/personalOffice/myHandleTask/content/TaskHandleInnerContent.vue';
|
||||||
|
|||||||
@@ -1,26 +1,60 @@
|
|||||||
<template>
|
<template>
|
||||||
<a-modal
|
<a-modal
|
||||||
v-model:open="visible"
|
v-model:open="visible"
|
||||||
:title="title"
|
|
||||||
:width="width"
|
:width="width"
|
||||||
centered
|
:top="80"
|
||||||
:footer="null"
|
:footer="null"
|
||||||
:maskClosable="true"
|
:maskClosable="true"
|
||||||
:bodyStyle="{ maxHeight: '70vh', overflowY: 'auto', padding: '16px 24px 20px' }"
|
:bodyStyle="{ maxHeight: '70vh', overflowY: 'auto', padding: '4px 24px 20px' }"
|
||||||
|
wrapClassName="xispeak-detail-modal"
|
||||||
destroyOnClose
|
destroyOnClose
|
||||||
>
|
>
|
||||||
<a-tabs v-model:activeKey="activeKey">
|
<a-tabs v-model:activeKey="activeKey">
|
||||||
<a-tab-pane key="item" tab="事项信息">
|
<a-tab-pane key="item" tab="事项信息">
|
||||||
<div class="detail-grid">
|
<div class="matter-detail">
|
||||||
<div
|
<section class="matter-summary">
|
||||||
v-for="field in detailFields"
|
<div class="summary-heading">
|
||||||
:key="field.label"
|
<span class="summary-heading-icon">
|
||||||
class="detail-row"
|
<Icon :icon="summaryIcon" />
|
||||||
:class="{ 'detail-row-full': field.fullWidth, 'detail-row-bold': field.bold }"
|
</span>
|
||||||
>
|
<div class="summary-heading-content">
|
||||||
<span class="detail-label">{{ field.label }}</span>
|
<span class="summary-kicker">{{ summaryKicker }}</span>
|
||||||
<span class="detail-value">{{ field.value }}</span>
|
<h3 class="summary-title">{{ detailSummary.title }}</h3>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="summary-meta">
|
||||||
|
<div class="summary-meta-item">
|
||||||
|
<Icon icon="ant-design:calendar-outlined" />
|
||||||
|
<span class="summary-meta-label">时间</span>
|
||||||
|
<span class="summary-meta-value">{{ detailSummary.time }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="summary-meta-item">
|
||||||
|
<Icon icon="ant-design:lock-outlined" />
|
||||||
|
<span class="summary-meta-label">密级</span>
|
||||||
|
<span class="summary-meta-value">{{ detailSummary.secretLevel }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="summary-tag-group">
|
||||||
|
<a-tag class="summary-tag">截止日期:{{ detailSummary.deadline }}</a-tag>
|
||||||
|
<a-tag class="summary-tag">督办次数:{{ detailSummary.supervisionCount }}</a-tag>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section v-for="group in detailGroups" :key="group.title" class="detail-section">
|
||||||
|
<div class="detail-section-header">
|
||||||
|
<span class="detail-section-icon">
|
||||||
|
<Icon :icon="group.icon" />
|
||||||
|
</span>
|
||||||
|
<h3>{{ group.title }}</h3>
|
||||||
|
</div>
|
||||||
|
<div class="detail-grid">
|
||||||
|
<div v-for="field in group.fields" :key="field.label" class="detail-item" :class="{ 'detail-item-full': field.fullWidth }">
|
||||||
|
<span class="detail-label">{{ field.label }}</span>
|
||||||
|
<span class="detail-value" :class="{ 'detail-value-strong': field.bold }">{{ field.value }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
|
|
||||||
@@ -31,12 +65,15 @@
|
|||||||
<Icon icon="ant-design:message-outlined" />
|
<Icon icon="ant-design:message-outlined" />
|
||||||
</span>
|
</span>
|
||||||
<span>办理情况</span>
|
<span>办理情况</span>
|
||||||
<a-tag class="feedback-summary-tag">共 {{ total }} 条</a-tag>
|
<a-tag class="feedback-summary-tag" color="warning" style="color: #8c6900">未开始 {{ count.notStarted }}</a-tag>
|
||||||
<a-tag class="feedback-summary-tag" color="success">已完成 {{ done }} 条</a-tag>
|
<a-tag class="feedback-summary-tag" color="error">进行中 {{ count.inProgress }}(已逾期 {{ count.overdue }})</a-tag>
|
||||||
|
<a-tag class="feedback-summary-tag" color="success">已完成 {{ count.completed }}</a-tag>
|
||||||
|
<a-tag class="feedback-summary-tag">共 {{ count.total }} 条</a-tag>
|
||||||
</div>
|
</div>
|
||||||
<div class="feedback-summary-desc">当前事项的落实情况反馈记录</div>
|
<div class="feedback-summary-desc">当前事项的落实情况反馈记录</div>
|
||||||
</div>
|
</div>
|
||||||
<a-table
|
<a-table
|
||||||
|
class="feedback-table"
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
size="small"
|
size="small"
|
||||||
bordered
|
bordered
|
||||||
@@ -45,6 +82,7 @@
|
|||||||
:pagination="false"
|
:pagination="false"
|
||||||
:scroll="{ x: 1500 }"
|
:scroll="{ x: 1500 }"
|
||||||
:loading="feedbackLoading"
|
:loading="feedbackLoading"
|
||||||
|
:rowClassName="feedbackStatusRowClassName"
|
||||||
>
|
>
|
||||||
<template #bodyCell="{ column, record: fb }">
|
<template #bodyCell="{ column, record: fb }">
|
||||||
<template v-if="column.key === 'action'">
|
<template v-if="column.key === 'action'">
|
||||||
@@ -74,10 +112,11 @@
|
|||||||
import SUploadFile from '/@/components/semri/fileComponent/SUploadFile.vue';
|
import SUploadFile from '/@/components/semri/fileComponent/SUploadFile.vue';
|
||||||
import BgXiSpeakFeedbackDetailModal from './BgXiSpeakFeedbackDetailModal.vue';
|
import BgXiSpeakFeedbackDetailModal from './BgXiSpeakFeedbackDetailModal.vue';
|
||||||
import { bgXiSpeakFeedbackList } from '../BgXiSpeak.api';
|
import { bgXiSpeakFeedbackList } from '../BgXiSpeak.api';
|
||||||
|
import { feedbackStatusRowClassName } from '/@/views/bg/utils/feedbackStatusRender';
|
||||||
|
import { countFeedbackStatus } from '/@/views/bg/utils/feedbackStatusCount';
|
||||||
import type { BasicColumn } from '/@/components/Table';
|
import type { BasicColumn } from '/@/components/Table';
|
||||||
|
|
||||||
const visible = ref(false);
|
const visible = ref(false);
|
||||||
const title = ref('详情');
|
|
||||||
const width = 1100;
|
const width = 1100;
|
||||||
const activeKey = ref('item');
|
const activeKey = ref('item');
|
||||||
const record = ref<Recordable>({});
|
const record = ref<Recordable>({});
|
||||||
@@ -103,25 +142,59 @@
|
|||||||
return fmt(record[`${key}_dictText`] || record[key]);
|
return fmt(record[`${key}_dictText`] || record[key]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const detailFields = computed(() => {
|
const summaryKicker = '习讲话事项';
|
||||||
|
const summaryIcon = 'ant-design:sound-outlined';
|
||||||
|
|
||||||
|
// 摘要区只承载最常用于快速判断事项状态的信息,详细字段放入下方分组,避免重复平铺。
|
||||||
|
const detailSummary = computed(() => {
|
||||||
|
const r = record.value || {};
|
||||||
|
return {
|
||||||
|
title: fmt(r.speakStatus),
|
||||||
|
time: fmtDate(r.time),
|
||||||
|
secretLevel: dictText(r, 'secretLevel'),
|
||||||
|
deadline: fmtDate(r.deadline),
|
||||||
|
supervisionCount: fmt(r.supervisionCount),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const detailGroups = computed(() => {
|
||||||
const r = record.value || {};
|
const r = record.value || {};
|
||||||
return [
|
return [
|
||||||
{ label: '重要讲话指示批示', value: fmt(r.speakStatus), fullWidth: true, bold: true },
|
{
|
||||||
{ label: '时间', value: fmtDate(r.time), bold: true },
|
title: '批示与传达',
|
||||||
{ label: '企业负责同志批示情况', value: fmt(r.elmComment) },
|
icon: 'ant-design:notification-outlined',
|
||||||
{ label: '学习传达研究部署', value: fmt(r.status) },
|
fields: [
|
||||||
{ label: '密级', value: dictText(r, 'secretLevel') },
|
{ label: '企业负责同志批示情况', value: fmt(r.elmComment) },
|
||||||
{ label: '所党委责任领导', value: dictText(r, 'responsiblePerson'), bold: true },
|
{ label: '学习传达研究部署', value: fmt(r.status) },
|
||||||
{ label: '牵头部门', value: dictText(r, 'implDept'), bold: true },
|
],
|
||||||
{ label: '贯彻落实措施', value: fmt(r.implMeasures) },
|
},
|
||||||
{ label: '会议决策数', value: fmt(r.numberOfResolutions) },
|
{
|
||||||
{ label: '是否需要所党委领导督办', value: ynText(r.needSdwApproval) },
|
title: '责任与落实',
|
||||||
{ label: '所党委领导列表', value: dictText(r, 'sdwLeaderList') },
|
icon: 'ant-design:team-outlined',
|
||||||
{ label: '指定落实措施条数', value: fmt(r.implCount) },
|
fields: [
|
||||||
{ label: '措施已闭环数量', value: fmt(r.closedCount) },
|
{ label: '所党委责任领导', value: dictText(r, 'responsiblePerson'), bold: true },
|
||||||
{ label: '报告反馈情况', value: fmt(r.reportFeedbackStatus) },
|
{ label: '牵头部门', value: dictText(r, 'implDept'), bold: true },
|
||||||
{ label: '截止日期', value: fmtDate(r.deadline) },
|
{ label: '贯彻落实措施', value: fmt(r.implMeasures), fullWidth: true },
|
||||||
{ label: '督办次数', value: fmt(r.supervisionCount) },
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '决策与进展',
|
||||||
|
icon: 'ant-design:fund-outlined',
|
||||||
|
fields: [
|
||||||
|
{ label: '会议决策数', value: fmt(r.numberOfResolutions) },
|
||||||
|
{ label: '指定落实措施条数', value: fmt(r.implCount) },
|
||||||
|
{ label: '措施已闭环数量', value: fmt(r.closedCount) },
|
||||||
|
{ label: '报告反馈情况', value: fmt(r.reportFeedbackStatus) },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '审批设置',
|
||||||
|
icon: 'ant-design:safety-outlined',
|
||||||
|
fields: [
|
||||||
|
{ label: '是否需要所党委领导督办', value: ynText(r.needSdwApproval) },
|
||||||
|
{ label: '所党委领导列表', value: dictText(r, 'sdwLeaderList'), fullWidth: true },
|
||||||
|
],
|
||||||
|
},
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -138,8 +211,7 @@
|
|||||||
{ title: '操作', dataIndex: 'action', key: 'action', align: 'center', width: 90, fixed: 'right' },
|
{ title: '操作', dataIndex: 'action', key: 'action', align: 'center', width: 90, fixed: 'right' },
|
||||||
];
|
];
|
||||||
|
|
||||||
const total = computed(() => feedbackList.value.length);
|
const count = computed(() => countFeedbackStatus(feedbackList.value, 'completionStatus', 'deadline'));
|
||||||
const done = computed(() => feedbackList.value.filter((item) => item.completionStatus_dictText === '已完成').length);
|
|
||||||
|
|
||||||
async function loadFeedback(mainId: string | number) {
|
async function loadFeedback(mainId: string | number) {
|
||||||
feedbackLoading.value = true;
|
feedbackLoading.value = true;
|
||||||
@@ -164,7 +236,6 @@
|
|||||||
|
|
||||||
function open(currentRecord: Recordable) {
|
function open(currentRecord: Recordable) {
|
||||||
record.value = currentRecord || {};
|
record.value = currentRecord || {};
|
||||||
title.value = '详情';
|
|
||||||
activeKey.value = 'item';
|
activeKey.value = 'item';
|
||||||
visible.value = true;
|
visible.value = true;
|
||||||
if (currentRecord?.id !== undefined && currentRecord?.id !== null && currentRecord?.id !== '') {
|
if (currentRecord?.id !== undefined && currentRecord?.id !== null && currentRecord?.id !== '') {
|
||||||
@@ -176,40 +247,265 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.detail-grid {
|
// 弹窗内容会 Teleport 到 body,通过专属类限定公共组件的字号,避免影响其他页面。
|
||||||
display: grid;
|
:global(.xispeak-detail-modal .ant-modal-title) {
|
||||||
grid-template-columns: repeat(2, 1fr);
|
font-size: 18px;
|
||||||
gap: 12px 32px;
|
line-height: 26px;
|
||||||
padding: 4px 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.detail-row {
|
:global(.xispeak-detail-modal .ant-tabs-tab-btn) {
|
||||||
|
font-size: 15px;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.xispeak-detail-modal .ant-table) {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.xispeak-detail-modal .ant-table-thead > tr > th),
|
||||||
|
:global(.xispeak-detail-modal .ant-table-tbody > tr > td) {
|
||||||
|
font-size: 15px;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.matter-detail {
|
||||||
|
padding: 4px 2px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.matter-summary {
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
padding: 20px 22px;
|
||||||
|
background: linear-gradient(135deg, #f2f8ff 0%, #f8fbff 48%, #ffffff 100%);
|
||||||
|
border: 1px solid #d6e8ff;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 4px 14px rgb(22 119 255 / 8%);
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
position: absolute;
|
||||||
|
top: -46px;
|
||||||
|
right: -24px;
|
||||||
|
width: 132px;
|
||||||
|
height: 132px;
|
||||||
|
background: rgb(22 119 255 / 5%);
|
||||||
|
border-radius: 50%;
|
||||||
|
content: '';
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-heading {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-heading-icon {
|
||||||
|
display: inline-flex;
|
||||||
|
flex: 0 0 40px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
color: #1677ff;
|
||||||
|
font-size: 20px;
|
||||||
|
background: #e6f4ff;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-heading-content {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.detail-row-full {
|
.summary-kicker {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
color: #64748b;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-title {
|
||||||
|
margin: 0;
|
||||||
|
color: #172033;
|
||||||
|
font-size: 21px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 30px;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-meta {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 10px 18px;
|
||||||
|
margin-top: 16px;
|
||||||
|
padding-top: 14px;
|
||||||
|
border-top: 1px solid #deebfa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-meta-item {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
min-height: 24px;
|
||||||
|
color: #64748b;
|
||||||
|
|
||||||
|
> :first-child {
|
||||||
|
color: #1677ff;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-meta-label {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-meta-value {
|
||||||
|
color: #1f2937;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-tag-group {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-tag {
|
||||||
|
margin-inline-end: 0;
|
||||||
|
padding: 2px 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 22px;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-section {
|
||||||
|
overflow: hidden;
|
||||||
|
margin-top: 14px;
|
||||||
|
background: #ffffff;
|
||||||
|
border: 1px solid #e7edf4;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-section-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
min-height: 44px;
|
||||||
|
padding: 10px 16px;
|
||||||
|
background: #f8fbff;
|
||||||
|
border-bottom: 1px solid #e7edf4;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin: 0;
|
||||||
|
color: #27364b;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-section-icon {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 26px;
|
||||||
|
height: 26px;
|
||||||
|
color: #1677ff;
|
||||||
|
background: #e6f4ff;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
gap: 1px;
|
||||||
|
background: #edf1f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-item {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 142px minmax(0, 1fr);
|
||||||
|
align-items: start;
|
||||||
|
gap: 12px;
|
||||||
|
min-width: 0;
|
||||||
|
min-height: 54px;
|
||||||
|
padding: 14px 16px;
|
||||||
|
background: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-item-full {
|
||||||
grid-column: 1 / -1;
|
grid-column: 1 / -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.detail-label {
|
.detail-label {
|
||||||
flex: 0 0 150px;
|
color: #7b8798;
|
||||||
color: #8c8c8c;
|
font-size: 14px;
|
||||||
line-height: 22px;
|
line-height: 26px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.detail-value {
|
.detail-value {
|
||||||
flex: 1;
|
min-width: 0;
|
||||||
color: #1f2937;
|
color: #27364b;
|
||||||
line-height: 22px;
|
font-size: 15px;
|
||||||
word-break: break-all;
|
line-height: 26px;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
}
|
}
|
||||||
|
|
||||||
.detail-row-bold .detail-value {
|
.detail-value-strong {
|
||||||
font-weight: 700;
|
color: #172033;
|
||||||
font-size: 15px;
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.matter-summary {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-meta {
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-tag-group {
|
||||||
|
flex-basis: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-grid {
|
||||||
|
grid-template-columns: minmax(0, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-item-full {
|
||||||
|
grid-column: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 576px) {
|
||||||
|
.summary-title {
|
||||||
|
font-size: 19px;
|
||||||
|
line-height: 27px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-meta {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(0, 1fr);
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-item {
|
||||||
|
grid-template-columns: minmax(0, 1fr);
|
||||||
|
gap: 2px;
|
||||||
|
padding: 11px 14px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.feedback-summary-header {
|
.feedback-summary-header {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
cancelText="关闭"
|
cancelText="关闭"
|
||||||
>
|
>
|
||||||
<BasicTable
|
<BasicTable
|
||||||
|
class="feedback-table"
|
||||||
size="small"
|
size="small"
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
:canResize="false"
|
:canResize="false"
|
||||||
@@ -19,6 +20,7 @@
|
|||||||
:columns="columns"
|
:columns="columns"
|
||||||
:dataSource="feedbackTableData"
|
:dataSource="feedbackTableData"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
|
:rowClassName="feedbackStatusRowClassName"
|
||||||
:pagination="false"
|
:pagination="false"
|
||||||
:bordered="false"
|
:bordered="false"
|
||||||
>
|
>
|
||||||
@@ -50,6 +52,7 @@
|
|||||||
import SUploadFile from '/@/components/semri/fileComponent/SUploadFile.vue';
|
import SUploadFile from '/@/components/semri/fileComponent/SUploadFile.vue';
|
||||||
import BgXiSpeakFeedbackDetailModal from './BgXiSpeakFeedbackDetailModal.vue';
|
import BgXiSpeakFeedbackDetailModal from './BgXiSpeakFeedbackDetailModal.vue';
|
||||||
import { bgXiSpeakFeedbackList } from '../BgXiSpeak.api';
|
import { bgXiSpeakFeedbackList } from '../BgXiSpeak.api';
|
||||||
|
import { feedbackStatusRowClassName } from '/@/views/bg/utils/feedbackStatusRender';
|
||||||
|
|
||||||
const visible = ref(false);
|
const visible = ref(false);
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
cancelText="关闭"
|
cancelText="关闭"
|
||||||
>
|
>
|
||||||
<BasicTable
|
<BasicTable
|
||||||
|
class="feedback-table"
|
||||||
size="small"
|
size="small"
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
:canResize="false"
|
:canResize="false"
|
||||||
@@ -19,6 +20,7 @@
|
|||||||
:dataSource="feedbackTableData"
|
:dataSource="feedbackTableData"
|
||||||
:loading="loading"
|
:loading="loading"
|
||||||
:pagination="false"
|
:pagination="false"
|
||||||
|
:rowClassName="feedbackStatusRowClassName"
|
||||||
:bordered="false"
|
:bordered="false"
|
||||||
>
|
>
|
||||||
<template #bodyCell="{ column, record }">
|
<template #bodyCell="{ column, record }">
|
||||||
@@ -44,6 +46,7 @@
|
|||||||
import JModal from '/@/components/Modal/src/JModal/JModal.vue';
|
import JModal from '/@/components/Modal/src/JModal/JModal.vue';
|
||||||
import SUploadFile from '/@/components/semri/fileComponent/SUploadFile.vue';
|
import SUploadFile from '/@/components/semri/fileComponent/SUploadFile.vue';
|
||||||
import { list } from '../DqInspectProgress.api';
|
import { list } from '../DqInspectProgress.api';
|
||||||
|
import { feedbackStatusRowClassName } from '/@/views/bg/utils/feedbackStatusRender';
|
||||||
|
|
||||||
const visible = ref(false);
|
const visible = ref(false);
|
||||||
const loading = ref(false);
|
const loading = ref(false);
|
||||||
|
|||||||
@@ -59,30 +59,7 @@
|
|||||||
<a-button type="primary" v-auth="'dqinspecttask:dq_inspect_task:exportXls'" preIcon="ant-design:export-outlined" @click="onExportXls">
|
<a-button type="primary" v-auth="'dqinspecttask:dq_inspect_task:exportXls'" preIcon="ant-design:export-outlined" @click="onExportXls">
|
||||||
导出</a-button
|
导出</a-button
|
||||||
>
|
>
|
||||||
<j-upload-button type="primary" v-auth="'dqinspecttask:dq_inspect_task:importExcel'" preIcon="ant-design:import-outlined" @click="onImportXls"
|
<a-button type="primary" v-auth="'dqinspecttask:dq_inspect_task:importExcel'" preIcon="ant-design:import-outlined" @click="openImportModal">导入</a-button>
|
||||||
>导入</j-upload-button
|
|
||||||
>
|
|
||||||
<a-button
|
|
||||||
type="primary"
|
|
||||||
v-auth="'dqinspecttask:dq_inspect_task:exportXls'"
|
|
||||||
preIcon="ant-design:download-outlined"
|
|
||||||
@click="handleDownloadTemplate"
|
|
||||||
>下载模板</a-button
|
|
||||||
>
|
|
||||||
<j-upload-button
|
|
||||||
type="primary"
|
|
||||||
v-auth="'dqinspecttask:dq_inspect_task:importExcel'"
|
|
||||||
preIcon="ant-design:check-outlined"
|
|
||||||
@click="handleCheckExcel"
|
|
||||||
>模板校验</j-upload-button
|
|
||||||
>
|
|
||||||
<j-upload-button
|
|
||||||
type="primary"
|
|
||||||
v-auth="'dqinspecttask:dq_inspect_task:importExcel'"
|
|
||||||
preIcon="ant-design:import-outlined"
|
|
||||||
@click="handleImportByTemplate"
|
|
||||||
>模板导入</j-upload-button
|
|
||||||
>
|
|
||||||
|
|
||||||
<a-button
|
<a-button
|
||||||
v-if="selectedRowKeys.length > 0"
|
v-if="selectedRowKeys.length > 0"
|
||||||
@@ -126,6 +103,33 @@
|
|||||||
<BpmPictureModal @register="registerBpmModal" />
|
<BpmPictureModal @register="registerBpmModal" />
|
||||||
<FlowScheduleStartModal ref="flowScheduleModalRef" @confirm="handleFlowScheduleConfirm" />
|
<FlowScheduleStartModal ref="flowScheduleModalRef" @confirm="handleFlowScheduleConfirm" />
|
||||||
<BusinessProcessListModal ref="businessProcessListModalRef" />
|
<BusinessProcessListModal ref="businessProcessListModalRef" />
|
||||||
|
<!-- 导入弹窗:下载模板 / 模板校验 / 模板导入 -->
|
||||||
|
<a-modal v-model:open="importModalVisible" title="导入数据" :footer="null" :width="460" centered destroyOnClose>
|
||||||
|
<div class="import-action-list">
|
||||||
|
<div v-auth="'dqinspecttask:dq_inspect_task:exportXls'" class="import-action-card" @click="handleDownloadTemplateFromModal">
|
||||||
|
<Icon icon="ant-design:download-outlined" class="import-action-icon" />
|
||||||
|
<div class="import-action-body">
|
||||||
|
<div class="import-action-title">下载模板</div>
|
||||||
|
<div class="import-action-desc">下载标准导入模板,按模板格式填写数据</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-auth="'dqinspecttask:dq_inspect_task:importExcel'" class="import-action-card" @click="chooseFile('check')">
|
||||||
|
<Icon icon="ant-design:check-outlined" class="import-action-icon" />
|
||||||
|
<div class="import-action-body">
|
||||||
|
<div class="import-action-title">模板校验</div>
|
||||||
|
<div class="import-action-desc">校验所选文件是否符合导入规范,提前发现错误</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div v-auth="'dqinspecttask:dq_inspect_task:importExcel'" class="import-action-card" @click="chooseFile('import')">
|
||||||
|
<Icon icon="ant-design:import-outlined" class="import-action-icon" />
|
||||||
|
<div class="import-action-body">
|
||||||
|
<div class="import-action-title">模板导入</div>
|
||||||
|
<div class="import-action-desc">导入校验通过的模板数据到当前台账</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<input ref="fileInputRef" type="file" accept=".xls,.xlsx" style="display: none" @change="onFileSelected" />
|
||||||
|
</a-modal>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -180,7 +184,7 @@
|
|||||||
const userStore = useUserStore();
|
const userStore = useUserStore();
|
||||||
const { createMessage } = useMessage();
|
const { createMessage } = useMessage();
|
||||||
//注册table数据
|
//注册table数据
|
||||||
const { prefixCls, tableContext, onExportXls, onImportXls } = useListPage({
|
const { prefixCls, tableContext, onExportXls } = useListPage({
|
||||||
tableProps: {
|
tableProps: {
|
||||||
title: 'dq_inspect_task',
|
title: 'dq_inspect_task',
|
||||||
api: ledgerList,
|
api: ledgerList,
|
||||||
@@ -295,6 +299,43 @@
|
|||||||
(selectedRowKeys.value = []) && reload();
|
(selectedRowKeys.value = []) && reload();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// ================= 导入弹窗(下载模板 / 模板校验 / 模板导入) =================
|
||||||
|
const importModalVisible = ref(false);
|
||||||
|
const fileInputRef = ref();
|
||||||
|
const pendingAction = ref<'check' | 'import' | null>(null);
|
||||||
|
|
||||||
|
function openImportModal() {
|
||||||
|
importModalVisible.value = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleDownloadTemplateFromModal() {
|
||||||
|
importModalVisible.value = false;
|
||||||
|
handleDownloadTemplate();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 触发隐藏文件选择框,记录当前动作(校验 or 导入) */
|
||||||
|
function chooseFile(action: 'check' | 'import') {
|
||||||
|
pendingAction.value = action;
|
||||||
|
fileInputRef.value?.click();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 文件选择后按动作分发到原校验/导入逻辑 */
|
||||||
|
function onFileSelected(e) {
|
||||||
|
const file = e.target.files?.[0];
|
||||||
|
e.target.value = '';
|
||||||
|
if (!file) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
const action = pendingAction.value;
|
||||||
|
pendingAction.value = null;
|
||||||
|
importModalVisible.value = false;
|
||||||
|
if (action === 'check') {
|
||||||
|
void handleCheckExcel({ file });
|
||||||
|
} else if (action === 'import') {
|
||||||
|
void handleImportByTemplate({ file });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function handleDownloadTemplate() {
|
async function handleDownloadTemplate() {
|
||||||
const data = await defHttp.get({ url: getExportXlsHeadersUrl, params: {}, responseType: 'blob', timeout: 60000 }, { isTransformResponse: false });
|
const data = await defHttp.get({ url: getExportXlsHeadersUrl, params: {}, responseType: 'blob', timeout: 60000 }, { isTransformResponse: false });
|
||||||
if (!data) {
|
if (!data) {
|
||||||
@@ -677,3 +718,50 @@
|
|||||||
background: #f6ffed !important;
|
background: #f6ffed !important;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
/* 导入弹窗操作卡片 */
|
||||||
|
.import-action-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
padding: 4px 8px 8px;
|
||||||
|
}
|
||||||
|
.import-action-card {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 14px;
|
||||||
|
padding: 14px 16px;
|
||||||
|
border: 1px solid #e5e6eb;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #fff;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: all 0.2s ease;
|
||||||
|
}
|
||||||
|
.import-action-card:hover {
|
||||||
|
border-color: #1677ff;
|
||||||
|
background: #f0f7ff;
|
||||||
|
box-shadow: 0 2px 8px rgba(22, 119, 255, 0.15);
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
.import-action-icon {
|
||||||
|
font-size: 26px;
|
||||||
|
color: #1677ff;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.import-action-body {
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
.import-action-title {
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1f2329;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
.import-action-desc {
|
||||||
|
margin-top: 4px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #8a919f;
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
@@ -167,6 +167,7 @@
|
|||||||
</a-space>
|
</a-space>
|
||||||
</div>
|
</div>
|
||||||
<a-table
|
<a-table
|
||||||
|
class="feedback-table"
|
||||||
v-if="showFeedbackInfo && showFeedbackDetail"
|
v-if="showFeedbackInfo && showFeedbackDetail"
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
size="small"
|
size="small"
|
||||||
@@ -175,6 +176,7 @@
|
|||||||
:data-source="feedbackList"
|
:data-source="feedbackList"
|
||||||
:pagination="false"
|
:pagination="false"
|
||||||
:scroll="{ x: 1200 }"
|
:scroll="{ x: 1200 }"
|
||||||
|
:rowClassName="feedbackStatusRowClassName"
|
||||||
>
|
>
|
||||||
<template #bodyCell="{ column, record, index }">
|
<template #bodyCell="{ column, record, index }">
|
||||||
<template v-if="column.key === 'seq'">{{ index + 1 }}</template>
|
<template v-if="column.key === 'seq'">{{ index + 1 }}</template>
|
||||||
@@ -269,6 +271,7 @@
|
|||||||
import Icon from '/@/components/Icon/index';
|
import Icon from '/@/components/Icon/index';
|
||||||
import { saveBpmForm, setProcessVariable, specificProblemOptions } from '../DqInspectTask.api';
|
import { saveBpmForm, setProcessVariable, specificProblemOptions } from '../DqInspectTask.api';
|
||||||
import { list as progressList, deleteOne as progressDelete } from '../../inspectProgress/DqInspectProgress.api';
|
import { list as progressList, deleteOne as progressDelete } from '../../inspectProgress/DqInspectProgress.api';
|
||||||
|
import { feedbackStatusRowClassName } from '/@/views/bg/utils/feedbackStatusRender';
|
||||||
import { usePermission } from '/@/hooks/web/usePermission';
|
import { usePermission } from '/@/hooks/web/usePermission';
|
||||||
import { CHARGE_LEADER_ROLE_ID } from '/@/constant/supervision';
|
import { CHARGE_LEADER_ROLE_ID } from '/@/constant/supervision';
|
||||||
import { useSelectNameResolver } from '/@/composables/useSelectNameResolver';
|
import { useSelectNameResolver } from '/@/composables/useSelectNameResolver';
|
||||||
|
|||||||
@@ -1,26 +1,65 @@
|
|||||||
<template>
|
<template>
|
||||||
<a-modal
|
<a-modal
|
||||||
v-model:open="visible"
|
v-model:open="visible"
|
||||||
:title="title"
|
|
||||||
:width="width"
|
:width="width"
|
||||||
centered
|
:top="80"
|
||||||
:footer="null"
|
:footer="null"
|
||||||
:maskClosable="true"
|
:maskClosable="true"
|
||||||
:bodyStyle="{ maxHeight: '70vh', overflowY: 'auto', padding: '16px 24px 20px' }"
|
:bodyStyle="{ maxHeight: '70vh', overflowY: 'auto', padding: '4px 24px 20px' }"
|
||||||
|
wrapClassName="inspecttask-detail-modal"
|
||||||
destroyOnClose
|
destroyOnClose
|
||||||
>
|
>
|
||||||
<a-tabs v-model:activeKey="activeKey">
|
<a-tabs v-model:activeKey="activeKey">
|
||||||
<a-tab-pane key="item" tab="事项信息">
|
<a-tab-pane key="item" tab="事项信息">
|
||||||
<div class="detail-grid">
|
<div class="matter-detail">
|
||||||
<div
|
<section class="matter-summary">
|
||||||
v-for="field in detailFields"
|
<div class="summary-heading">
|
||||||
:key="field.label"
|
<span class="summary-heading-icon">
|
||||||
class="detail-row"
|
<Icon :icon="summaryIcon" />
|
||||||
:class="{ 'detail-row-full': field.fullWidth, 'detail-row-bold': field.bold }"
|
</span>
|
||||||
>
|
<div class="summary-heading-content">
|
||||||
<span class="detail-label">{{ field.label }}</span>
|
<span class="summary-kicker">{{ summaryKicker }}</span>
|
||||||
<span class="detail-value">{{ field.value }}</span>
|
<h3 class="summary-title">{{ detailSummary.title }}</h3>
|
||||||
</div>
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="summary-meta">
|
||||||
|
<div class="summary-meta-item">
|
||||||
|
<Icon icon="ant-design:appstore-outlined" />
|
||||||
|
<span class="summary-meta-label">问题分类</span>
|
||||||
|
<span class="summary-meta-value">{{ detailSummary.categoryName }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="summary-meta-item">
|
||||||
|
<Icon icon="ant-design:lock-outlined" />
|
||||||
|
<span class="summary-meta-label">密级</span>
|
||||||
|
<span class="summary-meta-value">{{ detailSummary.secretLevel }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="summary-meta-item">
|
||||||
|
<Icon icon="ant-design:clock-circle-outlined" />
|
||||||
|
<span class="summary-meta-label">完成时限</span>
|
||||||
|
<span class="summary-meta-value">{{ detailSummary.deadline }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="summary-tag-group">
|
||||||
|
<a-tag class="summary-tag" :color="taskStatusTagColor">整改状态:{{ detailSummary.taskStatus }}</a-tag>
|
||||||
|
<a-tag class="summary-tag">督办状态:{{ detailSummary.bpmStatus }}</a-tag>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section v-for="group in detailGroups" :key="group.title" class="detail-section">
|
||||||
|
<div class="detail-section-header">
|
||||||
|
<span class="detail-section-icon">
|
||||||
|
<Icon :icon="group.icon" />
|
||||||
|
</span>
|
||||||
|
<h3>{{ group.title }}</h3>
|
||||||
|
</div>
|
||||||
|
<div class="detail-grid">
|
||||||
|
<div v-for="field in group.fields" :key="field.label" class="detail-item" :class="{ 'detail-item-full': field.fullWidth }">
|
||||||
|
<span class="detail-label">{{ field.label }}</span>
|
||||||
|
<span class="detail-value" :class="{ 'detail-value-strong': field.bold }">{{ field.value }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
</div>
|
</div>
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
|
|
||||||
@@ -37,6 +76,7 @@
|
|||||||
<div class="feedback-summary-desc">当前整改任务的工作进展反馈记录</div>
|
<div class="feedback-summary-desc">当前整改任务的工作进展反馈记录</div>
|
||||||
</div>
|
</div>
|
||||||
<a-table
|
<a-table
|
||||||
|
class="feedback-table"
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
size="small"
|
size="small"
|
||||||
bordered
|
bordered
|
||||||
@@ -45,6 +85,7 @@
|
|||||||
:pagination="false"
|
:pagination="false"
|
||||||
:scroll="{ x: 1200 }"
|
:scroll="{ x: 1200 }"
|
||||||
:loading="feedbackLoading"
|
:loading="feedbackLoading"
|
||||||
|
:rowClassName="feedbackStatusRowClassName"
|
||||||
>
|
>
|
||||||
<template #bodyCell="{ column, record: fb }">
|
<template #bodyCell="{ column, record: fb }">
|
||||||
<template v-if="column.key === 'attachments'">
|
<template v-if="column.key === 'attachments'">
|
||||||
@@ -69,10 +110,10 @@
|
|||||||
import Icon from '/@/components/Icon/index';
|
import Icon from '/@/components/Icon/index';
|
||||||
import SUploadFile from '/@/components/semri/fileComponent/SUploadFile.vue';
|
import SUploadFile from '/@/components/semri/fileComponent/SUploadFile.vue';
|
||||||
import { list } from '../../inspectProgress/DqInspectProgress.api';
|
import { list } from '../../inspectProgress/DqInspectProgress.api';
|
||||||
|
import { feedbackStatusRowClassName } from '/@/views/bg/utils/feedbackStatusRender';
|
||||||
import type { BasicColumn } from '/@/components/Table';
|
import type { BasicColumn } from '/@/components/Table';
|
||||||
|
|
||||||
const visible = ref(false);
|
const visible = ref(false);
|
||||||
const title = ref('详情');
|
|
||||||
const width = 1100;
|
const width = 1100;
|
||||||
const activeKey = ref('item');
|
const activeKey = ref('item');
|
||||||
const record = ref<Recordable>({});
|
const record = ref<Recordable>({});
|
||||||
@@ -97,29 +138,77 @@
|
|||||||
return fmt(record[`${key}_dictText`] || record[key]);
|
return fmt(record[`${key}_dictText`] || record[key]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const detailFields = computed(() => {
|
const summaryKicker = '整改任务';
|
||||||
|
const summaryIcon = 'ant-design:flag-outlined';
|
||||||
|
|
||||||
|
// 摘要区只承载最常用于快速判断事项状态的信息,详细字段放入下方分组,避免重复平铺。
|
||||||
|
const detailSummary = computed(() => {
|
||||||
|
const r = record.value || {};
|
||||||
|
return {
|
||||||
|
title: fmt(r.specificName),
|
||||||
|
categoryName: fmt(r.categoryName),
|
||||||
|
secretLevel: dictText(r, 'secretLevel'),
|
||||||
|
deadline: fmtDate(r.deadline),
|
||||||
|
taskStatus: fmt(r.taskStatus),
|
||||||
|
bpmStatus: dictText(r, 'bpmStatus'),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
const taskStatusTagColor = computed(() => {
|
||||||
|
const text = detailSummary.value.taskStatus;
|
||||||
|
if (text.includes('已完成')) return 'success';
|
||||||
|
if (text.includes('整改中') || text.includes('进行中')) return 'processing';
|
||||||
|
if (text.includes('未开始')) return 'warning';
|
||||||
|
return undefined;
|
||||||
|
});
|
||||||
|
|
||||||
|
const detailGroups = computed(() => {
|
||||||
const r = record.value || {};
|
const r = record.value || {};
|
||||||
return [
|
return [
|
||||||
{ label: '问题分类', value: fmt(r.categoryName) },
|
{
|
||||||
{ label: '面上问题', value: fmt(r.surfaceName) },
|
title: '问题定位',
|
||||||
{ label: '具体问题', value: fmt(r.specificName) },
|
icon: 'ant-design:flag-outlined',
|
||||||
{ label: '整改措施', value: fmt(r.improveMeasure), fullWidth: true, bold: true },
|
fields: [
|
||||||
{ label: '密级', value: dictText(r, 'secretLevel') },
|
{ label: '面上问题', value: fmt(r.surfaceName) },
|
||||||
{ label: '完成时限', value: fmtDate(r.deadline) },
|
{ label: '目标节点', value: fmt(r.targetNode) },
|
||||||
{ label: '目标节点', value: fmt(r.targetNode) },
|
],
|
||||||
{ label: '巡视建议', value: fmt(r.inspectAdvice) },
|
},
|
||||||
{ label: '检查情况', value: fmt(r.inspectionStatus) },
|
{
|
||||||
{ label: '监督意见', value: fmt(r.supervisoryOpinion) },
|
title: '整改要求',
|
||||||
{ label: '整改状态', value: fmt(r.taskStatus) },
|
icon: 'ant-design:edit-outlined',
|
||||||
{ label: '措施责任部门', value: dictText(r, 'measureResDept'), bold: true },
|
fields: [
|
||||||
{ label: '措施责任负责人', value: dictText(r, 'measureResLeader'), bold: true },
|
{ label: '整改措施', value: fmt(r.improveMeasure), fullWidth: true, bold: true },
|
||||||
{ label: '分管所领导', value: dictText(r, 'chargeLeaderId'), bold: true },
|
{ label: '巡视建议', value: fmt(r.inspectAdvice) },
|
||||||
{ label: '整改交付成果', value: fmt(r.rectificationEvidence) },
|
],
|
||||||
{ label: '成效评价标准', value: fmt(r.evaluationCriterion) },
|
},
|
||||||
{ label: '需党群负责人审批', value: ynText(r.isNeedAppro) },
|
{
|
||||||
{ label: '党群负责人', value: dictText(r, 'supDeptleaderid') },
|
title: '责任分工',
|
||||||
{ label: '督办状态', value: dictText(r, 'bpmStatus') },
|
icon: 'ant-design:team-outlined',
|
||||||
{ label: '督办次数', value: fmt(r.superviseCount) },
|
fields: [
|
||||||
|
{ label: '措施责任部门', value: dictText(r, 'measureResDept'), bold: true },
|
||||||
|
{ label: '措施责任负责人', value: dictText(r, 'measureResLeader'), bold: true },
|
||||||
|
{ label: '分管所领导', value: dictText(r, 'chargeLeaderId'), bold: true },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '成效与监督',
|
||||||
|
icon: 'ant-design:audit-outlined',
|
||||||
|
fields: [
|
||||||
|
{ label: '整改交付成果', value: fmt(r.rectificationEvidence) },
|
||||||
|
{ label: '成效评价标准', value: fmt(r.evaluationCriterion) },
|
||||||
|
{ label: '检查情况', value: fmt(r.inspectionStatus) },
|
||||||
|
{ label: '监督意见', value: fmt(r.supervisoryOpinion) },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '审批与进展',
|
||||||
|
icon: 'ant-design:safety-outlined',
|
||||||
|
fields: [
|
||||||
|
{ label: '需党群负责人审批', value: ynText(r.isNeedAppro) },
|
||||||
|
{ label: '党群负责人', value: dictText(r, 'supDeptleaderid') },
|
||||||
|
{ label: '督办次数', value: fmt(r.superviseCount) },
|
||||||
|
],
|
||||||
|
},
|
||||||
];
|
];
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -148,7 +237,6 @@
|
|||||||
|
|
||||||
function open(currentRecord: Recordable) {
|
function open(currentRecord: Recordable) {
|
||||||
record.value = currentRecord || {};
|
record.value = currentRecord || {};
|
||||||
title.value = '详情';
|
|
||||||
activeKey.value = 'item';
|
activeKey.value = 'item';
|
||||||
visible.value = true;
|
visible.value = true;
|
||||||
if (currentRecord?.id !== undefined && currentRecord?.id !== null && currentRecord?.id !== '') {
|
if (currentRecord?.id !== undefined && currentRecord?.id !== null && currentRecord?.id !== '') {
|
||||||
@@ -160,40 +248,265 @@
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.detail-grid {
|
// 弹窗内容会 Teleport 到 body,通过专属类限定公共组件的字号,避免影响其他页面。
|
||||||
display: grid;
|
:global(.inspecttask-detail-modal .ant-modal-title) {
|
||||||
grid-template-columns: repeat(2, 1fr);
|
font-size: 18px;
|
||||||
gap: 12px 32px;
|
line-height: 26px;
|
||||||
padding: 4px 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.detail-row {
|
:global(.inspecttask-detail-modal .ant-tabs-tab-btn) {
|
||||||
|
font-size: 15px;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.inspecttask-detail-modal .ant-table) {
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
|
||||||
|
:global(.inspecttask-detail-modal .ant-table-thead > tr > th),
|
||||||
|
:global(.inspecttask-detail-modal .ant-table-tbody > tr > td) {
|
||||||
|
font-size: 15px;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.matter-detail {
|
||||||
|
padding: 4px 2px 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.matter-summary {
|
||||||
|
position: relative;
|
||||||
|
overflow: hidden;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
padding: 20px 22px;
|
||||||
|
background: linear-gradient(135deg, #f2f8ff 0%, #f8fbff 48%, #ffffff 100%);
|
||||||
|
border: 1px solid #d6e8ff;
|
||||||
|
border-radius: 10px;
|
||||||
|
box-shadow: 0 4px 14px rgb(22 119 255 / 8%);
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
position: absolute;
|
||||||
|
top: -46px;
|
||||||
|
right: -24px;
|
||||||
|
width: 132px;
|
||||||
|
height: 132px;
|
||||||
|
background: rgb(22 119 255 / 5%);
|
||||||
|
border-radius: 50%;
|
||||||
|
content: '';
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-heading {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: flex-start;
|
align-items: flex-start;
|
||||||
gap: 12px;
|
gap: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-heading-icon {
|
||||||
|
display: inline-flex;
|
||||||
|
flex: 0 0 40px;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
color: #1677ff;
|
||||||
|
font-size: 20px;
|
||||||
|
background: #e6f4ff;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-heading-content {
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.detail-row-full {
|
.summary-kicker {
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 2px;
|
||||||
|
color: #64748b;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-title {
|
||||||
|
margin: 0;
|
||||||
|
color: #172033;
|
||||||
|
font-size: 21px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 30px;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-meta {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 10px 18px;
|
||||||
|
margin-top: 16px;
|
||||||
|
padding-top: 14px;
|
||||||
|
border-top: 1px solid #deebfa;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-meta-item {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 6px;
|
||||||
|
min-height: 24px;
|
||||||
|
color: #64748b;
|
||||||
|
|
||||||
|
> :first-child {
|
||||||
|
color: #1677ff;
|
||||||
|
font-size: 15px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-meta-label {
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-meta-value {
|
||||||
|
color: #1f2937;
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-tag-group {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-tag {
|
||||||
|
margin-inline-end: 0;
|
||||||
|
padding: 2px 10px;
|
||||||
|
font-size: 14px;
|
||||||
|
line-height: 22px;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-section {
|
||||||
|
overflow: hidden;
|
||||||
|
margin-top: 14px;
|
||||||
|
background: #ffffff;
|
||||||
|
border: 1px solid #e7edf4;
|
||||||
|
border-radius: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-section-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
min-height: 44px;
|
||||||
|
padding: 10px 16px;
|
||||||
|
background: #f8fbff;
|
||||||
|
border-bottom: 1px solid #e7edf4;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin: 0;
|
||||||
|
color: #27364b;
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
line-height: 24px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-section-icon {
|
||||||
|
display: inline-flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: 26px;
|
||||||
|
height: 26px;
|
||||||
|
color: #1677ff;
|
||||||
|
background: #e6f4ff;
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-grid {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||||
|
gap: 1px;
|
||||||
|
background: #edf1f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-item {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 142px minmax(0, 1fr);
|
||||||
|
align-items: start;
|
||||||
|
gap: 12px;
|
||||||
|
min-width: 0;
|
||||||
|
min-height: 54px;
|
||||||
|
padding: 14px 16px;
|
||||||
|
background: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-item-full {
|
||||||
grid-column: 1 / -1;
|
grid-column: 1 / -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
.detail-label {
|
.detail-label {
|
||||||
flex: 0 0 150px;
|
color: #7b8798;
|
||||||
color: #8c8c8c;
|
font-size: 14px;
|
||||||
line-height: 22px;
|
line-height: 26px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.detail-value {
|
.detail-value {
|
||||||
flex: 1;
|
min-width: 0;
|
||||||
color: #1f2937;
|
color: #27364b;
|
||||||
line-height: 22px;
|
font-size: 15px;
|
||||||
word-break: break-all;
|
line-height: 26px;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
overflow-wrap: anywhere;
|
||||||
}
|
}
|
||||||
|
|
||||||
.detail-row-bold .detail-value {
|
.detail-value-strong {
|
||||||
font-weight: 700;
|
color: #172033;
|
||||||
font-size: 15px;
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 768px) {
|
||||||
|
.matter-summary {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-meta {
|
||||||
|
align-items: flex-start;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-tag-group {
|
||||||
|
flex-basis: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-grid {
|
||||||
|
grid-template-columns: minmax(0, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-item-full {
|
||||||
|
grid-column: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 576px) {
|
||||||
|
.summary-title {
|
||||||
|
font-size: 19px;
|
||||||
|
line-height: 27px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.summary-meta {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: minmax(0, 1fr);
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-item {
|
||||||
|
grid-template-columns: minmax(0, 1fr);
|
||||||
|
gap: 2px;
|
||||||
|
padding: 11px 14px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.feedback-summary-header {
|
.feedback-summary-header {
|
||||||
|
|||||||
Reference in New Issue
Block a user