diff --git a/src/apis/project/type.ts b/src/apis/project/type.ts index cbdb186..506cb93 100644 --- a/src/apis/project/type.ts +++ b/src/apis/project/type.ts @@ -117,17 +117,22 @@ export interface ProjectKanbanStats { /** 项目看板数据 */ export interface ProjectKanbanData { - inProgressProjects: never[] + inProgressProjects: ProjectCard[] preparingProjects: ProjectCard[] ongoingProjects: ProjectCard[] pendingProjects: ProjectCard[] + suspendedProjects: ProjectCard[] + completedProjects: ProjectCard[] + acceptanceProjects: ProjectCard[] + collectionProjects: ProjectCard[] + settledProjects: ProjectCard[] } /** 项目卡片信息 */ export interface ProjectCard { id: string | number name: string - status: 'preparing' | 'ongoing' | 'pending' + status: number | 'preparing' | 'ongoing' | 'pending' | 'inProgress' | 'suspended' | 'completed' | 'acceptance' | 'collection' | 'settled' budget: number manager: string teamSize: number @@ -139,6 +144,9 @@ export interface ProjectCard { alerts?: ProjectAlert[] teamMembers: TeamMemberResp[] requirements: ProjectRequirementResp[] + farmName?: string + scale?: string + createTime?: string } /** 项目异常信息 */ diff --git a/src/types/auto-imports.d.ts b/src/types/auto-imports.d.ts index 369aad4..eab6be6 100644 --- a/src/types/auto-imports.d.ts +++ b/src/types/auto-imports.d.ts @@ -70,6 +70,6 @@ declare global { // for type re-export declare global { // @ts-ignore - export type { Component, ComponentPublicInstance, ComputedRef, DirectiveBinding, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, MaybeRef, MaybeRefOrGetter, VNode, WritableComputedRef } from 'vue' + export type { Component, ComponentPublicInstance, ComputedRef, ExtractDefaultPropTypes, ExtractPropTypes, ExtractPublicPropTypes, InjectionKey, PropType, Ref, VNode, WritableComputedRef } from 'vue' import('vue') } diff --git a/src/types/components.d.ts b/src/types/components.d.ts index 7fa6b1b..73dac61 100644 --- a/src/types/components.d.ts +++ b/src/types/components.d.ts @@ -7,7 +7,69 @@ export {} declare module 'vue' { export interface GlobalComponents { + ApprovalAssistant: typeof import('./../components/ApprovalAssistant/index.vue')['default'] + ApprovalMessageItem: typeof import('./../components/NotificationCenter/ApprovalMessageItem.vue')['default'] + Avatar: typeof import('./../components/Avatar/index.vue')['default'] + Breadcrumb: typeof import('./../components/Breadcrumb/index.vue')['default'] + CellCopy: typeof import('./../components/CellCopy/index.vue')['default'] + Chart: typeof import('./../components/Chart/index.vue')['default'] + ColumnSetting: typeof import('./../components/GiTable/src/components/ColumnSetting.vue')['default'] + CronForm: typeof import('./../components/GenCron/CronForm/index.vue')['default'] + CronModal: typeof import('./../components/GenCron/CronModal/index.vue')['default'] + DateRangePicker: typeof import('./../components/DateRangePicker/index.vue')['default'] + DayForm: typeof import('./../components/GenCron/CronForm/component/day-form.vue')['default'] + FilePreview: typeof import('./../components/FilePreview/index.vue')['default'] + GiCellAvatar: typeof import('./../components/GiCell/GiCellAvatar.vue')['default'] + GiCellGender: typeof import('./../components/GiCell/GiCellGender.vue')['default'] + GiCellStatus: typeof import('./../components/GiCell/GiCellStatus.vue')['default'] + GiCellTag: typeof import('./../components/GiCell/GiCellTag.vue')['default'] + GiCellTags: typeof import('./../components/GiCell/GiCellTags.vue')['default'] + GiCodeView: typeof import('./../components/GiCodeView/index.vue')['default'] + GiDot: typeof import('./../components/GiDot/index.tsx')['default'] + GiEditTable: typeof import('./../components/GiEditTable/GiEditTable.vue')['default'] + GiFooter: typeof import('./../components/GiFooter/index.vue')['default'] + GiForm: typeof import('./../components/GiForm/src/GiForm.vue')['default'] + GiIconBox: typeof import('./../components/GiIconBox/index.vue')['default'] + GiIconSelector: typeof import('./../components/GiIconSelector/index.vue')['default'] + GiIframe: typeof import('./../components/GiIframe/index.vue')['default'] + GiOption: typeof import('./../components/GiOption/index.vue')['default'] + GiOptionItem: typeof import('./../components/GiOptionItem/index.vue')['default'] + GiPageLayout: typeof import('./../components/GiPageLayout/index.vue')['default'] + GiSpace: typeof import('./../components/GiSpace/index.vue')['default'] + GiSplitButton: typeof import('./../components/GiSplitButton/index.vue')['default'] + GiSplitPane: typeof import('./../components/GiSplitPane/index.vue')['default'] + GiSplitPaneFlexibleBox: typeof import('./../components/GiSplitPane/components/GiSplitPaneFlexibleBox.vue')['default'] + GiSvgIcon: typeof import('./../components/GiSvgIcon/index.vue')['default'] + GiTable: typeof import('./../components/GiTable/src/GiTable.vue')['default'] + GiTag: typeof import('./../components/GiTag/index.tsx')['default'] + GiThemeBtn: typeof import('./../components/GiThemeBtn/index.vue')['default'] + HourForm: typeof import('./../components/GenCron/CronForm/component/hour-form.vue')['default'] + Icon403: typeof import('./../components/icons/Icon403.vue')['default'] + Icon404: typeof import('./../components/icons/Icon404.vue')['default'] + Icon500: typeof import('./../components/icons/Icon500.vue')['default'] + IconBorders: typeof import('./../components/icons/IconBorders.vue')['default'] + IconTableSize: typeof import('./../components/icons/IconTableSize.vue')['default'] + IconTreeAdd: typeof import('./../components/icons/IconTreeAdd.vue')['default'] + IconTreeReduce: typeof import('./../components/icons/IconTreeReduce.vue')['default'] + ImageImport: typeof import('./../components/ImageImport/index.vue')['default'] + ImageImportWizard: typeof import('./../components/ImageImportWizard/index.vue')['default'] + IndustrialImageList: typeof import('./../components/IndustrialImageList/index.vue')['default'] + JsonPretty: typeof import('./../components/JsonPretty/index.vue')['default'] + MinuteForm: typeof import('./../components/GenCron/CronForm/component/minute-form.vue')['default'] + MonthForm: typeof import('./../components/GenCron/CronForm/component/month-form.vue')['default'] + NotificationCenter: typeof import('./../components/NotificationCenter/index.vue')['default'] + ParentView: typeof import('./../components/ParentView/index.vue')['default'] RouterLink: typeof import('vue-router')['RouterLink'] RouterView: typeof import('vue-router')['RouterView'] + SecondForm: typeof import('./../components/GenCron/CronForm/component/second-form.vue')['default'] + SplitPanel: typeof import('./../components/SplitPanel/index.vue')['default'] + TextCopy: typeof import('./../components/TextCopy/index.vue')['default'] + TurbineGrid: typeof import('./../components/TurbineGrid/index.vue')['default'] + UserSelect: typeof import('./../components/UserSelect/index.vue')['default'] + Verify: typeof import('./../components/Verify/index.vue')['default'] + VerifyPoints: typeof import('./../components/Verify/Verify/VerifyPoints.vue')['default'] + VerifySlide: typeof import('./../components/Verify/Verify/VerifySlide.vue')['default'] + WeekForm: typeof import('./../components/GenCron/CronForm/component/week-form.vue')['default'] + YearForm: typeof import('./../components/GenCron/CronForm/component/year-form.vue')['default'] } } diff --git a/src/views/bussiness-data/bussiness.vue b/src/views/bussiness-data/bussiness.vue index 49f236c..dcc7abf 100644 --- a/src/views/bussiness-data/bussiness.vue +++ b/src/views/bussiness-data/bussiness.vue @@ -4,10 +4,9 @@ @@ -102,29 +101,7 @@ - - + @@ -305,7 +282,7 @@ - {{ formatFileSize(file.fileSize || file.size) }} + {{ formatFileListSize(file.fileSize || file.size) }} @@ -682,7 +659,7 @@ const fileListTemp = ref([]); const folderFormRef = ref(null); const uploadFormRef = ref(null); const uploadRef = ref(null); -const folderColor = '#165DFF'; +const folderColor = 'var(--color-primary)'; const refreshing = ref(false); const folderSubmitting = ref(false); const uploading = ref(false); @@ -1624,7 +1601,7 @@ const fileColor = (extension) => { bmp: '#722ed1', webp: '#13c2c2' }; - return colorMap[extension.toLowerCase()] || '#8c8c8c'; + return colorMap[extension.toLowerCase()] || 'var(--color-text-3)'; }; @@ -1881,8 +1858,8 @@ const showTextPreview = async (blob, fileName) => { maxWidth: '100%', maxHeight: '70vh', overflow: 'auto', - backgroundColor: '#f8f9fa', - border: '1px solid #e9ecef', + backgroundColor: 'var(--color-fill-1)', + border: '1px solid var(--color-border)', borderRadius: '8px', padding: '20px', fontFamily: "'Consolas', 'Monaco', 'Courier New', monospace", @@ -1890,7 +1867,7 @@ const showTextPreview = async (blob, fileName) => { lineHeight: '1.6', whiteSpace: 'pre-wrap', wordBreak: 'break-word', - color: '#333', + color: 'var(--color-text-1)', textAlign: 'left' } }, text) @@ -2336,6 +2313,21 @@ const formatFileSize = (fileSize) => { return `${(size / (1024 * 1024 * 1024)).toFixed(1)} GB`; }; +// 专门用于文件列表的格式化函数(假设后端返回的是KB单位) +const formatFileListSize = (fileSize) => { + const size = Number(fileSize); + if (isNaN(size) || size < 0) return '未知'; + + // 假设后端返回的是KB单位 + if (size < 1024) { + return `${size} KB`; + } else if (size < 1024 * 1024) { + return `${(size / 1024).toFixed(1)} MB`; + } else { + return `${(size / (1024 * 1024)).toFixed(1)} GB`; + } +}; + const fileTypeText = (type) => { @@ -2458,7 +2450,7 @@ onMounted(() => { \ No newline at end of file diff --git a/src/views/project-management/personnel-dispatch/construction-personnel.vue b/src/views/project-management/personnel-dispatch/construction-personnel.vue index 7deaaf4..c25741e 100644 --- a/src/views/project-management/personnel-dispatch/construction-personnel.vue +++ b/src/views/project-management/personnel-dispatch/construction-personnel.vue @@ -41,20 +41,6 @@ - - - - 当前应用了搜索筛选条件 - - 清除所有筛选 - - - @@ -72,7 +57,6 @@ placeholder="请选择项目岗位" style="width: 150px" allow-clear - @change="handleSearch" > - + 搜索 @@ -121,7 +99,6 @@ - - - - - - - {{ hasActiveFilters ? '没有找到匹配的团队成员' : '暂无团队成员数据' }} - - 请尝试调整搜索条件或 - 清除所有筛选 - - - 点击右上角的"新增成员"按钮来添加第一个团队成员 - - - - - @@ -161,9 +121,9 @@ - + {{ getStatusText(record.status || 'INACTIVE') }} - + @@ -380,7 +340,7 @@ const ROLE_TYPE_OPTIONS = [ const STATUS_OPTIONS = [ { label: '可用', value: 'ACTIVE' }, - { label: '忙碌', value: 'SUSPENDED' }, + { label: '忙碌', value: 'SUSPENDEN' }, { label: '离线', value: 'INACTIVE' } ] @@ -441,7 +401,7 @@ const statusForm = reactive<{ id: string | number name: string currentStatus: string - newStatus: 'ACTIVE' | 'SUSPENDED' | 'INACTIVE' + newStatus: 'ACTIVE' | 'SUSPENDEN' | 'INACTIVE' }>({ id: '', name: '', @@ -463,80 +423,56 @@ const loadData = async () => { try { console.log('正在加载项目团队成员数据,项目ID:', projectId) - // 构建查询参数,字段名要与后端接口匹配 + // 构建查询参数 const queryParams: TeamMemberQuery = { projectId: projectId, page: pagination.current, pageSize: pagination.pageSize, name: searchForm.name || undefined, - position: searchForm.roleType || undefined, // 后端使用position字段 + position: searchForm.roleType || undefined, status: searchForm.status || undefined } - console.log('查询参数:', queryParams) - const response = await getProjectTeamMembers(queryParams) - console.log('API响应数据:', response) + console.log('API响应数据:', response.data) - // 处理后端返回的数据 - let mappedData: TeamMemberResp[] = [] - let total = 0 + // 确保response.data是数组 + const rawData = Array.isArray(response.data) ? response.data : [response.data] - if (response.data) { - // 如果后端返回的是分页数据 - if (response.data.list && Array.isArray(response.data.list)) { - mappedData = response.data.list.map((item: BackendTeamMemberResp) => { - return { + console.log('处理后的原始数据:', rawData) + + // 处理后端返回的数据,将后端字段映射到前端期望的字段 + const mappedData = rawData.map((item: BackendTeamMemberResp) => { + const mappedItem: TeamMemberResp = { id: item.memberId, name: item.name || '', phone: item.phone || '', email: item.email || '', - roleType: item.roleType || item.position || '', // 优先使用roleType,fallback到position - status: (item.status === 'ACTIVE' ? 'ACTIVE' : item.status === 'SUSPENDED' ? 'SUSPENDED' : 'INACTIVE') as 'ACTIVE' | 'SUSPENDED' | 'INACTIVE', + roleType: item.roleType || '', // 映射项目岗位 + status: (item.status === 'ACTIVE' ? 'ACTIVE' : item.status === 'SUSPENDEN' ? 'SUSPENDEN' : 'INACTIVE') as 'ACTIVE' | 'SUSPENDEN' | 'INACTIVE', joinDate: item.joinDate || '', remark: item.remark || '', avatar: item.userAvatar || '' } - }) - total = response.data.total || mappedData.length - } - // 如果后端直接返回数组 - else if (Array.isArray(response.data)) { - mappedData = response.data.map((item: BackendTeamMemberResp) => { - return { - id: item.memberId, - name: item.name || '', - phone: item.phone || '', - email: item.email || '', - roleType: item.roleType || item.position || '', - status: (item.status === 'ACTIVE' ? 'ACTIVE' : item.status === 'SUSPENDED' ? 'SUSPENDED' : 'INACTIVE') as 'ACTIVE' | 'SUSPENDED' | 'INACTIVE', - joinDate: item.joinDate || '', - remark: item.remark || '', - avatar: item.userAvatar || '' - } - }) - total = mappedData.length - } - } + console.log('映射后的数据项:', mappedItem) + return mappedItem + }) dataList.value = mappedData - pagination.total = total + pagination.total = mappedData.length console.log('团队成员数据加载完成,显示数据:', dataList.value.length, '条,总计:', pagination.total, '条') } catch (error) { console.error('团队成员数据加载失败:', error) Message.error('团队成员数据加载失败') - dataList.value = [] - pagination.total = 0 } finally { loading.value = false } } const handleSearch = async () => { - console.log('执行搜索,搜索条件:', searchForm) pagination.current = 1 await loadData() } @@ -549,18 +485,15 @@ const handleReset = () => { status: '' }) pagination.current = 1 - // 重置后自动加载数据 loadData() } const onPageChange = (page: number) => { - console.log('页码变化:', page) pagination.current = page loadData() } const onPageSizeChange = (pageSize: number) => { - console.log('每页大小变化:', pageSize) pagination.pageSize = pageSize pagination.current = 1 loadData() @@ -664,7 +597,7 @@ const openStatusModal = (record: TeamMemberResp) => { const saveStatus = async () => { try { await updateTeamMember(statusForm.id, { - status: statusForm.newStatus as 'ACTIVE' | 'SUSPENDED' | 'INACTIVE' + status: statusForm.newStatus as 'ACTIVE' | 'SUSPENDEN' | 'INACTIVE' }) Message.success('状态更新成功') statusModalVisible.value = false @@ -783,7 +716,7 @@ const getRoleTypeText = (roleType: string) => { const getStatusColor = (status: string) => { const colorMap: Record = { ACTIVE: 'success', - SUSPENDED: 'warning', + SUSPENDEN: 'warning', INACTIVE: 'danger' } return colorMap[status] || 'danger' @@ -792,17 +725,12 @@ const getStatusColor = (status: string) => { const getStatusText = (status: string) => { const textMap: Record = { ACTIVE: '可用', - SUSPENDED: '忙碌', + SUSPENDEN: '忙碌', INACTIVE: '离线' } return textMap[status] || '未知' } -// 计算属性:判断是否有激活的搜索条件 -const hasActiveFilters = computed(() => { - return searchForm.name || searchForm.roleType || searchForm.status -}) - // 生命周期 onMounted(() => { console.log('团队成员管理页面加载,项目ID:', projectId) @@ -1011,29 +939,6 @@ onMounted(() => { padding: 24px; } - // 搜索状态提示样式 - .search-status { - display: flex; - align-items: center; - margin-bottom: 16px; - padding: 12px 16px; - background: rgba(102, 126, 234, 0.1); - border-radius: 8px; - border-left: 4px solid #667eea; - font-size: 14px; - color: #4e5969; - - .arco-btn { - padding: 2px 8px; - height: auto; - line-height: 1.4; - - &:hover { - background: rgba(102, 126, 234, 0.2); - } - } - } - :deep(.arco-form-item) { margin-bottom: 0; margin-right: 20px; @@ -1064,7 +969,7 @@ onMounted(() => { } :deep(.arco-btn) { - border-radius: 8px; + border-radius: 8px; font-weight: 600; transition: all 0.3s ease; @@ -1072,78 +977,87 @@ onMounted(() => { transform: translateY(-1px); box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); } - - &:disabled { - opacity: 0.6; - cursor: not-allowed; - - &:hover { - transform: none; - box-shadow: none; - } - } } } } -// 搜索结果统计样式 -.search-results-info { - margin-bottom: 16px; +// 表格区域样式 +:deep(.gi-table) { + background: white; + border-radius: 12px; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08); + margin-bottom: 24px; + overflow: hidden; - .results-card { - background: linear-gradient(135deg, #f8f9ff 0%, #f0f2ff 100%); - border-radius: 12px; - box-shadow: 0 2px 12px rgba(102, 126, 234, 0.1); - border: 1px solid rgba(102, 126, 234, 0.1); + .arco-table-container { + overflow-x: auto; + overflow-y: auto; + } + + .arco-table { + overflow: visible; + } + + .arco-table-body { + overflow-y: auto; + } + + // 表格头部样式 + .arco-table-thead { + background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - :deep(.arco-card-body) { - padding: 16px 20px; - } - - .results-summary { - display: flex; - align-items: center; - font-size: 14px; - color: #4e5969; + .arco-table-th { + background: transparent !important; + border-bottom: 2px solid rgba(255, 255, 255, 0.2); - strong { - color: #667eea; + .arco-table-th-item-title { + color: white !important; font-weight: 600; + font-size: 14px; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1); } } } + + // 表格行样式 + .arco-table-tbody { + .arco-table-tr { + transition: all 0.3s ease; + + &:hover { + background: linear-gradient(135deg, #f8f9ff 0%, #f0f2ff 100%); + transform: translateY(-1px); + box-shadow: 0 4px 12px rgba(102, 126, 234, 0.1); + } + + .arco-table-td { + border-bottom: 1px solid #f0f0f0; + padding: 16px 12px; + vertical-align: middle; + } + } + + // 斑马纹效果 + .arco-table-tr:nth-child(even) { + background: #fafbfc; + + &:hover { + background: linear-gradient(135deg, #f8f9ff 0%, #f0f2ff 100%); + } + } + } + + // 固定列样式 + .arco-table-fixed-left, + .arco-table-fixed-right { + .arco-table-td { + background: white; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); + } + } } -// 空状态样式 -.empty-state { - text-align: center; - padding: 60px 20px; - color: #c9cdd4; - - .empty-text { - h3 { - margin: 0 0 12px 0; - font-size: 18px; - font-weight: 600; - color: #4e5969; - } - - p { - margin: 8px 0; - font-size: 14px; - line-height: 1.6; - - .arco-link { - color: #667eea; - font-weight: 500; - - &:hover { - color: #4c5fd9; - } - } - } - } -} + .remark-content { max-width: 180px; @@ -1196,58 +1110,11 @@ onMounted(() => { transition: all 0.3s ease; &:hover { - transform: scale(1.05); + transform: translateY(-2px); box-shadow: 0 4px 16px rgba(102, 126, 234, 0.4); } } -// 状态标签样式 -.status-tag { - display: inline-block; - padding: 6px 12px; - border-radius: 20px; - font-weight: 600; - font-size: 12px; - text-align: center; - min-width: 60px; - transition: all 0.3s ease; - box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1); - - &:hover { - transform: scale(1.05); - } - - &.status-ACTIVE { - background: linear-gradient(135deg, #52c41a 0%, #73d13d 100%); - color: white; - box-shadow: 0 2px 8px rgba(82, 196, 26, 0.3); - - &:hover { - box-shadow: 0 4px 16px rgba(82, 196, 26, 0.4); - } - } - - &.status-SUSPENDED { - background: linear-gradient(135deg, #faad14 0%, #ffc53d 100%); - color: white; - box-shadow: 0 2px 8px rgba(250, 173, 20, 0.3); - - &:hover { - box-shadow: 0 4px 16px rgba(250, 173, 20, 0.4); - } - } - - &.status-INACTIVE { - background: linear-gradient(135deg, #ff4d4f 0%, #ff7875 100%); - color: white; - box-shadow: 0 2px 8px rgba(255, 77, 79, 0.3); - - &:hover { - box-shadow: 0 4px 16px rgba(255, 77, 79, 0.4); - } - } -} - .member-form { .form-row { display: grid; @@ -1440,80 +1307,4 @@ onMounted(() => { box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15); } } - -// 表格区域样式 -:deep(.gi-table) { - background: white; - border-radius: 12px; - box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08); - margin-bottom: 24px; - overflow: hidden; - - .arco-table-container { - overflow-x: auto; - overflow-y: auto; - } - - .arco-table { - overflow: visible; - } - - .arco-table-body { - overflow-y: auto; - } - - // 表格头部样式 - .arco-table-thead { - background: linear-gradient(135deg, #667eea 0%, #764ba2 100%); - - .arco-table-th { - background: transparent !important; - border-bottom: 2px solid rgba(255, 255, 255, 0.2); - - .arco-table-th-item-title { - color: white !important; - font-weight: 600; - font-size: 14px; - text-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); - } - } - } - - // 表格行样式 - .arco-table-tbody { - .arco-table-tr { - transition: all 0.3s ease; - - &:hover { - background: linear-gradient(135deg, #f8f9ff 0%, #f0f2ff 100%); - transform: translateY(-1px); - box-shadow: 0 4px 12px rgba(102, 126, 234, 0.1); - } - - .arco-table-td { - border-bottom: 1px solid #f0f0f0; - padding: 16px 12px; - vertical-align: middle; - } - } - - // 斑马纹效果 - .arco-table-tr:nth-child(even) { - background: #fafbfc; - - &:hover { - background: linear-gradient(135deg, #f8f9ff 0%, #f0f2ff 100%); - } - } - } - - // 固定列样式 - .arco-table-fixed-left, - .arco-table-fixed-right { - .arco-table-td { - background: white; - box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); - } - } -} \ No newline at end of file diff --git a/src/views/project-management/personnel-dispatch/index.vue b/src/views/project-management/personnel-dispatch/index.vue index dc86c4d..99bb392 100644 --- a/src/views/project-management/personnel-dispatch/index.vue +++ b/src/views/project-management/personnel-dispatch/index.vue @@ -26,133 +26,1293 @@ 刷新 - - - - - {{ stats.totalProjectsCount }} - - 总项目数 - - - - - {{ stats.inProgressProjectCount }} - - 进行中 - - - - - {{ stats.pendingProjectCount }} - - 准备中 - - - - - {{ stats.completedProjectCount }} - - 已完成 + + + + + + + + + {{ status.label }} + ({{ getStatusCount(status.value) }}) + - - - - + + + + 全部项目 ({{ totalProjects }}) + + + + + + + + {{ project.name }} + 未开工 + + + + + 预算: {{ formatBudget(project.budget) }} + + + + 负责人: {{ project.manager || '未指定' }} + + + + 团队: {{ project.teamSize > 0 ? project.teamSize + '人' : '未设置' }} + + + + 项目进度 + {{ project.progress || 0 }}% + + + + + + + + + + + + + 质量管理 + + + + 质量等级 + A级 + + + 检查次数 + 12次 + + + 合格率 + 98.5% + + + + + + + + + 安全管理 + + + + 安全等级 + 优秀 + + + 培训完成 + 100% + + + 安全检查 + 通过 + + + + + + + + + 现场管理 + + + + 在线人数 + {{ project.teamSize || 0 }}人 + + + 设备状态 + 正常 + + + 现场环境 + 良好 + + + + + + + + + + + {{ project.name }} + 筹备中 + + + + + 预算: {{ formatBudget(project.budget) }} + + + + 负责人: {{ project.manager || '未指定' }} + + + + 团队: {{ project.teamSize > 0 ? project.teamSize + '人' : '未设置' }} + + + + 项目进度 + {{ project.progress || 0 }}% + + + + + + + + + + + + + 质量管理 + + + + 质量等级 + A级 + + + 检查次数 + 12次 + + + 合格率 + 98.5% + + + + + + + + + 安全管理 + + + + 安全等级 + 优秀 + + + 培训完成 + 100% + + + 安全检查 + 通过 + + + + + + + + + 现场管理 + + + + 在线人数 + {{ project.teamSize || 0 }}人 + + + 设备状态 + 正常 + + + 现场环境 + 良好 + + + + + + + + + + + {{ project.name }} + 开工中 + + + + + 预算: {{ formatBudget(project.budget) }} + + + + 负责人: {{ project.manager || '未指定' }} + + + + 团队: {{ project.teamSize > 0 ? project.teamSize + '人' : '未设置' }} + + + + 项目进度 + {{ project.progress || 0 }}% + + + + + + + + + + + + + 质量管理 + + + + 质量等级 + A级 + + + 检查次数 + 12次 + + + 合格率 + 98.5% + + + + + + + + + 安全管理 + + + + 安全等级 + 优秀 + + + 培训完成 + 100% + + + 安全检查 + 通过 + + + + + + + + + 现场管理 + + + + 在线人数 + {{ project.teamSize || 0 }}人 + + + 设备状态 + 正常 + + + 现场环境 + 良好 + + + + + + + + + + + + {{ project.name }} + 暂停中 + + + + + 预算: {{ formatBudget(project.budget) }} + + + + 负责人: {{ project.manager || '未指定' }} + + + + 团队: {{ project.teamSize > 0 ? project.teamSize + '人' : '未设置' }} + + + + 项目进度 + {{ project.progress || 0 }}% + + + + + + + + + + + + + 质量管理 + + + + 质量等级 + A级 + + + 检查次数 + 12次 + + + 合格率 + 98.5% + + + + + + + + + 安全管理 + + + + 安全等级 + 优秀 + + + 培训完成 + 100% + + + 安全检查 + 通过 + + + + + + + + + 现场管理 + + + + 在线人数 + {{ project.teamSize || 0 }}人 + + + 设备状态 + 正常 + + + 现场环境 + 良好 + + + + + + + + + + + {{ project.name }} + 已完工 + + + + + 预算: {{ formatBudget(project.budget) }} + + + + 负责人: {{ project.manager || '未指定' }} + + + + 团队: {{ project.teamSize > 0 ? project.teamSize + '人' : '未设置' }} + + + + 项目进度 + {{ project.progress || 0 }}% + + + + + + + + + + + + + 质量管理 + + + + 质量等级 + A级 + + + 检查次数 + 12次 + + + 合格率 + 98.5% + + + + + + + + + 安全管理 + + + + 安全等级 + 优秀 + + + 培训完成 + 100% + + + 安全检查 + 通过 + + + + + + + + + 现场管理 + + + + 在线人数 + {{ project.teamSize || 0 }}人 + + + 设备状态 + 正常 + + + 现场环境 + 良好 + + + + + + + + + + + {{ project.name }} + 验收中 + + + + + 预算: {{ formatBudget(project.budget) }} + + + + 负责人: {{ project.manager || '未指定' }} + + + + 团队: {{ project.teamSize > 0 ? project.teamSize + '人' : '未设置' }} + + + + 验收进度 + {{ project.progress || 0 }}% + + + + + + + + + + + + + 质量管理 + + + + 质量等级 + A级 + + + 检查次数 + 12次 + + + 合格率 + 98.5% + + + + + + + + + 安全管理 + + + + 安全等级 + 优秀 + + + 培训完成 + 100% + + + 安全检查 + 通过 + + + + + + + + + 现场管理 + + + + 在线人数 + {{ project.teamSize || 0 }}人 + + + 设备状态 + 正常 + + + 现场环境 + 良好 + + + + + + + + + + + {{ project.name }} + 回款中 + + + + + 预算: {{ formatBudget(project.budget) }} + + + + 负责人: {{ project.manager || '未指定' }} + + + + 团队: {{ project.teamSize > 0 ? project.teamSize + '人' : '未设置' }} + + + + 回款进度 + {{ project.progress || 0 }}% + + + + + + + + + + + + + 质量管理 + + + + 质量等级 + A级 + + + 检查次数 + 12次 + + + 合格率 + 98.5% + + + + + + + + + 安全管理 + + + + 安全等级 + 优秀 + + + 培训完成 + 100% + + + 安全检查 + 通过 + + + + + + + + + 现场管理 + + + + 在线人数 + {{ project.teamSize || 0 }}人 + + + 设备状态 + 正常 + + + 现场环境 + 良好 + + + + + + + + + + + {{ project.name }} + 已结算 + + + + + 预算: {{ formatBudget(project.budget) }} + + + + 负责人: {{ project.manager || '未指定' }} + + + + 团队: {{ project.teamSize > 0 ? project.teamSize + '人' : '未设置' }} + + + + 结算状态 + 100% + + + + + + + + + + + + + 质量管理 + + + + 质量等级 + A级 + + + 检查次数 + 12次 + + + 合格率 + 98.5% + + + + + + + + + 安全管理 + + + + 安全等级 + 优秀 + + + 培训完成 + 100% + + + 安全检查 + 通过 + + + + + + + + + 现场管理 + + + + 在线人数 + {{ project.teamSize || 0 }}人 + + + 设备状态 + 正常 + + + 现场环境 + 良好 + + + + + + + + + + + + + + + 未开工 + {{ pendingProjects.length }} + + + + + + + + {{ project.name }} + 未开工 + + + + + 预算: {{ formatBudget(project.budget) }} + + + + 负责人: {{ project.manager || '未指定' }} + + + + 团队: {{ project.teamSize > 0 ? project.teamSize + '人' : '未设置' }} + + + + 项目进度 + {{ project.progress || 0 }}% + + + + + + + + + + + + + 质量管理 + + + + 质量等级 + A级 + + + 检查次数 + 12次 + + + 合格率 + 98.5% + + + + + + + + + 安全管理 + + + + 安全等级 + 优秀 + + + 培训完成 + 100% + + + 安全检查 + 通过 + + + + + + + + + 现场管理 + + + + 在线人数 + {{ project.teamSize || 0 }}人 + + + 设备状态 + 正常 + + + 现场环境 + 良好 + + + + + + + + + - 准备中 + 筹备中 {{ preparingProjects.length }} - - {{ project.name }} - 准备中 - - - - - 预算: {{ formatBudget(project.budget) }} + + + + {{ project.name }} + 筹备中 - - - 负责人: {{ project.manager }} - - - - 团队: {{ project.teamSize }}人 + + + + 预算: {{ formatBudget(project.budget) }} + + + + 负责人: {{ project.manager || '未指定' }} + + + + 团队: {{ project.teamSize > 0 ? project.teamSize + '人' : '未设置' }} + + + + 项目进度 + {{ project.progress || 0 }}% + + + + + - - - - + + + - 已开工 - {{ ongoingProjects.length }} + 开工中 + {{ inProgressProjects.length }} - - {{ project.name }} - 进行中 - - - - - 预算: {{ formatBudget(project.budget) }} + + + + {{ project.name }} + 开工中 - - - 负责人: {{ project.manager }} - - - - 团队: {{ project.teamSize }}人 + + + + 预算: {{ formatBudget(project.budget) }} + + + + 负责人: {{ project.manager || '未指定' }} + + + + 团队: {{ project.teamSize > 0 ? project.teamSize + '人' : '未设置' }} + + + + 项目进度 + {{ project.progress || 0 }}% + + + + + -
- 请尝试调整搜索条件或 - 清除所有筛选 -
- 点击右上角的"新增成员"按钮来添加第一个团队成员 -