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 {
|
||||
id: number; // 监区ID
|
||||
name?: string; // 监区名称
|
||||
code?: string; // 监区编码
|
||||
type: number; // 监区类型:1-普通监区 2-严管监区 3-医院 4-禁闭室
|
||||
capacity: number; // 容纳人数
|
||||
currentCount: number; // 当前人数
|
||||
sort: number; // 排序
|
||||
status?: number; // 状态:1-启用 2-禁用
|
||||
remark: string; // 备注
|
||||
}
|
||||
id: number // 监区ID
|
||||
name?: string // 监区名称
|
||||
code?: string // 监区编码
|
||||
parentId?: number // 父级ID,0表示顶级监区
|
||||
level?: number // 级别:1-监区(大队) 2-分监区(中队)
|
||||
type: number // 监区类型:1-普通监区 2-严管监区 3-集训监区 4-出监监区 5-医院监区 6-禁闭室
|
||||
capacity: number // 容纳人数
|
||||
currentCount: number // 当前人数
|
||||
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
|
||||
export const AreaApi = {
|
||||
// 查询监区信息分页
|
||||
getAreaPage: async (params: any) => {
|
||||
getAreaPage: async (params: AreaPageReqVO) => {
|
||||
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 })
|
||||
},
|
||||
|
||||
// 查询监区树形结构
|
||||
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 })
|
||||
},
|
||||
|
||||
// 修改监区信息
|
||||
updateArea: async (data: Area) => {
|
||||
updateArea: async (data: AreaSaveReqVO) => {
|
||||
return await request.put({ url: `/prison/area/update`, data })
|
||||
},
|
||||
|
||||
@ -49,5 +104,10 @@ export const AreaApi = {
|
||||
// 导出监区信息 Excel
|
||||
exportArea: async (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; // 排序
|
||||
status?: number; // 状态:1-启用 2-禁用
|
||||
remark: string; // 备注
|
||||
createTime?: Date; // 创建时间
|
||||
}
|
||||
|
||||
// 监室信息 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
|
||||
name: string
|
||||
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
|
||||
idCard: string
|
||||
ethnicity: string
|
||||
@ -15,15 +60,21 @@ export interface PrisonerVO {
|
||||
crime: string
|
||||
sentenceYears: number
|
||||
sentenceMonths: number
|
||||
lifeImprisonment: number
|
||||
deathSentenceReprieve: number
|
||||
courtName: string
|
||||
judgmentDate: string
|
||||
judgmentNo: string
|
||||
originalSentence: string
|
||||
imprisonmentDate: string
|
||||
releaseDate: string
|
||||
supervisionLevel: number
|
||||
riskLevel: number
|
||||
prisonAreaId: number
|
||||
subAreaId?: number // 分监区ID
|
||||
prisonCellId: number
|
||||
status: number
|
||||
photo?: string // 照片URL
|
||||
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 })
|
||||
}
|
||||
|
||||
// 修改服刑人员
|
||||
export const updatePrisoner = (data: PrisonerVO) => {
|
||||
export const updatePrisoner = (data: PrisonerCreateVO) => {
|
||||
return request.put({ url: '/prison/prisoner/update', data })
|
||||
}
|
||||
|
||||
@ -60,3 +111,61 @@ export const deletePrisonerList = (ids: number[]) => {
|
||||
export const exportPrisoner = (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 type { EChartsType } from 'echarts/core'
|
||||
|
||||
import {
|
||||
BarChart,
|
||||
@ -48,4 +49,23 @@ echarts.use([
|
||||
FunnelChart
|
||||
])
|
||||
|
||||
// 导出 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')
|
||||
}
|
||||
]
|
||||
},
|
||||
// 监管看板路由(开发测试用,上线后由后端菜单动态生成)
|
||||
{
|
||||
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_RECORD_PASS_STATUS = 'prison_record_pass_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>
|
||||
<Dialog :title="dialogTitle" v-model="dialogVisible">
|
||||
<Dialog :title="dialogTitle" v-model="dialogVisible" width="600px">
|
||||
<el-form
|
||||
ref="formRef"
|
||||
:model="formData"
|
||||
@ -13,6 +13,32 @@
|
||||
<el-form-item label="监区编码" prop="code">
|
||||
<el-input v-model="formData.code" placeholder="请输入监区编码" />
|
||||
</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-select v-model="formData.type" placeholder="请选择监区类型">
|
||||
<el-option
|
||||
@ -23,15 +49,18 @@
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="容纳人数" prop="capacity">
|
||||
<el-input v-model="formData.capacity" placeholder="请输入容纳人数" />
|
||||
</el-form-item>
|
||||
<el-form-item label="当前人数" prop="currentCount">
|
||||
<el-input v-model="formData.currentCount" placeholder="请输入当前人数" />
|
||||
</el-form-item>
|
||||
<el-form-item label="排序" prop="sort">
|
||||
<el-input v-model="formData.sort" placeholder="请输入排序" />
|
||||
</el-form-item>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="容纳人数" prop="capacity">
|
||||
<el-input-number v-model="formData.capacity" :min="0" placeholder="请输入容纳人数" controls-position="right" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="排序" prop="sort">
|
||||
<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-radio-group v-model="formData.status">
|
||||
<el-radio
|
||||
@ -42,7 +71,7 @@
|
||||
</el-radio-group>
|
||||
</el-form-item>
|
||||
<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>
|
||||
<template #footer>
|
||||
@ -51,9 +80,10 @@
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { AreaApi, Area } from '@/api/prison/area'
|
||||
import { AreaApi, AreaSaveReqVO } from '@/api/prison/area'
|
||||
|
||||
/** 监区信息 表单 */
|
||||
defineOptions({ name: 'AreaForm' })
|
||||
@ -65,35 +95,84 @@ const dialogVisible = ref(false) // 弹窗的是否展示
|
||||
const dialogTitle = ref('') // 弹窗的标题
|
||||
const formLoading = ref(false) // 表单的加载中:1)修改时的数据加载;2)提交的按钮禁用
|
||||
const formType = ref('') // 表单的类型:create - 新增;update - 修改
|
||||
|
||||
const formData = ref({
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
code: undefined,
|
||||
type: undefined,
|
||||
capacity: undefined,
|
||||
currentCount: undefined,
|
||||
sort: undefined,
|
||||
status: undefined,
|
||||
parentId: undefined as number | undefined,
|
||||
level: 1,
|
||||
type: 1,
|
||||
capacity: 0,
|
||||
sort: 0,
|
||||
status: 1,
|
||||
remark: undefined
|
||||
})
|
||||
|
||||
const formRules = reactive({
|
||||
name: [{ 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' }]
|
||||
})
|
||||
|
||||
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) => {
|
||||
dialogVisible.value = true
|
||||
dialogTitle.value = t('action.' + type)
|
||||
formType.value = type
|
||||
|
||||
// 加载父级监区选项
|
||||
await loadParentAreas()
|
||||
|
||||
resetForm()
|
||||
|
||||
// 修改时,设置数据
|
||||
if (id) {
|
||||
formLoading.value = true
|
||||
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 {
|
||||
formLoading.value = false
|
||||
}
|
||||
@ -109,7 +188,12 @@ const submitForm = async () => {
|
||||
// 提交请求
|
||||
formLoading.value = true
|
||||
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') {
|
||||
await AreaApi.createArea(data)
|
||||
message.success(t('common.createSuccess'))
|
||||
@ -131,13 +215,14 @@ const resetForm = () => {
|
||||
id: undefined,
|
||||
name: undefined,
|
||||
code: undefined,
|
||||
type: undefined,
|
||||
capacity: undefined,
|
||||
currentCount: undefined,
|
||||
sort: undefined,
|
||||
status: undefined,
|
||||
parentId: undefined,
|
||||
level: 1,
|
||||
type: 1,
|
||||
capacity: 0,
|
||||
sort: 0,
|
||||
status: 1,
|
||||
remark: undefined
|
||||
}
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
</script>
|
||||
</script>
|
||||
|
||||
@ -32,6 +32,21 @@
|
||||
/>
|
||||
</el-select>
|
||||
</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-select
|
||||
v-model="queryParams.status"
|
||||
@ -56,7 +71,7 @@
|
||||
@click="openForm('create')"
|
||||
v-hasPermi="['prison:area:create']"
|
||||
>
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增
|
||||
<Icon icon="ep:plus" class="mr-5px" /> 新增监区
|
||||
</el-button>
|
||||
<el-button
|
||||
type="success"
|
||||
@ -67,86 +82,144 @@
|
||||
>
|
||||
<Icon icon="ep:download" class="mr-5px" /> 导出
|
||||
</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>
|
||||
</ContentWrap>
|
||||
|
||||
<!-- 列表 -->
|
||||
<!-- 树形监区列表 -->
|
||||
<ContentWrap>
|
||||
<el-table
|
||||
v-loading="loading"
|
||||
:data="list"
|
||||
@selection-change="handleRowCheckboxChange"
|
||||
>
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column label="监区ID" align="center" prop="id" width="80" />
|
||||
<el-table-column label="监区名称" align="center" prop="name" width="120" />
|
||||
<el-table-column label="监区编码" align="center" prop="code" width="120" />
|
||||
<el-table-column label="监区类型" align="center" prop="type" width="100">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.PRISON_AREA_TYPE" :value="scope.row.type" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="容纳人数" align="center" prop="capacity" width="90" />
|
||||
<el-table-column label="当前人数" align="center" prop="currentCount" width="90" />
|
||||
<el-table-column label="排序" align="center" prop="sort" width="70" />
|
||||
<el-table-column label="状态" align="center" prop="status" width="90">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.PRISON_CELL_STATUS" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="创建时间" align="center" prop="createTime" width="180">
|
||||
<template #default="scope">
|
||||
{{ formatDate(scope.row.createTime) }}
|
||||
</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']"
|
||||
<el-row :gutter="20">
|
||||
<!-- 左侧树形结构 -->
|
||||
<el-col :span="showDetail ? 14 : 24">
|
||||
<el-card shadow="never" class="area-tree-card">
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span><Icon icon="ep:grid" class="mr-5px" /> 监区树形结构</span>
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
@click="handleRefreshTree"
|
||||
>
|
||||
<Icon icon="ep:refresh" class="mr-5px" /> 刷新
|
||||
</el-button>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<el-tree
|
||||
v-loading="treeLoading"
|
||||
:data="treeData"
|
||||
:props="treeProps"
|
||||
node-key="id"
|
||||
default-expand-all
|
||||
:expand-on-click-node="false"
|
||||
@node-click="handleNodeClick"
|
||||
>
|
||||
修改
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
link
|
||||
@click="handleDelete(scope.row.id)"
|
||||
v-hasPermi="['prison:area:delete']"
|
||||
>
|
||||
删除
|
||||
</el-button>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<Pagination
|
||||
:total="total"
|
||||
v-model:page="queryParams.pageNo"
|
||||
v-model:limit="queryParams.pageSize"
|
||||
@pagination="getList"
|
||||
/>
|
||||
<template #default="{ data }">
|
||||
<span class="area-tree-node">
|
||||
<el-icon :class="getLevelIconClass(data.level)">
|
||||
<component :is="getLevelIcon(data.level)" />
|
||||
</el-icon>
|
||||
<span class="node-name">{{ data.name }}</span>
|
||||
<el-tag size="small" :type="data.currentCount >= data.capacity ? 'danger' : 'success'">
|
||||
{{ data.currentCount }}/{{ data.capacity }}
|
||||
</el-tag>
|
||||
<el-tag
|
||||
v-if="data.level === 1"
|
||||
size="small"
|
||||
type="info"
|
||||
>
|
||||
{{ getTypeLabel(data.type) }}
|
||||
</el-tag>
|
||||
</span>
|
||||
</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>
|
||||
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<AreaForm ref="formRef" @success="getList" />
|
||||
<AreaForm ref="formRef" @success="handleRefreshTree" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
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 { OfficeBuilding, Files } from '@element-plus/icons-vue'
|
||||
|
||||
defineOptions({ name: 'Area' })
|
||||
|
||||
@ -161,21 +234,76 @@ const queryParams = reactive({
|
||||
pageSize: 10,
|
||||
name: undefined,
|
||||
type: undefined,
|
||||
level: undefined,
|
||||
status: undefined
|
||||
})
|
||||
const queryFormRef = ref()
|
||||
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 levelOptions = getIntDictOptions(DICT_TYPE.PRISON_AREA_LEVEL)
|
||||
const statusOptions = getIntDictOptions(DICT_TYPE.PRISON_CELL_STATUS)
|
||||
|
||||
// 树形组件配置
|
||||
const treeProps = {
|
||||
label: 'name',
|
||||
children: 'children'
|
||||
}
|
||||
|
||||
/** 日期格式化 */
|
||||
const formatDate = (date: string | Date | undefined) => {
|
||||
if (!date) return '-'
|
||||
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 () => {
|
||||
loading.value = true
|
||||
@ -206,29 +334,38 @@ const openForm = (type: string, id?: number) => {
|
||||
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) => {
|
||||
try {
|
||||
await message.delConfirm()
|
||||
await AreaApi.deleteArea(id)
|
||||
message.success(t('common.delSuccess'))
|
||||
await getList()
|
||||
} catch {}
|
||||
}
|
||||
|
||||
/** 批量删除按钮操作 */
|
||||
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()
|
||||
await getTreeList()
|
||||
showDetail.value = false
|
||||
currentNode.value = null
|
||||
} catch {}
|
||||
}
|
||||
|
||||
@ -247,6 +384,55 @@ const handleExport = async () => {
|
||||
|
||||
/** 初始化 */
|
||||
onMounted(() => {
|
||||
getTreeList()
|
||||
getList()
|
||||
})
|
||||
</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>
|
||||
<el-dialog
|
||||
v-model="dialogVisible"
|
||||
:title="dialogType === 'create' ? '新增服刑人员' : '修改服刑人员'"
|
||||
width="900px"
|
||||
:title="dialogType === 'create' ? '入监登记' : '修改罪犯信息'"
|
||||
width="1000px"
|
||||
:close-on-click-modal="false"
|
||||
>
|
||||
<el-form
|
||||
@ -12,181 +12,265 @@
|
||||
label-width="100px"
|
||||
v-loading="formLoading"
|
||||
>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="服刑人员编号" prop="prisonerNo">
|
||||
<el-input v-model="formData.prisonerNo" placeholder="请输入服刑人员编号" :disabled="dialogType === 'update'" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="姓名" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入姓名" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="性别" prop="gender">
|
||||
<el-select v-model="formData.gender" placeholder="请选择性别" clearable style="width: 100%">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="身份证号" prop="idCard">
|
||||
<el-input v-model="formData.idCard" placeholder="请输入身份证号" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="出生日期" prop="birthday">
|
||||
<el-date-picker
|
||||
v-model="formData.birthday"
|
||||
type="date"
|
||||
placeholder="请选择出生日期"
|
||||
value-format="YYYY-MM-DD"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="民族" prop="ethnicity">
|
||||
<el-input v-model="formData.ethnicity" placeholder="请输入民族" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="籍贯" prop="nativePlace">
|
||||
<el-input v-model="formData.nativePlace" placeholder="请输入籍贯" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="文化程度" prop="education">
|
||||
<el-select v-model="formData.education" placeholder="请选择文化程度" clearable style="width: 100%">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.PRISON_EDUCATION)"
|
||||
: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="occupation">
|
||||
<el-input v-model="formData.occupation" placeholder="请输入职业" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="家庭住址" prop="address">
|
||||
<el-input v-model="formData.address" placeholder="请输入家庭住址" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-divider border-style="dashed">刑罚信息</el-divider>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="罪名" prop="crime">
|
||||
<el-input v-model="formData.crime" placeholder="请输入罪名" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="刑期(年)" prop="sentenceYears">
|
||||
<el-input-number v-model="formData.sentenceYears" :min="0" :max="100" placeholder="请输入" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="刑期(月)" prop="sentenceMonths">
|
||||
<el-input-number v-model="formData.sentenceMonths" :min="0" :max="11" placeholder="请输入" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="入狱日期" prop="imprisonmentDate">
|
||||
<el-date-picker
|
||||
v-model="formData.imprisonmentDate"
|
||||
type="date"
|
||||
placeholder="请选择入狱日期"
|
||||
value-format="YYYY-MM-DD"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="释放日期" prop="releaseDate">
|
||||
<el-date-picker
|
||||
v-model="formData.releaseDate"
|
||||
type="date"
|
||||
placeholder="请选择释放日期"
|
||||
value-format="YYYY-MM-DD"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<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-row>
|
||||
<el-row :gutter="20">
|
||||
<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="监区ID" prop="prisonAreaId">
|
||||
<el-input-number v-model="formData.prisonAreaId" :min="0" placeholder="请输入监区ID" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="监室ID" prop="prisonCellId">
|
||||
<el-input-number v-model="formData.prisonCellId" :min="0" placeholder="请输入监室ID" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<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-col :span="16">
|
||||
<el-form-item label="备注" prop="remark">
|
||||
<el-input v-model="formData.remark" placeholder="请输入备注" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-tabs v-model="activeTab">
|
||||
<el-tab-pane label="基础信息" name="basic">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="罪犯编号" prop="prisonerNo">
|
||||
<el-input v-model="formData.prisonerNo" placeholder="请输入罪犯编号" :disabled="dialogType === 'update'" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="姓名" prop="name">
|
||||
<el-input v-model="formData.name" placeholder="请输入姓名" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="性别" prop="gender">
|
||||
<el-select v-model="formData.gender" placeholder="请选择性别" clearable style="width: 100%">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.SYSTEM_USER_SEX)"
|
||||
:key="dict.value"
|
||||
:label="dict.label"
|
||||
:value="dict.value"
|
||||
/>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="身份证号" prop="idCard">
|
||||
<el-input v-model="formData.idCard" placeholder="请输入身份证号" @blur="parseIdCard" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="出生日期" prop="birthday">
|
||||
<el-date-picker
|
||||
v-model="formData.birthday"
|
||||
type="date"
|
||||
placeholder="请选择出生日期"
|
||||
value-format="YYYY-MM-DD"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="民族" prop="ethnicity">
|
||||
<el-input v-model="formData.ethnicity" placeholder="请输入民族" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="籍贯" prop="nativePlace">
|
||||
<el-input v-model="formData.nativePlace" placeholder="请输入籍贯" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="文化程度" prop="education">
|
||||
<el-select v-model="formData.education" placeholder="请选择文化程度" clearable style="width: 100%">
|
||||
<el-option
|
||||
v-for="dict in getIntDictOptions(DICT_TYPE.PRISON_EDUCATION)"
|
||||
: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="occupation">
|
||||
<el-input v-model="formData.occupation" placeholder="请输入职业" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="24">
|
||||
<el-form-item label="家庭住址" prop="address">
|
||||
<el-input v-model="formData.address" placeholder="请输入家庭住址" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="刑罚信息" name="sentence">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-form-item label="罪名" prop="crime">
|
||||
<el-input v-model="formData.crime" placeholder="请输入罪名" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="刑期(年)" prop="sentenceYears">
|
||||
<el-input-number v-model="formData.sentenceYears" :min="0" :max="100" placeholder="请输入" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="刑期(月)" prop="sentenceMonths">
|
||||
<el-input-number v-model="formData.sentenceMonths" :min="0" :max="11" placeholder="请输入" style="width: 100%" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="6">
|
||||
<el-form-item label="是否无期" prop="lifeImprisonment">
|
||||
<el-select v-model="formData.lifeImprisonment" placeholder="请选择" clearable style="width: 100%">
|
||||
<el-option :key="0" :label="'否'" :value="0" />
|
||||
<el-option :key="1" :label="'是'" :value="1" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="6">
|
||||
<el-form-item label="是否死缓" prop="deathSentenceReprieve">
|
||||
<el-select v-model="formData.deathSentenceReprieve" placeholder="请选择" clearable style="width: 100%">
|
||||
<el-option :key="0" :label="'否'" :value="0" />
|
||||
<el-option :key="1" :label="'是'" :value="1" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-form-item label="原判刑期" prop="originalSentence">
|
||||
<el-input v-model="formData.originalSentence" placeholder="如:有期徒刑10年" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="判决法院" prop="courtName">
|
||||
<el-input v-model="formData.courtName" placeholder="请输入判决法院" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="判决日期" prop="judgmentDate">
|
||||
<el-date-picker
|
||||
v-model="formData.judgmentDate"
|
||||
type="date"
|
||||
placeholder="请选择判决日期"
|
||||
value-format="YYYY-MM-DD"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="判决书编号" prop="judgmentNo">
|
||||
<el-input v-model="formData.judgmentNo" placeholder="如:(2023)沪01刑初123号" />
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="8">
|
||||
<el-form-item label="入狱日期" prop="imprisonmentDate">
|
||||
<el-date-picker
|
||||
v-model="formData.imprisonmentDate"
|
||||
type="date"
|
||||
placeholder="请选择入狱日期"
|
||||
value-format="YYYY-MM-DD"
|
||||
style="width: 100%"
|
||||
@change="calculateReleaseDate"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<el-form-item label="释放日期" prop="releaseDate">
|
||||
<el-date-picker
|
||||
v-model="formData.releaseDate"
|
||||
type="date"
|
||||
placeholder="请选择释放日期"
|
||||
value-format="YYYY-MM-DD"
|
||||
style="width: 100%"
|
||||
/>
|
||||
</el-form-item>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-tab-pane>
|
||||
|
||||
<el-tab-pane label="监管信息" name="supervision">
|
||||
<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>
|
||||
<template #footer>
|
||||
<el-button @click="dialogVisible = false">取消</el-button>
|
||||
@ -197,18 +281,28 @@
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { PrisonerCreateVO } 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' })
|
||||
|
||||
const emit = defineEmits(['success'])
|
||||
|
||||
const { t } = useI18n()
|
||||
const message = useMessage()
|
||||
|
||||
const dialogVisible = ref(false)
|
||||
const dialogType = ref('create')
|
||||
const dialogType = ref<'create' | 'update'>('create')
|
||||
const formLoading = ref(false)
|
||||
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,
|
||||
prisonerNo: '',
|
||||
name: '',
|
||||
@ -223,18 +317,25 @@ const formData = ref({
|
||||
crime: '',
|
||||
sentenceYears: 0,
|
||||
sentenceMonths: 0,
|
||||
lifeImprisonment: undefined,
|
||||
deathSentenceReprieve: undefined,
|
||||
courtName: '',
|
||||
judgmentDate: '',
|
||||
judgmentNo: '',
|
||||
originalSentence: '',
|
||||
imprisonmentDate: '',
|
||||
releaseDate: '',
|
||||
supervisionLevel: undefined,
|
||||
riskLevel: undefined,
|
||||
prisonAreaId: undefined,
|
||||
subAreaId: undefined,
|
||||
prisonCellId: undefined,
|
||||
status: undefined,
|
||||
remark: ''
|
||||
})
|
||||
|
||||
const rules = {
|
||||
prisonerNo: [{ required: true, message: '请输入服刑人员编号', trigger: 'blur' }],
|
||||
prisonerNo: [{ required: true, message: '请输入罪犯编号', trigger: 'blur' }],
|
||||
name: [{ required: true, message: '请输入姓名', trigger: 'blur' }],
|
||||
gender: [{ required: true, message: '请选择性别', trigger: 'change' }],
|
||||
idCard: [{ required: true, message: '请输入身份证号', trigger: 'blur' }],
|
||||
@ -245,16 +346,107 @@ const rules = {
|
||||
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) => {
|
||||
dialogType.value = type
|
||||
dialogVisible.value = true
|
||||
activeTab.value = 'basic'
|
||||
resetForm()
|
||||
loadAreas()
|
||||
if (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 = () => {
|
||||
formData.value = {
|
||||
@ -272,15 +464,24 @@ const resetForm = () => {
|
||||
crime: '',
|
||||
sentenceYears: 0,
|
||||
sentenceMonths: 0,
|
||||
lifeImprisonment: undefined,
|
||||
deathSentenceReprieve: undefined,
|
||||
courtName: '',
|
||||
judgmentDate: '',
|
||||
judgmentNo: '',
|
||||
originalSentence: '',
|
||||
imprisonmentDate: '',
|
||||
releaseDate: '',
|
||||
supervisionLevel: undefined,
|
||||
riskLevel: undefined,
|
||||
prisonAreaId: undefined,
|
||||
subAreaId: undefined,
|
||||
prisonCellId: undefined,
|
||||
status: undefined,
|
||||
remark: ''
|
||||
}
|
||||
subAreaList.value = []
|
||||
cellList.value = []
|
||||
formRef.value?.resetFields()
|
||||
}
|
||||
|
||||
@ -289,7 +490,21 @@ const getPrisonerDetail = async (id: number) => {
|
||||
formLoading.value = true
|
||||
try {
|
||||
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 {
|
||||
formLoading.value = false
|
||||
}
|
||||
@ -303,7 +518,7 @@ const submitForm = async () => {
|
||||
try {
|
||||
if (dialogType.value === 'create') {
|
||||
await PrisonerApi.createPrisoner(formData.value)
|
||||
message.success('新增成功')
|
||||
message.success('入监登记成功')
|
||||
} else {
|
||||
await PrisonerApi.updatePrisoner(formData.value)
|
||||
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"
|
||||
label-width="100px"
|
||||
>
|
||||
<el-form-item label="服刑人员编号" prop="prisonerNo">
|
||||
<el-form-item label="罪犯编号" prop="prisonerNo">
|
||||
<el-input
|
||||
v-model="queryParams.prisonerNo"
|
||||
placeholder="请输入服刑人员编号"
|
||||
placeholder="请输入罪犯编号"
|
||||
clearable
|
||||
@keyup.enter="handleQuery"
|
||||
class="!w-240px"
|
||||
@ -71,6 +71,37 @@
|
||||
/>
|
||||
</el-select>
|
||||
</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-select
|
||||
v-model="queryParams.status"
|
||||
@ -95,7 +126,15 @@
|
||||
@click="openForm('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
|
||||
type="danger"
|
||||
@ -123,33 +162,36 @@
|
||||
<ContentWrap>
|
||||
<el-table v-loading="loading" :data="list" @selection-change="handleRowCheckboxChange">
|
||||
<el-table-column type="selection" width="55" />
|
||||
<el-table-column label="服刑人员编号" align="center" prop="prisonerNo" width="120" />
|
||||
<el-table-column label="罪犯编号" align="center" prop="prisonerNo" width="120" />
|
||||
<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">
|
||||
<dict-tag :type="DICT_TYPE.SYSTEM_USER_SEX" :value="scope.row.gender" />
|
||||
{{ formatIdCard(scope.row.idCard) }}
|
||||
</template>
|
||||
</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="supervisionLevel" width="100">
|
||||
<el-table-column label="刑期" align="center" width="120">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.PRISON_SUPERVISION_LEVEL" :value="scope.row.supervisionLevel" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="风险等级" align="center" prop="riskLevel" width="100">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.PRISON_RISK_LEVEL" :value="scope.row.riskLevel" />
|
||||
{{ formatSentence(scope.row) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="入狱日期" align="center" prop="imprisonmentDate" width="120" />
|
||||
<el-table-column label="状态" align="center" prop="status" width="100">
|
||||
<template #default="scope">
|
||||
<dict-tag :type="DICT_TYPE.PRISONER_STATUS" :value="scope.row.status" />
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="操作" align="center" width="200">
|
||||
<el-table-column label="监管等级" align="center" prop="supervisionLevelName" width="100" />
|
||||
<el-table-column label="风险等级" align="center" prop="riskLevelName" width="100" />
|
||||
<el-table-column label="当前监区" align="center" prop="prisonAreaName" width="120" />
|
||||
<el-table-column label="当前监室" align="center" prop="prisonCellName" width="100" />
|
||||
<el-table-column label="状态" align="center" prop="statusName" width="100" />
|
||||
<el-table-column label="操作" align="center" width="240">
|
||||
<template #default="scope">
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
@click="handleDetail(scope.row)"
|
||||
v-hasPermi="['prison:prisoner:query']"
|
||||
>
|
||||
详情
|
||||
</el-button>
|
||||
<el-button
|
||||
type="primary"
|
||||
link
|
||||
@ -158,6 +200,14 @@
|
||||
>
|
||||
修改
|
||||
</el-button>
|
||||
<el-button
|
||||
type="warning"
|
||||
link
|
||||
@click="handleTransfer(scope.row)"
|
||||
v-hasPermi="['prison:prisoner:transfer']"
|
||||
>
|
||||
调监
|
||||
</el-button>
|
||||
<el-button
|
||||
type="danger"
|
||||
link
|
||||
@ -180,14 +230,25 @@
|
||||
|
||||
<!-- 表单弹窗:添加/修改 -->
|
||||
<PrisonerForm ref="formRef" @success="getList" />
|
||||
|
||||
<!-- 详情弹窗 -->
|
||||
<PrisonerDetail ref="detailRef" />
|
||||
|
||||
<!-- 调监弹窗 -->
|
||||
<TransferForm ref="transferRef" @success="getList" />
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
|
||||
import { checkPermi } from '@/utils/permission'
|
||||
import PrisonerForm from './PrisonerForm.vue'
|
||||
import PrisonerDetail from './PrisonerDetail.vue'
|
||||
import TransferForm from './TransferForm.vue'
|
||||
import download from '@/utils/download'
|
||||
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' })
|
||||
|
||||
@ -197,6 +258,8 @@ const message = useMessage() // 消息弹窗
|
||||
const loading = ref(true) // 列表的加载中
|
||||
const total = ref(0) // 列表的总页数
|
||||
const list = ref([]) // 列表的数据
|
||||
const areaList = ref([]) // 监区列表
|
||||
const cellList = ref([]) // 监室列表
|
||||
const queryParams = reactive({
|
||||
pageNo: 1,
|
||||
pageSize: 10,
|
||||
@ -214,6 +277,9 @@ const queryParams = reactive({
|
||||
})
|
||||
const queryFormRef = ref() // 搜索的表单
|
||||
const exportLoading = ref(false) // 导出的加载中
|
||||
const formRef = ref() // 表单
|
||||
const detailRef = ref() // 详情
|
||||
const transferRef = ref() // 调监表单
|
||||
|
||||
/** 查询列表 */
|
||||
const getList = async () => {
|
||||
@ -236,6 +302,7 @@ const handleQuery = () => {
|
||||
/** 重置按钮操作 */
|
||||
const resetQuery = () => {
|
||||
queryFormRef.value.resetFields()
|
||||
queryParams.prisonCellId = undefined
|
||||
handleQuery()
|
||||
}
|
||||
|
||||
@ -247,7 +314,7 @@ const handleExport = async () => {
|
||||
// 发起导出
|
||||
exportLoading.value = true
|
||||
const data = await PrisonerApi.exportPrisoner(queryParams)
|
||||
download.excel(data, '服刑人员信息.xls')
|
||||
download.excel(data, '罪犯信息.xls')
|
||||
} catch {
|
||||
} finally {
|
||||
exportLoading.value = false
|
||||
@ -255,11 +322,65 @@ const handleExport = async () => {
|
||||
}
|
||||
|
||||
/** 添加/修改操作 */
|
||||
const formRef = ref()
|
||||
const openForm = (type: string, id?: number) => {
|
||||
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) => {
|
||||
try {
|
||||
@ -275,7 +396,7 @@ const handleDelete = async (id: number) => {
|
||||
|
||||
/** 批量删除按钮操作 */
|
||||
const checkedIds = ref<number[]>([])
|
||||
const handleRowCheckboxChange = (rows: PrisonerApi.PrisonerVO[]) => {
|
||||
const handleRowCheckboxChange = (rows: any[]) => {
|
||||
checkedIds.value = rows.map((row) => row.id)
|
||||
}
|
||||
|
||||
@ -295,5 +416,6 @@ const handleDeleteBatch = async () => {
|
||||
/** 初始化 **/
|
||||
onMounted(() => {
|
||||
getList()
|
||||
loadAreas()
|
||||
})
|
||||
</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