Merge remote-tracking branch 'origin/main'
This commit is contained in:
commit
b8ffc08513
|
@ -3,7 +3,9 @@
|
|||
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)
|
||||
VITE_API_WS_URL = 'ws://localhost:8000'
|
||||
|
|
|
@ -0,0 +1 @@
|
|||
node_modules
|
|
@ -2,6 +2,5 @@
|
|||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
<mapping directory="$PROJECT_DIR$/Industrial-image-management-system---web" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
File diff suppressed because it is too large
Load Diff
|
@ -143,6 +143,9 @@ importers:
|
|||
xgplayer:
|
||||
specifier: ^2.31.6
|
||||
version: 2.32.6
|
||||
xlsx:
|
||||
specifier: ^0.18.5
|
||||
version: 0.18.5
|
||||
devDependencies:
|
||||
'@antfu/eslint-config':
|
||||
specifier: ^2.16.3
|
||||
|
@ -1481,6 +1484,10 @@ packages:
|
|||
engines: {node: '>=0.4.0'}
|
||||
hasBin: true
|
||||
|
||||
adler-32@1.3.1:
|
||||
resolution: {integrity: sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==}
|
||||
engines: {node: '>=0.8'}
|
||||
|
||||
aieditor@1.0.13:
|
||||
resolution: {integrity: sha512-A1NIydCJgno3VvEKWPyHZlS7IF5FwBO1X4QO3GEKNcs8wMmmVGbcoVDPHON3uo9bTKaxuuIiONyfLCGHLBpW2Q==}
|
||||
|
||||
|
@ -1688,6 +1695,10 @@ packages:
|
|||
capital-case@1.0.4:
|
||||
resolution: {integrity: sha512-ds37W8CytHgwnhGGTi88pcPyR15qoNkOpYwmMMfnWqqWgESapLqvDx6huFjQ5vqWSn2Z06173XNA7LtMOeUh1A==}
|
||||
|
||||
cfb@1.2.2:
|
||||
resolution: {integrity: sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==}
|
||||
engines: {node: '>=0.8'}
|
||||
|
||||
chalk@1.1.3:
|
||||
resolution: {integrity: sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
@ -1759,6 +1770,10 @@ packages:
|
|||
codemirror@6.0.1:
|
||||
resolution: {integrity: sha512-J8j+nZ+CdWmIeFIGXEFbFPtpiYacFMDR8GlHK3IyHQJMCaVRfGx9NT+Hxivv1ckLWPvNdZqndbr/7lVhrf/Svg==}
|
||||
|
||||
codepage@1.15.0:
|
||||
resolution: {integrity: sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==}
|
||||
engines: {node: '>=0.8'}
|
||||
|
||||
collection-visit@1.0.0:
|
||||
resolution: {integrity: sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
@ -1849,6 +1864,11 @@ packages:
|
|||
resolution: {integrity: sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==}
|
||||
engines: {node: '>= 0.10'}
|
||||
|
||||
crc-32@1.2.2:
|
||||
resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==}
|
||||
engines: {node: '>=0.8'}
|
||||
hasBin: true
|
||||
|
||||
crelt@1.0.6:
|
||||
resolution: {integrity: sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==}
|
||||
|
||||
|
@ -2655,6 +2675,10 @@ packages:
|
|||
resolution: {integrity: sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==}
|
||||
engines: {node: '>= 6'}
|
||||
|
||||
frac@1.1.2:
|
||||
resolution: {integrity: sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==}
|
||||
engines: {node: '>=0.8'}
|
||||
|
||||
fragment-cache@0.2.1:
|
||||
resolution: {integrity: sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
@ -4106,6 +4130,10 @@ packages:
|
|||
resolution: {integrity: sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
ssf@0.11.2:
|
||||
resolution: {integrity: sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==}
|
||||
engines: {node: '>=0.8'}
|
||||
|
||||
stable@0.1.8:
|
||||
resolution: {integrity: sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w==}
|
||||
deprecated: 'Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility'
|
||||
|
@ -4687,10 +4715,18 @@ packages:
|
|||
resolution: {integrity: sha512-o0cyEG0e8GPzT4iGHphIOh0cJOV8fivsXxddQasHPHfoZf1ZexrfeA21w2NaEN1RHE+fXlfISmOE8R9N3u3Qig==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
wmf@1.0.2:
|
||||
resolution: {integrity: sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==}
|
||||
engines: {node: '>=0.8'}
|
||||
|
||||
word-wrap@1.2.5:
|
||||
resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
|
||||
word@0.3.0:
|
||||
resolution: {integrity: sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==}
|
||||
engines: {node: '>=0.8'}
|
||||
|
||||
wrap-ansi@7.0.0:
|
||||
resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==}
|
||||
engines: {node: '>=10'}
|
||||
|
@ -4713,6 +4749,11 @@ packages:
|
|||
resolution: {integrity: sha512-ESwYYcG8SQciPaN43tZkN3r0dS/jQ5RtyxyGbxn2+qcKgZQ861M899xq8Cab/z6qVVX+/4eIsxDbm3lfYGYzvA==}
|
||||
hasBin: true
|
||||
|
||||
xlsx@0.18.5:
|
||||
resolution: {integrity: sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==}
|
||||
engines: {node: '>=0.8'}
|
||||
hasBin: true
|
||||
|
||||
xml-name-validator@4.0.0:
|
||||
resolution: {integrity: sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==}
|
||||
engines: {node: '>=12'}
|
||||
|
@ -6166,6 +6207,8 @@ snapshots:
|
|||
|
||||
acorn@8.11.3: {}
|
||||
|
||||
adler-32@1.3.1: {}
|
||||
|
||||
aieditor@1.0.13(@tiptap/extension-code-block@2.5.8(@tiptap/core@2.5.8(@tiptap/pm@2.5.8))(@tiptap/pm@2.5.8)):
|
||||
dependencies:
|
||||
'@tiptap/core': 2.5.8(@tiptap/pm@2.5.8)
|
||||
|
@ -6421,6 +6464,11 @@ snapshots:
|
|||
tslib: 2.6.2
|
||||
upper-case-first: 2.0.2
|
||||
|
||||
cfb@1.2.2:
|
||||
dependencies:
|
||||
adler-32: 1.3.1
|
||||
crc-32: 1.2.2
|
||||
|
||||
chalk@1.1.3:
|
||||
dependencies:
|
||||
ansi-styles: 2.2.1
|
||||
|
@ -6521,6 +6569,8 @@ snapshots:
|
|||
transitivePeerDependencies:
|
||||
- '@lezer/common'
|
||||
|
||||
codepage@1.15.0: {}
|
||||
|
||||
collection-visit@1.0.0:
|
||||
dependencies:
|
||||
map-visit: 1.0.0
|
||||
|
@ -6608,6 +6658,8 @@ snapshots:
|
|||
object-assign: 4.1.1
|
||||
vary: 1.1.2
|
||||
|
||||
crc-32@1.2.2: {}
|
||||
|
||||
crelt@1.0.6: {}
|
||||
|
||||
cron-parser@4.9.0:
|
||||
|
@ -7548,6 +7600,8 @@ snapshots:
|
|||
combined-stream: 1.0.8
|
||||
mime-types: 2.1.35
|
||||
|
||||
frac@1.1.2: {}
|
||||
|
||||
fragment-cache@0.2.1:
|
||||
dependencies:
|
||||
map-cache: 0.2.2
|
||||
|
@ -9049,6 +9103,10 @@ snapshots:
|
|||
dependencies:
|
||||
extend-shallow: 3.0.2
|
||||
|
||||
ssf@0.11.2:
|
||||
dependencies:
|
||||
frac: 1.1.2
|
||||
|
||||
stable@0.1.8: {}
|
||||
|
||||
static-extend@0.1.2:
|
||||
|
@ -9717,8 +9775,12 @@ snapshots:
|
|||
dependencies:
|
||||
string-width: 5.1.2
|
||||
|
||||
wmf@1.0.2: {}
|
||||
|
||||
word-wrap@1.2.5: {}
|
||||
|
||||
word@0.3.0: {}
|
||||
|
||||
wrap-ansi@7.0.0:
|
||||
dependencies:
|
||||
ansi-styles: 4.3.0
|
||||
|
@ -9755,6 +9817,16 @@ snapshots:
|
|||
fs-extra: 5.0.0
|
||||
xgplayer-subtitles: 1.0.19
|
||||
|
||||
xlsx@0.18.5:
|
||||
dependencies:
|
||||
adler-32: 1.3.1
|
||||
cfb: 1.2.2
|
||||
codepage: 1.15.0
|
||||
crc-32: 1.2.2
|
||||
ssf: 0.11.2
|
||||
wmf: 1.0.2
|
||||
word: 0.3.0
|
||||
|
||||
xml-name-validator@4.0.0: {}
|
||||
|
||||
y18n@5.0.8: {}
|
||||
|
|
|
@ -17,14 +17,13 @@
|
|||
|
||||
<script setup lang="ts">
|
||||
import { useAppStore, useUserStore } from '@/stores'
|
||||
|
||||
// 1
|
||||
defineOptions({ name: 'App' })
|
||||
const userStore = useUserStore()
|
||||
const appStore = useAppStore()
|
||||
appStore.initTheme()
|
||||
appStore.initSiteConfig()
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.loading-icon {
|
||||
animation: arco-loading-circle 1s infinite cubic-bezier(0,0,1,1);
|
||||
|
|
|
@ -6,6 +6,7 @@ import type { AttachInfoData, BusinessTypeResult } from './type'
|
|||
* 批量新增附件信息
|
||||
* @param businessType 业务类型
|
||||
* @param files 文件列表
|
||||
* @returns
|
||||
*/
|
||||
export function batchAddAttachment(businessType: string, formData: FormData) {
|
||||
return request<AttachInfoData[]>({
|
||||
|
|
|
@ -1,64 +1,64 @@
|
|||
/** 用户信息响应类型 */
|
||||
export interface UserInfoResponse {
|
||||
code: number;
|
||||
status: number;
|
||||
success: boolean;
|
||||
msg: string;
|
||||
code: number
|
||||
status: number
|
||||
success: boolean
|
||||
msg: string
|
||||
data: {
|
||||
user: UserDetail;
|
||||
dept: DeptDetail;
|
||||
roles: RoleDetail[];
|
||||
posts: any[];
|
||||
};
|
||||
user: UserDetail
|
||||
dept: DeptDetail
|
||||
roles: RoleDetail[]
|
||||
posts: any[]
|
||||
}
|
||||
}
|
||||
|
||||
/** 用户详细信息 */
|
||||
export interface UserDetail {
|
||||
userId: string;
|
||||
account: string;
|
||||
name: string;
|
||||
status: number;
|
||||
userCode: string;
|
||||
userStatus: string;
|
||||
userType: string;
|
||||
mobile: string;
|
||||
createTime: string;
|
||||
avatar?: string;
|
||||
userId: string
|
||||
account: string
|
||||
name: string
|
||||
status: number
|
||||
userCode: string
|
||||
userStatus: string
|
||||
userType: string
|
||||
mobile: string
|
||||
createTime: string
|
||||
avatar?: string
|
||||
}
|
||||
|
||||
/** 部门详细信息 */
|
||||
export interface DeptDetail {
|
||||
deptId: string;
|
||||
deptName: string;
|
||||
parentId: string;
|
||||
orderNum: number;
|
||||
leaderId: string;
|
||||
status: string;
|
||||
deptId: string
|
||||
deptName: string
|
||||
parentId: string
|
||||
orderNum: number
|
||||
leaderId: string
|
||||
status: string
|
||||
}
|
||||
|
||||
/** 角色详细信息 */
|
||||
export interface RoleDetail {
|
||||
roleId: string;
|
||||
roleName: string;
|
||||
roleCode: string | null;
|
||||
roleKey: string;
|
||||
roleId: string
|
||||
roleName: string
|
||||
roleCode: string | null
|
||||
roleKey: string
|
||||
}
|
||||
|
||||
/** 用户类型 - 兼容旧版本 */
|
||||
export interface UserInfo {
|
||||
id: string;
|
||||
username: string;
|
||||
nickname: string;
|
||||
gender: 0 | 1 | 2;
|
||||
email: string;
|
||||
phone: string;
|
||||
avatar: string;
|
||||
pwdResetTime: string;
|
||||
pwdExpired: boolean;
|
||||
registrationDate: string;
|
||||
deptName: string;
|
||||
roles: string[];
|
||||
permissions: string[];
|
||||
id: string
|
||||
username: string
|
||||
nickname: string
|
||||
gender: 0 | 1 | 2
|
||||
email: string
|
||||
phone: string
|
||||
avatar: string
|
||||
pwdResetTime: string
|
||||
pwdExpired: boolean
|
||||
registrationDate: string
|
||||
deptName: string
|
||||
roles: string[]
|
||||
permissions: string[]
|
||||
}
|
||||
|
||||
/** 路由类型 */
|
||||
|
|
|
@ -16,6 +16,7 @@ export * as InsuranceTypeAPI from './insurance-type'
|
|||
export * as HealthRecordAPI from './health-record'
|
||||
export * as InsuranceFileAPI from './insurance-file'
|
||||
export * as EmployeeAPI from './employee'
|
||||
export * as RegulationAPI from './regulation'
|
||||
|
||||
export * from './area/type'
|
||||
export * from './auth/type'
|
||||
|
|
|
@ -23,9 +23,9 @@ export function deleteTaskGroup(id: number) {
|
|||
return http.del(`${BASE_URL}/group/${id}`)
|
||||
}
|
||||
|
||||
/** @desc 查询任务列表 */
|
||||
export function listTask(query: T.TaskPageQuery) {
|
||||
return http.get<PageRes<T.TaskResp[]>>(`${BASE_URL}`, query)
|
||||
/** @desc 查询任务列表(标准导出) */
|
||||
export const listTask = (params: any) => {
|
||||
return http.get('/project-task/list', params)
|
||||
}
|
||||
|
||||
/** @desc 获取任务详情 */
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
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}`)
|
||||
},
|
||||
|
||||
// 发布制度
|
||||
publishRegulation: (regulationId: string) => {
|
||||
return http.post(`/regulation/${regulationId}/publish`)
|
||||
},
|
||||
|
||||
// 获取已发布制度列表
|
||||
getPublishedRegulationList: (params: {
|
||||
page: number
|
||||
size: number
|
||||
status: string
|
||||
}) => {
|
||||
return http.get('/regulation', params)
|
||||
},
|
||||
|
||||
// 确认制度知晓
|
||||
confirmRegulation: (regulationId: string) => {
|
||||
return http.post(`/regulation/${regulationId}/confirm`)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,57 @@
|
|||
// 制度状态枚举
|
||||
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 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
|
||||
}
|
||||
|
||||
// 创建提案请求接口
|
||||
export interface CreateProposalRequest {
|
||||
title: string
|
||||
content: string
|
||||
regulationType: string
|
||||
scope: string
|
||||
level: RegulationLevel
|
||||
remark?: string
|
||||
}
|
||||
|
||||
// 分页参数接口
|
||||
export interface PaginationParams {
|
||||
page: number
|
||||
size: number
|
||||
}
|
|
@ -4,6 +4,7 @@ import http from '@/utils/http'
|
|||
export type * from './type'
|
||||
|
||||
const BASE_URL = '/system/role'
|
||||
const BASE_URL_NEW = '/role'
|
||||
|
||||
/** @desc 查询角色列表(已废弃) */
|
||||
export function listRole(query: T.RoleQuery) {
|
||||
|
@ -72,7 +73,7 @@ export function updateRolePermission(id: string, data: any) {
|
|||
|
||||
/** @desc 查询角色关联用户 */
|
||||
export function listRoleUser(id: string, query: T.RoleUserPageQuery) {
|
||||
return http.get<PageRes<T.RoleUserResp[]>>(`${BASE_URL}/${id}/user`, query)
|
||||
return http.get<PageRes<T.RoleUserResp[]>>(`${BASE_URL_NEW}/${id}/user`, query)
|
||||
}
|
||||
|
||||
/** @desc 分配角色给用户 */
|
||||
|
@ -87,5 +88,5 @@ export function unassignFromUsers(userRoleIds: Array<string | number>) {
|
|||
|
||||
/** @desc 查询角色关联用户 ID */
|
||||
export function listRoleUserId(id: string) {
|
||||
return http.get(`${BASE_URL}/${id}/user/id`)
|
||||
return http.get(`${BASE_URL_NEW}/${id}/user`)
|
||||
}
|
||||
|
|
|
@ -27,202 +27,258 @@ export const systemRoutes: RouteRecordRaw[] = [
|
|||
// ],
|
||||
// },
|
||||
{
|
||||
path: '/organization',
|
||||
name: 'Organization',
|
||||
path: '/regulation',
|
||||
name: 'Regulation',
|
||||
component: Layout,
|
||||
redirect: '/organization/hr/member',
|
||||
meta: { title: '组织架构', icon: 'user-group', hidden: false, sort: 2 },
|
||||
redirect: '/regulation/system-regulation',
|
||||
meta: { title: '制度管理', icon: 'file-text', hidden: false, sort: 1.5 },
|
||||
children: [
|
||||
{
|
||||
path: '/organization/hr',
|
||||
name: 'HRManagement',
|
||||
component: () => import('@/components/ParentView/index.vue'),
|
||||
redirect: '/organization/hr/member',
|
||||
meta: { title: '人员管理', icon: 'user', hidden: false },
|
||||
children: [
|
||||
{
|
||||
path: '/organization/hr/member',
|
||||
name: 'HRMember',
|
||||
component: () => import('@/views/system/user/index.vue'),
|
||||
meta: { title: '成员', icon: 'user', hidden: false },
|
||||
},
|
||||
{
|
||||
path: '/organization/hr/dept',
|
||||
name: 'HRDept',
|
||||
component: () => import('@/views/system/dept/index.vue'),
|
||||
meta: { title: '部门', icon: 'dept', hidden: false },
|
||||
},
|
||||
{
|
||||
path: '/organization/hr/workload',
|
||||
name: 'HRWorkload',
|
||||
component: () => import('@/views/hr/workload/index.vue'),
|
||||
meta: { title: '工作量', icon: 'workload', hidden: false },
|
||||
},
|
||||
{
|
||||
path: '/organization/hr/attendance',
|
||||
name: 'HRAttendance',
|
||||
component: () => import('@/views/hr/attendance/index.vue'),
|
||||
meta: { title: '考勤', icon: 'attendance', hidden: false },
|
||||
},
|
||||
{
|
||||
path: '/organization/hr/performance',
|
||||
name: 'HRPerformance',
|
||||
component: () => import('@/components/ParentView/index.vue'),
|
||||
meta: { title: '绩效', icon: 'performance', hidden: false },
|
||||
children: [
|
||||
{
|
||||
path: '/organization/hr/performance/dimention',
|
||||
name: 'Dimention',
|
||||
component: () => import('@/views/performance/setting/index.vue'),
|
||||
meta: { title: '绩效维度', icon: 'performance', hidden: false },
|
||||
|
||||
},
|
||||
{
|
||||
path: '/organization/hr/performance/rule',
|
||||
name: 'Rule',
|
||||
component: () => import('@/views/performance/rule.vue'),
|
||||
meta: { title: '绩效细则', icon: 'performance', hidden: false },
|
||||
|
||||
},
|
||||
{
|
||||
path: '/organization/hr/performance/my',
|
||||
name: 'MyPerformance',
|
||||
component: () => import('@/views/performance/my.vue'),
|
||||
meta: { title: '我的绩效', icon: 'performance', hidden: false },
|
||||
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/organization/hr/salary',
|
||||
name: 'HRSalary',
|
||||
component: () => import('@/components/ParentView/index.vue'),
|
||||
redirect: '/organization/hr/salary/overview',
|
||||
meta: { title: '工资', icon: 'salary', hidden: false },
|
||||
children: [
|
||||
{
|
||||
path: '/organization/hr/salary/overview',
|
||||
name: 'HRSalaryOverview',
|
||||
component: () => import('@/components/ParentView/index.vue'),
|
||||
meta: { title: '工资概览', icon: 'salary', hidden: false },
|
||||
children: [
|
||||
{
|
||||
path: '/organization/hr/salary/payroll',
|
||||
name: 'Payroll',
|
||||
component: () => import('@/views/salary-management/index.vue'),
|
||||
meta: { title: '工资单', icon: 'salary', hidden: false },
|
||||
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
},
|
||||
// {
|
||||
// path: '/organization/hr/salary/insurance',
|
||||
// name: 'HRInsurance',
|
||||
// component: () => import('@/components/ParentView/index.vue'),
|
||||
// redirect: '/organization/hr/salary/insurance/overview',
|
||||
// meta: { title: '保险', icon: 'safety', hidden: false },
|
||||
// children: [
|
||||
// {
|
||||
// path: '/organization/hr/salary/insurance/overview',
|
||||
// name: 'HRInsuranceOverview',
|
||||
// component: () => import('@/views/hr/salary/insurance/overview/index.vue'),
|
||||
// meta: { title: '工作台概览', icon: 'dashboard', hidden: false },
|
||||
// },
|
||||
// {
|
||||
// path: '/organization/hr/salary/insurance/my-insurance',
|
||||
// name: 'HRMyInsurance',
|
||||
// component: () => import('@/views/hr/salary/insurance/my-insurance/index.vue'),
|
||||
// meta: { title: '我的保险', icon: 'shield', hidden: false },
|
||||
// },
|
||||
// {
|
||||
// path: '/organization/hr/salary/insurance/health-records',
|
||||
// name: 'HRHealthRecords',
|
||||
// component: () => import('@/views/hr/salary/insurance/health-records/index.vue'),
|
||||
// meta: { title: '健康档案', icon: 'heart', hidden: false },
|
||||
// },
|
||||
// {
|
||||
// path: '/organization/hr/salary/insurance/policy-files',
|
||||
// name: 'HRPolicyFiles',
|
||||
// component: () => import('@/views/hr/salary/insurance/policy-files/index.vue'),
|
||||
// meta: { title: '保单文件', icon: 'file', hidden: false },
|
||||
// },
|
||||
// {
|
||||
// path: '/organization/hr/salary/insurance/personal-info',
|
||||
// name: 'HRPersonalInfo',
|
||||
// component: () => import('@/views/hr/salary/insurance/personal-info/index.vue'),
|
||||
// meta: { title: '个人信息', icon: 'user', hidden: false },
|
||||
// }
|
||||
// ]
|
||||
// },
|
||||
{
|
||||
path: '/organization/hr/salary/system-insurance/health-management',
|
||||
name: 'HRSystemHealthManagement',
|
||||
component: () => import('@/views/hr/salary/system-insurance/health-management/index.vue'),
|
||||
meta: { title: '健康档案管理', icon: 'heart', hidden: false },
|
||||
},
|
||||
{
|
||||
path: '/organization/hr/salary/system-insurance',
|
||||
name: 'HRSystemInsurance',
|
||||
component: () => import('@/components/ParentView/index.vue'),
|
||||
redirect: '/organization/hr/salary/system-insurance/overview',
|
||||
meta: { title: '人员保险', icon: 'settings', hidden: false },
|
||||
children: [
|
||||
{
|
||||
path: '/organization/hr/salary/system-insurance/overview',
|
||||
name: 'HRSystemInsuranceOverview',
|
||||
component: () => import('@/views/hr/salary/system-insurance/overview/index.vue'),
|
||||
meta: { title: '工作台概览', icon: 'dashboard', hidden: false },
|
||||
},
|
||||
{
|
||||
path: '/organization/hr/salary/system-insurance/management',
|
||||
name: 'HRSystemInsuranceManagement',
|
||||
component: () => import('@/views/hr/salary/system-insurance/management/index.vue'),
|
||||
meta: { title: '保险管理', icon: 'shield', hidden: false },
|
||||
},
|
||||
{
|
||||
path: '/organization/hr/salary/system-insurance/file-management',
|
||||
name: 'HRSystemFileManagement',
|
||||
component: () => import('@/views/hr/salary/system-insurance/file-management/index.vue'),
|
||||
meta: { title: '保单文件管理', icon: 'file', hidden: false },
|
||||
},
|
||||
{
|
||||
path: '/organization/hr/salary/system-insurance/company-management',
|
||||
name: 'HRSystemCompanyManagement',
|
||||
component: () => import('@/views/hr/salary/system-insurance/company-management/index.vue'),
|
||||
meta: { title: '保险公司管理', icon: 'building', hidden: false },
|
||||
},
|
||||
{
|
||||
path: '/organization/hr/salary/system-insurance/type-management',
|
||||
name: 'HRSystemTypeManagement',
|
||||
component: () => import('@/views/hr/salary/system-insurance/type-management/index.vue'),
|
||||
meta: { title: '保险类型管理', icon: 'category', hidden: false },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/organization/hr/salary/certification',
|
||||
name: 'HRCertification',
|
||||
component: () => import('@/views/hr/salary/certification/index.vue'),
|
||||
meta: { title: '人员资质管理', icon: 'idcard', hidden: false },
|
||||
},
|
||||
{
|
||||
path: '/organization/hr/contribution',
|
||||
name: 'HRContribution',
|
||||
component: () => import('@/views/hr/contribution/index.vue'),
|
||||
meta: { title: '责献积分制度', icon: 'contribution', hidden: false },
|
||||
},
|
||||
],
|
||||
path: '/regulation/system-regulation',
|
||||
name: 'SystemRegulation',
|
||||
component: () => import('@/views/regulation/repository.vue'),
|
||||
meta: { title: '制度确认', icon: 'file-text', hidden: false },
|
||||
},
|
||||
{
|
||||
path: '/organization/role',
|
||||
name: 'OrganizationRole',
|
||||
component: () => import('@/views/system/role/index.vue'),
|
||||
meta: { title: '角色管理', icon: 'role', hidden: false },
|
||||
path: '/regulation/process-management',
|
||||
name: 'ProcessManagement',
|
||||
component: () => import('@/views/regulation/confirm.vue'),
|
||||
meta: { title: '流程管理', icon: 'workflow', hidden: false },
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/organization',
|
||||
name: 'Organization',
|
||||
component: Layout,
|
||||
redirect: '/organization/dept',
|
||||
meta: { title: '组织架构', icon: 'user-group', hidden: false, sort: 2 },
|
||||
children: [
|
||||
{
|
||||
path: '/organization/user',
|
||||
name: 'OrganizationUser',
|
||||
component: () => import('@/views/system/user/index.vue'),
|
||||
meta: { title: '用户管理', icon: 'user', hidden: false, sort: 2.25 },
|
||||
},
|
||||
{
|
||||
path: '/organization/dept',
|
||||
name: 'OrganizationDept',
|
||||
component: () => import('@/views/system/dept/index.vue'),
|
||||
meta: { title: '部门管理', icon: 'mind-mapping', hidden: false, sort: 2.5 },
|
||||
},
|
||||
{
|
||||
path: '/organization/post',
|
||||
name: 'OrganizationPost',
|
||||
component: () => import('@/views/system/post/index.vue'),
|
||||
meta: { title: '岗位管理', icon: 'nav', hidden: false, sort: 2.75 },
|
||||
},
|
||||
{
|
||||
path: '/organization/hr/workload',
|
||||
name: 'HRWorkload',
|
||||
component: () => import('@/views/hr/workload/index.vue'),
|
||||
meta: { title: '任务管理', icon: 'bookmark', hidden: false },
|
||||
},
|
||||
],
|
||||
},
|
||||
// {
|
||||
// path: '/organization',
|
||||
// name: 'Organization',
|
||||
// component: Layout,
|
||||
// redirect: '/organization/hr/member',
|
||||
// meta: { title: '组织架构', icon: 'user-group', hidden: false, sort: 2 },
|
||||
// children: [
|
||||
// {
|
||||
// path: '/organization/hr',
|
||||
// name: 'HRManagement',
|
||||
// component: () => import('@/components/ParentView/index.vue'),
|
||||
// redirect: '/organization/hr/member',
|
||||
// meta: { title: '人员管理', icon: 'user', hidden: false },
|
||||
// children: [
|
||||
// {
|
||||
// path: '/organization/hr/member',
|
||||
// name: 'HRMember',
|
||||
// component: () => import('@/views/system/user/index.vue'),
|
||||
// meta: { title: '成员', icon: 'user', hidden: false },
|
||||
// },
|
||||
// {
|
||||
// path: '/organization/hr/dept',
|
||||
// name: 'HRDept',
|
||||
// component: () => import('@/views/system/dept/index.vue'),
|
||||
// meta: { title: '部门', icon: 'dept', hidden: false },
|
||||
// },
|
||||
// {
|
||||
// path: '/organization/hr/workload',
|
||||
// name: 'HRWorkload',
|
||||
// component: () => import('@/views/hr/workload/index.vue'),
|
||||
// meta: { title: '任务管理', icon: 'workload', hidden: false },
|
||||
// },
|
||||
// {
|
||||
// path: '/organization/hr/attendance',
|
||||
// name: 'HRAttendance',
|
||||
// component: () => import('@/views/hr/attendance/index.vue'),
|
||||
// meta: { title: '考勤', icon: 'attendance', hidden: false },
|
||||
// },
|
||||
// {
|
||||
// path: '/organization/hr/performance',
|
||||
// name: 'HRPerformance',
|
||||
// component: () => import('@/components/ParentView/index.vue'),
|
||||
// meta: { title: '绩效', icon: 'performance', hidden: false },
|
||||
// children: [
|
||||
// {
|
||||
// path: '/organization/hr/performance/dimention',
|
||||
// name: 'Dimention',
|
||||
// component: () => import('@/views/performance/setting/index.vue'),
|
||||
// meta: { title: '绩效维度', icon: 'performance', hidden: false },
|
||||
//
|
||||
// },
|
||||
// {
|
||||
// path: '/organization/hr/performance/rule',
|
||||
// name: 'Rule',
|
||||
// component: () => import('@/views/performance/rule.vue'),
|
||||
// meta: { title: '绩效细则', icon: 'performance', hidden: false },
|
||||
//
|
||||
// },
|
||||
// {
|
||||
// path: '/organization/hr/performance/my',
|
||||
// name: 'MyPerformance',
|
||||
// component: () => import('@/views/performance/my.vue'),
|
||||
// meta: { title: '我的绩效', icon: 'performance', hidden: false },
|
||||
//
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// {
|
||||
// path: '/organization/hr/salary',
|
||||
// name: 'HRSalary',
|
||||
// component: () => import('@/components/ParentView/index.vue'),
|
||||
// redirect: '/organization/hr/salary/overview',
|
||||
// meta: { title: '工资', icon: 'salary', hidden: false },
|
||||
// children: [
|
||||
// {
|
||||
// path: '/organization/hr/salary/overview',
|
||||
// name: 'HRSalaryOverview',
|
||||
// component: () => import('@/components/ParentView/index.vue'),
|
||||
// meta: { title: '工资概览', icon: 'salary', hidden: false },
|
||||
// children: [
|
||||
// {
|
||||
// path: '/organization/hr/salary/payroll',
|
||||
// name: 'Payroll',
|
||||
// component: () => import('@/views/salary-management/index.vue'),
|
||||
// meta: { title: '工资单', icon: 'salary', hidden: false },
|
||||
//
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
//
|
||||
// {
|
||||
// path: '/organization/hr/salary/insurance',
|
||||
// name: 'HRInsurance',
|
||||
// component: () => import('@/components/ParentView/index.vue'),
|
||||
// redirect: '/organization/hr/salary/insurance/overview',
|
||||
// meta: { title: '保险', icon: 'safety', hidden: false },
|
||||
// children: [
|
||||
// {
|
||||
// path: '/organization/hr/salary/insurance/overview',
|
||||
// name: 'HRInsuranceOverview',
|
||||
// component: () => import('@/views/hr/salary/insurance/overview/index.vue'),
|
||||
// meta: { title: '工作台概览', icon: 'dashboard', hidden: false },
|
||||
// },
|
||||
// {
|
||||
// path: '/organization/hr/salary/insurance/my-insurance',
|
||||
// name: 'HRMyInsurance',
|
||||
// component: () => import('@/views/hr/salary/insurance/my-insurance/index.vue'),
|
||||
// meta: { title: '我的保险', icon: 'shield', hidden: false },
|
||||
// },
|
||||
// {
|
||||
// path: '/organization/hr/salary/insurance/health-records',
|
||||
// name: 'HRHealthRecords',
|
||||
// component: () => import('@/views/hr/salary/insurance/health-records/index.vue'),
|
||||
// meta: { title: '健康档案', icon: 'heart', hidden: false },
|
||||
// },
|
||||
// {
|
||||
// path: '/organization/hr/salary/insurance/policy-files',
|
||||
// name: 'HRPolicyFiles',
|
||||
// component: () => import('@/views/hr/salary/insurance/policy-files/index.vue'),
|
||||
// meta: { title: '保单文件', icon: 'file', hidden: false },
|
||||
// },
|
||||
// {
|
||||
// path: '/organization/hr/salary/insurance/personal-info',
|
||||
// name: 'HRPersonalInfo',
|
||||
// component: () => import('@/views/hr/salary/insurance/personal-info/index.vue'),
|
||||
// meta: { title: '个人信息', icon: 'user', hidden: false },
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
//
|
||||
// {
|
||||
// path: '/organization/hr/salary/system-insurance/health-management',
|
||||
// name: 'HRSystemHealthManagement',
|
||||
// component: () => import('@/views/hr/salary/system-insurance/health-management/index.vue'),
|
||||
// meta: { title: '健康档案管理', icon: 'heart', hidden: false },
|
||||
// },
|
||||
// {
|
||||
// path: '/organization/hr/salary/system-insurance',
|
||||
// name: 'HRSystemInsurance',
|
||||
// component: () => import('@/components/ParentView/index.vue'),
|
||||
// redirect: '/organization/hr/salary/system-insurance/overview',
|
||||
// meta: { title: '人员保险', icon: 'settings', hidden: false },
|
||||
// children: [
|
||||
// {
|
||||
// path: '/organization/hr/salary/system-insurance/overview',
|
||||
// name: 'HRSystemInsuranceOverview',
|
||||
// component: () => import('@/views/hr/salary/system-insurance/overview/index.vue'),
|
||||
// meta: { title: '工作台概览', icon: 'dashboard', hidden: false },
|
||||
// },
|
||||
// {
|
||||
// path: '/organization/hr/salary/system-insurance/management',
|
||||
// name: 'HRSystemInsuranceManagement',
|
||||
// component: () => import('@/views/hr/salary/system-insurance/management/index.vue'),
|
||||
// meta: { title: '保险管理', icon: 'shield', hidden: false },
|
||||
// },
|
||||
// {
|
||||
// path: '/organization/hr/salary/system-insurance/file-management',
|
||||
// name: 'HRSystemFileManagement',
|
||||
// component: () => import('@/views/hr/salary/system-insurance/file-management/index.vue'),
|
||||
// meta: { title: '保单文件管理', icon: 'file', hidden: false },
|
||||
// },
|
||||
// {
|
||||
// path: '/organization/hr/salary/system-insurance/company-management',
|
||||
// name: 'HRSystemCompanyManagement',
|
||||
// component: () => import('@/views/hr/salary/system-insurance/company-management/index.vue'),
|
||||
// meta: { title: '保险公司管理', icon: 'building', hidden: false },
|
||||
// },
|
||||
// {
|
||||
// path: '/organization/hr/salary/system-insurance/type-management',
|
||||
// name: 'HRSystemTypeManagement',
|
||||
// component: () => import('@/views/hr/salary/system-insurance/type-management/index.vue'),
|
||||
// meta: { title: '保险类型管理', icon: 'category', hidden: false },
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// {
|
||||
// path: '/organization/hr/salary/certification',
|
||||
// name: 'HRCertification',
|
||||
// component: () => import('@/views/hr/salary/certification/index.vue'),
|
||||
// meta: { title: '人员资质管理', icon: 'idcard', hidden: false },
|
||||
// },
|
||||
// {
|
||||
// path: '/organization/hr/contribution',
|
||||
// name: 'HRContribution',
|
||||
// component: () => import('@/views/hr/contribution/index.vue'),
|
||||
// meta: { title: '责献积分制度', icon: 'contribution', hidden: false },
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// {
|
||||
// path: '/organization/role',
|
||||
// name: 'OrganizationRole',
|
||||
// component: () => import('@/views/system/role/index.vue'),
|
||||
// meta: { title: '角色管理', icon: 'role', hidden: false },
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
{
|
||||
path: '/asset-management',
|
||||
name: 'AssetManagement',
|
||||
|
@ -947,6 +1003,30 @@ export const systemRoutes: RouteRecordRaw[] = [
|
|||
// }
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/user/profile',
|
||||
name: 'UserProfile',
|
||||
component: Layout,
|
||||
redirect: '/user/profile',
|
||||
meta: {
|
||||
title: '个人中心',
|
||||
icon: 'user',
|
||||
hidden: false,
|
||||
sort: 100,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '/user/profile',
|
||||
name: 'UserProfile',
|
||||
component: () => import('@/views/user/profile/index.vue'),
|
||||
meta: {
|
||||
title: '个人中心',
|
||||
icon: 'user',
|
||||
hidden: false,
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/enterprise-settings',
|
||||
name: 'EnterpriseSettings',
|
||||
|
@ -1150,7 +1230,6 @@ export const systemRoutes: RouteRecordRaw[] = [
|
|||
},
|
||||
],
|
||||
},
|
||||
|
||||
{
|
||||
path: '/',
|
||||
redirect: '/project-management/project-template/project-aproval',
|
||||
|
@ -1176,6 +1255,11 @@ export const constantRoutes: RouteRecordRaw[] = [
|
|||
},
|
||||
],
|
||||
},
|
||||
// {
|
||||
// path: '/user/profile',
|
||||
// component: () => import('@/views/user/profile/index.vue'),
|
||||
// meta: { hidden: true },
|
||||
// },
|
||||
{
|
||||
path: '/:pathMatch(.*)*',
|
||||
component: () => import('@/views/default/error/404.vue'),
|
||||
|
|
|
@ -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
|
||||
}
|
||||
})
|
|
@ -4,10 +4,9 @@ import type { RouteRecordRaw } from 'vue-router'
|
|||
import { mapTree, toTreeArray } from 'xe-utils'
|
||||
import { cloneDeep, omit } from 'lodash-es'
|
||||
import { constantRoutes, systemRoutes } from '@/router/route'
|
||||
import { type RouteItem, getUserRouteWithAdapter } from '@/apis'
|
||||
import type { RouteItem } from '@/apis'
|
||||
import { transformPathToName } from '@/utils'
|
||||
import { asyncRouteModules } from '@/router/asyncModules'
|
||||
import { convertMenuData, type ApiMenuItem } from '@/utils/menuConverter'
|
||||
|
||||
const layoutComponentMap = {
|
||||
Layout: () => import('@/layout/index.vue'),
|
||||
|
@ -94,91 +93,63 @@ const storeSetup = () => {
|
|||
// 获取路由数据并已通过适配器转换
|
||||
// const { data } = await getUserRouteWithAdapter()
|
||||
const data = [{
|
||||
"id": 1000,
|
||||
"parentId": 0,
|
||||
"title": "系统管理",
|
||||
"type": 1,
|
||||
"path": "/system",
|
||||
"name": "System",
|
||||
"component": "Layout",
|
||||
"redirect": "/system/user",
|
||||
"icon": "settings",
|
||||
"isExternal": false,
|
||||
"isCache": false,
|
||||
"isHidden": false,
|
||||
"sort": 1,
|
||||
"children": [
|
||||
{
|
||||
"id": 1010,
|
||||
"parentId": 1000,
|
||||
"title": "用户管理",
|
||||
"type": 2,
|
||||
"path": "/system/user",
|
||||
"name": "SystemUser",
|
||||
"component": "system/user/index",
|
||||
"icon": "user",
|
||||
"isExternal": false,
|
||||
"isCache": false,
|
||||
"isHidden": false,
|
||||
"sort": 1
|
||||
},
|
||||
{
|
||||
"id": 1030,
|
||||
"parentId": 1000,
|
||||
"title": "角色管理",
|
||||
"type": 2,
|
||||
"path": "/system/role",
|
||||
"name": "SystemRole",
|
||||
"component": "system/role/index",
|
||||
"icon": "user-group",
|
||||
"isExternal": false,
|
||||
"isCache": false,
|
||||
"isHidden": false,
|
||||
"sort": 2
|
||||
},
|
||||
{
|
||||
"id": 1050,
|
||||
"parentId": 1000,
|
||||
"title": "菜单管理",
|
||||
"type": 2,
|
||||
"path": "/system/menu",
|
||||
"name": "SystemMenu",
|
||||
"component": "system/menu/index",
|
||||
"icon": "menu",
|
||||
"isExternal": false,
|
||||
"isCache": false,
|
||||
"isHidden": false,
|
||||
"sort": 3
|
||||
},
|
||||
{
|
||||
"id": 1070,
|
||||
"parentId": 1000,
|
||||
"title": "部门管理",
|
||||
"type": 2,
|
||||
"path": "/system/dept",
|
||||
"name": "SystemDept",
|
||||
"component": "system/dept/index",
|
||||
"icon": "mind-mapping",
|
||||
"isExternal": false,
|
||||
"isCache": false,
|
||||
"isHidden": false,
|
||||
"sort": 4
|
||||
},
|
||||
{
|
||||
"id": 1090,
|
||||
"parentId": 1000,
|
||||
"title": "岗位管理",
|
||||
"type": 2,
|
||||
"path": "/system/post",
|
||||
"name": "SystemPost",
|
||||
"component": "system/post/index",
|
||||
"icon": "settings",
|
||||
"isExternal": false,
|
||||
"isCache": false,
|
||||
"isHidden": false,
|
||||
"sort": 5
|
||||
}
|
||||
]
|
||||
id: 1000,
|
||||
parentId: 0,
|
||||
title: '系统管理',
|
||||
type: 1,
|
||||
path: '/system',
|
||||
name: 'System',
|
||||
component: 'Layout',
|
||||
redirect: '/system/user',
|
||||
icon: 'settings',
|
||||
isExternal: false,
|
||||
isCache: false,
|
||||
isHidden: false,
|
||||
sort: 1,
|
||||
children: [
|
||||
{
|
||||
id: 1010,
|
||||
parentId: 1000,
|
||||
title: '用户管理',
|
||||
type: 2,
|
||||
path: '/system/user',
|
||||
name: 'SystemUser',
|
||||
component: 'system/user/index',
|
||||
icon: 'user',
|
||||
isExternal: false,
|
||||
isCache: false,
|
||||
isHidden: false,
|
||||
sort: 1,
|
||||
},
|
||||
{
|
||||
id: 1030,
|
||||
parentId: 1000,
|
||||
title: '角色管理',
|
||||
type: 2,
|
||||
path: '/system/role',
|
||||
name: 'SystemRole',
|
||||
component: 'system/role/index',
|
||||
icon: 'user-group',
|
||||
isExternal: false,
|
||||
isCache: false,
|
||||
isHidden: false,
|
||||
sort: 2,
|
||||
},
|
||||
{
|
||||
id: 1050,
|
||||
parentId: 1000,
|
||||
title: '菜单管理',
|
||||
type: 2,
|
||||
path: '/system/menu',
|
||||
name: 'SystemMenu',
|
||||
component: 'system/menu/index',
|
||||
icon: 'menu',
|
||||
isExternal: false,
|
||||
isCache: false,
|
||||
isHidden: false,
|
||||
sort: 3,
|
||||
},
|
||||
],
|
||||
}]
|
||||
// 使用已转换的数据生成路由
|
||||
const asyncRoutes = formatAsyncRoutes(data as unknown as RouteItem[])
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
<!-- 考勤统计 -->
|
||||
<template>
|
||||
<GiPageLayout>
|
||||
<GiTable
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
<!--任务管理-->
|
||||
<template>
|
||||
<GiPageLayout>
|
||||
<a-button type="primary" style="margin-bottom: 16px" @click="openAddModal">发布任务</a-button>
|
||||
<GiTable
|
||||
row-key="id"
|
||||
title="工作量管理"
|
||||
row-key="taskId"
|
||||
title="任务记录"
|
||||
:data="dataList"
|
||||
:columns="tableColumns"
|
||||
:loading="loading"
|
||||
|
@ -11,38 +13,74 @@
|
|||
@page-change="onPageChange"
|
||||
@page-size-change="onPageSizeChange"
|
||||
@refresh="search"
|
||||
@row-click="onRowClick"
|
||||
>
|
||||
<template #top>
|
||||
<GiForm
|
||||
v-model="searchForm"
|
||||
search
|
||||
:columns="queryFormColumns"
|
||||
size="medium"
|
||||
@search="search"
|
||||
<GiForm
|
||||
v-model="searchForm"
|
||||
search
|
||||
:columns="queryFormColumns"
|
||||
size="medium"
|
||||
@search="search"
|
||||
@reset="reset"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #toolbar-left>
|
||||
<a-button type="primary" @click="openAddModal">
|
||||
<template #icon><icon-plus /></template>
|
||||
<template #default>新增工作量记录</template>
|
||||
</a-button>
|
||||
<template #status="{ record }">
|
||||
<a-tag :color="getTaskStatusColor(record.status)">{{ getTaskStatusText(record.status) }}</a-tag>
|
||||
</template>
|
||||
|
||||
<!-- 工作量显示 -->
|
||||
<template #workload="{ record }">
|
||||
<span class="font-medium text-blue-600">{{ record.workload }}小时</span>
|
||||
</template>
|
||||
|
||||
<!-- 操作列 -->
|
||||
<template #action="{ record }">
|
||||
<a-space>
|
||||
<a-link @click="editRecord(record)">编辑</a-link>
|
||||
<a-link status="danger" @click="deleteRecord(record)">删除</a-link>
|
||||
<a-link @click.stop="onRowClick(record)">详情</a-link>
|
||||
<a-link status="danger" @click.stop="deleteRecord(record)">删除</a-link>
|
||||
</a-space>
|
||||
</template>
|
||||
</GiTable>
|
||||
<!-- 发布任务弹窗 -->
|
||||
<a-modal v-model:visible="showAddModal" title="发布任务" @ok="handleAddTask" @cancel="showAddModal = false">
|
||||
<a-form :model="addForm" label-width="90px">
|
||||
<a-form-item label="任务ID">
|
||||
<a-input v-model="addForm.taskId" disabled />
|
||||
</a-form-item>
|
||||
<a-form-item label="任务描述">
|
||||
<a-input v-model="addForm.remark" placeholder="请输入任务描述" />
|
||||
</a-form-item>
|
||||
<a-form-item label="创建时间">
|
||||
<a-input v-model="addForm.createTime" disabled />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
<!-- 任务详情弹窗 -->
|
||||
<a-modal v-model:visible="showDetailModal" title="任务详情" @ok="handleUpdateTask" @cancel="showDetailModal = false" :footer="false">
|
||||
<a-form :model="detailForm" label-width="90px">
|
||||
<a-form-item label="任务ID">
|
||||
<a-input v-model="detailForm.taskId" disabled />
|
||||
</a-form-item>
|
||||
<a-form-item label="任务描述">
|
||||
<a-input v-model="detailForm.remark" />
|
||||
</a-form-item>
|
||||
<a-form-item label="分配岗位">
|
||||
<a-select v-model="detailForm.deptName" :options="deptOptions" allow-clear placeholder="请选择岗位" />
|
||||
</a-form-item>
|
||||
<a-form-item label="分配人员">
|
||||
<a-select v-model="detailForm.mainUserId" :options="userOptions" allow-clear placeholder="请选择人员" />
|
||||
</a-form-item>
|
||||
<a-form-item label="任务状态">
|
||||
<a-input :value="getTaskStatusText(detailForm.status)" disabled />
|
||||
</a-form-item>
|
||||
<a-form-item label="创建时间">
|
||||
<a-input v-model="detailForm.createTime" disabled />
|
||||
</a-form-item>
|
||||
<a-form-item label="完成时间">
|
||||
<a-input v-model="detailForm.finishTime" disabled />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
<template #footer>
|
||||
<a-space style="float: right">
|
||||
<a-button status="danger" @click="handleDeleteTask">删除</a-button>
|
||||
<a-button type="primary" @click="handleUpdateTask">确定</a-button>
|
||||
</a-space>
|
||||
</template>
|
||||
</a-modal>
|
||||
</GiPageLayout>
|
||||
</template>
|
||||
|
||||
|
@ -50,13 +88,52 @@
|
|||
import { ref, reactive, onMounted } from 'vue'
|
||||
import { Message } from '@arco-design/web-vue'
|
||||
import type { TableColumnData } from '@arco-design/web-vue'
|
||||
// import { listTask } from '@/apis/project/task' // 接口调用处注释
|
||||
|
||||
// 任务状态映射
|
||||
// 0未分配,1已分配,2已完成,3已取消
|
||||
const statusOptions = [
|
||||
{ label: '未分配', value: 0 },
|
||||
{ label: '已分配', value: 1 },
|
||||
{ label: '已完成', value: 2 },
|
||||
{ label: '已取消', value: 3 }
|
||||
]
|
||||
const getTaskStatusColor = (status: number) => {
|
||||
const colorMap: Record<number, string> = {
|
||||
0: 'gray',
|
||||
1: 'blue',
|
||||
2: 'green',
|
||||
3: 'red'
|
||||
}
|
||||
return colorMap[status] || 'gray'
|
||||
}
|
||||
const getTaskStatusText = (status: number) => {
|
||||
const textMap: Record<number, string> = {
|
||||
0: '未分配',
|
||||
1: '已分配',
|
||||
2: '已完成',
|
||||
3: '已取消'
|
||||
}
|
||||
return textMap[status] || status
|
||||
}
|
||||
|
||||
// 示例岗位和人员
|
||||
const deptOptions = [
|
||||
{ label: '开发', value: '开发' },
|
||||
{ label: '测试', value: '测试' },
|
||||
{ label: '运维', value: '运维' }
|
||||
]
|
||||
const userOptions = [
|
||||
{ label: '张三', value: '张三' },
|
||||
{ label: '李四', value: '李四' },
|
||||
{ label: '王五', value: '王五' }
|
||||
]
|
||||
|
||||
// 搜索表单
|
||||
let searchForm = reactive({
|
||||
userName: '',
|
||||
projectName: '',
|
||||
startDate: '',
|
||||
endDate: '',
|
||||
taskName: '',
|
||||
responsiblePerson: '', // mainUserId
|
||||
status: '',
|
||||
page: 1,
|
||||
size: 10
|
||||
})
|
||||
|
@ -64,119 +141,241 @@ let searchForm = reactive({
|
|||
// 查询条件配置
|
||||
const queryFormColumns = [
|
||||
{
|
||||
field: 'userName',
|
||||
label: '员工姓名',
|
||||
field: 'taskName',
|
||||
label: '任务标题',
|
||||
type: 'input' as const,
|
||||
props: {
|
||||
placeholder: '请输入员工姓名'
|
||||
placeholder: '请输入任务标题',
|
||||
style: { width: '200px' }
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'projectName',
|
||||
label: '项目名称',
|
||||
field: 'responsiblePerson',
|
||||
label: '分配用户',
|
||||
type: 'input' as const,
|
||||
props: {
|
||||
placeholder: '请输入项目名称'
|
||||
placeholder: '请输入分配用户',
|
||||
style: { width: '200px' }
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'status',
|
||||
label: '任务状态',
|
||||
type: 'select' as const,
|
||||
props: {
|
||||
placeholder: '请选择任务状态',
|
||||
options: statusOptions,
|
||||
style: { width: '200px' }
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
// 表格列配置
|
||||
const tableColumns: TableColumnData[] = [
|
||||
{ title: '员工姓名', dataIndex: 'userName', width: 120 },
|
||||
{ title: '部门', dataIndex: 'deptName', width: 120 },
|
||||
{ title: '项目名称', dataIndex: 'projectName', width: 200, ellipsis: true, tooltip: true },
|
||||
{ title: '工作内容', dataIndex: 'workContent', width: 250, ellipsis: true, tooltip: true },
|
||||
{ title: '工作量(小时)', dataIndex: 'workload', slotName: 'workload', width: 120 },
|
||||
{ title: '工作日期', dataIndex: 'workDate', width: 120 },
|
||||
{ title: '任务ID', dataIndex: 'taskId', width: 80 },
|
||||
{ title: '任务标题', dataIndex: 'taskName', width: 200, ellipsis: true, tooltip: true },
|
||||
{ title: '分配用户', dataIndex: 'mainUserId', width: 120 },
|
||||
{ title: '分配部门', dataIndex: 'deptName', width: 120 },
|
||||
{ title: '任务内容', dataIndex: 'remark', width: 250, ellipsis: true, tooltip: true },
|
||||
{ title: '任务状态', dataIndex: 'status', slotName: 'status', width: 120 },
|
||||
{ title: '创建时间', dataIndex: 'createTime', width: 160 },
|
||||
{ title: '操作', slotName: 'action', width: 120, fixed: 'right' }
|
||||
]
|
||||
|
||||
// 数据状态
|
||||
const loading = ref(false)
|
||||
const dataList = ref([
|
||||
{
|
||||
id: 1,
|
||||
userName: '张三',
|
||||
deptName: '技术部',
|
||||
projectName: '企业管理系统',
|
||||
workContent: '前端开发',
|
||||
workload: 8,
|
||||
workDate: '2024-01-15',
|
||||
createTime: '2024-01-15 10:30:00'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
userName: '李四',
|
||||
deptName: '技术部',
|
||||
projectName: '移动端应用',
|
||||
workContent: '后端接口开发',
|
||||
workload: 6,
|
||||
workDate: '2024-01-15',
|
||||
createTime: '2024-01-15 11:20:00'
|
||||
}
|
||||
])
|
||||
|
||||
const dataList = ref<any[]>([])
|
||||
const allData = ref<any[]>([])
|
||||
const pagination = reactive({
|
||||
current: 1,
|
||||
pageSize: 10,
|
||||
total: 2,
|
||||
total: 0,
|
||||
showTotal: true,
|
||||
showPageSize: true
|
||||
})
|
||||
|
||||
// 搜索和重置
|
||||
const search = async () => {
|
||||
loading.value = true
|
||||
// 模拟API请求
|
||||
setTimeout(() => {
|
||||
loading.value = false
|
||||
}, 1000)
|
||||
// 发布任务弹窗
|
||||
const showAddModal = ref(false)
|
||||
const addForm = reactive({
|
||||
taskId: '',
|
||||
remark: '',
|
||||
createTime: ''
|
||||
})
|
||||
const openAddModal = () => {
|
||||
addForm.taskId = Date.now().toString()
|
||||
addForm.remark = ''
|
||||
addForm.createTime = new Date().toLocaleString()
|
||||
showAddModal.value = true
|
||||
}
|
||||
const handleAddTask = () => {
|
||||
if (!addForm.remark) {
|
||||
Message.warning('请输入任务描述')
|
||||
return
|
||||
}
|
||||
const newTask = {
|
||||
taskId: addForm.taskId,
|
||||
taskName: addForm.remark,
|
||||
mainUserId: '',
|
||||
deptName: '',
|
||||
remark: addForm.remark,
|
||||
status: 0, // 未分配
|
||||
createTime: addForm.createTime,
|
||||
finishTime: ''
|
||||
}
|
||||
allData.value.unshift(newTask)
|
||||
filterAndPaginate()
|
||||
showAddModal.value = false
|
||||
Message.success('任务发布成功')
|
||||
}
|
||||
|
||||
const reset = () => {
|
||||
// 任务详情弹窗
|
||||
const showDetailModal = ref(false)
|
||||
const detailForm = reactive({
|
||||
taskId: '',
|
||||
taskName: '',
|
||||
mainUserId: '',
|
||||
deptName: '',
|
||||
remark: '',
|
||||
status: 0,
|
||||
createTime: '',
|
||||
finishTime: ''
|
||||
})
|
||||
let detailIndex = -1
|
||||
const onRowClick = (record: any) => {
|
||||
Object.assign(detailForm, record)
|
||||
detailIndex = allData.value.findIndex(item => item.taskId === record.taskId)
|
||||
showDetailModal.value = true
|
||||
}
|
||||
const handleUpdateTask = () => {
|
||||
if (detailIndex !== -1) {
|
||||
allData.value[detailIndex] = { ...detailForm }
|
||||
filterAndPaginate()
|
||||
showDetailModal.value = false
|
||||
Message.success('任务更新成功')
|
||||
}
|
||||
}
|
||||
const handleDeleteTask = () => {
|
||||
if (detailIndex !== -1) {
|
||||
allData.value.splice(detailIndex, 1)
|
||||
filterAndPaginate()
|
||||
showDetailModal.value = false
|
||||
Message.success('任务已删除')
|
||||
}
|
||||
}
|
||||
|
||||
// 获取任务数据(用示例数据,接口调用处注释)
|
||||
const fetchTaskList = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
// const res = await listTask(params)
|
||||
// const rows = res?.rows || res?.data?.rows || []
|
||||
// allData.value = rows
|
||||
// 示例数据:
|
||||
allData.value = [
|
||||
{
|
||||
taskId: '1710000000000',
|
||||
taskName: '示例任务A',
|
||||
mainUserId: '张三',
|
||||
deptName: '开发',
|
||||
remark: '开发新功能',
|
||||
status: 1,
|
||||
createTime: '2024-05-01 10:00:00',
|
||||
finishTime: ''
|
||||
},
|
||||
{
|
||||
taskId: '1710000000001',
|
||||
taskName: '示例任务B',
|
||||
mainUserId: '',
|
||||
deptName: '',
|
||||
remark: '待分配任务',
|
||||
status: 0,
|
||||
createTime: '2024-05-02 11:00:00',
|
||||
finishTime: ''
|
||||
},
|
||||
{
|
||||
taskId: '1710000000002',
|
||||
taskName: '示例任务C',
|
||||
mainUserId: '李四',
|
||||
deptName: '测试',
|
||||
remark: '测试任务',
|
||||
status: 2,
|
||||
createTime: '2024-05-03 09:00:00',
|
||||
finishTime: '2024-05-05 18:00:00'
|
||||
}
|
||||
]
|
||||
filterAndPaginate()
|
||||
pagination.total = allData.value.length
|
||||
} catch (e) {
|
||||
Message.error('获取任务数据失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 前端筛选和分页
|
||||
const filterAndPaginate = () => {
|
||||
let filtered = allData.value
|
||||
if (searchForm.taskName) {
|
||||
filtered = filtered.filter((item: any) =>
|
||||
item.taskName?.toLowerCase().includes(searchForm.taskName.toLowerCase())
|
||||
)
|
||||
}
|
||||
if (searchForm.responsiblePerson) {
|
||||
filtered = filtered.filter((item: any) =>
|
||||
item.mainUserId?.toLowerCase().includes(searchForm.responsiblePerson.toLowerCase())
|
||||
)
|
||||
}
|
||||
if (searchForm.status !== '' && searchForm.status !== undefined) {
|
||||
filtered = filtered.filter((item: any) => String(item.status) === String(searchForm.status))
|
||||
}
|
||||
// 分页
|
||||
const start = (pagination.current - 1) * pagination.pageSize
|
||||
const end = start + pagination.pageSize
|
||||
dataList.value = filtered.slice(start, end)
|
||||
pagination.total = filtered.length
|
||||
}
|
||||
|
||||
// 搜索和重置
|
||||
const search = async () => {
|
||||
pagination.current = 1
|
||||
filterAndPaginate()
|
||||
}
|
||||
|
||||
const reset = async () => {
|
||||
Object.assign(searchForm, {
|
||||
userName: '',
|
||||
projectName: '',
|
||||
startDate: '',
|
||||
endDate: '',
|
||||
taskName: '',
|
||||
responsiblePerson: '',
|
||||
status: '',
|
||||
page: 1,
|
||||
size: 10
|
||||
})
|
||||
pagination.current = 1
|
||||
search()
|
||||
filterAndPaginate()
|
||||
}
|
||||
|
||||
// 分页处理
|
||||
const onPageChange = (page: number) => {
|
||||
searchForm.page = page
|
||||
pagination.current = page
|
||||
search()
|
||||
filterAndPaginate()
|
||||
}
|
||||
|
||||
const onPageSizeChange = (size: number) => {
|
||||
searchForm.size = size
|
||||
searchForm.page = 1
|
||||
pagination.pageSize = size
|
||||
pagination.current = 1
|
||||
search()
|
||||
}
|
||||
|
||||
// 操作方法
|
||||
const openAddModal = () => {
|
||||
Message.info('新增工作量记录功能开发中...')
|
||||
}
|
||||
|
||||
const editRecord = (record: any) => {
|
||||
Message.info(`编辑工作量记录: ${record.userName}`)
|
||||
filterAndPaginate()
|
||||
}
|
||||
|
||||
// 删除操作(表格操作列)
|
||||
const deleteRecord = (record: any) => {
|
||||
Message.info(`删除工作量记录: ${record.userName}`)
|
||||
const idx = allData.value.findIndex(item => item.taskId === record.taskId)
|
||||
if (idx !== -1) {
|
||||
allData.value.splice(idx, 1)
|
||||
filterAndPaginate()
|
||||
Message.success('任务已删除')
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
search()
|
||||
fetchTaskList()
|
||||
})
|
||||
</script>
|
|
@ -0,0 +1,509 @@
|
|||
<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
|
||||
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="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="detail-footer">
|
||||
<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,
|
||||
type Regulation
|
||||
} 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 currentProposal = ref<Regulation | null>(null)
|
||||
|
||||
// 详情相关
|
||||
const detailModalVisible = ref(false)
|
||||
|
||||
// 当前用户 - 应该从用户认证系统获取
|
||||
const currentUser = ref('') // TODO: 从用户认证系统获取当前用户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 handleDelete = async (record: Regulation) => {
|
||||
try {
|
||||
await regulationApi.deleteProposal(record.regulationId)
|
||||
Message.success('删除成功')
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
.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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
.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,350 @@
|
|||
<template>
|
||||
<div class="system-regulation">
|
||||
<a-card title="已发布制度确认" :bordered="false">
|
||||
|
||||
|
||||
<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,
|
||||
status: "PUBLISHED"
|
||||
})
|
||||
|
||||
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 submitConfirm = async () => {
|
||||
if (!agreeTerms.value) {
|
||||
Message.warning('请先同意承诺条款')
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
if (currentRegulation.value) {
|
||||
await regulationApi.confirmRegulation(currentRegulation.value.regulationId)
|
||||
|
||||
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>
|
|
@ -1,4 +1,5 @@
|
|||
<template>
|
||||
|
||||
<GiPageLayout>
|
||||
<div>
|
||||
<a-radio-group v-model="viewType" type="button" size="small" style="margin-bottom: 16px;">
|
||||
|
@ -9,13 +10,15 @@
|
|||
<GiTable
|
||||
v-show="viewType === 'table'"
|
||||
ref="tableRef"
|
||||
row-key="id"
|
||||
row-key="deptId"
|
||||
:data="dataList"
|
||||
:columns="columns"
|
||||
:loading="loading"
|
||||
:scroll="{ x: '100%', y: '100%', minWidth: 1000 }"
|
||||
:pagination="false"
|
||||
:disabled-column-keys="['name']"
|
||||
:expanded-keys="expandedKeys"
|
||||
@expand="onExpand"
|
||||
@refresh="search"
|
||||
>
|
||||
<template #expand-icon="{ expanded }">
|
||||
|
@ -49,6 +52,7 @@
|
|||
<a-link v-permission="['system:dept:update']" title="修改" @click="onUpdate(record)">修改</a-link>
|
||||
<a-link
|
||||
v-permission="['system:dept:delete']"
|
||||
:disabled="record.children && record.children.length > 0"
|
||||
status="danger"
|
||||
title="删除"
|
||||
@click="onDelete(record)"
|
||||
|
@ -69,14 +73,14 @@
|
|||
:collapsable="true"
|
||||
:horizontal="false"
|
||||
:define-menus="menus"
|
||||
:expand-all="true"
|
||||
:default-expand-level="999"
|
||||
:props="{ id: 'deptId', parentId: 'parentId', label: 'deptName', children: 'children' }"
|
||||
center
|
||||
:node-add="handleAdd"
|
||||
:node-delete="onDelete"
|
||||
:node-edit="onUpdate"
|
||||
@on-expand-all="bool => nodeExpandAll = bool"
|
||||
:expanded-keys="nodeExpandedKeys"
|
||||
@node-click="(node) => toggleExpand(node.deptId)"
|
||||
>
|
||||
</Vue3TreeOrg>
|
||||
</a-dropdown>
|
||||
|
@ -90,6 +94,7 @@
|
|||
import 'vue3-tree-org/lib/vue3-tree-org.css'
|
||||
import { Vue3TreeOrg } from 'vue3-tree-org'
|
||||
import type { TableInstance } from '@arco-design/web-vue'
|
||||
import { Message } from '@arco-design/web-vue'
|
||||
import DeptAddModal from './DeptAddModal.vue'
|
||||
import { type DeptQuery, type DeptResp, deleteDept, getDeptTree } from '@/apis/system/dept'
|
||||
import type GiTable from '@/components/GiTable/index.vue'
|
||||
|
@ -97,6 +102,8 @@ import { useDownload, useTable } from '@/hooks'
|
|||
import { isMobile } from '@/utils'
|
||||
import has from '@/utils/has'
|
||||
|
||||
const $message = Message
|
||||
|
||||
defineOptions({ name: 'SystemDept' })
|
||||
|
||||
const queryForm = reactive<DeptQuery>({})
|
||||
|
@ -109,9 +116,21 @@ const {
|
|||
} = useTable<DeptResp>(() => getDeptTree(queryForm), {
|
||||
immediate: true,
|
||||
onSuccess: () => {
|
||||
nextTick(() => {
|
||||
tableRef.value?.tableRef?.expandAll(true)
|
||||
})
|
||||
if (isFirstLoad.value) {
|
||||
// 初始全部展开:递归收集所有节点 id
|
||||
const collectKeys = (nodes: DeptResp[]): string[] => {
|
||||
let keys: string[] = []
|
||||
nodes.forEach(node => {
|
||||
keys.push(node.deptId)
|
||||
if (node.children && node.children.length) {
|
||||
keys = keys.concat(collectKeys(node.children))
|
||||
}
|
||||
})
|
||||
return keys
|
||||
}
|
||||
expandedKeys.value = collectKeys(tableData.value)
|
||||
isFirstLoad.value = false
|
||||
}
|
||||
},
|
||||
})
|
||||
// 查看视图类型
|
||||
|
@ -123,7 +142,9 @@ const menus = [
|
|||
{ name: '删除部门', command: 'delete' },
|
||||
]
|
||||
// 所有节点展开状态
|
||||
const nodeExpandAll = ref<boolean>(true)
|
||||
// 表格视图展开的节点
|
||||
const expandedKeys = ref<string[]>([])
|
||||
const isFirstLoad = ref(true)
|
||||
// 过滤树
|
||||
const searchData = (name: string) => {
|
||||
const loop = (data: DeptResp[]) => {
|
||||
|
@ -180,6 +201,13 @@ const reset = () => {
|
|||
|
||||
// 删除
|
||||
const onDelete = (record: DeptResp) => {
|
||||
// 检查是否存在子节点
|
||||
if (record.children && record.children.length > 0) {
|
||||
// 如果有子节点,显示提示信息
|
||||
$message.warning('该部门存在子部门,无法直接删除')
|
||||
return Promise.reject()
|
||||
}
|
||||
|
||||
return handleDelete(() => deleteDept(record.deptId), {
|
||||
content: `是否确定删除部门「${record.deptName}」?`,
|
||||
showModal: true,
|
||||
|
@ -204,6 +232,31 @@ const handleAdd = (record: DeptResp) => {
|
|||
const onUpdate = (record: DeptResp) => {
|
||||
DeptAddModalRef.value?.onUpdate(record.deptId)
|
||||
}
|
||||
|
||||
const onExpand = (expanded: boolean, record: DeptResp) => {
|
||||
const key = record.deptId
|
||||
if (expanded) {
|
||||
if (!expandedKeys.value.includes(key)) {
|
||||
expandedKeys.value.push(key)
|
||||
}
|
||||
} else {
|
||||
expandedKeys.value = expandedKeys.value.filter(k => k !== key)
|
||||
}
|
||||
}
|
||||
|
||||
// 添加一个方法来切换所有节点的展开/收缩状态
|
||||
const toggleExpandAll = () => {
|
||||
nodeExpandAll.value = !nodeExpandAll.value
|
||||
}
|
||||
|
||||
// 修改: 添加响应式展开状态和切换方法
|
||||
const nodeExpandedKeys = ref<string[]>([])
|
||||
const toggleExpand = (deptId: string) => {
|
||||
const index = nodeExpandedKeys.value.indexOf(deptId)
|
||||
index > -1
|
||||
? nodeExpandedKeys.value.splice(index, 1)
|
||||
: nodeExpandedKeys.value.push(deptId)
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
|
|
|
@ -27,8 +27,8 @@
|
|||
<a-radio :value="0">停用</a-radio>
|
||||
</a-radio-group>
|
||||
</a-form-item>
|
||||
<a-form-item field="remark" label="备注">
|
||||
<a-textarea v-model="formData.remark" placeholder="请输入备注" allow-clear />
|
||||
<a-form-item field="remark" label="说明">
|
||||
<a-textarea v-model="formData.remark" placeholder="请输入岗位说明" allow-clear />
|
||||
</a-form-item>
|
||||
</a-form>
|
||||
</a-modal>
|
||||
|
|
|
@ -2,65 +2,276 @@
|
|||
<a-drawer
|
||||
v-model:visible="visible"
|
||||
title="岗位详情"
|
||||
width="500px"
|
||||
:footer="false"
|
||||
width="620px"
|
||||
unmount-on-close
|
||||
class="post-detail-drawer"
|
||||
@close="() => visible = false"
|
||||
>
|
||||
<a-descriptions
|
||||
:data="detailData"
|
||||
:column="1"
|
||||
:align="{ label: 'right' }"
|
||||
label-style="width: 120px"
|
||||
size="medium"
|
||||
:loading="loading"
|
||||
border
|
||||
>
|
||||
<template #label="{ label }">{{ label }}</template>
|
||||
<template #value="{ value }">
|
||||
<span v-if="value !== undefined && value !== null">{{ value }}</span>
|
||||
<span v-else>-</span>
|
||||
</template>
|
||||
</a-descriptions>
|
||||
<a-spin :loading="loading" class="detail-container">
|
||||
<div class="detail-card">
|
||||
<div class="detail-header">
|
||||
<div class="post-name">{{ primaryInfo?.name || '-' }}</div>
|
||||
<a-tag class="status-tag" :color="getStatusColor(primaryInfo?.status)">
|
||||
{{ getStatusText(primaryInfo?.status) }}
|
||||
</a-tag>
|
||||
</div>
|
||||
|
||||
<div class="detail-group">
|
||||
<div class="group-title">基本信息</div>
|
||||
<div class="info-grid">
|
||||
<div class="info-item">
|
||||
<div class="info-label">岗位ID</div>
|
||||
<div class="info-value">{{ primaryInfo?.id || '-' }}</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">岗位排序</div>
|
||||
<div class="info-value">{{ primaryInfo?.sort || '-' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="primaryInfo?.remark" class="detail-group">
|
||||
<div class="group-title">岗位说明</div>
|
||||
<div class="remark-container">
|
||||
<div class="remark-content">{{ primaryInfo.remark }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="detail-group">
|
||||
<div class="group-title">时间信息</div>
|
||||
<div class="info-grid">
|
||||
<div class="info-item">
|
||||
<div class="info-label">创建时间</div>
|
||||
<div class="info-value">{{ primaryInfo?.createTime || '-' }}</div>
|
||||
</div>
|
||||
<div class="info-item">
|
||||
<div class="info-label">更新时间</div>
|
||||
<div class="info-value">{{primaryInfo?.updateTime || '-' }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a-spin>
|
||||
|
||||
<template #footer>
|
||||
<div class="footer-actions">
|
||||
<a-button type="primary" @click="handleClose">关闭详情</a-button>
|
||||
</div>
|
||||
</template>
|
||||
</a-drawer>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { nextTick, ref } from 'vue'
|
||||
import { getPostDetail } from '@/apis/system/post'
|
||||
import { useLoading } from '@/hooks'
|
||||
//import { formatDate } from '@/utils/date'
|
||||
|
||||
defineOptions({ name: 'PostDetailDrawer' })
|
||||
|
||||
const visible = ref(false)
|
||||
const { loading, setLoading } = useLoading()
|
||||
const detailData = ref<Array<{ label: string; value: any }>>([])
|
||||
const primaryInfo = ref<any>(null)
|
||||
|
||||
// 获取详情
|
||||
const getDetail = async (id: string) => {
|
||||
try {
|
||||
setLoading(true)
|
||||
const { data } = await getPostDetail(id)
|
||||
if (data) {
|
||||
detailData.value = [
|
||||
{ label: '岗位名称', value: data.postName },
|
||||
{ label: '岗位排序', value: data.postSort },
|
||||
{ label: '状态', value: Number(data.status) === 1 ? '正常' : '停用' },
|
||||
{ label: '备注', value: data.remark },
|
||||
{ label: '创建时间', value: data.createTime },
|
||||
{ label: '更新时间', value: data.updateTime },
|
||||
]
|
||||
primaryInfo.value = null
|
||||
|
||||
const response = await getPostDetail(id)
|
||||
const data = response?.data || response
|
||||
|
||||
if (data && typeof data === 'object') {
|
||||
primaryInfo.value = {
|
||||
id: data.postId ?? data.id,
|
||||
name: data.postName ?? data.name,
|
||||
sort: data.postSort,
|
||||
status: data.status,
|
||||
remark: data.remark,
|
||||
createTime: data.createTime,
|
||||
updateTime: data.updateTime,
|
||||
}
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('获取岗位详情失败:', error)
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
// 获取状态文本
|
||||
const getStatusText = (status: number | string) => {
|
||||
if (status === 1 || status === '1') return '正常'
|
||||
if (status === 0 || status === '0') return '停用'
|
||||
return '未知状态'
|
||||
}
|
||||
|
||||
// 获取状态标签颜色
|
||||
const getStatusColor = (status: number | string) => {
|
||||
if (status === 1 || status === '1') return 'rgb(82, 196, 26)'
|
||||
if (status === 0 || status === '0') return 'rgb(245, 34, 45)'
|
||||
return 'rgb(150, 150, 150)'
|
||||
}
|
||||
|
||||
// 关闭抽屉
|
||||
const handleClose = () => {
|
||||
visible.value = false
|
||||
}
|
||||
|
||||
// 查看详情
|
||||
const onDetail = async (id: string) => {
|
||||
if (!id) return
|
||||
visible.value = true
|
||||
await nextTick()
|
||||
await getDetail(id)
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
onDetail,
|
||||
})
|
||||
</script>
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.post-detail-drawer {
|
||||
--primary-color: #3498db;
|
||||
--light-bg: #f9fafc;
|
||||
--border-color: #eaeaea;
|
||||
--label-color: #666;
|
||||
--value-color: #333;
|
||||
--group-title-color: #555;
|
||||
--group-bg: #f5f7fa;
|
||||
}
|
||||
|
||||
.detail-container {
|
||||
padding: 16px 24px;
|
||||
}
|
||||
|
||||
.detail-card {
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
|
||||
padding: 20px;
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.detail-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
margin-bottom: 28px;
|
||||
padding-bottom: 20px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.post-name {
|
||||
font-size: 22px;
|
||||
font-weight: 600;
|
||||
color: var(--value-color);
|
||||
margin-right: 15px;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.status-tag {
|
||||
padding: 4px 12px;
|
||||
border-radius: 4px;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
border: none;
|
||||
color: white !important;
|
||||
}
|
||||
|
||||
.detail-group {
|
||||
margin-bottom: 25px;
|
||||
position: relative;
|
||||
background-color: var(--group-bg);
|
||||
border-radius: 8px;
|
||||
padding: 12px 16px;
|
||||
}
|
||||
|
||||
.detail-group:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.group-title {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: var(--group-title-color);
|
||||
padding-bottom: 12px;
|
||||
border-bottom: 1px solid var(--border-color);
|
||||
margin-bottom: 15px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.group-title::before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: 4px;
|
||||
height: 16px;
|
||||
background-color: var(--primary-color);
|
||||
margin-right: 8px;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.info-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||||
gap: 18px;
|
||||
}
|
||||
|
||||
.info-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
background: white;
|
||||
padding: 12px 15px;
|
||||
border-radius: 6px;
|
||||
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.03);
|
||||
border: 1px solid var(--border-color);
|
||||
transition: transform 0.2s, box-shadow 0.2s;
|
||||
}
|
||||
|
||||
.info-item:hover {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.05);
|
||||
}
|
||||
|
||||
.info-label {
|
||||
font-size: 14px;
|
||||
color: var(--label-color);
|
||||
margin-bottom: 6px;
|
||||
font-weight: 500;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.info-label::after {
|
||||
content: ':';
|
||||
margin-right: 4px;
|
||||
}
|
||||
|
||||
.info-value {
|
||||
font-size: 16px;
|
||||
color: var(--value-color);
|
||||
font-weight: 500;
|
||||
word-break: break-word;
|
||||
}
|
||||
|
||||
.remark-container {
|
||||
padding: 16px;
|
||||
background: white;
|
||||
border-radius: 6px;
|
||||
border: 1px solid var(--border-color);
|
||||
}
|
||||
|
||||
.remark-content {
|
||||
color: var(--value-color);
|
||||
line-height: 1.7;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
.footer-actions {
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding: 16px 24px;
|
||||
}
|
||||
</style>
|
||||
|
|
|
@ -26,6 +26,7 @@
|
|||
</template>
|
||||
<template #action="{ record }">
|
||||
<a-space>
|
||||
<a-link v-permission="['system:post:query']" title="详情" @click="onDetail(record)">详情</a-link>
|
||||
<a-link v-permission="['system:post:update']" title="修改" @click="onUpdate(record)">修改</a-link>
|
||||
<a-link
|
||||
v-permission="['system:post:delete']"
|
||||
|
@ -45,11 +46,11 @@
|
|||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import type { TableColumnData, TableInstance } from '@arco-design/web-vue'
|
||||
import type { TableColumnData } from '@arco-design/web-vue'
|
||||
import PostAddModal from './PostAddModal.vue'
|
||||
import PostDetailDrawer from './PostDetailDrawer.vue'
|
||||
import { deletePost, pagePost,listPost } from '@/apis/system/post'
|
||||
import type { PostVO, PostPageQuery } from '@/apis/system/type'
|
||||
import { deletePost, listPost } from '@/apis/system/post'
|
||||
import type { PostVO } from '@/apis/system/type'
|
||||
import { useResetReactive, useTable } from '@/hooks'
|
||||
import { isMobile } from '@/utils'
|
||||
import has from '@/utils/has'
|
||||
|
@ -80,7 +81,7 @@ const queryFormColumns: ColumnItem[] = reactive([
|
|||
props: {
|
||||
options: [
|
||||
{ label: '正常', value: 1 },
|
||||
{ label: '停用', value: 0 }
|
||||
{ label: '停用', value: 0 },
|
||||
],
|
||||
placeholder: '请选择状态',
|
||||
},
|
||||
|
@ -93,8 +94,8 @@ const {
|
|||
pagination,
|
||||
search,
|
||||
handleDelete,
|
||||
} = useTable((params) => listPost({
|
||||
...queryForm,
|
||||
} = useTable((params) => listPost({
|
||||
...queryForm,
|
||||
}), { immediate: true })
|
||||
|
||||
const tableColumns = ref<TableColumnData[]>([
|
||||
|
@ -105,50 +106,50 @@ const tableColumns = ref<TableColumnData[]>([
|
|||
render: ({ rowIndex }) => h('span', {}, rowIndex + 1 + (pagination.current - 1) * pagination.pageSize),
|
||||
fixed: !isMobile() ? 'left' : undefined,
|
||||
},
|
||||
{
|
||||
title: '岗位名称',
|
||||
dataIndex: 'postName',
|
||||
minWidth: 140,
|
||||
ellipsis: true,
|
||||
{
|
||||
title: '岗位名称',
|
||||
dataIndex: 'postName',
|
||||
minWidth: 140,
|
||||
ellipsis: true,
|
||||
tooltip: true,
|
||||
fixed: !isMobile() ? 'left' : undefined,
|
||||
},
|
||||
{
|
||||
title: '岗位排序',
|
||||
dataIndex: 'postSort',
|
||||
minWidth: 100,
|
||||
ellipsis: true,
|
||||
tooltip: true
|
||||
{
|
||||
title: '岗位排序',
|
||||
dataIndex: 'postSort',
|
||||
minWidth: 100,
|
||||
ellipsis: true,
|
||||
tooltip: true,
|
||||
},
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
slotName: 'status',
|
||||
align: 'center',
|
||||
width: 100
|
||||
{
|
||||
title: '状态',
|
||||
dataIndex: 'status',
|
||||
slotName: 'status',
|
||||
align: 'center',
|
||||
width: 100,
|
||||
},
|
||||
{
|
||||
title: '备注',
|
||||
dataIndex: 'remark',
|
||||
minWidth: 180,
|
||||
ellipsis: true,
|
||||
tooltip: true
|
||||
{
|
||||
title: '说明',
|
||||
dataIndex: 'remark',
|
||||
minWidth: 180,
|
||||
ellipsis: true,
|
||||
tooltip: true,
|
||||
},
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createTime',
|
||||
minWidth: 180,
|
||||
ellipsis: true,
|
||||
{
|
||||
title: '创建时间',
|
||||
dataIndex: 'createTime',
|
||||
minWidth: 180,
|
||||
ellipsis: true,
|
||||
tooltip: true,
|
||||
sortable: {
|
||||
sortDirections: ['ascend', 'descend'],
|
||||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
slotName: 'action',
|
||||
width: 160,
|
||||
width: 200,
|
||||
fixed: !isMobile() ? 'right' : undefined,
|
||||
show: has.hasPermOr(['system:post:update', 'system:post:delete']),
|
||||
},
|
||||
|
@ -187,4 +188,4 @@ const onDetail = (record: PostVO) => {
|
|||
}
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss"></style>
|
||||
<style scoped lang="scss"></style>
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
<template>
|
||||
<a-modal
|
||||
v-model:visible="visible"
|
||||
title="分配角色"
|
||||
:mask-closable="false"
|
||||
:esc-to-close="false"
|
||||
:width="width >= 1100 ? 1100 : '100%'"
|
||||
draggable
|
||||
@before-ok="save"
|
||||
@close="reset"
|
||||
v-model:visible="visible"
|
||||
title="分配角色"
|
||||
:mask-closable="false"
|
||||
:esc-to-close="false"
|
||||
:width="width >= 1100 ? 1100 : '100%'"
|
||||
draggable
|
||||
@before-ok="save"
|
||||
@close="reset"
|
||||
>
|
||||
<UserSelect v-if="visible" ref="UserSelectRef" v-model:value="selectedUsers" :role-id="dataId" @select-user="onSelectUser" />
|
||||
</a-modal>
|
||||
|
@ -17,6 +17,7 @@
|
|||
import { Message } from '@arco-design/web-vue'
|
||||
import { useWindowSize } from '@vueuse/core'
|
||||
import { assignToUsers } from '@/apis/system/role'
|
||||
// 根据角色分配用户
|
||||
|
||||
const emit = defineEmits<{
|
||||
(e: 'save-success'): void
|
||||
|
|
|
@ -98,21 +98,11 @@ const columns: TableInstance['columns'] = [
|
|||
render: ({ rowIndex }) => h('span', {}, rowIndex + 1 + (pagination.current - 1) * pagination.pageSize),
|
||||
fixed: !isMobile() ? 'left' : undefined,
|
||||
},
|
||||
{
|
||||
title: '昵称',
|
||||
dataIndex: 'nickname',
|
||||
slotName: 'nickname',
|
||||
minWidth: 130,
|
||||
ellipsis: true,
|
||||
tooltip: true,
|
||||
fixed: !isMobile() ? 'left' : undefined,
|
||||
},
|
||||
{ title: '用户名', dataIndex: 'username', slotName: 'username', minWidth: 120, ellipsis: true, tooltip: true },
|
||||
{ title: '状态', dataIndex: 'status', slotName: 'status', align: 'center' },
|
||||
{ title: '用户名', dataIndex: 'name', slotName: 'name', minWidth: 120, ellipsis: true, tooltip: true },
|
||||
{ title: '性别', dataIndex: 'gender', slotName: 'gender', align: 'center' },
|
||||
{ title: '状态', dataIndex: 'status', slotName: 'status', align: 'center' },
|
||||
{ title: '所属部门', dataIndex: 'deptName', minWidth: 140, ellipsis: true, tooltip: true },
|
||||
{ title: '角色', dataIndex: 'roleNames', slotName: 'roleNames', minWidth: 165 },
|
||||
{ title: '描述', dataIndex: 'description', minWidth: 130, ellipsis: true, tooltip: true },
|
||||
{
|
||||
title: '操作',
|
||||
dataIndex: 'action',
|
||||
|
@ -142,7 +132,7 @@ const onMulDelete = () => {
|
|||
content: `是否确定取消分配角色给所选的${selectedKeys.value.length}个用户?`,
|
||||
hideCancel: false,
|
||||
onOk: async () => {
|
||||
await unassignFromUsers(selectedKeys.value)
|
||||
await unassignFromUsers(selectedKeys.value.map(id => String(id)))
|
||||
Message.success('取消成功')
|
||||
search()
|
||||
},
|
||||
|
@ -151,7 +141,7 @@ const onMulDelete = () => {
|
|||
|
||||
// 删除
|
||||
const onDelete = (record: RoleUserResp) => {
|
||||
return handleDelete(() => unassignFromUsers([record.id]), {
|
||||
return handleDelete(() => unassignFromUsers([String(record.id)]), {
|
||||
content: `是否确定取消分配角色给用户「${record.nickname}(${record.username})」?`,
|
||||
successTip: '取消成功',
|
||||
showModal: true,
|
||||
|
|
|
@ -18,6 +18,7 @@
|
|||
import RoleTree from './tree/index.vue'
|
||||
import Permission from './components/Permission.vue'
|
||||
import RoleUser from './components/RoleUser.vue'
|
||||
import {listRoleUserId} from "@/apis";
|
||||
|
||||
defineOptions({ name: 'SystemRole' })
|
||||
|
||||
|
|
|
@ -60,7 +60,7 @@ const save = async () => {
|
|||
try {
|
||||
const isInvalid = await formRef.value?.formRef?.validate()
|
||||
if (isInvalid) return false
|
||||
await updateUserRole({ roleIds: form.roleIds }, dataId.value)
|
||||
await updateUserRole({ roleIds: form.roleIds.map(id => String(id)) }, dataId.value)
|
||||
Message.success('分配成功')
|
||||
emit('save-success')
|
||||
return true
|
||||
|
|
|
@ -93,7 +93,7 @@ const handleSubmit = async () => {
|
|||
|
||||
await bindUserRole({
|
||||
userId: props.userData.userId,
|
||||
roleIds: selectedRoles.value
|
||||
roleIds: selectedRoles.value.map(id => String(id))
|
||||
})
|
||||
|
||||
Message.success('角色分配成功')
|
||||
|
|
|
@ -313,17 +313,17 @@ const columns: TableInstance['columns'] = [
|
|||
},
|
||||
{ title: '账号', dataIndex: 'account', minWidth: 140, ellipsis: true, tooltip: true },
|
||||
{ title: '员工编码', dataIndex: 'userCode', minWidth: 120, ellipsis: true, tooltip: true },
|
||||
{ title: '在职状态', dataIndex: 'userStatus', slotName: 'userStatus', align: 'center', width: 100 },
|
||||
{ title: '员工性质', dataIndex: 'userType', slotName: 'userType', align: 'center', width: 100 },
|
||||
{ title: '性别', dataIndex: 'gender', slotName: 'gender', align: 'center', width: 80 },
|
||||
{ title: '所属部门', dataIndex: 'deptName', minWidth: 180, ellipsis: true, tooltip: true },
|
||||
{ title: '角色', dataIndex: 'roleIds', slotName: 'roleIds', minWidth: 165 },
|
||||
{ title: '手机号', dataIndex: 'mobile', minWidth: 170, ellipsis: true, tooltip: true },
|
||||
{ title: '邮箱', dataIndex: 'email', minWidth: 170, ellipsis: true, tooltip: true },
|
||||
{ title: '入职日期', dataIndex: 'hiredate', width: 120, ellipsis: true, tooltip: true },
|
||||
{ title: '出生日期', dataIndex: 'birthdate', width: 120, ellipsis: true, tooltip: true, show: false },
|
||||
{ title: '所属部门', dataIndex: 'deptName', minWidth: 180, ellipsis: true, tooltip: true },
|
||||
{ title: '学历', dataIndex: 'educationLabel', width: 100, ellipsis: true, tooltip: true, show: false },
|
||||
{ title: '专业', dataIndex: 'majorField', width: 120, ellipsis: true, tooltip: true, show: false },
|
||||
{ title: '在职状态', dataIndex: 'userStatus', slotName: 'userStatus', align: 'center', width: 100 },
|
||||
{ title: '员工性质', dataIndex: 'userType', slotName: 'userType', align: 'center', width: 100 },
|
||||
{ title: '角色', dataIndex: 'roleName', slotName: 'roleName', minWidth: 185 },
|
||||
{ title: '邮箱', dataIndex: 'email', slotName: 'email', minWidth: 170, ellipsis: true, tooltip: true },
|
||||
{ title: '入职日期', dataIndex: 'hiredate', width: 120, ellipsis: true, tooltip: true },
|
||||
{ title: '出生日期', dataIndex: 'birthdate', width: 120, ellipsis: true, tooltip: true, show: false },
|
||||
{ title: '工作方向', dataIndex: 'workField', width: 120, ellipsis: true, tooltip: true, show: false },
|
||||
{ title: '身份证', dataIndex: 'identityCard', width: 180, ellipsis: true, tooltip: true, show: false },
|
||||
{
|
||||
|
|
|
@ -17,25 +17,25 @@
|
|||
</template>
|
||||
</a-upload>
|
||||
<div class="name">
|
||||
<span style="margin-right: 10px">{{ userInfo.nickname }}</span>
|
||||
<span style="margin-right: 10px">{{ userInfo.name }}</span>
|
||||
<icon-edit :size="16" class="btn" @click="onUpdate" />
|
||||
</div>
|
||||
<div class="id">
|
||||
<GiSvgIcon name="id" :size="16" />
|
||||
<span>{{ userInfo.id }}</span>
|
||||
<span>{{ userInfo.userId }}</span>
|
||||
</div>
|
||||
</section>
|
||||
<footer>
|
||||
<a-descriptions :column="4" size="large">
|
||||
<a-descriptions-item :span="4">
|
||||
<template #label> <icon-user /><span style="margin-left: 5px">用户名</span></template>
|
||||
{{ userInfo.username }}
|
||||
{{ userInfo.account }}
|
||||
<icon-man v-if="userInfo.gender === 1" style="color: #19bbf1" />
|
||||
<icon-woman v-else-if="userInfo.gender === 2" style="color: #fa7fa9" />
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item :span="4">
|
||||
<template #label> <icon-phone /><span style="margin-left: 5px">手机</span></template>
|
||||
{{ userInfo.phone || '暂无' }}
|
||||
{{ userInfo.mobile || '暂无' }}
|
||||
</a-descriptions-item>
|
||||
<a-descriptions-item :span="4">
|
||||
<template #label> <icon-email /><span style="margin-left: 5px">邮箱</span></template>
|
||||
|
@ -47,12 +47,12 @@
|
|||
</a-descriptions-item>
|
||||
<a-descriptions-item :span="4">
|
||||
<template #label> <icon-user-group /><span style="margin-left: 5px">角色</span></template>
|
||||
{{ userInfo.roles.join(',') }}
|
||||
{{ userInfo.roles?.map(role => role.roleName).join(', ') || '暂无' }}
|
||||
</a-descriptions-item>
|
||||
</a-descriptions>
|
||||
</footer>
|
||||
</div>
|
||||
<div class="footer">注册于 {{ userInfo.registrationDate }}</div>
|
||||
<div class="footer">注册于 {{ userInfo.createTime }}</div>
|
||||
</a-card>
|
||||
|
||||
<a-modal v-model:visible="visible" title="上传头像" :width="width >= 400 ? 400 : '100%'" :footer="false" draggable @close="reset">
|
||||
|
@ -102,19 +102,22 @@ import { uploadAvatar } from '@/apis/system'
|
|||
import 'vue-cropper/dist/index.css'
|
||||
import { useUserStore } from '@/stores'
|
||||
import getAvatar from '@/utils/avatar'
|
||||
import XG_DEBUG from 'xgplayer/es/utils/debug'
|
||||
import config = XG_DEBUG.config
|
||||
|
||||
const { width } = useWindowSize()
|
||||
const userStore = useUserStore()
|
||||
const userInfo = computed(() => userStore.userInfo)
|
||||
|
||||
const avatar = {
|
||||
const avatar = computed(() => ({
|
||||
uid: '-2',
|
||||
name: 'avatar.png',
|
||||
url: userInfo.value.avatar,
|
||||
}
|
||||
const avatarList = ref<FileItem[]>([avatar])
|
||||
const fileRef = ref(reactive({ name: 'avatar.png' }))
|
||||
const options: cropperOptions = reactive({
|
||||
url: userInfo.value.avatar || getAvatar(userInfo.value.avatar, undefined),
|
||||
}))
|
||||
|
||||
const avatarList = computed<FileItem[]>(() => [avatar.value])
|
||||
const fileRef = ref<File | null>(null)
|
||||
const options = reactive<cropperOptions>({
|
||||
img: '',
|
||||
autoCrop: true,
|
||||
autoCropWidth: 160,
|
||||
|
@ -128,13 +131,14 @@ const options: cropperOptions = reactive({
|
|||
outputType: 'png',
|
||||
})
|
||||
const visible = ref(false)
|
||||
|
||||
// 打开裁剪框
|
||||
const onBeforeUpload = (file: File): boolean => {
|
||||
fileRef.value = file
|
||||
const reader = new FileReader()
|
||||
reader.readAsDataURL(file)
|
||||
reader.onload = () => {
|
||||
options.img = reader.result
|
||||
options.img = reader.result as string
|
||||
}
|
||||
visible.value = true
|
||||
return false
|
||||
|
|
Loading…
Reference in New Issue