制度相关页面开发初步实现
This commit is contained in:
parent
9741192bee
commit
1f56b0a30f
|
@ -3,7 +3,9 @@
|
||||||
VITE_API_PREFIX = '/dev-api'
|
VITE_API_PREFIX = '/dev-api'
|
||||||
|
|
||||||
# 接口地址
|
# 接口地址
|
||||||
VITE_API_BASE_URL = 'http://pms.dtyx.net:9158/'
|
# VITE_API_BASE_URL = 'http://pms.dtyx.net:9158/'
|
||||||
|
VITE_API_BASE_URL = 'http://localhost:8888/'
|
||||||
|
# VITE_API_BASE_URL = 'http://10.18.34.213:8888/'
|
||||||
|
|
||||||
# 接口地址 (WebSocket)
|
# 接口地址 (WebSocket)
|
||||||
VITE_API_WS_URL = 'ws://localhost:8000'
|
VITE_API_WS_URL = 'ws://localhost:8000'
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
node_modules
|
File diff suppressed because it is too large
Load Diff
|
@ -16,6 +16,7 @@ export * as InsuranceTypeAPI from './insurance-type'
|
||||||
export * as HealthRecordAPI from './health-record'
|
export * as HealthRecordAPI from './health-record'
|
||||||
export * as InsuranceFileAPI from './insurance-file'
|
export * as InsuranceFileAPI from './insurance-file'
|
||||||
export * as EmployeeAPI from './employee'
|
export * as EmployeeAPI from './employee'
|
||||||
|
export * as RegulationAPI from './regulation'
|
||||||
|
|
||||||
export * from './area/type'
|
export * from './area/type'
|
||||||
export * from './auth/type'
|
export * from './auth/type'
|
||||||
|
|
|
@ -0,0 +1,89 @@
|
||||||
|
import http from '@/utils/http'
|
||||||
|
|
||||||
|
// 制度管理API接口
|
||||||
|
export const regulationApi = {
|
||||||
|
// 获取制度列表
|
||||||
|
getRegulationList: (params: {
|
||||||
|
page: number
|
||||||
|
size: number
|
||||||
|
}) => {
|
||||||
|
return http.get('/regulation', params)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取制度详情
|
||||||
|
getRegulationDetail: (regulationId: string) => {
|
||||||
|
return http.get(`/regulation/${regulationId}`)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 创建制度提案
|
||||||
|
createProposal: (data: {
|
||||||
|
title: string
|
||||||
|
content: string
|
||||||
|
regulationType: string
|
||||||
|
scope: string
|
||||||
|
level: string
|
||||||
|
remark?: string
|
||||||
|
}) => {
|
||||||
|
return http.post('/regulation/proposal', data)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 更新制度提案
|
||||||
|
updateProposal: (regulationId: string, data: any) => {
|
||||||
|
return http.put(`/regulation/proposal/${regulationId}`, data)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 删除制度提案
|
||||||
|
deleteProposal: (regulationId: string) => {
|
||||||
|
return http.del(`/regulation/proposal/${regulationId}`)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 提交投票
|
||||||
|
submitVote: (regulationId: string, data: {
|
||||||
|
voteType: 'FOR' | 'AGAINST' | 'ABSTAIN'
|
||||||
|
reason?: string
|
||||||
|
}) => {
|
||||||
|
return http.post(`/regulation/${regulationId}/vote`, data)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取投票结果
|
||||||
|
getVoteResult: (regulationId: string) => {
|
||||||
|
return http.get(`/regulation/${regulationId}/vote-result`)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 发布制度
|
||||||
|
publishRegulation: (regulationId: string) => {
|
||||||
|
return http.post(`/regulation/${regulationId}/publish`)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取讨论列表
|
||||||
|
getDiscussionList: (regulationId: string) => {
|
||||||
|
return http.get(`/regulation/${regulationId}/discussions`)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 发表评论
|
||||||
|
addComment: (regulationId: string, data: {
|
||||||
|
content: string
|
||||||
|
}) => {
|
||||||
|
return http.post(`/regulation/${regulationId}/discussions`, data)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取已发布制度列表
|
||||||
|
getPublishedRegulationList: (params: {
|
||||||
|
page: number
|
||||||
|
size: number
|
||||||
|
}) => {
|
||||||
|
return http.get('/regulation/published', params)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 确认制度知晓
|
||||||
|
confirmRegulation: (regulationId: string, data: {
|
||||||
|
agreeTerms: boolean
|
||||||
|
}) => {
|
||||||
|
return http.post(`/regulation/${regulationId}/confirm`, data)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 批量确认制度
|
||||||
|
confirmAllRegulations: () => {
|
||||||
|
return http.post('/regulation/confirm-all')
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,105 @@
|
||||||
|
// 制度状态枚举
|
||||||
|
export enum RegulationStatus {
|
||||||
|
DRAFT = 'DRAFT', // 草稿
|
||||||
|
VOTING = 'VOTING', // 投票中
|
||||||
|
APPROVED = 'APPROVED', // 已通过
|
||||||
|
REJECTED = 'REJECTED', // 已否决
|
||||||
|
PUBLISHED = 'PUBLISHED', // 已发布
|
||||||
|
ARCHIVED = 'ARCHIVED' // 已归档
|
||||||
|
}
|
||||||
|
|
||||||
|
// 制度级别枚举
|
||||||
|
export enum RegulationLevel {
|
||||||
|
LOW = 'LOW', // 低
|
||||||
|
MEDIUM = 'MEDIUM', // 中
|
||||||
|
HIGH = 'HIGH' // 高
|
||||||
|
}
|
||||||
|
|
||||||
|
// 投票类型枚举
|
||||||
|
export enum VoteType {
|
||||||
|
FOR = 'FOR', // 赞成
|
||||||
|
AGAINST = 'AGAINST', // 反对
|
||||||
|
ABSTAIN = 'ABSTAIN' // 弃权
|
||||||
|
}
|
||||||
|
|
||||||
|
// 制度信息接口
|
||||||
|
export interface Regulation {
|
||||||
|
regulationId: string
|
||||||
|
title: string
|
||||||
|
content: string
|
||||||
|
regulationType: string
|
||||||
|
status: RegulationStatus
|
||||||
|
publisherId: string
|
||||||
|
publisherName: string
|
||||||
|
publishTime: string
|
||||||
|
effectiveTime: string
|
||||||
|
expireTime: string
|
||||||
|
scope: string
|
||||||
|
level: RegulationLevel
|
||||||
|
version: string
|
||||||
|
remark?: string
|
||||||
|
createBy: string
|
||||||
|
updateBy: string
|
||||||
|
createTime: string
|
||||||
|
updateTime: string
|
||||||
|
page: number
|
||||||
|
pageSize: number
|
||||||
|
delFlag: string
|
||||||
|
requirements?: string
|
||||||
|
notes?: string
|
||||||
|
confirmStatus?: 'pending' | 'confirmed'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 制度列表响应接口
|
||||||
|
export interface RegulationListResponse {
|
||||||
|
status: number
|
||||||
|
data: {
|
||||||
|
records: Regulation[]
|
||||||
|
total: number
|
||||||
|
size: number
|
||||||
|
current: number
|
||||||
|
pages: number
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 创建提案请求接口
|
||||||
|
export interface CreateProposalRequest {
|
||||||
|
title: string
|
||||||
|
content: string
|
||||||
|
regulationType: string
|
||||||
|
scope: string
|
||||||
|
level: RegulationLevel
|
||||||
|
remark?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 投票请求接口
|
||||||
|
export interface VoteRequest {
|
||||||
|
voteType: VoteType
|
||||||
|
reason?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 投票结果接口
|
||||||
|
export interface VoteResult {
|
||||||
|
regulationId: string
|
||||||
|
voteFor: number
|
||||||
|
voteAgainst: number
|
||||||
|
voteAbstain: number
|
||||||
|
totalVotes: number
|
||||||
|
approvalRate: number
|
||||||
|
}
|
||||||
|
|
||||||
|
// 讨论评论接口
|
||||||
|
export interface Discussion {
|
||||||
|
id: string
|
||||||
|
regulationId: string
|
||||||
|
authorId: string
|
||||||
|
authorName: string
|
||||||
|
content: string
|
||||||
|
createTime: string
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分页参数接口
|
||||||
|
export interface PaginationParams {
|
||||||
|
page: number
|
||||||
|
size: number
|
||||||
|
}
|
|
@ -0,0 +1,131 @@
|
||||||
|
import { RegulationStatus, RegulationLevel } from '@/apis/regulation/type'
|
||||||
|
|
||||||
|
// Mock数据
|
||||||
|
export const mockRegulations = [
|
||||||
|
{
|
||||||
|
regulationId: 'reg001',
|
||||||
|
title: '员工考勤管理制度优化提案',
|
||||||
|
content: '建议优化考勤管理制度,增加弹性工作时间,提高员工工作积极性。具体包括:1. 允许员工在合理范围内调整上下班时间;2. 建立完善的请假制度;3. 优化加班管理流程。',
|
||||||
|
regulationType: '人事制度',
|
||||||
|
status: RegulationStatus.VOTING,
|
||||||
|
publisherId: '1',
|
||||||
|
publisherName: '张三',
|
||||||
|
publishTime: '2024-01-01T12:00:00',
|
||||||
|
effectiveTime: '2024-01-01T12:00:00',
|
||||||
|
expireTime: '2024-01-08T12:00:00',
|
||||||
|
scope: '全体员工',
|
||||||
|
level: RegulationLevel.MEDIUM,
|
||||||
|
version: '1.0',
|
||||||
|
remark: '需要与人事部门协调实施',
|
||||||
|
createBy: 'admin',
|
||||||
|
updateBy: 'admin',
|
||||||
|
createTime: '2024-01-01 10:00:00',
|
||||||
|
updateTime: '2024-01-01 10:00:00',
|
||||||
|
page: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
delFlag: '0',
|
||||||
|
voteFor: 15,
|
||||||
|
voteAgainst: 3,
|
||||||
|
voteAbstain: 2
|
||||||
|
},
|
||||||
|
{
|
||||||
|
regulationId: 'reg002',
|
||||||
|
title: '财务报销流程简化提案',
|
||||||
|
content: '建议简化财务报销流程,提高工作效率。具体包括:1. 优化审批流程,减少不必要的审批环节;2. 简化单据要求,使用电子化单据;3. 建立快速报销通道。',
|
||||||
|
regulationType: '财务制度',
|
||||||
|
status: RegulationStatus.PUBLISHED,
|
||||||
|
publisherId: '2',
|
||||||
|
publisherName: '李四',
|
||||||
|
publishTime: '2024-01-15T14:30:00',
|
||||||
|
effectiveTime: '2024-01-15T14:30:00',
|
||||||
|
expireTime: '2024-01-22T14:30:00',
|
||||||
|
scope: '财务部门及相关员工',
|
||||||
|
level: RegulationLevel.HIGH,
|
||||||
|
version: '1.0',
|
||||||
|
remark: '已获得财务部门支持',
|
||||||
|
createBy: 'admin',
|
||||||
|
updateBy: 'admin',
|
||||||
|
createTime: '2024-01-15 14:30:00',
|
||||||
|
updateTime: '2024-01-15 14:30:00',
|
||||||
|
page: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
delFlag: '0',
|
||||||
|
voteFor: 20,
|
||||||
|
voteAgainst: 1,
|
||||||
|
voteAbstain: 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
regulationId: 'reg003',
|
||||||
|
title: '安全生产管理制度',
|
||||||
|
content: '建立完善的安全生产管理制度,确保员工人身安全和设备安全。具体包括:1. 制定详细的安全操作规程;2. 建立安全培训制度;3. 完善安全检查机制。',
|
||||||
|
regulationType: '安全制度',
|
||||||
|
status: RegulationStatus.DRAFT,
|
||||||
|
publisherId: '3',
|
||||||
|
publisherName: '王五',
|
||||||
|
publishTime: '2024-01-20T09:15:00',
|
||||||
|
effectiveTime: '2024-01-20T09:15:00',
|
||||||
|
expireTime: '2024-12-31T23:59:59',
|
||||||
|
scope: '所有工作场所和员工',
|
||||||
|
level: RegulationLevel.HIGH,
|
||||||
|
version: '1.0',
|
||||||
|
remark: '需要安全部门配合',
|
||||||
|
createBy: 'admin',
|
||||||
|
updateBy: 'admin',
|
||||||
|
createTime: '2024-01-20 09:15:00',
|
||||||
|
updateTime: '2024-01-20 09:15:00',
|
||||||
|
page: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
delFlag: '0'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
regulationId: 'reg004',
|
||||||
|
title: '设备维护制度',
|
||||||
|
content: '为保障设备正常运行,延长设备使用寿命,特制定本制度。具体包括:1. 建立设备维护计划;2. 制定维护标准;3. 建立维护记录制度。',
|
||||||
|
regulationType: '设备制度',
|
||||||
|
status: RegulationStatus.ARCHIVED,
|
||||||
|
publisherId: '1',
|
||||||
|
publisherName: '管理员',
|
||||||
|
publishTime: '2024-01-01T12:00:00',
|
||||||
|
effectiveTime: '2024-01-01T12:00:00',
|
||||||
|
expireTime: '2024-12-31T23:59:59',
|
||||||
|
scope: '设备部门',
|
||||||
|
level: RegulationLevel.MEDIUM,
|
||||||
|
version: '1.0',
|
||||||
|
remark: '已归档',
|
||||||
|
createBy: 'admin',
|
||||||
|
updateBy: 'admin',
|
||||||
|
createTime: '2024-01-01 12:00:00',
|
||||||
|
updateTime: '2024-01-01 12:00:00',
|
||||||
|
page: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
delFlag: '0'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
// Mock讨论数据
|
||||||
|
export const mockDiscussions = [
|
||||||
|
{
|
||||||
|
id: '1',
|
||||||
|
regulationId: 'reg001',
|
||||||
|
authorId: '2',
|
||||||
|
authorName: '李四',
|
||||||
|
content: '这个提案很有建设性,建议增加一些具体的实施细则。',
|
||||||
|
createTime: '2024-01-01 11:00:00'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '2',
|
||||||
|
regulationId: 'reg001',
|
||||||
|
authorId: '3',
|
||||||
|
authorName: '王五',
|
||||||
|
content: '同意这个提案,但需要考虑实施成本。',
|
||||||
|
createTime: '2024-01-01 14:30:00'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: '3',
|
||||||
|
regulationId: 'reg002',
|
||||||
|
authorId: '1',
|
||||||
|
authorName: '张三',
|
||||||
|
content: '简化流程确实能提高效率,支持这个提案。',
|
||||||
|
createTime: '2024-01-15 16:00:00'
|
||||||
|
}
|
||||||
|
]
|
|
@ -26,6 +26,27 @@ export const systemRoutes: RouteRecordRaw[] = [
|
||||||
// }
|
// }
|
||||||
// ],
|
// ],
|
||||||
// },
|
// },
|
||||||
|
{
|
||||||
|
path: '/regulation',
|
||||||
|
name: 'Regulation',
|
||||||
|
component: Layout,
|
||||||
|
redirect: '/regulation/system-regulation',
|
||||||
|
meta: { title: '制度管理', icon: 'file-text', hidden: false, sort: 1.5 },
|
||||||
|
children: [
|
||||||
|
{
|
||||||
|
path: '/regulation/system-regulation',
|
||||||
|
name: 'SystemRegulation',
|
||||||
|
component: () => import('@/views/regulation/repository.vue'),
|
||||||
|
meta: { title: '制度确认', icon: 'file-text', hidden: false },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/regulation/process-management',
|
||||||
|
name: 'ProcessManagement',
|
||||||
|
component: () => import('@/views/regulation/confirm.vue'),
|
||||||
|
meta: { title: '流程管理', icon: 'workflow', hidden: false },
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: '/organization',
|
path: '/organization',
|
||||||
name: 'Organization',
|
name: 'Organization',
|
||||||
|
|
|
@ -0,0 +1,49 @@
|
||||||
|
import { defineStore } from 'pinia'
|
||||||
|
import { ref } from 'vue'
|
||||||
|
|
||||||
|
export interface Regulation {
|
||||||
|
id: string
|
||||||
|
name: string
|
||||||
|
type: string
|
||||||
|
typeName: string
|
||||||
|
publisher: string
|
||||||
|
publishTime: string
|
||||||
|
effectiveDate: string
|
||||||
|
confirmStatus: 'pending' | 'confirmed'
|
||||||
|
content: string
|
||||||
|
scope: string
|
||||||
|
requirements: string
|
||||||
|
notes: string
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useRegulationStore = defineStore('regulation', () => {
|
||||||
|
// 已发布的制度列表
|
||||||
|
const publishedRegulations = ref<Regulation[]>([])
|
||||||
|
|
||||||
|
// 添加新发布的制度
|
||||||
|
const addPublishedRegulation = (regulation: Regulation) => {
|
||||||
|
publishedRegulations.value.unshift(regulation)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新制度确认状态
|
||||||
|
const updateRegulationConfirmStatus = (id: string, status: 'pending' | 'confirmed') => {
|
||||||
|
const regulation = publishedRegulations.value.find(item => item.id === id)
|
||||||
|
if (regulation) {
|
||||||
|
regulation.confirmStatus = status
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 批量确认所有制度
|
||||||
|
const confirmAllRegulations = () => {
|
||||||
|
publishedRegulations.value.forEach(regulation => {
|
||||||
|
regulation.confirmStatus = 'confirmed'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
publishedRegulations,
|
||||||
|
addPublishedRegulation,
|
||||||
|
updateRegulationConfirmStatus,
|
||||||
|
confirmAllRegulations
|
||||||
|
}
|
||||||
|
})
|
|
@ -0,0 +1,791 @@
|
||||||
|
<template>
|
||||||
|
<div class="process-management">
|
||||||
|
<a-card title="制度提案管理" :bordered="false">
|
||||||
|
<template #extra>
|
||||||
|
<a-button type="primary" @click="handleAdd">
|
||||||
|
<template #icon>
|
||||||
|
<GiSvgIcon name="plus" />
|
||||||
|
</template>
|
||||||
|
提交制度提案
|
||||||
|
</a-button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<a-table
|
||||||
|
:columns="columns"
|
||||||
|
:data="tableData"
|
||||||
|
:loading="loading"
|
||||||
|
:pagination="pagination"
|
||||||
|
@page-change="handlePageChange"
|
||||||
|
@page-size-change="handlePageSizeChange"
|
||||||
|
>
|
||||||
|
<template #status="{ record }">
|
||||||
|
<a-tag :color="getStatusColor(record.status)">
|
||||||
|
{{ getStatusText(record.status) }}
|
||||||
|
</a-tag>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #level="{ record }">
|
||||||
|
<a-tag :color="getLevelColor(record.level)">
|
||||||
|
{{ getLevelText(record.level) }}
|
||||||
|
</a-tag>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #operations="{ record }">
|
||||||
|
<a-space>
|
||||||
|
<a-button type="text" size="small" @click="handleView(record)">
|
||||||
|
查看详情
|
||||||
|
</a-button>
|
||||||
|
<a-button type="text" size="small" @click="handleDiscuss(record)">
|
||||||
|
讨论
|
||||||
|
</a-button>
|
||||||
|
<a-button
|
||||||
|
v-if="record.status === RegulationStatus.VOTING"
|
||||||
|
type="text"
|
||||||
|
size="small"
|
||||||
|
@click="handleVote(record)"
|
||||||
|
>
|
||||||
|
投票
|
||||||
|
</a-button>
|
||||||
|
<a-button
|
||||||
|
v-if="record.status === RegulationStatus.PUBLISHED"
|
||||||
|
type="text"
|
||||||
|
size="small"
|
||||||
|
disabled
|
||||||
|
>
|
||||||
|
已发布
|
||||||
|
</a-button>
|
||||||
|
<a-button
|
||||||
|
v-if="record.publisherId === currentUser && record.status === RegulationStatus.DRAFT"
|
||||||
|
type="text"
|
||||||
|
size="small"
|
||||||
|
@click="handleEdit(record)"
|
||||||
|
>
|
||||||
|
编辑
|
||||||
|
</a-button>
|
||||||
|
<a-popconfirm
|
||||||
|
v-if="record.publisherId === currentUser && record.status === RegulationStatus.DRAFT"
|
||||||
|
content="确定要删除这个提案吗?"
|
||||||
|
@ok="handleDelete(record)"
|
||||||
|
>
|
||||||
|
<a-button type="text" size="small" status="danger">
|
||||||
|
删除
|
||||||
|
</a-button>
|
||||||
|
</a-popconfirm>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<!-- 提案表单弹窗 -->
|
||||||
|
<a-modal
|
||||||
|
v-model:visible="modalVisible"
|
||||||
|
:title="modalTitle"
|
||||||
|
width="800px"
|
||||||
|
@ok="handleSubmit"
|
||||||
|
@cancel="handleCancel"
|
||||||
|
>
|
||||||
|
<a-form
|
||||||
|
ref="formRef"
|
||||||
|
:model="formData"
|
||||||
|
:rules="rules"
|
||||||
|
layout="vertical"
|
||||||
|
>
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="提案标题" field="title">
|
||||||
|
<a-input v-model="formData.title" placeholder="请输入提案标题" />
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="提案类型" field="regulationType">
|
||||||
|
<a-select v-model="formData.regulationType" placeholder="请选择提案类型">
|
||||||
|
<a-option value="人事制度">人事制度</a-option>
|
||||||
|
<a-option value="财务制度">财务制度</a-option>
|
||||||
|
<a-option value="安全制度">安全制度</a-option>
|
||||||
|
<a-option value="设备制度">设备制度</a-option>
|
||||||
|
<a-option value="工作流程">工作流程</a-option>
|
||||||
|
<a-option value="其他制度">其他制度</a-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
|
||||||
|
<a-row :gutter="16">
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="适用范围" field="scope">
|
||||||
|
<a-input v-model="formData.scope" placeholder="请输入适用范围" />
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
<a-col :span="12">
|
||||||
|
<a-form-item label="制度级别" field="level">
|
||||||
|
<a-select v-model="formData.level" placeholder="请选择制度级别">
|
||||||
|
<a-option :value="RegulationLevel.LOW">低</a-option>
|
||||||
|
<a-option :value="RegulationLevel.MEDIUM">中</a-option>
|
||||||
|
<a-option :value="RegulationLevel.HIGH">高</a-option>
|
||||||
|
</a-select>
|
||||||
|
</a-form-item>
|
||||||
|
</a-col>
|
||||||
|
</a-row>
|
||||||
|
|
||||||
|
<a-form-item label="提案内容" field="content">
|
||||||
|
<a-textarea
|
||||||
|
v-model="formData.content"
|
||||||
|
placeholder="请详细描述提案的具体内容"
|
||||||
|
:rows="6"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
|
||||||
|
<a-form-item label="备注" field="remark">
|
||||||
|
<a-textarea
|
||||||
|
v-model="formData.remark"
|
||||||
|
placeholder="请输入其他补充说明"
|
||||||
|
:rows="2"
|
||||||
|
/>
|
||||||
|
</a-form-item>
|
||||||
|
</a-form>
|
||||||
|
</a-modal>
|
||||||
|
|
||||||
|
<!-- 讨论弹窗 -->
|
||||||
|
<a-modal
|
||||||
|
v-model:visible="discussModalVisible"
|
||||||
|
title="提案讨论"
|
||||||
|
width="800px"
|
||||||
|
:footer="false"
|
||||||
|
>
|
||||||
|
<div class="discussion-container" v-if="currentProposal">
|
||||||
|
<div class="proposal-info">
|
||||||
|
<h4>{{ currentProposal.title }}</h4>
|
||||||
|
<p>{{ currentProposal.content }}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a-divider />
|
||||||
|
|
||||||
|
<div class="discussion-list">
|
||||||
|
<div v-for="comment in discussionList" :key="comment.id" class="comment-item">
|
||||||
|
<div class="comment-header">
|
||||||
|
<span class="comment-author">{{ comment.authorName }}</span>
|
||||||
|
<span class="comment-time">{{ formatDate(comment.createTime) }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="comment-content">{{ comment.content }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="add-comment">
|
||||||
|
<a-textarea
|
||||||
|
v-model="newComment"
|
||||||
|
placeholder="请输入您的评论"
|
||||||
|
:rows="3"
|
||||||
|
/>
|
||||||
|
<a-button type="primary" style="margin-top: 8px;" @click="submitComment">
|
||||||
|
发表评论
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-modal>
|
||||||
|
|
||||||
|
<!-- 投票弹窗 -->
|
||||||
|
<a-modal
|
||||||
|
v-model:visible="voteModalVisible"
|
||||||
|
title="提案投票"
|
||||||
|
width="600px"
|
||||||
|
@ok="submitVote"
|
||||||
|
@cancel="voteModalVisible = false"
|
||||||
|
>
|
||||||
|
<div class="vote-container" v-if="currentProposal">
|
||||||
|
<h4>{{ currentProposal.title }}</h4>
|
||||||
|
<p>{{ currentProposal.content }}</p>
|
||||||
|
|
||||||
|
<a-divider />
|
||||||
|
|
||||||
|
<h4>请选择您的投票</h4>
|
||||||
|
<a-radio-group v-model="voteChoice">
|
||||||
|
<a-radio :value="VoteType.FOR">赞成</a-radio>
|
||||||
|
<a-radio :value="VoteType.AGAINST">反对</a-radio>
|
||||||
|
<a-radio :value="VoteType.ABSTAIN">弃权</a-radio>
|
||||||
|
</a-radio-group>
|
||||||
|
|
||||||
|
<a-textarea
|
||||||
|
v-model="voteReason"
|
||||||
|
placeholder="请说明投票理由(可选)"
|
||||||
|
:rows="3"
|
||||||
|
style="margin-top: 16px;"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</a-modal>
|
||||||
|
|
||||||
|
<!-- 提案详情弹窗 -->
|
||||||
|
<a-modal
|
||||||
|
v-model:visible="detailModalVisible"
|
||||||
|
title="提案详情"
|
||||||
|
width="800px"
|
||||||
|
:footer="false"
|
||||||
|
>
|
||||||
|
<div class="proposal-detail" v-if="currentProposal">
|
||||||
|
<div class="detail-header">
|
||||||
|
<h3>{{ currentProposal.title }}</h3>
|
||||||
|
<div class="detail-meta">
|
||||||
|
<span>提案人: {{ currentProposal.publisherName }}</span>
|
||||||
|
<span>提案类型: {{ currentProposal.regulationType }}</span>
|
||||||
|
<span>适用范围: {{ currentProposal.scope }}</span>
|
||||||
|
<span>级别: <a-tag :color="getLevelColor(currentProposal.level)">{{ getLevelText(currentProposal.level) }}</a-tag></span>
|
||||||
|
<span>创建时间: {{ formatDate(currentProposal.createTime) }}</span>
|
||||||
|
<span>状态: <a-tag :color="getStatusColor(currentProposal.status)">{{ getStatusText(currentProposal.status) }}</a-tag></span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a-divider />
|
||||||
|
|
||||||
|
<div class="detail-content">
|
||||||
|
<h4>提案内容</h4>
|
||||||
|
<div class="content-text">{{ currentProposal.content }}</div>
|
||||||
|
|
||||||
|
<h4>备注</h4>
|
||||||
|
<div class="content-text">{{ currentProposal.remark || '暂无备注' }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a-divider />
|
||||||
|
|
||||||
|
<div class="vote-summary" v-if="currentProposal.status === RegulationStatus.VOTING || currentProposal.status === RegulationStatus.PUBLISHED || currentProposal.status === RegulationStatus.REJECTED">
|
||||||
|
<h4>投票结果</h4>
|
||||||
|
<div class="vote-stats">
|
||||||
|
<div class="vote-item">
|
||||||
|
<span class="vote-label">赞成:</span>
|
||||||
|
<span class="vote-count">{{ currentProposal.voteFor || 0 }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="vote-item">
|
||||||
|
<span class="vote-label">反对:</span>
|
||||||
|
<span class="vote-count">{{ currentProposal.voteAgainst || 0 }}</span>
|
||||||
|
</div>
|
||||||
|
<div class="vote-item">
|
||||||
|
<span class="vote-label">弃权:</span>
|
||||||
|
<span class="vote-count">{{ currentProposal.voteAbstain || 0 }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="detail-footer">
|
||||||
|
<a-button type="primary" @click="handleDiscuss(currentProposal)">
|
||||||
|
参与讨论
|
||||||
|
</a-button>
|
||||||
|
<a-button
|
||||||
|
v-if="currentProposal.status === RegulationStatus.VOTING"
|
||||||
|
@click="handleVote(currentProposal)"
|
||||||
|
>
|
||||||
|
参与投票
|
||||||
|
</a-button>
|
||||||
|
<a-button
|
||||||
|
v-if="currentProposal.status === RegulationStatus.PUBLISHED"
|
||||||
|
disabled
|
||||||
|
>
|
||||||
|
已自动发布
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-modal>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, reactive, onMounted } from 'vue'
|
||||||
|
import { Message } from '@arco-design/web-vue'
|
||||||
|
import { useRegulationStore } from '@/stores/modules/regulation'
|
||||||
|
import { regulationApi } from '@/apis/regulation'
|
||||||
|
import {
|
||||||
|
RegulationStatus,
|
||||||
|
RegulationLevel,
|
||||||
|
VoteType,
|
||||||
|
type Regulation,
|
||||||
|
type VoteResult,
|
||||||
|
type Discussion
|
||||||
|
} from '@/apis/regulation/type'
|
||||||
|
|
||||||
|
defineOptions({ name: 'ProcessManagement' })
|
||||||
|
|
||||||
|
// 表格列定义
|
||||||
|
const columns = [
|
||||||
|
{ title: '提案标题', dataIndex: 'title', key: 'title' },
|
||||||
|
{ title: '提案人', dataIndex: 'publisherName', key: 'publisherName' },
|
||||||
|
{ title: '提案类型', dataIndex: 'regulationType', key: 'regulationType' },
|
||||||
|
{ title: '状态', dataIndex: 'status', key: 'status', slotName: 'status' },
|
||||||
|
{ title: '级别', dataIndex: 'level', key: 'level', slotName: 'level' },
|
||||||
|
{ title: '创建时间', dataIndex: 'createTime', key: 'createTime' },
|
||||||
|
{ title: '操作', key: 'operations', slotName: 'operations', width: 280 }
|
||||||
|
]
|
||||||
|
|
||||||
|
// 表格数据
|
||||||
|
const tableData = ref<Regulation[]>([])
|
||||||
|
const loading = ref(false)
|
||||||
|
const pagination = reactive({
|
||||||
|
current: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 0,
|
||||||
|
showTotal: true,
|
||||||
|
showJumper: true,
|
||||||
|
showPageSize: true
|
||||||
|
})
|
||||||
|
|
||||||
|
// 弹窗相关
|
||||||
|
const modalVisible = ref(false)
|
||||||
|
const modalTitle = ref('提交制度提案')
|
||||||
|
const formRef = ref()
|
||||||
|
const formData = reactive({
|
||||||
|
regulationId: '',
|
||||||
|
title: '',
|
||||||
|
regulationType: '',
|
||||||
|
content: '',
|
||||||
|
scope: '',
|
||||||
|
level: RegulationLevel.MEDIUM,
|
||||||
|
remark: ''
|
||||||
|
})
|
||||||
|
|
||||||
|
// 讨论相关
|
||||||
|
const discussModalVisible = ref(false)
|
||||||
|
const currentProposal = ref<Regulation | null>(null)
|
||||||
|
const discussionList = ref<Discussion[]>([])
|
||||||
|
const newComment = ref('')
|
||||||
|
|
||||||
|
// 投票相关
|
||||||
|
const voteModalVisible = ref(false)
|
||||||
|
const voteChoice = ref<VoteType | ''>('')
|
||||||
|
const voteReason = ref('')
|
||||||
|
|
||||||
|
// 详情相关
|
||||||
|
const detailModalVisible = ref(false)
|
||||||
|
|
||||||
|
// 当前用户(模拟)
|
||||||
|
const currentUser = ref('1') // 使用用户ID而不是用户名
|
||||||
|
|
||||||
|
// 制度管理store
|
||||||
|
const regulationStore = useRegulationStore()
|
||||||
|
|
||||||
|
// 表单验证规则
|
||||||
|
const rules = {
|
||||||
|
title: [{ required: true, message: '请输入提案标题' }],
|
||||||
|
regulationType: [{ required: true, message: '请选择提案类型' }],
|
||||||
|
content: [{ required: true, message: '请输入提案内容' }],
|
||||||
|
scope: [{ required: true, message: '请输入适用范围' }]
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取状态颜色
|
||||||
|
const getStatusColor = (status: RegulationStatus) => {
|
||||||
|
const colors = {
|
||||||
|
[RegulationStatus.DRAFT]: 'blue',
|
||||||
|
[RegulationStatus.VOTING]: 'orange',
|
||||||
|
[RegulationStatus.REJECTED]: 'red',
|
||||||
|
[RegulationStatus.PUBLISHED]: 'purple',
|
||||||
|
[RegulationStatus.APPROVED]: 'green',
|
||||||
|
[RegulationStatus.ARCHIVED]: 'gray'
|
||||||
|
}
|
||||||
|
return colors[status] || 'blue'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取状态文本
|
||||||
|
const getStatusText = (status: RegulationStatus) => {
|
||||||
|
const texts = {
|
||||||
|
[RegulationStatus.DRAFT]: '草稿',
|
||||||
|
[RegulationStatus.VOTING]: '投票中',
|
||||||
|
[RegulationStatus.REJECTED]: '已否决',
|
||||||
|
[RegulationStatus.PUBLISHED]: '已发布',
|
||||||
|
[RegulationStatus.APPROVED]: '已通过',
|
||||||
|
[RegulationStatus.ARCHIVED]: '已归档'
|
||||||
|
}
|
||||||
|
return texts[status] || '草稿'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 格式化日期
|
||||||
|
const formatDate = (dateString: string) => {
|
||||||
|
if (!dateString) return '-'
|
||||||
|
const date = new Date(dateString)
|
||||||
|
return date.toLocaleDateString('zh-CN', {
|
||||||
|
year: 'numeric',
|
||||||
|
month: '2-digit',
|
||||||
|
day: '2-digit',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取级别颜色
|
||||||
|
const getLevelColor = (level: RegulationLevel) => {
|
||||||
|
const colors = {
|
||||||
|
[RegulationLevel.LOW]: 'blue',
|
||||||
|
[RegulationLevel.MEDIUM]: 'orange',
|
||||||
|
[RegulationLevel.HIGH]: 'red'
|
||||||
|
}
|
||||||
|
return colors[level] || 'blue'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取级别文本
|
||||||
|
const getLevelText = (level: RegulationLevel) => {
|
||||||
|
const texts = {
|
||||||
|
[RegulationLevel.LOW]: '低',
|
||||||
|
[RegulationLevel.MEDIUM]: '中',
|
||||||
|
[RegulationLevel.HIGH]: '高'
|
||||||
|
}
|
||||||
|
return texts[level] || '中'
|
||||||
|
}
|
||||||
|
|
||||||
|
// 获取表格数据
|
||||||
|
const getTableData = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const response = await regulationApi.getRegulationList({
|
||||||
|
page: pagination.current,
|
||||||
|
size: pagination.pageSize
|
||||||
|
})
|
||||||
|
|
||||||
|
if (response.status === 200) {
|
||||||
|
tableData.value = response.data.records
|
||||||
|
pagination.total = response.data.total
|
||||||
|
pagination.current = response.data.current
|
||||||
|
} else {
|
||||||
|
Message.error('获取数据失败')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取制度列表失败:', error)
|
||||||
|
Message.error('获取数据失败')
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 新增
|
||||||
|
const handleAdd = () => {
|
||||||
|
modalTitle.value = '提交制度提案'
|
||||||
|
modalVisible.value = true
|
||||||
|
resetForm()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 编辑
|
||||||
|
const handleEdit = (record: Regulation) => {
|
||||||
|
modalTitle.value = '编辑制度提案'
|
||||||
|
modalVisible.value = true
|
||||||
|
Object.assign(formData, {
|
||||||
|
regulationId: record.regulationId,
|
||||||
|
title: record.title,
|
||||||
|
regulationType: record.regulationType,
|
||||||
|
content: record.content,
|
||||||
|
scope: record.scope,
|
||||||
|
level: record.level,
|
||||||
|
remark: record.remark
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查看
|
||||||
|
const handleView = (record: Regulation) => {
|
||||||
|
currentProposal.value = record
|
||||||
|
detailModalVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 讨论
|
||||||
|
const handleDiscuss = async (record: Regulation) => {
|
||||||
|
currentProposal.value = record
|
||||||
|
discussModalVisible.value = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await regulationApi.getDiscussionList(record.regulationId)
|
||||||
|
if (response.status === 200) {
|
||||||
|
discussionList.value = response.data
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取讨论列表失败:', error)
|
||||||
|
Message.error('获取讨论列表失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 投票
|
||||||
|
const handleVote = (record: Regulation) => {
|
||||||
|
currentProposal.value = record
|
||||||
|
voteModalVisible.value = true
|
||||||
|
voteChoice.value = ''
|
||||||
|
voteReason.value = ''
|
||||||
|
}
|
||||||
|
|
||||||
|
// 删除
|
||||||
|
const handleDelete = async (record: Regulation) => {
|
||||||
|
try {
|
||||||
|
await regulationApi.deleteProposal(record.regulationId)
|
||||||
|
Message.success('删除成功')
|
||||||
|
getTableData()
|
||||||
|
} catch (error) {
|
||||||
|
console.error('删除失败:', error)
|
||||||
|
Message.error('删除失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交评论
|
||||||
|
const submitComment = async () => {
|
||||||
|
if (!newComment.value.trim()) {
|
||||||
|
Message.warning('请输入评论内容')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!currentProposal.value) {
|
||||||
|
Message.error('当前提案信息不存在')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await regulationApi.addComment(currentProposal.value.regulationId, {
|
||||||
|
content: newComment.value
|
||||||
|
})
|
||||||
|
|
||||||
|
// 重新获取讨论列表
|
||||||
|
const response = await regulationApi.getDiscussionList(currentProposal.value.regulationId)
|
||||||
|
if (response.status === 200) {
|
||||||
|
discussionList.value = response.data
|
||||||
|
}
|
||||||
|
|
||||||
|
newComment.value = ''
|
||||||
|
Message.success('评论发表成功')
|
||||||
|
} catch (error) {
|
||||||
|
console.error('评论发表失败:', error)
|
||||||
|
Message.error('评论发表失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交投票
|
||||||
|
const submitVote = async () => {
|
||||||
|
if (!voteChoice.value) {
|
||||||
|
Message.warning('请选择投票选项')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!currentProposal.value) {
|
||||||
|
Message.error('当前提案信息不存在')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
await regulationApi.submitVote(currentProposal.value.regulationId, {
|
||||||
|
voteType: voteChoice.value,
|
||||||
|
reason: voteReason.value
|
||||||
|
})
|
||||||
|
|
||||||
|
Message.success('投票提交成功')
|
||||||
|
voteModalVisible.value = false
|
||||||
|
getTableData()
|
||||||
|
} catch (error) {
|
||||||
|
console.error('投票提交失败:', error)
|
||||||
|
Message.error('投票提交失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交表单
|
||||||
|
const handleSubmit = async () => {
|
||||||
|
try {
|
||||||
|
await formRef.value.validate()
|
||||||
|
|
||||||
|
if (formData.regulationId) {
|
||||||
|
// 更新
|
||||||
|
await regulationApi.updateProposal(formData.regulationId, {
|
||||||
|
title: formData.title,
|
||||||
|
regulationType: formData.regulationType,
|
||||||
|
content: formData.content,
|
||||||
|
scope: formData.scope,
|
||||||
|
level: formData.level,
|
||||||
|
remark: formData.remark
|
||||||
|
})
|
||||||
|
Message.success('更新成功')
|
||||||
|
} else {
|
||||||
|
// 新增
|
||||||
|
await regulationApi.createProposal({
|
||||||
|
title: formData.title,
|
||||||
|
regulationType: formData.regulationType,
|
||||||
|
content: formData.content,
|
||||||
|
scope: formData.scope,
|
||||||
|
level: formData.level,
|
||||||
|
remark: formData.remark
|
||||||
|
})
|
||||||
|
Message.success('提案提交成功')
|
||||||
|
}
|
||||||
|
|
||||||
|
modalVisible.value = false
|
||||||
|
getTableData()
|
||||||
|
} catch (error) {
|
||||||
|
console.error('操作失败:', error)
|
||||||
|
Message.error('操作失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 取消
|
||||||
|
const handleCancel = () => {
|
||||||
|
modalVisible.value = false
|
||||||
|
resetForm()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 重置表单
|
||||||
|
const resetForm = () => {
|
||||||
|
Object.assign(formData, {
|
||||||
|
regulationId: '',
|
||||||
|
title: '',
|
||||||
|
regulationType: '',
|
||||||
|
content: '',
|
||||||
|
scope: '',
|
||||||
|
level: RegulationLevel.MEDIUM,
|
||||||
|
remark: ''
|
||||||
|
})
|
||||||
|
formRef.value?.resetFields()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分页事件
|
||||||
|
const handlePageChange = (page: number) => {
|
||||||
|
pagination.current = page
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
|
const handlePageSizeChange = (pageSize: number) => {
|
||||||
|
pagination.pageSize = pageSize
|
||||||
|
pagination.current = 1
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getTableData()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.process-management {
|
||||||
|
.arco-card {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.discussion-container {
|
||||||
|
.proposal-info {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
|
||||||
|
h4 {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
color: var(--color-text-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
color: var(--color-text-2);
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.discussion-list {
|
||||||
|
max-height: 300px;
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
.comment-item {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
padding: 12px;
|
||||||
|
background: var(--color-fill-1);
|
||||||
|
border-radius: 6px;
|
||||||
|
|
||||||
|
.comment-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
|
||||||
|
.comment-author {
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--color-text-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.comment-time {
|
||||||
|
color: var(--color-text-3);
|
||||||
|
font-size: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.comment-content {
|
||||||
|
color: var(--color-text-2);
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.add-comment {
|
||||||
|
margin-top: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vote-container {
|
||||||
|
h4 {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
color: var(--color-text-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
p {
|
||||||
|
color: var(--color-text-2);
|
||||||
|
line-height: 1.6;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.proposal-detail {
|
||||||
|
.detail-header {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
color: var(--color-text-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-meta {
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
color: var(--color-text-3);
|
||||||
|
font-size: 14px;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-content {
|
||||||
|
h4 {
|
||||||
|
margin: 16px 0 8px 0;
|
||||||
|
color: var(--color-text-1);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-text {
|
||||||
|
color: var(--color-text-2);
|
||||||
|
line-height: 1.6;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
padding: 12px;
|
||||||
|
background: var(--color-fill-1);
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.vote-summary {
|
||||||
|
h4 {
|
||||||
|
margin: 16px 0 8px 0;
|
||||||
|
color: var(--color-text-1);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.vote-stats {
|
||||||
|
display: flex;
|
||||||
|
gap: 20px;
|
||||||
|
|
||||||
|
.vote-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
|
||||||
|
.vote-label {
|
||||||
|
color: var(--color-text-2);
|
||||||
|
}
|
||||||
|
|
||||||
|
.vote-count {
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--color-text-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-footer {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
justify-content: center;
|
||||||
|
margin-top: 20px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,103 @@
|
||||||
|
<template>
|
||||||
|
<GiPageLayout
|
||||||
|
:margin="true"
|
||||||
|
:default-collapsed="false"
|
||||||
|
:header-style="isDesktop ? { padding: 0, borderBottomWidth: 0 } : { borderBottomWidth: '1px' } "
|
||||||
|
>
|
||||||
|
<template v-if="isDesktop" #left>
|
||||||
|
<a-tabs v-model:active-key="activeKey" type="rounded" position="left" hide-content size="large" @change="change">
|
||||||
|
<a-tab-pane v-for="(item) in menuList" :key="item.key">
|
||||||
|
<template #title>
|
||||||
|
<div style="display: flex; align-items: center">
|
||||||
|
<GiSvgIcon :name="item.icon" :size="18" style="margin-right: 4px" />
|
||||||
|
{{ item.name }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</a-tab-pane>
|
||||||
|
</a-tabs>
|
||||||
|
</template>
|
||||||
|
<template #header>
|
||||||
|
<a-tabs v-if="!isDesktop" v-model:active-key="activeKey" type="rounded" position="top" size="large" @change="change">
|
||||||
|
<a-tab-pane v-for="(item) in menuList" :key="item.key" :title="item.name">
|
||||||
|
<template #title>
|
||||||
|
<div style="display: flex; align-items: center">
|
||||||
|
<GiSvgIcon :name="item.icon" :size="18" style="margin-right: 4px" />
|
||||||
|
{{ item.name }}
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</a-tab-pane>
|
||||||
|
</a-tabs>
|
||||||
|
</template>
|
||||||
|
<transition name="fade-slide" mode="out-in" appear>
|
||||||
|
<component :is="menuList.find((item) => item.key === activeKey)?.value"></component>
|
||||||
|
</transition>
|
||||||
|
</GiPageLayout>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="tsx">
|
||||||
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
import SystemRegulation from './system-regulation/index.vue'
|
||||||
|
import ProcessManagement from './process-management/index.vue'
|
||||||
|
import { useDevice } from '@/hooks'
|
||||||
|
|
||||||
|
defineOptions({ name: 'ZhiduManagement' })
|
||||||
|
|
||||||
|
const { isDesktop } = useDevice()
|
||||||
|
|
||||||
|
const data = [
|
||||||
|
{ name: '制度规范', key: 'system-regulation', icon: 'file-text', value: SystemRegulation, path: '/zhidu/system-regulation' },
|
||||||
|
{ name: '流程管理', key: 'process-management', icon: 'workflow', value: ProcessManagement, path: '/zhidu/process-management' },
|
||||||
|
]
|
||||||
|
|
||||||
|
const menuList = computed(() => {
|
||||||
|
return data
|
||||||
|
})
|
||||||
|
|
||||||
|
const route = useRoute()
|
||||||
|
const router = useRouter()
|
||||||
|
|
||||||
|
// 根据当前路由确定激活的标签页
|
||||||
|
const activeKey = computed(() => {
|
||||||
|
const currentPath = route.path
|
||||||
|
const menuItem = menuList.value.find(item => item.path === currentPath)
|
||||||
|
return menuItem ? menuItem.key : menuList.value[0].key
|
||||||
|
})
|
||||||
|
watch(
|
||||||
|
() => route.path,
|
||||||
|
() => {
|
||||||
|
// 路由变化时会自动更新activeKey
|
||||||
|
},
|
||||||
|
{ immediate: true },
|
||||||
|
)
|
||||||
|
const change = (key: string | number) => {
|
||||||
|
const menuItem = menuList.value.find(item => item.key === key)
|
||||||
|
if (menuItem) {
|
||||||
|
router.push(menuItem.path)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.gi_page {
|
||||||
|
padding-top: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
:deep(.arco-tabs-nav-vertical.arco-tabs-nav-type-line .arco-tabs-tab) {
|
||||||
|
margin: 0;
|
||||||
|
padding: 8px 16px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--color-fill-1);
|
||||||
|
|
||||||
|
.arco-tabs-tab-title {
|
||||||
|
&::before {
|
||||||
|
display: none !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.arco-tabs-tab-active {
|
||||||
|
background: rgba(var(--primary-6), 0.08);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
|
@ -0,0 +1,373 @@
|
||||||
|
<template>
|
||||||
|
<div class="system-regulation">
|
||||||
|
<a-card title="已发布制度确认" :bordered="false">
|
||||||
|
<template #extra>
|
||||||
|
<a-button type="primary" @click="handleConfirmAll">
|
||||||
|
<template #icon>
|
||||||
|
<icon-check />
|
||||||
|
</template>
|
||||||
|
一键确认全部
|
||||||
|
</a-button>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<a-table
|
||||||
|
:columns="columns"
|
||||||
|
:data="tableData"
|
||||||
|
:loading="loading"
|
||||||
|
:pagination="pagination"
|
||||||
|
@page-change="onPageChange"
|
||||||
|
@page-size-change="onPageSizeChange"
|
||||||
|
>
|
||||||
|
<template #confirmStatus="{ record }">
|
||||||
|
<a-tag :color="record.confirmStatus === 'confirmed' ? 'green' : 'orange'">
|
||||||
|
{{ record.confirmStatus === 'confirmed' ? '已确认' : '待确认' }}
|
||||||
|
</a-tag>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<template #operations="{ record }">
|
||||||
|
<a-space>
|
||||||
|
<a-button type="text" size="small" @click="handleView(record)">
|
||||||
|
查看详情
|
||||||
|
</a-button>
|
||||||
|
<a-button
|
||||||
|
v-if="record.confirmStatus !== 'confirmed'"
|
||||||
|
type="text"
|
||||||
|
size="small"
|
||||||
|
@click="handleConfirm(record)"
|
||||||
|
>
|
||||||
|
确认知晓
|
||||||
|
</a-button>
|
||||||
|
<a-button type="text" size="small" @click="handleDownload(record)">
|
||||||
|
下载
|
||||||
|
</a-button>
|
||||||
|
</a-space>
|
||||||
|
</template>
|
||||||
|
</a-table>
|
||||||
|
</a-card>
|
||||||
|
|
||||||
|
<!-- 制度详情弹窗 -->
|
||||||
|
<a-modal
|
||||||
|
v-model:visible="detailModalVisible"
|
||||||
|
title="制度详情"
|
||||||
|
width="800px"
|
||||||
|
:footer="false"
|
||||||
|
>
|
||||||
|
<div class="regulation-detail" v-if="currentRegulation">
|
||||||
|
<div class="detail-header">
|
||||||
|
<h3>{{ currentRegulation.title }}</h3>
|
||||||
|
<div class="detail-meta">
|
||||||
|
<span>发布人: {{ currentRegulation.publisherName }}</span>
|
||||||
|
<span>发布时间: {{ currentRegulation.publishTime }}</span>
|
||||||
|
<span>生效日期: {{ currentRegulation.effectiveTime }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a-divider />
|
||||||
|
|
||||||
|
<div class="detail-content">
|
||||||
|
<h4>制度内容</h4>
|
||||||
|
<div class="content-text">{{ currentRegulation.content }}</div>
|
||||||
|
|
||||||
|
<h4>适用范围</h4>
|
||||||
|
<div class="content-text">{{ currentRegulation.scope }}</div>
|
||||||
|
|
||||||
|
<h4>实施要求</h4>
|
||||||
|
<div class="content-text">{{ currentRegulation.requirements }}</div>
|
||||||
|
|
||||||
|
<h4>注意事项</h4>
|
||||||
|
<div class="content-text">{{ currentRegulation.notes }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<a-divider />
|
||||||
|
|
||||||
|
<div class="detail-footer">
|
||||||
|
<a-button type="primary" @click="handleConfirm(currentRegulation)">
|
||||||
|
确认知晓并遵守
|
||||||
|
</a-button>
|
||||||
|
<a-button @click="handleDownload(currentRegulation)">
|
||||||
|
下载制度文件
|
||||||
|
</a-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</a-modal>
|
||||||
|
|
||||||
|
<!-- 确认承诺弹窗 -->
|
||||||
|
<a-modal
|
||||||
|
v-model:visible="confirmModalVisible"
|
||||||
|
title="制度确认承诺"
|
||||||
|
width="600px"
|
||||||
|
@ok="submitConfirm"
|
||||||
|
@cancel="confirmModalVisible = false"
|
||||||
|
>
|
||||||
|
<div class="confirm-content">
|
||||||
|
<p>我确认已仔细阅读并理解《{{ currentRegulation?.title }}》的全部内容,承诺:</p>
|
||||||
|
<ul>
|
||||||
|
<li>严格遵守该制度的各项规定</li>
|
||||||
|
<li>认真履行相关职责和义务</li>
|
||||||
|
<li>如有违反,愿意承担相应责任</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
|
<a-checkbox v-model="agreeTerms">
|
||||||
|
我已阅读并同意上述承诺
|
||||||
|
</a-checkbox>
|
||||||
|
</div>
|
||||||
|
</a-modal>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, reactive, onMounted } from 'vue'
|
||||||
|
import type { Regulation } from '@/apis/regulation/type'
|
||||||
|
import { Message } from '@arco-design/web-vue'
|
||||||
|
import { useRegulationStore } from '@/stores/modules/regulation'
|
||||||
|
import { regulationApi } from '@/apis/regulation'
|
||||||
|
|
||||||
|
defineOptions({ name: 'SystemRegulation' })
|
||||||
|
|
||||||
|
// 表格列定义
|
||||||
|
const columns = [
|
||||||
|
{ title: '制度名称', dataIndex: 'title', key: 'title' },
|
||||||
|
{ title: '制度类型', dataIndex: 'regulationType', key: 'regulationType' },
|
||||||
|
{ title: '发布人', dataIndex: 'publisherName', key: 'publisherName' },
|
||||||
|
{ title: '发布时间', dataIndex: 'publishTime', key: 'publishTime' },
|
||||||
|
{ title: '生效日期', dataIndex: 'effectiveTime', key: 'effectiveTime' },
|
||||||
|
{ title: '确认状态', dataIndex: 'confirmStatus', key: 'confirmStatus', slotName: 'confirmStatus' },
|
||||||
|
{ title: '操作', key: 'operations', slotName: 'operations', width: 250 }
|
||||||
|
]
|
||||||
|
|
||||||
|
// 表格数据
|
||||||
|
const tableData = ref<Regulation[]>([])
|
||||||
|
const loading = ref(false)
|
||||||
|
const pagination = reactive({
|
||||||
|
current: 1,
|
||||||
|
pageSize: 10,
|
||||||
|
total: 0,
|
||||||
|
showTotal: true,
|
||||||
|
showJumper: true,
|
||||||
|
showPageSize: true
|
||||||
|
})
|
||||||
|
|
||||||
|
// 弹窗相关
|
||||||
|
const detailModalVisible = ref(false)
|
||||||
|
const confirmModalVisible = ref(false)
|
||||||
|
const currentRegulation = ref<Regulation | null>(null)
|
||||||
|
const agreeTerms = ref(false)
|
||||||
|
|
||||||
|
// 制度管理store
|
||||||
|
const regulationStore = useRegulationStore()
|
||||||
|
|
||||||
|
// 获取表格数据
|
||||||
|
const getTableData = async () => {
|
||||||
|
loading.value = true
|
||||||
|
try {
|
||||||
|
const response = await regulationApi.getPublishedRegulationList({
|
||||||
|
page: pagination.current,
|
||||||
|
size: pagination.pageSize
|
||||||
|
})
|
||||||
|
|
||||||
|
if (response.status === 200) {
|
||||||
|
tableData.value = response.data.records
|
||||||
|
pagination.pageSize = response.data.size
|
||||||
|
pagination.current = response.data.current
|
||||||
|
pagination.total = response.data.total
|
||||||
|
} else {
|
||||||
|
Message.error('获取数据失败')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('获取已发布制度列表失败:', error)
|
||||||
|
Message.error('获取数据失败')
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 分页事件
|
||||||
|
const onPageChange = (page: number) => {
|
||||||
|
pagination.current = page
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
|
const onPageSizeChange = (pageSize: number) => {
|
||||||
|
pagination.pageSize = pageSize
|
||||||
|
pagination.current = 1
|
||||||
|
getTableData()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 查看详情
|
||||||
|
const handleView = (record: any) => {
|
||||||
|
currentRegulation.value = record
|
||||||
|
detailModalVisible.value = true
|
||||||
|
}
|
||||||
|
|
||||||
|
// 确认知晓
|
||||||
|
const handleConfirm = (record: any) => {
|
||||||
|
currentRegulation.value = record
|
||||||
|
confirmModalVisible.value = true
|
||||||
|
agreeTerms.value = false
|
||||||
|
}
|
||||||
|
|
||||||
|
// 下载
|
||||||
|
const handleDownload = (record: any) => {
|
||||||
|
try {
|
||||||
|
// 创建制度文档内容
|
||||||
|
const content = `
|
||||||
|
制度名称:${record.title}
|
||||||
|
制度类型:${record.regulationType}
|
||||||
|
发布人:${record.publisherName}
|
||||||
|
发布时间:${record.publishTime}
|
||||||
|
生效日期:${record.effectiveTime}
|
||||||
|
|
||||||
|
制度内容:
|
||||||
|
${record.content}
|
||||||
|
|
||||||
|
适用范围:
|
||||||
|
${record.scope}
|
||||||
|
|
||||||
|
实施要求:
|
||||||
|
${record.requirements || '请按照制度要求执行'}
|
||||||
|
|
||||||
|
注意事项:
|
||||||
|
${record.notes || '请严格遵守制度规定'}
|
||||||
|
`.trim()
|
||||||
|
|
||||||
|
// 创建Blob对象
|
||||||
|
const blob = new Blob([content], { type: 'text/plain;charset=utf-8' })
|
||||||
|
|
||||||
|
// 创建下载链接
|
||||||
|
const url = window.URL.createObjectURL(blob)
|
||||||
|
const link = document.createElement('a')
|
||||||
|
link.href = url
|
||||||
|
link.download = `${record.title}.txt`
|
||||||
|
|
||||||
|
// 触发下载
|
||||||
|
document.body.appendChild(link)
|
||||||
|
link.click()
|
||||||
|
|
||||||
|
// 清理
|
||||||
|
document.body.removeChild(link)
|
||||||
|
window.URL.revokeObjectURL(url)
|
||||||
|
|
||||||
|
Message.success('制度文件下载成功')
|
||||||
|
} catch (error) {
|
||||||
|
Message.error('下载失败')
|
||||||
|
console.error('下载错误:', error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 一键确认全部
|
||||||
|
const handleConfirmAll = async () => {
|
||||||
|
try {
|
||||||
|
await regulationApi.confirmAllRegulations()
|
||||||
|
|
||||||
|
// 更新本地数据状态
|
||||||
|
tableData.value.forEach(item => {
|
||||||
|
item.confirmStatus = 'confirmed'
|
||||||
|
})
|
||||||
|
|
||||||
|
Message.success('已确认所有待确认的制度')
|
||||||
|
} catch (error) {
|
||||||
|
console.error('批量确认失败:', error)
|
||||||
|
Message.error('确认失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 提交确认
|
||||||
|
const submitConfirm = async () => {
|
||||||
|
if (!agreeTerms.value) {
|
||||||
|
Message.warning('请先同意承诺条款')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (currentRegulation.value) {
|
||||||
|
await regulationApi.confirmRegulation(currentRegulation.value.regulationId, {
|
||||||
|
agreeTerms: agreeTerms.value
|
||||||
|
})
|
||||||
|
|
||||||
|
Message.success('确认成功,您已承诺遵守该制度')
|
||||||
|
confirmModalVisible.value = false
|
||||||
|
|
||||||
|
// 更新本地数据状态
|
||||||
|
const index = tableData.value.findIndex(item => item.regulationId === currentRegulation.value.regulationId)
|
||||||
|
if (index !== -1) {
|
||||||
|
tableData.value[index].confirmStatus = 'confirmed'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('确认失败:', error)
|
||||||
|
Message.error('确认失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
getTableData()
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.system-regulation {
|
||||||
|
.arco-card {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.regulation-detail {
|
||||||
|
.detail-header {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
|
||||||
|
h3 {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
color: var(--color-text-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-meta {
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
color: var(--color-text-3);
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-content {
|
||||||
|
h4 {
|
||||||
|
margin: 16px 0 8px 0;
|
||||||
|
color: var(--color-text-1);
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.content-text {
|
||||||
|
color: var(--color-text-2);
|
||||||
|
line-height: 1.6;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
padding: 12px;
|
||||||
|
background: var(--color-fill-1);
|
||||||
|
border-radius: 6px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-footer {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.confirm-content {
|
||||||
|
p {
|
||||||
|
margin-bottom: 16px;
|
||||||
|
color: var(--color-text-1);
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
ul {
|
||||||
|
margin-bottom: 20px;
|
||||||
|
padding-left: 20px;
|
||||||
|
|
||||||
|
li {
|
||||||
|
margin-bottom: 8px;
|
||||||
|
color: var(--color-text-2);
|
||||||
|
line-height: 1.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
Loading…
Reference in New Issue