# Lyzsys 前端开发文档

## 目录

- [项目概述](#项目概述)
- [技术栈](#技术栈)
- [项目结构](#项目结构)
- [开发指南](#开发指南)
- [核心功能](#核心功能)
- [组件库](#组件库)
- [样式指南](#样式指南)
- [部署指南](#部署指南)

---

## 项目概述

Lyzsys 前端是基于 Vue 3 + TypeScript + Element Plus 构建的现代化管理系统，采用 Composition API 和模块化架构，支持多租户、权限管理、国际化等功能。

### 核心特性

- **Vue 3 Composition API**: 充分利用 Vue 3 的新特性
- **TypeScript**: 完整的类型支持
- **Vite 构建**: 极速的开发体验
- **Element Plus**: 企业级 UI 组件库
- **权限管理**: 基于 RBAC 的权限控制
- **国际化**: 支持多语言切换
- **主题定制**: 灵活的主题配置

---

## 技术栈

### 核心框架

| 技术 | 版本 | 说明 |
|------|------|------|
| Vue | 3.2+ | 渐进式 JavaScript 框架 |
| TypeScript | 4.x | JavaScript 的超集 |
| Vite | 4.x | 下一代前端构建工具 |
| Vue Router | 4.x | Vue.js 官方路由管理器 |
| Pinia | 2.x | Vue 官方状态管理库 |

### UI 框架

| 技术 | 版本 | 说明 |
|------|------|------|
| Element Plus | 2.x | 基于 Vue 3 的组件库 |
| UnoCSS | - | 原子化 CSS 引擎 |
| Animate.css | - | CSS 动画库 |
| Iconify | - | 统一的图标框架 |

### 工具库

| 技术 | 版本 | 说明 |
|------|------|------|
| Axios | - | HTTP 客户端 |
| Day.js | - | 轻量级日期处理库 |
| Lodash | - | JavaScript 实用工具库 |
| Crypto-js | - | JavaScript 加密库 |
| DOMPurify | - | XSS 过滤器 |
| WangEditor | - | 富文本编辑器 |
| ECharts | - | 数据可视化库 |

### 开发工具

| 技术 | 版本 | 说明 |
|------|------|------|
| ESLint | - | 代码检查工具 |
| Prettier | - | 代码格式化工具 |
| Stylelint | - | CSS 代码检查工具 |
| Commitlint | - | Git 提交信息检查 |
| unplugin-auto-import | - | 自动导入 API |
| unplugin-vue-components | - | 自动导入组件 |

---

## 项目结构

### 目录结构

```
lyzsys-ui-admin/
├── .github/              # GitHub Actions 配置
├── .husky/              # Git Hooks 配置
├── .vscode/             # VSCode 配置
├── public/              # 静态资源
├── src/                 # 源代码
│   ├── api/             # API 接口管理
│   │   ├── ai/          # AI 相关接口
│   │   ├── bpm/         # 工作流接口
│   │   ├── crm/         # CRM 接口
│   │   ├── erp/         # ERP 接口
│   │   ├── system/      # 系统管理接口
│   │   └── login/       # 登录接口
│   ├── assets/          # 静态资源
│   │   ├── images/      # 图片
│   │   ├── styles/      # 样式文件
│   │   └── svg/         # SVG 图标
│   ├── components/      # 全局组件
│   ├── config/          # 全局配置
│   ├── directives/      # 自定义指令
│   ├── hooks/           # 组合式函数
│   ├── layout/          # 布局组件
│   ├── locales/         # 国际化文件
│   │   ├── en/          # 英文
│   │   └── zh-CN/       # 简体中文
│   ├── plugins/         # 插件配置
│   ├── router/          # 路由配置
│   ├── store/           # 状态管理
│   │   ├── modules/     # 模块化 Store
│   │   └── index.ts     # Store 入口
│   ├── styles/          # 全局样式
│   ├── types/           # TypeScript 类型定义
│   ├── utils/           # 工具函数
│   ├── views/           # 页面组件
│   ├── App.vue          # 应用根组件
│   ├── main.ts          # 应用入口
│   └── permission.ts    # 路由权限控制
├── types/               # 全局类型定义
├── .env.base            # 基础环境变量
├── .env.dev             # 开发环境变量
├── .env.pro             # 生产环境变量
├── .env.test            # 测试环境变量
├── .eslintrc.js         # ESLint 配置
├── .gitignore           # Git 忽略文件
├── .prettierrc.js       # Prettier 配置
├── index.html           # HTML 模板
├── package.json         # 项目依赖
├── tsconfig.json        # TypeScript 配置
└── vite.config.ts       # Vite 配置
```

### 核心文件说明

#### main.ts

应用入口文件，负责初始化 Vue 应用：

```typescript
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import { setupStore } from './store'
import { setupRouter } from './router'
import { setupElPlus, setupIcons } from './plugins'

async function bootstrap() {
  const app = createApp(App)

  // 配置 Store
  setupStore(app)

  // 配置 Router
  await setupRouter(app)

  // 配置插件
  setupElPlus(app)
  setupIcons(app)

  // 挂载应用
  app.mount('#app')
}

bootstrap()
```

#### App.vue

应用根组件：

```vue
<template>
  <ConfigProvider :locale="getLocale">
    <router-view />
  </ConfigProvider>
</template>

<script setup lang="ts">
import { ConfigProvider } from 'element-plus'
import { useLocale } from './locales/useLocale'
import { getLocale } from './locales/getLocale'
</script>
```

#### permission.ts

路由权限控制：

```typescript
import router from './router'
import { useUserStore } from './store/modules/user'
import { usePermissionStore } from './store/modules/permission'

router.beforeEach(async (to, from, next) => {
  const userStore = useUserStore()
  const permissionStore = usePermissionStore()

  // 检查是否登录
  if (userStore.getToken) {
    // 已登录
    if (to.path === '/login') {
      next({ path: '/' })
    } else {
      // 检查是否已获取权限
      if (permissionStore.getIsAddRouters) {
        next()
      } else {
        // 获取权限和路由
        await permissionStore.generateRoutes()
        next({ ...to, replace: true })
      }
    }
  } else {
    // 未登录
    if (to.path === '/login') {
      next()
    } else {
      next(`/login?redirect=${to.path}`)
    }
  }
})
```

---

## 开发指南

### 环境准备

#### 必需软件

| 软件 | 版本要求 | 下载地址 |
|------|----------|----------|
| Node.js | 16+ | https://nodejs.org/ |
| pnpm | 8+ | https://pnpm.io/ |
| VSCode | 最新版 | https://code.visualstudio.com/ |

#### 推荐插件

- Vue - Official
- TypeScript Vue Plugin (Volar)
- ESLint
- Prettier - Code formatter
- Stylelint
- UnoCSS

### 快速开始

#### 1. 克隆项目

```bash
git clone https://github.com/your-org/lyzsys.git
cd lyzsys/lyzsys-ui-admin
```

#### 2. 安装依赖

```bash
pnpm install
```

#### 3. 启动开发服务器

```bash
pnpm dev
```

#### 4. 访问应用

- 应用地址: http://localhost:5173
- 用户名: admin
- 密码: admin123

### 开发规范

#### 命名规范

**文件命名**:
- 组件文件：大驼峰 `UserList.vue`
- 工具文件：小驼峰 `formatDate.ts`
- 样式文件：小驼峰 `userList.scss`

**变量命名**:
```typescript
// 常量：全大写
const MAX_SIZE = 100

// 变量：小驼峰
const userName = 'admin'

// 接口：大驼峰
interface UserInfo {
  id: number
  name: string
}

// 类型：大驼峰
type UserRole = 'admin' | 'user'

// 枚举：大驼峰
enum UserStatus {
  Active = 'active',
  Inactive = 'inactive'
}
```

**组件命名**:
```vue
<script setup lang="ts">
// 组件名：大驼峰
defineOptions({
  name: 'UserList'
})
</script>
```

#### 注释规范

**函数注释**:
```typescript
/**
 * 获取用户列表
 * @param params 查询参数
 * @returns 用户列表
 */
async function getUserList(params: UserPageReqVO) {
  return await userApi.getUserPage(params)
}
```

**组件注释**:
```vue
<!--
  @description 用户列表组件
  @author Lyzsys Team
-->
<template>
  <!-- ... -->
</template>
```

#### 代码风格

**使用 Composition API**:
```vue
<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'

// 响应式数据
const loading = ref(false)
const userList = ref<User[]>([])

// 计算属性
const totalCount = computed(() => userList.value.length)

// 方法
async function fetchUsers() {
  loading.value = true
  try {
    userList.value = await userApi.getUserList()
  } finally {
    loading.value = false
  }
}

// 生命周期
onMounted(() => {
  fetchUsers()
})
</script>
```

**使用 TypeScript**:
```typescript
interface User {
  id: number
  name: string
  email: string
}

type UserRole = 'admin' | 'user' | 'guest'

const user: User = {
  id: 1,
  name: 'Admin',
  email: 'admin@example.com'
}

const role: UserRole = 'admin'
```

### 组件开发

#### 创建组件

```vue
<!-- components/UserList.vue -->
<template>
  <div class="user-list">
    <el-table :data="userList" :loading="loading">
      <el-table-column prop="name" label="姓名" />
      <el-table-column prop="email" label="邮箱" />
      <el-table-column label="操作">
        <template #default="{ row }">
          <el-button link @click="handleEdit(row)">编辑</el-button>
          <el-button link @click="handleDelete(row)">删除</el-button>
        </template>
      </el-table-column>
    </el-table>
  </div>
</template>

<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { ElMessage } from 'element-plus'

interface User {
  id: number
  name: string
  email: string
}

defineOptions({
  name: 'UserList'
})

const loading = ref(false)
const userList = ref<User[]>([])

async function fetchUsers() {
  loading.value = true
  try {
    const { data } = await userApi.getUserList()
    userList.value = data
  } finally {
    loading.value = false
  }
}

function handleEdit(user: User) {
  console.log('编辑用户', user)
}

function handleDelete(user: User) {
  console.log('删除用户', user)
}

onMounted(() => {
  fetchUsers()
})
</script>

<style scoped lang="scss">
.user-list {
  padding: 20px;
}
</style>
```

#### 组件通信

**Props**:
```vue
<script setup lang="ts">
interface Props {
  userId: number
  userName?: string
}

const props = withDefaults(defineProps<Props>(), {
  userName: ''
})
</script>
```

**Emits**:
```vue
<script setup lang="ts">
interface Emits {
  (e: 'update:modelValue', value: string): void
  (e: 'change', value: string): void
}

const emit = defineEmits<Emits>()

function handleChange(value: string) {
  emit('update:modelValue', value)
  emit('change', value)
}
</script>
```

**Slots**:
```vue
<template>
  <div class="card">
    <slot name="header">
      <h3>默认标题</h3>
    </slot>
    <slot>
      默认内容
    </slot>
    <slot name="footer">
      <el-button>确定</el-button>
    </slot>
  </div>
</template>
```

### API 开发

#### API 定义

```typescript
// api/system/user/index.ts
import request from '@/utils/request'

// 获取用户分页
export function getUserPage(params: UserPageReqVO) {
  return request.get({ url: '/system/user/page', params })
}

// 获取用户详情
export function getUser(id: number) {
  return request.get({ url: `/system/user/get?id=${id}` })
}

// 创建用户
export function createUser(data: UserCreateReqVO) {
  return request.post({ url: '/system/user/create', data })
}

// 更新用户
export function updateUser(data: UserUpdateReqVO) {
  return request.put({ url: '/system/user/update', data })
}

// 删除用户
export function deleteUser(id: number) {
  return request.delete({ url: `/system/user/delete?id=${id}` })
}
```

#### API 调用

```vue
<script setup lang="ts">
import { ref } from 'vue'
import { getUserPage } from '@/api/system/user'

const loading = ref(false)
const userList = ref([])

async function fetchUsers() {
  loading.value = true
  try {
    const { data } = await getUserPage({
      pageNo: 1,
      pageSize: 10
    })
    userList.value = data.list
  } finally {
    loading.value = false
  }
}
</script>
```

### 路由开发

#### 路由配置

```typescript
// router/modules/system.ts
import type { AppRouteModule } from '@/router/types'

const system: AppRouteModule = {
  path: '/system',
  name: 'System',
  component: 'LAYOUT',
  redirect: '/system/user',
  meta: {
    title: '系统管理',
    icon: 'system',
    orderNo: 1000
  },
  children: [
    {
      path: 'user',
      name: 'SystemUser',
      component: () => import('@/views/system/user/index.vue'),
      meta: {
        title: '用户管理',
        icon: 'user',
        noCache: true
      }
    },
    {
      path: 'role',
      name: 'SystemRole',
      component: () => import('@/views/system/role/index.vue'),
      meta: {
        title: '角色管理',
        icon: 'role',
        noCache: true
      }
    }
  ]
}

export default system
```

#### 路由跳转

```typescript
import { useRouter } from 'vue-router'

const router = useRouter()

// 路径跳转
router.push('/system/user')

// 命名路由跳转
router.push({ name: 'SystemUser' })

// 带参数跳转
router.push({
  path: '/system/user',
  query: { id: 1 }
})

// 替换当前路由
router.replace('/system/user')

// 后退
router.back()

// 前进
router.forward()
```

### 状态管理

#### Store 定义

```typescript
// store/modules/user.ts
import { defineStore } from 'pinia'
import { getUserInfo, login } from '@/api/system/user'

interface UserState {
  token: string
  userInfo: UserInfo | null
}

export const useUserStore = defineStore('user', {
  state: (): UserState => ({
    token: '',
    userInfo: null
  }),

  getters: {
    getToken: (state) => state.token,
    getUserInfo: (state) => state.userInfo
  },

  actions: {
    setToken(token: string) {
      this.token = token
    },

    async login(params: LoginReqVO) {
      const { data } = await login(params)
      this.setToken(data.token)
    },

    async getUserInfoAction() {
      const { data } = await getUserInfo()
      this.userInfo = data
    },

    async logout() {
      this.token = ''
      this.userInfo = null
    }
  },

  persist: {
    key: 'user-store',
    storage: localStorage
  }
})
```

#### Store 使用

```vue
<script setup lang="ts">
import { useUserStore } from '@/store/modules/user'

const userStore = useUserStore()

// 访问 state
console.log(userStore.token)

// 访问 getters
console.log(userStore.getToken)

// 调用 actions
await userStore.login({
  username: 'admin',
  password: 'admin123'
})

// 重置 state
userStore.$reset()
</script>
```

---

## 核心功能

### 权限管理

#### 权限指令

```vue
<template>
  <!-- 有权限时显示 -->
  <el-button v-hasPermi="['system:user:create']">创建用户</el-button>

  <!-- 有角色时显示 -->
  <el-button v-hasRole="['admin']">管理员操作</el-button>

  <!-- 同时满足多个权限 -->
  <el-button v-hasPermi="['system:user:update', 'system:user:delete']">
    操作
  </el-button>
</template>
```

#### 权限函数

```typescript
import { usePermission } from '@/hooks/web/usePermission'

const { hasPermission, hasRole } = usePermission()

// 检查权限
if (hasPermission(['system:user:create'])) {
  // 有权限
}

// 检查角色
if (hasRole(['admin'])) {
  // 是管理员
}
```

### 国际化

#### 使用翻译

```vue
<template>
  <!-- 使用翻译 -->
  <h1>{{ $t('common.title') }}</h1>

  <!-- 带参数的翻译 -->
  <p>{{ $t('common.welcome', { name: 'Admin' }) }}</p>
</template>

<script setup lang="ts">
import { useI18n } from '@/hooks/web/useI18n'

const { t } = useI18n()

// 使用翻译
const message = t('common.title')
const welcome = t('common.welcome', { name: 'Admin' })
</script>
```

#### 添加翻译

```typescript
// locales/zh-CN/common.ts
export default {
  title: 'Lyzsys 管理系统',
  welcome: '欢迎，{name}',
  logout: '退出登录'
}
```

```typescript
// locales/en/common.ts
export default {
  title: 'Lyzsys Admin',
  welcome: 'Welcome, {name}',
  logout: 'Logout'
}
```

### 主题定制

#### 修改主题色

```typescript
// styles/variables.scss
$primary-color: #409eff;
$success-color: #67c23a;
$warning-color: #e6a23c;
$danger-color: #f56c6c;
$error-color: #f56c6c;
```

#### 动态切换主题

```vue
<script setup lang="ts">
import { useAppStore } from '@/store/modules/app'

const appStore = useAppStore()

// 切换暗黑模式
function toggleDarkMode() {
  appStore.setDarkMode(!appStore.getDarkMode)
}
</script>
```

### 文件上传

#### 单文件上传

```vue
<template>
  <el-upload
    :action="uploadUrl"
    :headers="uploadHeaders"
    :on-success="handleSuccess"
    :before-upload="beforeUpload"
  >
    <el-button type="primary">上传文件</el-button>
  </el-upload>
</template>

<script setup lang="ts">
import { ref } from 'vue'
import { ElMessage } from 'element-plus'
import { getAccessToken } from '@/utils/auth'

const uploadUrl = import.meta.env.VITE_BASE_URL + '/admin-api/infra/file/upload'
const uploadHeaders = ref({
  Authorization: 'Bearer ' + getAccessToken()
})

function beforeUpload(file: File) {
  const isLt2M = file.size / 1024 / 1024 < 2
  if (!isLt2M) {
    ElMessage.error('上传文件大小不能超过 2MB!')
  }
  return isLt2M
}

function handleSuccess(response: any) {
  if (response.code === 0) {
    ElMessage.success('上传成功')
    console.log('文件 URL:', response.data)
  } else {
    ElMessage.error(response.msg)
  }
}
</script>
```

#### 多文件上传

```vue
<template>
  <el-upload
    :action="uploadUrl"
    :headers="uploadHeaders"
    multiple
    :limit="3"
    :on-exceed="handleExceed"
    :on-success="handleSuccess"
  >
    <el-button type="primary">多文件上传</el-button>
  </el-upload>
</template>

<script setup lang="ts">
function handleExceed() {
  ElMessage.warning('最多只能上传 3 个文件')
}
</script>
```

### 表单验证

#### 表单定义

```vue
<template>
  <el-form
    ref="formRef"
    :model="form"
    :rules="rules"
    label-width="100px"
  >
    <el-form-item label="用户名" prop="username">
      <el-input v-model="form.username" placeholder="请输入用户名" />
    </el-form-item>

    <el-form-item label="邮箱" prop="email">
      <el-input v-model="form.email" placeholder="请输入邮箱" />
    </el-form-item>

    <el-form-item label="密码" prop="password">
      <el-input
        v-model="form.password"
        type="password"
        placeholder="请输入密码"
      />
    </el-form-item>

    <el-form-item>
      <el-button type="primary" @click="handleSubmit">提交</el-button>
      <el-button @click="handleReset">重置</el-button>
    </el-form-item>
  </el-form>
</template>

<script setup lang="ts">
import { ref, reactive } from 'vue'
import type { FormInstance, FormRules } from 'element-plus'

const formRef = ref<FormInstance>()

const form = reactive({
  username: '',
  email: '',
  password: ''
})

const rules: FormRules = {
  username: [
    { required: true, message: '请输入用户名', trigger: 'blur' },
    { min: 3, max: 20, message: '长度在 3 到 20 个字符', trigger: 'blur' }
  ],
  email: [
    { required: true, message: '请输入邮箱', trigger: 'blur' },
    { type: 'email', message: '请输入正确的邮箱地址', trigger: 'blur' }
  ],
  password: [
    { required: true, message: '请输入密码', trigger: 'blur' },
    { min: 6, max: 20, message: '长度在 6 到 20 个字符', trigger: 'blur' }
  ]
}

async function handleSubmit() {
  if (!formRef.value) return

  await formRef.value.validate((valid) => {
    if (valid) {
      console.log('表单数据:', form)
      // 提交表单
    }
  })
}

function handleReset() {
  formRef.value?.resetFields()
}
</script>
```

### 表格操作

#### 基础表格

```vue
<template>
  <el-table :data="tableData" :loading="loading" border>
    <el-table-column prop="id" label="ID" width="80" />
    <el-table-column prop="username" label="用户名" />
    <el-table-column prop="email" label="邮箱" />
    <el-table-column prop="status" label="状态">
      <template #default="{ row }">
        <el-tag :type="row.status === 'active' ? 'success' : 'danger'">
          {{ row.status === 'active' ? '激活' : '禁用' }}
        </el-tag>
      </template>
    </el-table-column>
    <el-table-column label="操作" width="200" fixed="right">
      <template #default="{ row }">
        <el-button link @click="handleEdit(row)">编辑</el-button>
        <el-button link @click="handleDelete(row)">删除</el-button>
      </template>
    </el-table-column>
  </el-table>
</template>

<script setup lang="ts">
import { ref } from 'vue'

interface User {
  id: number
  username: string
  email: string
  status: string
}

const loading = ref(false)
const tableData = ref<User[]>([])

function handleEdit(user: User) {
  console.log('编辑用户', user)
}

function handleDelete(user: User) {
  console.log('删除用户', user)
}
</script>
```

#### 分页表格

```vue
<template>
  <div>
    <el-table :data="tableData" :loading="loading" border>
      <!-- 表格列 -->
    </el-table>

    <el-pagination
      v-model:current-page="queryParams.pageNo"
      v-model:page-size="queryParams.pageSize"
      :total="total"
      :page-sizes="[10, 20, 50, 100]"
      layout="total, sizes, prev, pager, next, jumper"
      @size-change="handleSizeChange"
      @current-change="handleCurrentChange"
    />
  </div>
</template>

<script setup lang="ts">
import { ref, reactive } from 'vue'

const loading = ref(false)
const tableData = ref([])
const total = ref(0)

const queryParams = reactive({
  pageNo: 1,
  pageSize: 10
})

async function fetchTableData() {
  loading.value = true
  try {
    const { data } = await userApi.getUserPage(queryParams)
    tableData.value = data.list
    total.value = data.total
  } finally {
    loading.value = false
  }
}

function handleSizeChange() {
  fetchTableData()
}

function handleCurrentChange() {
  fetchTableData()
}

// 初始加载
fetchTableData()
</script>
```

---

## 组件库

### Element Plus

#### 按钮

```vue
<el-button>默认按钮</el-button>
<el-button type="primary">主要按钮</el-button>
<el-button type="success">成功按钮</el-button>
<el-button type="warning">警告按钮</el-button>
<el-button type="danger">危险按钮</el-button>
<el-button link>文字按钮</el-button>
```

#### 表单

```vue
<el-input v-model="value" placeholder="请输入内容" />
<el-input v-model="value" type="textarea" />
<el-input v-model="value" type="password" />
```

#### 选择器

```vue
<el-select v-model="value" placeholder="请选择">
  <el-option label="选项1" value="1" />
  <el-option label="选项2" value="2" />
</el-select>
```

#### 日期选择器

```vue
<el-date-picker
  v-model="value"
  type="date"
  placeholder="选择日期"
/>

<el-date-picker
  v-model="value"
  type="daterange"
  start-placeholder="开始日期"
  end-placeholder="结束日期"
/>
```

#### 对话框

```vue
<el-dialog v-model="visible" title="提示" width="30%">
  <span>这是一段内容</span>
  <template #footer>
    <el-button @click="visible = false">取消</el-button>
    <el-button type="primary" @click="visible = false">确定</el-button>
  </template>
</el-dialog>
```

#### 消息提示

```typescript
import { ElMessage, ElMessageBox, ElNotification } from 'element-plus'

// 消息提示
ElMessage.success('操作成功')
ElMessage.warning('警告消息')
ElMessage.error('错误消息')
ElMessage.info('提示消息')

// 确认框
ElMessageBox.confirm('确定要删除吗?', '提示', {
  confirmButtonText: '确定',
  cancelButtonText: '取消',
  type: 'warning'
})
  .then(() => {
    ElMessage.success('删除成功')
  })
  .catch(() => {
    ElMessage.info('已取消删除')
  })

// 通知
ElNotification({
  title: '通知',
  message: '这是一条通知消息',
  type: 'success'
})
```

### 自定义组件

#### 图表组件 (ECharts)

```vue
<template>
  <div ref="chartRef" style="width: 100%; height: 400px"></div>
</template>

<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'
import * as echarts from 'echarts'
import type { EChartsOption } from 'echarts'

const chartRef = ref<HTMLDivElement>()
let chartInstance: echarts.ECharts | null = null

onMounted(() => {
  if (chartRef.value) {
    chartInstance = echarts.init(chartRef.value)

    const option: EChartsOption = {
      title: {
        text: '示例图表'
      },
      xAxis: {
        data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
      },
      yAxis: {},
      series: [
        {
          type: 'line',
          data: [150, 230, 224, 218, 135, 147, 260]
        }
      ]
    }

    chartInstance.setOption(option)
  }
})

onUnmounted(() => {
  chartInstance?.dispose()
})
</script>
```

#### 富文本编辑器 (WangEditor)

```vue
<template>
  <div ref="editorRef" style="height: 500px"></div>
</template>

<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'
import { createEditor, createToolbar } from '@wangeditor/editor-for-vue'

const editorRef = ref<HTMLDivElement>()
let editor: any = null
let toolbar: any = null

onMounted(() => {
  if (editorRef.value) {
    editor = createEditor({
      selector: editorRef.value,
      html: '<p>默认内容</p>',
      config: {}
    })

    toolbar = createToolbar({
      editor,
      config: {}
    })
  }
})

onUnmounted(() => {
  editor?.destroy()
  toolbar?.destroy()
})
</script>
```

---

## 样式指南

### CSS 预处理器

使用 SCSS 作为 CSS 预处理器：

```scss
// 使用变量
$primary-color: #409eff;
$border-radius: 4px;

// 使用嵌套
.button {
  background: $primary-color;
  border-radius: $border-radius;

  &:hover {
    background: darken($primary-color, 10%);
  }

  &.active {
    background: lighten($primary-color, 10%);
  }
}
```

### UnoCSS

使用原子化 CSS：

```vue
<template>
  <div class="flex items-center justify-center h-screen bg-gray-100">
    <div class="p-6 bg-white rounded-lg shadow-lg">
      <h1 class="text-2xl font-bold text-gray-800">标题</h1>
      <p class="mt-4 text-gray-600">内容</p>
    </div>
  </div>
</template>
```

### 响应式设计

```scss
// 使用媒体查询
.container {
  padding: 20px;

  @media (max-width: 768px) {
    padding: 10px;
  }

  @media (max-width: 480px) {
    padding: 5px;
  }
}
```

### 主题定制

```scss
// 自定义主题色
:root {
  --el-color-primary: #409eff;
  --el-color-success: #67c23a;
  --el-color-warning: #e6a23c;
  --el-color-danger: #f56c6c;
}

// 暗黑模式
.dark {
  --el-bg-color: #1a1a1a;
  --el-text-color-primary: #ffffff;
}
```

---

## 部署指南

### 本地构建

```bash
# 开发环境构建
pnpm build:dev

# 生产环境构建
pnpm build:prod

# 测试环境构建
pnpm build:test
```

### 环境变量配置

```bash
# .env.base
VITE_BASE_URL=/api

# .env.dev
VITE_BASE_URL=http://localhost:48080

# .env.prod
VITE_BASE_URL=https://api.example.com
```

### Docker 部署

#### 1. 构建镜像

```bash
docker build -t lyzsys/lyzsys-ui-admin:latest .
```

#### 2. 运行容器

```bash
docker run -d \
  --name lyzsys-ui-admin \
  -p 80:80 \
  lyzsys/lyzsys-ui-admin:latest
```

### Nginx 配置

```nginx
server {
  listen 80;
  server_name example.com;

  root /usr/share/nginx/html;
  index index.html;

  location / {
    try_files $uri $uri/ /index.html;
  }

  location /api {
    proxy_pass http://backend:48080;
    proxy_set_header Host $host;
    proxy_set_header X-Real-IP $remote_addr;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  }

  gzip on;
  gzip_types text/plain text/css application/json application/javascript;
}
```

---

## 附录

### 常见问题

#### Q1: 如何添加新页面？

1. 在 `views/` 目录下创建页面组件
2. 在 `router/modules/` 中配置路由
3. 添加国际化翻译（如需要）
4. 在后台系统中配置菜单权限

#### Q2: 如何调用后端 API？

1. 在 `api/` 目录下创建 API 文件
2. 定义 API 接口函数
3. 在组件中导入并调用
4. 处理响应数据

#### Q3: 如何实现权限控制？

1. 使用 `v-hasPermi` 指令控制按钮显示
2. 使用 `v-hasRole` 指令控制角色访问
3. 在路由配置中设置权限 meta
4. 后端接口进行权限验证

#### Q4: 如何切换语言？

1. 在 `locales/` 目录下添加语言文件
2. 使用 `$t()` 函数进行翻译
3. 在语言切换器中调用 `setLocale()` 方法

### 参考资源

- [Vue 3 官方文档](https://cn.vuejs.org/)
- [TypeScript 官方文档](https://www.typescriptlang.org/zh/)
- [Vite 官方文档](https://cn.vitejs.dev/)
- [Element Plus 官方文档](https://element-plus.org/zh-CN/)
- [Pinia 官方文档](https://pinia.vuejs.org/zh/)
- [UnoCSS 官方文档](https://unocss.dev/)

---

**文档版本**: v1.0.0
**最后更新**: 2025-01-19
**维护团队**: Lyzsys 开发团队
