Initial commit - xlcp frontend
This commit is contained in:
parent
d4cb996085
commit
4be92f62bd
1
public/china.json
Normal file
1
public/china.json
Normal file
File diff suppressed because one or more lines are too long
@ -3,21 +3,66 @@ import type { Dayjs } from 'dayjs';
|
|||||||
|
|
||||||
/** 监区信息信息 */
|
/** 监区信息信息 */
|
||||||
export interface Area {
|
export interface Area {
|
||||||
id: number; // 监区ID
|
id: number // 监区ID
|
||||||
name?: string; // 监区名称
|
name?: string // 监区名称
|
||||||
code?: string; // 监区编码
|
code?: string // 监区编码
|
||||||
type: number; // 监区类型:1-普通监区 2-严管监区 3-医院 4-禁闭室
|
parentId?: number // 父级ID,0表示顶级监区
|
||||||
capacity: number; // 容纳人数
|
level?: number // 级别:1-监区(大队) 2-分监区(中队)
|
||||||
currentCount: number; // 当前人数
|
type: number // 监区类型:1-普通监区 2-严管监区 3-集训监区 4-出监监区 5-医院监区 6-禁闭室
|
||||||
sort: number; // 排序
|
capacity: number // 容纳人数
|
||||||
status?: number; // 状态:1-启用 2-禁用
|
currentCount: number // 当前人数
|
||||||
remark: string; // 备注
|
sort: number // 排序
|
||||||
}
|
status?: number // 状态:1-启用 2-禁用
|
||||||
|
remark: string // 备注
|
||||||
|
children?: Area[] // 子监区列表(树形结构)
|
||||||
|
createTime?: Date // 创建时间
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监区树形节点
|
||||||
|
export interface AreaNode {
|
||||||
|
id: number
|
||||||
|
name: string
|
||||||
|
code: string
|
||||||
|
parentId: number
|
||||||
|
level: number
|
||||||
|
type: number
|
||||||
|
capacity: number
|
||||||
|
currentCount: number
|
||||||
|
sort: number
|
||||||
|
status: number
|
||||||
|
remark: string
|
||||||
|
children?: AreaNode[]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监区信息创建/更新请求
|
||||||
|
export interface AreaSaveReqVO {
|
||||||
|
id?: number
|
||||||
|
name: string
|
||||||
|
code: string
|
||||||
|
parentId?: number
|
||||||
|
level?: number
|
||||||
|
type: number
|
||||||
|
capacity: number
|
||||||
|
sort?: number
|
||||||
|
status?: number
|
||||||
|
remark?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监区信息分页查询
|
||||||
|
export interface AreaPageReqVO {
|
||||||
|
pageNo: number
|
||||||
|
pageSize: number
|
||||||
|
name?: string
|
||||||
|
code?: string
|
||||||
|
type?: number
|
||||||
|
level?: number
|
||||||
|
status?: number
|
||||||
|
}
|
||||||
|
|
||||||
// 监区信息 API
|
// 监区信息 API
|
||||||
export const AreaApi = {
|
export const AreaApi = {
|
||||||
// 查询监区信息分页
|
// 查询监区信息分页
|
||||||
getAreaPage: async (params: any) => {
|
getAreaPage: async (params: AreaPageReqVO) => {
|
||||||
return await request.get({ url: `/prison/area/page`, params })
|
return await request.get({ url: `/prison/area/page`, params })
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -26,13 +71,23 @@ export const AreaApi = {
|
|||||||
return await request.get({ url: `/prison/area/get?id=` + id })
|
return await request.get({ url: `/prison/area/get?id=` + id })
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 查询监区树形结构
|
||||||
|
getAreaTree: async () => {
|
||||||
|
return await request.get({ url: `/prison/area/tree` })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 查询父级监区列表(用于新增/编辑时选择)
|
||||||
|
getParentAreas: async (level?: number) => {
|
||||||
|
return await request.get({ url: `/prison/area/parent-list`, params: { level } })
|
||||||
|
},
|
||||||
|
|
||||||
// 新增监区信息
|
// 新增监区信息
|
||||||
createArea: async (data: Area) => {
|
createArea: async (data: AreaSaveReqVO) => {
|
||||||
return await request.post({ url: `/prison/area/create`, data })
|
return await request.post({ url: `/prison/area/create`, data })
|
||||||
},
|
},
|
||||||
|
|
||||||
// 修改监区信息
|
// 修改监区信息
|
||||||
updateArea: async (data: Area) => {
|
updateArea: async (data: AreaSaveReqVO) => {
|
||||||
return await request.put({ url: `/prison/area/update`, data })
|
return await request.put({ url: `/prison/area/update`, data })
|
||||||
},
|
},
|
||||||
|
|
||||||
@ -49,5 +104,10 @@ export const AreaApi = {
|
|||||||
// 导出监区信息 Excel
|
// 导出监区信息 Excel
|
||||||
exportArea: async (params) => {
|
exportArea: async (params) => {
|
||||||
return await request.download({ url: `/prison/area/export-excel`, params })
|
return await request.download({ url: `/prison/area/export-excel`, params })
|
||||||
|
},
|
||||||
|
|
||||||
|
// 同步监区人数
|
||||||
|
syncCurrentCount: async (id: number) => {
|
||||||
|
return await request.put({ url: `/prison/area/sync-count?id=` + id })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -12,6 +12,7 @@ export interface Cell {
|
|||||||
sort: number; // 排序
|
sort: number; // 排序
|
||||||
status?: number; // 状态:1-启用 2-禁用
|
status?: number; // 状态:1-启用 2-禁用
|
||||||
remark: string; // 备注
|
remark: string; // 备注
|
||||||
|
createTime?: Date; // 创建时间
|
||||||
}
|
}
|
||||||
|
|
||||||
// 监室信息 API
|
// 监室信息 API
|
||||||
|
|||||||
36
src/api/prison/dashboard/index.ts
Normal file
36
src/api/prison/dashboard/index.ts
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
/** 看板统计响应 */
|
||||||
|
export interface DashboardStatisticsVO {
|
||||||
|
totalPrisoners: number // 在册罪犯总数
|
||||||
|
monthlyReleased: number // 本月释放人数
|
||||||
|
monthlyTransferred: number // 本月移交人数
|
||||||
|
hospitalCount: number // 当前就医人数
|
||||||
|
solitaryCount: number // 当前禁闭人数
|
||||||
|
ageDistribution: ChartDataVO[] // 年龄分布
|
||||||
|
sentenceDistribution: ChartDataVO[] // 刑期分布
|
||||||
|
educationDistribution: ChartDataVO[] // 文化程度分布
|
||||||
|
provinceDistribution: ProvinceChartVO[] // 省份分布
|
||||||
|
statisticsTime: string // 统计时间
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 图表数据项 */
|
||||||
|
export interface ChartDataVO {
|
||||||
|
name: string // 分组名称
|
||||||
|
value: number // 数量
|
||||||
|
percentage?: number // 占比
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 省份数据 */
|
||||||
|
export interface ProvinceChartVO {
|
||||||
|
province: string // 省份名称
|
||||||
|
provinceCode: number // 省份编码
|
||||||
|
count: number // 人数
|
||||||
|
}
|
||||||
|
|
||||||
|
export const DashboardApi = {
|
||||||
|
// 获取看板统计数据
|
||||||
|
getStatistics: async (): Promise<DashboardStatisticsVO> => {
|
||||||
|
return await request.get({ url: '/prison/dashboard/statistics' })
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -5,6 +5,51 @@ export interface PrisonerVO {
|
|||||||
prisonerNo: string
|
prisonerNo: string
|
||||||
name: string
|
name: string
|
||||||
gender: number
|
gender: number
|
||||||
|
genderName?: string // 性别名称
|
||||||
|
birthday: string
|
||||||
|
idCard: string
|
||||||
|
ethnicity: string
|
||||||
|
nativePlace: string
|
||||||
|
education: number
|
||||||
|
educationName?: string // 文化程度名称
|
||||||
|
occupation: string
|
||||||
|
address: string
|
||||||
|
crime: string
|
||||||
|
sentenceYears: number
|
||||||
|
sentenceMonths: number
|
||||||
|
lifeImprisonment: number
|
||||||
|
deathSentenceReprieve: number
|
||||||
|
courtName: string
|
||||||
|
judgmentDate: string
|
||||||
|
judgmentNo: string
|
||||||
|
originalSentence: string
|
||||||
|
imprisonmentDate: string
|
||||||
|
releaseDate: string
|
||||||
|
supervisionLevel: number
|
||||||
|
supervisionLevelName?: string // 监管等级名称
|
||||||
|
riskLevel: number
|
||||||
|
riskLevelName?: string // 风险等级名称
|
||||||
|
prisonAreaId: number
|
||||||
|
prisonAreaName?: string // 监区名称
|
||||||
|
subAreaId?: number // 分监区ID
|
||||||
|
subAreaName?: string // 分监区名称
|
||||||
|
prisonCellId: number
|
||||||
|
prisonCellName?: string // 监室名称
|
||||||
|
status: number
|
||||||
|
statusName?: string // 状态名称
|
||||||
|
photo?: string // 照片URL
|
||||||
|
releaseType?: number // 释放类型
|
||||||
|
releaseReason?: string // 释放原因
|
||||||
|
remark: string
|
||||||
|
createTime: Date
|
||||||
|
}
|
||||||
|
|
||||||
|
// 服刑人员创建/更新请求
|
||||||
|
export interface PrisonerCreateVO {
|
||||||
|
id?: number
|
||||||
|
prisonerNo: string
|
||||||
|
name: string
|
||||||
|
gender: number
|
||||||
birthday: string
|
birthday: string
|
||||||
idCard: string
|
idCard: string
|
||||||
ethnicity: string
|
ethnicity: string
|
||||||
@ -15,15 +60,21 @@ export interface PrisonerVO {
|
|||||||
crime: string
|
crime: string
|
||||||
sentenceYears: number
|
sentenceYears: number
|
||||||
sentenceMonths: number
|
sentenceMonths: number
|
||||||
|
lifeImprisonment: number
|
||||||
|
deathSentenceReprieve: number
|
||||||
|
courtName: string
|
||||||
|
judgmentDate: string
|
||||||
|
judgmentNo: string
|
||||||
|
originalSentence: string
|
||||||
imprisonmentDate: string
|
imprisonmentDate: string
|
||||||
releaseDate: string
|
releaseDate: string
|
||||||
supervisionLevel: number
|
supervisionLevel: number
|
||||||
riskLevel: number
|
riskLevel: number
|
||||||
prisonAreaId: number
|
prisonAreaId: number
|
||||||
|
subAreaId?: number // 分监区ID
|
||||||
prisonCellId: number
|
prisonCellId: number
|
||||||
status: number
|
photo?: string // 照片URL
|
||||||
remark: string
|
remark: string
|
||||||
createTime: Date
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 服刑人员分页查询
|
// 服刑人员分页查询
|
||||||
@ -37,12 +88,12 @@ export const getPrisoner = (id: number) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 新增服刑人员
|
// 新增服刑人员
|
||||||
export const createPrisoner = (data: PrisonerVO) => {
|
export const createPrisoner = (data: PrisonerCreateVO) => {
|
||||||
return request.post({ url: '/prison/prisoner/create', data })
|
return request.post({ url: '/prison/prisoner/create', data })
|
||||||
}
|
}
|
||||||
|
|
||||||
// 修改服刑人员
|
// 修改服刑人员
|
||||||
export const updatePrisoner = (data: PrisonerVO) => {
|
export const updatePrisoner = (data: PrisonerCreateVO) => {
|
||||||
return request.put({ url: '/prison/prisoner/update', data })
|
return request.put({ url: '/prison/prisoner/update', data })
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -60,3 +111,61 @@ export const deletePrisonerList = (ids: number[]) => {
|
|||||||
export const exportPrisoner = (params) => {
|
export const exportPrisoner = (params) => {
|
||||||
return request.download({ url: '/prison/prisoner/export-excel', params })
|
return request.download({ url: '/prison/prisoner/export-excel', params })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 调监请求
|
||||||
|
export interface TransferReqVO {
|
||||||
|
prisonerId: number
|
||||||
|
targetCellId: number
|
||||||
|
reason?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 执行调监
|
||||||
|
export const doTransfer = (data: TransferReqVO) => {
|
||||||
|
return request.post({ url: '/prison/prisoner/transfer', params: data })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 罪犯位置历史记录
|
||||||
|
export interface PrisonerAreaLogVO {
|
||||||
|
id: number
|
||||||
|
prisonerId: number
|
||||||
|
prisonerNo: string
|
||||||
|
prisonerName: string
|
||||||
|
fromAreaId: number
|
||||||
|
fromAreaName: string
|
||||||
|
fromCellId: number
|
||||||
|
fromCellName: string
|
||||||
|
toAreaId: number
|
||||||
|
toAreaName: string
|
||||||
|
toCellId: number
|
||||||
|
toCellName: string
|
||||||
|
transferType: number
|
||||||
|
transferTypeName: string
|
||||||
|
reason: string
|
||||||
|
operatorId: number
|
||||||
|
operatorName: string
|
||||||
|
createTime: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取罪犯位置历史
|
||||||
|
export const getPrisonerAreaHistory = (prisonerId: number) => {
|
||||||
|
return request.get({ url: '/prison/prisoner-area-log/list-by-prisoner-id', params: { prisonerId } })
|
||||||
|
}
|
||||||
|
|
||||||
|
// 导入服刑人员
|
||||||
|
export const importPrisoner = (data: FormData) => {
|
||||||
|
return request.upload({ url: '/prison/prisoner/import-excel', data })
|
||||||
|
}
|
||||||
|
|
||||||
|
// PrisonerApi 对象 - 用于组件导入
|
||||||
|
export const PrisonerApi = {
|
||||||
|
getPage: getPrisonerPage,
|
||||||
|
get: getPrisoner,
|
||||||
|
create: createPrisoner,
|
||||||
|
update: updatePrisoner,
|
||||||
|
delete: deletePrisoner,
|
||||||
|
deleteList: deletePrisonerList,
|
||||||
|
export: exportPrisoner,
|
||||||
|
doTransfer: doTransfer,
|
||||||
|
getAreaHistory: getPrisonerAreaHistory,
|
||||||
|
import: importPrisoner
|
||||||
|
}
|
||||||
|
|||||||
81
src/api/prison/release/index.ts
Normal file
81
src/api/prison/release/index.ts
Normal file
@ -0,0 +1,81 @@
|
|||||||
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
export interface Release {
|
||||||
|
id: number
|
||||||
|
prisonerId: number
|
||||||
|
prisonerNo: string
|
||||||
|
prisonerName: string
|
||||||
|
releaseType: number
|
||||||
|
releaseTypeName: string
|
||||||
|
releaseReason: string
|
||||||
|
courtName: string
|
||||||
|
judgmentNo: string
|
||||||
|
actualReleaseDate: string
|
||||||
|
handoverPerson: string
|
||||||
|
handoverUnit: string
|
||||||
|
certificateType: number
|
||||||
|
certificateNo: string
|
||||||
|
status: number
|
||||||
|
statusName: string
|
||||||
|
remark: string
|
||||||
|
operatorId: number
|
||||||
|
operatorName: string
|
||||||
|
createTime: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ReleasePageReqVO {
|
||||||
|
prisonerNo?: string
|
||||||
|
prisonerName?: string
|
||||||
|
releaseType?: number
|
||||||
|
status?: number
|
||||||
|
actualReleaseDateStart?: string
|
||||||
|
actualReleaseDateEnd?: string
|
||||||
|
pageNo: number
|
||||||
|
pageSize: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ReleaseSaveReqVO {
|
||||||
|
id?: number
|
||||||
|
prisonerId: number
|
||||||
|
releaseType: number
|
||||||
|
releaseReason?: string
|
||||||
|
courtName?: string
|
||||||
|
judgmentNo?: string
|
||||||
|
actualReleaseDate: string
|
||||||
|
handoverPerson?: string
|
||||||
|
handoverUnit?: string
|
||||||
|
certificateType?: number
|
||||||
|
certificateNo?: string
|
||||||
|
remark?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 释放登记 API
|
||||||
|
export const ReleaseApi = {
|
||||||
|
getReleasePage: (params: ReleasePageReqVO) => {
|
||||||
|
return request.get({ url: '/prison/release/page', params })
|
||||||
|
},
|
||||||
|
getRelease: (id: number) => {
|
||||||
|
return request.get({ url: `/prison/release/get?id=${id}` })
|
||||||
|
},
|
||||||
|
createRelease: (data: ReleaseSaveReqVO) => {
|
||||||
|
return request.post({ url: '/prison/release/create', data })
|
||||||
|
},
|
||||||
|
updateRelease: (data: ReleaseSaveReqVO) => {
|
||||||
|
return request.put({ url: '/prison/release/update', data })
|
||||||
|
},
|
||||||
|
deleteRelease: (id: number) => {
|
||||||
|
return request.delete({ url: `/prison/release/delete?id=${id}` })
|
||||||
|
},
|
||||||
|
deleteReleaseList: (ids: number[]) => {
|
||||||
|
return request.delete({ url: `/prison/release/delete-list?ids=${ids.join(',')}` })
|
||||||
|
},
|
||||||
|
doRelease: (id: number) => {
|
||||||
|
return request.post({ url: `/prison/release/do-release?id=${id}` })
|
||||||
|
},
|
||||||
|
cancelRelease: (id: number) => {
|
||||||
|
return request.post({ url: `/prison/release/cancel-release?id=${id}` })
|
||||||
|
},
|
||||||
|
exportRelease: (params: ReleasePageReqVO) => {
|
||||||
|
return request.download({ url: '/prison/release/export-excel', params })
|
||||||
|
}
|
||||||
|
}
|
||||||
66
src/api/prison/score-detail/index.ts
Normal file
66
src/api/prison/score-detail/index.ts
Normal file
@ -0,0 +1,66 @@
|
|||||||
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
export interface ScoreDetail {
|
||||||
|
id: number
|
||||||
|
prisonerId: number
|
||||||
|
prisonerNo: string
|
||||||
|
recordDate: string
|
||||||
|
ruleId: number
|
||||||
|
ruleName: string
|
||||||
|
score: number
|
||||||
|
scoreType: number
|
||||||
|
scoreTypeName: string
|
||||||
|
remark: string
|
||||||
|
recorderId: number
|
||||||
|
recorderName: string
|
||||||
|
status: number
|
||||||
|
statusName: string
|
||||||
|
createTime: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ScoreDetailPageReqVO {
|
||||||
|
prisonerNo?: string
|
||||||
|
prisonerId?: number
|
||||||
|
recordDateStart?: string
|
||||||
|
recordDateEnd?: string
|
||||||
|
scoreType?: number
|
||||||
|
ruleId?: number
|
||||||
|
status?: number
|
||||||
|
pageNo: number
|
||||||
|
pageSize: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ScoreDetailSaveReqVO {
|
||||||
|
id?: number
|
||||||
|
prisonerId: number
|
||||||
|
recordDate: string
|
||||||
|
ruleId: number
|
||||||
|
score: number
|
||||||
|
scoreType: number
|
||||||
|
remark?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 考核记录 API
|
||||||
|
export const ScoreDetailApi = {
|
||||||
|
getPage: (params: ScoreDetailPageReqVO) => {
|
||||||
|
return request.get({ url: '/prison/score-detail/page', params })
|
||||||
|
},
|
||||||
|
get: (id: number) => {
|
||||||
|
return request.get({ url: `/prison/score-detail/get?id=${id}` })
|
||||||
|
},
|
||||||
|
create: (data: ScoreDetailSaveReqVO) => {
|
||||||
|
return request.post({ url: '/prison/score-detail/create', data })
|
||||||
|
},
|
||||||
|
update: (data: ScoreDetailSaveReqVO) => {
|
||||||
|
return request.put({ url: '/prison/score-detail/update', data })
|
||||||
|
},
|
||||||
|
delete: (id: number) => {
|
||||||
|
return request.delete({ url: `/prison/score-detail/delete?id=${id}` })
|
||||||
|
},
|
||||||
|
deleteList: (ids: number[]) => {
|
||||||
|
return request.delete({ url: `/prison/score-detail/delete-list?ids=${ids.join(',')}` })
|
||||||
|
},
|
||||||
|
export: (params: ScoreDetailPageReqVO) => {
|
||||||
|
return request.download({ url: '/prison/score-detail/export-excel', params })
|
||||||
|
}
|
||||||
|
}
|
||||||
67
src/api/prison/score-rule/index.ts
Normal file
67
src/api/prison/score-rule/index.ts
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
import request from '@/config/axios'
|
||||||
|
|
||||||
|
export interface ScoreRule {
|
||||||
|
id: number
|
||||||
|
category: number
|
||||||
|
categoryName: string
|
||||||
|
itemName: string
|
||||||
|
itemCode: string
|
||||||
|
score: number
|
||||||
|
maxDailyScore: number
|
||||||
|
maxMonthlyScore: number
|
||||||
|
description: string
|
||||||
|
status: number
|
||||||
|
statusName: string
|
||||||
|
sort: number
|
||||||
|
createTime: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ScoreRulePageReqVO {
|
||||||
|
category?: number
|
||||||
|
itemName?: string
|
||||||
|
itemCode?: string
|
||||||
|
status?: number
|
||||||
|
pageNo: number
|
||||||
|
pageSize: number
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ScoreRuleSaveReqVO {
|
||||||
|
id?: number
|
||||||
|
category: number
|
||||||
|
itemName: string
|
||||||
|
itemCode: string
|
||||||
|
score: number
|
||||||
|
maxDailyScore?: number
|
||||||
|
maxMonthlyScore?: number
|
||||||
|
description?: string
|
||||||
|
status?: number
|
||||||
|
sort?: number
|
||||||
|
}
|
||||||
|
|
||||||
|
// 考核规则 API
|
||||||
|
export const ScoreRuleApi = {
|
||||||
|
getRulePage: (params: ScoreRulePageReqVO) => {
|
||||||
|
return request.get({ url: '/prison/score-rule/page', params })
|
||||||
|
},
|
||||||
|
getRule: (id: number) => {
|
||||||
|
return request.get({ url: `/prison/score-rule/get?id=${id}` })
|
||||||
|
},
|
||||||
|
createRule: (data: ScoreRuleSaveReqVO) => {
|
||||||
|
return request.post({ url: '/prison/score-rule/create', data })
|
||||||
|
},
|
||||||
|
updateRule: (data: ScoreRuleSaveReqVO) => {
|
||||||
|
return request.put({ url: '/prison/score-rule/update', data })
|
||||||
|
},
|
||||||
|
deleteRule: (id: number) => {
|
||||||
|
return request.delete({ url: `/prison/score-rule/delete?id=${id}` })
|
||||||
|
},
|
||||||
|
deleteRuleList: (ids: number[]) => {
|
||||||
|
return request.delete({ url: `/prison/score-rule/delete-list?ids=${ids.join(',')}` })
|
||||||
|
},
|
||||||
|
getRuleByCategory: (category: number) => {
|
||||||
|
return request.get({ url: `/prison/score-rule/list-by-category?category=${category}` })
|
||||||
|
},
|
||||||
|
exportRule: (params: ScoreRulePageReqVO) => {
|
||||||
|
return request.download({ url: '/prison/score-rule/export-excel', params })
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +1,5 @@
|
|||||||
import * as echarts from 'echarts/core'
|
import * as echarts from 'echarts/core'
|
||||||
|
import type { EChartsType } from 'echarts/core'
|
||||||
|
|
||||||
import {
|
import {
|
||||||
BarChart,
|
BarChart,
|
||||||
@ -48,4 +49,23 @@ echarts.use([
|
|||||||
FunnelChart
|
FunnelChart
|
||||||
])
|
])
|
||||||
|
|
||||||
|
// 导出 echarts 实例和类型
|
||||||
export default echarts
|
export default echarts
|
||||||
|
export type { EChartsType }
|
||||||
|
|
||||||
|
// 动态注册中国地图
|
||||||
|
let chinaMapRegistered = false
|
||||||
|
export const registerChinaMap = async () => {
|
||||||
|
if (chinaMapRegistered) return
|
||||||
|
try {
|
||||||
|
const response = await fetch('/china.json')
|
||||||
|
if (!response.ok) {
|
||||||
|
throw new Error('Failed to fetch China map data')
|
||||||
|
}
|
||||||
|
const chinaJson = await response.json()
|
||||||
|
echarts.registerMap('china', chinaJson)
|
||||||
|
chinaMapRegistered = true
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载中国地图数据失败:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@ -746,6 +746,30 @@ const remainingRouter: AppRouteRecordRaw[] = [
|
|||||||
component: () => import('@/views/iot/ota/firmware/detail/index.vue')
|
component: () => import('@/views/iot/ota/firmware/detail/index.vue')
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
},
|
||||||
|
// 监管看板路由(开发测试用,上线后由后端菜单动态生成)
|
||||||
|
{
|
||||||
|
path: '/prison',
|
||||||
|
component: Layout,
|
||||||
|
name: 'Prison',
|
||||||
|
meta: {
|
||||||
|
hidden: true
|
||||||
|
},
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: 'dashboard',
|
||||||
|
component: () => import('@/views/prison/dashboard/index.vue'),
|
||||||
|
name: 'PrisonDashboard',
|
||||||
|
meta: {
|
||||||
|
title: '监管看板',
|
||||||
|
icon: 'ep:data-board',
|
||||||
|
permission: 'prison:dashboard:query',
|
||||||
|
noCache: false,
|
||||||
|
hidden: true,
|
||||||
|
canTo: true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|||||||
289
src/utils/__tests__/is.test.ts
Normal file
289
src/utils/__tests__/is.test.ts
Normal file
@ -0,0 +1,289 @@
|
|||||||
|
import { describe, it, expect } from 'vitest'
|
||||||
|
import {
|
||||||
|
is,
|
||||||
|
isDef,
|
||||||
|
isUnDef,
|
||||||
|
isObject,
|
||||||
|
isEmpty,
|
||||||
|
isDate,
|
||||||
|
isNull,
|
||||||
|
isNullAndUnDef,
|
||||||
|
isNullOrUnDef,
|
||||||
|
isNumber,
|
||||||
|
isPromise,
|
||||||
|
isString,
|
||||||
|
isFunction,
|
||||||
|
isBoolean,
|
||||||
|
isRegExp,
|
||||||
|
isArray,
|
||||||
|
isElement,
|
||||||
|
isMap,
|
||||||
|
isUrl,
|
||||||
|
isImgPath,
|
||||||
|
isEmptyVal
|
||||||
|
} from '../is'
|
||||||
|
|
||||||
|
describe('is', () => {
|
||||||
|
describe('is()', () => {
|
||||||
|
it('should return true for matching type', () => {
|
||||||
|
expect(is({}, 'Object')).toBe(true)
|
||||||
|
expect(is([], 'Object')).toBe(true)
|
||||||
|
expect(is('test', 'String')).toBe(true)
|
||||||
|
expect(is(123, 'Number')).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return false for non-matching type', () => {
|
||||||
|
expect(is('test', 'Object')).toBe(false)
|
||||||
|
expect(is(123, 'String')).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('isDef()', () => {
|
||||||
|
it('should return true for defined values', () => {
|
||||||
|
expect(isDef(0)).toBe(true)
|
||||||
|
expect(isDef('')).toBe(true)
|
||||||
|
expect(isDef(false)).toBe(true)
|
||||||
|
expect(isDef({})).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return false for undefined', () => {
|
||||||
|
expect(isDef(undefined)).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('isUnDef()', () => {
|
||||||
|
it('should return true for undefined', () => {
|
||||||
|
expect(isUnDef(undefined)).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return false for defined values', () => {
|
||||||
|
expect(isUnDef(null)).toBe(false)
|
||||||
|
expect(isUnDef(0)).toBe(false)
|
||||||
|
expect(isUnDef('')).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('isObject()', () => {
|
||||||
|
it('should return true for objects', () => {
|
||||||
|
expect(isObject({})).toBe(true)
|
||||||
|
expect(isObject({ a: 1 })).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return false for null and non-objects', () => {
|
||||||
|
expect(isObject(null)).toBe(false)
|
||||||
|
expect(isObject([])).toBe(false)
|
||||||
|
expect(isObject('string')).toBe(false)
|
||||||
|
expect(isObject(123)).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('isEmpty()', () => {
|
||||||
|
it('should return true for empty values', () => {
|
||||||
|
expect(isEmpty(null)).toBe(true)
|
||||||
|
expect(isEmpty(undefined)).toBe(true)
|
||||||
|
expect(isEmpty('')).toBe(true)
|
||||||
|
expect(isEmpty([])).toBe(true)
|
||||||
|
expect(isEmpty({})).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return false for non-empty values', () => {
|
||||||
|
expect(isEmpty('test')).toBe(false)
|
||||||
|
expect(isEmpty([1, 2, 3])).toBe(false)
|
||||||
|
expect(isEmpty({ a: 1 })).toBe(false)
|
||||||
|
expect(isEmpty(0)).toBe(false)
|
||||||
|
expect(isEmpty(false)).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('isDate()', () => {
|
||||||
|
it('should return true for Date objects', () => {
|
||||||
|
expect(isDate(new Date())).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return false for non-date values', () => {
|
||||||
|
expect(isDate('2024-01-01')).toBe(false)
|
||||||
|
expect(isDate(123456)).toBe(false)
|
||||||
|
expect(isDate(null)).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('isNull()', () => {
|
||||||
|
it('should return true for null', () => {
|
||||||
|
expect(isNull(null)).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return false for non-null values', () => {
|
||||||
|
expect(isNull(undefined)).toBe(false)
|
||||||
|
expect(isNull(0)).toBe(false)
|
||||||
|
expect(isNull('')).toBe(false)
|
||||||
|
expect(isNull({})).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('isNullAndUnDef()', () => {
|
||||||
|
it('should return true for null and undefined', () => {
|
||||||
|
expect(isNullAndUnDef(null)).toBe(true)
|
||||||
|
expect(isNullAndUnDef(undefined)).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return false for other values', () => {
|
||||||
|
expect(isNullAndUnDef(0)).toBe(false)
|
||||||
|
expect(isNullAndUnDef('')).toBe(false)
|
||||||
|
expect(isNullAndUnDef({})).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('isNullOrUnDef()', () => {
|
||||||
|
it('should return true for null or undefined', () => {
|
||||||
|
expect(isNullOrUnDef(null)).toBe(true)
|
||||||
|
expect(isNullOrUnDef(undefined)).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return false for defined values', () => {
|
||||||
|
expect(isNullOrUnDef(0)).toBe(false)
|
||||||
|
expect(isNullOrUnDef('')).toBe(false)
|
||||||
|
expect(isNullOrUnDef(false)).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('isNumber()', () => {
|
||||||
|
it('should return true for number type', () => {
|
||||||
|
expect(isNumber(123)).toBe(true)
|
||||||
|
expect(isNumber(0)).toBe(true)
|
||||||
|
expect(isNumber(-123)).toBe(true)
|
||||||
|
expect(isNumber(3.14)).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return false for non-number values', () => {
|
||||||
|
expect(isNumber('123')).toBe(false)
|
||||||
|
expect(isNumber(true)).toBe(false)
|
||||||
|
expect(isNumber(null)).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('isString()', () => {
|
||||||
|
it('should return true for string type', () => {
|
||||||
|
expect(isString('test')).toBe(true)
|
||||||
|
expect(isString('')).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return false for non-string values', () => {
|
||||||
|
expect(isString(123)).toBe(false)
|
||||||
|
expect(isString(true)).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('isFunction()', () => {
|
||||||
|
it('should return true for functions', () => {
|
||||||
|
expect(isFunction(function () {})).toBe(true)
|
||||||
|
expect(isFunction(() => {})).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return false for non-functions', () => {
|
||||||
|
expect(isFunction('function')).toBe(false)
|
||||||
|
expect(isFunction({})).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('isBoolean()', () => {
|
||||||
|
it('should return true for boolean type', () => {
|
||||||
|
expect(isBoolean(true)).toBe(true)
|
||||||
|
expect(isBoolean(false)).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return false for non-boolean values', () => {
|
||||||
|
expect(isBoolean(1)).toBe(false)
|
||||||
|
expect(isBoolean('true')).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('isRegExp()', () => {
|
||||||
|
it('should return true for RegExp', () => {
|
||||||
|
expect(isRegExp(/test/)).toBe(true)
|
||||||
|
expect(isRegExp(new RegExp('test'))).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return false for non-RegExp', () => {
|
||||||
|
expect(isRegExp('test')).toBe(false)
|
||||||
|
expect(isRegExp({})).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('isArray()', () => {
|
||||||
|
it('should return true for arrays', () => {
|
||||||
|
expect(isArray([])).toBe(true)
|
||||||
|
expect(isArray([1, 2, 3])).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return false for non-arrays', () => {
|
||||||
|
expect(isArray({})).toBe(false)
|
||||||
|
expect(isArray('array')).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('isElement()', () => {
|
||||||
|
it('should return true for DOM elements', () => {
|
||||||
|
const div = document.createElement('div')
|
||||||
|
expect(isElement(div)).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return false for non-elements', () => {
|
||||||
|
expect(isElement({})).toBe(false)
|
||||||
|
expect(isElement('div')).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('isMap()', () => {
|
||||||
|
it('should return true for Map', () => {
|
||||||
|
expect(isMap(new Map())).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return false for non-Map', () => {
|
||||||
|
expect(isMap({})).toBe(false)
|
||||||
|
expect(isMap([])).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('isUrl()', () => {
|
||||||
|
it('should return true for valid URLs', () => {
|
||||||
|
expect(isUrl('https://example.com')).toBe(true)
|
||||||
|
expect(isUrl('http://example.com/path')).toBe(true)
|
||||||
|
expect(isUrl('https://example.com/path?query=value')).toBe(true)
|
||||||
|
expect(isUrl('https://example.com/path#hash')).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return false for invalid URLs', () => {
|
||||||
|
expect(isUrl('not-a-url')).toBe(false)
|
||||||
|
expect(isUrl('')).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('isImgPath()', () => {
|
||||||
|
it('should return true for image paths', () => {
|
||||||
|
expect(isImgPath('https://example.com/image.png')).toBe(true)
|
||||||
|
expect(isImgPath('http://example.com/photo.jpg')).toBe(true)
|
||||||
|
expect(isImgPath('/path/to/image.gif')).toBe(true)
|
||||||
|
expect(isImgPath('data:image/png;base64,abc123')).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return false for non-image paths', () => {
|
||||||
|
expect(isImgPath('https://example.com/page.html')).toBe(false)
|
||||||
|
expect(isImgPath('/path/to/file.pdf')).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
describe('isEmptyVal()', () => {
|
||||||
|
it('should return true for empty values', () => {
|
||||||
|
expect(isEmptyVal('')).toBe(true)
|
||||||
|
expect(isEmptyVal(null)).toBe(true)
|
||||||
|
expect(isEmptyVal(undefined)).toBe(true)
|
||||||
|
})
|
||||||
|
|
||||||
|
it('should return false for non-empty values', () => {
|
||||||
|
expect(isEmptyVal('test')).toBe(false)
|
||||||
|
expect(isEmptyVal(0)).toBe(false)
|
||||||
|
expect(isEmptyVal(false)).toBe(false)
|
||||||
|
expect(isEmptyVal({})).toBe(false)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
@ -266,5 +266,12 @@ export enum DICT_TYPE {
|
|||||||
PRISON_CELL_STATUS = 'prison_cell_status', // 监室状态
|
PRISON_CELL_STATUS = 'prison_cell_status', // 监室状态
|
||||||
PRISON_RECORD_PASS_STATUS = 'prison_record_pass_status', // 问卷答题是否及格
|
PRISON_RECORD_PASS_STATUS = 'prison_record_pass_status', // 问卷答题是否及格
|
||||||
PRISON_RECORD_STATUS = 'prison_record_status', // 问卷答题记录状态
|
PRISON_RECORD_STATUS = 'prison_record_status', // 问卷答题记录状态
|
||||||
PRISON_QUESTION_AUTO_FILL_SOURCE = 'prison_question_auto_fill_source' // 问卷问题自动填充来源
|
PRISON_QUESTION_AUTO_FILL_SOURCE = 'prison_question_auto_fill_source', // 问卷问题自动填充来源
|
||||||
|
PRISON_AREA_LEVEL = 'prison_area_level', // 监区级别:1-监区(大队) 2-分监区(中队)
|
||||||
|
PRISON_RELEASE_TYPE = 'prison_release_type', // 释放类型:1-刑满释放 2-假释 3-暂予监外执行 4-减刑 5-法院裁定释放 6-死亡 7-其他
|
||||||
|
PRISON_AREA_CHANGE_TYPE = 'prison_area_change_type', // 变动类型:1-入监分配 2-调监 3-出监 4-移交转入 5-移交转出
|
||||||
|
PRISON_SCORE_CATEGORY = 'prison_score_category', // 考核类别:1-劳动改造 2-教育改造 3-日常行为 4-卫生纪律 5-加分项 6-扣分项
|
||||||
|
PRISON_SCORE_TYPE = 'prison_score_type', // 考核类型:1-加分 2-扣分
|
||||||
|
PRISON_RELEASE_STATUS = 'prison_release_status', // 释放状态:1-待释放 2-已释放 3-已取消
|
||||||
|
PRISON_CERTIFICATE_TYPE = 'prison_certificate_type' // 证件类型:1-身份证 2-户口簿 3-其他
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<Dialog :title="dialogTitle" v-model="dialogVisible">
|
<Dialog :title="dialogTitle" v-model="dialogVisible" width="600px">
|
||||||
<el-form
|
<el-form
|
||||||
ref="formRef"
|
ref="formRef"
|
||||||
:model="formData"
|
:model="formData"
|
||||||
@ -13,6 +13,32 @@
|
|||||||
<el-form-item label="监区编码" prop="code">
|
<el-form-item label="监区编码" prop="code">
|
||||||
<el-input v-model="formData.code" placeholder="请输入监区编码" />
|
<el-input v-model="formData.code" placeholder="请输入监区编码" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="级别" prop="level">
|
||||||
|
<el-select v-model="formData.level" placeholder="请选择级别" @change="handleLevelChange">
|
||||||
|
<el-option
|
||||||
|
v-for="dict in getIntDictOptions(DICT_TYPE.PRISON_AREA_LEVEL)"
|
||||||
|
:key="dict.value"
|
||||||
|
:label="dict.label"
|
||||||
|
:value="dict.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="父级监区" prop="parentId">
|
||||||
|
<el-select v-model="formData.parentId" placeholder="请选择父级监区" clearable :disabled="formData.level === 1">
|
||||||
|
<el-option
|
||||||
|
v-for="area in parentAreaOptions"
|
||||||
|
:key="area.id"
|
||||||
|
:label="area.name"
|
||||||
|
:value="area.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
<el-form-item label="监区类型" prop="type">
|
<el-form-item label="监区类型" prop="type">
|
||||||
<el-select v-model="formData.type" placeholder="请选择监区类型">
|
<el-select v-model="formData.type" placeholder="请选择监区类型">
|
||||||
<el-option
|
<el-option
|
||||||
@ -23,15 +49,18 @@
|
|||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="容纳人数" prop="capacity">
|
<el-row :gutter="20">
|
||||||
<el-input v-model="formData.capacity" placeholder="请输入容纳人数" />
|
<el-col :span="12">
|
||||||
</el-form-item>
|
<el-form-item label="容纳人数" prop="capacity">
|
||||||
<el-form-item label="当前人数" prop="currentCount">
|
<el-input-number v-model="formData.capacity" :min="0" placeholder="请输入容纳人数" controls-position="right" />
|
||||||
<el-input v-model="formData.currentCount" placeholder="请输入当前人数" />
|
</el-form-item>
|
||||||
</el-form-item>
|
</el-col>
|
||||||
<el-form-item label="排序" prop="sort">
|
<el-col :span="12">
|
||||||
<el-input v-model="formData.sort" placeholder="请输入排序" />
|
<el-form-item label="排序" prop="sort">
|
||||||
</el-form-item>
|
<el-input-number v-model="formData.sort" :min="0" placeholder="请输入排序" controls-position="right" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
<el-form-item label="状态" prop="status">
|
<el-form-item label="状态" prop="status">
|
||||||
<el-radio-group v-model="formData.status">
|
<el-radio-group v-model="formData.status">
|
||||||
<el-radio
|
<el-radio
|
||||||
@ -42,7 +71,7 @@
|
|||||||
</el-radio-group>
|
</el-radio-group>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="备注" prop="remark">
|
<el-form-item label="备注" prop="remark">
|
||||||
<el-input v-model="formData.remark" placeholder="请输入备注" />
|
<el-input v-model="formData.remark" type="textarea" placeholder="请输入备注" :rows="3" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
@ -51,9 +80,10 @@
|
|||||||
</template>
|
</template>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||||
import { AreaApi, Area } from '@/api/prison/area'
|
import { AreaApi, AreaSaveReqVO } from '@/api/prison/area'
|
||||||
|
|
||||||
/** 监区信息 表单 */
|
/** 监区信息 表单 */
|
||||||
defineOptions({ name: 'AreaForm' })
|
defineOptions({ name: 'AreaForm' })
|
||||||
@ -65,35 +95,84 @@ const dialogVisible = ref(false) // 弹窗的是否展示
|
|||||||
const dialogTitle = ref('') // 弹窗的标题
|
const dialogTitle = ref('') // 弹窗的标题
|
||||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||||
|
|
||||||
const formData = ref({
|
const formData = ref({
|
||||||
id: undefined,
|
id: undefined,
|
||||||
name: undefined,
|
name: undefined,
|
||||||
code: undefined,
|
code: undefined,
|
||||||
type: undefined,
|
parentId: undefined as number | undefined,
|
||||||
capacity: undefined,
|
level: 1,
|
||||||
currentCount: undefined,
|
type: 1,
|
||||||
sort: undefined,
|
capacity: 0,
|
||||||
status: undefined,
|
sort: 0,
|
||||||
|
status: 1,
|
||||||
remark: undefined
|
remark: undefined
|
||||||
})
|
})
|
||||||
|
|
||||||
const formRules = reactive({
|
const formRules = reactive({
|
||||||
name: [{ required: true, message: '监区名称不能为空', trigger: 'blur' }],
|
name: [{ required: true, message: '监区名称不能为空', trigger: 'blur' }],
|
||||||
code: [{ required: true, message: '监区编码不能为空', trigger: 'blur' }],
|
code: [{ required: true, message: '监区编码不能为空', trigger: 'blur' }],
|
||||||
|
level: [{ required: true, message: '级别不能为空', trigger: 'blur' }],
|
||||||
|
type: [{ required: true, message: '监区类型不能为空', trigger: 'blur' }],
|
||||||
status: [{ required: true, message: '状态不能为空', trigger: 'blur' }]
|
status: [{ required: true, message: '状态不能为空', trigger: 'blur' }]
|
||||||
})
|
})
|
||||||
|
|
||||||
const formRef = ref() // 表单 Ref
|
const formRef = ref() // 表单 Ref
|
||||||
|
const parentAreaOptions = ref<{ id: number; name: string }[]>([]) // 父级监区选项
|
||||||
|
|
||||||
|
/** 加载父级监区列表 */
|
||||||
|
const loadParentAreas = async () => {
|
||||||
|
// 加载级别为1的监区作为父级选项
|
||||||
|
try {
|
||||||
|
const data = await AreaApi.getParentAreas(1)
|
||||||
|
parentAreaOptions.value = data
|
||||||
|
} catch {
|
||||||
|
parentAreaOptions.value = []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 级别变化时处理 */
|
||||||
|
const handleLevelChange = (level: number) => {
|
||||||
|
// 选择监区级别时,不能选择自己为父级
|
||||||
|
if (formData.value.id && level === 2) {
|
||||||
|
parentAreaOptions.value = parentAreaOptions.value.filter(
|
||||||
|
(area) => area.id !== formData.value.id
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// 重置父级选择
|
||||||
|
if (level === 1) {
|
||||||
|
formData.value.parentId = undefined
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** 打开弹窗 */
|
/** 打开弹窗 */
|
||||||
const open = async (type: string, id?: number) => {
|
const open = async (type: string, id?: number) => {
|
||||||
dialogVisible.value = true
|
dialogVisible.value = true
|
||||||
dialogTitle.value = t('action.' + type)
|
dialogTitle.value = t('action.' + type)
|
||||||
formType.value = type
|
formType.value = type
|
||||||
|
|
||||||
|
// 加载父级监区选项
|
||||||
|
await loadParentAreas()
|
||||||
|
|
||||||
resetForm()
|
resetForm()
|
||||||
|
|
||||||
// 修改时,设置数据
|
// 修改时,设置数据
|
||||||
if (id) {
|
if (id) {
|
||||||
formLoading.value = true
|
formLoading.value = true
|
||||||
try {
|
try {
|
||||||
formData.value = await AreaApi.getArea(id)
|
const data = await AreaApi.getArea(id)
|
||||||
|
formData.value = {
|
||||||
|
id: data.id,
|
||||||
|
name: data.name,
|
||||||
|
code: data.code,
|
||||||
|
parentId: data.parentId || undefined,
|
||||||
|
level: data.level || 1,
|
||||||
|
type: data.type,
|
||||||
|
capacity: data.capacity,
|
||||||
|
sort: data.sort,
|
||||||
|
status: data.status,
|
||||||
|
remark: data.remark
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
formLoading.value = false
|
formLoading.value = false
|
||||||
}
|
}
|
||||||
@ -109,7 +188,12 @@ const submitForm = async () => {
|
|||||||
// 提交请求
|
// 提交请求
|
||||||
formLoading.value = true
|
formLoading.value = true
|
||||||
try {
|
try {
|
||||||
const data = formData.value as unknown as Area
|
const data = formData.value as unknown as AreaSaveReqVO
|
||||||
|
// 监区级别不能选择自己为父级
|
||||||
|
if (data.level === 2 && data.parentId === data.id) {
|
||||||
|
message.error('父级监区不能选择自己')
|
||||||
|
return
|
||||||
|
}
|
||||||
if (formType.value === 'create') {
|
if (formType.value === 'create') {
|
||||||
await AreaApi.createArea(data)
|
await AreaApi.createArea(data)
|
||||||
message.success(t('common.createSuccess'))
|
message.success(t('common.createSuccess'))
|
||||||
@ -131,13 +215,14 @@ const resetForm = () => {
|
|||||||
id: undefined,
|
id: undefined,
|
||||||
name: undefined,
|
name: undefined,
|
||||||
code: undefined,
|
code: undefined,
|
||||||
type: undefined,
|
parentId: undefined,
|
||||||
capacity: undefined,
|
level: 1,
|
||||||
currentCount: undefined,
|
type: 1,
|
||||||
sort: undefined,
|
capacity: 0,
|
||||||
status: undefined,
|
sort: 0,
|
||||||
|
status: 1,
|
||||||
remark: undefined
|
remark: undefined
|
||||||
}
|
}
|
||||||
formRef.value?.resetFields()
|
formRef.value?.resetFields()
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@ -32,6 +32,21 @@
|
|||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="级别" prop="level">
|
||||||
|
<el-select
|
||||||
|
v-model="queryParams.level"
|
||||||
|
placeholder="请选择"
|
||||||
|
clearable
|
||||||
|
class="!w-100px"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="dict in levelOptions"
|
||||||
|
:key="dict.value"
|
||||||
|
:label="dict.label"
|
||||||
|
:value="dict.value"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
<el-form-item label="状态" prop="status">
|
<el-form-item label="状态" prop="status">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="queryParams.status"
|
v-model="queryParams.status"
|
||||||
@ -56,7 +71,7 @@
|
|||||||
@click="openForm('create')"
|
@click="openForm('create')"
|
||||||
v-hasPermi="['prison:area:create']"
|
v-hasPermi="['prison:area:create']"
|
||||||
>
|
>
|
||||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
<Icon icon="ep:plus" class="mr-5px" /> 新增监区
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
type="success"
|
type="success"
|
||||||
@ -67,86 +82,144 @@
|
|||||||
>
|
>
|
||||||
<Icon icon="ep:download" class="mr-5px" /> 导出
|
<Icon icon="ep:download" class="mr-5px" /> 导出
|
||||||
</el-button>
|
</el-button>
|
||||||
<el-button
|
|
||||||
type="danger"
|
|
||||||
plain
|
|
||||||
:disabled="checkedIds.length === 0"
|
|
||||||
@click="handleDeleteBatch"
|
|
||||||
v-hasPermi="['prison:area:delete']"
|
|
||||||
>
|
|
||||||
<Icon icon="ep:delete" class="mr-5px" /> 批量删除
|
|
||||||
</el-button>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
</ContentWrap>
|
</ContentWrap>
|
||||||
|
|
||||||
<!-- 列表 -->
|
<!-- 树形监区列表 -->
|
||||||
<ContentWrap>
|
<ContentWrap>
|
||||||
<el-table
|
<el-row :gutter="20">
|
||||||
v-loading="loading"
|
<!-- 左侧树形结构 -->
|
||||||
:data="list"
|
<el-col :span="showDetail ? 14 : 24">
|
||||||
@selection-change="handleRowCheckboxChange"
|
<el-card shadow="never" class="area-tree-card">
|
||||||
>
|
<template #header>
|
||||||
<el-table-column type="selection" width="55" />
|
<div class="card-header">
|
||||||
<el-table-column label="监区ID" align="center" prop="id" width="80" />
|
<span><Icon icon="ep:grid" class="mr-5px" /> 监区树形结构</span>
|
||||||
<el-table-column label="监区名称" align="center" prop="name" width="120" />
|
<el-button
|
||||||
<el-table-column label="监区编码" align="center" prop="code" width="120" />
|
type="primary"
|
||||||
<el-table-column label="监区类型" align="center" prop="type" width="100">
|
link
|
||||||
<template #default="scope">
|
@click="handleRefreshTree"
|
||||||
<dict-tag :type="DICT_TYPE.PRISON_AREA_TYPE" :value="scope.row.type" />
|
>
|
||||||
</template>
|
<Icon icon="ep:refresh" class="mr-5px" /> 刷新
|
||||||
</el-table-column>
|
</el-button>
|
||||||
<el-table-column label="容纳人数" align="center" prop="capacity" width="90" />
|
</div>
|
||||||
<el-table-column label="当前人数" align="center" prop="currentCount" width="90" />
|
</template>
|
||||||
<el-table-column label="排序" align="center" prop="sort" width="70" />
|
|
||||||
<el-table-column label="状态" align="center" prop="status" width="90">
|
<el-tree
|
||||||
<template #default="scope">
|
v-loading="treeLoading"
|
||||||
<dict-tag :type="DICT_TYPE.PRISON_CELL_STATUS" :value="scope.row.status" />
|
:data="treeData"
|
||||||
</template>
|
:props="treeProps"
|
||||||
</el-table-column>
|
node-key="id"
|
||||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
default-expand-all
|
||||||
<template #default="scope">
|
:expand-on-click-node="false"
|
||||||
{{ formatDate(scope.row.createTime) }}
|
@node-click="handleNodeClick"
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column label="操作" align="center" width="120">
|
|
||||||
<template #default="scope">
|
|
||||||
<el-button
|
|
||||||
type="primary"
|
|
||||||
link
|
|
||||||
@click="openForm('update', scope.row.id)"
|
|
||||||
v-hasPermi="['prison:area:update']"
|
|
||||||
>
|
>
|
||||||
修改
|
<template #default="{ data }">
|
||||||
</el-button>
|
<span class="area-tree-node">
|
||||||
<el-button
|
<el-icon :class="getLevelIconClass(data.level)">
|
||||||
type="danger"
|
<component :is="getLevelIcon(data.level)" />
|
||||||
link
|
</el-icon>
|
||||||
@click="handleDelete(scope.row.id)"
|
<span class="node-name">{{ data.name }}</span>
|
||||||
v-hasPermi="['prison:area:delete']"
|
<el-tag size="small" :type="data.currentCount >= data.capacity ? 'danger' : 'success'">
|
||||||
>
|
{{ data.currentCount }}/{{ data.capacity }}
|
||||||
删除
|
</el-tag>
|
||||||
</el-button>
|
<el-tag
|
||||||
</template>
|
v-if="data.level === 1"
|
||||||
</el-table-column>
|
size="small"
|
||||||
</el-table>
|
type="info"
|
||||||
<Pagination
|
>
|
||||||
:total="total"
|
{{ getTypeLabel(data.type) }}
|
||||||
v-model:page="queryParams.pageNo"
|
</el-tag>
|
||||||
v-model:limit="queryParams.pageSize"
|
</span>
|
||||||
@pagination="getList"
|
</template>
|
||||||
/>
|
</el-tree>
|
||||||
|
|
||||||
|
<el-empty v-if="!treeLoading && treeData.length === 0" description="暂无监区数据" />
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<!-- 右侧详情面板 -->
|
||||||
|
<el-col v-if="showDetail" :span="10">
|
||||||
|
<el-card shadow="never">
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span><Icon icon="ep:info-filled" class="mr-5px" /> 监区详情</span>
|
||||||
|
<el-button type="primary" link @click="showDetail = false">
|
||||||
|
<Icon icon="ep:close" /> 关闭
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<el-descriptions :column="1" border v-if="currentNode">
|
||||||
|
<el-descriptions-item label="监区名称">
|
||||||
|
{{ currentNode.name }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="监区编码">
|
||||||
|
{{ currentNode.code }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="级别">
|
||||||
|
<dict-tag :type="DICT_TYPE.PRISON_AREA_LEVEL" :value="currentNode.level" />
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="监区类型">
|
||||||
|
<dict-tag :type="DICT_TYPE.PRISON_AREA_TYPE" :value="currentNode.type" />
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="容纳人数">
|
||||||
|
{{ currentNode.capacity }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="当前人数">
|
||||||
|
<el-tag :type="currentNode.currentCount >= currentNode.capacity ? 'danger' : 'success'">
|
||||||
|
{{ currentNode.currentCount }}
|
||||||
|
</el-tag>
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="状态">
|
||||||
|
<dict-tag :type="DICT_TYPE.PRISON_CELL_STATUS" :value="currentNode.status" />
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item v-if="currentNode.parentId && currentNode.parentId > 0" label="父级监区">
|
||||||
|
{{ getParentName(currentNode.parentId) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item v-if="currentNode.remark" label="备注">
|
||||||
|
{{ currentNode.remark }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
|
||||||
|
<div class="detail-actions" v-if="currentNode">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
@click="openForm('update', currentNode.id)"
|
||||||
|
v-hasPermi="['prison:area:update']"
|
||||||
|
>
|
||||||
|
<Icon icon="ep:edit" class="mr-5px" /> 修改
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
type="warning"
|
||||||
|
@click="handleSyncCount(currentNode.id)"
|
||||||
|
v-hasPermi="['prison:area:update']"
|
||||||
|
>
|
||||||
|
<Icon icon="ep:refresh" class="mr-5px" /> 同步人数
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
type="danger"
|
||||||
|
@click="handleDelete(currentNode.id)"
|
||||||
|
v-hasPermi="['prison:area:delete']"
|
||||||
|
>
|
||||||
|
<Icon icon="ep:delete" class="mr-5px" /> 删除
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
</ContentWrap>
|
</ContentWrap>
|
||||||
|
|
||||||
<!-- 表单弹窗:添加/修改 -->
|
<!-- 表单弹窗:添加/修改 -->
|
||||||
<AreaForm ref="formRef" @success="getList" />
|
<AreaForm ref="formRef" @success="handleRefreshTree" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||||
import download from '@/utils/download'
|
import download from '@/utils/download'
|
||||||
import { AreaApi, Area } from '@/api/prison/area'
|
import { AreaApi, AreaNode } from '@/api/prison/area'
|
||||||
import AreaForm from './AreaForm.vue'
|
import AreaForm from './AreaForm.vue'
|
||||||
|
import { OfficeBuilding, Files } from '@element-plus/icons-vue'
|
||||||
|
|
||||||
defineOptions({ name: 'Area' })
|
defineOptions({ name: 'Area' })
|
||||||
|
|
||||||
@ -161,21 +234,76 @@ const queryParams = reactive({
|
|||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
name: undefined,
|
name: undefined,
|
||||||
type: undefined,
|
type: undefined,
|
||||||
|
level: undefined,
|
||||||
status: undefined
|
status: undefined
|
||||||
})
|
})
|
||||||
const queryFormRef = ref()
|
const queryFormRef = ref()
|
||||||
const exportLoading = ref(false)
|
const exportLoading = ref(false)
|
||||||
|
|
||||||
|
// 树形数据
|
||||||
|
const treeLoading = ref(false)
|
||||||
|
const treeData = ref<AreaNode[]>([])
|
||||||
|
const currentNode = ref<AreaNode | null>(null)
|
||||||
|
const showDetail = ref(false)
|
||||||
|
|
||||||
// 使用字典获取选项
|
// 使用字典获取选项
|
||||||
const typeOptions = getIntDictOptions(DICT_TYPE.PRISON_AREA_TYPE)
|
const typeOptions = getIntDictOptions(DICT_TYPE.PRISON_AREA_TYPE)
|
||||||
|
const levelOptions = getIntDictOptions(DICT_TYPE.PRISON_AREA_LEVEL)
|
||||||
const statusOptions = getIntDictOptions(DICT_TYPE.PRISON_CELL_STATUS)
|
const statusOptions = getIntDictOptions(DICT_TYPE.PRISON_CELL_STATUS)
|
||||||
|
|
||||||
|
// 树形组件配置
|
||||||
|
const treeProps = {
|
||||||
|
label: 'name',
|
||||||
|
children: 'children'
|
||||||
|
}
|
||||||
|
|
||||||
/** 日期格式化 */
|
/** 日期格式化 */
|
||||||
const formatDate = (date: string | Date | undefined) => {
|
const formatDate = (date: string | Date | undefined) => {
|
||||||
if (!date) return '-'
|
if (!date) return '-'
|
||||||
return new Date(date).toLocaleString('zh-CN')
|
return new Date(date).toLocaleString('zh-CN')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 获取级别图标 */
|
||||||
|
const getLevelIcon = (level: number) => {
|
||||||
|
return level === 1 ? OfficeBuilding : Files
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取级别图标样式 */
|
||||||
|
const getLevelIconClass = (level: number) => {
|
||||||
|
return level === 1 ? 'area-level-icon area-level-main' : 'area-level-icon area-level-sub'
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取类型标签 */
|
||||||
|
const getTypeLabel = (type: number) => {
|
||||||
|
const dict = typeOptions.find((d: any) => d.value === type)
|
||||||
|
return dict ? dict.label : '-'
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取父级监区名称 */
|
||||||
|
const getParentName = (parentId: number) => {
|
||||||
|
const findParent = (nodes: AreaNode[]): string => {
|
||||||
|
for (const node of nodes) {
|
||||||
|
if (node.id === parentId) return node.name
|
||||||
|
if (node.children && node.children.length > 0) {
|
||||||
|
const found = findParent(node.children)
|
||||||
|
if (found) return found
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
return findParent(treeData.value) || '-'
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取树形数据 */
|
||||||
|
const getTreeList = async () => {
|
||||||
|
treeLoading.value = true
|
||||||
|
try {
|
||||||
|
treeData.value = await AreaApi.getAreaTree()
|
||||||
|
} finally {
|
||||||
|
treeLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** 查询列表 */
|
/** 查询列表 */
|
||||||
const getList = async () => {
|
const getList = async () => {
|
||||||
loading.value = true
|
loading.value = true
|
||||||
@ -206,29 +334,38 @@ const openForm = (type: string, id?: number) => {
|
|||||||
formRef.value.open(type, id)
|
formRef.value.open(type, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 树节点点击 */
|
||||||
|
const handleNodeClick = (data: AreaNode) => {
|
||||||
|
currentNode.value = data
|
||||||
|
showDetail.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 刷新树形数据 */
|
||||||
|
const handleRefreshTree = () => {
|
||||||
|
getTreeList()
|
||||||
|
showDetail.value = false
|
||||||
|
currentNode.value = null
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 同步人数 */
|
||||||
|
const handleSyncCount = async (id: number) => {
|
||||||
|
try {
|
||||||
|
await message.confirm('确认要同步该监区的人数吗?')
|
||||||
|
await AreaApi.syncCurrentCount(id)
|
||||||
|
message.success('人数同步成功')
|
||||||
|
await getTreeList()
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
/** 删除按钮操作 */
|
/** 删除按钮操作 */
|
||||||
const handleDelete = async (id: number) => {
|
const handleDelete = async (id: number) => {
|
||||||
try {
|
try {
|
||||||
await message.delConfirm()
|
await message.delConfirm()
|
||||||
await AreaApi.deleteArea(id)
|
await AreaApi.deleteArea(id)
|
||||||
message.success(t('common.delSuccess'))
|
message.success(t('common.delSuccess'))
|
||||||
await getList()
|
await getTreeList()
|
||||||
} catch {}
|
showDetail.value = false
|
||||||
}
|
currentNode.value = null
|
||||||
|
|
||||||
/** 批量删除按钮操作 */
|
|
||||||
const checkedIds = ref<number[]>([])
|
|
||||||
const handleRowCheckboxChange = (rows: Area[]) => {
|
|
||||||
checkedIds.value = rows.map((row) => row.id!)
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleDeleteBatch = async () => {
|
|
||||||
try {
|
|
||||||
await message.delConfirm()
|
|
||||||
await AreaApi.deleteAreaList(checkedIds.value)
|
|
||||||
checkedIds.value = []
|
|
||||||
message.success(t('common.delSuccess'))
|
|
||||||
await getList()
|
|
||||||
} catch {}
|
} catch {}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -247,6 +384,55 @@ const handleExport = async () => {
|
|||||||
|
|
||||||
/** 初始化 */
|
/** 初始化 */
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
getTreeList()
|
||||||
getList()
|
getList()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.area-tree-card {
|
||||||
|
:deep(.el-card__body) {
|
||||||
|
max-height: 600px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.area-tree-node {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
flex: 1;
|
||||||
|
padding-right: 8px;
|
||||||
|
|
||||||
|
.area-level-icon {
|
||||||
|
font-size: 16px;
|
||||||
|
|
||||||
|
&.area-level-main {
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.area-level-sub {
|
||||||
|
color: var(--el-color-success);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.node-name {
|
||||||
|
flex: 1;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-actions {
|
||||||
|
margin-top: 20px;
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|||||||
239
src/views/prison/dashboard/components/ChinaMap.vue
Normal file
239
src/views/prison/dashboard/components/ChinaMap.vue
Normal file
@ -0,0 +1,239 @@
|
|||||||
|
<template>
|
||||||
|
<div class="china-map-container">
|
||||||
|
<div ref="chartRef" class="chart-container"></div>
|
||||||
|
<el-empty v-if="!loading && data.length === 0" description="暂无省份分布数据" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, onMounted, watch, onUnmounted } from 'vue'
|
||||||
|
import type { EChartsOption } from 'echarts'
|
||||||
|
import echarts from '@/plugins/echarts'
|
||||||
|
import { registerChinaMap } from '@/plugins/echarts'
|
||||||
|
import { ProvinceChartVO } from '@/api/prison/dashboard'
|
||||||
|
import { debounce } from 'lodash-es'
|
||||||
|
|
||||||
|
defineOptions({ name: 'ChinaMap' })
|
||||||
|
|
||||||
|
interface Props {
|
||||||
|
data: ProvinceChartVO[]
|
||||||
|
loading?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
const props = withDefaults(defineProps<Props>(), {
|
||||||
|
loading: false
|
||||||
|
})
|
||||||
|
|
||||||
|
const chartRef = ref<HTMLDivElement>()
|
||||||
|
let chartInstance: echarts.ECharts | null = null
|
||||||
|
|
||||||
|
// 省份编码到名称的映射(支持 2 位和 6 位 adcode)
|
||||||
|
const provinceCodeMap: Record<string, string> = {
|
||||||
|
// 2位编码
|
||||||
|
'11': '北京市',
|
||||||
|
'12': '天津市',
|
||||||
|
'13': '河北省',
|
||||||
|
'14': '山西省',
|
||||||
|
'15': '内蒙古自治区',
|
||||||
|
'21': '辽宁省',
|
||||||
|
'22': '吉林省',
|
||||||
|
'23': '黑龙江省',
|
||||||
|
'31': '上海市',
|
||||||
|
'32': '江苏省',
|
||||||
|
'33': '浙江省',
|
||||||
|
'34': '安徽省',
|
||||||
|
'35': '福建省',
|
||||||
|
'36': '江西省',
|
||||||
|
'37': '山东省',
|
||||||
|
'41': '河南省',
|
||||||
|
'42': '湖北省',
|
||||||
|
'43': '湖南省',
|
||||||
|
'44': '广东省',
|
||||||
|
'45': '广西壮族自治区',
|
||||||
|
'46': '海南省',
|
||||||
|
'50': '重庆市',
|
||||||
|
'51': '四川省',
|
||||||
|
'52': '贵州省',
|
||||||
|
'53': '云南省',
|
||||||
|
'54': '西藏自治区',
|
||||||
|
'61': '陕西省',
|
||||||
|
'62': '甘肃省',
|
||||||
|
'63': '青海省',
|
||||||
|
'64': '宁夏回族自治区',
|
||||||
|
'65': '新疆维吾尔自治区',
|
||||||
|
'71': '台湾省',
|
||||||
|
'81': '香港特别行政区',
|
||||||
|
'82': '澳门特别行政区',
|
||||||
|
// 6位 adcode
|
||||||
|
'110000': '北京市',
|
||||||
|
'120000': '天津市',
|
||||||
|
'130000': '河北省',
|
||||||
|
'140000': '山西省',
|
||||||
|
'150000': '内蒙古自治区',
|
||||||
|
'210000': '辽宁省',
|
||||||
|
'220000': '吉林省',
|
||||||
|
'230000': '黑龙江省',
|
||||||
|
'310000': '上海市',
|
||||||
|
'320000': '江苏省',
|
||||||
|
'330000': '浙江省',
|
||||||
|
'340000': '安徽省',
|
||||||
|
'350000': '福建省',
|
||||||
|
'360000': '江西省',
|
||||||
|
'370000': '山东省',
|
||||||
|
'410000': '河南省',
|
||||||
|
'420000': '湖北省',
|
||||||
|
'430000': '湖南省',
|
||||||
|
'440000': '广东省',
|
||||||
|
'450000': '广西壮族自治区',
|
||||||
|
'460000': '海南省',
|
||||||
|
'500000': '重庆市',
|
||||||
|
'510000': '四川省',
|
||||||
|
'520000': '贵州省',
|
||||||
|
'530000': '云南省',
|
||||||
|
'540000': '西藏自治区',
|
||||||
|
'610000': '陕西省',
|
||||||
|
'620000': '甘肃省',
|
||||||
|
'630000': '青海省',
|
||||||
|
'640000': '宁夏回族自治区',
|
||||||
|
'650000': '新疆维吾尔自治区',
|
||||||
|
'710000': '台湾省',
|
||||||
|
'810000': '香港特别行政区',
|
||||||
|
'820000': '澳门特别行政区'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取省份名称
|
||||||
|
const getProvinceName = (code: string | number): string => {
|
||||||
|
const codeStr = String(code)
|
||||||
|
// 优先尝试完整匹配(6位 adcode)
|
||||||
|
if (provinceCodeMap[codeStr]) {
|
||||||
|
return provinceCodeMap[codeStr]
|
||||||
|
}
|
||||||
|
// 如果是2位编码,尝试匹配
|
||||||
|
const shortCode = codeStr.padStart(2, '0')
|
||||||
|
return provinceCodeMap[shortCode] || codeStr
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式化数据为 ECharts 地图数据格式
|
||||||
|
const formatMapData = (data: ProvinceChartVO[]) => {
|
||||||
|
return data.map((item) => ({
|
||||||
|
name: getProvinceName(item.provinceCode),
|
||||||
|
value: item.count
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取最大值
|
||||||
|
const getMaxValue = (data: ProvinceChartVO[]): number => {
|
||||||
|
if (data.length === 0) return 100
|
||||||
|
return Math.max(...data.map((item) => item.count), 100)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新图表
|
||||||
|
const updateChart = () => {
|
||||||
|
if (!chartInstance || !chartRef.value) return
|
||||||
|
|
||||||
|
const mapData = formatMapData(props.data)
|
||||||
|
const maxValue = getMaxValue(props.data)
|
||||||
|
|
||||||
|
const option: EChartsOption = {
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'item',
|
||||||
|
formatter: (params: any) => {
|
||||||
|
if (params.value === undefined) {
|
||||||
|
return `${params.name}: 暂无数据`
|
||||||
|
}
|
||||||
|
return `${params.name}: ${params.value}人`
|
||||||
|
}
|
||||||
|
},
|
||||||
|
visualMap: {
|
||||||
|
min: 0,
|
||||||
|
max: maxValue,
|
||||||
|
left: 'left',
|
||||||
|
top: 'bottom',
|
||||||
|
text: ['高', '低'],
|
||||||
|
calculable: true,
|
||||||
|
inRange: {
|
||||||
|
color: ['#e0f3f8', '#91c7ae', '#c23531']
|
||||||
|
}
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: '籍贯分布',
|
||||||
|
type: 'map',
|
||||||
|
map: 'china',
|
||||||
|
roam: true,
|
||||||
|
zoom: 1.2,
|
||||||
|
scaleLimit: {
|
||||||
|
min: 0.5,
|
||||||
|
max: 3
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
fontSize: 10,
|
||||||
|
color: '#333'
|
||||||
|
},
|
||||||
|
emphasis: {
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
fontSize: 12,
|
||||||
|
fontWeight: 'bold'
|
||||||
|
},
|
||||||
|
itemStyle: {
|
||||||
|
areaColor: '#ffd700'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data: mapData
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
chartInstance.setOption(option)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 初始化图表
|
||||||
|
const initChart = () => {
|
||||||
|
if (!chartRef.value) return
|
||||||
|
|
||||||
|
chartInstance = echarts.init(chartRef.value)
|
||||||
|
updateChart()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 监听数据变化
|
||||||
|
watch(
|
||||||
|
() => props.data,
|
||||||
|
() => {
|
||||||
|
updateChart()
|
||||||
|
},
|
||||||
|
{ deep: true }
|
||||||
|
)
|
||||||
|
|
||||||
|
// 响应式调整
|
||||||
|
const resizeHandler = debounce(() => {
|
||||||
|
chartInstance?.resize()
|
||||||
|
}, 100)
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
// 注册中国地图
|
||||||
|
await registerChinaMap()
|
||||||
|
// 初始化图表
|
||||||
|
initChart()
|
||||||
|
window.addEventListener('resize', resizeHandler)
|
||||||
|
})
|
||||||
|
|
||||||
|
onUnmounted(() => {
|
||||||
|
window.removeEventListener('resize', resizeHandler)
|
||||||
|
chartInstance?.dispose()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.china-map-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 500px;
|
||||||
|
position: relative;
|
||||||
|
|
||||||
|
.chart-container {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
92
src/views/prison/dashboard/components/StatCard.vue
Normal file
92
src/views/prison/dashboard/components/StatCard.vue
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
<template>
|
||||||
|
<el-card shadow="hover" class="stat-card">
|
||||||
|
<div class="stat-content">
|
||||||
|
<div class="stat-info">
|
||||||
|
<div class="stat-value" v-loading="loading">
|
||||||
|
<el-skeleton v-if="loading" animated :rows="0" />
|
||||||
|
<template v-else>{{ formatNumber(value) }}</template>
|
||||||
|
</div>
|
||||||
|
<div class="stat-title">{{ title }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="stat-icon" :class="iconClass">
|
||||||
|
<Icon :icon="icon" :size="36" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
interface Props {
|
||||||
|
title: string
|
||||||
|
value: number
|
||||||
|
icon: string
|
||||||
|
iconClass?: string
|
||||||
|
loading?: boolean
|
||||||
|
}
|
||||||
|
|
||||||
|
defineOptions({ name: 'StatCard' })
|
||||||
|
|
||||||
|
const props = defineProps<Props>()
|
||||||
|
|
||||||
|
const formatNumber = (num: number) => {
|
||||||
|
return new Intl.NumberFormat('zh-CN').format(num)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.stat-card {
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
:deep(.el-card__body) {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-content {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-value {
|
||||||
|
font-size: 28px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-title {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #909399;
|
||||||
|
margin-top: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-icon {
|
||||||
|
width: 56px;
|
||||||
|
height: 56px;
|
||||||
|
border-radius: 8px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
&.primary {
|
||||||
|
background: #ecf5ff;
|
||||||
|
color: #409eff;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.success {
|
||||||
|
background: #f0f9eb;
|
||||||
|
color: #67c23a;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.warning {
|
||||||
|
background: #fdf6ec;
|
||||||
|
color: #e6a23c;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.danger {
|
||||||
|
background: #fef0f0;
|
||||||
|
color: #f56c6c;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
295
src/views/prison/dashboard/index.vue
Normal file
295
src/views/prison/dashboard/index.vue
Normal file
@ -0,0 +1,295 @@
|
|||||||
|
<template>
|
||||||
|
<div class="dashboard-container">
|
||||||
|
<!-- 核心指标卡片 -->
|
||||||
|
<el-row :gutter="16" class="mb-16px">
|
||||||
|
<el-col :xs="24" :sm="12" :md="8" :lg="24 / 5" v-for="item in statCards" :key="item.title">
|
||||||
|
<StatCard v-bind="item" :loading="loading" :value="item.value" />
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<!-- 饼图区域 -->
|
||||||
|
<el-row :gutter="16" class="mb-16px">
|
||||||
|
<el-col :xs="24" :sm="24" :md="8">
|
||||||
|
<el-card shadow="never">
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span><Icon icon="ep:pie-chart" class="mr-5px" />年龄分布</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<EChart v-if="!loading" :options="ageChartOptions" :height="'350px'" />
|
||||||
|
<div v-else class="chart-loading">
|
||||||
|
<el-skeleton animated :rows="6" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="24" :sm="24" :md="8">
|
||||||
|
<el-card shadow="never">
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span><Icon icon="ep:pie-chart" class="mr-5px" />刑期分布</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<EChart v-if="!loading" :options="sentenceChartOptions" :height="'350px'" />
|
||||||
|
<div v-else class="chart-loading">
|
||||||
|
<el-skeleton animated :rows="6" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="24" :sm="24" :md="8">
|
||||||
|
<el-card shadow="never">
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span><Icon icon="ep:pie-chart" class="mr-5px" />文化程度</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<EChart v-if="!loading" :options="educationChartOptions" :height="'350px'" />
|
||||||
|
<div v-else class="chart-loading">
|
||||||
|
<el-skeleton animated :rows="6" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<!-- 地图区域 -->
|
||||||
|
<el-row :gutter="16">
|
||||||
|
<el-col :span="24">
|
||||||
|
<el-card shadow="never">
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span><Icon icon="ep:location" class="mr-5px" />籍贯分布</span>
|
||||||
|
<span v-if="statisticsTime" class="statistics-time">
|
||||||
|
统计时间:{{ formatDate(statisticsTime) }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<ChinaMap v-if="!loading" :data="provinceData" :loading="loading" />
|
||||||
|
<div v-else class="chart-loading">
|
||||||
|
<el-skeleton animated :rows="10" />
|
||||||
|
</div>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, computed, onMounted } from 'vue'
|
||||||
|
import type { EChartsOption } from 'echarts'
|
||||||
|
import { StatCard } from './components'
|
||||||
|
import { DashboardApi, type DashboardStatisticsVO } from '@/api/prison/dashboard'
|
||||||
|
import EChart from '@/components/Echart/src/Echart.vue'
|
||||||
|
import ChinaMap from './components/ChinaMap.vue'
|
||||||
|
|
||||||
|
defineOptions({ name: 'PrisonDashboard' })
|
||||||
|
|
||||||
|
const loading = ref(true)
|
||||||
|
const data = ref<DashboardStatisticsVO | null>(null)
|
||||||
|
|
||||||
|
const statCards = computed(() => [
|
||||||
|
{
|
||||||
|
title: '在册罪犯',
|
||||||
|
value: data.value?.totalPrisoners || 0,
|
||||||
|
icon: 'ep:user',
|
||||||
|
iconClass: 'primary'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '本月释放',
|
||||||
|
value: data.value?.monthlyReleased || 0,
|
||||||
|
icon: 'ep:select',
|
||||||
|
iconClass: 'success'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '本月移交',
|
||||||
|
value: data.value?.monthlyTransferred || 0,
|
||||||
|
icon: 'ep:rank-list',
|
||||||
|
iconClass: 'warning'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '当前就医',
|
||||||
|
value: data.value?.hospitalCount || 0,
|
||||||
|
icon: 'ep:first-aid-kit',
|
||||||
|
iconClass: 'danger'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '当前禁闭',
|
||||||
|
value: data.value?.solitaryCount || 0,
|
||||||
|
icon: 'ep:lock',
|
||||||
|
iconClass: 'danger'
|
||||||
|
}
|
||||||
|
])
|
||||||
|
|
||||||
|
const ageChartOptions = computed<EChartsOption>(() => ({
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'item',
|
||||||
|
formatter: '{b}: {c} ({d}%)'
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
orient: 'vertical',
|
||||||
|
right: 10,
|
||||||
|
top: 'center',
|
||||||
|
icon: 'circle'
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
type: 'pie',
|
||||||
|
radius: ['45%', '70%'],
|
||||||
|
center: ['40%', '50%'],
|
||||||
|
avoidLabelOverlap: false,
|
||||||
|
itemStyle: {
|
||||||
|
borderRadius: 6,
|
||||||
|
borderColor: '#fff',
|
||||||
|
borderWidth: 2
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
show: false,
|
||||||
|
position: 'center'
|
||||||
|
},
|
||||||
|
emphasis: {
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: 'bold'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
labelLine: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
data: data.value?.ageDistribution || []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}))
|
||||||
|
|
||||||
|
const sentenceChartOptions = computed<EChartsOption>(() => ({
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'item',
|
||||||
|
formatter: '{b}: {c} ({d}%)'
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
orient: 'vertical',
|
||||||
|
right: 10,
|
||||||
|
top: 'center',
|
||||||
|
icon: 'circle'
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
type: 'pie',
|
||||||
|
radius: ['45%', '70%'],
|
||||||
|
center: ['40%', '50%'],
|
||||||
|
avoidLabelOverlap: false,
|
||||||
|
itemStyle: {
|
||||||
|
borderRadius: 6,
|
||||||
|
borderColor: '#fff',
|
||||||
|
borderWidth: 2
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
show: false,
|
||||||
|
position: 'center'
|
||||||
|
},
|
||||||
|
emphasis: {
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: 'bold'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
labelLine: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
data: data.value?.sentenceDistribution || []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}))
|
||||||
|
|
||||||
|
const educationChartOptions = computed<EChartsOption>(() => ({
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'item',
|
||||||
|
formatter: '{b}: {c} ({d}%)'
|
||||||
|
},
|
||||||
|
legend: {
|
||||||
|
orient: 'vertical',
|
||||||
|
right: 10,
|
||||||
|
top: 'center',
|
||||||
|
icon: 'circle'
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
type: 'pie',
|
||||||
|
radius: ['45%', '70%'],
|
||||||
|
center: ['40%', '50%'],
|
||||||
|
avoidLabelOverlap: false,
|
||||||
|
itemStyle: {
|
||||||
|
borderRadius: 6,
|
||||||
|
borderColor: '#fff',
|
||||||
|
borderWidth: 2
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
show: false,
|
||||||
|
position: 'center'
|
||||||
|
},
|
||||||
|
emphasis: {
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
fontSize: 16,
|
||||||
|
fontWeight: 'bold'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
labelLine: {
|
||||||
|
show: false
|
||||||
|
},
|
||||||
|
data: data.value?.educationDistribution || []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}))
|
||||||
|
|
||||||
|
const provinceData = computed(() => data.value?.provinceDistribution || [])
|
||||||
|
|
||||||
|
const statisticsTime = computed(() => data.value?.statisticsTime)
|
||||||
|
|
||||||
|
const formatDate = (dateStr: string) => {
|
||||||
|
if (!dateStr) return '-'
|
||||||
|
return new Date(dateStr).toLocaleString('zh-CN')
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchData = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
data.value = await DashboardApi.getStatistics()
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取看板数据失败:', error)
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
fetchData()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.dashboard-container {
|
||||||
|
padding: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.mb-16px {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
font-weight: 500;
|
||||||
|
|
||||||
|
.statistics-time {
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: normal;
|
||||||
|
color: #909399;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.chart-loading {
|
||||||
|
height: 350px;
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
117
src/views/prison/prisoner/PrisonerDetail.vue
Normal file
117
src/views/prison/prisoner/PrisonerDetail.vue
Normal file
@ -0,0 +1,117 @@
|
|||||||
|
<template>
|
||||||
|
<el-drawer v-model="dialogVisible" title="罪犯详情" size="600px">
|
||||||
|
<div v-loading="loading">
|
||||||
|
<!-- 基本信息 -->
|
||||||
|
<el-descriptions title="基本信息" :column="2" border>
|
||||||
|
<el-descriptions-item label="罪犯编号">{{ data.prisonerNo }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="姓名">{{ data.name }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="性别">
|
||||||
|
<dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="data.gender" />
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="身份证号">{{ data.idCard }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="出生日期">{{ data.birthday }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="民族">{{ data.ethnicity }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="籍贯" :span="2">{{ data.nativePlace }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="文化程度">
|
||||||
|
<dict-tag :type="DICT_TYPE.PRISON_EDUCATION" :value="data.education" />
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="职业">{{ data.occupation }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="家庭住址" :span="2">{{ data.address }}</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
|
||||||
|
<!-- 刑罚信息 -->
|
||||||
|
<el-descriptions title="刑罚信息" :column="2" border style="margin-top: 20px">
|
||||||
|
<el-descriptions-item label="罪名">{{ data.crime }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="刑期">
|
||||||
|
{{ formatSentence(data) }}
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="判决法院">{{ data.courtName }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="判决日期">{{ data.judgmentDate }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="判决书编号" :span="2">{{ data.judgmentNo }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="入狱日期">{{ data.imprisonmentDate }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="释放日期">{{ data.releaseDate }}</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
|
||||||
|
<!-- 监管信息 -->
|
||||||
|
<el-descriptions title="监管信息" :column="2" border style="margin-top: 20px">
|
||||||
|
<el-descriptions-item label="监管等级">
|
||||||
|
<dict-tag :type="DICT_TYPE.PRISON_SUPERVISION_LEVEL" :value="data.supervisionLevel" />
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="风险等级">
|
||||||
|
<dict-tag :type="DICT_TYPE.PRISON_RISK_LEVEL" :value="data.riskLevel" />
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="当前监区">{{ data.areaName }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="当前监室">{{ data.cellName }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="状态">
|
||||||
|
<dict-tag :type="DICT_TYPE.PRISONER_STATUS" :value="data.status" />
|
||||||
|
</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="备注">{{ data.remark }}</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
|
||||||
|
<!-- 位置历史 -->
|
||||||
|
<div style="margin-top: 20px">
|
||||||
|
<h4>位置变更历史</h4>
|
||||||
|
<el-timeline v-if="areaHistory.length > 0">
|
||||||
|
<el-timeline-item
|
||||||
|
v-for="(log, index) in areaHistory"
|
||||||
|
:key="index"
|
||||||
|
:timestamp="log.createTime"
|
||||||
|
placement="top"
|
||||||
|
>
|
||||||
|
<el-card>
|
||||||
|
<p><strong>从:</strong>{{ log.fromAreaName || '-' }} / {{ log.fromCellName || '-' }}</p>
|
||||||
|
<p><strong>到:</strong>{{ log.toAreaName || '-' }} / {{ log.toCellName || '-' }}</p>
|
||||||
|
<p><strong>原因:</strong>{{ log.reason || '-' }}</p>
|
||||||
|
<p><strong>操作人:</strong>{{ log.operatorName }}</p>
|
||||||
|
</el-card>
|
||||||
|
</el-timeline-item>
|
||||||
|
</el-timeline>
|
||||||
|
<el-empty v-else description="暂无位置变更记录" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</el-drawer>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { DICT_TYPE } from '@/utils/dict'
|
||||||
|
import * as PrisonerApi from '@/api/prison/prisoner'
|
||||||
|
|
||||||
|
defineOptions({ name: 'PrisonPrisonerDetail' })
|
||||||
|
|
||||||
|
const dialogVisible = ref(false)
|
||||||
|
const loading = ref(false)
|
||||||
|
const data = ref<any>({})
|
||||||
|
const areaHistory = ref([])
|
||||||
|
|
||||||
|
/** 打开弹窗 */
|
||||||
|
const open = async (id: number) => {
|
||||||
|
dialogVisible.value = true
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
// 获取罪犯基本信息
|
||||||
|
data.value = await PrisonerApi.getPrisoner(id)
|
||||||
|
// 获取位置历史
|
||||||
|
areaHistory.value = await PrisonerApi.getPrisonerAreaHistory(id)
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 刑期显示格式化 */
|
||||||
|
const formatSentence = (row: any) => {
|
||||||
|
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 || '-'
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ open })
|
||||||
|
</script>
|
||||||
@ -1,8 +1,8 @@
|
|||||||
<template>
|
<template>
|
||||||
<el-dialog
|
<el-dialog
|
||||||
v-model="dialogVisible"
|
v-model="dialogVisible"
|
||||||
:title="dialogType === 'create' ? '新增服刑人员' : '修改服刑人员'"
|
:title="dialogType === 'create' ? '入监登记' : '修改罪犯信息'"
|
||||||
width="900px"
|
width="1000px"
|
||||||
:close-on-click-modal="false"
|
:close-on-click-modal="false"
|
||||||
>
|
>
|
||||||
<el-form
|
<el-form
|
||||||
@ -12,181 +12,265 @@
|
|||||||
label-width="100px"
|
label-width="100px"
|
||||||
v-loading="formLoading"
|
v-loading="formLoading"
|
||||||
>
|
>
|
||||||
<el-row :gutter="20">
|
<el-tabs v-model="activeTab">
|
||||||
<el-col :span="8">
|
<el-tab-pane label="基础信息" name="basic">
|
||||||
<el-form-item label="服刑人员编号" prop="prisonerNo">
|
<el-row :gutter="20">
|
||||||
<el-input v-model="formData.prisonerNo" placeholder="请输入服刑人员编号" :disabled="dialogType === 'update'" />
|
<el-col :span="8">
|
||||||
</el-form-item>
|
<el-form-item label="罪犯编号" prop="prisonerNo">
|
||||||
</el-col>
|
<el-input v-model="formData.prisonerNo" placeholder="请输入罪犯编号" :disabled="dialogType === 'update'" />
|
||||||
<el-col :span="8">
|
</el-form-item>
|
||||||
<el-form-item label="姓名" prop="name">
|
</el-col>
|
||||||
<el-input v-model="formData.name" placeholder="请输入姓名" />
|
<el-col :span="8">
|
||||||
</el-form-item>
|
<el-form-item label="姓名" prop="name">
|
||||||
</el-col>
|
<el-input v-model="formData.name" placeholder="请输入姓名" />
|
||||||
<el-col :span="8">
|
</el-form-item>
|
||||||
<el-form-item label="性别" prop="gender">
|
</el-col>
|
||||||
<el-select v-model="formData.gender" placeholder="请选择性别" clearable style="width: 100%">
|
<el-col :span="8">
|
||||||
<el-option
|
<el-form-item label="性别" prop="gender">
|
||||||
v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)"
|
<el-select v-model="formData.gender" placeholder="请选择性别" clearable style="width: 100%">
|
||||||
:key="dict.value"
|
<el-option
|
||||||
:label="dict.label"
|
v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)"
|
||||||
:value="dict.value"
|
:key="dict.value"
|
||||||
/>
|
:label="dict.label"
|
||||||
</el-select>
|
:value="dict.value"
|
||||||
</el-form-item>
|
/>
|
||||||
</el-col>
|
</el-select>
|
||||||
</el-row>
|
</el-form-item>
|
||||||
<el-row :gutter="20">
|
</el-col>
|
||||||
<el-col :span="8">
|
</el-row>
|
||||||
<el-form-item label="身份证号" prop="idCard">
|
<el-row :gutter="20">
|
||||||
<el-input v-model="formData.idCard" placeholder="请输入身份证号" />
|
<el-col :span="8">
|
||||||
</el-form-item>
|
<el-form-item label="身份证号" prop="idCard">
|
||||||
</el-col>
|
<el-input v-model="formData.idCard" placeholder="请输入身份证号" @blur="parseIdCard" />
|
||||||
<el-col :span="8">
|
</el-form-item>
|
||||||
<el-form-item label="出生日期" prop="birthday">
|
</el-col>
|
||||||
<el-date-picker
|
<el-col :span="8">
|
||||||
v-model="formData.birthday"
|
<el-form-item label="出生日期" prop="birthday">
|
||||||
type="date"
|
<el-date-picker
|
||||||
placeholder="请选择出生日期"
|
v-model="formData.birthday"
|
||||||
value-format="YYYY-MM-DD"
|
type="date"
|
||||||
style="width: 100%"
|
placeholder="请选择出生日期"
|
||||||
/>
|
value-format="YYYY-MM-DD"
|
||||||
</el-form-item>
|
style="width: 100%"
|
||||||
</el-col>
|
/>
|
||||||
<el-col :span="8">
|
</el-form-item>
|
||||||
<el-form-item label="民族" prop="ethnicity">
|
</el-col>
|
||||||
<el-input v-model="formData.ethnicity" placeholder="请输入民族" />
|
<el-col :span="8">
|
||||||
</el-form-item>
|
<el-form-item label="民族" prop="ethnicity">
|
||||||
</el-col>
|
<el-input v-model="formData.ethnicity" placeholder="请输入民族" />
|
||||||
</el-row>
|
</el-form-item>
|
||||||
<el-row :gutter="20">
|
</el-col>
|
||||||
<el-col :span="8">
|
</el-row>
|
||||||
<el-form-item label="籍贯" prop="nativePlace">
|
<el-row :gutter="20">
|
||||||
<el-input v-model="formData.nativePlace" placeholder="请输入籍贯" />
|
<el-col :span="8">
|
||||||
</el-form-item>
|
<el-form-item label="籍贯" prop="nativePlace">
|
||||||
</el-col>
|
<el-input v-model="formData.nativePlace" placeholder="请输入籍贯" />
|
||||||
<el-col :span="8">
|
</el-form-item>
|
||||||
<el-form-item label="文化程度" prop="education">
|
</el-col>
|
||||||
<el-select v-model="formData.education" placeholder="请选择文化程度" clearable style="width: 100%">
|
<el-col :span="8">
|
||||||
<el-option
|
<el-form-item label="文化程度" prop="education">
|
||||||
v-for="dict in getIntDictOptions(DICT_TYPE.PRISON_EDUCATION)"
|
<el-select v-model="formData.education" placeholder="请选择文化程度" clearable style="width: 100%">
|
||||||
:key="dict.value"
|
<el-option
|
||||||
:label="dict.label"
|
v-for="dict in getIntDictOptions(DICT_TYPE.PRISON_EDUCATION)"
|
||||||
:value="dict.value"
|
:key="dict.value"
|
||||||
/>
|
:label="dict.label"
|
||||||
</el-select>
|
:value="dict.value"
|
||||||
</el-form-item>
|
/>
|
||||||
</el-col>
|
</el-select>
|
||||||
<el-col :span="8">
|
</el-form-item>
|
||||||
<el-form-item label="职业" prop="occupation">
|
</el-col>
|
||||||
<el-input v-model="formData.occupation" placeholder="请输入职业" />
|
<el-col :span="8">
|
||||||
</el-form-item>
|
<el-form-item label="职业" prop="occupation">
|
||||||
</el-col>
|
<el-input v-model="formData.occupation" placeholder="请输入职业" />
|
||||||
</el-row>
|
</el-form-item>
|
||||||
<el-row :gutter="20">
|
</el-col>
|
||||||
<el-col :span="24">
|
</el-row>
|
||||||
<el-form-item label="家庭住址" prop="address">
|
<el-row :gutter="20">
|
||||||
<el-input v-model="formData.address" placeholder="请输入家庭住址" />
|
<el-col :span="24">
|
||||||
</el-form-item>
|
<el-form-item label="家庭住址" prop="address">
|
||||||
</el-col>
|
<el-input v-model="formData.address" placeholder="请输入家庭住址" />
|
||||||
</el-row>
|
</el-form-item>
|
||||||
<el-divider border-style="dashed">刑罚信息</el-divider>
|
</el-col>
|
||||||
<el-row :gutter="20">
|
</el-row>
|
||||||
<el-col :span="12">
|
</el-tab-pane>
|
||||||
<el-form-item label="罪名" prop="crime">
|
|
||||||
<el-input v-model="formData.crime" placeholder="请输入罪名" />
|
<el-tab-pane label="刑罚信息" name="sentence">
|
||||||
</el-form-item>
|
<el-row :gutter="20">
|
||||||
</el-col>
|
<el-col :span="12">
|
||||||
<el-col :span="6">
|
<el-form-item label="罪名" prop="crime">
|
||||||
<el-form-item label="刑期(年)" prop="sentenceYears">
|
<el-input v-model="formData.crime" placeholder="请输入罪名" />
|
||||||
<el-input-number v-model="formData.sentenceYears" :min="0" :max="100" placeholder="请输入" />
|
</el-form-item>
|
||||||
</el-form-item>
|
</el-col>
|
||||||
</el-col>
|
<el-col :span="6">
|
||||||
<el-col :span="6">
|
<el-form-item label="刑期(年)" prop="sentenceYears">
|
||||||
<el-form-item label="刑期(月)" prop="sentenceMonths">
|
<el-input-number v-model="formData.sentenceYears" :min="0" :max="100" placeholder="请输入" style="width: 100%" />
|
||||||
<el-input-number v-model="formData.sentenceMonths" :min="0" :max="11" placeholder="请输入" />
|
</el-form-item>
|
||||||
</el-form-item>
|
</el-col>
|
||||||
</el-col>
|
<el-col :span="6">
|
||||||
</el-row>
|
<el-form-item label="刑期(月)" prop="sentenceMonths">
|
||||||
<el-row :gutter="20">
|
<el-input-number v-model="formData.sentenceMonths" :min="0" :max="11" placeholder="请输入" style="width: 100%" />
|
||||||
<el-col :span="8">
|
</el-form-item>
|
||||||
<el-form-item label="入狱日期" prop="imprisonmentDate">
|
</el-col>
|
||||||
<el-date-picker
|
</el-row>
|
||||||
v-model="formData.imprisonmentDate"
|
<el-row :gutter="20">
|
||||||
type="date"
|
<el-col :span="6">
|
||||||
placeholder="请选择入狱日期"
|
<el-form-item label="是否无期" prop="lifeImprisonment">
|
||||||
value-format="YYYY-MM-DD"
|
<el-select v-model="formData.lifeImprisonment" placeholder="请选择" clearable style="width: 100%">
|
||||||
style="width: 100%"
|
<el-option :key="0" :label="'否'" :value="0" />
|
||||||
/>
|
<el-option :key="1" :label="'是'" :value="1" />
|
||||||
</el-form-item>
|
</el-select>
|
||||||
</el-col>
|
</el-form-item>
|
||||||
<el-col :span="8">
|
</el-col>
|
||||||
<el-form-item label="释放日期" prop="releaseDate">
|
<el-col :span="6">
|
||||||
<el-date-picker
|
<el-form-item label="是否死缓" prop="deathSentenceReprieve">
|
||||||
v-model="formData.releaseDate"
|
<el-select v-model="formData.deathSentenceReprieve" placeholder="请选择" clearable style="width: 100%">
|
||||||
type="date"
|
<el-option :key="0" :label="'否'" :value="0" />
|
||||||
placeholder="请选择释放日期"
|
<el-option :key="1" :label="'是'" :value="1" />
|
||||||
value-format="YYYY-MM-DD"
|
</el-select>
|
||||||
style="width: 100%"
|
</el-form-item>
|
||||||
/>
|
</el-col>
|
||||||
</el-form-item>
|
<el-col :span="12">
|
||||||
</el-col>
|
<el-form-item label="原判刑期" prop="originalSentence">
|
||||||
<el-col :span="8">
|
<el-input v-model="formData.originalSentence" placeholder="如:有期徒刑10年" />
|
||||||
<el-form-item label="监管等级" prop="supervisionLevel">
|
</el-form-item>
|
||||||
<el-select v-model="formData.supervisionLevel" placeholder="请选择监管等级" clearable style="width: 100%">
|
</el-col>
|
||||||
<el-option
|
</el-row>
|
||||||
v-for="dict in getIntDictOptions(DICT_TYPE.PRISON_SUPERVISION_LEVEL)"
|
<el-row :gutter="20">
|
||||||
:key="dict.value"
|
<el-col :span="8">
|
||||||
:label="dict.label"
|
<el-form-item label="判决法院" prop="courtName">
|
||||||
:value="dict.value"
|
<el-input v-model="formData.courtName" placeholder="请输入判决法院" />
|
||||||
/>
|
</el-form-item>
|
||||||
</el-select>
|
</el-col>
|
||||||
</el-form-item>
|
<el-col :span="8">
|
||||||
</el-col>
|
<el-form-item label="判决日期" prop="judgmentDate">
|
||||||
</el-row>
|
<el-date-picker
|
||||||
<el-row :gutter="20">
|
v-model="formData.judgmentDate"
|
||||||
<el-col :span="8">
|
type="date"
|
||||||
<el-form-item label="风险等级" prop="riskLevel">
|
placeholder="请选择判决日期"
|
||||||
<el-select v-model="formData.riskLevel" placeholder="请选择风险等级" clearable style="width: 100%">
|
value-format="YYYY-MM-DD"
|
||||||
<el-option
|
style="width: 100%"
|
||||||
v-for="dict in getIntDictOptions(DICT_TYPE.PRISON_RISK_LEVEL)"
|
/>
|
||||||
:key="dict.value"
|
</el-form-item>
|
||||||
:label="dict.label"
|
</el-col>
|
||||||
:value="dict.value"
|
<el-col :span="8">
|
||||||
/>
|
<el-form-item label="判决书编号" prop="judgmentNo">
|
||||||
</el-select>
|
<el-input v-model="formData.judgmentNo" placeholder="如:(2023)沪01刑初123号" />
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :span="8">
|
</el-row>
|
||||||
<el-form-item label="监区ID" prop="prisonAreaId">
|
<el-row :gutter="20">
|
||||||
<el-input-number v-model="formData.prisonAreaId" :min="0" placeholder="请输入监区ID" />
|
<el-col :span="8">
|
||||||
</el-form-item>
|
<el-form-item label="入狱日期" prop="imprisonmentDate">
|
||||||
</el-col>
|
<el-date-picker
|
||||||
<el-col :span="8">
|
v-model="formData.imprisonmentDate"
|
||||||
<el-form-item label="监室ID" prop="prisonCellId">
|
type="date"
|
||||||
<el-input-number v-model="formData.prisonCellId" :min="0" placeholder="请输入监室ID" />
|
placeholder="请选择入狱日期"
|
||||||
</el-form-item>
|
value-format="YYYY-MM-DD"
|
||||||
</el-col>
|
style="width: 100%"
|
||||||
</el-row>
|
@change="calculateReleaseDate"
|
||||||
<el-row :gutter="20">
|
/>
|
||||||
<el-col :span="8">
|
</el-form-item>
|
||||||
<el-form-item label="状态" prop="status">
|
</el-col>
|
||||||
<el-select v-model="formData.status" placeholder="请选择状态" clearable style="width: 100%">
|
<el-col :span="8">
|
||||||
<el-option
|
<el-form-item label="释放日期" prop="releaseDate">
|
||||||
v-for="dict in getIntDictOptions(DICT_TYPE.PRISONER_STATUS)"
|
<el-date-picker
|
||||||
:key="dict.value"
|
v-model="formData.releaseDate"
|
||||||
:label="dict.label"
|
type="date"
|
||||||
:value="dict.value"
|
placeholder="请选择释放日期"
|
||||||
/>
|
value-format="YYYY-MM-DD"
|
||||||
</el-select>
|
style="width: 100%"
|
||||||
</el-form-item>
|
/>
|
||||||
</el-col>
|
</el-form-item>
|
||||||
<el-col :span="16">
|
</el-col>
|
||||||
<el-form-item label="备注" prop="remark">
|
</el-row>
|
||||||
<el-input v-model="formData.remark" placeholder="请输入备注" />
|
</el-tab-pane>
|
||||||
</el-form-item>
|
|
||||||
</el-col>
|
<el-tab-pane label="监管信息" name="supervision">
|
||||||
</el-row>
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-form-item label="监管等级" prop="supervisionLevel">
|
||||||
|
<el-select v-model="formData.supervisionLevel" placeholder="请选择监管等级" clearable style="width: 100%">
|
||||||
|
<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-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-form-item label="风险等级" prop="riskLevel">
|
||||||
|
<el-select v-model="formData.riskLevel" placeholder="请选择风险等级" clearable style="width: 100%">
|
||||||
|
<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-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-form-item label="状态" prop="status">
|
||||||
|
<el-select v-model="formData.status" placeholder="请选择状态" clearable style="width: 100%">
|
||||||
|
<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-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-form-item label="监区" prop="prisonAreaId">
|
||||||
|
<el-select v-model="formData.prisonAreaId" placeholder="请选择监区" clearable style="width: 100%" @change="handleAreaChange">
|
||||||
|
<el-option
|
||||||
|
v-for="area in areaList"
|
||||||
|
:key="area.id"
|
||||||
|
:label="area.name"
|
||||||
|
:value="area.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-form-item label="分监区" prop="subAreaId">
|
||||||
|
<el-select v-model="formData.subAreaId" placeholder="请选择分监区" clearable style="width: 100%" @change="handleSubAreaChange">
|
||||||
|
<el-option
|
||||||
|
v-for="subArea in subAreaList"
|
||||||
|
:key="subArea.id"
|
||||||
|
:label="subArea.name"
|
||||||
|
:value="subArea.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="8">
|
||||||
|
<el-form-item label="监室" prop="prisonCellId">
|
||||||
|
<el-select v-model="formData.prisonCellId" placeholder="请选择监室" clearable style="width: 100%">
|
||||||
|
<el-option
|
||||||
|
v-for="cell in cellList"
|
||||||
|
:key="cell.id"
|
||||||
|
:label="cell.name"
|
||||||
|
:value="cell.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="24">
|
||||||
|
<el-form-item label="备注" prop="remark">
|
||||||
|
<el-input v-model="formData.remark" placeholder="请输入备注" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
</el-form>
|
</el-form>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<el-button @click="dialogVisible = false">取消</el-button>
|
<el-button @click="dialogVisible = false">取消</el-button>
|
||||||
@ -197,18 +281,28 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||||
|
import { PrisonerCreateVO } from '@/api/prison/prisoner'
|
||||||
import * as PrisonerApi from '@/api/prison/prisoner'
|
import * as PrisonerApi from '@/api/prison/prisoner'
|
||||||
|
import { AreaApi } from '@/api/prison/area'
|
||||||
|
import { CellApi } from '@/api/prison/cell'
|
||||||
|
|
||||||
defineOptions({ name: 'PrisonPrisonerForm' })
|
defineOptions({ name: 'PrisonPrisonerForm' })
|
||||||
|
|
||||||
|
const emit = defineEmits(['success'])
|
||||||
|
|
||||||
const { t } = useI18n()
|
const { t } = useI18n()
|
||||||
const message = useMessage()
|
const message = useMessage()
|
||||||
|
|
||||||
const dialogVisible = ref(false)
|
const dialogVisible = ref(false)
|
||||||
const dialogType = ref('create')
|
const dialogType = ref<'create' | 'update'>('create')
|
||||||
const formLoading = ref(false)
|
const formLoading = ref(false)
|
||||||
const formRef = ref()
|
const formRef = ref()
|
||||||
const formData = ref({
|
const activeTab = ref('basic')
|
||||||
|
const areaList = ref([])
|
||||||
|
const subAreaList = ref([])
|
||||||
|
const cellList = ref([])
|
||||||
|
|
||||||
|
const formData = ref<PrisonerCreateVO>({
|
||||||
id: undefined,
|
id: undefined,
|
||||||
prisonerNo: '',
|
prisonerNo: '',
|
||||||
name: '',
|
name: '',
|
||||||
@ -223,18 +317,25 @@ const formData = ref({
|
|||||||
crime: '',
|
crime: '',
|
||||||
sentenceYears: 0,
|
sentenceYears: 0,
|
||||||
sentenceMonths: 0,
|
sentenceMonths: 0,
|
||||||
|
lifeImprisonment: undefined,
|
||||||
|
deathSentenceReprieve: undefined,
|
||||||
|
courtName: '',
|
||||||
|
judgmentDate: '',
|
||||||
|
judgmentNo: '',
|
||||||
|
originalSentence: '',
|
||||||
imprisonmentDate: '',
|
imprisonmentDate: '',
|
||||||
releaseDate: '',
|
releaseDate: '',
|
||||||
supervisionLevel: undefined,
|
supervisionLevel: undefined,
|
||||||
riskLevel: undefined,
|
riskLevel: undefined,
|
||||||
prisonAreaId: undefined,
|
prisonAreaId: undefined,
|
||||||
|
subAreaId: undefined,
|
||||||
prisonCellId: undefined,
|
prisonCellId: undefined,
|
||||||
status: undefined,
|
status: undefined,
|
||||||
remark: ''
|
remark: ''
|
||||||
})
|
})
|
||||||
|
|
||||||
const rules = {
|
const rules = {
|
||||||
prisonerNo: [{ required: true, message: '请输入服刑人员编号', trigger: 'blur' }],
|
prisonerNo: [{ required: true, message: '请输入罪犯编号', trigger: 'blur' }],
|
||||||
name: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
|
name: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
|
||||||
gender: [{ required: true, message: '请选择性别', trigger: 'change' }],
|
gender: [{ required: true, message: '请选择性别', trigger: 'change' }],
|
||||||
idCard: [{ required: true, message: '请输入身份证号', trigger: 'blur' }],
|
idCard: [{ required: true, message: '请输入身份证号', trigger: 'blur' }],
|
||||||
@ -245,16 +346,107 @@ const rules = {
|
|||||||
status: [{ required: true, message: '请选择状态', trigger: 'change' }]
|
status: [{ required: true, message: '请选择状态', trigger: 'change' }]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 加载监区列表 */
|
||||||
|
const loadAreas = async () => {
|
||||||
|
const treeData = await AreaApi.getAreaTree()
|
||||||
|
// 过滤出顶级监区(大队级)
|
||||||
|
areaList.value = treeData.filter((item: any) => item.level === 1 || item.parentId === 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 监区变化时加载分监区 */
|
||||||
|
const handleAreaChange = async (areaId: number) => {
|
||||||
|
formData.value.subAreaId = undefined
|
||||||
|
formData.value.prisonCellId = undefined
|
||||||
|
subAreaList.value = []
|
||||||
|
cellList.value = []
|
||||||
|
if (areaId) {
|
||||||
|
// 获取子监区(分监区)
|
||||||
|
const treeData = await AreaApi.getAreaTree()
|
||||||
|
const area = findArea(treeData, areaId)
|
||||||
|
if (area && area.children) {
|
||||||
|
subAreaList.value = area.children
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 分监区变化时加载监室 */
|
||||||
|
const handleSubAreaChange = async (subAreaId: number) => {
|
||||||
|
formData.value.prisonCellId = undefined
|
||||||
|
cellList.value = []
|
||||||
|
if (subAreaId) {
|
||||||
|
cellList.value = await CellApi.getCellPage({ areaId: subAreaId, pageNo: 1, pageSize: 200 }).then((res: any) => res.list || [])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 递归查找监区 */
|
||||||
|
const findArea = (areas: any[], id: number): any => {
|
||||||
|
for (const area of areas) {
|
||||||
|
if (area.id === id) return area
|
||||||
|
if (area.children) {
|
||||||
|
const found = findArea(area.children, id)
|
||||||
|
if (found) return found
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 从身份证号解析出生日期 */
|
||||||
|
const parseIdCard = () => {
|
||||||
|
const idCard = formData.value.idCard
|
||||||
|
if (idCard && idCard.length === 18) {
|
||||||
|
const birthday = idCard.substring(6, 14)
|
||||||
|
const formattedBirthday = `${birthday.substring(0, 4)}-${birthday.substring(4, 6)}-${birthday.substring(6, 8)}`
|
||||||
|
formData.value.birthday = formattedBirthday
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 计算释放日期 */
|
||||||
|
const calculateReleaseDate = () => {
|
||||||
|
if (!formData.value.imprisonmentDate) return
|
||||||
|
|
||||||
|
const imprisonmentDate = new Date(formData.value.imprisonmentDate)
|
||||||
|
let releaseDate = new Date(imprisonmentDate)
|
||||||
|
|
||||||
|
// 如果是无期或死缓,不自动计算释放日期
|
||||||
|
if (formData.value.lifeImprisonment === 1 || formData.value.deathSentenceReprieve === 1) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据刑期计算释放日期
|
||||||
|
const years = formData.value.sentenceYears || 0
|
||||||
|
const months = formData.value.sentenceMonths || 0
|
||||||
|
|
||||||
|
releaseDate.setFullYear(releaseDate.getFullYear() + years)
|
||||||
|
releaseDate.setMonth(releaseDate.getMonth() + months)
|
||||||
|
|
||||||
|
formData.value.releaseDate = releaseDate.toISOString().split('T')[0]
|
||||||
|
}
|
||||||
|
|
||||||
/** 打开弹窗 */
|
/** 打开弹窗 */
|
||||||
const open = (type: string, id?: number) => {
|
const open = (type: string, id?: number) => {
|
||||||
dialogType.value = type
|
dialogType.value = type
|
||||||
dialogVisible.value = true
|
dialogVisible.value = true
|
||||||
|
activeTab.value = 'basic'
|
||||||
resetForm()
|
resetForm()
|
||||||
|
loadAreas()
|
||||||
if (id) {
|
if (id) {
|
||||||
getPrisonerDetail(id)
|
getPrisonerDetail(id)
|
||||||
|
} else {
|
||||||
|
// 新增时自动生成编号
|
||||||
|
generatePrisonerNo()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 自动生成罪犯编号 */
|
||||||
|
const generatePrisonerNo = async () => {
|
||||||
|
try {
|
||||||
|
const date = new Date()
|
||||||
|
const prefix = `Z${date.getFullYear().toString().slice(2)}`
|
||||||
|
// 这里可以调用后端接口生成编号
|
||||||
|
formData.value.prisonerNo = prefix + Math.random().toString().slice(2, 8)
|
||||||
|
} catch {}
|
||||||
|
}
|
||||||
|
|
||||||
/** 重置表单 */
|
/** 重置表单 */
|
||||||
const resetForm = () => {
|
const resetForm = () => {
|
||||||
formData.value = {
|
formData.value = {
|
||||||
@ -272,15 +464,24 @@ const resetForm = () => {
|
|||||||
crime: '',
|
crime: '',
|
||||||
sentenceYears: 0,
|
sentenceYears: 0,
|
||||||
sentenceMonths: 0,
|
sentenceMonths: 0,
|
||||||
|
lifeImprisonment: undefined,
|
||||||
|
deathSentenceReprieve: undefined,
|
||||||
|
courtName: '',
|
||||||
|
judgmentDate: '',
|
||||||
|
judgmentNo: '',
|
||||||
|
originalSentence: '',
|
||||||
imprisonmentDate: '',
|
imprisonmentDate: '',
|
||||||
releaseDate: '',
|
releaseDate: '',
|
||||||
supervisionLevel: undefined,
|
supervisionLevel: undefined,
|
||||||
riskLevel: undefined,
|
riskLevel: undefined,
|
||||||
prisonAreaId: undefined,
|
prisonAreaId: undefined,
|
||||||
|
subAreaId: undefined,
|
||||||
prisonCellId: undefined,
|
prisonCellId: undefined,
|
||||||
status: undefined,
|
status: undefined,
|
||||||
remark: ''
|
remark: ''
|
||||||
}
|
}
|
||||||
|
subAreaList.value = []
|
||||||
|
cellList.value = []
|
||||||
formRef.value?.resetFields()
|
formRef.value?.resetFields()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -289,7 +490,21 @@ const getPrisonerDetail = async (id: number) => {
|
|||||||
formLoading.value = true
|
formLoading.value = true
|
||||||
try {
|
try {
|
||||||
const data = await PrisonerApi.getPrisoner(id)
|
const data = await PrisonerApi.getPrisoner(id)
|
||||||
formData.value = data
|
formData.value = {
|
||||||
|
...formData.value,
|
||||||
|
...data
|
||||||
|
}
|
||||||
|
// 如果有监区,加载分监区和监室
|
||||||
|
if (data.prisonAreaId) {
|
||||||
|
const treeData = await AreaApi.getAreaTree()
|
||||||
|
const area = findArea(treeData, data.prisonAreaId)
|
||||||
|
if (area && area.children) {
|
||||||
|
subAreaList.value = area.children
|
||||||
|
}
|
||||||
|
if (data.subAreaId) {
|
||||||
|
cellList.value = await CellApi.getCellPage({ areaId: data.subAreaId, pageNo: 1, pageSize: 200 }).then((res: any) => res.list || [])
|
||||||
|
}
|
||||||
|
}
|
||||||
} finally {
|
} finally {
|
||||||
formLoading.value = false
|
formLoading.value = false
|
||||||
}
|
}
|
||||||
@ -303,7 +518,7 @@ const submitForm = async () => {
|
|||||||
try {
|
try {
|
||||||
if (dialogType.value === 'create') {
|
if (dialogType.value === 'create') {
|
||||||
await PrisonerApi.createPrisoner(formData.value)
|
await PrisonerApi.createPrisoner(formData.value)
|
||||||
message.success('新增成功')
|
message.success('入监登记成功')
|
||||||
} else {
|
} else {
|
||||||
await PrisonerApi.updatePrisoner(formData.value)
|
await PrisonerApi.updatePrisoner(formData.value)
|
||||||
message.success('修改成功')
|
message.success('修改成功')
|
||||||
|
|||||||
201
src/views/prison/prisoner/TransferForm.vue
Normal file
201
src/views/prison/prisoner/TransferForm.vue
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
v-model="dialogVisible"
|
||||||
|
title="调监操作"
|
||||||
|
width="500px"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
>
|
||||||
|
<el-form ref="formRef" :model="formData" :rules="rules" label-width="100px" v-loading="loading">
|
||||||
|
<!-- 罪犯信息展示 -->
|
||||||
|
<el-form-item label="罪犯信息">
|
||||||
|
<el-descriptions :column="1" border size="small">
|
||||||
|
<el-descriptions-item label="罪犯编号">{{ prisoner.prisonerNo }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="姓名">{{ prisoner.name }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="当前监区">{{ prisoner.areaName }}</el-descriptions-item>
|
||||||
|
<el-descriptions-item label="当前监室">{{ prisoner.cellName }}</el-descriptions-item>
|
||||||
|
</el-descriptions>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="目标监区" prop="targetAreaId">
|
||||||
|
<el-select
|
||||||
|
v-model="formData.targetAreaId"
|
||||||
|
placeholder="请选择目标监区"
|
||||||
|
clearable
|
||||||
|
style="width: 100%"
|
||||||
|
@change="handleTargetAreaChange"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="area in areaList"
|
||||||
|
:key="area.id"
|
||||||
|
:label="area.name"
|
||||||
|
:value="area.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="目标分监区" prop="targetSubAreaId">
|
||||||
|
<el-select
|
||||||
|
v-model="formData.targetSubAreaId"
|
||||||
|
placeholder="请选择目标分监区"
|
||||||
|
clearable
|
||||||
|
style="width: 100%"
|
||||||
|
@change="handleTargetSubAreaChange"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="subArea in subAreaList"
|
||||||
|
:key="subArea.id"
|
||||||
|
:label="subArea.name"
|
||||||
|
:value="subArea.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="目标监室" prop="targetCellId">
|
||||||
|
<el-select
|
||||||
|
v-model="formData.targetCellId"
|
||||||
|
placeholder="请选择目标监室"
|
||||||
|
clearable
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="cell in cellList"
|
||||||
|
:key="cell.id"
|
||||||
|
:label="`${cell.name} (${cell.currentCount}/${cell.capacity})`"
|
||||||
|
:value="cell.id"
|
||||||
|
:disabled="cell.currentCount >= cell.capacity"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="调监原因" prop="reason">
|
||||||
|
<el-input v-model="formData.reason" type="textarea" :rows="3" placeholder="请输入调监原因" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="dialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" @click="handleSubmit" :loading="loading">确定</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import * as PrisonerApi from '@/api/prison/prisoner'
|
||||||
|
import { AreaApi } from '@/api/prison/area'
|
||||||
|
import { CellApi } from '@/api/prison/cell'
|
||||||
|
|
||||||
|
defineOptions({ name: 'PrisonTransferForm' })
|
||||||
|
|
||||||
|
const emit = defineEmits(['success'])
|
||||||
|
|
||||||
|
const dialogVisible = ref(false)
|
||||||
|
const loading = ref(false)
|
||||||
|
const formRef = ref()
|
||||||
|
const prisoner = ref<any>({})
|
||||||
|
const areaList = ref([])
|
||||||
|
const subAreaList = ref([])
|
||||||
|
const cellList = ref([])
|
||||||
|
|
||||||
|
const formData = reactive({
|
||||||
|
prisonerId: undefined as number | undefined,
|
||||||
|
targetAreaId: undefined as number | undefined,
|
||||||
|
targetSubAreaId: undefined as number | undefined,
|
||||||
|
targetCellId: undefined as number | undefined,
|
||||||
|
reason: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const rules = {
|
||||||
|
targetAreaId: [{ required: true, message: '请选择目标监区', trigger: 'change' }],
|
||||||
|
targetSubAreaId: [{ required: true, message: '请选择目标分监区', trigger: 'change' }],
|
||||||
|
targetCellId: [{ required: true, message: '请选择目标监室', trigger: 'change' }]
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 打开弹窗 */
|
||||||
|
const open = async (prisonerId: number) => {
|
||||||
|
dialogVisible.value = true
|
||||||
|
loading.value = true
|
||||||
|
resetForm()
|
||||||
|
try {
|
||||||
|
// 获取罪犯信息
|
||||||
|
prisoner.value = await PrisonerApi.getPrisoner(prisonerId)
|
||||||
|
formData.prisonerId = prisonerId
|
||||||
|
// 加载监区列表
|
||||||
|
const treeData = await AreaApi.getAreaTree()
|
||||||
|
areaList.value = treeData.filter((item: any) => item.level === 1 || item.parentId === 0)
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 目标监区变化 */
|
||||||
|
const handleTargetAreaChange = async (areaId: number) => {
|
||||||
|
formData.targetSubAreaId = undefined
|
||||||
|
formData.targetCellId = undefined
|
||||||
|
subAreaList.value = []
|
||||||
|
cellList.value = []
|
||||||
|
if (areaId) {
|
||||||
|
const treeData = await AreaApi.getAreaTree()
|
||||||
|
const area = findArea(treeData, areaId)
|
||||||
|
if (area && area.children) {
|
||||||
|
subAreaList.value = area.children
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 目标分监区变化 */
|
||||||
|
const handleTargetSubAreaChange = async (subAreaId: number) => {
|
||||||
|
formData.targetCellId = undefined
|
||||||
|
cellList.value = []
|
||||||
|
if (subAreaId) {
|
||||||
|
cellList.value = await CellApi.getCellPage({ areaId: subAreaId, pageNo: 1, pageSize: 200 }).then((res: any) => res.list || [])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 递归查找监区 */
|
||||||
|
const findArea = (areas: any[], id: number): any => {
|
||||||
|
for (const area of areas) {
|
||||||
|
if (area.id === id) return area
|
||||||
|
if (area.children) {
|
||||||
|
const found = findArea(area.children, id)
|
||||||
|
if (found) return found
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 重置表单 */
|
||||||
|
const resetForm = () => {
|
||||||
|
formData.prisonerId = undefined
|
||||||
|
formData.targetAreaId = undefined
|
||||||
|
formData.targetSubAreaId = undefined
|
||||||
|
formData.targetCellId = undefined
|
||||||
|
formData.reason = ''
|
||||||
|
prisoner.value = {}
|
||||||
|
subAreaList.value = []
|
||||||
|
cellList.value = []
|
||||||
|
formRef.value?.resetFields()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 提交 */
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
const valid = await formRef.value?.validate()
|
||||||
|
if (!valid) return
|
||||||
|
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
await PrisonerApi.doTransfer({
|
||||||
|
prisonerId: formData.prisonerId!,
|
||||||
|
targetCellId: formData.targetCellId!,
|
||||||
|
reason: formData.reason
|
||||||
|
})
|
||||||
|
ElMessage.success('调监成功')
|
||||||
|
dialogVisible.value = false
|
||||||
|
emit('success')
|
||||||
|
} catch {
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ open })
|
||||||
|
</script>
|
||||||
@ -8,10 +8,10 @@
|
|||||||
:inline="true"
|
:inline="true"
|
||||||
label-width="100px"
|
label-width="100px"
|
||||||
>
|
>
|
||||||
<el-form-item label="服刑人员编号" prop="prisonerNo">
|
<el-form-item label="罪犯编号" prop="prisonerNo">
|
||||||
<el-input
|
<el-input
|
||||||
v-model="queryParams.prisonerNo"
|
v-model="queryParams.prisonerNo"
|
||||||
placeholder="请输入服刑人员编号"
|
placeholder="请输入罪犯编号"
|
||||||
clearable
|
clearable
|
||||||
@keyup.enter="handleQuery"
|
@keyup.enter="handleQuery"
|
||||||
class="!w-240px"
|
class="!w-240px"
|
||||||
@ -71,6 +71,37 @@
|
|||||||
/>
|
/>
|
||||||
</el-select>
|
</el-select>
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
<el-form-item label="监区" prop="prisonAreaId">
|
||||||
|
<el-select
|
||||||
|
v-model="queryParams.prisonAreaId"
|
||||||
|
placeholder="请选择监区"
|
||||||
|
clearable
|
||||||
|
class="!w-240px"
|
||||||
|
@change="handleAreaChange"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="area in areaList"
|
||||||
|
:key="area.id"
|
||||||
|
:label="area.name"
|
||||||
|
:value="area.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</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-form-item label="状态" prop="status">
|
||||||
<el-select
|
<el-select
|
||||||
v-model="queryParams.status"
|
v-model="queryParams.status"
|
||||||
@ -95,7 +126,15 @@
|
|||||||
@click="openForm('create')"
|
@click="openForm('create')"
|
||||||
v-hasPermi="['prison:prisoner:create']"
|
v-hasPermi="['prison:prisoner:create']"
|
||||||
>
|
>
|
||||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
<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>
|
||||||
<el-button
|
<el-button
|
||||||
type="danger"
|
type="danger"
|
||||||
@ -123,33 +162,36 @@
|
|||||||
<ContentWrap>
|
<ContentWrap>
|
||||||
<el-table v-loading="loading" :data="list" @selection-change="handleRowCheckboxChange">
|
<el-table v-loading="loading" :data="list" @selection-change="handleRowCheckboxChange">
|
||||||
<el-table-column type="selection" width="55" />
|
<el-table-column type="selection" width="55" />
|
||||||
<el-table-column label="服刑人员编号" align="center" prop="prisonerNo" width="120" />
|
<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="name" width="100" />
|
||||||
<el-table-column label="性别" align="center" prop="gender" width="80">
|
<el-table-column label="性别" align="center" prop="genderName" width="80" />
|
||||||
|
<el-table-column label="身份证号" align="center" prop="idCard" width="180">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="scope.row.gender" />
|
{{ formatIdCard(scope.row.idCard) }}
|
||||||
</template>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="身份证号" align="center" prop="idCard" width="180" />
|
|
||||||
<el-table-column label="罪名" align="center" prop="crime" width="150" />
|
<el-table-column label="罪名" align="center" prop="crime" width="150" />
|
||||||
<el-table-column label="监管等级" align="center" prop="supervisionLevel" width="100">
|
<el-table-column label="刑期" align="center" width="120">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
<dict-tag :type="DICT_TYPE.PRISON_SUPERVISION_LEVEL" :value="scope.row.supervisionLevel" />
|
{{ formatSentence(scope.row) }}
|
||||||
</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>
|
</template>
|
||||||
</el-table-column>
|
</el-table-column>
|
||||||
<el-table-column label="入狱日期" align="center" prop="imprisonmentDate" width="120" />
|
<el-table-column label="入狱日期" align="center" prop="imprisonmentDate" width="120" />
|
||||||
<el-table-column label="状态" align="center" prop="status" width="100">
|
<el-table-column label="监管等级" align="center" prop="supervisionLevelName" width="100" />
|
||||||
<template #default="scope">
|
<el-table-column label="风险等级" align="center" prop="riskLevelName" width="100" />
|
||||||
<dict-tag :type="DICT_TYPE.PRISONER_STATUS" :value="scope.row.status" />
|
<el-table-column label="当前监区" align="center" prop="prisonAreaName" width="120" />
|
||||||
</template>
|
<el-table-column label="当前监室" align="center" prop="prisonCellName" width="100" />
|
||||||
</el-table-column>
|
<el-table-column label="状态" align="center" prop="statusName" width="100" />
|
||||||
<el-table-column label="操作" align="center" width="200">
|
<el-table-column label="操作" align="center" width="240">
|
||||||
<template #default="scope">
|
<template #default="scope">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
link
|
||||||
|
@click="handleDetail(scope.row)"
|
||||||
|
v-hasPermi="['prison:prisoner:query']"
|
||||||
|
>
|
||||||
|
详情
|
||||||
|
</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
type="primary"
|
type="primary"
|
||||||
link
|
link
|
||||||
@ -158,6 +200,14 @@
|
|||||||
>
|
>
|
||||||
修改
|
修改
|
||||||
</el-button>
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
type="warning"
|
||||||
|
link
|
||||||
|
@click="handleTransfer(scope.row)"
|
||||||
|
v-hasPermi="['prison:prisoner:transfer']"
|
||||||
|
>
|
||||||
|
调监
|
||||||
|
</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
type="danger"
|
type="danger"
|
||||||
link
|
link
|
||||||
@ -180,14 +230,25 @@
|
|||||||
|
|
||||||
<!-- 表单弹窗:添加/修改 -->
|
<!-- 表单弹窗:添加/修改 -->
|
||||||
<PrisonerForm ref="formRef" @success="getList" />
|
<PrisonerForm ref="formRef" @success="getList" />
|
||||||
|
|
||||||
|
<!-- 详情弹窗 -->
|
||||||
|
<PrisonerDetail ref="detailRef" />
|
||||||
|
|
||||||
|
<!-- 调监弹窗 -->
|
||||||
|
<TransferForm ref="transferRef" @success="getList" />
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||||
import { checkPermi } from '@/utils/permission'
|
import { checkPermi } from '@/utils/permission'
|
||||||
import PrisonerForm from './PrisonerForm.vue'
|
import PrisonerForm from './PrisonerForm.vue'
|
||||||
|
import PrisonerDetail from './PrisonerDetail.vue'
|
||||||
|
import TransferForm from './TransferForm.vue'
|
||||||
import download from '@/utils/download'
|
import download from '@/utils/download'
|
||||||
import * as PrisonerApi from '@/api/prison/prisoner'
|
import * as PrisonerApi from '@/api/prison/prisoner'
|
||||||
|
import { AreaApi } from '@/api/prison/area'
|
||||||
|
import { CellApi } from '@/api/prison/cell'
|
||||||
|
import { ref, reactive, onMounted } from 'vue'
|
||||||
|
|
||||||
defineOptions({ name: 'PrisonPrisoner' })
|
defineOptions({ name: 'PrisonPrisoner' })
|
||||||
|
|
||||||
@ -197,6 +258,8 @@ const message = useMessage() // 消息弹窗
|
|||||||
const loading = ref(true) // 列表的加载中
|
const loading = ref(true) // 列表的加载中
|
||||||
const total = ref(0) // 列表的总页数
|
const total = ref(0) // 列表的总页数
|
||||||
const list = ref([]) // 列表的数据
|
const list = ref([]) // 列表的数据
|
||||||
|
const areaList = ref([]) // 监区列表
|
||||||
|
const cellList = ref([]) // 监室列表
|
||||||
const queryParams = reactive({
|
const queryParams = reactive({
|
||||||
pageNo: 1,
|
pageNo: 1,
|
||||||
pageSize: 10,
|
pageSize: 10,
|
||||||
@ -214,6 +277,9 @@ const queryParams = reactive({
|
|||||||
})
|
})
|
||||||
const queryFormRef = ref() // 搜索的表单
|
const queryFormRef = ref() // 搜索的表单
|
||||||
const exportLoading = ref(false) // 导出的加载中
|
const exportLoading = ref(false) // 导出的加载中
|
||||||
|
const formRef = ref() // 表单
|
||||||
|
const detailRef = ref() // 详情
|
||||||
|
const transferRef = ref() // 调监表单
|
||||||
|
|
||||||
/** 查询列表 */
|
/** 查询列表 */
|
||||||
const getList = async () => {
|
const getList = async () => {
|
||||||
@ -236,6 +302,7 @@ const handleQuery = () => {
|
|||||||
/** 重置按钮操作 */
|
/** 重置按钮操作 */
|
||||||
const resetQuery = () => {
|
const resetQuery = () => {
|
||||||
queryFormRef.value.resetFields()
|
queryFormRef.value.resetFields()
|
||||||
|
queryParams.prisonCellId = undefined
|
||||||
handleQuery()
|
handleQuery()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -247,7 +314,7 @@ const handleExport = async () => {
|
|||||||
// 发起导出
|
// 发起导出
|
||||||
exportLoading.value = true
|
exportLoading.value = true
|
||||||
const data = await PrisonerApi.exportPrisoner(queryParams)
|
const data = await PrisonerApi.exportPrisoner(queryParams)
|
||||||
download.excel(data, '服刑人员信息.xls')
|
download.excel(data, '罪犯信息.xls')
|
||||||
} catch {
|
} catch {
|
||||||
} finally {
|
} finally {
|
||||||
exportLoading.value = false
|
exportLoading.value = false
|
||||||
@ -255,11 +322,65 @@ const handleExport = async () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** 添加/修改操作 */
|
/** 添加/修改操作 */
|
||||||
const formRef = ref()
|
|
||||||
const openForm = (type: string, id?: number) => {
|
const openForm = (type: string, id?: number) => {
|
||||||
formRef.value.open(type, id)
|
formRef.value.open(type, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 查看详情 */
|
||||||
|
const handleDetail = (row: any) => {
|
||||||
|
detailRef.value.open(row.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 打开调监弹窗 */
|
||||||
|
const openTransferForm = () => {
|
||||||
|
if (checkedIds.value.length !== 1) {
|
||||||
|
message.warning('请先选择一个罪犯进行调监')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
transferRef.value.open(checkedIds.value[0])
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 调监操作 */
|
||||||
|
const handleTransfer = (row: any) => {
|
||||||
|
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: any) => res.list || [])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 加载监区列表 */
|
||||||
|
const loadAreas = async () => {
|
||||||
|
areaList.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: any) => {
|
||||||
|
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) => {
|
const handleDelete = async (id: number) => {
|
||||||
try {
|
try {
|
||||||
@ -275,7 +396,7 @@ const handleDelete = async (id: number) => {
|
|||||||
|
|
||||||
/** 批量删除按钮操作 */
|
/** 批量删除按钮操作 */
|
||||||
const checkedIds = ref<number[]>([])
|
const checkedIds = ref<number[]>([])
|
||||||
const handleRowCheckboxChange = (rows: PrisonerApi.PrisonerVO[]) => {
|
const handleRowCheckboxChange = (rows: any[]) => {
|
||||||
checkedIds.value = rows.map((row) => row.id)
|
checkedIds.value = rows.map((row) => row.id)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -295,5 +416,6 @@ const handleDeleteBatch = async () => {
|
|||||||
/** 初始化 **/
|
/** 初始化 **/
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getList()
|
getList()
|
||||||
|
loadAreas()
|
||||||
})
|
})
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
224
src/views/prison/release/ReleaseForm.vue
Normal file
224
src/views/prison/release/ReleaseForm.vue
Normal file
@ -0,0 +1,224 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
v-model="dialogVisible"
|
||||||
|
:title="isDetail ? '释放登记详情' : (formData.id ? '编辑释放登记' : '新增释放登记')"
|
||||||
|
width="600px"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
>
|
||||||
|
<el-form ref="formRef" :model="formData" :rules="rules" label-width="120px" :disabled="isDetail">
|
||||||
|
<el-form-item label="罪犯" prop="prisonerId">
|
||||||
|
<el-select
|
||||||
|
v-model="formData.prisonerId"
|
||||||
|
placeholder="请选择罪犯"
|
||||||
|
filterable
|
||||||
|
remote
|
||||||
|
:remote-method="searchPrisoner"
|
||||||
|
:loading="prisonerLoading"
|
||||||
|
@change="handlePrisonerChange"
|
||||||
|
style="width: 100%"
|
||||||
|
>
|
||||||
|
<el-option
|
||||||
|
v-for="item in prisonerList"
|
||||||
|
:key="item.id"
|
||||||
|
:label="`${item.prisonerNo} - ${item.name}`"
|
||||||
|
:value="item.id"
|
||||||
|
/>
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="罪犯编号">
|
||||||
|
<el-input v-model="formData.prisonerNo" disabled />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="罪犯姓名">
|
||||||
|
<el-input v-model="formData.prisonerName" disabled />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="释放类型" prop="releaseType">
|
||||||
|
<el-select v-model="formData.releaseType" placeholder="请选择释放类型" style="width: 100%">
|
||||||
|
<el-option label="刑满释放" :value="1" />
|
||||||
|
<el-option label="假释" :value="2" />
|
||||||
|
<el-option label="暂予监外执行" :value="3" />
|
||||||
|
<el-option label="减刑" :value="4" />
|
||||||
|
<el-option label="法院裁定释放" :value="5" />
|
||||||
|
<el-option label="死亡" :value="6" />
|
||||||
|
<el-option label="其他" :value="7" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="释放原因" prop="releaseReason">
|
||||||
|
<el-input v-model="formData.releaseReason" type="textarea" :rows="2" placeholder="请输入释放原因" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="实际释放日期" prop="actualReleaseDate">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="formData.actualReleaseDate"
|
||||||
|
type="date"
|
||||||
|
placeholder="请选择释放日期"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
style="width: 100%"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
<template v-if="formData.releaseType === 2 || formData.releaseType === 4">
|
||||||
|
<el-form-item label="裁定法院">
|
||||||
|
<el-input v-model="formData.courtName" placeholder="请输入裁定法院" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="裁定书编号">
|
||||||
|
<el-input v-model="formData.judgmentNo" placeholder="请输入裁定书编号" />
|
||||||
|
</el-form-item>
|
||||||
|
</template>
|
||||||
|
<template v-if="formData.releaseType === 5">
|
||||||
|
<el-form-item label="裁定法院">
|
||||||
|
<el-input v-model="formData.courtName" placeholder="请输入裁定法院" />
|
||||||
|
</el-form-item>
|
||||||
|
</template>
|
||||||
|
<el-form-item label="交接人">
|
||||||
|
<el-input v-model="formData.handoverPerson" placeholder="请输入交接人" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="交接单位">
|
||||||
|
<el-input v-model="formData.handoverUnit" placeholder="请输入交接单位" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="证件类型">
|
||||||
|
<el-select v-model="formData.certificateType" placeholder="请选择证件类型" style="width: 100%">
|
||||||
|
<el-option label="身份证" :value="1" />
|
||||||
|
<el-option label="户口簿" :value="2" />
|
||||||
|
<el-option label="其他" :value="3" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="证件号码">
|
||||||
|
<el-input v-model="formData.certificateNo" placeholder="请输入证件号码" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="备注">
|
||||||
|
<el-input v-model="formData.remark" type="textarea" :rows="2" placeholder="请输入备注" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="dialogVisible = false">关闭</el-button>
|
||||||
|
<el-button v-if="!isDetail" type="primary" :loading="submitLoading" @click="handleSubmit">
|
||||||
|
确定
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, reactive } from 'vue'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { ReleaseApi } from '@/api/prison/release'
|
||||||
|
import { PrisonerApi } from '@/api/prison/prisoner'
|
||||||
|
|
||||||
|
defineOptions({ name: 'PrisonReleaseForm' })
|
||||||
|
|
||||||
|
const emit = defineEmits(['success'])
|
||||||
|
|
||||||
|
const dialogVisible = ref(false)
|
||||||
|
const isDetail = ref(false)
|
||||||
|
const submitLoading = ref(false)
|
||||||
|
const prisonerLoading = ref(false)
|
||||||
|
const prisonerList = ref<any[]>([])
|
||||||
|
|
||||||
|
const formData = reactive({
|
||||||
|
id: undefined as number | undefined,
|
||||||
|
prisonerId: undefined as number | undefined,
|
||||||
|
prisonerNo: '',
|
||||||
|
prisonerName: '',
|
||||||
|
releaseType: undefined as number | undefined,
|
||||||
|
releaseReason: '',
|
||||||
|
courtName: '',
|
||||||
|
judgmentNo: '',
|
||||||
|
actualReleaseDate: '',
|
||||||
|
handoverPerson: '',
|
||||||
|
handoverUnit: '',
|
||||||
|
certificateType: undefined as number | undefined,
|
||||||
|
certificateNo: '',
|
||||||
|
remark: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
const rules = {
|
||||||
|
prisonerId: [{ required: true, message: '请选择罪犯', trigger: 'change' }],
|
||||||
|
releaseType: [{ required: true, message: '请选择释放类型', trigger: 'change' }],
|
||||||
|
actualReleaseDate: [{ required: true, message: '请选择释放日期', trigger: 'change' }]
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 打开弹窗 */
|
||||||
|
const open = (id?: number, detail = false) => {
|
||||||
|
dialogVisible.value = true
|
||||||
|
isDetail.value = detail
|
||||||
|
resetForm()
|
||||||
|
if (id) {
|
||||||
|
loadData(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 重置表单 */
|
||||||
|
const resetForm = () => {
|
||||||
|
formData.id = undefined
|
||||||
|
formData.prisonerId = undefined
|
||||||
|
formData.prisonerNo = ''
|
||||||
|
formData.prisonerName = ''
|
||||||
|
formData.releaseType = undefined
|
||||||
|
formData.releaseReason = ''
|
||||||
|
formData.courtName = ''
|
||||||
|
formData.judgmentNo = ''
|
||||||
|
formData.actualReleaseDate = ''
|
||||||
|
formData.handoverPerson = ''
|
||||||
|
formData.handoverUnit = ''
|
||||||
|
formData.certificateType = undefined
|
||||||
|
formData.certificateNo = ''
|
||||||
|
formData.remark = ''
|
||||||
|
prisonerList.value = []
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 加载数据 */
|
||||||
|
const loadData = async (id: number) => {
|
||||||
|
const data = await ReleaseApi.getRelease(id)
|
||||||
|
Object.assign(formData, data)
|
||||||
|
// 加载罪犯信息用于显示
|
||||||
|
prisonerList.value = [{
|
||||||
|
id: data.prisonerId,
|
||||||
|
prisonerNo: data.prisonerNo,
|
||||||
|
name: data.prisonerName
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 搜索罪犯 */
|
||||||
|
const searchPrisoner = async (keyword: string) => {
|
||||||
|
if (!keyword) return
|
||||||
|
prisonerLoading.value = true
|
||||||
|
try {
|
||||||
|
const list = await PrisonerApi.getPrisonerPage({
|
||||||
|
prisonerNo: keyword,
|
||||||
|
name: keyword,
|
||||||
|
pageNo: 1,
|
||||||
|
pageSize: 10
|
||||||
|
})
|
||||||
|
prisonerList.value = list.list || []
|
||||||
|
} finally {
|
||||||
|
prisonerLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 罪犯选择变化 */
|
||||||
|
const handlePrisonerChange = (val: number) => {
|
||||||
|
const prisoner = prisonerList.value.find((item: any) => item.id === val)
|
||||||
|
if (prisoner) {
|
||||||
|
formData.prisonerNo = prisoner.prisonerNo
|
||||||
|
formData.prisonerName = prisoner.name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 提交 */
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
submitLoading.value = true
|
||||||
|
try {
|
||||||
|
if (formData.id) {
|
||||||
|
await ReleaseApi.updateRelease(formData)
|
||||||
|
ElMessage.success('更新成功')
|
||||||
|
} else {
|
||||||
|
await ReleaseApi.createRelease(formData)
|
||||||
|
ElMessage.success('创建成功')
|
||||||
|
}
|
||||||
|
dialogVisible.value = false
|
||||||
|
emit('success')
|
||||||
|
} finally {
|
||||||
|
submitLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ open })
|
||||||
|
</script>
|
||||||
302
src/views/prison/release/index.vue
Normal file
302
src/views/prison/release/index.vue
Normal file
@ -0,0 +1,302 @@
|
|||||||
|
<template>
|
||||||
|
<div class="release-page">
|
||||||
|
<!-- 搜索表单 -->
|
||||||
|
<el-card shadow="never" class="search-card">
|
||||||
|
<el-form ref="searchFormRef" :model="searchForm" label-width="100px">
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="6">
|
||||||
|
<el-form-item label="罪犯编号" prop="prisonerNo">
|
||||||
|
<el-input v-model="searchForm.prisonerNo" placeholder="请输入罪犯编号" clearable />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="6">
|
||||||
|
<el-form-item label="罪犯姓名" prop="prisonerName">
|
||||||
|
<el-input v-model="searchForm.prisonerName" placeholder="请输入罪犯姓名" clearable />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="6">
|
||||||
|
<el-form-item label="释放类型" prop="releaseType">
|
||||||
|
<el-select v-model="searchForm.releaseType" placeholder="请选择" clearable>
|
||||||
|
<el-option label="刑满释放" :value="1" />
|
||||||
|
<el-option label="假释" :value="2" />
|
||||||
|
<el-option label="暂予监外执行" :value="3" />
|
||||||
|
<el-option label="减刑" :value="4" />
|
||||||
|
<el-option label="法院裁定释放" :value="5" />
|
||||||
|
<el-option label="死亡" :value="6" />
|
||||||
|
<el-option label="其他" :value="7" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="6">
|
||||||
|
<el-form-item label="状态" prop="status">
|
||||||
|
<el-select v-model="searchForm.status" placeholder="请选择" clearable>
|
||||||
|
<el-option label="待释放" :value="1" />
|
||||||
|
<el-option label="已释放" :value="2" />
|
||||||
|
<el-option label="已取消" :value="3" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item label="释放日期">
|
||||||
|
<el-date-picker
|
||||||
|
v-model="dateRange"
|
||||||
|
type="daterange"
|
||||||
|
range-separator="至"
|
||||||
|
start-placeholder="开始日期"
|
||||||
|
end-placeholder="结束日期"
|
||||||
|
value-format="YYYY-MM-DD"
|
||||||
|
@change="handleDateChange"
|
||||||
|
/>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="12">
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="handleSearch">
|
||||||
|
<Icon icon="ep:search" class="mr-5px" /> 搜索
|
||||||
|
</el-button>
|
||||||
|
<el-button @click="handleReset">
|
||||||
|
<Icon icon="ep:refresh" class="mr-5px" /> 重置
|
||||||
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-form>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<!-- 数据表格 -->
|
||||||
|
<el-card shadow="never">
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span>释放登记列表</span>
|
||||||
|
<el-button type="primary" @click="handleCreate" v-hasPermi="'prison:release:create'">
|
||||||
|
<Icon icon="ep:plus" class="mr-5px" /> 新增释放登记
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<el-table v-loading="loading" :data="tableData" border stripe>
|
||||||
|
<el-table-column prop="prisonerNo" label="罪犯编号" width="120" />
|
||||||
|
<el-table-column prop="prisonerName" label="罪犯姓名" width="100" />
|
||||||
|
<el-table-column prop="releaseTypeName" label="释放类型" width="120" />
|
||||||
|
<el-table-column prop="releaseReason" label="释放原因" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="actualReleaseDate" label="实际释放日期" width="120" />
|
||||||
|
<el-table-column prop="handoverPerson" label="交接人" width="100" />
|
||||||
|
<el-table-column prop="handoverUnit" label="交接单位" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="statusName" label="状态" width="100">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tag :type="getStatusType(row.status)">
|
||||||
|
{{ row.statusName }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="operatorName" label="操作人" width="100" />
|
||||||
|
<el-table-column prop="createTime" label="创建时间" width="160" />
|
||||||
|
<el-table-column label="操作" width="200" fixed="right">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-button link type="primary" @click="handleDetail(row)" v-hasPermi="'prison:release:query'">
|
||||||
|
详情
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
@click="handleEdit(row)"
|
||||||
|
v-if="row.status === 1"
|
||||||
|
v-hasPermi="'prison:release:update'"
|
||||||
|
>
|
||||||
|
编辑
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
link
|
||||||
|
type="success"
|
||||||
|
@click="handleDoRelease(row)"
|
||||||
|
v-if="row.status === 1"
|
||||||
|
v-hasPermi="'prison:release:update'"
|
||||||
|
>
|
||||||
|
执行释放
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
link
|
||||||
|
type="warning"
|
||||||
|
@click="handleCancel(row)"
|
||||||
|
v-if="row.status === 1"
|
||||||
|
v-hasPermi="'prison:release:update'"
|
||||||
|
>
|
||||||
|
取消
|
||||||
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
link
|
||||||
|
type="danger"
|
||||||
|
@click="handleDelete(row)"
|
||||||
|
v-if="row.status === 1"
|
||||||
|
v-hasPermi="'prison:release:delete'"
|
||||||
|
>
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<Pagination
|
||||||
|
:total="total"
|
||||||
|
v-model:page="searchForm.pageNo"
|
||||||
|
v-model:limit="searchForm.pageSize"
|
||||||
|
@pagination="getPage"
|
||||||
|
/>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<!-- 释放登记表单弹窗 -->
|
||||||
|
<ReleaseForm ref="formRef" @success="getPage" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, reactive, onMounted } from 'vue'
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||||
|
import ReleaseForm from './ReleaseForm.vue'
|
||||||
|
import { ReleaseApi, type ReleasePageReqVO } from '@/api/prison/release'
|
||||||
|
|
||||||
|
defineOptions({ name: 'PrisonRelease' })
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
|
const tableData = ref([])
|
||||||
|
const total = ref(0)
|
||||||
|
const dateRange = ref([])
|
||||||
|
|
||||||
|
const searchForm = reactive<ReleasePageReqVO>({
|
||||||
|
prisonerNo: '',
|
||||||
|
prisonerName: '',
|
||||||
|
releaseType: undefined,
|
||||||
|
status: undefined,
|
||||||
|
actualReleaseDateStart: '',
|
||||||
|
actualReleaseDateEnd: '',
|
||||||
|
pageNo: 1,
|
||||||
|
pageSize: 20
|
||||||
|
})
|
||||||
|
|
||||||
|
const formRef = ref()
|
||||||
|
|
||||||
|
/** 获取分页数据 */
|
||||||
|
const getPage = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const data = await ReleaseApi.getReleasePage(searchForm)
|
||||||
|
tableData.value = data.list
|
||||||
|
total.value = data.total
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 搜索 */
|
||||||
|
const handleSearch = () => {
|
||||||
|
searchForm.pageNo = 1
|
||||||
|
getPage()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 重置 */
|
||||||
|
const handleReset = () => {
|
||||||
|
searchForm.prisonerNo = ''
|
||||||
|
searchForm.prisonerName = ''
|
||||||
|
searchForm.releaseType = undefined
|
||||||
|
searchForm.status = undefined
|
||||||
|
searchForm.actualReleaseDateStart = ''
|
||||||
|
searchForm.actualReleaseDateEnd = ''
|
||||||
|
dateRange.value = []
|
||||||
|
getPage()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 日期范围变化 */
|
||||||
|
const handleDateChange = (val: any) => {
|
||||||
|
if (val) {
|
||||||
|
searchForm.actualReleaseDateStart = val[0]
|
||||||
|
searchForm.actualReleaseDateEnd = val[1]
|
||||||
|
} else {
|
||||||
|
searchForm.actualReleaseDateStart = ''
|
||||||
|
searchForm.actualReleaseDateEnd = ''
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 新增 */
|
||||||
|
const handleCreate = () => {
|
||||||
|
formRef.value.open()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 编辑 */
|
||||||
|
const handleEdit = (row: any) => {
|
||||||
|
formRef.value.open(row.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 详情 */
|
||||||
|
const handleDetail = (row: any) => {
|
||||||
|
formRef.value.open(row.id, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 执行释放 */
|
||||||
|
const handleDoRelease = (row: any) => {
|
||||||
|
ElMessageBox.confirm(`确认要执行释放罪犯【${row.prisonerName}】吗?`, '操作确认', {
|
||||||
|
type: 'warning'
|
||||||
|
}).then(async () => {
|
||||||
|
await ReleaseApi.doRelease(row.id)
|
||||||
|
ElMessage.success('执行释放成功')
|
||||||
|
getPage()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 取消释放 */
|
||||||
|
const handleCancel = (row: any) => {
|
||||||
|
ElMessageBox.confirm(`确认要取消释放登记吗?`, '操作确认', {
|
||||||
|
type: 'warning'
|
||||||
|
}).then(async () => {
|
||||||
|
await ReleaseApi.cancelRelease(row.id)
|
||||||
|
ElMessage.success('取消释放成功')
|
||||||
|
getPage()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除 */
|
||||||
|
const handleDelete = (row: any) => {
|
||||||
|
ElMessageBox.confirm(`确认要删除该释放登记吗?`, '操作确认', {
|
||||||
|
type: 'warning'
|
||||||
|
}).then(async () => {
|
||||||
|
await ReleaseApi.deleteRelease(row.id)
|
||||||
|
ElMessage.success('删除成功')
|
||||||
|
getPage()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 状态样式 */
|
||||||
|
const getStatusType = (status: number) => {
|
||||||
|
switch (status) {
|
||||||
|
case 1:
|
||||||
|
return 'info'
|
||||||
|
case 2:
|
||||||
|
return 'success'
|
||||||
|
case 3:
|
||||||
|
return 'warning'
|
||||||
|
default:
|
||||||
|
return 'info'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getPage()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.release-page {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-card {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
134
src/views/prison/score-rule/ScoreRuleForm.vue
Normal file
134
src/views/prison/score-rule/ScoreRuleForm.vue
Normal file
@ -0,0 +1,134 @@
|
|||||||
|
<template>
|
||||||
|
<el-dialog
|
||||||
|
v-model="dialogVisible"
|
||||||
|
:title="formData.id ? '编辑考核规则' : '新增考核规则'"
|
||||||
|
width="500px"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
>
|
||||||
|
<el-form ref="formRef" :model="formData" :rules="rules" label-width="100px">
|
||||||
|
<el-form-item label="考核类别" prop="category">
|
||||||
|
<el-select v-model="formData.category" placeholder="请选择考核类别" style="width: 100%">
|
||||||
|
<el-option label="劳动改造" :value="1" />
|
||||||
|
<el-option label="教育改造" :value="2" />
|
||||||
|
<el-option label="日常行为" :value="3" />
|
||||||
|
<el-option label="卫生纪律" :value="4" />
|
||||||
|
<el-option label="加分项" :value="5" />
|
||||||
|
<el-option label="扣分项" :value="6" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="项目名称" prop="itemName">
|
||||||
|
<el-input v-model="formData.itemName" placeholder="请输入项目名称" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="项目编码" prop="itemCode">
|
||||||
|
<el-input v-model="formData.itemCode" placeholder="请输入项目编码(唯一)" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="分值" prop="score">
|
||||||
|
<el-input-number v-model="formData.score" :precision="2" :step="0.5" :min="-100" :max="100" style="width: 100%" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="日最高分">
|
||||||
|
<el-input-number v-model="formData.maxDailyScore" :precision="2" :step="0.5" :min="0" :max="100" style="width: 100%" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="月最高分">
|
||||||
|
<el-input-number v-model="formData.maxMonthlyScore" :precision="2" :step="0.5" :min="0" :max="500" style="width: 100%" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="规则说明">
|
||||||
|
<el-input v-model="formData.description" type="textarea" :rows="3" placeholder="请输入规则说明" />
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="状态" prop="status">
|
||||||
|
<el-radio-group v-model="formData.status">
|
||||||
|
<el-radio :label="1">启用</el-radio>
|
||||||
|
<el-radio :label="2">禁用</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
</el-form-item>
|
||||||
|
<el-form-item label="排序">
|
||||||
|
<el-input-number v-model="formData.sort" :min="0" :max="999" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="dialogVisible = false">取消</el-button>
|
||||||
|
<el-button type="primary" :loading="submitLoading" @click="handleSubmit">确定</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, reactive } from 'vue'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import { ScoreRuleApi, type ScoreRuleSaveReqVO } from '@/api/prison/score-rule'
|
||||||
|
|
||||||
|
defineOptions({ name: 'PrisonScoreRuleForm' })
|
||||||
|
|
||||||
|
const emit = defineEmits(['success'])
|
||||||
|
|
||||||
|
const dialogVisible = ref(false)
|
||||||
|
const submitLoading = ref(false)
|
||||||
|
|
||||||
|
const formData = reactive<ScoreRuleSaveReqVO>({
|
||||||
|
id: undefined,
|
||||||
|
category: undefined,
|
||||||
|
itemName: '',
|
||||||
|
itemCode: '',
|
||||||
|
score: 0,
|
||||||
|
maxDailyScore: undefined,
|
||||||
|
maxMonthlyScore: undefined,
|
||||||
|
description: '',
|
||||||
|
status: 1,
|
||||||
|
sort: 0
|
||||||
|
})
|
||||||
|
|
||||||
|
const rules = {
|
||||||
|
category: [{ required: true, message: '请选择考核类别', trigger: 'change' }],
|
||||||
|
itemName: [{ required: true, message: '请输入项目名称', trigger: 'blur' }],
|
||||||
|
itemCode: [{ required: true, message: '请输入项目编码', trigger: 'blur' }],
|
||||||
|
score: [{ required: true, message: '请输入分值', trigger: 'blur' }]
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 打开弹窗 */
|
||||||
|
const open = (id?: number) => {
|
||||||
|
dialogVisible.value = true
|
||||||
|
resetForm()
|
||||||
|
if (id) {
|
||||||
|
loadData(id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 重置表单 */
|
||||||
|
const resetForm = () => {
|
||||||
|
formData.id = undefined
|
||||||
|
formData.category = undefined
|
||||||
|
formData.itemName = ''
|
||||||
|
formData.itemCode = ''
|
||||||
|
formData.score = 0
|
||||||
|
formData.maxDailyScore = undefined
|
||||||
|
formData.maxMonthlyScore = undefined
|
||||||
|
formData.description = ''
|
||||||
|
formData.status = 1
|
||||||
|
formData.sort = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 加载数据 */
|
||||||
|
const loadData = async (id: number) => {
|
||||||
|
const data = await ScoreRuleApi.getRule(id)
|
||||||
|
Object.assign(formData, data)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 提交 */
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
submitLoading.value = true
|
||||||
|
try {
|
||||||
|
if (formData.id) {
|
||||||
|
await ScoreRuleApi.updateRule(formData)
|
||||||
|
ElMessage.success('更新成功')
|
||||||
|
} else {
|
||||||
|
await ScoreRuleApi.createRule(formData)
|
||||||
|
ElMessage.success('创建成功')
|
||||||
|
}
|
||||||
|
dialogVisible.value = false
|
||||||
|
emit('success')
|
||||||
|
} finally {
|
||||||
|
submitLoading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
defineExpose({ open })
|
||||||
|
</script>
|
||||||
200
src/views/prison/score-rule/index.vue
Normal file
200
src/views/prison/score-rule/index.vue
Normal file
@ -0,0 +1,200 @@
|
|||||||
|
<template>
|
||||||
|
<div class="score-rule-page">
|
||||||
|
<!-- 搜索表单 -->
|
||||||
|
<el-card shadow="never" class="search-card">
|
||||||
|
<el-form ref="searchFormRef" :model="searchForm" label-width="100px">
|
||||||
|
<el-row :gutter="20">
|
||||||
|
<el-col :span="6">
|
||||||
|
<el-form-item label="考核类别" prop="category">
|
||||||
|
<el-select v-model="searchForm.category" placeholder="请选择" clearable>
|
||||||
|
<el-option label="劳动改造" :value="1" />
|
||||||
|
<el-option label="教育改造" :value="2" />
|
||||||
|
<el-option label="日常行为" :value="3" />
|
||||||
|
<el-option label="卫生纪律" :value="4" />
|
||||||
|
<el-option label="加分项" :value="5" />
|
||||||
|
<el-option label="扣分项" :value="6" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="6">
|
||||||
|
<el-form-item label="项目名称" prop="itemName">
|
||||||
|
<el-input v-model="searchForm.itemName" placeholder="请输入项目名称" clearable />
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="6">
|
||||||
|
<el-form-item label="状态" prop="status">
|
||||||
|
<el-select v-model="searchForm.status" placeholder="请选择" clearable>
|
||||||
|
<el-option label="启用" :value="1" />
|
||||||
|
<el-option label="禁用" :value="2" />
|
||||||
|
</el-select>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="6">
|
||||||
|
<el-form-item>
|
||||||
|
<el-button type="primary" @click="handleSearch">
|
||||||
|
<Icon icon="ep:search" class="mr-5px" /> 搜索
|
||||||
|
</el-button>
|
||||||
|
<el-button @click="handleReset">
|
||||||
|
<Icon icon="ep:refresh" class="mr-5px" /> 重置
|
||||||
|
</el-button>
|
||||||
|
</el-form-item>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-form>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<!-- 数据表格 -->
|
||||||
|
<el-card shadow="never">
|
||||||
|
<template #header>
|
||||||
|
<div class="card-header">
|
||||||
|
<span>考核规则配置</span>
|
||||||
|
<el-button type="primary" @click="handleCreate" v-hasPermi="'prison:score-rule:create'">
|
||||||
|
<Icon icon="ep:plus" class="mr-5px" /> 新增规则
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<el-table v-loading="loading" :data="tableData" border stripe>
|
||||||
|
<el-table-column prop="categoryName" label="考核类别" width="120" />
|
||||||
|
<el-table-column prop="itemName" label="项目名称" width="180" />
|
||||||
|
<el-table-column prop="itemCode" label="项目编码" width="120" />
|
||||||
|
<el-table-column prop="score" label="分值" width="100">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<span :class="{ 'text-danger': row.score < 0, 'text-success': row.score > 0 }">
|
||||||
|
{{ row.score > 0 ? '+' : '' }}{{ row.score }}
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="maxDailyScore" label="日最高分" width="100" />
|
||||||
|
<el-table-column prop="maxMonthlyScore" label="月最高分" width="100" />
|
||||||
|
<el-table-column prop="description" label="规则说明" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="statusName" label="状态" width="100">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-tag :type="row.status === 1 ? 'success' : 'danger'">
|
||||||
|
{{ row.statusName }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="sort" label="排序" width="80" />
|
||||||
|
<el-table-column label="操作" width="150" fixed="right">
|
||||||
|
<template #default="{ row }">
|
||||||
|
<el-button link type="primary" @click="handleEdit(row)" v-hasPermi="'prison:score-rule:update'">
|
||||||
|
编辑
|
||||||
|
</el-button>
|
||||||
|
<el-button link type="danger" @click="handleDelete(row)" v-hasPermi="'prison:score-rule:delete'">
|
||||||
|
删除
|
||||||
|
</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<Pagination
|
||||||
|
:total="total"
|
||||||
|
v-model:page="searchForm.pageNo"
|
||||||
|
v-model:limit="searchForm.pageSize"
|
||||||
|
@pagination="getPage"
|
||||||
|
/>
|
||||||
|
</el-card>
|
||||||
|
|
||||||
|
<!-- 规则表单弹窗 -->
|
||||||
|
<ScoreRuleForm ref="formRef" @success="getPage" />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, reactive, onMounted } from 'vue'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
import ScoreRuleForm from './ScoreRuleForm.vue'
|
||||||
|
import { ScoreRuleApi, type ScoreRulePageReqVO } from '@/api/prison/score-rule'
|
||||||
|
|
||||||
|
defineOptions({ name: 'PrisonScoreRule' })
|
||||||
|
|
||||||
|
const loading = ref(false)
|
||||||
|
const tableData = ref([])
|
||||||
|
const total = ref(0)
|
||||||
|
|
||||||
|
const searchForm = reactive<ScoreRulePageReqVO>({
|
||||||
|
category: undefined,
|
||||||
|
itemName: '',
|
||||||
|
status: undefined,
|
||||||
|
pageNo: 1,
|
||||||
|
pageSize: 20
|
||||||
|
})
|
||||||
|
|
||||||
|
const formRef = ref()
|
||||||
|
|
||||||
|
/** 获取分页数据 */
|
||||||
|
const getPage = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const data = await ScoreRuleApi.getRulePage(searchForm)
|
||||||
|
tableData.value = data.list
|
||||||
|
total.value = data.total
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 搜索 */
|
||||||
|
const handleSearch = () => {
|
||||||
|
searchForm.pageNo = 1
|
||||||
|
getPage()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 重置 */
|
||||||
|
const handleReset = () => {
|
||||||
|
searchForm.category = undefined
|
||||||
|
searchForm.itemName = ''
|
||||||
|
searchForm.status = undefined
|
||||||
|
getPage()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 新增 */
|
||||||
|
const handleCreate = () => {
|
||||||
|
formRef.value.open()
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 编辑 */
|
||||||
|
const handleEdit = (row: any) => {
|
||||||
|
formRef.value.open(row.id)
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 删除 */
|
||||||
|
const handleDelete = (row: any) => {
|
||||||
|
ElMessageBox.confirm(`确认要删除规则【${row.itemName}】吗?`, '操作确认', {
|
||||||
|
type: 'warning'
|
||||||
|
}).then(async () => {
|
||||||
|
await ScoreRuleApi.deleteRule(row.id)
|
||||||
|
ElMessage.success('删除成功')
|
||||||
|
getPage()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getPage()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped>
|
||||||
|
.score-rule-page {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-card {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.card-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-danger {
|
||||||
|
color: #f56c6c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.text-success {
|
||||||
|
color: #67c23a;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Loading…
x
Reference in New Issue
Block a user