tangweijie 79cfcf9c6d feat(report): 更新评估报告前端组件和 API
- 优化 DimensionAnalysisPanel 维度分析面板
- 更新 LlmResultPanel LLM 结果展示组件
- 完善 PromptEditor 提示词编辑器功能
- 改进 ReportForm 报告表单交互
- 优化 ReportEditDrawer 报告编辑抽屉
- 调整 prisoner 页面显示
- 更新 evaluation-report 和 report API

Co-Authored-By: Claude <noreply@anthropic.com>
2026-01-20 12:13:13 +08:00

548 lines
16 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-form
class="-mb-15px"
:model="queryParams"
ref="queryFormRef"
:inline="true"
label-width="100px"
>
<el-form-item label="罪犯编号" prop="prisonerNo">
<el-input
v-model="queryParams.prisonerNo"
placeholder="请输入罪犯编号"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="姓名" prop="name">
<el-input
v-model="queryParams.name"
placeholder="请输入姓名"
clearable
@keyup.enter="handleQuery"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="性别" prop="gender">
<el-select
v-model="queryParams.gender"
placeholder="请选择性别"
clearable
class="!w-240px"
>
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="监管等级" prop="supervisionLevel">
<el-select
v-model="queryParams.supervisionLevel"
placeholder="请选择监管等级"
clearable
class="!w-240px"
>
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.PRISON_SUPERVISION_LEVEL)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="风险等级" prop="riskLevel">
<el-select
v-model="queryParams.riskLevel"
placeholder="请选择风险等级"
clearable
class="!w-240px"
>
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.PRISON_RISK_LEVEL)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item label="监区" prop="prisonAreaId">
<el-tree-select
v-model="queryParams.prisonAreaId"
:data="areaTreeList"
:props="{ label: 'name', value: 'id', children: 'children' }"
placeholder="请选择监区"
clearable
filterable
class="!w-240px"
@change="handleAreaChange"
:render-after-expand="false"
/>
</el-form-item>
<el-form-item label="入狱日期" prop="imprisonmentDate">
<el-date-picker
v-model="queryParams.imprisonmentDate"
type="daterange"
start-placeholder="开始日期"
end-placeholder="结束日期"
value-format="YYYY-MM-DD"
class="!w-240px"
/>
</el-form-item>
<el-form-item label="监室" prop="prisonCellId">
<el-select
v-model="queryParams.prisonCellId"
placeholder="请选择监室"
clearable
class="!w-240px"
>
<el-option
v-for="cell in cellList"
:key="cell.id"
:label="cell.name"
:value="cell.id"
/>
</el-select>
</el-form-item>
<el-form-item label="状态" prop="status">
<el-select
v-model="queryParams.status"
placeholder="请选择状态"
clearable
class="!w-240px"
>
<el-option
v-for="dict in getIntDictOptions(DICT_TYPE.PRISONER_STATUS)"
:key="dict.value"
:label="dict.label"
:value="dict.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<el-button @click="handleQuery"><Icon icon="ep:search" class="mr-5px" /> 搜索</el-button>
<el-button @click="resetQuery"><Icon icon="ep:refresh" class="mr-5px" /> 重置</el-button>
<el-button
type="primary"
plain
@click="openForm('create')"
v-hasPermi="['prison:prisoner:create']"
>
<Icon icon="ep:plus" class="mr-5px" /> 入监登记
</el-button>
<el-button
type="warning"
plain
@click="openTransferForm"
v-hasPermi="['prison:prisoner:transfer']"
>
<Icon icon="ep:rank" class="mr-5px" /> 调监
</el-button>
<el-button
type="danger"
plain
:disabled="checkedIds.length === 0"
@click="handleDeleteBatch"
v-hasPermi="['prison:prisoner:delete']"
>
<Icon icon="ep:delete" class="mr-5px" /> 批量删除
</el-button>
<el-button
type="success"
plain
@click="handleExport"
:loading="exportLoading"
v-hasPermi="['prison:prisoner:export']"
>
<Icon icon="ep:download" class="mr-5px" /> 导出
</el-button>
</el-form-item>
</el-form>
</ContentWrap>
<!-- 列表 -->
<ContentWrap>
<el-table v-loading="loading" :data="list" @selection-change="handleRowCheckboxChange">
<el-table-column type="selection" width="55" />
<el-table-column label="罪犯编号" align="center" prop="prisonerNo" width="120" />
<el-table-column label="姓名" align="center" prop="name" width="100" />
<el-table-column label="性别" align="center" prop="gender" width="80">
<template #default="scope">
<dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="scope.row.gender" />
</template>
</el-table-column>
<el-table-column label="身份证号" align="center" prop="idCard" width="180">
<template #default="scope">
{{ formatIdCard(scope.row.idCard) }}
</template>
</el-table-column>
<el-table-column label="罪名" align="center" prop="crime" width="150" />
<el-table-column label="刑期" align="center" width="120">
<template #default="scope">
{{ formatSentence(scope.row) }}
</template>
</el-table-column>
<el-table-column label="入狱日期" align="center" prop="imprisonmentDate" width="120" />
<el-table-column label="监管等级" align="center" prop="supervisionLevel" width="100">
<template #default="scope">
<dict-tag :type="DICT_TYPE.PRISON_SUPERVISION_LEVEL" :value="scope.row.supervisionLevel" />
</template>
</el-table-column>
<el-table-column label="风险等级" align="center" prop="riskLevel" width="100">
<template #default="scope">
<dict-tag :type="DICT_TYPE.PRISON_RISK_LEVEL" :value="scope.row.riskLevel" />
</template>
</el-table-column>
<el-table-column label="当前监区" align="center" prop="prisonAreaName" width="120" />
<el-table-column label="当前监室" align="center" prop="prisonCellName" width="100" />
<el-table-column label="状态" align="center" prop="status" width="100">
<template #default="scope">
<dict-tag :type="DICT_TYPE.PRISONER_STATUS" :value="scope.row.status" />
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="360" fixed="right">
<template #default="scope">
<el-button
type="success"
link
@click="handleDashboard(scope.row)"
v-hasPermi="['prison:prisoner:dashboard']"
>
看板
</el-button>
<el-button
type="primary"
link
@click="handleWorkbench(scope.row)"
v-hasPermi="['prison:prisoner:query']"
>
工作台
</el-button>
<el-button
type="primary"
link
@click="handleDetail(scope.row)"
v-hasPermi="['prison:prisoner:query']"
>
详情
</el-button>
<el-button
type="primary"
link
@click="openForm('update', scope.row.id)"
v-hasPermi="['prison:prisoner:update']"
>
修改
</el-button>
<el-button
type="warning"
link
@click="handleTransfer(scope.row)"
v-hasPermi="['prison:prisoner:transfer']"
>
调监
</el-button>
<el-button
type="danger"
link
@click="handleDelete(scope.row.id)"
v-hasPermi="['prison:prisoner:delete']"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页组件 -->
<Pagination
:total="total"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getList"
/>
</ContentWrap>
<!-- 表单弹窗添加/修改 -->
<PrisonerForm ref="formRef" @success="getList" />
<!-- 详情弹窗 -->
<PrisonerDetail ref="detailRef" />
<!-- 工作台弹窗 -->
<PrisonerWorkbench ref="workbenchRef" />
<!-- 调监弹窗 -->
<TransferForm ref="transferRef" @success="getList" />
<!-- 看板弹窗 -->
<el-drawer v-model="dashboardVisible" title="罪犯看板" size="80%">
<template #header>
<div class="dashboard-drawer-header">
<span>罪犯看板</span>
<el-button type="primary" link @click="expandDashboard">
<Icon icon="ep:full-screen" class="mr-5px" /> 放大
</el-button>
</div>
</template>
<div v-loading="dashboardLoading" class="dashboard-drawer-content">
<iframe
v-if="dashboardPrisonerId"
:src="`/prisoner/prisoner/dashboard?prisonerId=${dashboardPrisonerId}`"
class="dashboard-iframe"
frameborder="0"
></iframe>
</div>
</el-drawer>
</template>
<script lang="ts" setup>
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
import PrisonerForm from './PrisonerForm.vue'
import PrisonerDetail from './PrisonerDetail.vue'
import PrisonerWorkbench from './PrisonerWorkbench.vue'
import TransferForm from './TransferForm.vue'
import download from '@/utils/download'
import { PrisonerApi } from '@/api/prison/prisoner'
import type { PrisonerVO } from '@/api/prison/prisoner'
import { AreaApi, type AreaNode } from '@/api/prison/area'
import { CellApi, type CellVO } from '@/api/prison/cell'
import { ref, reactive, onMounted } from 'vue'
defineOptions({ name: 'PrisonPrisoner' })
const { t } = useI18n() // 国际化
const message = useMessage() // 消息弹窗
const loading = ref(true) // 列表的加载中
const total = ref(0) // 列表的总页数
const list = ref<PrisonerVO[]>([]) // 列表的数据
const areaTreeList = ref<AreaNode[]>([]) // 监区树形列表
const cellList = ref<CellVO[]>([]) // 监室列表
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
prisonerNo: undefined as string | undefined,
name: undefined as string | undefined,
gender: undefined as number | undefined,
crime: undefined as string | undefined,
supervisionLevel: undefined as number | undefined,
riskLevel: undefined as number | undefined,
prisonAreaId: undefined as number | undefined,
prisonCellId: undefined as number | undefined,
status: undefined as number | undefined,
imprisonmentDate: [] as string[], // 日期范围数组
imprisonmentDateStart: undefined as string | undefined,
imprisonmentDateEnd: undefined as string | undefined
})
const queryFormRef = ref() // 搜索的表单
const exportLoading = ref(false) // 导出的加载中
const formRef = ref() // 表单
const detailRef = ref() // 详情
const workbenchRef = ref() // 工作台
const transferRef = ref() // 调监表单
/** 查询列表 */
const getList = async () => {
loading.value = true
try {
// 处理日期范围
if (queryParams.imprisonmentDate && queryParams.imprisonmentDate.length === 2) {
queryParams.imprisonmentDateStart = queryParams.imprisonmentDate[0]
queryParams.imprisonmentDateEnd = queryParams.imprisonmentDate[1]
} else {
queryParams.imprisonmentDateStart = undefined
queryParams.imprisonmentDateEnd = undefined
}
const data = await PrisonerApi.getPage(queryParams)
list.value = data.list
total.value = data.total
} finally {
loading.value = false
}
}
/** 搜索按钮操作 */
const handleQuery = () => {
queryParams.pageNo = 1
getList()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryFormRef.value.resetFields()
queryParams.prisonCellId = undefined
queryParams.imprisonmentDate = []
queryParams.imprisonmentDateStart = undefined
queryParams.imprisonmentDateEnd = undefined
handleQuery()
}
/** 导出按钮操作 */
const handleExport = async () => {
try {
// 导出的二次确认
await message.exportConfirm()
// 发起导出
exportLoading.value = true
const data = await PrisonerApi.export(queryParams)
download.excel(data, '罪犯信息.xls')
} catch {
} finally {
exportLoading.value = false
}
}
/** 添加/修改操作 */
const openForm = (type: string, id?: number) => {
formRef.value.open(type, id)
}
/** 查看详情 */
const handleDetail = (row: PrisonerVO) => {
detailRef.value.open(row.id)
}
/** 查看看板 */
const dashboardVisible = ref(false)
const dashboardPrisonerId = ref<number | null>(null)
const dashboardLoading = ref(true)
const handleDashboard = (row: PrisonerVO) => {
dashboardPrisonerId.value = row.id
dashboardLoading.value = true
dashboardVisible.value = true
// 模拟加载完成
setTimeout(() => {
dashboardLoading.value = false
}, 1000)
}
/** 放大看板 */
const expandDashboard = () => {
if (dashboardPrisonerId.value) {
window.open(`/prisoner/prisoner/dashboard?prisonerId=${dashboardPrisonerId.value}`, '_blank')
}
}
/** 打开工作台 */
const handleWorkbench = (row: PrisonerVO) => {
workbenchRef.value.open(row.id)
}
/** 打开调监弹窗 */
const openTransferForm = () => {
if (checkedIds.value.length !== 1) {
message.warning('请先选择一个罪犯进行调监')
return
}
transferRef.value.open(checkedIds.value[0])
}
/** 调监操作 */
const handleTransfer = (row: PrisonerVO) => {
transferRef.value.open(row.id)
}
/** 监区变化时加载监室列表 */
const handleAreaChange = async (areaId: number) => {
queryParams.prisonCellId = undefined
cellList.value = []
if (areaId) {
cellList.value = await CellApi.getCellPage({ areaId, pageNo: 1, pageSize: 200 }).then((res: { list: CellVO[] }) => res.list || [])
}
}
/** 加载监区列表 */
const loadAreas = async () => {
areaTreeList.value = await AreaApi.getAreaTree()
}
/** 身份证脱敏显示 */
const formatIdCard = (idCard: string) => {
if (!idCard || idCard.length < 8) return idCard
return idCard.replace(/(\d{6})\d{8}(\d{4})/, '$1********$2')
}
/** 刑期显示格式化 */
const formatSentence = (row: PrisonerVO) => {
if (row.lifeImprisonment === 1) {
return '无期'
}
if (row.deathSentenceReprieve === 1) {
return '死缓'
}
const years = row.sentenceYears || 0
const months = row.sentenceMonths || 0
let result = ''
if (years > 0) result += `${years}`
if (months > 0) result += `${months}`
return result || '-'
}
/** 删除按钮操作 */
const handleDelete = async (id: number) => {
try {
// 删除的二次确认
await message.delConfirm()
// 发起删除
await PrisonerApi.delete(id)
message.success(t('common.delSuccess'))
// 刷新列表
await getList()
} catch {}
}
/** 批量删除按钮操作 */
const checkedIds = ref<number[]>([])
const handleRowCheckboxChange = (rows: PrisonerVO[]) => {
checkedIds.value = rows.map((row) => row.id)
}
const handleDeleteBatch = async () => {
try {
// 删除的二次确认
await message.delConfirm()
// 发起批量删除
await PrisonerApi.deleteList(checkedIds.value)
checkedIds.value = []
message.success(t('common.delSuccess'))
// 刷新列表
await getList()
} catch {}
}
/** 初始化 **/
onMounted(() => {
getList()
loadAreas()
})
</script>
<style lang="scss" scoped>
.dashboard-drawer-header {
display: flex;
align-items: center;
justify-content: space-between;
flex: 1;
padding-right: 20px;
}
.dashboard-drawer-content {
height: calc(100vh - 100px);
background: #000;
border-radius: 4px;
overflow: hidden;
}
.dashboard-iframe {
width: 100%;
height: 100%;
border: none;
}
</style>