模版下载

This commit is contained in:
sjl
2025-08-26 15:39:20 +08:00
parent 1fb833c6ad
commit cf91f53b45
4 changed files with 355 additions and 214 deletions

View File

@@ -366,7 +366,7 @@ export function exportResult(data: any, assessId?: string | number) {
} }
//评估 //导入评估
export const evaluation = (assessId: string, file: File) => { export const evaluation = (assessId: string, file: File) => {
const formData = new FormData() const formData = new FormData()
formData.append('assessId', assessId) formData.append('assessId', assessId)
@@ -380,4 +380,16 @@ export const evaluation = (assessId: string, file: File) => {
'Content-Type': 'multipart/form-data' 'Content-Type': 'multipart/form-data'
} }
}) })
} }
//直接评估
//下载模板
export const downloadAssessTemplate = () => {
return createAxios({
url: '/advance-boot/secondaryEvaluation/downTemplateAssessResult',
method: 'get',
responseType: 'blob'
})
}

View File

@@ -0,0 +1,139 @@
<template>
<el-dialog
draggable
class="cn-operate-dialog"
v-model="eventDataUploadVisible"
:title="title"
style="width: 415px"
top="25vh"
>
<el-scrollbar>
<el-form :inline="false" :model="form" label-width="120px" ref="formRef">
<el-form-item label="用户数据文件">
<el-upload
v-model:file-list="fileList"
ref="uploadEventData"
action=""
:limit="1"
:on-exceed="handleExceed"
:auto-upload="false"
:on-change="choose"
>
<template #trigger>
<el-button type="primary">选择数据文件</el-button>
</template>
</el-upload>
</el-form-item>
</el-form>
</el-scrollbar>
<template #footer>
<span class="dialog-footer">
<el-button @click="eventDataUploadVisible = false">取消</el-button>
<el-button type="primary" @click="submit">评估</el-button>
</span>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { ref, reactive, inject } from 'vue'
import TableStore from '@/utils/tableStore'
import { ElMessage } from 'element-plus'
import type { UploadInstance, UploadProps, UploadRawFile, UploadUserFile } from 'element-plus'
import { genFileId } from 'element-plus'
import { deVReportImportSensitive } from '@/api/supervision-boot/userReport/form'
import {evaluation} from '@/api/advance-boot/assess'
const fileList = ref<UploadUserFile[]>([])
const formRef = ref()
const tableStore = inject('tableStore') as TableStore
const eventDataUploadVisible = ref(false)
const title = ref('')
const uploadEventData = ref<UploadInstance>()
const currentNodeId = ref('')
// 注意不要和表单ref的命名冲突
const form = reactive({
file: null
})
//弹出界面,默认选择用户的第一个生产线的第一条进线进行数据导入
const open = async (text: string , treeId:string) => {
currentNodeId.value = treeId
title.value = text
resetForm()
form.file = null
fileList.value = []
eventDataUploadVisible.value = true
}
//重置表单内容
const resetForm = () => {
if (formRef.value) {
formRef.value.resetFields()
}
}
/**
* 选择待上传文件
*/
const choose = (e: any) => {
form.file = e.raw
}
const handleExceed: UploadProps['onExceed'] = files => {
uploadEventData.value!.clearFiles()
const file = files[0] as UploadRawFile
file.uid = genFileId()
uploadEventData.value!.handleStart(file)
fileList.value = [{ name: file.name, url: '' }]
}
const emit = defineEmits<{
(e: 'data-success'): void
}>()
/**
* 提交用户表单数据
*/
const submit = async () => {
if (form.file) {
formRef.value.validate(async (valid: any) => {
if (valid) {
try {
ElMessage.info('评估中...')
// 调用后端评估接口
const formData = new FormData()
formData.append('assessId',currentNodeId.value)
formData.append('file', form.file)
// 假设你有一个调用评估接口的方法,如 evaluation
// 你需要在 API 文件中实现这个方法
const response = await evaluation(currentNodeId.value, form.file)
if (response.data) {
ElMessage.success('评估成功!')
emit('data-success')
} else {
ElMessage.error('评估失败!')
}
eventDataUploadVisible.value = false
} catch (error) {
ElMessage.error('评估失败,请重试!')
}
}
})
} else {
ElMessage.error('请选择数据文件')
}
}
defineExpose({ open })
</script>
<style scoped>
.el-form-item__content div {
width: 100% !important;
}
</style>

View File

@@ -1,57 +1,50 @@
<template> <template>
<div :style="height"> <div :style="height">
<splitpanes style="height: 100%" class="default-theme" id="navigation-splitpanes"> <splitpanes style="height: 100%" class="default-theme" id="navigation-splitpanes">
<pane :size="size"> <pane :size="size">
<AssessTree <AssessTree
:default-expand-all="false" :default-expand-all="false"
@node-click="handleNodeClick" @node-click="handleNodeClick"
@init="handleNodeClick" @init="handleNodeClick"
ref="assessTreeRef" ref="assessTreeRef"
:current-node-key="currentTreeKey" :current-node-key="currentTreeKey"
></AssessTree> ></AssessTree>
</pane> </pane>
<pane style="background: #fff"> <pane style="background: #fff">
<div class="actionButtons mb10"> <div class="actionButtons mb10">
<el-button type="primary" icon="el-icon-Upload" @click="handleImportClick">导入数据背景</el-button> <el-button icon="el-icon-Download" type="primary" @click="exportExcelTemplate" :loading="loading">模版下载</el-button>
<el-upload <el-button type="primary" icon="el-icon-Upload" @click="handleImportClick">导入数据背景</el-button>
ref="uploadRef" <el-button type="primary" icon="el-icon-Memo" class="ml10" @click="assess2">评估</el-button>
class="import-data-upload" <el-button type="primary" icon="el-icon-Download" @click="exportReport">导出报告</el-button>
action="#" </div>
:auto-upload="false" <div :style="collapseHeight" style="overflow-y: auto">
:show-file-list="false" <el-collapse v-model="collapseName" class="pl10 pr10">
:on-change="handleFileChange" <el-collapse-item title="基本信息" :name="1">
:accept="'.xlsx,.xls,.csv'" <div class="loading-container">
style="display: none" <div v-if="infoLoading" class="loading-wrapper">
>
</el-upload>
<el-button type="primary" icon="el-icon-Memo" class="ml10" @click="assess">评估</el-button>
<el-button type="primary" icon="el-icon-Download" @click="exportReport">导出报告</el-button>
</div>
<div :style="collapseHeight" style="overflow-y: auto">
<el-collapse v-model="collapseName" class="pl10 pr10">
<el-collapse-item title="基本信息" :name="1">
<div class="loading-container">
<div v-if="infoLoading" class="loading-wrapper">
<el-icon class="is-loading"><Loading /></el-icon>
<span class="loading-text">加载中...</span>
</div>
<information v-show="!infoLoading" :node-id="currentNodeId" @data-loaded="() => infoLoading = false" />
</div>
</el-collapse-item>
<el-collapse-item title="评估结果信息" :name="2">
<div class="loading-container">
<div v-if="outcomeLoading" class="loading-wrapper">
<el-icon class="is-loading"><Loading /></el-icon> <el-icon class="is-loading"><Loading /></el-icon>
<span class="loading-text">加载中...</span> <span class="loading-text">加载中...</span>
</div> </div>
<Outcome v-show="!outcomeLoading" :node-id="currentNodeId" @data-status="handleDataStatus" @data-loaded="() => outcomeLoading = false" /> <information v-show="!infoLoading" :node-id="currentNodeId" @data-loaded="() => infoLoading = false" />
</div> </div>
</el-collapse-item> </el-collapse-item>
</el-collapse> <el-collapse-item title="评估结果信息" :name="2">
</div> <div class="loading-container">
</pane> <div v-if="outcomeLoading" class="loading-wrapper">
</splitpanes> <el-icon class="is-loading"><Loading /></el-icon>
</div> <span class="loading-text">加载中...</span>
</div>
<Outcome v-show="!outcomeLoading" :node-id="currentNodeId" @data-status="handleDataStatus" @data-loaded="() => outcomeLoading = false" />
</div>
</el-collapse-item>
</el-collapse>
</div>
</pane>
</splitpanes>
</div>
<AssessTemplate ref="assessTemplate" @data-success = "assess"></AssessTemplate>
</template> </template>
<script setup lang="ts"> <script setup lang="ts">
@@ -65,11 +58,13 @@ import TableHeader from '@/components/table/header/index.vue'
import { mainHeight } from '@/utils/layout' import { mainHeight } from '@/utils/layout'
import information from './information.vue' import information from './information.vue'
import Outcome from './outcome.vue' import Outcome from './outcome.vue'
import {exportResult,evaluation} from '@/api/advance-boot/assess' import {exportResult,downloadAssessTemplate} from '@/api/advance-boot/assess'
import { Loading } from '@element-plus/icons-vue' import { Loading } from '@element-plus/icons-vue'
import AssessTemplate from './assessTemplate.vue'
defineOptions({ defineOptions({
// name: 'harmonic-boot/report/word' // name: 'harmonic-boot/report/word'
}) })
const collapseName = ref([1, 2]) const collapseName = ref([1, 2])
const height = mainHeight(80) const height = mainHeight(80)
@@ -78,147 +73,138 @@ const size = ref(0)
const dotList: any = ref({}) const dotList: any = ref({})
const infoLoading = ref(false) const infoLoading = ref(false)
const outcomeLoading = ref(false) const outcomeLoading = ref(false)
const loading = ref(false)
const assessTemplate = ref()
// 添加当前节点 ID 的响应式变量 // 添加当前节点 ID 的响应式变量
const currentNodeId = ref<string | number | null>(null) const currentNodeId = ref<string | number | null>(null)
const currentNodeName = ref('')
const tableStore = new TableStore({ const tableStore = new TableStore({
url: '', url: '',
method: 'POST', method: 'POST',
column: [], column: [],
beforeSearchFun: () => {}, beforeSearchFun: () => {},
loadCallback: () => {} loadCallback: () => {}
}) })
provide('tableStore', tableStore) provide('tableStore', tableStore)
//有无评估结果 //有无评估结果
const result = ref(true) const result = ref(true)
const handleDataStatus = (hasData: boolean) => { const handleDataStatus = (hasData: boolean) => {
if (!hasData) { if (!hasData) {
result.value = false result.value = false
} else { } else {
result.value = true result.value = true
} }
} }
//模板下载
const exportExcelTemplate = async () => {
loading.value = true
downloadAssessTemplate().then((res: any) => {
let blob = new Blob([res], {
type: 'application/vnd.ms-excel'
})
const url = window.URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.download = '评估结果模板'
document.body.appendChild(link)
link.click()
link.remove()
})
await setTimeout(() => {
loading.value = false
}, 0)
}
const exportReport = () => { const exportReport = () => {
if (!result.value){ if (!result.value){
ElMessage.warning('请先评估!') ElMessage.warning('请先评估!')
return return
} }
// 显示导出中提示 // 显示导出中提示
const loading = ElLoading.service({ const loading = ElLoading.service({
lock: true, lock: true,
text: '导出中...', text: '导出中...',
background: 'rgba(0, 0, 0, 0.7)' background: 'rgba(0, 0, 0, 0.7)'
}) })
exportResult({}, currentNodeId.value).then((res: any) => { exportResult({}, currentNodeId.value).then((res: any) => {
let blob = new Blob([res], { // 注意使用 res.data 而不是 res let blob = new Blob([res], { // 注意使用 res.data 而不是 res
type: 'application/vnd.ms-excel' type: 'application/vnd.ms-excel'
}) })
const url = window.URL.createObjectURL(blob) const url = window.URL.createObjectURL(blob)
const link = document.createElement('a') const link = document.createElement('a')
link.href = url link.href = url
link.download = '评估结果.xls' // 建议添加文件扩展名 link.download = currentNodeName.value + '-评估结果.xls' // 建议添加文件扩展名
document.body.appendChild(link) document.body.appendChild(link)
link.click() link.click()
link.remove() link.remove()
window.URL.revokeObjectURL(url) window.URL.revokeObjectURL(url)
// 关闭加载提示并显示成功消息 // 关闭加载提示并显示成功消息
loading.close() loading.close()
ElMessage.success('导出成功!') ElMessage.success('导出成功!')
}).catch(error => { }).catch(error => {
// 处理导出失败情况 // 处理导出失败情况
loading.close() loading.close()
ElMessage.error('导出失败,请重试!') ElMessage.error('导出失败,请重试!')
}) })
} }
// 添加文件上传相关引用
const uploadRef = ref()
const selectedFile = ref<File | null>(null)
const handleImportClick = () => { const handleImportClick = () => {
// 手动触发文件选择 if (!currentNodeId.value) {
const inputElement = uploadRef.value.$el.querySelector('input[type="file"]') ElMessage.warning('请选择评估节点!')
if (inputElement) { return
inputElement.click() }
} assessTemplate.value.open('导入背景数据', currentNodeId.value.toString())
} }
const handleFileChange = (file: any) => {
const selectedFileObj = file.raw
if (selectedFileObj) {
selectedFile.value = selectedFileObj
ElMessage.success(`已选择文件: ${selectedFileObj.name}`)
}
}
// 评估功能 // 导入通知父组件更新
const assess = async () => { const assess = async () => {
// 检查是否已选择文件
if (!selectedFile.value) { // 展开评估结果
ElMessage.warning('请先导入数据背景文件!') collapseName.value = [2]
return // 更新评估状态
} result.value = true
// 检查是否已选择节点 // ✅ 强制更新 nodeId触发子组件的 watch
if (!currentNodeId.value) { // 可以通过修改 currentNodeId 的值,或者重新赋值来触发 watch
ElMessage.warning('请选择评估节点!') const tempId = currentNodeId.value
return currentNodeId.value = null // 先置空
} setTimeout(() => {
currentNodeId.value = tempId // 再恢复,触发 watch
try { }, 0)
ElMessage.info('评估中...')
// 调用后端评估接口
const formData = new FormData()
formData.append('assessId', currentNodeId.value.toString())
formData.append('file', selectedFile.value)
// 假设你有一个调用评估接口的方法,如 evaluation
// 你需要在 API 文件中实现这个方法
const response = await evaluation(currentNodeId.value.toString(), selectedFile.value)
//loading.close()
selectedFile.value = null
if (response.data) {
ElMessage.success('评估成功!')
// 展开评估结果
collapseName.value = [2]
// 更新评估状态
result.value = true
// ✅ 强制更新 nodeId触发子组件的 watch
// 可以通过修改 currentNodeId 的值,或者重新赋值来触发 watch
const tempId = currentNodeId.value
currentNodeId.value = null // 先置空
setTimeout(() => {
currentNodeId.value = tempId // 再恢复,触发 watch
}, 0)
} else {
ElMessage.error('评估失败!')
result.value = false
}
} catch (error) {
ElMessage.error('评估失败,请重试!')
result.value = false
}
} }
//评估
const assess2 = async () => {
// 展开评估结果
collapseName.value = [2]
// 更新评估状态
result.value = true
const tempId = currentNodeId.value
currentNodeId.value = null // 先置空
setTimeout(() => {
currentNodeId.value = tempId // 再恢复,触发 watch
}, 0)
}
onMounted(() => { onMounted(() => {
const dom = document.getElementById('navigation-splitpanes') const dom = document.getElementById('navigation-splitpanes')
setTimeout(() => { setTimeout(() => {
if (dom) { if (dom) {
size.value = Math.round((180 / dom.offsetHeight) * 100) size.value = Math.round((180 / dom.offsetHeight) * 100)
} }
}, 100) }, 100)
}) })
// 添加树组件的引用 // 添加树组件的引用
@@ -229,82 +215,82 @@ const currentTreeKey = ref<string | number | null>(null)
const lastValidNodeKey = ref<string | number | null>(null) const lastValidNodeKey = ref<string | number | null>(null)
const handleNodeClick = (data: any, node: any) => { const handleNodeClick = (data: any, node: any) => {
// 阻止点击父节点的逻辑处理 // 阻止点击父节点的逻辑处理
if (!data || if (!data ||
(node && !node.isLeaf) || (node && !node.isLeaf) ||
(data.children && data.children.length > 0) || (data.children && data.children.length > 0) ||
(node && node.childNodes && node.childNodes.length > 0)) { (node && node.childNodes && node.childNodes.length > 0)) {
// 点击父节点时,保持树的高亮状态为上一次有效的子节点
currentTreeKey.value = lastValidNodeKey.value
return
}
// 点击的是子节点,更新高亮状态 // 点击父节点时,保持树的高亮状态为上一次有效的子节点
lastValidNodeKey.value = data?.id || null
currentTreeKey.value = lastValidNodeKey.value currentTreeKey.value = lastValidNodeKey.value
dotList.value = data return
currentNodeId.value = data?.id || null }
// 点击的是子节点,更新高亮状态
lastValidNodeKey.value = data?.id || null
currentTreeKey.value = lastValidNodeKey.value
dotList.value = data
currentNodeId.value = data?.id || null
currentNodeName.value = data?.name || ''
// ✅ 开启两个 loading // ✅ 开启两个 loading
infoLoading.value = true infoLoading.value = true
outcomeLoading.value = true outcomeLoading.value = true
} }
</script> </script>
<style lang="scss" scoped> <style lang="scss" scoped>
.splitpanes.default-theme .splitpanes__pane { .splitpanes.default-theme .splitpanes__pane {
background: #eaeef1; background: #eaeef1;
} }
:deep(.el-collapse-item__header) { :deep(.el-collapse-item__header) {
// font-family: AlimamaDongFangDaKai; // font-family: AlimamaDongFangDaKai;
font-size: 20px; font-size: 20px;
&::before { &::before {
content: '▍'; /* 添加星号 */ content: '▍'; /* 添加星号 */
font-size: 16px; font-size: 16px;
color: var(--el-color-primary); color: var(--el-color-primary);
} }
} }
.actionButtons { .actionButtons {
display: flex; display: flex;
justify-content: end; justify-content: end;
} }
.loading-container { .loading-container {
position: relative; position: relative;
min-height: 100px; /* 防止空白时高度塌陷 */ min-height: 100px; /* 防止空白时高度塌陷 */
} }
.loading-wrapper { .loading-wrapper {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
padding: 20px; padding: 20px;
} }
.loading-text { .loading-text {
margin-left: 8px; margin-left: 8px;
color: #606266; color: #606266;
} }
.is-loading { .is-loading {
animation: rotating 2s linear infinite; animation: rotating 2s linear infinite;
} }
@keyframes rotating { @keyframes rotating {
0% { 0% {
transform: rotate(0deg); transform: rotate(0deg);
} }
100% { 100% {
transform: rotate(360deg); transform: rotate(360deg);
} }
} }
.ml10 { .ml10 {
margin-left: 10px; margin-left: 10px;
} }
</style> </style>

View File

@@ -393,8 +393,12 @@ const typeId = ref(null)
// 过滤数据 // 过滤数据
const formatter = (row: any) => { const formatter = (row: any) => {
if (row.column.field == 'transtypeId') { if (row.column.field == 'transtypeId') {
const found = transformer.value.find((item: any) => item.transtypeId == row.cellValue) if (row.cellValue) {
return found.transtypeName || '' // 使用可选链并提供默认值 const found = transformer.value.find((item: any) => item.transtypeId == row.cellValue)
return found?.transtypeName || ''
} else {
return '' // 或者返回默认值
}
}else if(row.column.field =='inpactloadtypeId'){ }else if(row.column.field =='inpactloadtypeId'){
const found = shockRef.value.find((item: any) => item.inpactloadtypeId == row.cellValue) const found = shockRef.value.find((item: any) => item.inpactloadtypeId == row.cellValue)
typeId.value = found.inpactloadtypeId typeId.value = found.inpactloadtypeId