# 前端页面设计规范

本文档定义项目详情页面的前端设计规范，包括页面架构、组件设计、交互行为和视觉规范。

---

## 1. 页面文件结构

```
frontend/src/modules/admin/
├── pages/
│   ├── admin/
│   │   └── ProjectDetail.vue         # 超级管理员端项目详情
│   └── user/
│       └── UserProjectDetail.vue     # 普通用户端项目详情
├── components/
│   ├── BasicInfoForm.vue             # 基本信息编辑表单
│   ├── BasicInfoReadonly.vue         # 基本信息只读展示
│   ├── BusinessInfoReadonly.vue      # 业务信息只读展示
│   ├── DeploymentBusinessForm.vue    # 部署业务编辑表单
│   ├── DeploymentEnvironmentForm.vue # 部署环境编辑表单
│   ├── EnvironmentInfoReadonly.vue   # 环境信息只读展示
│   ├── HostsInfoReadonly.vue         # 主机信息只读展示
│   ├── HostsManagement.vue           # 主机管理组件
│   ├── MiddlewareCardsGrid.vue       # 中间件卡片网格
│   ├── MiddlewareInfoReadonly.vue    # 中间件只读展示
│   ├── AuthorizationManagement.vue   # 授权管理 (SuperAdmin Only)
│   ├── VersionHistory.vue            # 版本历史 (SuperAdmin Only)
│   ├── SaveConfirmDialog.vue         # 保存确认对话框
│   ├── CopyableField.vue             # 可复制字段组件
│   └── DiffTextField.vue             # 差异高亮输入框
```

---

## 2. 页面架构设计

### 2.1 整体布局

采用 **「固定头部 + 固定 Tab 导航 + 可滚动内容区域」** 三段式布局：

```
┌─────────────────────────────────────────────────────────────────────────┐
│  [固定区域] 生命周期状态提示横幅 (Alert Banner)                           │
├─────────────────────────────────────────────────────────────────────────┤
│  [固定区域] 页面头部 Header                                               │
│  ┌─────────────────────────────────────┬─────────────────────────────┐  │
│  │  ← 返回  项目名称                     │  [查看工单] [打回] [通过]   │  │
│  │          Namespace | 省份 城市        │  [下载配置] [编辑/保存]     │  │
│  │          状态标签组                    │                            │  │
│  └─────────────────────────────────────┴─────────────────────────────┘  │
├─────────────────────────────────────────────────────────────────────────┤
│  [固定区域] Tab 导航栏                                                    │
│  ┌─────────────────────────────────────────────────────────────────────┐│
│  │  基本信息 | 部署业务 | 部署环境 | 主机管理 | 中间件 | 授权 | 版本历史 ││
│  └─────────────────────────────────────────────────────────────────────┘│
├─────────────────────────────────────────────────────────────────────────┤
│  [滚动区域] Tab 内容区域                                                  │
└─────────────────────────────────────────────────────────────────────────┘
```

### 2.2 CSS 布局核心

```css
.project-detail-page {
  height: 100%;
  max-height: 100%;
  overflow: hidden;
  display: flex;
  flex-direction: column;
}

.header-section {
  flex-shrink: 0;
  background: rgb(var(--v-theme-surface));
  z-index: 1;
}

.content-area {
  flex: 1 1 auto;
  overflow-y: auto;
  overflow-x: hidden;
  min-height: 0;  /* 关键：防止 Flex 子元素撑破父容器 */
  padding-bottom: 24px;
}
```

---

## 3. 查看/编辑状态分离

### 3.1 状态定义

| 状态 | 变量名 | 说明 |
|:---|:---|:---|
| **查看状态** | `isEditMode = false` | 默认状态，展示 `*Readonly.vue` 组件 |
| **编辑状态** | `isEditMode = true` | 编辑模式，展示 `*Form.vue` 组件 |

### 3.2 查看状态交互

| 交互 | 实现 |
|:---|:---|
| 一键复制 | `CopyableField` 组件，点击图标复制到剪贴板 |
| 密码查看 | 点击"小眼睛"切换明文/密文 |
| 链接跳转 | URL 字段点击新窗口打开 |

### 3.3 编辑状态数据流

```typescript
// 进入编辑模式
const enterEditMode = () => {
  editForm.value = JSON.parse(JSON.stringify(masterData.value)) // 深拷贝
  isEditMode.value = true
}

// 脏数据检测
const hasChanges = computed(() => {
  return JSON.stringify(editForm.value) !== JSON.stringify(masterData.value)
})

// 退出保护
const exitEditMode = () => {
  if (hasChanges.value) {
    exitConfirmDialog.value = true  // 弹出确认对话框
  } else {
    isEditMode.value = false
  }
}
```

---

## 4. 用户侧 vs 管理侧差异

| 特性 | 管理员端 (ProjectDetail.vue) | 用户端 (UserProjectDetail.vue) |
|:---|:---|:---|
| **默认模式** | 查看模式 | 根据工单状态决定 |
| **授权信息 Tab** | ✅ 可见 | ❌ 不可见 |
| **版本历史 Tab** | ✅ 可见 | ❌ 不可见 |
| **主机管理 Tab** | ✅ 可见 | ❌ 不可见 |
| **基本信息** | 可编辑 | 只读（由管理员填写） |
| **编辑操作** | 直接保存（上帝模式） | 草稿 → 提交审核（工单流程） |
| **审批按钮** | ✅ 通过/打回 | ❌ 无 |
| **保存按钮** | 「保存修改」 | 「保存草稿」 |

### Tab 导航配置

```html
<!-- 管理员端 -->
<v-tabs v-model="activeTab">
  <v-tab value="basic">基本信息</v-tab>
  <v-tab value="business">部署业务</v-tab>
  <v-tab value="environment">部署环境</v-tab>
  <v-tab value="hosts">主机管理</v-tab>
  <v-tab value="middlewares">中间件</v-tab>
  <v-tab value="authorization" v-if="isSuperAdmin">授权信息</v-tab>
  <v-tab value="version-history" v-if="isSuperAdmin">版本历史</v-tab>
</v-tabs>

<!-- 用户端 -->
<v-tabs v-model="activeTab">
  <v-tab value="basic">基本信息</v-tab>
  <v-tab value="business">部署业务</v-tab>
  <v-tab value="environment">部署环境</v-tab>
  <v-tab value="middlewares">中间件</v-tab>
</v-tabs>
```

---

## 5. 生命周期状态展示

### 5.1 状态标签配置

```typescript
// 生命周期状态枚举
export const LIFECYCLE_STATUS = {
  init: '初始化',
  drafting: '填写中',
  reviewing: '审核中',
  released: '已发布',
  modifying: '变更中',
  archived: '已归档'
}

// 状态颜色映射
export const LIFECYCLE_STATUS_COLORS: Record<string, string> = {
  init: 'grey',
  drafting: 'info',
  reviewing: 'warning',
  released: 'success',
  modifying: 'primary',
  archived: 'grey-darken-1'
}

// 状态图标
const LIFECYCLE_STATUS_ICONS: Record<string, string> = {
  init: 'mdi-clock-outline',
  drafting: 'mdi-pencil',
  reviewing: 'mdi-eye',
  released: 'mdi-check-circle',
  modifying: 'mdi-sync',
  archived: 'mdi-archive'
}
```

### 5.2 生命周期提示横幅

根据当前状态显示上下文提示：

```typescript
const lifecycleStatusAlert = computed(() => {
  const status = masterData.value?.lifecycle_status
  switch (status) {
    case 'init':
      return { type: 'info', message: '项目已创建，等待指定填写人录入详细信息' }
    case 'drafting':
      return { type: 'info', message: `项目详情正在由 ${masterData.value.detail_filler_name} 填写中` }
    case 'reviewing':
      return { type: 'warning', message: '项目详情已提交，等待审核' }
    case 'modifying':
      return { type: 'info', message: '项目存在活跃的变更工单，主线数据不受影响' }
    case 'archived':
      return { type: 'warning', message: '项目已归档，仅保留历史数据' }
    default:
      return null
  }
})
```

---

## 6. 工单关联与跳转

### 6.1 工单按钮显示逻辑

```typescript
const showWorkflowButton = computed(() => {
  if (!masterData.value?.workflow_id) return false
  const status = masterData.value.lifecycle_status
  return ['drafting', 'reviewing', 'modifying'].includes(status)
})

const workflowButtonText = computed(() => {
  const status = masterData.value?.lifecycle_status
  switch (status) {
    case 'drafting': return '查看填写工单'
    case 'reviewing': return '查看审核工单'
    case 'modifying': return '查看修改工单'
    default: return '查看工单'
  }
})
```

### 6.2 多工单场景（MODIFYING 状态）

当存在多个修改工单时，使用下拉菜单或对话框展示工单列表：

```html
<v-menu v-if="multipleWorkflows">
  <template v-slot:activator="{ props }">
    <v-btn v-bind="props" color="info" variant="tonal">
      查看工单 ({{ workflowCount }})
      <v-icon end>mdi-chevron-down</v-icon>
    </v-btn>
  </template>
  <v-list>
    <v-list-item
      v-for="wf in relatedWorkflows"
      :key="wf.workflow_id"
      @click="navigateToWorkflow(wf.workflow_id)"
    >
      <v-list-item-title>{{ wf.workflow_id }}</v-list-item-title>
      <v-list-item-subtitle>{{ wf.creator_name }} | {{ formatDate(wf.created_at) }}</v-list-item-subtitle>
    </v-list-item>
  </v-list>
</v-menu>
```

---

## 7. 模块字段规范

### 7.1 基本信息模块

| 字段 | 只读模式 | 编辑模式 |
|:---|:---|:---|
| 项目名称 | 文本 + 复制 | `v-text-field` |
| 命名空间 | 文本 + 复制 | `disabled` 不可编辑 |
| 省份/城市 | 文本 | 级联选择器 |
| 项目性质 | 文本 | `v-select` |

### 7.2 部署业务模块

| 字段 | 只读模式 | 编辑模式 |
|:---|:---|:---|
| 部署人姓名 | 文本 | `v-text-field` 或用户搜索 |
| 业务入口 URL | 可点击链接 | `v-text-field` |
| 超管密码 | 脱敏 `******` + 查看按钮 | `v-text-field` 密码输入 |

### 7.3 中间件模块

采用 **卡片网格** 设计：
- 每个中间件一张卡片，响应式布局
- 卡片包含：类型图标 + 标题 + IP/Port
- 编辑模式：右上角显示「编辑」「删除」按钮
- 列表末尾显示「添加中间件」虚线框卡片

```typescript
const MIDDLEWARE_ICONS: Record<string, string> = {
  'mysql': 'mdi-database',
  'redis': 'mdi-database-clock',
  'emqx': 'mdi-broadcast',
  'minio': 'mdi-bucket',
  'influxdb': 'mdi-chart-timeline-variant',
  'nacos': 'mdi-cog-outline',
  'k8s-dashboard': 'mdi-kubernetes'
}
```

---

## 8. 核心组件设计

### 8.1 CopyableField - 可复制字段

```html
<template>
  <div class="copyable-field d-flex align-center gap-2">
    <span class="field-value">{{ displayValue }}</span>
    <v-btn icon="mdi-content-copy" size="x-small" variant="text" @click="copyToClipboard">
      <v-tooltip activator="parent" location="top">复制</v-tooltip>
    </v-btn>
  </div>
</template>
```

### 8.2 SaveConfirmDialog - 保存确认

展示变更 Diff 表格：

```html
<v-table density="compact">
  <thead>
    <tr><th>字段</th><th>修改前</th><th>修改后</th></tr>
  </thead>
  <tbody>
    <tr v-for="item in diffItems" :key="item.field">
      <td>{{ item.label }}</td>
      <td class="text-error">{{ item.oldValue || '空' }}</td>
      <td class="text-success">{{ item.newValue || '空' }}</td>
    </tr>
  </tbody>
</v-table>
```

### 8.3 DiffTextField - 差异高亮输入框

编辑模式下显示与主线数据的差异：

```html
<v-text-field
  v-model="inputValue"
  :label="label"
  :class="{ 'diff-highlight': hasDiff }"
  :hint="hasDiff ? `主线值: ${masterValue}` : ''"
  persistent-hint
>
  <template v-slot:prepend-inner v-if="hasDiff">
    <v-icon color="warning" size="small">mdi-alert-circle</v-icon>
  </template>
</v-text-field>

<style scoped>
.diff-highlight :deep(.v-field__outline) {
  --v-field-border-color: rgb(var(--v-theme-warning));
}
</style>
```

---

## 9. 视觉设计规范

### 9.1 色彩系统

| 用途 | Vuetify 类 |
|:---|:---|
| 主色调 | `color="primary"` (Deep Purple) |
| 成功状态 | `color="success"` (Green) |
| 警告状态 | `color="warning"` (Orange) |
| 错误状态 | `color="error"` (Red) |
| 页面背景 | `bg-grey-lighten-4` |

### 9.2 卡片设计

```html
<v-card elevation="2" rounded="lg" class="pa-4">
  <!-- 圆角 8px, 阴影 level-2, 内边距 16px -->
</v-card>
```

### 9.3 排版规范

| 元素 | 样式类 |
|:---|:---|
| 页面标题 | `text-h4 font-weight-bold` |
| 卡片标题 | `text-h6` |
| 字段标签 | `text-medium-emphasis text-body-2` |
| 字段值 | `text-high-emphasis` |

### 9.4 间距规范（8px 网格）

| 间距 | 类 | 值 |
|:---|:---|:---|
| 紧凑 | `pa-2` | 8px |
| 标准 | `pa-4` | 16px |
| 宽松 | `pa-6` | 24px |

---

## 10. 响应式设计

### 10.1 断点

| 断点 | 宽度 |
|:---|:---|
| xs | < 600px |
| sm | 600px - 960px |
| md | 960px - 1280px |
| lg | 1280px - 1920px |

### 10.2 中间件卡片响应式

```html
<v-row>
  <v-col v-for="mw in middlewares" :key="mw.type"
    cols="12" sm="6" md="4" lg="3"
  >
    <MiddlewareCard :data="mw" />
  </v-col>
</v-row>
```

---

## 11. TypeScript 类型定义

```typescript
// 项目详情
interface ProjectDetail {
  id: number
  project_id: string
  project_name: string
  namespace: string
  province: string
  city: string
  project_nature: string
  lifecycle_status: string
  project_certification: string
  workflow_id: string
  detail_filler_id: number
  detail_filler_name: string
  deployment_business: DeploymentBusiness | null
  deployment_environment: DeploymentEnvironment | null
  middlewares: Middleware[]
  hosts: Host[]
  draft_data: Record<string, unknown> | null
}

// Diff 项
interface DiffItem {
  field: string
  label: string
  oldValue: string | number | boolean
  newValue: string | number | boolean
}
```

---

## 12. 组件清单

| 组件 | 说明 | 复用范围 |
|:---|:---|:---|
| `BasicInfoForm.vue` | 基本信息编辑表单 | 管理员/用户 |
| `BasicInfoReadonly.vue` | 基本信息只读 | 管理员/用户 |
| `DeploymentBusinessForm.vue` | 业务信息表单 | 管理员/用户 |
| `DeploymentEnvironmentForm.vue` | 环境信息表单 | 管理员/用户 |
| `MiddlewareCardsGrid.vue` | 中间件卡片网格 | 管理员/用户 |
| `AuthorizationManagement.vue` | 授权管理 | 仅管理员 |
| `VersionHistory.vue` | 版本历史 | 仅管理员 |
| `HostsManagement.vue` | 主机管理 | 仅管理员 |
| `SaveConfirmDialog.vue` | 保存确认对话框 | 管理员 |
| `CopyableField.vue` | 可复制字段 | 通用 |
| `DiffTextField.vue` | 差异高亮输入框 | 通用 |
