2026-01-27 18:00:43 +08:00

425 lines
12 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>
<div class="report-manage-container">
<!-- 左侧服刑人员列表 -->
<div class="prisoner-panel">
<div class="panel-header">
<span class="panel-title">服刑人员列表</span>
</div>
<!-- 搜索表单 -->
<el-form :model="queryParams" ref="queryFormRef" :inline="true" label-width="70px" class="search-form">
<el-form-item label="罪犯编号" prop="prisonerNo">
<el-input
v-model="queryParams.prisonerNo"
placeholder="请输入"
clearable
@keyup.enter="handleQuery"
class="!w-120px"
/>
</el-form-item>
<el-form-item label="姓名" prop="name">
<el-input
v-model="queryParams.name"
placeholder="请输入"
clearable
@keyup.enter="handleQuery"
class="!w-100px"
/>
</el-form-item>
<el-form-item>
<el-button type="primary" @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-form-item>
</el-form>
<!-- 服刑人员表格 -->
<el-table
v-loading="prisonerLoading"
:data="prisonerList"
@row-click="selectPrisoner"
highlight-current-row
row-class-name="clickable-row"
class="prisoner-table"
>
<el-table-column label="罪犯信息" min-width="150">
<template #default="{ row }">
<div class="prisoner-info">
<span class="prisoner-name">{{ row.name }}</span>
<span class="prisoner-no">{{ row.prisonerNo }}</span>
</div>
</template>
</el-table-column>
<el-table-column label="监区" prop="prisonAreaName" width="100" />
<el-table-column label="状态" width="80">
<template #default="{ row }">
<dict-tag :type="DICT_TYPE.PRISONER_STATUS" :value="row.status" />
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<Pagination
:total="prisonerTotal"
v-model:page="queryParams.pageNo"
v-model:limit="queryParams.pageSize"
@pagination="getPrisonerList"
class="prisoner-pagination"
/>
</div>
<!-- 右侧报告管理区 -->
<div class="report-panel">
<template v-if="selectedPrisoner">
<!-- 顶部信息栏 -->
<div class="report-header">
<div class="prisoner-detail">
<span class="label">当前服刑人员</span>
<span class="value">{{ selectedPrisoner.name }}</span>
<span class="value">({{ selectedPrisoner.prisonerNo }})</span>
<span class="value"> - {{ selectedPrisoner.prisonAreaName || '未分配监区' }}</span>
</div>
<div class="actions">
<el-button type="primary" @click="handleCreateReport" v-hasPermi="['prison:evaluation-report:report:create']">
<Icon icon="ep:plus" class="mr-5px" /> 新建报告
</el-button>
</div>
</div>
<!-- 报告列表 -->
<el-table v-loading="reportLoading" :data="reportList" class="report-table">
<el-table-column label="报告编号" align="center" prop="reportNo" width="150" />
<el-table-column label="评估日期" align="center" prop="evaluationDate" width="120">
<template #default="{ row }">
{{ formatDateTime(row.evaluationDate, 'YYYY-MM-DD') }}
</template>
</el-table-column>
<el-table-column label="模板" align="center" prop="templateName" width="150" />
<el-table-column label="风险等级" align="center" prop="riskLevel" width="100">
<template #default="{ row }">
<el-tag v-if="row.riskLevel" :type="getRiskLevelType(row.riskLevel)" size="small">
{{ getDictLabel(DICT_TYPE.PRISON_RISK_LEVEL, row.riskLevel) }}
</el-tag>
<span v-else>-</span>
</template>
</el-table-column>
<el-table-column label="状态" align="center" prop="status" width="100">
<template #default="{ row }">
<dict-tag :type="DICT_TYPE.PRISON_REPORT_STATUS" :value="row.status" />
</template>
</el-table-column>
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
<template #default="{ row }">
{{ formatDateTime(row.createTime) }}
</template>
</el-table-column>
<el-table-column label="操作" align="center" width="180" fixed="right">
<template #default="{ row }">
<el-button
type="primary"
link
@click.stop="handleEditReport(row)"
v-hasPermi="['prison:evaluation-report:report:query']"
>
编辑
</el-button>
<el-button
type="primary"
link
@click.stop="handleExportReport(row)"
v-hasPermi="['prison:evaluation-report:report:query']"
>
导出
</el-button>
<el-button
type="warning"
link
@click.stop="handleSubmitReport(row)"
v-hasPermi="['prison:evaluation-report:report:update']"
v-if="row.status === 1"
>
提交
</el-button>
<el-button
type="danger"
link
@click.stop="handleDeleteReport(row)"
v-hasPermi="['prison:evaluation-report:report:delete']"
>
删除
</el-button>
</template>
</el-table-column>
</el-table>
</template>
<template v-else>
<el-empty description="请从左侧列表选择一名服刑人员" />
</template>
</div>
<!-- 编辑报告抽屉 -->
<ReportEditDrawer ref="editDrawerRef" @success="loadReports" />
<!-- 新建报告对话框 -->
<CreateReportDialog ref="createDialogRef" @success="loadReports" />
<!-- 导出报告对话框 -->
<CreateReportOutput ref="exportDialogRef" />
</div>
</template>
<script setup lang="ts">
import { DICT_TYPE, getIntDictOptions, getDictLabel } from '@/utils/dict'
import { formatDateTime } from '@/utils/formatTime'
import { PrisonerApi, type PrisonerVO } from '@/api/prison/prisoner'
import { ReportApi, type ReportVO } from '@/api/prison/evaluation-report'
import ReportEditDrawer from '../report/ReportEditDrawer.vue'
import CreateReportDialog from '../report/CreateReportDialog.vue'
import CreateReportOutput from '../report/CreateReportOutput.vue'
defineOptions({ name: 'PrisonerReportManage' })
const { t } = useI18n()
const message = useMessage()
// ========== 服刑人员相关 ==========
const prisonerLoading = ref(false)
const prisonerList = ref<PrisonerVO[]>([])
const prisonerTotal = ref(0)
const queryParams = reactive({
pageNo: 1,
pageSize: 10,
prisonerNo: undefined as string | undefined,
name: undefined as string | undefined,
status: undefined as number | undefined
})
const queryFormRef = ref()
// ========== 报告相关 ==========
const reportLoading = ref(false)
const reportList = ref<ReportVO[]>([])
const selectedPrisoner = ref<PrisonerVO | null>(null)
const editDrawerRef = ref()
const exportDialogRef = ref()
const createDialogRef = ref()
/** 获取服刑人员列表 */
const getPrisonerList = async () => {
prisonerLoading.value = true
try {
const data = await PrisonerApi.getPage(queryParams)
prisonerList.value = data.list
prisonerTotal.value = data.total
} finally {
prisonerLoading.value = false
}
}
/** 搜索 */
const handleQuery = () => {
queryParams.pageNo = 1
getPrisonerList()
}
/** 重置 */
const resetQuery = () => {
queryFormRef.value?.resetFields()
handleQuery()
}
/** 选择服刑人员 */
const selectPrisoner = async (row: PrisonerVO) => {
selectedPrisoner.value = row
await loadReports()
}
/** 加载报告列表 */
const loadReports = async () => {
if (!selectedPrisoner.value?.id) return
reportLoading.value = true
try {
reportList.value = await ReportApi.getReportsByPrisonerId(selectedPrisoner.value.id)
} finally {
reportLoading.value = false
}
}
/** 获取风险等级样式 */
const getRiskLevelType = (level: number) => {
const map: Record<number, string> = {
1: 'success',
2: 'warning',
3: 'danger',
4: 'danger'
}
return map[level] || 'info'
}
/** 新建报告 */
const handleCreateReport = () => {
if (!selectedPrisoner.value) {
message.warning('请先选择一名服刑人员')
return
}
createDialogRef.value?.open(selectedPrisoner.value.id, `${selectedPrisoner.value.name} (${selectedPrisoner.value.prisonerNo})`)
}
/** 编辑报告 */
const handleEditReport = (row: ReportVO) => {
editDrawerRef.value?.open(row.id!, selectedPrisoner.value!.id)
}
/** 导出报告 */
const handleExportReport = (row: ReportVO) => {
exportDialogRef.value?.open(row.id!, selectedPrisoner.value!.id)
}
/** 提交报告审核 */
const handleSubmitReport = async (row: ReportVO) => {
try {
await message.confirm('确定要提交该报告进行审核吗?')
await ReportApi.submitReport(row.id!)
message.success('提交成功')
await loadReports()
} catch (error) {
if (error !== 'cancel') {
message.error(error?.msg || '提交失败')
}
}
}
/** 删除报告 */
const handleDeleteReport = async (row: ReportVO) => {
try {
await message.delConfirm()
await ReportApi.deleteReport(row.id!)
message.success(t('common.delSuccess'))
await loadReports()
} catch (error) {
if (error !== 'cancel') {
message.error(error?.msg || '删除失败')
}
}
}
/** 初始化 */
onMounted(() => {
getPrisonerList()
})
</script>
<style lang="scss" scoped>
.report-manage-container {
display: flex;
height: calc(100vh - 110px);
background-color: #f5f7fa;
gap: 16px;
padding: 16px;
}
.prisoner-panel {
width: 400px;
min-width: 350px;
background-color: #fff;
border-radius: 4px;
display: flex;
flex-direction: column;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
.panel-header {
padding: 15px 20px;
border-bottom: 1px solid var(--el-border-color-light);
.panel-title {
font-size: 16px;
font-weight: 500;
color: var(--el-text-color-primary);
}
}
.search-form {
padding: 15px 20px;
border-bottom: 1px solid var(--el-border-color-light);
}
.prisoner-table {
flex: 1;
overflow-y: auto;
:deep(.clickable-row) {
cursor: pointer;
&:hover {
background-color: var(--el-fill-color-light);
}
&.current-row {
background-color: var(--el-color-primary-light-9);
}
}
.prisoner-info {
display: flex;
flex-direction: column;
gap: 4px;
.prisoner-name {
font-weight: 500;
color: var(--el-text-color-primary);
}
.prisoner-no {
font-size: 12px;
color: var(--el-text-color-secondary);
}
}
}
.prisoner-pagination {
padding: 10px 20px;
border-top: 1px solid var(--el-border-color-light);
}
}
.report-panel {
flex: 1;
background-color: #fff;
border-radius: 4px;
display: flex;
flex-direction: column;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
overflow: hidden;
.report-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 15px 20px;
border-bottom: 1px solid var(--el-border-color-light);
.prisoner-detail {
font-size: 14px;
.label {
color: var(--el-text-color-secondary);
}
.value {
font-weight: 500;
color: var(--el-text-color-primary);
margin-right: 4px;
&:last-child {
margin-right: 0;
}
}
}
}
.report-table {
flex: 1;
overflow-y: auto;
}
}
</style>