外观
开发一个页面
1、运行后端接口
安装
vs
后端开发软件后,通过项目模板 新建项目MyCompanyName.MyModuleName
后,双击MyCompanyName.MyModuleName.sln
文件,设置MyCompanyName.MyProjectName.Host
为启动项目, 选择调试-开始执行(不调试) 或者 Ctrl + F5 直接编译运行项目在
MyCompanyName.MyModuleName\src\MyCompanyName.MyModuleName.Host
目录打开cmd
输入以下命令回车后运行项目
dotnet run MyCompanyName.MyProjectName.Host
运行成功后自动打开接口文档界面
2、安装&运行前端项目
通过 VSCode 配置 后,运行vscode
打开ui\zhontai.ui.admin.vue3
项目,按住 Ctrol + ` 或者 选择查看-终端运行终端
输入以下命令 或者 直接点击 NPM 脚本 inistall:pkg 安装 npm 包
npm run install:pkg
输入以下命令 或者 直接点击 NPM 脚本 dev 运行项目
npm run dev
运行成功后会自动跳转到登录界面
3、生成前端接口
- 打开
gen/gen-api.js
,配置接口输出目录和swagger.json地址
js
const apis = [
// {
// output: path.resolve(projectPath, './src/api/admin'),
// url: 'http://localhost:8000/admin/swagger/admin/swagger.json',
// enumUrl: 'http://localhost:8000/api/admin/api/get-enums',
// },
{
output: path.resolve(projectPath, 'src/api/app'),
url: 'http://localhost:8000/app/swagger/app/swagger.json',
},
]
- 输入以下命令 或者 直接点击 NPM 脚本 gen:api 生成前端接口
npm run gen:api
4、复制和重命名功能菜单
登录界面输入用户名 admin,密码 123asd 回车登录进去后查看相似的功能菜单
这里复制租户管理界面用于模块开发
访问平台管理-权限管理-权限管理菜单,查看租户管理视图地址为
admin/tenant/index
,对应项目里的视图地址为views/admin/tenant/index
- 在
views
目录下新建app
目录,复制admin/tenant
目录到app
中,将tenant
重命名为module
- 删除
module/components
目录下的多余组件只保留tenant-form.vue
表单组件,并重命名为module-form.vue
- 打开
module-form.vue
表单组件,将admin
替换为app
、Admin
替换为App
,将tenant
替换为module
、Tenant
替换为Module
,修复异常,删除冗余代码 - 剩下eventBus.emit('refreshModule')异常提示,打开
types/mitt.d.ts
在MittType
中增加refreshModule
module/components/module-form.vue
ts
<template>
<div>
<el-dialog
v-model="state.showDialog"
destroy-on-close
:title="title"
draggable
:close-on-click-modal="false"
:close-on-press-escape="false"
width="769px"
>
<el-form ref="formRef" :model="form" size="default" label-width="80px">
<el-row :gutter="35">
<el-col :xs="24" :sm="24" :md="24" :lg="24" :xl="24">
<el-form-item label="名称" prop="name" :rules="[{ required: true, message: '请输入名称', trigger: ['blur', 'change'] }]">
<el-input v-model="form.name" autocomplete="off" />
</el-form-item>
</el-col>
</el-row>
</el-form>
<template #footer>
<span class="dialog-footer">
<el-button @click="onCancel" size="default">取 消</el-button>
<el-button type="primary" @click="onSure" size="default" :loading="state.sureLoading">确 定</el-button>
</span>
</template>
</el-dialog>
</div>
</template>
<script lang="ts" setup name="app/module/form">
import { reactive, toRefs, getCurrentInstance, ref, computed } from 'vue'
import { ModuleAddInput, ModuleUpdateInput } from '/@/api/app/data-contracts'
import { ModuleApi } from '/@/api/app/Module'
import eventBus from '/@/utils/mitt'
defineProps({
title: {
type: String,
default: '',
},
})
const { proxy } = getCurrentInstance() as any
const formRef = ref()
const state = reactive({
showDialog: false,
sureLoading: false,
form: {} as ModuleAddInput & ModuleUpdateInput,
})
const { form } = toRefs(state)
// 打开对话框
const open = async (row: any = {}) => {
if (row.id > 0) {
const res = await new ModuleApi().get({ id: row.id }, { loading: true }).catch(() => {
proxy.$modal.closeLoading()
})
if (res?.success) {
state.form = res.data as ModuleAddInput & ModuleUpdateInput
}
} else {
state.form = {} as ModuleAddInput & ModuleUpdateInput
}
state.showDialog = true
}
// 取消
const onCancel = () => {
state.showDialog = false
}
// 确定
const onSure = () => {
formRef.value.validate(async (valid: boolean) => {
if (!valid) return
state.sureLoading = true
let res = {} as any
if (state.form.id != undefined && state.form.id > 0) {
res = await new ModuleApi().update(state.form, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
} else {
res = await new ModuleApi().add(state.form, { showSuccessMessage: true }).catch(() => {
state.sureLoading = false
})
}
state.sureLoading = false
if (res?.success) {
eventBus.emit('refreshModule')
state.showDialog = false
}
})
}
defineExpose({
open,
})
</script>
- 打开
index.vue
视图组件,将admin
替换为app
、Admin
替换为App
,将tenant
替换为module
、Tenant
替换为Module
,租户替换为模块,修复异常,删除冗余代码
module/index.vue
ts
<template>
<div class="my-layout">
<el-card class="mt8" shadow="never" :body-style="{ paddingBottom: '0' }">
<el-form :inline="true" @submit.stop.prevent>
<el-form-item label="模块名称">
<el-input v-model="state.filter.name" placeholder="模块名称" @keyup.enter="onQuery" />
</el-form-item>
<el-form-item>
<el-button type="primary" icon="ele-Search" @click="onQuery"> 查询 </el-button>
<el-button v-auth="'api:app:module:add'" type="primary" icon="ele-Plus" @click="onAdd"> 新增 </el-button>
</el-form-item>
</el-form>
</el-card>
<el-card class="my-fill mt8" shadow="never">
<el-table v-loading="state.loading" :data="state.data" row-key="id" height="'100%'" style="width: 100%; height: 100%">
<el-table-column prop="name" label="模块名称" min-width="120" show-overflow-tooltip />
<el-table-column label="操作" width="140" header-align="center" align="center" fixed="right">
<template #default="{ row }">
<el-button v-auth="'api:app:module:update'" icon="ele-EditPen" size="small" text type="primary" @click="onEdit(row)">编辑</el-button>
<my-dropdown-more v-auths="['api:app:module:delete']">
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item v-if="auth('api:app:module:delete')" @click="onDelete(row)">删除模块</el-dropdown-item>
</el-dropdown-menu>
</template>
</my-dropdown-more>
</template>
</el-table-column>
</el-table>
<div class="my-flex my-flex-end" style="margin-top: 20px">
<el-pagination
v-model:currentPage="state.pageInput.currentPage"
v-model:page-size="state.pageInput.pageSize"
:total="state.total"
:page-sizes="[10, 20, 50, 100]"
small
background
@size-change="onSizeChange"
@current-change="onCurrentChange"
layout="total, sizes, prev, pager, next, jumper"
/>
</div>
</el-card>
<module-form ref="moduleFormRef" :title="state.moduleFormTitle"></module-form>
</div>
</template>
<script lang="ts" setup name="app/module">
import { ref, reactive, onMounted, getCurrentInstance, onBeforeMount, defineAsyncComponent } from 'vue'
import { PageInputModuleGetPageInput, ModuleGetPageOutput } from '/@/api/app/data-contracts'
import { ModuleApi } from '/@/api/app/Module'
import eventBus from '/@/utils/mitt'
import { auth } from '/@/utils/authFunction'
// 引入组件
const ModuleForm = defineAsyncComponent(() => import('./components/module-form.vue'))
const MyDropdownMore = defineAsyncComponent(() => import('/@/components/my-dropdown-more/index.vue'))
const { proxy } = getCurrentInstance() as any
const moduleFormRef = ref()
const state = reactive({
loading: false,
moduleFormTitle: '',
total: 0,
filter: {
name: '',
},
pageInput: {
currentPage: 1,
pageSize: 20,
} as PageInputModuleGetPageInput,
data: [] as Array<ModuleGetPageOutput>,
})
onMounted(() => {
onQuery()
eventBus.off('refreshModule')
eventBus.on('refreshModule', async () => {
onQuery()
})
})
onBeforeMount(() => {
eventBus.off('refreshModule')
})
const onQuery = async () => {
state.loading = true
state.pageInput.filter = state.filter
const res = await new ModuleApi().getPage(state.pageInput).catch(() => {
state.loading = false
})
state.data = res?.data?.list ?? []
state.total = res?.data?.total ?? 0
state.loading = false
}
const onAdd = () => {
state.moduleFormTitle = '新增模块'
moduleFormRef.value.open()
}
const onEdit = (row: ModuleGetPageOutput) => {
state.moduleFormTitle = '编辑模块'
moduleFormRef.value.open(row)
}
const onDelete = (row: ModuleGetPageOutput) => {
proxy.$modal
.confirmDelete(`确定要删除【${row.name}】?`)
.then(async () => {
await new ModuleApi().delete({ id: row.id }, { loading: true, showSuccessMessage: true })
onQuery()
})
.catch(() => {})
}
const onSizeChange = (val: number) => {
state.pageInput.currentPage = 1
state.pageInput.pageSize = val
onQuery()
}
const onCurrentChange = (val: number) => {
state.pageInput.currentPage = val
onQuery()
}
</script>
5、配置功能菜单
- 打开平台管理-系统管理-视图管理,添加应用管理分组,在应用管理下添加模块管理视图
- 打开平台管理-权限管理-权限管理,添加应用管理分组,在应用管理下添加模块管理菜单
F5刷新页面后访问应用管理-模块管理,如图所示
6、配置菜单操作权限点
- 打开平台管理-系统管理-接口管理,点击同步按钮同步接口数据
- 打开平台管理-权限管理-权限管理,在应用管理-模块管理下新增权限点
新增【修改权限点】时API接口需要关联查询模块和修改模块2个接口
新增【删除权限点】时API接口建议使用软删除接口,采用逻辑删除业务数据
7、F5刷新界面访问菜单
F5刷新界面后访问模块管理菜单,可以看到新增、编辑和删除操作,接下来继续完善和开发其它业务操作