diff --git a/devcloud/web/src/layout/DashboardLayout.vue b/devcloud/web/src/layout/DashboardLayout.vue index 61835e7..cd76db4 100644 --- a/devcloud/web/src/layout/DashboardLayout.vue +++ b/devcloud/web/src/layout/DashboardLayout.vue @@ -6,7 +6,7 @@
- +
@@ -31,20 +31,18 @@ diff --git a/devcloud/web/src/pages/project/ProjectList.vue b/devcloud/web/src/pages/project/ProjectList.vue new file mode 100644 index 0000000..214ec63 --- /dev/null +++ b/devcloud/web/src/pages/project/ProjectList.vue @@ -0,0 +1,193 @@ + + + + + diff --git a/devcloud/web/src/pages/project/components/ProjectCard.vue b/devcloud/web/src/pages/project/components/ProjectCard.vue new file mode 100644 index 0000000..2f489de --- /dev/null +++ b/devcloud/web/src/pages/project/components/ProjectCard.vue @@ -0,0 +1,131 @@ + + + + + diff --git a/devcloud/web/src/pages/project/components/ProjectFormModal.vue b/devcloud/web/src/pages/project/components/ProjectFormModal.vue new file mode 100644 index 0000000..a66b41e --- /dev/null +++ b/devcloud/web/src/pages/project/components/ProjectFormModal.vue @@ -0,0 +1,100 @@ + + + + + diff --git a/devcloud/web/src/pages/project/mockData.js b/devcloud/web/src/pages/project/mockData.js new file mode 100644 index 0000000..190b9c3 --- /dev/null +++ b/devcloud/web/src/pages/project/mockData.js @@ -0,0 +1,193 @@ +// mockData.js + +/** + * 生成随机日期(最近365天内) + */ +const generateRandomDate = () => { + const now = new Date() + const past = new Date(now) + past.setDate(now.getDate() - 365) + return new Date(past.getTime() + Math.random() * (now.getTime() - past.getTime())) +} + +/** + * 生成随机项目描述 + */ +const generateDescription = (index) => { + const descriptors = [ + '企业级应用开发项目', + '微服务架构重构项目', + '前端技术升级专项', + 'DevOps平台建设项目', + 'AI能力集成项目', + '大数据分析平台', + '移动端应用重设计', + '云原生迁移项目', + ] + const types = ['核心业务系统', '技术中台', '数据平台', '用户增长项目', '基础设施升级'] + return `${types[index % types.length]} - ${descriptors[index % descriptors.length]}` +} + +/** + * 模拟用户数据 + */ +export const mockUsers = [ + { id: 'user-1', name: '张管理员', role: 'admin' }, + { id: 'user-2', name: '李开发', role: 'dev' }, + { id: 'user-3', name: '王测试', role: 'qa' }, + { id: 'user-4', name: '赵产品', role: 'product' }, + { id: 'user-5', name: '刘架构', role: 'arch' }, +] + +/** + * 模拟项目数据 + */ +export const mockProjects = Array.from({ length: 36 }, (_, i) => { + const statuses = ['active', 'archived', 'planning'] + const status = statuses[i % 3] + + return { + id: `project-${i + 1}`, + name: `研发项目 ${String(i + 1).padStart(2, '0')}`, + description: generateDescription(i), + status, + owner: mockUsers[i % mockUsers.length].id, + members: mockUsers.slice(0, (i % 4) + 1), + createTime: generateRandomDate().getTime(), + updateTime: generateRandomDate().getTime(), + stats: { + tasks: Math.floor(Math.random() * 50) + 10, + completed: Math.floor(Math.random() * 45), + progress: Math.floor(Math.random() * 100), + }, + } +}) + +/** + * 项目状态映射 + */ +export const projectStatusMap = { + active: { text: '活跃', color: '#00B42A' }, + archived: { text: '已归档', color: '#86909C' }, + planning: { text: '规划中', color: '#FF7D00' }, +} + +/** + * 获取项目状态信息 + */ +export const getProjectStatusInfo = (status) => { + return projectStatusMap[status] || { text: '未知', color: '#86909C' } +} + +/** + * 模拟API响应格式 + */ +export const mockApiResponse = (data, options = {}) => { + return { + code: 200, + data, + message: 'success', + ...options, + } +} + +/** + * 模拟获取项目列表API + */ +export const fetchProjects = (params = {}) => { + const { page = 1, pageSize = 10, search = '', status } = params + + let filtered = [...mockProjects] + + // 模拟筛选 + if (search) { + const keyword = search.toLowerCase() + filtered = filtered.filter( + (p) => + p.name.toLowerCase().includes(keyword) || p.description.toLowerCase().includes(keyword), + ) + } + + if (status) { + filtered = filtered.filter((p) => p.status === status) + } + + // 模拟分页 + const start = (page - 1) * pageSize + const end = start + pageSize + const pageData = filtered.slice(start, end) + + return new Promise((resolve) => { + setTimeout(() => { + resolve( + mockApiResponse({ + list: pageData, + total: filtered.length, + page, + pageSize, + }), + ) + }, 500) // 模拟网络延迟 + }) +} + +/** + * 模拟获取项目详情API + */ +export const fetchProjectDetail = (id) => { + const project = mockProjects.find((p) => p.id === id) + return new Promise((resolve) => { + setTimeout(() => { + if (project) { + resolve(mockApiResponse(project)) + } else { + resolve(mockApiResponse(null, { code: 404, message: '项目不存在' })) + } + }, 300) + }) +} + +/** + * 模拟创建项目API + */ +export const createProject = (data) => { + const newProject = { + id: `project-${mockProjects.length + 1}`, + ...data, + createTime: Date.now(), + updateTime: Date.now(), + members: [mockUsers[0]], + stats: { + tasks: 0, + completed: 0, + progress: 0, + }, + } + + mockProjects.unshift(newProject) + + return new Promise((resolve) => { + setTimeout(() => { + resolve(mockApiResponse(newProject)) + }, 400) + }) +} + +/** + * 模拟更新项目API + */ +export const updateProject = (id, data) => { + const index = mockProjects.findIndex((p) => p.id === id) + + return new Promise((resolve) => { + setTimeout(() => { + if (index >= 0) { + const updated = { ...mockProjects[index], ...data, updateTime: Date.now() } + mockProjects.splice(index, 1, updated) + resolve(mockApiResponse(updated)) + } else { + resolve(mockApiResponse(null, { code: 404, message: '项目不存在' })) + } + }, 400) + }) +} diff --git a/devcloud/web/src/router/index.js b/devcloud/web/src/router/index.js index fb4beb6..ac0b48c 100644 --- a/devcloud/web/src/router/index.js +++ b/devcloud/web/src/router/index.js @@ -39,29 +39,87 @@ const router = createRouter({ }, ], }, + // 项目管理 { path: '/project', name: 'ProjectSystem', - redirect: { name: 'AppPage' }, + redirect: { name: 'ProjectList' }, component: MenuLayout, + meta: { + title: '项目管理', + }, children: [ { - path: 'app', - name: 'AppPage', - component: () => import('@/pages/project/AppPage.vue'), + path: 'list', + name: 'ProjectList', + component: () => import('@/pages/project/ProjectList.vue'), + meta: { + title: '项目空间', + }, }, ], }, + // 研发交付 { path: '/develop', name: 'DevelopSystem', redirect: { name: 'SprintPage' }, component: MenuLayout, + meta: { + title: '研发交付', + }, children: [ { - path: 'sprint', - name: 'SprintPage', - component: () => import('@/pages/develop/SprintPage.vue'), + path: 'app', + name: 'AppPage', + component: () => import('@/pages/develop/AppPage.vue'), + meta: { + title: '应用管理', + }, + }, + { + path: 'version_iteration', + name: 'VersionIteration', + component: () => import('@/pages/develop/VersionIteration.vue'), + meta: { + title: '版本迭代', + }, + }, + { + path: 'pipeline_template', + name: 'PipelineTemplate', + component: () => import('@/pages/develop/PipelineTemplate.vue'), + meta: { + title: '流水线模板', + }, + }, + ], + }, + // 制品库 + { + path: '/artifact', + name: 'ArtifactSystem', + redirect: { name: 'SprintPage' }, + component: MenuLayout, + meta: { + title: '制品库', + }, + children: [ + { + path: 'registry', + name: 'RegistryPage', + component: () => import('@/pages/artifact/RegistryPage.vue'), + meta: { + title: '制品仓库', + }, + }, + { + path: 'asset', + name: 'AssetPage', + component: () => import('@/pages/artifact/AssetPage.vue'), + meta: { + title: '制品管理', + }, }, ], }, diff --git a/devcloud/web/src/storage/app.js b/devcloud/web/src/storage/app.js index e69de29..98714cf 100644 --- a/devcloud/web/src/storage/app.js +++ b/devcloud/web/src/storage/app.js @@ -0,0 +1,28 @@ +import { useStorage } from '@vueuse/core' + +export default useStorage( + 'app', + { + current_system: 'DashBoard', + current_menu: { + DashBoard: { + sub_menu: '', + menu_item: '1', + }, + ProjectSystem: { + sub_menu: '', + menu_item: '1', + }, + DevelopSystem: { + sub_menu: '', + menu_item: '1', + }, + ArtifactSystem: { + sub_menu: '', + menu_item: 'RegistryPage', + }, + }, + }, + localStorage, + { mergeDefaults: true }, +)