|
|
@@ -8,16 +8,15 @@
|
|
|
<el-form-item label="预案名称">
|
|
|
<el-input v-model="queryParams.planName" placeholder="请输入预案名称" clearable style="width: 200px" @keyup.enter="handleSearch" />
|
|
|
</el-form-item>
|
|
|
- <el-form-item label="创建时间">
|
|
|
- <el-date-picker
|
|
|
- v-model="queryParams.dateRange"
|
|
|
- type="daterange"
|
|
|
- range-separator="至"
|
|
|
- start-placeholder="开始日期"
|
|
|
- end-placeholder="结束日期"
|
|
|
- style="width: 240px"
|
|
|
- value-format="YYYY-MM-DD"
|
|
|
- />
|
|
|
+ <el-form-item label="预案类型">
|
|
|
+ <el-select v-model="queryParams.planType" placeholder="全部类型" clearable style="width: 160px">
|
|
|
+ <el-option v-for="d in dis_plan_type" :key="d.value" :label="d.label" :value="d.value" />
|
|
|
+ </el-select>
|
|
|
+ </el-form-item>
|
|
|
+ <el-form-item label="状态">
|
|
|
+ <el-select v-model="queryParams.status" placeholder="全部状态" clearable style="width: 140px">
|
|
|
+ <el-option v-for="d in planStatusOptions" :key="d.value" :label="d.label" :value="d.value" />
|
|
|
+ </el-select>
|
|
|
</el-form-item>
|
|
|
<el-form-item>
|
|
|
<el-button type="primary" :icon="Search" @click="handleSearch">搜索</el-button>
|
|
|
@@ -31,27 +30,9 @@
|
|
|
</el-form-item>
|
|
|
</el-form>
|
|
|
|
|
|
- <!-- 主体:左侧树 + 右侧表格 -->
|
|
|
- <div class="main-layout">
|
|
|
- <!-- 左侧预案分类树(紧凑窄栏,不再挤压表格) -->
|
|
|
- <div class="left-tree">
|
|
|
- <div class="tree-title">预案分类</div>
|
|
|
- <el-tree
|
|
|
- ref="treeRef"
|
|
|
- :data="treeData"
|
|
|
- :props="treeProps"
|
|
|
- node-key="id"
|
|
|
- default-expand-all
|
|
|
- highlight-current
|
|
|
- @node-click="handleNodeClick"
|
|
|
- />
|
|
|
- </div>
|
|
|
-
|
|
|
- <!-- 右侧表格 -->
|
|
|
- <div class="right-table">
|
|
|
+ <div class="plan-list">
|
|
|
<el-table :data="pageList" border stripe @selection-change="handleSelectionChange" style="width: 100%">
|
|
|
<el-table-column type="selection" width="55" align="center" />
|
|
|
- <el-table-column label="序号" type="index" width="60" align="center" />
|
|
|
<el-table-column prop="planNo" label="预案编号" width="140" />
|
|
|
<el-table-column prop="planName" label="预案名称" min-width="180" show-overflow-tooltip />
|
|
|
<el-table-column prop="planType" label="预案类型" width="130">
|
|
|
@@ -64,35 +45,20 @@
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
<el-table-column prop="version" label="版本" width="90" />
|
|
|
- <el-table-column prop="attachName" label="附件名称" min-width="200" show-overflow-tooltip>
|
|
|
- <template #default="{ row }">
|
|
|
- <!-- 附件名称即下载入口:有附件时可点击下载,无附件显示 - -->
|
|
|
- <el-link
|
|
|
- v-if="row.attachName"
|
|
|
- type="primary"
|
|
|
- :underline="false"
|
|
|
- :disabled="!row.attachPath"
|
|
|
- @click="handleDownload(row)"
|
|
|
- >
|
|
|
- <el-icon style="margin-right:2px"><Download /></el-icon>{{ row.attachName }}
|
|
|
- </el-link>
|
|
|
- <span v-else>-</span>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
- <el-table-column prop="publishDate" label="发布时间" width="120" align="center" />
|
|
|
- <el-table-column prop="createTime" label="创建时间" width="150" align="center" />
|
|
|
- <el-table-column prop="compileUnit" label="编制单位" width="100" />
|
|
|
+ <el-table-column prop="compileUnit" label="编制单位" min-width="130" show-overflow-tooltip />
|
|
|
<el-table-column prop="statusName" label="状态" width="100" align="center">
|
|
|
<template #default="{ row }">
|
|
|
<el-tag :type="statusTagType(row.status)">{{ row.statusName }}</el-tag>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
- <el-table-column label="操作" width="270" align="center">
|
|
|
+ <el-table-column label="操作" width="330" align="center">
|
|
|
<template #default="{ row }">
|
|
|
+ <div class="plan-actions">
|
|
|
<el-button type="primary" plain size="small" @click="handleEdit(row)" v-hasPermi="['dispatch:plan:edit']">{{ isReviseRow(row) ? '修订' : '编辑' }}</el-button>
|
|
|
<el-button type="danger" plain size="small" @click="handleDelete(row)" v-hasPermi="['dispatch:plan:remove']">删除</el-button>
|
|
|
<el-button v-if="row.submittable" type="success" plain size="small" @click="handleSubmitApproval(row)" v-hasPermi="['dispatch:plan:submit']">提交审核</el-button>
|
|
|
<el-button type="warning" plain size="small" @click="handleVersions(row)">版本</el-button>
|
|
|
+ </div>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
</el-table>
|
|
|
@@ -108,7 +74,6 @@
|
|
|
@size-change="onSizeChange"
|
|
|
@current-change="onPageChange"
|
|
|
/>
|
|
|
- </div>
|
|
|
</div>
|
|
|
</el-tab-pane>
|
|
|
|
|
|
@@ -119,21 +84,23 @@
|
|
|
<el-button type="primary" size="small" @click="handleTemplateAdd" v-hasPermi="['dispatch:planLibrary:add']">新增模板</el-button>
|
|
|
</div>
|
|
|
<el-table :data="templatePageList" border stripe style="width:100%" v-loading="templateLoading">
|
|
|
- <el-table-column type="index" :index="templateIndex" label="序号" width="60" align="center" />
|
|
|
<el-table-column prop="planName" label="模板名称" min-width="200" show-overflow-tooltip />
|
|
|
- <el-table-column prop="eventType" label="类型" width="100" align="center" />
|
|
|
- <el-table-column prop="eventLevel" label="等级" width="90" align="center" />
|
|
|
- <el-table-column prop="responsibleUnit" label="责任单位" width="120">
|
|
|
- <template #default="{ row }">{{ row.responsibleUnit || '-' }}</template>
|
|
|
+ <el-table-column prop="eventType" label="预案类型" width="110" align="center">
|
|
|
+ <template #default="{ row }">{{ dictLabel(dis_plan_type, row.eventType) }}</template>
|
|
|
</el-table-column>
|
|
|
- <el-table-column prop="planSummary" label="概要" min-width="260" show-overflow-tooltip>
|
|
|
- <template #default="{ row }">{{ row.planSummary || '-' }}</template>
|
|
|
+ <el-table-column prop="eventLevel" label="预案级别" width="100" align="center">
|
|
|
+ <template #default="{ row }">{{ dictLabel(dis_plan_level, row.eventLevel) }}</template>
|
|
|
+ </el-table-column>
|
|
|
+ <el-table-column prop="responsibleUnit" label="编制单位" width="120">
|
|
|
+ <template #default="{ row }">{{ row.responsibleUnit || '-' }}</template>
|
|
|
</el-table-column>
|
|
|
<el-table-column prop="createTime" label="创建时间" width="150" align="center" />
|
|
|
- <el-table-column label="操作" width="140" align="center">
|
|
|
+ <el-table-column label="操作" width="150" align="center">
|
|
|
<template #default="{ row }">
|
|
|
+ <div class="plan-actions">
|
|
|
<el-button type="primary" plain size="small" @click="handleTemplateEdit(row)" v-hasPermi="['dispatch:planLibrary:add']">编辑</el-button>
|
|
|
<el-button type="danger" plain size="small" @click="handleTemplateDelete(row)" v-hasPermi="['dispatch:planLibrary:remove']">删除</el-button>
|
|
|
+ </div>
|
|
|
</template>
|
|
|
</el-table-column>
|
|
|
</el-table>
|
|
|
@@ -170,7 +137,9 @@
|
|
|
@click="pickCreate(tpl)"
|
|
|
>
|
|
|
<div style="font-weight:600;font-size:14px">{{ tpl.planName }}</div>
|
|
|
- <div style="font-size:12px;color:#909399;margin-top:4px">{{ tpl.planSummary || '暂无概要' }}</div>
|
|
|
+ <div style="font-size:12px;color:#909399;margin-top:4px">
|
|
|
+ {{ dictLabel(dis_plan_type, tpl.eventType) }} · {{ dictLabel(dis_plan_level, tpl.eventLevel) }}
|
|
|
+ </div>
|
|
|
</div>
|
|
|
</template>
|
|
|
<template #footer>
|
|
|
@@ -180,7 +149,7 @@
|
|
|
|
|
|
<!-- 新增/修改预案弹窗 -->
|
|
|
<el-dialog v-model="dialogVisible" :title="dialogTitle" width="840px" top="5vh" destroy-on-close class="plan-dialog">
|
|
|
- <div @paste="handleDialogPaste">
|
|
|
+ <div>
|
|
|
<el-form ref="formRef" :model="form" :rules="formRules" label-width="100px">
|
|
|
<el-row :gutter="16">
|
|
|
<el-col :span="12">
|
|
|
@@ -211,51 +180,13 @@
|
|
|
</el-col>
|
|
|
</el-row>
|
|
|
<el-form-item label="编制单位" prop="compileUnit">
|
|
|
- <!-- 可从系统部门选择,也可手动输入(allow-create 允许创建新条目) -->
|
|
|
- <el-select
|
|
|
- v-model="form.compileUnit"
|
|
|
- filterable
|
|
|
- allow-create
|
|
|
- default-first-option
|
|
|
- clearable
|
|
|
- placeholder="选择系统部门或手动输入"
|
|
|
- style="width: 100%"
|
|
|
- >
|
|
|
- <el-option
|
|
|
- v-for="d in deptOptions"
|
|
|
- :key="d.deptId"
|
|
|
- :label="d.deptName"
|
|
|
- :value="d.deptName"
|
|
|
- />
|
|
|
- </el-select>
|
|
|
+ <el-input v-model="form.compileUnit" clearable placeholder="请输入编制单位" />
|
|
|
</el-form-item>
|
|
|
<el-form-item v-if="isRevising" label="修订说明" prop="changeNote">
|
|
|
<el-input v-model="form.changeNote" type="textarea" :rows="3" placeholder="请填写本次修订说明(修订已通过预案必填)" />
|
|
|
</el-form-item>
|
|
|
<el-form-item label="预案正文">
|
|
|
- <!-- 与模板库章节正文同一套纯文本富文本(无图片/视频控件);内容随版本快照保存 -->
|
|
|
- <editor v-model="form.content" :media="false" :min-height="320" placeholder="请输入预案正文(将随版本快照保存,可在版本管理中查看历史正文)" />
|
|
|
- </el-form-item>
|
|
|
- <el-form-item label="附件上传">
|
|
|
- <el-upload
|
|
|
- ref="uploadRef"
|
|
|
- v-model:file-list="form.fileList"
|
|
|
- :action="uploadUrl"
|
|
|
- :headers="uploadHeaders"
|
|
|
- :limit="1"
|
|
|
- accept=".pdf,.doc,.docx"
|
|
|
- :on-success="handleUploadSuccess"
|
|
|
- :on-error="handleUploadError"
|
|
|
- :on-remove="handleUploadRemove"
|
|
|
- :on-exceed="() => ElMessage.warning('只能上传一个文件')"
|
|
|
- :before-upload="handleBeforeUpload"
|
|
|
- style="width:100%"
|
|
|
- >
|
|
|
- <el-button type="primary">添加文件</el-button>
|
|
|
- <template #tip>
|
|
|
- <div class="upload-tip">仅支持 PDF / Word 格式文件,单个文件不超过 50MB,支持 Ctrl+V 粘贴</div>
|
|
|
- </template>
|
|
|
- </el-upload>
|
|
|
+ <NoticeEditor v-if="dialogVisible" v-model="form.content" placeholder="请输入预案正文(将随版本快照保存,可在版本管理中查看历史正文)" @paste.stop />
|
|
|
</el-form-item>
|
|
|
</el-form>
|
|
|
</div>
|
|
|
@@ -273,35 +204,29 @@
|
|
|
</el-form-item>
|
|
|
<el-row :gutter="16">
|
|
|
<el-col :span="12">
|
|
|
- <el-form-item label="类型">
|
|
|
+ <el-form-item label="预案类型">
|
|
|
<el-select v-model="templateForm.eventType" placeholder="请选择" style="width:100%">
|
|
|
- <el-option v-for="t in ['综合', '供水', '燃气', '排水']" :key="t" :label="t" :value="t" />
|
|
|
+ <el-option v-for="t in dis_plan_type" :key="t.value" :label="t.label" :value="t.value" />
|
|
|
</el-select>
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
<el-col :span="12">
|
|
|
- <el-form-item label="等级">
|
|
|
+ <el-form-item label="预案级别">
|
|
|
<el-select v-model="templateForm.eventLevel" placeholder="请选择" style="width:100%">
|
|
|
- <el-option v-for="l in ['Ⅰ级', 'Ⅱ级', 'Ⅲ级']" :key="l" :label="l" :value="l" />
|
|
|
+ <el-option v-for="l in dis_plan_level" :key="l.value" :label="l.label" :value="l.value" />
|
|
|
</el-select>
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
</el-row>
|
|
|
<el-row :gutter="16">
|
|
|
<el-col :span="12">
|
|
|
- <el-form-item label="责任单位">
|
|
|
- <el-input v-model="templateForm.responsibleUnit" maxlength="50" placeholder="请输入责任单位(选填)" />
|
|
|
- </el-form-item>
|
|
|
- </el-col>
|
|
|
- <el-col :span="12">
|
|
|
- <el-form-item label="模板概要">
|
|
|
- <el-input v-model="templateForm.planSummary" maxlength="200" show-word-limit placeholder="一句话说明模板适用场景(新建预案选择时展示)" />
|
|
|
+ <el-form-item label="编制单位">
|
|
|
+ <el-input v-model="templateForm.responsibleUnit" maxlength="50" placeholder="请输入编制单位(选填)" />
|
|
|
</el-form-item>
|
|
|
</el-col>
|
|
|
</el-row>
|
|
|
<el-form-item label="章节正文">
|
|
|
- <!-- 纯文本富文本:无图片/视频/链接控件 -->
|
|
|
- <editor v-model="templateForm.planContent" :media="false" :min-height="320" placeholder="模板章节大纲/正文,新建预案套用后可在正文基础上编写" />
|
|
|
+ <NoticeEditor v-if="templateFormVisible" v-model="templateForm.planContent" placeholder="模板章节大纲/正文,新建预案套用后可在正文基础上编写" />
|
|
|
</el-form-item>
|
|
|
</el-form>
|
|
|
<template #footer>
|
|
|
@@ -317,12 +242,6 @@
|
|
|
</div>
|
|
|
<el-table :data="versionList" border stripe style="width: 100%">
|
|
|
<el-table-column prop="version" label="版本号" width="90" />
|
|
|
- <el-table-column prop="attachName" label="附件" min-width="160" show-overflow-tooltip>
|
|
|
- <template #default="{ row }">
|
|
|
- <el-link v-if="row.attachPath" type="primary" :underline="false" @click="downloadVersionAttach(row)">{{ row.attachName || '下载附件' }}</el-link>
|
|
|
- <span v-else>—</span>
|
|
|
- </template>
|
|
|
- </el-table-column>
|
|
|
<el-table-column prop="editor" label="修订人" width="110" />
|
|
|
<el-table-column prop="editTime" label="修订时间" width="160" />
|
|
|
<el-table-column prop="changeNote" label="变更说明" min-width="160" show-overflow-tooltip />
|
|
|
@@ -358,14 +277,14 @@
|
|
|
<script setup>
|
|
|
import { ref, computed, onMounted } from 'vue'
|
|
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
|
|
-import { Download, Search, Refresh } from '@element-plus/icons-vue'
|
|
|
+import { Search, Refresh } from '@element-plus/icons-vue'
|
|
|
import * as XLSX from 'xlsx'
|
|
|
-import { getToken } from '@/utils/auth'
|
|
|
import { useDict } from '@/utils/dict'
|
|
|
+import NoticeEditor from '@/views/system/notice/NoticeEditor.vue'
|
|
|
import {
|
|
|
getEventPlanPage, getEventPlanById, saveEventPlan, deleteEventPlan,
|
|
|
submitEventPlan, getPlanVersions, getPlanVersion, getPlanLibraryList,
|
|
|
- savePlanLibrary, deletePlanLibrary, getDeptOptions
|
|
|
+ savePlanLibrary, deletePlanLibrary, getPlanStatusOptions
|
|
|
} from '@/api/dispatch'
|
|
|
|
|
|
// ---------- 字典 ----------
|
|
|
@@ -389,35 +308,28 @@ const levelTagType = (level) => LEVEL_TAG[level] || 'info'
|
|
|
const VER_TAG = { 0: 'info', 1: 'success', 2: 'warning', 3: 'danger' }
|
|
|
const verTagType = (verStatus) => VER_TAG[verStatus] || 'info'
|
|
|
|
|
|
-// ---------- 分类树(复用预案类型字典,后端不再单独维护分类表) ----------
|
|
|
-const treeRef = ref(null)
|
|
|
-const treeProps = { children: 'children', label: 'label' }
|
|
|
-const treeData = computed(() => (dis_plan_type.value || []).map(d => ({ id: d.value, label: d.label })))
|
|
|
+// ---------- 查询 ----------
|
|
|
+const queryParams = ref({ planName: '', planType: '', status: '' })
|
|
|
+const planStatusOptions = ref([])
|
|
|
|
|
|
-const selectedTreeNode = ref(null)
|
|
|
-const handleNodeClick = (node) => {
|
|
|
- selectedTreeNode.value = node
|
|
|
- pagination.value.page = 1
|
|
|
- loadData()
|
|
|
+async function loadPlanStatusOptions() {
|
|
|
+ try {
|
|
|
+ const res = await getPlanStatusOptions()
|
|
|
+ planStatusOptions.value = res?.data || []
|
|
|
+ } catch {
|
|
|
+ planStatusOptions.value = []
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
-// ---------- 查询 ----------
|
|
|
-const queryParams = ref({ planName: '', dateRange: null })
|
|
|
-
|
|
|
// ---------- 数据 ----------
|
|
|
const tableData = ref([])
|
|
|
|
|
|
async function loadData() {
|
|
|
try {
|
|
|
- // 手工拼参数:dateRange 是数组不能直接展开进查询参数
|
|
|
- const params = { planName: queryParams.value.planName }
|
|
|
- if (selectedTreeNode.value) {
|
|
|
- params.planType = selectedTreeNode.value.id
|
|
|
- }
|
|
|
- if (queryParams.value.dateRange && queryParams.value.dateRange.length === 2) {
|
|
|
- params.startDate = queryParams.value.dateRange[0]
|
|
|
- params.endDate = queryParams.value.dateRange[1]
|
|
|
- }
|
|
|
+ const params = {}
|
|
|
+ if (queryParams.value.planName) params.planName = queryParams.value.planName
|
|
|
+ if (queryParams.value.planType !== '' && queryParams.value.planType != null) params.planType = queryParams.value.planType
|
|
|
+ if (queryParams.value.status !== '' && queryParams.value.status != null) params.status = queryParams.value.status
|
|
|
const res = await getEventPlanPage(1, 999, params)
|
|
|
tableData.value = res?.data?.records ?? res?.data ?? res ?? []
|
|
|
} catch {
|
|
|
@@ -425,18 +337,7 @@ async function loadData() {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-onMounted(() => { loadData(); loadDeptOptions() })
|
|
|
-
|
|
|
-// ---------- 编制单位候选(系统部门,可手输) ----------
|
|
|
-const deptOptions = ref([])
|
|
|
-async function loadDeptOptions() {
|
|
|
- try {
|
|
|
- const res = await getDeptOptions()
|
|
|
- deptOptions.value = res.data || []
|
|
|
- } catch {
|
|
|
- deptOptions.value = []
|
|
|
- }
|
|
|
-}
|
|
|
+onMounted(() => { loadData(); loadPlanStatusOptions() })
|
|
|
|
|
|
// ---------- 分页 ----------
|
|
|
const pagination = ref({ page: 1, size: 10 })
|
|
|
@@ -450,10 +351,7 @@ const onPageChange = () => {}
|
|
|
// ---------- 搜索 / 重置 ----------
|
|
|
const handleSearch = () => { pagination.value.page = 1; loadData() }
|
|
|
const handleReset = () => {
|
|
|
- queryParams.value.planName = ''
|
|
|
- queryParams.value.dateRange = null
|
|
|
- selectedTreeNode.value = null
|
|
|
- treeRef.value?.setCurrentKey(null)
|
|
|
+ queryParams.value = { planName: '', planType: '', status: '' }
|
|
|
pagination.value.page = 1
|
|
|
loadData()
|
|
|
}
|
|
|
@@ -468,7 +366,7 @@ const dialogTitle = ref('新增预案')
|
|
|
const formRef = ref(null)
|
|
|
const defaultForm = () => ({
|
|
|
id: null, planName: '', planType: '', planLevel: '', version: '', compileUnit: '',
|
|
|
- status: null, changeNote: '', content: '', attachName: '', attachPath: '', fileList: []
|
|
|
+ status: null, changeNote: '', content: ''
|
|
|
})
|
|
|
const form = ref(defaultForm())
|
|
|
|
|
|
@@ -512,8 +410,6 @@ const templatePageList = computed(() => {
|
|
|
const start = (templatePagination.value.page - 1) * templatePagination.value.pageSize
|
|
|
return templateList.value.slice(start, start + templatePagination.value.pageSize)
|
|
|
})
|
|
|
-// 序号跨页连续(第2页从11开始)
|
|
|
-const templateIndex = (index) => (templatePagination.value.page - 1) * templatePagination.value.pageSize + index + 1
|
|
|
const templateLoading = ref(false)
|
|
|
const templateFormVisible = ref(false)
|
|
|
const templateFormRef = ref(null)
|
|
|
@@ -553,7 +449,7 @@ async function loadTemplateList() {
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-const defaultTemplateForm = () => ({ id: null, planName: '', eventType: '', eventLevel: '', responsibleUnit: '', planSummary: '', planContent: '' })
|
|
|
+const defaultTemplateForm = () => ({ id: null, planName: '', eventType: '', eventLevel: '', responsibleUnit: '', planContent: '' })
|
|
|
|
|
|
function handleTemplateAdd() {
|
|
|
templateForm.value = defaultTemplateForm()
|
|
|
@@ -608,10 +504,6 @@ const formRules = {
|
|
|
changeNote: [{ required: true, message: '请填写修订说明', trigger: 'blur' }],
|
|
|
}
|
|
|
|
|
|
-// 预案附件统一存 MinIO
|
|
|
-const uploadUrl = import.meta.env.VITE_APP_BASE_API + '/minio/upload?dir=dispatch_plan'
|
|
|
-const uploadHeaders = { Authorization: 'Bearer ' + getToken() }
|
|
|
-
|
|
|
const handleEdit = async (row) => {
|
|
|
let detail = { ...row }
|
|
|
try {
|
|
|
@@ -620,63 +512,11 @@ const handleEdit = async (row) => {
|
|
|
} catch {
|
|
|
// 详情接口失败时退回列表行数据
|
|
|
}
|
|
|
- form.value = {
|
|
|
- ...detail,
|
|
|
- changeNote: '',
|
|
|
- fileList: detail.attachName ? [{ name: detail.attachName, url: detail.attachPath }] : []
|
|
|
- }
|
|
|
+ form.value = { ...detail, changeNote: '' }
|
|
|
dialogTitle.value = detail.status === 2 ? '修订预案' : '修改预案'
|
|
|
dialogVisible.value = true
|
|
|
}
|
|
|
|
|
|
-// 附件仅支持 PDF / Word(预案正文文档),单个 ≤50MB;弹窗内支持 Ctrl+V 粘贴
|
|
|
-const ALLOWED_PLAN_EXT = ['pdf', 'doc', 'docx']
|
|
|
-
|
|
|
-function handleBeforeUpload(file) {
|
|
|
- const ext = String(file.name).split('.').pop().toLowerCase()
|
|
|
- if (!ALLOWED_PLAN_EXT.includes(ext)) {
|
|
|
- ElMessage.error('仅支持 PDF / Word 格式文件')
|
|
|
- return false
|
|
|
- }
|
|
|
- if (file.size / 1024 / 1024 >= 50) {
|
|
|
- ElMessage.error('文件大小不能超过 50MB')
|
|
|
- return false
|
|
|
- }
|
|
|
- return true
|
|
|
-}
|
|
|
-
|
|
|
-function handleUploadSuccess(res) {
|
|
|
- if (res.code === 200) {
|
|
|
- form.value.attachName = res.originalFilename
|
|
|
- // 库内只存相对路径(迁移环境不用改数据),展示/下载由后端拼 MinIO 地址
|
|
|
- form.value.attachPath = res.relativePath || res.url
|
|
|
- } else {
|
|
|
- ElMessage.error(res.msg || '上传失败')
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-// 移除已上传文件时同步清空附件字段,避免列表残留旧附件
|
|
|
-function handleUploadRemove() {
|
|
|
- form.value.attachName = ''
|
|
|
- form.value.attachPath = ''
|
|
|
-}
|
|
|
-
|
|
|
-function handleUploadError() {
|
|
|
- ElMessage.error('文件上传失败')
|
|
|
-}
|
|
|
-
|
|
|
-// 弹窗内 Ctrl+V 粘贴文件:塞给 el-upload 走同一套校验与上传流程(预案只允许 1 个附件)
|
|
|
-function handleDialogPaste(event) {
|
|
|
- const files = Array.from(event.clipboardData?.files || [])
|
|
|
- if (!files.length || !uploadRef.value) return
|
|
|
- if ((form.value.fileList?.length || 0) >= 1) {
|
|
|
- ElMessage.warning('只能上传一个文件,请先移除已上传的文件')
|
|
|
- return
|
|
|
- }
|
|
|
- files.slice(0, 1).forEach(file => uploadRef.value.handleStart(file))
|
|
|
- uploadRef.value.submit()
|
|
|
-}
|
|
|
-
|
|
|
const handleSubmit = () => {
|
|
|
formRef.value?.validate(async (valid) => {
|
|
|
if (!valid) return
|
|
|
@@ -688,8 +528,6 @@ const handleSubmit = () => {
|
|
|
planLevel: form.value.planLevel,
|
|
|
compileUnit: form.value.compileUnit,
|
|
|
content: form.value.content,
|
|
|
- attachName: form.value.attachName,
|
|
|
- attachPath: form.value.attachPath,
|
|
|
changeNote: form.value.changeNote
|
|
|
})
|
|
|
dialogVisible.value = false
|
|
|
@@ -727,25 +565,10 @@ const handleBatchDelete = () => {
|
|
|
}).catch(() => {})
|
|
|
}
|
|
|
|
|
|
-// ---------- 下载 / 导出 ----------
|
|
|
-const handleDownload = (row) => {
|
|
|
- if (!row.attachPath) return
|
|
|
- window.open(row.attachPath, '_blank')
|
|
|
-}
|
|
|
-
|
|
|
-// Excel 公式参数转义(URL/文件名一般不含引号,兜底处理)
|
|
|
-const excelQuote = (value) => String(value ?? '').replace(/"/g, '""')
|
|
|
-
|
|
|
-// 附件列单元格:有附件写 HYPERLINK 公式(Excel/WPS 中点击打开附件文件),无附件留空
|
|
|
-function attachCell(row) {
|
|
|
- if (!row.attachPath) return ''
|
|
|
- const name = row.attachName || '附件'
|
|
|
- return `=HYPERLINK("${excelQuote(row.attachPath)}","${excelQuote(name)}")`
|
|
|
-}
|
|
|
-
|
|
|
+// ---------- 导出 ----------
|
|
|
const handleExport = () => {
|
|
|
if (!tableData.value.length) return ElMessage.warning('暂无数据可导出')
|
|
|
- const headers = ['预案编号', '预案名称', '预案类型', '预案级别', '版本', '附件名称', '发布时间', '编制单位', '状态']
|
|
|
+ const headers = ['预案编号', '预案名称', '预案类型', '预案级别', '版本', '发布时间', '编制单位', '状态']
|
|
|
const aoa = [headers]
|
|
|
tableData.value.forEach(row => {
|
|
|
aoa.push([
|
|
|
@@ -754,19 +577,17 @@ const handleExport = () => {
|
|
|
dictLabel(dis_plan_type.value, row.planType),
|
|
|
dictLabel(dis_plan_level.value, row.planLevel),
|
|
|
row.version,
|
|
|
- // 附件列:HYPERLINK 公式,Excel/WPS 中点击即打开 MinIO 附件文件
|
|
|
- attachCell(row),
|
|
|
row.publishDate,
|
|
|
row.compileUnit,
|
|
|
row.statusName
|
|
|
])
|
|
|
})
|
|
|
const ws = XLSX.utils.aoa_to_sheet(aoa)
|
|
|
- ws['!cols'] = [{ wch: 18 }, { wch: 24 }, { wch: 12 }, { wch: 10 }, { wch: 8 }, { wch: 28 }, { wch: 14 }, { wch: 16 }, { wch: 10 }]
|
|
|
+ ws['!cols'] = [{ wch: 18 }, { wch: 24 }, { wch: 12 }, { wch: 10 }, { wch: 8 }, { wch: 14 }, { wch: 16 }, { wch: 10 }]
|
|
|
const wb = XLSX.utils.book_new()
|
|
|
XLSX.utils.book_append_sheet(wb, ws, '应急预案')
|
|
|
XLSX.writeFile(wb, '应急预案_' + new Date().toISOString().slice(0, 10) + '.xlsx')
|
|
|
- ElMessage.success('导出成功(附件列可点击打开附件文件)')
|
|
|
+ ElMessage.success('导出成功')
|
|
|
}
|
|
|
|
|
|
const handleSubmitApproval = (row) => {
|
|
|
@@ -801,11 +622,6 @@ async function handleVersions(row) {
|
|
|
const contentVisible = ref(false)
|
|
|
const currentVersion = ref({})
|
|
|
|
|
|
-// 版本快照附件下载(MinIO 直链)
|
|
|
-const downloadVersionAttach = (row) => {
|
|
|
- if (row?.attachPath) window.open(row.attachPath, '_blank')
|
|
|
-}
|
|
|
-
|
|
|
// 正文为空时给出占位提示,避免 v-html 里再嵌引号
|
|
|
const versionContentHtml = computed(
|
|
|
() => currentVersion.value.content || '<p style="color:#909399">暂无正文</p>'
|
|
|
@@ -873,10 +689,18 @@ async function handlePreviewVersion(row) {
|
|
|
min-width: 0;
|
|
|
}
|
|
|
|
|
|
-.upload-tip {
|
|
|
- font-size: 12px;
|
|
|
- color: #909399;
|
|
|
- margin-top: 4px;
|
|
|
+.plan-actions {
|
|
|
+ display: flex;
|
|
|
+ flex-wrap: nowrap;
|
|
|
+ justify-content: center;
|
|
|
+ align-items: center;
|
|
|
+ gap: 4px;
|
|
|
+ white-space: nowrap;
|
|
|
+
|
|
|
+ :deep(.el-button) {
|
|
|
+ margin-left: 0;
|
|
|
+ flex: 0 0 auto;
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
/* 宽体弹窗:小屏下内容超出时在弹窗体内滚动,footer 始终可见 */
|