xlcp-frontend/src/views/prison/report-template/ReportTemplateForm.vue
tangweijie bbf4c64391 feat(prison): 新增评估报告等前端页面,优化问卷与罪犯管理
核心变更:
1. 新增页面模块
   - 快捷评语管理 (quick-comment)
   - 报告模板管理 (report-template)
   - 评估报告编辑 (report)
   - 风险分析页面 (risk)
   - 预警管理 (warning)
   - 服刑情况跟踪 (situation)

2. 功能优化
   - 罪犯管理: 新增Workbench工作台页面
   - 问卷模块: 完善QuestionForm组件
   - 计分考核: 优化ScoreForm支持多种评分方式
   - 危险评估: 完善RiskAssessmentForm

3. UI改进
   - 登录页面: 新增监狱特色Loading动画
   - 罪犯详情: 优化展示效果
   - 消费记录: 增强查询功能

4. 基础设施
   - 新增JusticeIcon图标组件
   - 优化字典格式化工具
   - 更新路由配置

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-16 20:15:17 +08:00

312 lines
10 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>
<Dialog :title="dialogTitle" v-model="dialogVisible" width="900px">
<el-form
ref="formRef"
:model="formData"
:rules="formRules"
label-width="100px"
v-loading="formLoading"
>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="模板名称" prop="name">
<el-input v-model="formData.name" placeholder="请输入模板名称" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="模板类型" prop="type">
<el-select v-model="formData.type" placeholder="请选择模板类型" class="!w-100%">
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.PRISON_REPORT_TEMPLATE_TYPE)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="24">
<el-form-item label="标题格式" prop="titleFormat">
<el-input v-model="formData.titleFormat" placeholder="如:{罪犯姓名}服刑期间综合评估报告" />
<div class="form-tip">可使用变量:{罪犯姓名}、{评估日期}、{监区名称}</div>
</el-form-item>
</el-col>
</el-row>
<el-row :gutter="20">
<el-col :span="12">
<el-form-item label="状态" prop="status">
<el-radio-group v-model="formData.status">
<el-radio
v-for="dict in getIntDictOptions(DICT_TYPE.PRISON_COMMON_STATUS)"
:key="dict.value"
:value="dict.value"
>{{ dict.label }}</el-radio>
</el-radio-group>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="设为默认" prop="isDefault">
<el-switch v-model="formData.isDefault" />
</el-form-item>
</el-col>
</el-row>
<!-- 评估维度配置 -->
<el-form-item label="评估维度" prop="dimensions">
<div class="dimension-container">
<div class="dimension-header">
<el-button type="primary" link @click="handleAddDimension">
<Icon icon="ep:plus" class="mr-5px" /> 添加维度
</el-button>
</div>
<el-table :data="formData.dimensions" border style="width: 100%">
<el-table-column label="排序" width="80">
<template #default="{ $index }">
<el-input-number
v-model="formData.dimensions[$index].sort"
:min="0"
:max="999"
size="small"
/>
</template>
</el-table-column>
<el-table-column label="维度名称" min-width="150">
<template #default="{ row }">
<el-input v-model="row.name" placeholder="如:犯罪情况分析" size="small" />
</template>
</el-table-column>
<el-table-column label="数据源" min-width="200">
<template #default="{ row }">
<el-select
v-model="row.dataSources"
multiple
placeholder="选择数据源"
size="small"
class="!w-100%"
>
<el-option
v-for="ds in dataSourceOptions"
:key="ds.value"
:label="ds.label"
:value="ds.value"
/>
</el-select>
</template>
</el-table-column>
<el-table-column label="AI生成" width="100" align="center">
<template #default="{ row }">
<el-switch v-model="row.enableAi" size="small" />
</template>
</el-table-column>
<el-table-column label="输出格式" width="120">
<template #default="{ row }">
<el-select v-model="row.outputFormat" size="small" class="!w-100%">
<el-option label="文本" value="text" />
<el-option label="段落" value="paragraph" />
<el-option label="列表" value="list" />
</el-select>
</template>
</el-table-column>
<el-table-column label="编辑器" width="120">
<template #default="{ row }">
<el-select v-model="row.editorType" size="small" class="!w-100%">
<el-option label="文本框" value="text" />
<el-option label="富文本" value="richtext" />
<el-option label="下拉选择" value="select" />
</el-select>
</template>
</el-table-column>
<el-table-column label="操作" width="80" align="center">
<template #default="{ $index }">
<el-button type="danger" link size="small" @click="handleRemoveDimension($index)">
<Icon icon="ep:delete" />
</el-button>
</template>
</el-table-column>
</el-table>
</div>
</el-form-item>
<!-- AI提示词配置 -->
<el-form-item label="AI提示词" prop="aiPromptConfig">
<el-input
v-model="formData.aiPromptConfig"
type="textarea"
:rows="6"
placeholder="请输入AI提示词模板用于指导AI生成评估内容"
/>
<div class="form-tip">
提示:可以使用变量,如 {罪犯档案数据}、{考核数据}、{消费数据} 等
</div>
</el-form-item>
<el-form-item label="备注" prop="remark">
<el-input v-model="formData.remark" type="textarea" :rows="3" placeholder="请输入备注" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="submitForm" type="primary" :disabled="formLoading"> </el-button>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
</template>
<script setup lang="ts">
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import { ReportTemplateApi, ReportTemplate, ReportDimension } from '@/api/prison/report'
/** 评估报告模板 表单 */
defineOptions({ name: 'ReportTemplateForm' })
const { t } = useI18n()
const message = useMessage()
const dialogVisible = ref(false)
const dialogTitle = ref('')
const formLoading = ref(false)
const formType = ref('')
const formRef = ref()
// 可选数据源
const dataSourceOptions = [
{ value: 'prisoner', label: '罪犯档案' },
{ value: 'consumption', label: '消费记录' },
{ value: 'score', label: '计分考核' },
{ value: 'questionnaire_record', label: '问卷答题' },
{ value: 'risk_assessment', label: '风险评估' },
{ value: 'violation', label: '违规记录' },
{ value: 'reward', label: '奖励记录' },
{ value: 'visit', label: '会见记录' },
{ value: 'labor', label: '劳动数据' },
{ value: 'family', label: '家庭帮教' },
{ value: 'psychology', label: '心理测评' }
]
const formData = ref({
id: undefined,
name: '',
type: undefined,
titleFormat: '',
dimensions: [] as ReportDimension[],
aiPromptConfig: '',
styleConfig: '',
status: 1,
isDefault: false,
version: 1,
remark: ''
})
const formRules = reactive({
name: [{ required: true, message: '模板名称不能为空', trigger: 'blur' }],
type: [{ required: true, message: '模板类型不能为空', trigger: 'change' }],
titleFormat: [{ required: true, message: '标题格式不能为空', trigger: 'blur' }]
})
/** 打开弹窗 */
const open = async (type: string, id?: number) => {
dialogVisible.value = true
dialogTitle.value = t('action.' + type)
formType.value = type
resetForm()
if (id) {
formLoading.value = true
try {
const data = await ReportTemplateApi.getTemplate(id)
formData.value = {
...data,
dimensions: data.dimensions || []
}
} finally {
formLoading.value = false
}
}
}
defineExpose({ open })
/** 提交表单 */
const emit = defineEmits(['success'])
const submitForm = async () => {
await formRef.value.validate()
formLoading.value = true
try {
const data = formData.value
if (formType.value === 'create') {
await ReportTemplateApi.createTemplate(data)
message.success(t('common.createSuccess'))
} else {
await ReportTemplateApi.updateTemplate(data)
message.success(t('common.updateSuccess'))
}
dialogVisible.value = false
emit('success')
} finally {
formLoading.value = false
}
}
/** 重置表单 */
const resetForm = () => {
formData.value = {
id: undefined,
name: '',
type: undefined,
titleFormat: '',
dimensions: [
{ name: '基本信息', dataSources: ['prisoner'], outputFormat: 'text', enableAi: false, editorType: 'text', sort: 1 },
{ name: '服刑表现评估', dataSources: ['score'], outputFormat: 'paragraph', enableAi: true, editorType: 'richtext', sort: 2 },
{ name: '消费行为分析', dataSources: ['consumption'], outputFormat: 'paragraph', enableAi: true, editorType: 'richtext', sort: 3 },
{ name: '综合评估结论', dataSources: ['prisoner', 'score', 'risk_assessment'], outputFormat: 'paragraph', enableAi: true, editorType: 'richtext', sort: 4 }
],
aiPromptConfig: '',
styleConfig: '',
status: 1,
isDefault: false,
version: 1,
remark: ''
}
formRef.value?.resetFields()
}
/** 添加维度 */
const handleAddDimension = () => {
formData.value.dimensions.push({
name: '',
dataSources: [],
outputFormat: 'paragraph',
enableAi: true,
editorType: 'richtext',
sort: formData.value.dimensions.length + 1
})
}
/** 删除维度 */
const handleRemoveDimension = (index: number) => {
formData.value.dimensions.splice(index, 1)
}
</script>
<style lang="scss" scoped>
.dimension-container {
width: 100%;
.dimension-header {
margin-bottom: 10px;
}
}
.form-tip {
font-size: 12px;
color: #909399;
margin-top: 5px;
line-height: 1.4;
}
</style>