Skip to content

开发指南

开发环境搭建

系统要求

  • 操作系统: macOS / Windows / Linux
  • Node.js: >= 18.0.0
  • 包管理器: npm >= 9.0.0 或 pnpm >= 8.0.0
  • Git: >= 2.30.0

推荐工具

  • IDE: VSCode / WebStorm
  • 浏览器: Chrome / Edge / Safari(最新版)
  • 移动端调试: Chrome DevTools / Safari DevTools

项目架构

技术栈

层级技术
前端框架Vue 3 + TypeScript
状态管理Pinia
路由Vue Router
UI 组件自研组件库
构建工具Vite
移动端UniApp / Taro
桌面端Electron
服务端Node.js / Go
数据库MySQL / Redis / MongoDB
消息队列RabbitMQ / Kafka

目录结构

pmspilot/
├── apps/                    # 应用层
│   ├── mobile/             # 移动端(UniApp)
│   ├── desktop/            # 桌面端(Electron)
│   ├── web/                # Web 端
│   └── miniapp/            # 小程序
├── packages/               # 共享包
│   ├── sdk/                # PMS SDK
│   ├── ui/                 # UI 组件库
│   ├── shared/             # 共享工具
│   └── types/              # 类型定义
├── server/                 # 服务端
│   ├── gateway/            # API 网关
│   ├── project/            # 项目服务
│   ├── task/               # 任务服务
│   └── im/                 # 即时通讯服务
├── docs/                   # 文档
└── scripts/                # 脚本工具

开发规范

代码规范

  • 使用 ESLint + Prettier 进行代码格式化
  • 遵循 Vue 3 组合式 API 风格
  • TypeScript 严格模式启用
  • 组件命名使用 PascalCase
  • 函数命名使用 camelCase

Git 规范

类型(scope): 简短描述

详细描述(可选)

Footer(可选)

类型说明:

  • feat: 新功能
  • fix: 修复
  • docs: 文档
  • style: 格式
  • refactor: 重构
  • perf: 性能优化
  • test: 测试
  • chore: 构建/工具

示例:

feat(project): 添加甘特图拖拽调整功能

- 支持拖拽调整任务时间
- 支持任务时长调整
- 添加延期风险预警

Closes #123

分支管理

  • main: 主分支,稳定版本
  • develop: 开发分支
  • feature/*: 功能分支
  • hotfix/*: 紧急修复
  • release/*: 发布分支

模块开发

SDK 开发

typescript
// packages/sdk/src/core/client.ts
export class PmsClient {
  private ws: WebSocket
  private options: ClientOptions
  private eventEmitter: EventEmitter

  constructor(options: ClientOptions) {
    this.options = options
    this.eventEmitter = new EventEmitter()
  }

  async connect(): Promise<void> {
    this.ws = new WebSocket(this.options.wsUrl)
    this.ws.onopen = () => this.eventEmitter.emit('connect')
    this.ws.onmessage = (e) => this.handleMessage(e.data)
    this.ws.onclose = () => this.eventEmitter.emit('disconnect')
  }

  async createProject(project: ProjectCreateDTO): Promise<Project> {
    return this.request('POST', '/projects', project)
  }

  async getProjects(): Promise<Project[]> {
    return this.request('GET', '/projects')
  }
}

UI 组件开发

vue
<!-- packages/ui/src/components/TaskCard.vue -->
<template>
  <div class="task-card" :class="priorityClass" @click="handleClick">
    <div class="task-header">
      <span class="priority-badge">{{ priorityLabel }}</span>
      <span class="task-time">{{ formatTime(dueDate) }}</span>
    </div>
    <div class="task-title">{{ title }}</div>
    <div class="task-footer">
      <Avatar :src="assigneeAvatar" :size="24" />
      <span class="assignee-name">{{ assigneeName }}</span>
    </div>
  </div>
</template>

<script setup lang="ts">
interface Props {
  title: string
  priority: 'important_urgent' | 'important' | 'urgent' | 'normal'
  dueDate: number
  assigneeAvatar: string
  assigneeName: string
}

const props = defineProps<Props>()

const priorityLabel = computed(() => {
  const map = {
    important_urgent: '重要紧急',
    important: '重要不紧急',
    urgent: '紧急不重要',
    normal: '不重要不紧急'
  }
  return map[props.priority]
})
</script>

页面开发

vue
<!-- apps/web/src/pages/project/kanban.vue -->
<template>
  <div class="kanban-page">
    <ProjectHeader :project="currentProject" />
    <KanbanBoard
      :columns="columns"
      :tasks="tasks"
      @task-move="handleTaskMove"
      @task-click="handleTaskClick"
    />
    <TaskDetailDrawer
      v-model:visible="detailVisible"
      :task="selectedTask"
    />
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { useProjectStore } from '@/stores/project'

const projectStore = useProjectStore()
const currentProject = ref<Project>()
const tasks = ref<Task[]>([])
const detailVisible = ref(false)
const selectedTask = ref<Task>()

onMounted(async () => {
  await projectStore.loadProject(projectId)
  currentProject.value = projectStore.currentProject
  tasks.value = projectStore.tasks
})

const handleTaskMove = async (taskId: string, targetColumn: string) => {
  await projectStore.moveTask(taskId, targetColumn)
}
</script>

调试技巧

Web 端调试

  1. Vue DevTools: 安装浏览器扩展
  2. Network: 查看 API 请求和 WebSocket 连接状态
  3. Console: 查看 SDK 日志输出

移动端调试

bash
# H5 调试
npm run dev:h5

# 微信小程序调试
npm run dev:mp-weixin

# APP 调试
npm run dev:app

桌面端调试

bash
# 开发模式
npm run dev:desktop

# 调试主进程
npm run debug:main

# 调试渲染进程
npm run debug:renderer

测试

单元测试

bash
# 运行所有测试
npm run test

# 运行指定模块测试
npm run test -- packages/sdk

# 覆盖率报告
npm run test:coverage

E2E 测试

bash
# 运行 E2E 测试
npm run test:e2e

# headed 模式
npm run test:e2e -- --headed

性能优化

前端优化

  1. 虚拟列表: 长任务列表使用虚拟滚动
  2. 甘特图渲染: 大量任务时使用 Canvas 渲染替代 DOM
  3. 数据分页: 项目任务分页加载
  4. WebWorker: 甘特图计算放后台线程

服务端优化

  1. 连接池: 数据库连接池管理
  2. 缓存策略: Redis 缓存热点数据
  3. 消息队列: 削峰填谷,异步处理
  4. 分库分表: 大数据量水平拆分

部署

Docker 部署

bash
# 构建镜像
docker build -t pmspilot:latest .

# 运行容器
docker run -d -p 8080:8080 pmspilot:latest

Kubernetes 部署

bash
# 应用配置
kubectl apply -f k8s/

# 查看状态
kubectl get pods -n pmspilot

常见问题

Q: WebSocket 连接失败?

A: 检查:

  1. 服务端是否启动
  2. 防火墙是否放行端口
  3. Nginx 代理配置是否正确

Q: 甘特图渲染卡顿?

A: 检查:

  1. 任务数量是否过多
  2. 是否开启了 Canvas 渲染模式
  3. 浏览器 GPU 加速是否开启

Q: 多端数据不同步?

A: 检查:

  1. WebSocket 连接是否正常
  2. 数据版本号是否一致
  3. 缓存是否清理

相关资源