tangweijie 4bac14276a 重构评估报告模块为评估管理模块
- 重命名report为evaluation-report,更新相关API接口
- 重构评估模板、评估维度、评估报告、快捷评语等模块
- 新增评估管理页面,包含模板配置、维度管理、报告生成等功能
- 更新囚犯工作台,增加评估报告相关功能
- 删除旧的report相关组件
2026-01-19 22:19:23 +08:00

340 lines
12 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<ContentWrap>
<el-row :gutter="20">
<!-- 左侧模板列表 -->
<el-col :span="8">
<ContentWrap title="模板列表" class="!mb-0">
<template #header>
<el-button type="primary" @click="openTemplateForm('create')" v-hasPermi="['prison:evaluation-report:template:create']">
<Icon icon="ep:plus" class="mr-5px" /> 新建模板
</el-button>
</template>
<el-input v-model="templateFilter" placeholder="搜索模板" class="!mb-15px" clearable />
<div class="template-list">
<el-card
v-for="template in filteredTemplateList"
:key="template.id"
class="template-card mb-10px cursor-pointer"
:class="{ 'template-card-active': selectedTemplate?.id === template.id }"
shadow="hover"
@click="selectTemplate(template)"
>
<div class="template-card-header">
<span class="template-name">{{ template.name }}</span>
<el-tag :type="template.status === 1 ? 'success' : 'info'" size="small">
{{ template.status === 1 ? '启用' : '停用' }}
</el-tag>
</div>
<div class="template-info">
<span class="text-gray-500 text-12px">
{{ getDictLabel(DICT_TYPE.PRISON_REPORT_TEMPLATE_TYPE, template.type) }}
</span>
<span class="text-gray-500 text-12px">
{{ getDictLabel(DICT_TYPE.PRISON_EVALUATION_CYCLE, template.evaluationCycle) }}
</span>
</div>
<div class="template-actions">
<el-button type="primary" link size="small" @click.stop="openTemplateForm('update', template.id)">
编辑
</el-button>
<el-button type="success" link size="small" @click.stop="copyTemplate(template)">
复制
</el-button>
<el-button type="danger" link size="small" @click.stop="handleDeleteTemplate(template.id!)" v-hasPermi="['prison:evaluation-report:template:delete']">
删除
</el-button>
</div>
</el-card>
</div>
<el-empty v-if="templateList.length === 0" description="暂无模板" />
</ContentWrap>
</el-col>
<!-- 右侧:模板详情和维度配置 -->
<el-col :span="16">
<ContentWrap v-if="selectedTemplate" class="!mb-0">
<template #header>
<span class="text-16px font-bold">模板详情</span>
</template>
<el-tabs v-model="activeTab">
<!-- 基本信息 Tab -->
<el-tab-pane label="基本信息" name="basic">
<el-descriptions :column="2" border>
<el-descriptions-item label="模板名称">{{ selectedTemplate.name }}</el-descriptions-item>
<el-descriptions-item label="模板编码">{{ selectedTemplate.code }}</el-descriptions-item>
<el-descriptions-item label="模板类型">
{{ getDictLabel(DICT_TYPE.PRISON_REPORT_TEMPLATE_TYPE, selectedTemplate.type) }}
</el-descriptions-item>
<el-descriptions-item label="评估周期">
{{ getDictLabel(DICT_TYPE.PRISON_EVALUATION_CYCLE, selectedTemplate.evaluationCycle) }}
</el-descriptions-item>
<el-descriptions-item label="适用人群">{{ selectedTemplate.applicableCrowd || '-' }}</el-descriptions-item>
<el-descriptions-item label="AI生成">
<el-tag :type="selectedTemplate.aiEnabled === 1 ? 'success' : 'info'" size="small">
{{ selectedTemplate.aiEnabled === 1 ? '启用' : '禁用' }}
</el-tag>
</el-descriptions-item>
<el-descriptions-item label="排序" :span="2">{{ selectedTemplate.sort }}</el-descriptions-item>
<el-descriptions-item label="描述" :span="2">{{ selectedTemplate.description || '-' }}</el-descriptions-item>
<el-descriptions-item label="AI提示词" :span="2">
<el-input type="textarea" :rows="3" :value="selectedTemplate.aiPrompt" readonly />
</el-descriptions-item>
</el-descriptions>
<div class="mt-15px text-right">
<el-button type="primary" @click="openTemplateForm('update', selectedTemplate.id)">
<Icon icon="ep:edit" class="mr-5px" /> 编辑基本信息
</el-button>
</div>
</el-tab-pane>
<!-- 维度配置 Tab -->
<el-tab-pane label="维度配置" name="dimension">
<div class="dimension-header mb-15px">
<el-button type="primary" @click="openDimensionForm(selectedTemplate.id!)" v-hasPermi="['prison:evaluation-report:dimension:create']">
<Icon icon="ep:plus" class="mr-5px" /> 新增维度
</el-button>
</div>
<div class="dimension-list">
<el-card
v-for="(dimension, index) in dimensionList"
:key="dimension.id"
class="dimension-card mb-10px"
shadow="hover"
>
<div class="dimension-header">
<div class="dimension-title">
<span class="dimension-sort">{{ index + 1 }}</span>
<span class="dimension-name">{{ dimension.name }}</span>
<el-tag v-if="dimension.aiEnabled === 1" type="success" size="small">AI生成</el-tag>
</div>
<div class="dimension-actions">
<el-button type="primary" link size="small" @click="openDimensionForm(selectedTemplate.id!, dimension.id)">
编辑
</el-button>
<el-button type="danger" link size="small" @click="handleDeleteDimension(dimension.id!)" v-hasPermi="['prison:evaluation-report:dimension:delete']">
删除
</el-button>
</div>
</div>
<div class="dimension-info">
<el-tag>{{ getDictLabel(DICT_TYPE.PRISON_EVALUATION_DIMENSION_TYPE, dimension.dimensionType) }}</el-tag>
<el-tag>{{ getDictLabel(DICT_TYPE.PRISON_EVALUATION_OUTPUT_FORMAT, dimension.outputFormat) }}</el-tag>
<el-tag>{{ getDictLabel(DICT_TYPE.PRISON_EVALUATION_METHOD, dimension.evaluationMethod) }}</el-tag>
</div>
<div class="dimension-desc text-gray-500 text-12px mt-5px" v-if="dimension.description">
{{ dimension.description }}
</div>
</el-card>
<el-empty v-if="dimensionList.length === 0" description="暂无维度配置,请添加维度" />
</div>
</el-tab-pane>
</el-tabs>
</ContentWrap>
<ContentWrap v-else class="!mb-0">
<el-empty description="请选择左侧模板查看详情" />
</ContentWrap>
</el-col>
</el-row>
</ContentWrap>
<!-- 模板表单弹窗 -->
<TemplateForm ref="templateFormRef" @success="getTemplateList" />
<!-- 维度表单弹窗 -->
<DimensionForm ref="dimensionFormRef" @success="getDimensionsByTemplateId" />
</template>
<script setup lang="ts">
import { DICT_TYPE, getDictLabel } from '@/utils/dict'
import { TemplateApi, TemplateVO, DimensionApi, DimensionVO } from '@/api/prison/evaluation-report'
import TemplateForm from './TemplateForm.vue'
import DimensionForm from './DimensionForm.vue'
defineOptions({ name: 'EvaluationTemplate' })
const { t } = useI18n()
const message = useMessage()
const loading = ref(true)
const templateList = ref<TemplateVO[]>([])
const templateFilter = ref('')
const selectedTemplate = ref<TemplateVO | null>(null)
const activeTab = ref('basic')
const dimensionList = ref<DimensionVO[]>([])
const templateFormRef = ref()
const dimensionFormRef = ref()
/** 获取模板列表 */
const getTemplateList = async () => {
loading.value = true
try {
const data = await TemplateApi.getTemplatePage({ pageNo: 1, pageSize: 100 })
templateList.value = data.list
} finally {
loading.value = false
}
}
/** 过滤后的模板列表 */
const filteredTemplateList = computed(() => {
if (!templateFilter.value) return templateList.value
return templateList.value.filter(item =>
item.name?.includes(templateFilter.value) ||
item.code?.includes(templateFilter.value)
)
})
/** 选择模板 */
const selectTemplate = async (template: TemplateVO) => {
selectedTemplate.value = template
activeTab.value = 'basic'
await getDimensionsByTemplateId(template.id!)
}
/** 根据模板ID获取维度列表 */
const getDimensionsByTemplateId = async (templateId: number) => {
if (!templateId) return
dimensionList.value = await DimensionApi.getDimensionsByTemplateId(templateId)
}
/** 打开模板表单 */
const openTemplateForm = (type: string, id?: number) => {
templateFormRef.value.open(type, id)
}
/** 打开维度表单 */
const openDimensionForm = (templateId: number, id?: number) => {
dimensionFormRef.value.open('create', templateId, id)
}
/** 复制模板 */
const copyTemplate = async (template: TemplateVO) => {
try {
await message.confirm('确定要复制该模板吗?')
const newTemplate: TemplateVO = {
...template,
id: undefined,
name: `${template.name}_副本`,
code: `${template.code}_copy`,
status: 0 // 复制后默认停用
}
delete newTemplate.id
await TemplateApi.createTemplate(newTemplate)
message.success('复制成功')
getTemplateList()
} catch {}
}
/** 删除模板 */
const handleDeleteTemplate = async (id: number) => {
try {
await message.delConfirm()
await TemplateApi.deleteTemplate(id)
message.success(t('common.delSuccess'))
if (selectedTemplate.value?.id === id) {
selectedTemplate.value = null
dimensionList.value = []
}
getTemplateList()
} catch {}
}
/** 删除维度 */
const handleDeleteDimension = async (id: number) => {
try {
await message.delConfirm()
await DimensionApi.deleteDimension(id)
message.success(t('common.delSuccess'))
getDimensionsByTemplateId(selectedTemplate.value?.id!)
} catch {}
}
/** 初始化 */
onMounted(() => {
getTemplateList()
})
</script>
<style lang="scss" scoped>
.template-list {
max-height: calc(100vh - 280px);
overflow-y: auto;
}
.template-card {
transition: all 0.3s;
&.template-card-active {
border-color: var(--el-color-primary);
background-color: var(--el-color-primary-light-9);
}
.template-card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
}
.template-name {
font-weight: 500;
font-size: 14px;
}
.template-info {
display: flex;
gap: 10px;
margin-bottom: 8px;
}
.template-actions {
border-top: 1px solid var(--el-border-color-lighter);
padding-top: 8px;
}
}
.dimension-list {
max-height: calc(100vh - 350px);
overflow-y: auto;
}
.dimension-card {
.dimension-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
}
.dimension-title {
display: flex;
align-items: center;
gap: 10px;
}
.dimension-sort {
width: 20px;
height: 20px;
line-height: 20px;
text-align: center;
background-color: var(--el-color-primary);
color: #fff;
border-radius: 50%;
font-size: 12px;
}
.dimension-name {
font-weight: 500;
}
.dimension-info {
display: flex;
gap: 8px;
}
.dimension-desc {
line-height: 1.5;
}
}
</style>