综合报告
This commit is contained in:
parent
343cb8c76d
commit
180ccde18b
|
|
@ -6,7 +6,7 @@ spring:
|
|||
druid:
|
||||
# 主库数据源
|
||||
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
|
||||
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']
|
||||
}
|
||||
},
|
||||
// 综合评估
|
||||
{
|
||||
path: 'report/comprehensive',
|
||||
name: 'ComprehensiveAssessment',
|
||||
component: () => import('@/views/psychology/report/comprehensive'),
|
||||
meta: {
|
||||
title: '综合评估',
|
||||
icon: 'chart',
|
||||
roles: ['admin']
|
||||
}
|
||||
},
|
||||
// 量表权限管理
|
||||
{
|
||||
path: 'permission',
|
||||
|
|
|
|||
|
|
@ -521,7 +521,6 @@ export default {
|
|||
// 如果获取角色列表失败,尝试使用默认值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