pangu-user-platform/docs/05-技术方案/基础数据模块_前端开发文档.md

1231 lines
30 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 盘古用户平台 - 基础数据模块前端开发文档
---
| 文档信息 | 内容 |
| -------- | --------------------------- |
| **文档版本** | V1.0 |
| **项目名称** | 盘古用户平台Pangu User Platform |
| **模块名称** | 基础数据管理-前端开发 |
| **编写团队** | pangu |
| **创建日期** | 2026-01-31 |
---
## 1. 开发环境要求
| 环境 | 版本要求 | 说明 |
| -------- | --------- | --------- |
| Node.js | ≥18.0.0 | 运行环境 |
| npm | ≥9.0.0 | 包管理器 |
| Vue | 3.5.x | 前端框架 |
| Vite | 7.3.x | 构建工具 |
| Element Plus | 2.13.x | UI组件库 |
---
## 2. 目录结构
```
pangu-ui/src/
├── api/ # API接口定义
│ ├── grade.js # 年级管理API
│ ├── class.js # 班级管理API
│ ├── subject.js # 学科管理API
│ └── region.js # 区域管理API
├── mock/ # Mock数据
│ ├── grade.js # 年级Mock
│ ├── class.js # 班级Mock
│ ├── subject.js # 学科Mock
│ └── region.js # 区域Mock
├── views/base/ # 页面组件
│ ├── grade/
│ │ └── index.vue # 年级管理页面
│ ├── class/
│ │ └── index.vue # 班级管理页面
│ ├── subject/
│ │ └── index.vue # 学科管理页面
│ └── region/
│ └── index.vue # 区域管理页面
└── router/
└── index.js # 路由配置
```
---
## 3. API接口定义
### 3.1 年级管理APIapi/grade.js
```javascript
/**
* 年级管理API
* @author pangu
*/
import request from '@/utils/request'
/**
* 获取年级分页列表
* @param {Object} params - 查询参数
* @param {string} params.gradeName - 年级名称(模糊查询)
* @param {string} params.status - 状态0正常 1停用
* @param {number} params.pageNum - 页码
* @param {number} params.pageSize - 每页条数
*/
export function getGradeList(params) {
return request({
url: '/api/grade/list',
method: 'get',
params
})
}
/**
* 获取年级选项列表(下拉选择用)
* @returns {Promise} 返回启用状态的年级列表
*/
export function getGradeOptions() {
return request({
url: '/api/grade/options',
method: 'get'
})
}
/**
* 获取年级详情
* @param {number} gradeId - 年级ID
*/
export function getGradeDetail(gradeId) {
return request({
url: `/api/grade/${gradeId}`,
method: 'get'
})
}
/**
* 新增年级
* @param {Object} data - 年级数据
* @param {string} data.gradeName - 年级名称(必填)
* @param {number} data.orderNum - 排序号
* @param {string} data.status - 状态
*/
export function addGrade(data) {
return request({
url: '/api/grade',
method: 'post',
data
})
}
/**
* 修改年级
* @param {Object} data - 年级数据含gradeId
*/
export function updateGrade(data) {
return request({
url: '/api/grade',
method: 'put',
data
})
}
/**
* 删除年级
* @param {number} gradeId - 年级ID
*/
export function deleteGrade(gradeId) {
return request({
url: `/api/grade/${gradeId}`,
method: 'delete'
})
}
```
### 3.2 班级管理APIapi/class.js
```javascript
/**
* 班级管理API
* @author pangu
*/
import request from '@/utils/request'
/**
* 获取班级分页列表
*/
export function getClassList(params) {
return request({
url: '/api/class/list',
method: 'get',
params
})
}
/**
* 获取班级选项列表
*/
export function getClassOptions() {
return request({
url: '/api/class/options',
method: 'get'
})
}
/**
* 获取班级详情
*/
export function getClassDetail(classId) {
return request({
url: `/api/class/${classId}`,
method: 'get'
})
}
/**
* 新增班级
*/
export function addClass(data) {
return request({
url: '/api/class',
method: 'post',
data
})
}
/**
* 修改班级
*/
export function updateClass(data) {
return request({
url: '/api/class',
method: 'put',
data
})
}
/**
* 删除班级
*/
export function deleteClass(classId) {
return request({
url: `/api/class/${classId}`,
method: 'delete'
})
}
```
### 3.3 学科管理APIapi/subject.js
```javascript
/**
* 学科管理API
* @author pangu
*/
import request from '@/utils/request'
/**
* 获取学科分页列表
*/
export function getSubjectList(params) {
return request({
url: '/api/subject/list',
method: 'get',
params
})
}
/**
* 获取学科选项列表
*/
export function getSubjectOptions() {
return request({
url: '/api/subject/options',
method: 'get'
})
}
/**
* 获取学科详情
*/
export function getSubjectDetail(subjectId) {
return request({
url: `/api/subject/${subjectId}`,
method: 'get'
})
}
/**
* 新增学科
*/
export function addSubject(data) {
return request({
url: '/api/subject',
method: 'post',
data
})
}
/**
* 修改学科
*/
export function updateSubject(data) {
return request({
url: '/api/subject',
method: 'put',
data
})
}
/**
* 删除学科
*/
export function deleteSubject(subjectId) {
return request({
url: `/api/subject/${subjectId}`,
method: 'delete'
})
}
```
### 3.4 区域管理APIapi/region.js
```javascript
/**
* 区域管理API
* @author pangu
*/
import request from '@/utils/request'
/**
* 获取区域树
* @returns {Promise} 返回树形结构的区域数据
*/
export function getRegionTree() {
return request({
url: '/api/region/tree',
method: 'get'
})
}
/**
* 获取区域详情
*/
export function getRegionDetail(regionId) {
return request({
url: `/api/region/${regionId}`,
method: 'get'
})
}
/**
* 新增区域
* @param {Object} data - 区域数据
* @param {number} data.parentId - 父区域ID0为顶级
* @param {string} data.regionName - 区域名称
*/
export function addRegion(data) {
return request({
url: '/api/region',
method: 'post',
data
})
}
/**
* 修改区域
*/
export function updateRegion(data) {
return request({
url: '/api/region',
method: 'put',
data
})
}
/**
* 删除区域
*/
export function deleteRegion(regionId) {
return request({
url: `/api/region/${regionId}`,
method: 'delete'
})
}
```
---
## 4. 页面组件开发
### 4.1 年级管理页面views/base/grade/index.vue
```vue
<template>
<div class="app-container">
<!-- 搜索区域 -->
<el-card shadow="never" class="search-wrapper">
<el-form :model="queryParams" :inline="true">
<el-form-item label="年级名称">
<el-input
v-model="queryParams.gradeName"
placeholder="请输入年级名称"
clearable
style="width: 200px"
@keyup.enter="handleQuery"
/>
</el-form-item>
<el-form-item label="状态">
<el-select
v-model="queryParams.status"
placeholder="全部"
clearable
style="width: 100px"
>
<el-option label="正常" value="0" />
<el-option label="停用" value="1" />
</el-select>
</el-form-item>
<el-form-item>
<el-button type="primary" :icon="Search" @click="handleQuery">搜索</el-button>
<el-button :icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-card>
<!-- 表格区域 -->
<el-card shadow="never" style="margin-top: 12px">
<el-row :gutter="10" style="margin-bottom: 12px">
<el-col :span="1.5">
<el-button type="primary" :icon="Plus" @click="handleAdd">新增</el-button>
</el-col>
</el-row>
<el-table
v-loading="loading"
:data="tableData"
border
stripe
:header-cell-style="{ background: '#f5f7fa', color: '#606266' }"
style="width: 100%"
>
<el-table-column prop="gradeName" label="年级名称" min-width="150" />
<el-table-column prop="gradeCode" label="年级编码" width="120" />
<el-table-column prop="orderNum" label="排序" width="80" align="center" />
<el-table-column prop="status" label="状态" width="80" align="center">
<template #default="{ row }">
<el-tag :type="row.status === '0' ? 'success' : 'danger'">
{{ row.status === '0' ? '正常' : '停用' }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间" width="160" />
<el-table-column label="操作" width="150" fixed="right" align="center">
<template #default="{ row }">
<el-button type="primary" link :icon="Edit" @click="handleEdit(row)">编辑</el-button>
<el-button type="danger" link :icon="Delete" @click="handleDelete(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
<!-- 分页 -->
<el-pagination
v-model:current-page="queryParams.pageNum"
v-model:page-size="queryParams.pageSize"
:page-sizes="[10, 20, 50, 100]"
:total="total"
layout="total, sizes, prev, pager, next, jumper"
style="margin-top: 16px; justify-content: flex-end"
@size-change="getList"
@current-change="getList"
/>
</el-card>
<!-- 新增/编辑弹窗 -->
<el-dialog
v-model="dialogVisible"
:title="dialogTitle"
width="500px"
:close-on-click-modal="false"
destroy-on-close
>
<el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
<el-form-item label="年级名称" prop="gradeName">
<el-input v-model="form.gradeName" placeholder="请输入年级名称" maxlength="50" />
</el-form-item>
<el-form-item label="年级编码">
<el-input v-model="form.gradeCode" placeholder="自动生成" disabled />
</el-form-item>
<el-form-item label="排序" prop="orderNum">
<el-input-number v-model="form.orderNum" :min="0" :max="999" />
</el-form-item>
<el-form-item label="状态" prop="status">
<el-switch
v-model="form.status"
active-value="0"
inactive-value="1"
active-text="正常"
inactive-text="停用"
/>
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" :loading="submitLoading" @click="handleSubmit">确定</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup>
/**
* 年级管理页面
* @author pangu
*/
import { Delete, Edit, Plus, Refresh, Search } from '@element-plus/icons-vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { onMounted, ref } from 'vue'
import { getGradeList, addGrade, updateGrade, deleteGrade } from '@/api/grade'
// 状态变量
const loading = ref(false)
const submitLoading = ref(false)
const tableData = ref([])
const total = ref(0)
// 查询参数
const queryParams = ref({
pageNum: 1,
pageSize: 10,
gradeName: '',
status: ''
})
// 弹窗相关
const dialogVisible = ref(false)
const dialogTitle = ref('')
const formRef = ref()
const form = ref({
gradeId: null,
gradeName: '',
gradeCode: '',
orderNum: 0,
status: '0'
})
// 表单校验规则
const rules = {
gradeName: [
{ required: true, message: '请输入年级名称', trigger: 'blur' },
{ max: 50, message: '年级名称长度不能超过50个字符', trigger: 'blur' }
]
}
/**
* 获取年级列表
*/
const getList = async () => {
loading.value = true
try {
const res = await getGradeList(queryParams.value)
if (res.code === 200) {
tableData.value = res.rows
total.value = res.total
}
} finally {
loading.value = false
}
}
/**
* 搜索
*/
const handleQuery = () => {
queryParams.value.pageNum = 1
getList()
}
/**
* 重置搜索
*/
const resetQuery = () => {
queryParams.value = {
pageNum: 1,
pageSize: 10,
gradeName: '',
status: ''
}
getList()
}
/**
* 新增年级
*/
const handleAdd = () => {
dialogTitle.value = '新增年级'
form.value = {
gradeId: null,
gradeName: '',
gradeCode: '(自动生成)',
orderNum: tableData.value.length + 1,
status: '0'
}
dialogVisible.value = true
}
/**
* 编辑年级
*/
const handleEdit = (row) => {
dialogTitle.value = '编辑年级'
form.value = { ...row }
dialogVisible.value = true
}
/**
* 提交表单
*/
const handleSubmit = async () => {
await formRef.value?.validate()
submitLoading.value = true
try {
const isEdit = !!form.value.gradeId
const res = isEdit
? await updateGrade(form.value)
: await addGrade(form.value)
if (res.code === 200) {
ElMessage.success(isEdit ? '修改成功' : '新增成功')
dialogVisible.value = false
getList()
} else {
ElMessage.error(res.msg || '操作失败')
}
} finally {
submitLoading.value = false
}
}
/**
* 删除年级
*/
const handleDelete = (row) => {
ElMessageBox.confirm(
`确定要删除年级"${row.gradeName}"吗?删除后不可恢复!`,
'删除确认',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
).then(async () => {
const res = await deleteGrade(row.gradeId)
if (res.code === 200) {
ElMessage.success('删除成功')
getList()
} else {
// 被引用时显示后端返回的提示
ElMessage.error(res.msg || '删除失败')
}
}).catch(() => {
// 用户取消删除
})
}
// 页面加载时获取数据
onMounted(() => {
getList()
})
</script>
<style scoped>
.app-container {
padding: 16px;
}
.search-wrapper {
margin-bottom: 0;
}
</style>
```
### 4.2 区域管理页面views/base/region/index.vue
```vue
<template>
<div class="app-container">
<!-- 操作区域 -->
<el-card shadow="never">
<el-row :gutter="10">
<el-col :span="1.5">
<el-button type="primary" :icon="Plus" @click="handleAdd">新增</el-button>
</el-col>
<el-col :span="1.5">
<el-button :icon="Sort" @click="toggleExpandAll">
{{ isExpandAll ? '折叠全部' : '展开全部' }}
</el-button>
</el-col>
</el-row>
</el-card>
<!-- 树形表格 -->
<el-card shadow="never" style="margin-top: 12px">
<el-table
v-if="refreshTable"
v-loading="loading"
:data="treeData"
row-key="regionId"
border
:default-expand-all="isExpandAll"
:tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
:header-cell-style="{ background: '#f5f7fa', color: '#606266' }"
style="width: 100%"
>
<el-table-column prop="regionName" label="区域名称" min-width="200" />
<el-table-column prop="regionCode" label="区域编码" width="150" />
<el-table-column prop="level" label="层级" width="80" align="center">
<template #default="{ row }">
<el-tag :type="getLevelType(row.level)">
{{ getLevelText(row.level) }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="orderNum" label="排序" width="80" align="center" />
<el-table-column prop="createTime" label="创建时间" width="160" />
<el-table-column label="操作" width="200" fixed="right" align="center">
<template #default="{ row }">
<!-- 只有省、市级可以新增下级 -->
<el-button
v-if="row.level < 3"
type="primary"
link
:icon="Plus"
@click="handleAddChild(row)"
>
新增下级
</el-button>
<el-button type="primary" link :icon="Edit" @click="handleEdit(row)">编辑</el-button>
<el-button type="danger" link :icon="Delete" @click="handleDelete(row)">删除</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
<!-- 新增/编辑弹窗 -->
<el-dialog
v-model="dialogVisible"
:title="dialogTitle"
width="500px"
:close-on-click-modal="false"
destroy-on-close
>
<el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
<el-form-item label="上级区域" v-if="form.parentId !== 0">
<el-input v-model="form.parentName" disabled />
</el-form-item>
<el-form-item label="区域名称" prop="regionName">
<el-input v-model="form.regionName" placeholder="请输入区域名称" maxlength="100" />
</el-form-item>
<el-form-item label="区域编码" v-if="form.regionId">
<el-input v-model="form.regionCode" disabled />
</el-form-item>
<el-form-item label="排序" prop="orderNum">
<el-input-number v-model="form.orderNum" :min="0" :max="999" />
</el-form-item>
</el-form>
<template #footer>
<el-button @click="dialogVisible = false">取消</el-button>
<el-button type="primary" :loading="submitLoading" @click="handleSubmit">确定</el-button>
</template>
</el-dialog>
</div>
</template>
<script setup>
/**
* 区域管理页面
* @author pangu
*/
import { Delete, Edit, Plus, Sort } from '@element-plus/icons-vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { nextTick, onMounted, ref } from 'vue'
import { getRegionTree, addRegion, updateRegion, deleteRegion } from '@/api/region'
// 状态变量
const loading = ref(false)
const submitLoading = ref(false)
const treeData = ref([])
const isExpandAll = ref(false)
const refreshTable = ref(true)
// 弹窗相关
const dialogVisible = ref(false)
const dialogTitle = ref('')
const formRef = ref()
const form = ref({
regionId: null,
parentId: 0,
parentName: '',
regionName: '',
regionCode: '',
orderNum: 0
})
// 表单校验规则
const rules = {
regionName: [
{ required: true, message: '请输入区域名称', trigger: 'blur' },
{ max: 100, message: '区域名称长度不能超过100个字符', trigger: 'blur' }
]
}
/**
* 获取区域树
*/
const getTree = async () => {
loading.value = true
try {
const res = await getRegionTree()
if (res.code === 200) {
treeData.value = res.data
}
} finally {
loading.value = false
}
}
/**
* 展开/折叠全部
*/
const toggleExpandAll = () => {
refreshTable.value = false
isExpandAll.value = !isExpandAll.value
nextTick(() => {
refreshTable.value = true
})
}
/**
* 获取层级文本
*/
const getLevelText = (level) => {
const map = { 1: '省', 2: '市', 3: '区/县' }
return map[level] || '未知'
}
/**
* 获取层级标签类型
*/
const getLevelType = (level) => {
const map = { 1: 'primary', 2: 'success', 3: 'info' }
return map[level] || 'info'
}
/**
* 新增顶级区域
*/
const handleAdd = () => {
dialogTitle.value = '新增区域'
form.value = {
regionId: null,
parentId: 0,
parentName: '',
regionName: '',
regionCode: '',
orderNum: 0
}
dialogVisible.value = true
}
/**
* 新增下级区域
*/
const handleAddChild = (row) => {
dialogTitle.value = '新增下级区域'
form.value = {
regionId: null,
parentId: row.regionId,
parentName: row.regionName,
regionName: '',
regionCode: '',
orderNum: 0
}
dialogVisible.value = true
}
/**
* 编辑区域
*/
const handleEdit = (row) => {
dialogTitle.value = '编辑区域'
form.value = {
regionId: row.regionId,
parentId: row.parentId,
parentName: findParentName(row.parentId),
regionName: row.regionName,
regionCode: row.regionCode,
orderNum: row.orderNum
}
dialogVisible.value = true
}
/**
* 查找父区域名称
*/
const findParentName = (parentId) => {
if (parentId === 0) return ''
const findNode = (nodes, id) => {
for (const node of nodes) {
if (node.regionId === id) return node.regionName
if (node.children) {
const found = findNode(node.children, id)
if (found) return found
}
}
return ''
}
return findNode(treeData.value, parentId)
}
/**
* 提交表单
*/
const handleSubmit = async () => {
await formRef.value?.validate()
submitLoading.value = true
try {
const isEdit = !!form.value.regionId
const res = isEdit
? await updateRegion(form.value)
: await addRegion(form.value)
if (res.code === 200) {
ElMessage.success(isEdit ? '修改成功' : '新增成功')
dialogVisible.value = false
getTree()
} else {
ElMessage.error(res.msg || '操作失败')
}
} finally {
submitLoading.value = false
}
}
/**
* 删除区域
*/
const handleDelete = (row) => {
// 前端先检查是否有子级
if (row.children && row.children.length > 0) {
ElMessage.warning('存在下级区域,不能删除')
return
}
ElMessageBox.confirm(
`确定要删除区域"${row.regionName}"吗?`,
'删除确认',
{
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning'
}
).then(async () => {
const res = await deleteRegion(row.regionId)
if (res.code === 200) {
ElMessage.success('删除成功')
getTree()
} else {
// 被学校/会员引用时显示后端提示
ElMessage.error(res.msg || '删除失败')
}
}).catch(() => {
// 用户取消删除
})
}
// 页面加载时获取数据
onMounted(() => {
getTree()
})
</script>
<style scoped>
.app-container {
padding: 16px;
}
</style>
```
---
## 5. Mock数据配置
### 5.1 年级Mockmock/grade.js
```javascript
/**
* 年级管理Mock数据
* @author pangu
*/
import Mock from 'mockjs'
// 年级预置数据
const gradeData = [
{ gradeId: 1, gradeName: '一年级', gradeCode: 'GRD001', orderNum: 1, status: '0', createTime: '2026-01-01 10:00:00' },
{ gradeId: 2, gradeName: '二年级', gradeCode: 'GRD002', orderNum: 2, status: '0', createTime: '2026-01-01 10:00:00' },
{ gradeId: 3, gradeName: '三年级', gradeCode: 'GRD003', orderNum: 3, status: '0', createTime: '2026-01-01 10:00:00' },
{ gradeId: 4, gradeName: '四年级', gradeCode: 'GRD004', orderNum: 4, status: '0', createTime: '2026-01-01 10:00:00' },
{ gradeId: 5, gradeName: '五年级', gradeCode: 'GRD005', orderNum: 5, status: '0', createTime: '2026-01-01 10:00:00' },
{ gradeId: 6, gradeName: '六年级', gradeCode: 'GRD006', orderNum: 6, status: '0', createTime: '2026-01-01 10:00:00' },
{ gradeId: 7, gradeName: '七年级', gradeCode: 'GRD007', orderNum: 7, status: '0', createTime: '2026-01-01 10:00:00' },
{ gradeId: 8, gradeName: '八年级', gradeCode: 'GRD008', orderNum: 8, status: '0', createTime: '2026-01-01 10:00:00' },
{ gradeId: 9, gradeName: '九年级', gradeCode: 'GRD009', orderNum: 9, status: '0', createTime: '2026-01-01 10:00:00' },
{ gradeId: 10, gradeName: '高一', gradeCode: 'GRD010', orderNum: 10, status: '0', createTime: '2026-01-01 10:00:00' },
{ gradeId: 11, gradeName: '高二', gradeCode: 'GRD011', orderNum: 11, status: '0', createTime: '2026-01-01 10:00:00' },
{ gradeId: 12, gradeName: '高三', gradeCode: 'GRD012', orderNum: 12, status: '0', createTime: '2026-01-01 10:00:00' }
]
// 获取年级列表
Mock.mock(/\/api\/grade\/list/, 'get', (options) => {
const url = new URL('http://localhost' + options.url)
const gradeName = url.searchParams.get('gradeName') || ''
const status = url.searchParams.get('status')
const pageNum = parseInt(url.searchParams.get('pageNum')) || 1
const pageSize = parseInt(url.searchParams.get('pageSize')) || 10
let list = gradeData.filter(item => {
let match = true
if (gradeName) {
match = match && item.gradeName.includes(gradeName)
}
if (status !== null && status !== '') {
match = match && item.status === status
}
return match
})
const total = list.length
const start = (pageNum - 1) * pageSize
const rows = list.slice(start, start + pageSize)
return {
code: 200,
msg: '查询成功',
total,
rows
}
})
// 获取年级选项
Mock.mock(/\/api\/grade\/options/, 'get', () => {
return {
code: 200,
msg: '查询成功',
data: gradeData.filter(item => item.status === '0').map(item => ({
gradeId: item.gradeId,
gradeName: item.gradeName,
gradeCode: item.gradeCode
}))
}
})
// 新增年级
Mock.mock('/api/grade', 'post', () => {
return { code: 200, msg: '新增成功' }
})
// 修改年级
Mock.mock('/api/grade', 'put', () => {
return { code: 200, msg: '修改成功' }
})
// 删除年级
Mock.mock(/\/api\/grade\/\d+/, 'delete', () => {
// 模拟被引用时的删除失败
// return { code: 500, msg: '该年级已被学校使用,不能删除' }
return { code: 200, msg: '删除成功' }
})
```
### 5.2 区域Mockmock/region.js
```javascript
/**
* 区域管理Mock数据
* @author pangu
*/
import Mock from 'mockjs'
// 区域树形数据
const regionTree = [
{
regionId: 1,
parentId: 0,
regionName: '湖北',
regionCode: 'REG01',
level: 1,
orderNum: 1,
createTime: '2026-01-01 10:00:00',
children: [
{
regionId: 11,
parentId: 1,
regionName: '武汉',
regionCode: 'REG0101',
level: 2,
orderNum: 1,
createTime: '2026-01-01 10:00:00',
children: [
{ regionId: 111, parentId: 11, regionName: '武昌区', regionCode: 'REG010101', level: 3, orderNum: 1, createTime: '2026-01-01 10:00:00' },
{ regionId: 112, parentId: 11, regionName: '汉口区', regionCode: 'REG010102', level: 3, orderNum: 2, createTime: '2026-01-01 10:00:00' },
{ regionId: 113, parentId: 11, regionName: '汉阳区', regionCode: 'REG010103', level: 3, orderNum: 3, createTime: '2026-01-01 10:00:00' },
{ regionId: 114, parentId: 11, regionName: '江夏区', regionCode: 'REG010104', level: 3, orderNum: 4, createTime: '2026-01-01 10:00:00' },
{ regionId: 115, parentId: 11, regionName: '新洲区', regionCode: 'REG010105', level: 3, orderNum: 5, createTime: '2026-01-01 10:00:00' },
{ regionId: 116, parentId: 11, regionName: '黄陂区', regionCode: 'REG010106', level: 3, orderNum: 6, createTime: '2026-01-01 10:00:00' }
]
},
{
regionId: 12,
parentId: 1,
regionName: '黄冈',
regionCode: 'REG0102',
level: 2,
orderNum: 2,
createTime: '2026-01-01 10:00:00',
children: [
{ regionId: 121, parentId: 12, regionName: '黄州区', regionCode: 'REG010201', level: 3, orderNum: 1, createTime: '2026-01-01 10:00:00' },
{ regionId: 122, parentId: 12, regionName: '红安县', regionCode: 'REG010202', level: 3, orderNum: 2, createTime: '2026-01-01 10:00:00' },
{ regionId: 123, parentId: 12, regionName: '麻城市', regionCode: 'REG010203', level: 3, orderNum: 3, createTime: '2026-01-01 10:00:00' }
]
}
]
},
{ regionId: 2, parentId: 0, regionName: '北京', regionCode: 'REG02', level: 1, orderNum: 2, createTime: '2026-01-01 10:00:00', children: [] },
{ regionId: 3, parentId: 0, regionName: '香港', regionCode: 'REG03', level: 1, orderNum: 3, createTime: '2026-01-01 10:00:00', children: [] },
{ regionId: 4, parentId: 0, regionName: '吉宁', regionCode: 'REG04', level: 1, orderNum: 4, createTime: '2026-01-01 10:00:00', children: [] }
]
// 获取区域树
Mock.mock('/api/region/tree', 'get', () => {
return {
code: 200,
msg: '查询成功',
data: regionTree
}
})
// 新增区域
Mock.mock('/api/region', 'post', () => {
return { code: 200, msg: '新增成功' }
})
// 修改区域
Mock.mock('/api/region', 'put', () => {
return { code: 200, msg: '修改成功' }
})
// 删除区域
Mock.mock(/\/api\/region\/\d+/, 'delete', () => {
return { code: 200, msg: '删除成功' }
})
```
---
## 6. 路由配置
```javascript
// router/index.js
import { createRouter, createWebHistory } from 'vue-router'
import Layout from '@/layout/index.vue'
const routes = [
// ... 其他路由
{
path: '/base',
component: Layout,
redirect: '/base/grade',
name: 'Base',
meta: { title: '基础数据', icon: 'Setting' },
children: [
{
path: 'grade',
name: 'Grade',
component: () => import('@/views/base/grade/index.vue'),
meta: { title: '年级管理' }
},
{
path: 'class',
name: 'Class',
component: () => import('@/views/base/class/index.vue'),
meta: { title: '班级管理' }
},
{
path: 'subject',
name: 'Subject',
component: () => import('@/views/base/subject/index.vue'),
meta: { title: '学科管理' }
},
{
path: 'region',
name: 'Region',
component: () => import('@/views/base/region/index.vue'),
meta: { title: '区域管理' }
}
]
}
]
const router = createRouter({
history: createWebHistory(),
routes
})
export default router
```
---
## 7. 开发检查清单
### 7.1 年级管理
- [ ] API接口封装api/grade.js
- [ ] Mock数据配置mock/grade.js
- [ ] 页面组件开发views/base/grade/index.vue
- [ ] 列表查询功能
- [ ] 分页功能
- [ ] 新增功能
- [ ] 编辑功能
- [ ] 删除功能(含删除确认)
- [ ] 表单校验
### 7.2 班级管理
- [ ] API接口封装
- [ ] Mock数据配置
- [ ] 页面组件开发
- [ ] CRUD功能
### 7.3 学科管理
- [ ] API接口封装
- [ ] Mock数据配置
- [ ] 页面组件开发
- [ ] CRUD功能
### 7.4 区域管理
- [ ] API接口封装
- [ ] Mock数据配置
- [ ] 页面组件开发(树形表格)
- [ ] 展开/折叠全部功能
- [ ] 新增下级功能
- [ ] 删除前子级检查
### 7.5 路由配置
- [ ] 基础数据菜单配置
- [ ] 四个子页面路由配置
---
*文档结束*