xlcp-frontend/src/views/prison/report/components/VersionHistoryDialog.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

285 lines
7.9 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="历史版本" v-model="dialogVisible" width="900px">
<div class="version-history-dialog">
<!-- 版本列表 -->
<div class="version-list" v-loading="loading">
<el-table
:data="versionList"
stripe
@row-click="handleSelectVersion"
:row-class-name="getRowClassName"
>
<el-table-column label="版本号" width="100" align="center">
<template #default="{ row }">
<el-tag type="info" size="small">v{{ row.version }}</el-tag>
</template>
</el-table-column>
<el-table-column label="修改人" prop="modifierName" width="120" align="center" />
<el-table-column label="修改时间" prop="modifyTime" width="180" align="center" />
<el-table-column label="版本备注" prop="comment" min-width="150" show-overflow-tooltip />
<el-table-column label="操作" width="150" align="center">
<template #default="{ row }">
<el-button
type="primary"
link
size="small"
@click.stop="handleCompare(row)"
:disabled="!selectedVersion"
>
对比
</el-button>
<el-button
type="success"
link
size="small"
@click.stop="handleRestore(row)"
:disabled="row.version === currentVersion"
>
恢复
</el-button>
</template>
</el-table-column>
</el-table>
<el-empty v-if="!loading && versionList.length === 0" description="暂无历史版本" />
</div>
<!-- 版本对比区 -->
<div v-if="compareMode" class="version-compare">
<div class="compare-header">
<h4>版本对比</h4>
<el-button type="primary" link @click="compareMode = false">关闭对比</el-button>
</div>
<div class="compare-content">
<div class="compare-panel left-panel">
<div class="panel-header">
<span>v{{ selectedVersion?.version }}</span>
<span class="modifier">{{ selectedVersion?.modifierName }}</span>
<span class="time">{{ selectedVersion?.modifyTime }}</span>
</div>
<div class="panel-content" v-html="diffHtml.oldContent"></div>
</div>
<div class="compare-divider">
<Icon icon="ep:arrow-right" />
</div>
<div class="compare-panel right-panel">
<div class="panel-header">
<span>v{{ currentVersion }}</span>
<span class="modifier">当前版本</span>
</div>
<div class="panel-content" v-html="diffHtml.newContent"></div>
</div>
</div>
<div class="compare-actions">
<el-button @click="handleRestore(selectedVersion!)">
<Icon icon="ep:refresh-left" class="mr-5px" /> 恢复到 v{{ selectedVersion?.version }}
</el-button>
</div>
</div>
</div>
<template #footer>
<el-button @click="dialogVisible = false"> </el-button>
</template>
</Dialog>
</template>
<script setup lang="ts">
import { ReportVersionApi, ReportVersion } from '@/api/prison/report'
/** 历史版本弹窗 */
defineOptions({ name: 'PrisonVersionHistoryDialog' })
const props = defineProps<{
reportId?: number
}>()
const message = useMessage()
const dialogVisible = ref(false)
const loading = ref(false)
const versionList = ref<ReportVersion[]>([])
const currentVersion = ref(0)
const selectedVersion = ref<ReportVersion | null>(null)
const compareMode = ref(false)
const diffHtml = ref({ oldContent: '', newContent: '' })
/** 打开弹窗 */
const open = async () => {
if (!props.reportId) {
message.warning('请先保存报告')
return
}
dialogVisible.value = true
compareMode.value = false
selectedVersion.value = null
loadVersions()
}
defineExpose({ open })
/** 加载版本历史 */
const loadVersions = async () => {
if (!props.reportId) return
loading.value = true
try {
const data = await ReportVersionApi.getVersionList(props.reportId)
versionList.value = data
if (data.length > 0) {
currentVersion.value = data[0].version
}
} finally {
loading.value = false
}
}
/** 选择版本查看 */
const handleSelectVersion = async (row: ReportVersion) => {
selectedVersion.value = row
}
/** 获取行样式 */
const getRowClassName = ({ row }: { row: ReportVersion }) => {
return selectedVersion.value?.id === row.id ? 'selected-row' : ''
}
/** 对比版本 */
const handleCompare = async (version: ReportVersion) => {
selectedVersion.value = version
compareMode.value = true
// 获取版本对比数据
try {
// const data = await ReportVersionApi.compareVersions(version.id, currentVersionId)
// diffHtml.value = data
// 模拟对比数据
diffHtml.value = {
oldContent: `<div style="padding: 10px; line-height: 1.6;">
<p><span style="background: #ffcccc;">该犯在服刑期间表现良好,</span>遵守监规纪律,</p>
<p><span style="background: #ffcccc;">积极参加劳动,</span>完成劳动任务。</p>
<p><span style="background: #ffcccc;">月度考核成绩均在85分以上</span>无违规记录。</p>
</div>`,
newContent: `<div style="padding: 10px; line-height: 1.6;">
<p><span style="background: #ccffcc;">该犯在服刑期间表现优秀,严格遵守监规纪律,</span>遵守监规纪律,</p>
<p><span style="background: #ccffcc;">积极参加劳动生产,</span>完成劳动任务。</p>
<p><span style="background: #ccffcc;">月度考核成绩均在90分以上</span>无违规记录。</p>
</div>`
}
} catch {}
}
/** 恢复版本 */
const handleRestore = async (version: ReportVersion) => {
try {
await message.confirm(`确认要恢复到 v${version.version} 版本吗?当前版本内容将被覆盖。`)
await ReportVersionApi.restoreVersion(version.id)
message.success('版本已恢复')
emit('restore', version.id)
loadVersions()
} catch {}
}
const emit = defineEmits(['restore'])
</script>
<style lang="scss" scoped>
.version-history-dialog {
.version-list {
margin-bottom: 20px;
:deep(.selected-row) {
background: #ecf5ff !important;
}
:deep(.el-table__row) {
cursor: pointer;
}
}
.version-compare {
border: 1px solid #e4e7ed;
border-radius: 4px;
overflow: hidden;
.compare-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 15px;
background: #f5f7fa;
border-bottom: 1px solid #e4e7ed;
h4 {
margin: 0;
font-size: 14px;
color: #303133;
}
}
.compare-content {
display: flex;
min-height: 300px;
.compare-panel {
flex: 1;
display: flex;
flex-direction: column;
.panel-header {
padding: 10px 15px;
background: #fafafa;
border-bottom: 1px solid #e4e7ed;
font-size: 13px;
color: #606266;
.modifier {
margin-left: 10px;
color: #909399;
}
.time {
margin-left: 10px;
color: #909399;
}
}
.panel-content {
flex: 1;
padding: 15px;
overflow-y: auto;
font-size: 14px;
line-height: 1.8;
}
&.left-panel {
border-right: 1px solid #e4e7ed;
}
}
.compare-divider {
display: flex;
align-items: center;
justify-content: center;
width: 50px;
background: #f5f7fa;
color: #909399;
}
}
.compare-actions {
padding: 15px;
background: #f5f7fa;
border-top: 1px solid #e4e7ed;
text-align: center;
}
}
}
</style>