综合报告
This commit is contained in:
parent
343cb8c76d
commit
180ccde18b
|
|
@ -6,7 +6,7 @@ spring:
|
||||||
druid:
|
druid:
|
||||||
# 主库数据源
|
# 主库数据源
|
||||||
master:
|
master:
|
||||||
url: jdbc:mysql://127.0.0.1:3306/ry_xinli?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
|
url: jdbc:mysql://1.15.149.240:3306/ry_xinli?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true
|
||||||
username: ry_xinli
|
username: ry_xinli
|
||||||
password: ZLZBcfGtsWJe5r4z
|
password: ZLZBcfGtsWJe5r4z
|
||||||
# 从库数据源
|
# 从库数据源
|
||||||
|
|
|
||||||
190
sql/添加综合评估菜单.sql
Normal file
190
sql/添加综合评估菜单.sql
Normal file
|
|
@ -0,0 +1,190 @@
|
||||||
|
-- 添加综合评估菜单
|
||||||
|
-- 菜单类型:C(菜单)
|
||||||
|
-- 父菜单ID:2009(心理测评管理)
|
||||||
|
-- 排序:在报告管理之后,设置为14(在主观题评分order_num=13之后)
|
||||||
|
|
||||||
|
-- 设置字符集(确保中文正确显示)
|
||||||
|
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
|
||||||
|
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
|
||||||
|
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
|
||||||
|
/*!50503 SET NAMES utf8mb4 */;
|
||||||
|
|
||||||
|
-- 添加综合评估菜单(菜单项)
|
||||||
|
INSERT INTO `sys_menu` (
|
||||||
|
`menu_name`,
|
||||||
|
`parent_id`,
|
||||||
|
`order_num`,
|
||||||
|
`path`,
|
||||||
|
`component`,
|
||||||
|
`query`,
|
||||||
|
`route_name`,
|
||||||
|
`is_frame`,
|
||||||
|
`is_cache`,
|
||||||
|
`menu_type`,
|
||||||
|
`visible`,
|
||||||
|
`status`,
|
||||||
|
`perms`,
|
||||||
|
`icon`,
|
||||||
|
`create_by`,
|
||||||
|
`create_time`,
|
||||||
|
`remark`
|
||||||
|
) VALUES (
|
||||||
|
'综合评估', -- 菜单名称
|
||||||
|
2009, -- 父菜单ID(心理测评管理)
|
||||||
|
14, -- 显示顺序(在主观题评分order_num=13之后)
|
||||||
|
'report/comprehensive', -- 路由地址
|
||||||
|
'psychology/report/comprehensive', -- 组件路径
|
||||||
|
NULL, -- 路由参数
|
||||||
|
'ComprehensiveAssessment', -- 路由名称
|
||||||
|
1, -- 是否为外链(1否)
|
||||||
|
0, -- 是否缓存(0缓存)
|
||||||
|
'C', -- 菜单类型(C菜单)
|
||||||
|
'0', -- 菜单状态(0显示)
|
||||||
|
'0', -- 菜单状态(0正常)
|
||||||
|
'psychology:report:comprehensive', -- 权限标识
|
||||||
|
'chart', -- 菜单图标
|
||||||
|
'admin', -- 创建者
|
||||||
|
NOW(), -- 创建时间
|
||||||
|
'综合评估菜单' -- 备注
|
||||||
|
);
|
||||||
|
|
||||||
|
-- 添加综合评估的按钮权限
|
||||||
|
-- 综合评估查询
|
||||||
|
INSERT INTO `sys_menu` (
|
||||||
|
`menu_name`,
|
||||||
|
`parent_id`,
|
||||||
|
`order_num`,
|
||||||
|
`path`,
|
||||||
|
`component`,
|
||||||
|
`query`,
|
||||||
|
`route_name`,
|
||||||
|
`is_frame`,
|
||||||
|
`is_cache`,
|
||||||
|
`menu_type`,
|
||||||
|
`visible`,
|
||||||
|
`status`,
|
||||||
|
`perms`,
|
||||||
|
`icon`,
|
||||||
|
`create_by`,
|
||||||
|
`create_time`,
|
||||||
|
`remark`
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
'综合评估查询',
|
||||||
|
menu_id,
|
||||||
|
1,
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
NULL,
|
||||||
|
'',
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
'F',
|
||||||
|
'0',
|
||||||
|
'0',
|
||||||
|
'psychology:report:comprehensive:query',
|
||||||
|
'#',
|
||||||
|
'admin',
|
||||||
|
NOW(),
|
||||||
|
''
|
||||||
|
FROM sys_menu
|
||||||
|
WHERE menu_name = '综合评估' AND parent_id = 2009
|
||||||
|
LIMIT 1;
|
||||||
|
|
||||||
|
-- 综合评估生成
|
||||||
|
INSERT INTO `sys_menu` (
|
||||||
|
`menu_name`,
|
||||||
|
`parent_id`,
|
||||||
|
`order_num`,
|
||||||
|
`path`,
|
||||||
|
`component`,
|
||||||
|
`query`,
|
||||||
|
`route_name`,
|
||||||
|
`is_frame`,
|
||||||
|
`is_cache`,
|
||||||
|
`menu_type`,
|
||||||
|
`visible`,
|
||||||
|
`status`,
|
||||||
|
`perms`,
|
||||||
|
`icon`,
|
||||||
|
`create_by`,
|
||||||
|
`create_time`,
|
||||||
|
`remark`
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
'综合评估生成',
|
||||||
|
menu_id,
|
||||||
|
2,
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
NULL,
|
||||||
|
'',
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
'F',
|
||||||
|
'0',
|
||||||
|
'0',
|
||||||
|
'psychology:report:comprehensive:generate',
|
||||||
|
'#',
|
||||||
|
'admin',
|
||||||
|
NOW(),
|
||||||
|
''
|
||||||
|
FROM sys_menu
|
||||||
|
WHERE menu_name = '综合评估' AND parent_id = 2009
|
||||||
|
LIMIT 1;
|
||||||
|
|
||||||
|
-- 综合评估导出
|
||||||
|
INSERT INTO `sys_menu` (
|
||||||
|
`menu_name`,
|
||||||
|
`parent_id`,
|
||||||
|
`order_num`,
|
||||||
|
`path`,
|
||||||
|
`component`,
|
||||||
|
`query`,
|
||||||
|
`route_name`,
|
||||||
|
`is_frame`,
|
||||||
|
`is_cache`,
|
||||||
|
`menu_type`,
|
||||||
|
`visible`,
|
||||||
|
`status`,
|
||||||
|
`perms`,
|
||||||
|
`icon`,
|
||||||
|
`create_by`,
|
||||||
|
`create_time`,
|
||||||
|
`remark`
|
||||||
|
)
|
||||||
|
SELECT
|
||||||
|
'综合评估导出',
|
||||||
|
menu_id,
|
||||||
|
3,
|
||||||
|
'',
|
||||||
|
'',
|
||||||
|
NULL,
|
||||||
|
'',
|
||||||
|
1,
|
||||||
|
0,
|
||||||
|
'F',
|
||||||
|
'0',
|
||||||
|
'0',
|
||||||
|
'psychology:report:comprehensive:export',
|
||||||
|
'#',
|
||||||
|
'admin',
|
||||||
|
NOW(),
|
||||||
|
''
|
||||||
|
FROM sys_menu
|
||||||
|
WHERE menu_name = '综合评估' AND parent_id = 2009
|
||||||
|
LIMIT 1;
|
||||||
|
|
||||||
|
-- 为管理员角色(role_id=1)添加菜单权限
|
||||||
|
INSERT INTO `sys_role_menu` (`role_id`, `menu_id`)
|
||||||
|
SELECT 1, menu_id FROM sys_menu WHERE menu_name = '综合评估' AND parent_id = 2009
|
||||||
|
ON DUPLICATE KEY UPDATE role_id = role_id;
|
||||||
|
|
||||||
|
-- 为管理员角色添加按钮权限
|
||||||
|
INSERT INTO `sys_role_menu` (`role_id`, `menu_id`)
|
||||||
|
SELECT 1, m2.menu_id
|
||||||
|
FROM sys_menu m1
|
||||||
|
INNER JOIN sys_menu m2 ON m2.parent_id = m1.menu_id
|
||||||
|
WHERE m1.menu_name = '综合评估' AND m1.parent_id = 2009
|
||||||
|
ON DUPLICATE KEY UPDATE role_id = role_id;
|
||||||
|
|
||||||
|
|
@ -347,6 +347,17 @@ export const dynamicRoutes = [
|
||||||
roles: ['admin']
|
roles: ['admin']
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
// 综合评估
|
||||||
|
{
|
||||||
|
path: 'report/comprehensive',
|
||||||
|
name: 'ComprehensiveAssessment',
|
||||||
|
component: () => import('@/views/psychology/report/comprehensive'),
|
||||||
|
meta: {
|
||||||
|
title: '综合评估',
|
||||||
|
icon: 'chart',
|
||||||
|
roles: ['admin']
|
||||||
|
}
|
||||||
|
},
|
||||||
// 量表权限管理
|
// 量表权限管理
|
||||||
{
|
{
|
||||||
path: 'permission',
|
path: 'permission',
|
||||||
|
|
|
||||||
|
|
@ -521,7 +521,6 @@ export default {
|
||||||
// 如果获取角色列表失败,尝试使用默认值101
|
// 如果获取角色列表失败,尝试使用默认值101
|
||||||
resolve(101);
|
resolve(101);
|
||||||
});
|
});
|
||||||
>>>>>>> 13bbd8b9e742b8b2a020195d81f66187af7d4066
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
// 取消按钮
|
// 取消按钮
|
||||||
|
|
|
||||||
599
xinli-ui/src/views/psychology/report/comprehensive.vue
Normal file
599
xinli-ui/src/views/psychology/report/comprehensive.vue
Normal file
|
|
@ -0,0 +1,599 @@
|
||||||
|
<template>
|
||||||
|
<div class="comprehensive-assessment">
|
||||||
|
<el-card class="user-selector" shadow="never">
|
||||||
|
<el-form :inline="true" size="small">
|
||||||
|
<el-form-item label="选择用户">
|
||||||
|
<el-select
|
||||||
|
v-model="selectedUserId"
|
||||||
|
style="width: 280px"
|
||||||
|
clearable
|
||||||
|
filterable
|
||||||
|
remote
|
||||||
|
reserve-keyword
|
||||||
|
:remote-method="searchUsers"
|
||||||
|
:loading="userOptionsLoading"
|
||||||
|
placeholder="输入姓名或账号搜索"
|
||||||
|
@change="handleUserChange"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in userOptions"
|
||||||
|
:key="item.userId"
|
||||||
|
:label="buildUserLabel(item)"
|
||||||
|
:value="item.userId"
|
||||||
|
>
|
||||||
|
<div class="user-option">
|
||||||
|
<span class="name">{{ item.nickName || item.userName }}</span>
|
||||||
|
<span class="dept">{{ item.deptName || '未分配单位' }}</span>
|
||||||
|
</div>
|
||||||
|
</el-option>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" icon="el-icon-search" :disabled="!selectedUserId" @click="loadUserData">
|
||||||
|
载入
|
||||||
|
</el-button>
|
||||||
|
<el-button icon="el-icon-refresh" @click="resetSelection">重置</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<el-card v-if="userProfile" class="user-info" shadow="never">
|
||||||
|
<el-descriptions :column="3" border size="small">
|
||||||
|
<el-descriptions-item label="姓名">{{ userProfile.userName || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="年龄">{{ calculateAge(userProfile.birthday) || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="信息编号">{{ userProfile.infoNumber || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="罪名">{{ userProfile.crimeName || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="刑期">{{ userProfile.sentenceTerm || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="刑期起日">{{ formatDate(userProfile.sentenceStartDate) || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="刑期止日">{{ formatDate(userProfile.sentenceEndDate) || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="文化程度">{{ userProfile.educationLevel || '-' }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="民族">{{ userProfile.nation || '-' }}</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<el-card class="scale-list" shadow="never" v-loading="loading">
|
||||||
|
<div slot="header">
|
||||||
|
<span>量表列表(请勾选需要分析的量表)</span>
|
||||||
|
<div class="header-actions">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
plain
|
||||||
|
icon="el-icon-magic-stick"
|
||||||
|
size="mini"
|
||||||
|
:disabled="selectedReports.length === 0 || generating"
|
||||||
|
:loading="generating"
|
||||||
|
@click="generateComprehensiveReport"
|
||||||
|
>
|
||||||
|
AI生成综合报告
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<el-table
|
||||||
|
ref="reportTable"
|
||||||
|
:data="reportOptions"
|
||||||
|
@selection-change="handleReportSelection"
|
||||||
|
border
|
||||||
|
empty-text="暂无可用的测评报告"
|
||||||
|
>
|
||||||
|
<el-table-column type="selection" width="55" />
|
||||||
|
<el-table-column prop="scaleName" label="量表名称" min-width="200" />
|
||||||
|
<el-table-column prop="reportTitle" label="报告标题" min-width="240" :show-overflow-tooltip="true" />
|
||||||
|
<el-table-column label="测评时间" width="200" align="center">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
{{ formatDateTime(scope.row.submitTime) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column label="得分" width="100" align="center">
|
||||||
|
<template slot-scope="scope">
|
||||||
|
{{ scope.row.totalScore != null ? scope.row.totalScore : '-' }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<el-empty
|
||||||
|
v-if="!loading && reportOptions.length === 0"
|
||||||
|
description="该用户暂无可用的量表报告"
|
||||||
|
></el-empty>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<el-dialog
|
||||||
|
title="综合评估报告"
|
||||||
|
:visible.sync="reportDialogVisible"
|
||||||
|
width="80%"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
append-to-body
|
||||||
|
>
|
||||||
|
<div v-loading="generating" class="report-preview">
|
||||||
|
<div v-if="comprehensiveReport" v-html="comprehensiveReport" class="report-content"></div>
|
||||||
|
<div v-else class="empty-report">报告生成中,请稍候...</div>
|
||||||
|
</div>
|
||||||
|
<div slot="footer">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
icon="el-icon-download"
|
||||||
|
:disabled="!comprehensiveReport"
|
||||||
|
@click="exportReport('word')"
|
||||||
|
>导出报告</el-button>
|
||||||
|
</div>
|
||||||
|
</el-dialog>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
import { getUserAssessmentSummary, getStudentOptions } from '@/api/psychology/assessment'
|
||||||
|
import { getProfileByUserId } from '@/api/psychology/profile'
|
||||||
|
import { getReport } from '@/api/psychology/report'
|
||||||
|
import { parseTime } from '@/utils/ruoyi'
|
||||||
|
import axios from 'axios'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'ComprehensiveAssessment',
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
selectedUserId: undefined,
|
||||||
|
userOptions: [],
|
||||||
|
userOptionsLoading: false,
|
||||||
|
userProfile: null,
|
||||||
|
userSummary: null,
|
||||||
|
reportOptions: [],
|
||||||
|
selectedReports: [],
|
||||||
|
loading: false,
|
||||||
|
generating: false,
|
||||||
|
reportDialogVisible: false,
|
||||||
|
comprehensiveReport: '',
|
||||||
|
OLLAMA_URL: 'http://192.168.0.106:11434/api/generate',
|
||||||
|
MODEL: 'deepseek-r1:32b'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
this.searchUsers('')
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
searchUsers(query) {
|
||||||
|
this.userOptionsLoading = true
|
||||||
|
getStudentOptions({ keyword: query, limit: 20 })
|
||||||
|
.then((res) => {
|
||||||
|
this.userOptions = res.data || []
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
this.userOptionsLoading = false
|
||||||
|
})
|
||||||
|
},
|
||||||
|
buildUserLabel(option) {
|
||||||
|
if (!option) {
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
const name = option.nickName || option.userName || ''
|
||||||
|
const dept = option.deptName ? ` - ${option.deptName}` : ''
|
||||||
|
return name + dept
|
||||||
|
},
|
||||||
|
handleUserChange() {
|
||||||
|
if (!this.selectedUserId) {
|
||||||
|
this.resetSelection()
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async loadUserData() {
|
||||||
|
if (!this.selectedUserId) {
|
||||||
|
this.$message.warning('请先选择用户')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
this.loading = true
|
||||||
|
try {
|
||||||
|
// 加载用户测评汇总
|
||||||
|
const summaryResponse = await getUserAssessmentSummary(this.selectedUserId)
|
||||||
|
this.userSummary = summaryResponse.data || null
|
||||||
|
|
||||||
|
// 转换量表数据
|
||||||
|
const rawScales = (this.userSummary && Array.isArray(this.userSummary.scales)) ? this.userSummary.scales : []
|
||||||
|
this.reportOptions = rawScales.reduce((result, scale) => {
|
||||||
|
const attempts = Array.isArray(scale.attempts) ? scale.attempts : []
|
||||||
|
const reportRows = attempts
|
||||||
|
.filter((attempt) => attempt && attempt.reportId)
|
||||||
|
.map((attempt) => ({
|
||||||
|
key: `${scale.scaleId}-${attempt.assessmentId}`,
|
||||||
|
scaleId: scale.scaleId,
|
||||||
|
scaleName: scale.scaleName,
|
||||||
|
assessmentId: attempt.assessmentId,
|
||||||
|
reportId: attempt.reportId,
|
||||||
|
reportTitle: attempt.reportTitle || `${scale.scaleName}测评报告`,
|
||||||
|
submitTime: attempt.submitTime || attempt.startTime,
|
||||||
|
totalScore: attempt.totalScore,
|
||||||
|
summary: attempt.reportSummary || '',
|
||||||
|
status: attempt.status
|
||||||
|
}))
|
||||||
|
return result.concat(reportRows)
|
||||||
|
}, [])
|
||||||
|
this.selectedReports = []
|
||||||
|
this.$nextTick(() => {
|
||||||
|
if (this.$refs.reportTable && this.$refs.reportTable.doLayout) {
|
||||||
|
this.$refs.reportTable.doLayout()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// 加载用户档案信息
|
||||||
|
try {
|
||||||
|
const profileResponse = await getProfileByUserId(this.selectedUserId)
|
||||||
|
this.userProfile = profileResponse.data || null
|
||||||
|
} catch (error) {
|
||||||
|
console.warn('获取用户档案失败:', error)
|
||||||
|
this.userProfile = null
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载用户数据失败:', error)
|
||||||
|
this.$message.error('加载用户数据失败')
|
||||||
|
} finally {
|
||||||
|
this.loading = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
resetSelection() {
|
||||||
|
this.selectedUserId = undefined
|
||||||
|
this.userProfile = null
|
||||||
|
this.userSummary = null
|
||||||
|
this.reportOptions = []
|
||||||
|
this.selectedReports = []
|
||||||
|
this.comprehensiveReport = ''
|
||||||
|
},
|
||||||
|
formatDateTime(value) {
|
||||||
|
if (!value) return '-'
|
||||||
|
return parseTime(value)
|
||||||
|
},
|
||||||
|
formatDate(value) {
|
||||||
|
if (!value) return '-'
|
||||||
|
if (typeof value === 'string') return value
|
||||||
|
return parseTime(value, '{y}-{m}-{d}')
|
||||||
|
},
|
||||||
|
calculateAge(birthday) {
|
||||||
|
if (!birthday) return '-'
|
||||||
|
const birth = new Date(birthday)
|
||||||
|
const today = new Date()
|
||||||
|
let age = today.getFullYear() - birth.getFullYear()
|
||||||
|
const monthDiff = today.getMonth() - birth.getMonth()
|
||||||
|
if (monthDiff < 0 || (monthDiff === 0 && today.getDate() < birth.getDate())) {
|
||||||
|
age--
|
||||||
|
}
|
||||||
|
return age + '岁'
|
||||||
|
},
|
||||||
|
handleReportSelection(selection) {
|
||||||
|
this.selectedReports = selection
|
||||||
|
},
|
||||||
|
async generateComprehensiveReport() {
|
||||||
|
if (this.selectedReports.length === 0) {
|
||||||
|
this.$message.warning('请至少选择一个报告')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
this.generating = true
|
||||||
|
this.reportDialogVisible = true
|
||||||
|
this.comprehensiveReport = ''
|
||||||
|
|
||||||
|
try {
|
||||||
|
// 1. 获取选中量表的报告内容
|
||||||
|
const scaleReports = await this.fetchSelectedReports()
|
||||||
|
|
||||||
|
// 2. 构建用户信息摘要
|
||||||
|
const userInfoSummary = this.buildUserInfoSummary()
|
||||||
|
|
||||||
|
// 3. 构建AI提示词
|
||||||
|
const prompt = this.buildAIPrompt(userInfoSummary, scaleReports)
|
||||||
|
|
||||||
|
// 4. 调用AI生成报告
|
||||||
|
const aiReport = await this.callOLLAMA(prompt)
|
||||||
|
|
||||||
|
// 5. 格式化并展示报告
|
||||||
|
this.comprehensiveReport = this.formatReport(aiReport, userInfoSummary, scaleReports)
|
||||||
|
} catch (error) {
|
||||||
|
console.error('生成报告失败:', error)
|
||||||
|
this.$message.error('生成报告失败:' + (error.message || '未知错误'))
|
||||||
|
} finally {
|
||||||
|
this.generating = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
async fetchSelectedReports() {
|
||||||
|
const reports = []
|
||||||
|
for (const row of this.selectedReports) {
|
||||||
|
if (!row.reportId) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const response = await getReport(row.reportId, 'assessment')
|
||||||
|
if (response && response.data) {
|
||||||
|
reports.push({
|
||||||
|
scaleName: row.scaleName,
|
||||||
|
submitTime: row.submitTime,
|
||||||
|
totalScore: row.totalScore,
|
||||||
|
summary: response.data.summary || row.summary || '',
|
||||||
|
content: response.data.reportContent || ''
|
||||||
|
})
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.warn(`获取量表 ${row.scaleName} 的报告失败:`, error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return reports
|
||||||
|
},
|
||||||
|
buildUserInfoSummary() {
|
||||||
|
if (!this.userProfile) {
|
||||||
|
return {
|
||||||
|
userName: this.userSummary?.userName || this.userSummary?.nickName || '未知',
|
||||||
|
age: '-',
|
||||||
|
crimeName: '-',
|
||||||
|
sentenceTerm: '-',
|
||||||
|
sentenceEndDate: '-',
|
||||||
|
educationLevel: '-',
|
||||||
|
nation: '-'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {
|
||||||
|
userName: this.userProfile.userName || this.userSummary?.userName || '未知',
|
||||||
|
age: this.calculateAge(this.userProfile.birthday),
|
||||||
|
crimeName: this.userProfile.crimeName || '-',
|
||||||
|
sentenceTerm: this.userProfile.sentenceTerm || '-',
|
||||||
|
sentenceEndDate: this.formatDate(this.userProfile.sentenceEndDate),
|
||||||
|
educationLevel: this.userProfile.educationLevel || '-',
|
||||||
|
nation: this.userProfile.nation || '-'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
buildAIPrompt(userInfo, scaleReports) {
|
||||||
|
const SYSTEM_PROMPT = [
|
||||||
|
'你是专业心理测评综合评估分析师。请根据提供的用户信息和多个量表测评结果,生成一份综合评估报告。',
|
||||||
|
'要求:',
|
||||||
|
'1. 综合分析用户的心理状态,结合年龄、罪名、刑期等背景信息;',
|
||||||
|
'2. 对比分析所选量表的测评结果,找出共同点和差异点;',
|
||||||
|
'3. 提供专业的综合评估结论(800-1200字);',
|
||||||
|
'4. 使用HTML格式输出,包含:综合概述、量表结果分析、综合评估、建议措施等部分;',
|
||||||
|
'5. 使用<h3>标签作为小标题,<p>标签作为段落;',
|
||||||
|
'6. 仅输出分析结果,不要包含思考过程、<think>标签或</think>标签。'
|
||||||
|
].join('\n')
|
||||||
|
|
||||||
|
const userInfoText = `
|
||||||
|
用户基本信息:
|
||||||
|
- 姓名:${userInfo.userName}
|
||||||
|
- 年龄:${userInfo.age}
|
||||||
|
- 罪名:${userInfo.crimeName}
|
||||||
|
- 刑期:${userInfo.sentenceTerm}
|
||||||
|
- 刑期止日:${userInfo.sentenceEndDate}
|
||||||
|
- 文化程度:${userInfo.educationLevel}
|
||||||
|
- 民族:${userInfo.nation}
|
||||||
|
`.trim()
|
||||||
|
|
||||||
|
const scaleReportsText = scaleReports
|
||||||
|
.map((report, index) => {
|
||||||
|
const contentText = report.content
|
||||||
|
.replace(/<[^>]*>/g, '')
|
||||||
|
.replace(/ /g, ' ')
|
||||||
|
.substring(0, 500)
|
||||||
|
return `
|
||||||
|
量表${index + 1}:${report.scaleName}
|
||||||
|
测评时间:${this.formatDateTime(report.submitTime)}
|
||||||
|
总分:${report.totalScore}
|
||||||
|
报告摘要:${report.summary || '无'}
|
||||||
|
报告内容:${contentText}...
|
||||||
|
`.trim()
|
||||||
|
})
|
||||||
|
.join('\n\n')
|
||||||
|
|
||||||
|
return `${SYSTEM_PROMPT}\n\n${userInfoText}\n\n${scaleReportsText}`
|
||||||
|
},
|
||||||
|
async callOLLAMA(prompt) {
|
||||||
|
try {
|
||||||
|
const { data } = await axios.post(this.OLLAMA_URL, {
|
||||||
|
model: this.MODEL,
|
||||||
|
prompt: prompt,
|
||||||
|
temperature: 0.2,
|
||||||
|
num_predict: 2000,
|
||||||
|
stream: false
|
||||||
|
}, {
|
||||||
|
timeout: 120000
|
||||||
|
})
|
||||||
|
|
||||||
|
let response = data?.response ?? ''
|
||||||
|
|
||||||
|
// 清理响应内容
|
||||||
|
response = response
|
||||||
|
.replace(/<think>[\s\S]*?<\/think>/gi, '')
|
||||||
|
.replace(/<think>[\s\S]*?<\/redacted_reasoning>/gi, '')
|
||||||
|
.replace(/```html\s*/gi, '')
|
||||||
|
.replace(/```\s*/g, '')
|
||||||
|
.replace(/```[a-z]*\s*/gi, '')
|
||||||
|
.trim()
|
||||||
|
|
||||||
|
if (!response) {
|
||||||
|
throw new Error('AI分析返回结果为空')
|
||||||
|
}
|
||||||
|
|
||||||
|
return response
|
||||||
|
} catch (error) {
|
||||||
|
console.error('AI分析失败:', error)
|
||||||
|
throw new Error('AI分析失败:' + (error.message || '未知错误'))
|
||||||
|
}
|
||||||
|
},
|
||||||
|
formatReport(aiReport, userInfo, scaleReports) {
|
||||||
|
// 构建完整的报告HTML
|
||||||
|
const reportHtml = `
|
||||||
|
<div class="comprehensive-report">
|
||||||
|
<h1 style="text-align: center; margin-bottom: 30px;">综合评估报告</h1>
|
||||||
|
|
||||||
|
<div class="user-info-section" style="margin-bottom: 30px; padding: 15px; background-color: #f5f7fa; border-radius: 4px;">
|
||||||
|
<h2 style="margin-top: 0;">用户基本信息</h2>
|
||||||
|
<table style="width: 100%; border-collapse: collapse;">
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 8px; border: 1px solid #ddd; width: 150px;"><strong>姓名</strong></td>
|
||||||
|
<td style="padding: 8px; border: 1px solid #ddd;">${userInfo.userName}</td>
|
||||||
|
<td style="padding: 8px; border: 1px solid #ddd; width: 150px;"><strong>年龄</strong></td>
|
||||||
|
<td style="padding: 8px; border: 1px solid #ddd;">${userInfo.age}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 8px; border: 1px solid #ddd;"><strong>罪名</strong></td>
|
||||||
|
<td style="padding: 8px; border: 1px solid #ddd;">${userInfo.crimeName}</td>
|
||||||
|
<td style="padding: 8px; border: 1px solid #ddd;"><strong>刑期</strong></td>
|
||||||
|
<td style="padding: 8px; border: 1px solid #ddd;">${userInfo.sentenceTerm}</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<td style="padding: 8px; border: 1px solid #ddd;"><strong>刑期止日</strong></td>
|
||||||
|
<td style="padding: 8px; border: 1px solid #ddd;">${userInfo.sentenceEndDate}</td>
|
||||||
|
<td style="padding: 8px; border: 1px solid #ddd;"><strong>文化程度</strong></td>
|
||||||
|
<td style="padding: 8px; border: 1px solid #ddd;">${userInfo.educationLevel}</td>
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="scale-summary" style="margin-bottom: 30px;">
|
||||||
|
<h2>参与测评的量表</h2>
|
||||||
|
<ul>
|
||||||
|
${scaleReports.map((r, i) => `<li>${i + 1}. ${r.scaleName}(测评时间:${this.formatDateTime(r.submitTime)},总分:${r.totalScore})</li>`).join('')}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="ai-analysis">
|
||||||
|
<h2 style="color: #67C23A; border-bottom: 2px solid #67C23A; padding-bottom: 10px;">AI综合评估分析</h2>
|
||||||
|
${this.formatAIResult(aiReport)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div style="margin-top: 30px; padding-top: 20px; border-top: 1px solid #ddd; text-align: right; color: #909399; font-size: 12px;">
|
||||||
|
报告生成时间:${parseTime(new Date(), '{y}-{m}-{d} {h}:{i}:{s}')}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`
|
||||||
|
return reportHtml
|
||||||
|
},
|
||||||
|
formatAIResult(text) {
|
||||||
|
let html = text.trim()
|
||||||
|
|
||||||
|
// 如果已经是HTML格式,清理后返回
|
||||||
|
if (html.includes('<h3>') || html.includes('<p>') || html.includes('<div>')) {
|
||||||
|
return html
|
||||||
|
}
|
||||||
|
|
||||||
|
// 处理标题
|
||||||
|
html = html.replace(/^(\d+[\.、]?\s*[^\n]+)$/gm, '<h3>$1</h3>')
|
||||||
|
html = html.replace(/^([^\n]*(?:结论|分析|建议|总结|概述)[^\n]*)$/gm, '<h3>$1</h3>')
|
||||||
|
|
||||||
|
// 将段落分隔符转换为<p>标签
|
||||||
|
html = html.split('\n\n').map((para) => {
|
||||||
|
para = para.trim()
|
||||||
|
if (!para) return ''
|
||||||
|
if (para.startsWith('<h3>')) return para
|
||||||
|
return '<p>' + para.replace(/\n/g, '<br>') + '</p>'
|
||||||
|
}).join('')
|
||||||
|
|
||||||
|
return html
|
||||||
|
},
|
||||||
|
exportReport(format) {
|
||||||
|
if (!this.comprehensiveReport) {
|
||||||
|
this.$message.warning('报告内容为空')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const reportHtml = `
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8" />
|
||||||
|
<title>综合评估报告</title>
|
||||||
|
<style>
|
||||||
|
body { font-family: 'Microsoft Yahei', sans-serif; padding: 32px; color: #303133; }
|
||||||
|
h1, h2, h3 { margin: 16px 0; }
|
||||||
|
h1 { text-align: center; font-size: 24px; }
|
||||||
|
h2 { font-size: 20px; color: #1f2d3d; }
|
||||||
|
h3 { font-size: 18px; color: #606266; }
|
||||||
|
table { width: 100%; border-collapse: collapse; margin: 20px 0; }
|
||||||
|
table td, table th { border: 1px solid #ddd; padding: 8px; }
|
||||||
|
ul { line-height: 1.8; }
|
||||||
|
p { line-height: 1.8; margin: 10px 0; }
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
${this.comprehensiveReport}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
`
|
||||||
|
|
||||||
|
if (format === 'word') {
|
||||||
|
const blob = new Blob(['\ufeff', reportHtml], { type: 'application/msword' })
|
||||||
|
const filename = `综合评估报告_${this.userProfile?.userName || '用户'}_${Date.now()}.doc`
|
||||||
|
const link = document.createElement('a')
|
||||||
|
link.href = window.URL.createObjectURL(blob)
|
||||||
|
link.download = filename
|
||||||
|
document.body.appendChild(link)
|
||||||
|
link.click()
|
||||||
|
document.body.removeChild(link)
|
||||||
|
window.URL.revokeObjectURL(link.href)
|
||||||
|
this.$message.success('导出成功')
|
||||||
|
} else {
|
||||||
|
const printWindow = window.open('', '_blank')
|
||||||
|
if (!printWindow) {
|
||||||
|
this.$message.error('无法打开打印窗口,请检查浏览器是否阻止了弹窗')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
printWindow.document.write(reportHtml)
|
||||||
|
printWindow.document.close()
|
||||||
|
printWindow.focus()
|
||||||
|
printWindow.print()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.comprehensive-assessment {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-selector {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-info {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.scale-list {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-actions {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-option {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-option .name {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-option .dept {
|
||||||
|
color: #909399;
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.report-preview {
|
||||||
|
min-height: 400px;
|
||||||
|
max-height: 600px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.report-content {
|
||||||
|
line-height: 1.8;
|
||||||
|
}
|
||||||
|
|
||||||
|
.report-content h3 {
|
||||||
|
color: #409EFF;
|
||||||
|
margin-top: 20px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.report-content p {
|
||||||
|
margin: 10px 0;
|
||||||
|
text-indent: 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.empty-report {
|
||||||
|
text-align: center;
|
||||||
|
padding: 100px 0;
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
Loading…
Reference in New Issue
Block a user