项目介绍
🔥🔥项目是基于Thinkphp6 (opens new window)、Vue3 (opens new window)、TypeScript (opens new window)、Vite (opens new window)、Element Plus (opens new window)、Pinia (opens new window)等的后台管理系统,详细介绍请查看首页 (opens new window),数据库使用Mysql (opens new window),并支持国际化i18n (opens new window)。此项目永久免费开源,无需授权即可商业使用。
快速了解
源码分析
管理员界面
组件构成
组件文件:
layouts/backend/index.vue
模板:
<template>
<component :is="config.layout.layoutMode"></component>
</template>
config 是由pinia管理的Store对象,其中定义了layout 属性,layout 的layoutMode 属性,决定显示模式。
// 后台布局方式,可选值<Default|Classic|Streamline>
layoutMode: 'Default',
function setLayoutMode(data: string) {
layout.layoutMode = data
// 切换布局时,如果是为默认配色方案,对菜单激活背景色重新赋值
if (data == 'Classic' && layout.headerBarBackground == '#ffffff' && layout.headerBarBackground == '#ffffff') {
layout.headerBarTabActiveBackground = '#f5f5f5'
} else if (data == 'Default' && layout.headerBarBackground == '#ffffff' && layout.headerBarTabActiveBackground == '#f5f5f5') {
layout.headerBarTabActiveBackground = '#ffffff'
}
}
- 三个模式对应
layouts/backend/container
三个组件布局;界面大小变化时会切换显示模式。
动态路由加载
/router/static.ts
中只定义了静态路由,管理界面首页菜单中的菜单项的路由保存在数据库中,需要动态加载。
在index.vue
中的onMounted
生命中期内完成。
const init = () => {
index().then((res) => {
console.log('layout/backend axios res @ ', res.data)
siteConfig.$state = res.data.siteConfig
terminal.changePort(res.data.terminal.install_service_port)
terminal.changePackageManager(res.data.terminal.npm_package_manager)
adminInfo.super = res.data.adminInfo.super
if (res.data.menus) {
let menuRule = handleAdminRoute(res.data.menus)
// 更新stores中的路由菜单数据
navTabs.setTabsViewRoutes(menuRule)
// 预跳转到上次路径
if (route.query && route.query.url && route.query.url != adminBaseRoute.path) {
// 检查路径是否有权限
let menuPaths = getMenuPaths(menuRule)
if (menuPaths.indexOf(route.query.url as string) !== -1) {
let query = JSON.parse(route.query.query as string)
router.push({ path: route.query.url as string, query: Object.keys(query).length ? query : {} })
return
}
}
// 跳转到第一个菜单
let firstRoute = getFirstRoute(navTabs.state.tabsViewRoutes)
if (firstRoute) routePush('', {}, firstRoute.path)
}
})
}
子路由显示分析
选择default
模式布局进行分析
模板:
<template>
<el-container class="layout-container">
<Aside />
<el-container class="content-wrapper">
<Header />
<Main />
</el-container>
</el-container>
<CloseFullScreen v-if="navTabs.state.tabFullScreen" />
</template>
dashboard 新增目录
表格API
管理界面由不同表格构成,
index.vue 是默认界面,显示所有数据;popupForm.vue是弹框,信息修改组件
在index.vue中为表格绑定api:
// export const authAdmin = '/index.php/admin/auth.admin/'
import { authAdmin } from '/@/api/controllerUrls'
const baTable = new baTableClass(
new baTableApi(authAdmin),
baTableApi 生成对应api下增删查改路径:
export class baTableApi {
private controllerUrl
public actionUrl
constructor(controllerUrl: string) {
this.controllerUrl = controllerUrl
this.actionUrl = new Map([
['index', controllerUrl + 'index'],
['add', controllerUrl + 'add'],
['edit', controllerUrl + 'edit'],
['del', controllerUrl + 'del'],
['sortable', controllerUrl + 'sortable'],
])
}
index(filter: anyObj = {}): ApiPromise<TableDefaultData> {
return createAxios({
url: this.actionUrl.get('index'),
method: 'get',
params: filter,
}) as ApiPromise
}
edit(params: anyObj) {
return createAxios({
url: this.actionUrl.get('edit'),
method: 'get',
params: params,
})
}
del(ids: string[]) {
return createAxios(
{
url: this.actionUrl.get('del'),
method: 'DELETE',
data: {
ids: ids,
},
},
{
showSuccessMessage: true,
}
)
}
postData(action: string, data: anyObj) {
if (!this.actionUrl.has(action)) {
throw new Error('action 不存在!')
}
return createAxios(
{
url: this.actionUrl.get(action),
method: 'post',
data: data,
},
{
showSuccessMessage: true,
}
)
}
sortableApi(id: number, targetId: number) {
return createAxios({
url: this.actionUrl.get('sortable'),
method: 'post',
data: {
id: id,
targetId: targetId,
},
})
}
}
baTable类自动完成事件绑定
// 构造函数传入baTableApi 对象实例
constructor(api: baTableApi, table: BaTable, form: BaTableForm = {}, before: BaTableBefore = {}, after: BaTableAfter = {}) {
this.api = api
this.form = Object.assign(this.form, form)
this.table = Object.assign(this.table, table)
this.before = before
this.after = after
this.activate = true
const route = useRoute()
this.initComSearch(!_.isUndefined(route) ? route.query : {})
}
// 自动事件绑定
// 编辑
requestEdit = (id: string) => {
if (this.runBefore('requestEdit', { id }) === false) return
this.form.loading = true
this.form.items = {}
return this.api
.edit({
[this.table.pk!]: id,
})
.then((res) => {
this.form.loading = false
this.form.items = res.data.row
this.runAfter('requestEdit', { res })
})
}
自定义api
-
定义api入口, 例如
/index.php/admin/auth.menu/
-
创建增删查改管理对象 baTableApi
-
创建类
baTable
的对象示例,传入自定义baTableApi
对象