更新人员组织查看工作组功能

This commit is contained in:
马诗敏 2025-08-15 09:46:31 +08:00
parent de3c826d4e
commit 4ea535b82f
3 changed files with 792 additions and 60 deletions

View File

@ -117,6 +117,86 @@ export interface ProjectKanbanStats {
teamLeaderCount: string teamLeaderCount: string
} }
// ==================== 工作组相关类型 ====================
/** 工作组类型 */
export interface WorkGroup {
id: string
name: string
description: string
projectId: string
status: 'ACTIVE' | 'INACTIVE' | 'COMPLETED'
workTypeGroups: WorkTypeGroup[]
memberCount: number
requiredCount: number
members?: WorkGroupMember[]
createTime: string
updateTime?: string
}
/** 工种分组类型 */
export interface WorkTypeGroup {
id?: string
name: string
requiredCount: number
priority: 'HIGH' | 'MEDIUM' | 'LOW'
assignedCount?: number
}
/** 工作组成员类型 */
export interface WorkGroupMember {
id: string
name: string
role: string
avatar?: string
phone?: string
email?: string
workTypes?: string[]
joinTime?: string
}
/** 工作组查询参数 */
export interface WorkGroupQuery {
projectId?: string
name?: string
status?: string
workType?: string
}
/** 工作组分页查询参数 */
export interface WorkGroupPageQuery extends WorkGroupQuery, PageQuery {}
/** 创建工作组请求参数 */
export interface CreateWorkGroupReq {
name: string
description?: string
projectId: string
workTypeGroups: WorkTypeGroup[]
}
/** 更新工作组请求参数 */
export interface UpdateWorkGroupReq {
id: string
name?: string
description?: string
status?: string
workTypeGroups?: WorkTypeGroup[]
}
/** 分配工人到工作组请求参数 */
export interface AssignWorkerReq {
workGroupId: string
workerIds: string[]
workTypeName: string
}
/** 从工作组移除工人请求参数 */
export interface RemoveWorkerReq {
workGroupId: string
workerId: string
workTypeName: string
}
/** 项目看板数据 */ /** 项目看板数据 */
export interface ProjectKanbanData { export interface ProjectKanbanData {
inProgressProjects: ProjectCard[] inProgressProjects: ProjectCard[]
@ -472,3 +552,88 @@ export interface PageRes<T> {
page: number page: number
pageSize: number pageSize: number
} }
// ==================== 工作组相关类型 ====================
/** 工作组类型 */
export interface WorkGroup {
id: string
name: string
description: string
projectId: string
status: 'ACTIVE' | 'INACTIVE' | 'COMPLETED'
workTypeGroups: WorkTypeGroup[]
memberCount: number
requiredCount: number
members?: WorkGroupMember[]
createTime: string
updateTime?: string
}
/** 工种分组类型 */
export interface WorkTypeGroup {
id?: string
name: string
requiredCount: number
priority: 'HIGH' | 'MEDIUM' | 'LOW'
assignedCount?: number
}
/** 工作组成员类型 */
export interface WorkGroupMember {
id: string
name: string
role: string
avatar?: string
phone?: string
email?: string
workTypes?: string[]
joinTime?: string
}
/** 工作组查询参数 */
export interface WorkGroupQuery {
projectId?: string
name?: string
status?: string
workType?: string
}
/** 工作组分页查询参数 */
export interface WorkGroupPageQuery extends WorkGroupQuery, PageQuery {}
/** 创建工作组请求参数 */
export interface CreateWorkGroupReq {
name: string
description?: string
projectId: string
workTypeGroups: WorkTypeGroup[]
}
/** 更新工作组请求参数 */
export interface UpdateWorkGroupReq {
id: string
name?: string
description?: string
status?: string
workTypeGroups?: WorkTypeGroup[]
}
/** 分配工人到工作组请求参数 */
export interface AssignWorkerReq {
workGroupId: string
workerIds: string[]
workTypeName: string
}
/** 从工作组移除工人请求参数 */
export interface RemoveWorkerReq {
workGroupId: string
workerId: string
workTypeName: string
}
// 扩展项目类型,包含工作组信息
export interface ProjectWithWorkGroups extends ProjectResp {
workGroups?: WorkGroup[]
}

View File

@ -0,0 +1,81 @@
import request from '@/utils/http'
import type {
WorkGroup,
WorkTypeGroup,
WorkGroupPageQuery,
CreateWorkGroupReq,
UpdateWorkGroupReq,
AssignWorkerReq,
RemoveWorkerReq,
PageRes
} from './type'
/**
*
*/
export function getWorkGroupList(params: WorkGroupPageQuery) {
return request.get<PageRes<WorkGroup>>('/api/work-groups', params)
}
/**
* ID获取工作组列表
*/
export function getWorkGroupsByProject(projectId: string) {
return request.get<WorkGroup[]>(`/api/projects/${projectId}/work-groups`)
}
/**
*
*/
export function getWorkGroupDetail(id: string) {
return request.get<WorkGroup>(`/api/work-groups/${id}`)
}
/**
*
*/
export function createWorkGroup(data: CreateWorkGroupReq) {
return request.post<WorkGroup>('/api/work-groups', data)
}
/**
*
*/
export function updateWorkGroup(id: string, data: UpdateWorkGroupReq) {
return request.put<WorkGroup>(`/api/work-groups/${id}`, data)
}
/**
*
*/
export function deleteWorkGroup(id: string) {
return request.del(`/api/work-groups/${id}`)
}
/**
*
*/
export function assignWorkerToGroup(data: AssignWorkerReq) {
return request.post('/api/work-groups/assign-worker', data)
}
/**
*
*/
export function removeWorkerFromGroup(data: RemoveWorkerReq) {
return request.post('/api/work-groups/remove-worker', data)
}
/**
*
*/
export function updateWorkGroupStatus(id: string, status: string) {
return request.patch<WorkGroup>(`/api/work-groups/${id}/status`, { status })
}
/**
*
*/
export function getWorkGroupStats(projectId?: string) {
return request.get('/api/work-groups/stats', { projectId })
}

View File

@ -1,5 +1,5 @@
<template> <template>
<GiPageLayout :body-style="{ overflow: 'auto' }"> <GiPageLayout>
<div class="personnel-organization-container"> <div class="personnel-organization-container">
<!-- 流程导航 --> <!-- 流程导航 -->
<div class="process-flow"> <div class="process-flow">
@ -60,11 +60,40 @@
<p class="project-farm">{{ project.farmName }}</p> <p class="project-farm">{{ project.farmName }}</p>
</div> </div>
<!-- 显示项目下的工作组信息 -->
<div class="project-work-groups" v-if="project.workGroups && project.workGroups.length > 0">
<div class="work-groups-header">
<icon-user-group />
<span>工作组 ({{ project.workGroups.length }})</span>
</div>
<div class="work-groups-list">
<div
v-for="workGroup in project.workGroups.slice(0, 3)"
:key="workGroup.id"
class="work-group-preview"
>
<span class="work-group-name">{{ workGroup.name }}</span>
<span class="work-group-count">{{ workGroup.memberCount }}/{{ workGroup.requiredCount }}</span>
</div>
<div v-if="project.workGroups.length > 3" class="more-work-groups">
+{{ project.workGroups.length - 3 }} 个工作组
</div>
</div>
</div>
<div class="project-actions"> <div class="project-actions">
<a-button type="primary" @click.stop="enterWorkGroupCreation(project)"> <a-button type="primary" @click.stop="enterWorkGroupCreation(project)">
<template #icon><icon-arrow-right /></template> <template #icon><icon-arrow-right /></template>
进入工作组创建 进入工作组创建
</a-button> </a-button>
<a-button
v-if="project.workGroups && project.workGroups.length > 0"
@click.stop="viewProjectWorkGroups(project)"
style="margin-top: 8px;"
>
<template #icon><icon-eye /></template>
查看工作组详情
</a-button>
</div> </div>
</div> </div>
</div> </div>
@ -412,6 +441,67 @@
</div> </div>
</div> </div>
</a-modal> </a-modal>
<!-- 工作组详情弹窗 -->
<a-modal
v-model:visible="workGroupDetailModalVisible"
title="工作组详情"
width="800px"
:footer="false"
>
<div class="work-group-detail">
<div class="project-info-section">
<h3>{{ selectedProjectForDetail?.projectName }}</h3>
<p class="project-code">项目编号: {{ selectedProjectForDetail?.projectCode }}</p>
</div>
<div class="work-groups-section">
<h4>工作组列表</h4>
<div class="work-groups-detail-list">
<div
v-for="workGroup in selectedProjectForDetail?.workGroups"
:key="workGroup.id"
class="work-group-detail-item"
>
<div class="work-group-detail-header">
<h5>{{ workGroup.name }}</h5>
<div class="work-group-detail-stats">
<span class="member-count">{{ workGroup.memberCount }}/{{ workGroup.requiredCount }}</span>
<a-tag :color="getWorkGroupStatusColor(workGroup.status)" size="small">
{{ getWorkGroupStatusText(workGroup.status) }}
</a-tag>
</div>
</div>
<div class="work-group-detail-members" v-if="workGroup.members && workGroup.members.length > 0">
<div class="members-header">
<span>成员列表</span>
</div>
<div class="members-grid">
<div
v-for="member in workGroup.members"
:key="member.id"
class="member-detail-item"
>
<a-avatar :src="member.avatar" :size="32">
{{ member.name.charAt(0) }}
</a-avatar>
<div class="member-detail-info">
<span class="member-name">{{ member.name }}</span>
<span class="member-role">{{ member.role }}</span>
</div>
</div>
</div>
</div>
<div class="work-group-detail-description" v-if="workGroup.description">
<p><strong>描述</strong>{{ workGroup.description }}</p>
</div>
</div>
</div>
</div>
</div>
</a-modal>
</div> </div>
</GiPageLayout> </GiPageLayout>
</template> </template>
@ -436,13 +526,57 @@ import {
IconCheck, IconCheck,
IconFilter, IconFilter,
IconClose, IconClose,
IconSave IconSave,
IconEye
} from '@arco-design/web-vue/es/icon' } from '@arco-design/web-vue/es/icon'
import type { import type {
ProjectResp, ProjectResp,
ConstructorResp, ConstructorResp,
CertificationResp CertificationResp,
WorkGroup,
WorkTypeGroup,
WorkGroupMember,
ProjectWithWorkGroups
} from '@/apis/project/type' } from '@/apis/project/type'
import {
getWorkGroupsByProject,
createWorkGroup as createWorkGroupAPI,
assignWorkerToGroup,
removeWorkerFromGroup
} from '@/apis/project/workGroup'
// 使
// interface WorkGroup {
// id: string
// name: string
// description: string
// projectId: string
// status: 'ACTIVE' | 'INACTIVE' | 'COMPLETED'
// workTypeGroups: WorkTypeGroup[]
// memberCount: number
// requiredCount: number
// members?: WorkGroupMember[]
// createTime: string
// }
// interface WorkTypeGroup {
// name: string
// requiredCount: number
// priority: 'HIGH' | 'MEDIUM' | 'LOW'
// }
// interface WorkGroupMember {
// id: string
// name: string
// role: string
// avatar?: string
// phone?: string
// }
//
// interface ProjectWithWorkGroups extends ProjectResp {
// workGroups?: WorkGroup[]
// }
defineOptions({ name: 'PersonnelOrganization' }) defineOptions({ name: 'PersonnelOrganization' })
@ -450,11 +584,13 @@ defineOptions({ name: 'PersonnelOrganization' })
const currentStep = ref(1) const currentStep = ref(1)
// //
const projectList = ref<ProjectResp[]>([]) const projectList = ref<ProjectWithWorkGroups[]>([])
const selectedProject = ref<ProjectResp | null>(null) const selectedProject = ref<ProjectWithWorkGroups | null>(null)
const selectedProjectForDetail = ref<ProjectWithWorkGroups | null>(null)
// //
const workGroupForm = reactive({ const workGroupForm = reactive({
id: '', // ID
name: '', name: '',
description: '', description: '',
projectId: '', projectId: '',
@ -483,6 +619,7 @@ const workerAssignments = ref<Record<string, ConstructorResp[]>>({})
// //
const certificateModalVisible = ref(false) const certificateModalVisible = ref(false)
const selectedWorkerCertificates = ref<CertificationResp[]>([]) const selectedWorkerCertificates = ref<CertificationResp[]>([])
const workGroupDetailModalVisible = ref(false)
// //
const draggedWorker = ref<ConstructorResp | null>(null) const draggedWorker = ref<ConstructorResp | null>(null)
@ -499,7 +636,7 @@ const getGroupMembers = (groupName: string) => {
// //
const loadProjectList = async () => { const loadProjectList = async () => {
try { try {
// 使 // 使
projectList.value = [ projectList.value = [
{ {
projectId: '1', projectId: '1',
@ -507,7 +644,44 @@ const loadProjectList = async () => {
projectName: '风电场A区建设项目', projectName: '风电场A区建设项目',
farmName: '风电场A区', farmName: '风电场A区',
status: 1, status: 1,
statusLabel: '进行中' statusLabel: '进行中',
workGroups: [
{
id: 'wg1',
name: '高空作业组',
description: '负责塔上登高作业',
projectId: '1',
status: 'ACTIVE',
workTypeGroups: [
{ name: '塔上登高作业', requiredCount: 5, priority: 'HIGH' }
],
memberCount: 3,
requiredCount: 5,
members: [
{ id: 'm1', name: '张登高', role: '组长', avatar: '' },
{ id: 'm2', name: '李高空', role: '组员', avatar: '' },
{ id: 'm3', name: '王高空', role: '组员', avatar: '' }
],
createTime: '2024-01-15'
},
{
id: 'wg2',
name: '地勤保障组',
description: '负责地面保障工作',
projectId: '1',
status: 'ACTIVE',
workTypeGroups: [
{ name: '地勤人员', requiredCount: 3, priority: 'MEDIUM' }
],
memberCount: 2,
requiredCount: 3,
members: [
{ id: 'm4', name: '赵地勤', role: '组长', avatar: '' },
{ id: 'm5', name: '孙地勤', role: '组员', avatar: '' }
],
createTime: '2024-01-16'
}
]
}, },
{ {
projectId: '2', projectId: '2',
@ -515,7 +689,29 @@ const loadProjectList = async () => {
projectName: '风电场B区维护项目', projectName: '风电场B区维护项目',
farmName: '风电场B区', farmName: '风电场B区',
status: 2, status: 2,
statusLabel: '已完成' statusLabel: '已完成',
workGroups: [
{
id: 'wg3',
name: '维护保养组',
description: '负责设备维护保养',
projectId: '2',
status: 'COMPLETED',
workTypeGroups: [
{ name: '机械工程师', requiredCount: 2, priority: 'HIGH' },
{ name: '电气工程师', requiredCount: 2, priority: 'HIGH' }
],
memberCount: 4,
requiredCount: 4,
members: [
{ id: 'm6', name: '周机械', role: '机械工程师', avatar: '' },
{ id: 'm7', name: '吴电气', role: '电气工程师', avatar: '' },
{ id: 'm8', name: '郑机械', role: '机械工程师', avatar: '' },
{ id: 'm9', name: '王电气', role: '电气工程师', avatar: '' }
],
createTime: '2024-01-10'
}
]
}, },
{ {
projectId: '3', projectId: '3',
@ -523,9 +719,21 @@ const loadProjectList = async () => {
projectName: '风电场C区升级项目', projectName: '风电场C区升级项目',
farmName: '风电场C区', farmName: '风电场C区',
status: 0, status: 0,
statusLabel: '准备中' statusLabel: '准备中',
workGroups: []
} }
] ]
//
for (const project of projectList.value) {
try {
const workGroups = await getWorkGroupsByProject(project.projectId)
project.workGroups = workGroups
} catch (error) {
console.warn(`加载项目 ${project.projectName} 的工作组失败:`, error)
//
}
}
} catch (error) { } catch (error) {
Message.error('加载项目列表失败') Message.error('加载项目列表失败')
} }
@ -662,11 +870,11 @@ const loadWorkers = async () => {
} }
} }
const selectProject = (project: ProjectResp) => { const selectProject = (project: ProjectWithWorkGroups) => {
selectedProject.value = project selectedProject.value = project
} }
const enterWorkGroupCreation = (project: ProjectResp) => { const enterWorkGroupCreation = (project: ProjectWithWorkGroups) => {
selectedProject.value = project selectedProject.value = project
workGroupForm.projectId = project.projectId workGroupForm.projectId = project.projectId
currentStep.value = 2 currentStep.value = 2
@ -696,7 +904,7 @@ const goBackToStep2 = () => {
currentStep.value = 2 currentStep.value = 2
} }
const createWorkGroup = () => { const createWorkGroup = async () => {
if (!workGroupForm.name || !workGroupForm.projectId) { if (!workGroupForm.name || !workGroupForm.projectId) {
Message.warning('请填写工作组名称和关联项目') Message.warning('请填写工作组名称和关联项目')
return return
@ -708,11 +916,38 @@ const createWorkGroup = () => {
return return
} }
try {
// API
const newWorkGroup = await createWorkGroupAPI({
name: workGroupForm.name,
description: workGroupForm.description,
projectId: workGroupForm.projectId,
workTypeGroups: workGroupForm.workTypeGroups
})
Message.success('工作组创建成功')
// ID
workGroupForm.id = newWorkGroup.id
//
if (selectedProject.value) {
if (!selectedProject.value.workGroups) {
selectedProject.value.workGroups = []
}
selectedProject.value.workGroups.push(newWorkGroup)
}
currentStep.value = 3 currentStep.value = 3
loadWorkers() loadWorkers()
} catch (error) {
Message.error('创建工作组失败')
console.error('创建工作组错误:', error)
}
} }
const resetWorkGroupForm = () => { const resetWorkGroupForm = () => {
workGroupForm.id = '' // ID
workGroupForm.name = '' workGroupForm.name = ''
workGroupForm.description = '' workGroupForm.description = ''
workGroupForm.projectId = '' workGroupForm.projectId = ''
@ -802,11 +1037,29 @@ const handleDrop = (event: DragEvent, group: any) => {
Message.success(`已将 ${draggedWorker.value.name} 分配到 ${groupName}`) Message.success(`已将 ${draggedWorker.value.name} 分配到 ${groupName}`)
} }
const removeFromGroup = (member: ConstructorResp, groupName: string) => { const removeFromGroup = async (member: ConstructorResp, groupName: string) => {
try {
if (!workGroupForm.id) {
Message.error('工作组ID不存在')
return
}
await removeWorkerFromGroup({
workGroupId: workGroupForm.id,
workerId: member.id,
workTypeName: groupName
})
//
const index = workerAssignments.value[groupName].findIndex(m => m.id === member.id) const index = workerAssignments.value[groupName].findIndex(m => m.id === member.id)
if (index > -1) { if (index > -1) {
workerAssignments.value[groupName].splice(index, 1) workerAssignments.value[groupName].splice(index, 1)
}
Message.success(`已从 ${groupName} 移除 ${member.name}`) Message.success(`已从 ${groupName} 移除 ${member.name}`)
} catch (error) {
Message.error('移除工人失败')
console.error('移除工人错误:', error)
} }
} }
@ -832,13 +1085,50 @@ const saveAssignment = async () => {
return return
} }
if (!workGroupForm.id) {
Message.error('工作组ID不存在')
return
}
// API
const assignmentPromises = []
for (const [workTypeName, workers] of Object.entries(workerAssignments.value)) {
if (workers.length > 0) {
const workerIds = workers.map(w => w.id)
assignmentPromises.push(
assignWorkerToGroup({
workGroupId: workGroupForm.id,
workerIds,
workTypeName
})
)
}
}
await Promise.all(assignmentPromises)
Message.success('分配保存成功') Message.success('分配保存成功')
// API
//
if (selectedProject.value) {
try {
const workGroups = await getWorkGroupsByProject(selectedProject.value.projectId)
selectedProject.value.workGroups = workGroups
} catch (error) {
console.warn('刷新工作组信息失败:', error)
}
}
} catch (error) { } catch (error) {
Message.error('保存分配失败') Message.error('保存分配失败')
console.error('保存分配错误:', error)
} }
} }
const viewProjectWorkGroups = (project: ProjectWithWorkGroups) => {
selectedProjectForDetail.value = project
workGroupDetailModalVisible.value = true
}
// //
const getStatusColor = (status: number) => { const getStatusColor = (status: number) => {
const statusMap: Record<number, string> = { const statusMap: Record<number, string> = {
@ -885,6 +1175,24 @@ const getCertificateStatusText = (status: string) => {
return statusMap[status] || status return statusMap[status] || status
} }
const getWorkGroupStatusColor = (status: string) => {
const statusMap: Record<string, string> = {
'ACTIVE': 'green',
'INACTIVE': 'red',
'COMPLETED': 'gray'
}
return statusMap[status] || 'gray'
}
const getWorkGroupStatusText = (status: string) => {
const statusMap: Record<string, string> = {
'ACTIVE': '进行中',
'INACTIVE': '已暂停',
'COMPLETED': '已完成'
}
return statusMap[status] || status
}
// //
onMounted(async () => { onMounted(async () => {
await loadProjectList() await loadProjectList()
@ -896,16 +1204,7 @@ onMounted(async () => {
padding: 20px; padding: 20px;
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%); background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
min-height: 100vh; min-height: 100vh;
overflow-y: auto; overflow-y: auto; //
height: auto;
position: relative;
z-index: 1;
box-sizing: border-box;
}
//
:deep(.gi-page-layout__body) {
overflow: auto !important;
} }
// //
@ -918,6 +1217,7 @@ onMounted(async () => {
background: white; background: white;
border-radius: 16px; border-radius: 16px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08); box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
flex-shrink: 0; //
} }
.flow-step { .flow-step {
@ -958,8 +1258,7 @@ onMounted(async () => {
.step-content { .step-content {
max-width: 1200px; max-width: 1200px;
margin: 0 auto; margin: 0 auto;
overflow-y: auto; margin-bottom: 30px; //
height: auto;
} }
// //
@ -1047,8 +1346,49 @@ onMounted(async () => {
} }
} }
.project-work-groups {
margin-top: 20px;
padding-top: 16px;
border-top: 1px solid #e9ecef;
.work-groups-header {
display: flex;
align-items: center;
gap: 8px;
font-size: 16px;
font-weight: 600;
color: #1d2129;
margin-bottom: 12px;
}
.work-groups-list {
display: flex;
flex-wrap: wrap;
gap: 12px;
}
.work-group-preview {
background: #f8f9fa;
border: 1px solid #e9ecef;
border-radius: 8px;
padding: 8px 12px;
font-size: 14px;
color: #1d2129;
display: flex;
align-items: center;
gap: 8px;
}
.more-work-groups {
font-size: 14px;
color: #86909c;
font-weight: 500;
}
}
.project-actions { .project-actions {
text-align: center; text-align: center;
margin-top: 20px;
} }
// //
@ -1103,16 +1443,14 @@ onMounted(async () => {
display: grid; display: grid;
grid-template-columns: 280px 1fr 320px; grid-template-columns: 280px 1fr 320px;
gap: 24px; gap: 24px;
min-height: calc(100vh - 200px); min-height: 600px; //
height: auto;
} }
.filter-sidebar, .filter-sidebar,
.work-groups-sidebar { .work-groups-sidebar {
.filter-card, .filter-card,
.work-groups-card { .work-groups-card {
min-height: 100%; height: 100%;
height: auto;
border-radius: 16px; border-radius: 16px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08); box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
@ -1134,8 +1472,7 @@ onMounted(async () => {
.worker-table-section { .worker-table-section {
.worker-table-card { .worker-table-card {
min-height: 100%; height: 100%;
height: auto;
border-radius: 16px; border-radius: 16px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08); box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08);
@ -1155,9 +1492,8 @@ onMounted(async () => {
} }
.worker-table { .worker-table {
min-height: calc(100vh - 300px); max-height: 500px; //
max-height: calc(100vh - 300px); overflow-y: auto; //
overflow-y: auto;
padding: 16px; padding: 16px;
} }
@ -1254,9 +1590,8 @@ onMounted(async () => {
// //
.work-groups-content { .work-groups-content {
min-height: calc(100vh - 300px); max-height: 400px; //
max-height: calc(100vh - 300px); overflow-y: auto; //
overflow-y: auto;
padding: 16px; padding: 16px;
} }
@ -1382,4 +1717,155 @@ onMounted(async () => {
color: #4e5969; color: #4e5969;
} }
} }
//
.work-group-detail {
.project-info-section {
margin-bottom: 20px;
padding-bottom: 16px;
border-bottom: 1px solid #e9ecef;
h3 {
margin: 0 0 8px 0;
font-size: 18px;
font-weight: 600;
color: #1d2129;
}
.project-code {
font-size: 14px;
color: #86909c;
}
}
.work-groups-section {
h4 {
margin: 0 0 16px 0;
font-size: 16px;
font-weight: 600;
color: #1d2129;
}
.work-groups-detail-list {
.work-group-detail-item {
background: #f8f9fa;
border: 1px solid #e9ecef;
border-radius: 8px;
padding: 16px;
margin-bottom: 16px;
.work-group-detail-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
h5 {
margin: 0;
font-size: 15px;
font-weight: 600;
color: #1d2129;
}
.work-group-detail-stats {
display: flex;
align-items: center;
gap: 8px;
font-size: 14px;
color: #86909c;
.member-count {
color: #52c41a;
}
}
}
.work-group-detail-members {
.members-header {
margin-bottom: 12px;
font-size: 14px;
color: #1d2129;
font-weight: 500;
}
.members-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(100px, 1fr));
gap: 12px;
}
.member-detail-item {
display: flex;
align-items: center;
gap: 12px;
.member-detail-info {
.member-name {
font-size: 14px;
color: #1d2129;
font-weight: 600;
}
.member-role {
font-size: 12px;
color: #86909c;
}
}
}
}
.work-group-detail-description {
p {
margin: 0;
font-size: 13px;
color: #4e5969;
}
}
}
}
}
}
//
@media (max-width: 1200px) {
.worker-assignment-layout {
grid-template-columns: 250px 1fr 280px;
gap: 16px;
}
}
@media (max-width: 992px) {
.worker-assignment-layout {
grid-template-columns: 1fr;
gap: 16px;
}
.filter-sidebar,
.work-groups-sidebar {
order: 2;
}
.worker-table-section {
order: 1;
}
}
@media (max-width: 768px) {
.personnel-organization-container {
padding: 16px;
}
.project-grid {
grid-template-columns: 1fr;
gap: 16px;
}
.process-flow {
flex-direction: column;
gap: 16px;
.flow-arrow {
transform: rotate(90deg);
}
}
}
</style> </style>