Skip to content

开发一个页面

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替换为appAdmin替换为App,将tenant替换为moduleTenant替换为Module,修复异常,删除冗余代码
  • 剩下eventBus.emit('refreshModule')异常提示,打开types/mitt.d.tsMittType中增加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替换为appAdmin替换为App,将tenant替换为moduleTenant替换为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接口需要关联查询模块接口和修改模块接口

新增删除权限点时API接口建议使用软删除接口,采用逻辑删除业务数据

7、F5刷新界面访问功能菜单

F5刷新界面后访问模块管理菜单,可以看到新增、编辑和删除操作,接下来继续完善和开发其它业务操作