# 盘古用户平台 - 应用管理模块前端技术方案
---
| 文档信息 | 内容 |
|---------|------|
| **文档版本** | V1.0 |
| **模块名称** | 应用管理模块 - 前端 |
| **编写团队 | pangu |
| **创建日期** | 2026-01-31 |
| **审核状态** | 待评审 |
---
## 1. 技术栈
| 技术 | 版本 | 用途 |
|------|------|------|
| Vue | 3.x | 前端框架 |
| Element Plus | 2.x | UI组件库 |
| Axios | 1.x | HTTP客户端 |
| Pinia | 2.x | 状态管理 |
| Vite | 5.x | 构建工具 |
---
## 2. 目录结构
```
frontend/src/
├── api/
│ └── application.js # 应用管理API接口封装
├── views/
│ └── application/
│ ├── index.vue # 应用列表页(主页面)
│ └── components/
│ ├── AppDialog.vue # 新增/编辑弹窗组件
│ └── SecretDialog.vue # 密钥展示弹窗组件
├── mock/
│ └── application.js # Mock数据(开发阶段使用)
└── router/
└── index.js # 路由配置(添加应用管理路由)
```
---
## 3. 页面组件设计
### 3.1 应用列表页 (index.vue)
#### 3.1.1 页面功能
- 应用列表展示(分页)
- 按应用名称、编码、状态筛选
- 新增应用入口
- 编辑应用入口
- 重置密钥操作
- 删除应用操作
#### 3.1.2 页面布局
```
┌─────────────────────────────────────────────────────────────────────┐
│ 搜索区域(el-card) │
│ ┌────────────────┐ ┌────────────────┐ ┌──────────┐ ┌────┐ ┌────┐ │
│ │ 应用名称 │ │ 应用编码 │ │ 状态 ▼ │ │搜索│ │重置│ │
│ └────────────────┘ └────────────────┘ └──────────┘ └────┘ └────┘ │
└─────────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ 操作区域 │
│ [+ 新增] │
├─────────────────────────────────────────────────────────────────────┤
│ 表格区域(el-table) │
│ ┌───────┬──────────┬─────────────────────┬────────┬────────┬──────┐│
│ │应用名称│ 应用编码 │ 授权接口 │ 状态 │ 创建时间│ 操作 ││
│ ├───────┼──────────┼─────────────────────┼────────┼────────┼──────┤│
│ │AI智慧 │ YY000001 │ [学校][年级][班级] │ ● 正常 │01-01 │ 编辑 ││
│ │平台 │ │ │ │10:00 │ 重置 ││
│ │ │ │ │ │ │ 删除 ││
│ ├───────┼──────────┼─────────────────────┼────────┼────────┼──────┤│
│ │在线课 │ YY000002 │ [学生][会员]+2 │ ● 停用 │01-02 │ 编辑 ││
│ │堂系统 │ │ │ │14:30 │ 重置 ││
│ │ │ │ │ │ │ 删除 ││
│ └───────┴──────────┴─────────────────────┴────────┴────────┴──────┘│
├─────────────────────────────────────────────────────────────────────┤
│ 分页区域 │
│ 共20条 [<] 1 2 3 ... [>] │
└─────────────────────────────────────────────────────────────────────┘
```
#### 3.1.3 组件状态
```javascript
// 查询参数
const queryParams = ref({
pageNum: 1,
pageSize: 10,
appName: '', // 应用名称
appCode: '', // 应用编码
status: '' // 状态:空=全部,0=正常,1=停用
})
// 列表数据
const loading = ref(false) // 加载状态
const tableData = ref([]) // 表格数据
const total = ref(0) // 总记录数
// 子组件引用
const appDialogRef = ref() // 新增/编辑弹窗
const secretDialogRef = ref() // 密钥弹窗
```
#### 3.1.4 核心方法
| 方法名 | 功能说明 | 调用时机 |
|-------|---------|---------|
| `getList` | 获取应用列表 | 页面加载、搜索、分页 |
| `handleQuery` | 搜索查询 | 点击搜索按钮 |
| `resetQuery` | 重置查询 | 点击重置按钮 |
| `handleAdd` | 新增应用 | 点击新增按钮 |
| `handleEdit` | 编辑应用 | 点击编辑按钮 |
| `handleResetSecret` | 重置密钥 | 点击重置密钥按钮 |
| `handleDelete` | 删除应用 | 点击删除按钮 |
#### 3.1.5 完整代码
```vue
搜索
重置
新增
{{ api.apiName }}
+{{ row.apis.length - 3 }}
未授权
{{ row.status === '0' ? '正常' : '停用' }}
编辑
重置密钥
删除
```
---
### 3.2 新增/编辑弹窗 (AppDialog.vue)
#### 3.2.1 组件功能
- 新增应用表单
- 编辑应用表单
- 接口授权勾选
- 表单验证
#### 3.2.2 表单布局
```
┌─────────────────────────────────────────────────────────────────┐
│ 新增应用 / 编辑应用 [X] │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 应用名称* [________________________] (必填,最大100字符) │
│ │
│ 应用编码 [保存后自动生成___________] (只读,系统生成) │
│ │
│ 应用描述 ┌────────────────────────┐ │
│ │ │ (选填,最大500字符) │
│ │ │ │
│ └────────────────────────┘ │
│ │
│ 联系人 [________________________] (选填) │
│ │
│ 联系电话 [________________________] (选填,手机号格式) │
│ │
│ 状态 ◉ 正常 ○ 停用 │
│ │
│ 接口授权 ┌────────────────────────────────────────────┐ │
│ │ [✓] 查询学生信息 [✓] 查询学校信息 │ │
│ │ [✓] 查询年级信息 [ ] 查询班级信息 │ │
│ │ [ ] 查询会员信息 [ ] 查询区域树 │ │
│ └────────────────────────────────────────────┘ │
│ │
├─────────────────────────────────────────────────────────────────┤
│ [取消] [确定] │
└─────────────────────────────────────────────────────────────────┘
```
#### 3.2.3 组件状态
```javascript
// 弹窗状态
const visible = ref(false)
const submitLoading = ref(false)
const isEdit = ref(false)
// 表单数据
const form = reactive({
appId: null,
appName: '',
appCode: '',
appDesc: '',
contactPerson: '',
contactPhone: '',
status: '0',
apiCodes: []
})
// 接口授权选项
const apiOptions = ref([])
```
#### 3.2.4 表单验证规则
```javascript
const rules = {
appName: [
{ required: true, message: '请输入应用名称', trigger: 'blur' },
{ max: 100, message: '应用名称不能超过100个字符', trigger: 'blur' }
],
contactPhone: [
{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号', trigger: 'blur' }
],
appDesc: [
{ max: 500, message: '应用描述不能超过500个字符', trigger: 'blur' }
]
}
```
#### 3.2.5 完整代码
```vue
正常
停用
{{ item.apiName }}
勾选后,该应用可调用对应的开放API接口
取消
确定
```
---
### 3.3 密钥展示弹窗 (SecretDialog.vue)
#### 3.3.1 组件功能
- 展示应用密钥
- 提供复制功能
- 安全提示
#### 3.3.2 弹窗布局
```
┌─────────────────────────────────────────────────────────────────┐
│ 应用密钥 [X] │
├─────────────────────────────────────────────────────────────────┤
│ │
│ ⚠️ 请妥善保管密钥,密钥重置后旧密钥将立即失效 │
│ │
├─────────────────────────────────────────────────────────────────┤
│ │
│ 应用名称 AI智慧平台 │
│ │
│ 应用编码 YY000001 │
│ │
│ 应用密钥 ┌────────────────────────────────┬──────┐ │
│ │ a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6 │ 复制 │ │
│ └────────────────────────────────┴──────┘ │
│ │
├─────────────────────────────────────────────────────────────────┤
│ [关闭] │
└─────────────────────────────────────────────────────────────────┘
```
#### 3.3.3 完整代码
```vue
{{ appInfo.appName }}
{{ appInfo.appCode }}
复制
关闭
```
---
## 4. API接口封装
### 4.1 接口定义 (api/application.js)
```javascript
/**
* 应用管理API接口
* @author pangu
*/
import request from '@/utils/request'
/**
* 获取应用列表(分页)
* @param {Object} params 查询参数
* @param {string} params.appName 应用名称
* @param {string} params.appCode 应用编码
* @param {string} params.status 状态:0正常,1停用
* @param {number} params.pageNum 页码
* @param {number} params.pageSize 每页条数
*/
export function listApplication(params) {
return request({
url: '/api/application/list',
method: 'get',
params
})
}
/**
* 获取应用详情
* @param {number} appId 应用ID
*/
export function getApplication(appId) {
return request({
url: `/api/application/${appId}`,
method: 'get'
})
}
/**
* 新增应用
* @param {Object} data 应用数据
*/
export function addApplication(data) {
return request({
url: '/api/application',
method: 'post',
data
})
}
/**
* 修改应用
* @param {Object} data 应用数据
*/
export function updateApplication(data) {
return request({
url: '/api/application',
method: 'put',
data
})
}
/**
* 删除应用
* @param {number} appId 应用ID
*/
export function deleteApplication(appId) {
return request({
url: `/api/application/${appId}`,
method: 'delete'
})
}
/**
* 重置应用密钥
* @param {number} appId 应用ID
*/
export function resetAppSecret(appId) {
return request({
url: `/api/application/resetSecret/${appId}`,
method: 'put'
})
}
/**
* 获取API接口列表(用于授权选择)
*/
export function getApiList() {
return request({
url: '/api/application/apiList',
method: 'get'
})
}
```
---
## 5. Mock数据 (mock/application.js)
> 开发阶段使用,后端接口完成后移除
```javascript
/**
* 应用管理Mock数据
* @author pangu
*/
import Mock from 'mockjs'
// API接口字典
const apiDict = [
{ apiCode: 'STUDENT_LIST', apiName: '查询学生信息', apiPath: '/open/student/list' },
{ apiCode: 'SCHOOL_LIST', apiName: '查询学校信息', apiPath: '/open/school/list' },
{ apiCode: 'GRADE_LIST', apiName: '查询年级信息', apiPath: '/open/grade/list' },
{ apiCode: 'CLASS_LIST', apiName: '查询班级信息', apiPath: '/open/class/list' },
{ apiCode: 'MEMBER_LIST', apiName: '查询会员信息', apiPath: '/open/member/list' },
{ apiCode: 'REGION_TREE', apiName: '查询区域树', apiPath: '/open/region/tree' }
]
// 生成应用编码
let appSeq = 1
const generateAppCode = () => `YY${String(appSeq++).padStart(6, '0')}`
// 生成32位密钥
const generateSecret = () => Mock.Random.string('abcdefghijklmnopqrstuvwxyz0123456789', 32)
// 模拟应用数据
let applicationList = [
{
appId: 1,
appCode: 'YY000001',
appName: 'AI智慧平台',
appSecret: generateSecret(),
appDesc: 'AI智慧教育平台',
contactPerson: '张经理',
contactPhone: '13812345678',
status: '0',
apis: [
{ apiCode: 'SCHOOL_LIST', apiName: '查询学校信息', apiPath: '/open/school/list' },
{ apiCode: 'GRADE_LIST', apiName: '查询年级信息', apiPath: '/open/grade/list' },
{ apiCode: 'CLASS_LIST', apiName: '查询班级信息', apiPath: '/open/class/list' }
],
createTime: '2026-01-01 10:00:00',
createBy: 'admin'
}
]
// 获取应用列表
Mock.mock(/\/api\/application\/list/, 'get', (options) => {
const url = new URL('http://localhost' + options.url)
const appName = url.searchParams.get('appName') || ''
const appCode = url.searchParams.get('appCode') || ''
const status = url.searchParams.get('status') || ''
const pageNum = parseInt(url.searchParams.get('pageNum')) || 1
const pageSize = parseInt(url.searchParams.get('pageSize')) || 10
let filtered = applicationList.filter(item => {
let match = true
if (appName) match = match && item.appName.includes(appName)
if (appCode) match = match && item.appCode.includes(appCode)
if (status) match = match && item.status === status
return match
})
const total = filtered.length
const start = (pageNum - 1) * pageSize
const rows = filtered.slice(start, start + pageSize)
return { code: 200, msg: '查询成功', total, rows }
})
// 获取应用详情
Mock.mock(/\/api\/application\/\d+$/, 'get', (options) => {
const appId = parseInt(options.url.match(/\/api\/application\/(\d+)/)[1])
const app = applicationList.find(item => item.appId === appId)
if (app) {
return { code: 200, msg: '查询成功', data: app }
}
return { code: 500, msg: '应用不存在' }
})
// 新增应用
Mock.mock('/api/application', 'post', (options) => {
const data = JSON.parse(options.body)
const newApp = {
appId: applicationList.length + 1,
appCode: generateAppCode(),
appSecret: generateSecret(),
appName: data.appName,
appDesc: data.appDesc,
contactPerson: data.contactPerson,
contactPhone: data.contactPhone,
status: data.status || '0',
apis: apiDict.filter(api => data.apiCodes?.includes(api.apiCode)),
createTime: Mock.Random.now('yyyy-MM-dd HH:mm:ss'),
createBy: 'admin'
}
applicationList.unshift(newApp)
return {
code: 200,
msg: '新增成功',
data: { appCode: newApp.appCode, appSecret: newApp.appSecret }
}
})
// 修改应用
Mock.mock('/api/application', 'put', (options) => {
const data = JSON.parse(options.body)
const index = applicationList.findIndex(item => item.appId === data.appId)
if (index !== -1) {
applicationList[index] = {
...applicationList[index],
appName: data.appName,
appDesc: data.appDesc,
contactPerson: data.contactPerson,
contactPhone: data.contactPhone,
status: data.status,
apis: apiDict.filter(api => data.apiCodes?.includes(api.apiCode))
}
return { code: 200, msg: '修改成功' }
}
return { code: 500, msg: '应用不存在' }
})
// 删除应用
Mock.mock(/\/api\/application\/\d+$/, 'delete', (options) => {
const appId = parseInt(options.url.match(/\/api\/application\/(\d+)/)[1])
const index = applicationList.findIndex(item => item.appId === appId)
if (index !== -1) {
applicationList.splice(index, 1)
return { code: 200, msg: '删除成功' }
}
return { code: 500, msg: '应用不存在' }
})
// 重置密钥
Mock.mock(/\/api\/application\/resetSecret\/\d+/, 'put', (options) => {
const appId = parseInt(options.url.match(/\/api\/application\/resetSecret\/(\d+)/)[1])
const app = applicationList.find(item => item.appId === appId)
if (app) {
const newSecret = generateSecret()
app.appSecret = newSecret
return { code: 200, msg: '重置成功', data: { appSecret: newSecret } }
}
return { code: 500, msg: '应用不存在' }
})
// 获取API接口列表
Mock.mock('/api/application/apiList', 'get', () => {
return { code: 200, msg: '查询成功', data: apiDict }
})
```
---
## 6. 路由配置
在 `router/index.js` 中添加路由:
```javascript
{
path: '/application',
component: Layout,
children: [
{
path: '',
name: 'Application',
component: () => import('@/views/application/index.vue'),
meta: { title: '应用管理', icon: 'app', roles: ['admin'] }
}
]
}
```
---
## 7. 开发规范
### 7.1 命名规范
| 类型 | 规范 | 示例 |
|------|------|------|
| 组件文件名 | 大驼峰 | `AppDialog.vue` |
| 组件名 | 大驼峰 | `AppDialog` |
| 变量名 | 小驼峰 | `tableData` |
| 常量名 | 大写下划线 | `API_BASE_URL` |
| 方法名 | 小驼峰,事件用handle前缀 | `handleQuery` |
| CSS类名 | 短横线分隔 | `app-container` |
### 7.2 代码规范
- 使用 `