| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662 |
- <script setup lang="ts" name="Menu">
- import apis from "@a"
- import message from "@@/utils/message"
- const { sys_normal_disable, sys_show_hide } = useDict("sys_normal_disable", "sys_show_hide")
- const tableRef = ref()
- const modalRef = ref()
- const iconShow = ref(false)
- const opts = reactive({
- columns: [
- { field: "menuId", name: "菜单ID", width: 100, isSort: true, visible: false },
- { field: "menuName", name: "菜单名称", visible: true },
- { field: "icon", name: "菜单图标", visible: true, width: 80 },
- { field: "path", name: "路由地址/按钮", visible: true, width: 120 },
- //{ field: "query", name: "路由参数", visible: true, width: 120 },
- { field: "component", name: "组件路径", visible: true },
- { field: "isFrame", name: "是否为外链", visible: true, width: 90 },
- { field: "isCache", name: "是否缓存", visible: true, width: 80 },
- { field: "menuType", name: "菜单类型", visible: true, width: 80 },
- { field: "visible", name: "菜单显示", visible: true, width: 80 },
- { field: "status", name: "菜单状态", visible: true, width: 80 },
- { field: "perms", name: "权限标识", visible: true },
- { field: "actions", name: `操作`, width: 150 }
- ] as any,
- queryParams: {
- menuName: undefined,
- visible: undefined,
- status: undefined
- },
- searchFormItems: [
- {
- field: "menuName",
- label: "菜单名称",
- class: "w-100",
- required: false,
- placeholder: "请输入菜单名称",
- listeners: {
- keyup: (e: KeyboardEvent) => {
- if (e.code == "Enter") {
- handleQuery()
- }
- }
- }
- },
- {
- field: "visible",
- label: "菜单显示",
- class: "w-100",
- required: false,
- component: "Dict",
- props: {
- placeholder: "请选择菜单显示",
- dictType: "sys_show_hide"
- }
- },
- {
- field: "status",
- label: "菜单状态",
- class: "w-100",
- required: false,
- component: "Dict",
- props: {
- placeholder: "请选择菜单状态",
- dictType: "sys_normal_disable"
- }
- }
- ] as any,
- permission: "system:menu",
- handleBtns: [
- {
- key: "handleUpdate",
- show: false
- },
- {
- key: "handleDelete",
- show: false
- }
- ],
- handleFuns: {
- handleCreate
- },
- customBtns: [],
- tableListFun: getTableData,
- getEntityFun: apis.system.menuApi.getMenu,
- deleteEntityFun: apis.system.menuApi.delMenu,
- modalTitle: "菜单",
- formRules: {
- menuName: [{ required: true, message: "菜单名称不能为空", trigger: "blur" }],
- orderNum: [{ required: true, message: "菜单顺序不能为空", trigger: "blur" }],
- path: [{ required: true, message: "路由地址不能为空", trigger: "blur" }]
- },
- labelWidth: "80px",
- emptyFormData: {
- parentId: 0,
- menuId: undefined,
- menuName: undefined,
- orderNum: undefined,
- path: undefined,
- component: undefined,
- query: undefined,
- isFrame: undefined,
- isCache: undefined,
- menuType: undefined,
- visible: "0",
- status: "0",
- perms: undefined,
- icon: undefined,
- btnClass: undefined,
- btnScript: undefined,
- remark: undefined
- }
- })
- const { queryParams, emptyFormData } = toRefs(opts)
- const form = ref<any>(emptyFormData.value)
- /** 搜索按钮操作 */
- function handleQuery(query?: any) {
- query = query || tableRef.value?.getQueryParams() || queryParams.value
- tableRef.value?.query(query)
- }
- /** 重置按钮操作 */
- function resetQuery() {
- //
- }
- const menuOptions = ref()
- const defaultBtnStyle = ref()
- function reset() {
- form.value = emptyFormData.value
- defaultBtnStyle.value = undefined
- getMenuTreeSelect()
- }
- /** 新增按钮操作 */
- function handleCreate(row: any) {
- reset()
- if (row != null && row.menuId) {
- form.value.parentId = row.menuId
- } else {
- form.value.parentId = 0
- }
- modalRef.value.changePrefixTitle("添加")
- modalRef.value.show()
- }
- /** 修改按钮操作 */
- function handleUpdate(row: any) {
- reset()
- if (row.menuId) {
- apis.system.menuApi.getMenu(row.menuId).then((res: any) => {
- form.value = res.data
- modalRef.value.changePrefixTitle("修改")
- modalRef.value.show()
- })
- }
- }
- function getMenuTreeSelect() {
- menuOptions.value = []
- apis.system.menuApi.listMenu({}).then((response) => {
- const menu: any = { menuId: 0, menuName: "主类目", children: [] }
- menu.children = handleTree(response.data, "menuId")
- menuOptions.value.push(menu)
- })
- }
- /** 提交按钮 */
- function submitForm() {
- if (form.value.menuId != null) {
- apis.system.menuApi.updateMenu(form.value).then(() => {
- message.msgSuccess("修改成功")
- handleQuery()
- })
- } else {
- apis.system.menuApi.addMenu(form.value).then(() => {
- message.msgSuccess("新增成功")
- handleQuery()
- })
- }
- }
- /** 删除按钮操作 */
- function handleDelete(row: any) {
- message
- .confirm('是否确认删除菜单为"' + row.menuName + '"的数据项?')
- .then(() => {
- return apis.system.menuApi.delMenu(row.menuId).then(() => {
- handleQuery()
- })
- })
- .catch(() => {
- //
- })
- }
- function defaultBtnChange(v: any) {
- switch (v) {
- case "A":
- form.value.btnClass = "btn btn-light-primary"
- form.value.btnScript = "handleCreate"
- break
- case "U":
- form.value.btnClass = "btn btn-light-success"
- form.value.btnScript = "handleUpdate@1"
- break
- case "D":
- form.value.btnClass = "btn btn-light-danger"
- form.value.btnScript = "handleDelete@0"
- break
- case "I":
- form.value.btnClass = "btn btn-light-warning"
- form.value.btnScript = "handleImport"
- break
- case "E":
- form.value.btnClass = "btn btn-light-info"
- form.value.btnScript = "handleExport"
- break
- case "H":
- form.value.btnClass = "btn btn-light"
- form.value.btnScript = "hide"
- break
- }
- }
- function getTableData(q: any) {
- return new Promise((resolve) => {
- apis.system.menuApi.listMenuByParentId(q).then((res) => {
- //res.data = handleTree(res.data, "menuId")
- resolve(res)
- })
- })
- }
- const showChooseIcon = ref(false)
- const iconSelectRef = ref()
- /** 展示下拉图标 */
- function showSelectIcon() {
- iconSelectRef.value?.reset()
- showChooseIcon.value = true
- }
- /** 选择图标 */
- function selected(name: string) {
- form.value.icon = name
- showChooseIcon.value = false
- }
- onMounted(() => {
- setTimeout(() => {
- iconShow.value = true
- }, 500)
- })
- </script>
- <template>
- <div>
- <VbDataTable
- ref="tableRef"
- :is-tree="true"
- :is-lazy="true"
- keyField="menuId"
- iconField="menuName"
- parentField="parentId"
- root-id="0"
- childrenField="children"
- leaf-field="leaf"
- :check-multiple="false"
- :has-checkbox="false"
- :no-page="true"
- :expand-depth="1"
- :interval-left="10"
- :show-search-bar="false"
- :show-right-search-btn="false"
- :show-right-column-btn="false"
- :handle-perm="opts.permission"
- :handle-btns="opts.handleBtns"
- :handle-funs="opts.handleFuns"
- :search-form-items="opts.searchFormItems"
- :columns="opts.columns"
- :custom-btns="opts.customBtns"
- :remote-fun="opts.tableListFun"
- v-model:query-params="queryParams"
- :reset-search-form-fun="resetQuery"
- :custom-search-fun="handleQuery">
- <template #icon="{ row }">
- <i :class="`fs-4 text-primary bi bi-${row.icon}`"></i>
- </template>
- <template #path="{ row }">
- <template v-if="row.menuType == 'F'">
- <template v-if="row.btnScript">
- <template v-if="row.btnClass">
- <div
- class="d-flex justify-content-center align-items-center w-100"
- style="transform: scale(0.7)">
- <span :class="row.btnClass">{{ row.btnScript }}</span>
- </div>
- </template>
- <template v-else>{{ row.btnScript }}</template>
- </template>
- <template v-else>{{ row.btnClass }}</template>
- </template>
- <template v-else>{{ row.path }}</template>
- </template>
- <template #component="{ row }">
- <template v-if="row.component">
- <vb-tooltip :content="row.component" :auto-hide="true"></vb-tooltip>
- </template>
- <span v-else class="text-muted">-</span>
- </template>
- <template #query="{ row }">
- <template v-if="row.query">
- <vb-tooltip :content="row.query" :auto-hide="true"></vb-tooltip>
- </template>
- <span v-else class="text-muted">-</span>
- </template>
- <template #perms="{ row }">
- <template v-if="row.perms">
- <vb-tooltip :content="row.perms" :auto-hide="true"></vb-tooltip>
- </template>
- <span v-else class="text-muted">-</span>
- </template>
- <template #menuType="{ row }">
- <vb-tag
- :data="[
- { label: '目录', value: 'M', type: 'primary' },
- { label: '菜单', value: 'C', type: 'success' },
- { label: '按钮', value: 'F', type: 'warning' }
- ]"
- :value="row.menuType"></vb-tag>
- </template>
- <template #isCache="{ row }">
- <vb-tag
- :data="[
- { label: '是', value: '0', type: 'primary' },
- { label: '否', value: '1', type: 'danger' }
- ]"
- :value="row.isCache"></vb-tag>
- </template>
- <template #isFrame="{ row }">
- <vb-tag
- :data="[
- { label: '是', value: '0', type: 'primary' },
- { label: '否', value: '1', type: 'danger' }
- ]"
- :value="row.isFrame"></vb-tag>
- </template>
- <template #visible="{ row }">
- <DictTag type="sys_show_hide" :value="row.visible"></DictTag>
- </template>
- <template #status="{ row }">
- <DictTag type="sys_normal_disable" :value="row.status"></DictTag>
- </template>
- <template #actions="{ row }">
- <vb-tooltip content="新增" placement="top">
- <el-button
- link
- type="primary"
- @click="handleCreate(row)"
- v-hasPermission="'system:menu:add'">
- <template #icon>
- <VbIcon icon-name="plus-square" icon-type="duotone" class="fs-3"></VbIcon>
- </template>
- </el-button>
- </vb-tooltip>
- <vb-tooltip content="修改" placement="top">
- <el-button
- link
- type="primary"
- @click="handleUpdate(row)"
- v-hasPermission="'system:menu:edit'">
- <template #icon>
- <VbIcon icon-name="notepad-edit" icon-type="duotone" class="fs-3"></VbIcon>
- </template>
- </el-button>
- </vb-tooltip>
- <vb-tooltip content="删除" placement="top">
- <el-button
- link
- type="primary"
- @click="handleDelete(row)"
- v-hasPermission="'system:menu:remove'">
- <template #icon>
- <VbIcon icon-name="trash-square" icon-type="duotone" class="fs-3"></VbIcon>
- </template>
- </el-button>
- </vb-tooltip>
- </template>
- </VbDataTable>
- <VbModal
- v-model:modal="modalRef"
- :title="opts.modalTitle"
- :form-data="form"
- :form-rules="opts.formRules"
- :label-width="opts.labelWidth"
- :hide-event="() => (showChooseIcon = false)"
- @confirm="submitForm"
- append-to-body>
- <template #form>
- <el-row>
- <el-col :span="24">
- <el-form-item label="上级菜单" prop="parentId">
- <el-tree-select
- v-model="form.parentId"
- class="w-100"
- :data="menuOptions"
- :props="{ value: 'menuId', label: 'menuName', children: 'children' }"
- value-key="menuId"
- placeholder="选择上级菜单"
- check-strictly />
- </el-form-item>
- </el-col>
- <el-col :span="24">
- <el-form-item label="菜单类型" prop="menuType">
- <el-radio-group v-model="form.menuType">
- <el-radio label="M">目录</el-radio>
- <el-radio label="C">菜单</el-radio>
- <el-radio label="F">按钮</el-radio>
- </el-radio-group>
- </el-form-item>
- </el-col>
- <el-col :span="24" v-if="iconShow">
- <!-- <el-form-item label="菜单图标" prop="icon">
- <el-popover
- placement="bottom-start"
- :width="600"
- v-model:visible="showChooseIcon"
- trigger="focus"
- @show="showSelectIcon">
- <template #reference>
- <el-input
- v-model="form.icon"
- placeholder="点击选择图标"
- @blur="showChooseIcon = true"
- @focus="showChooseIcon = true"
- readonly>
- <template #prefix>
- <i v-if="form.icon" :class="`text-primary bi bi-${form.icon}`"></i>
- <i v-else class="text-muted bi bi-search"></i>
- </template>
- </el-input>
- </template>
- <icon-select ref="iconSelectRef" @selected="selected" :active-icon="form.icon" />
- </el-popover>
- </el-form-item> -->
- <el-form-item label="菜单图标" prop="icon">
- <el-input v-model="form.icon" placeholder="请输入菜单图标" class="w-100" />
- </el-form-item>
- </el-col>
- <el-col :span="12">
- <el-form-item label="菜单名称" prop="menuName">
- <el-input v-model="form.menuName" placeholder="请输入菜单名称" class="w-100" />
- </el-form-item>
- </el-col>
- <el-col :span="12">
- <el-form-item label="显示顺序" prop="orderNum">
- <el-input-number v-model="form.orderNum" placeholder="请输入显示顺序" class="w-100" />
- </el-form-item>
- </el-col>
- <el-col :span="12" v-if="form.menuType != 'M'">
- <el-form-item label="权限标识" prop="perms">
- <template #label>
- <span>
- <vb-tooltip
- :width="320"
- content="控制器中定义的权限字符,如:@PreAuthorize(`@ss.hasPermission('system:user:list')`)"
- placement="top">
- <span class="bi bi-question-circle-fill text-muted"></span>
- </vb-tooltip>
- 权限字符
- </span>
- </template>
- <el-input v-model="form.perms" placeholder="请输入权限标识" class="w-100" />
- </el-form-item>
- </el-col>
- <el-col :span="24" v-if="form.menuType == 'F'">
- <el-form-item label="常用样式">
- <template #label>
- <span>
- <vb-tooltip
- :width="320"
- content="列举了几种常用样式脚本,方便快捷的设置按钮样式及按钮脚本"
- placement="top">
- <span class="bi bi-question-circle-fill text-muted"></span>
- </vb-tooltip>
- 常用样式
- </span>
- </template>
- <el-radio-group v-model="defaultBtnStyle" @change="defaultBtnChange">
- <el-radio label="A">
- <span class="btn btn-light-primary" style="transform: scale(0.7)">新增</span>
- </el-radio>
- <el-radio label="U">
- <span class="btn btn-light-success" style="transform: scale(0.7)">编辑</span>
- </el-radio>
- <el-radio label="D">
- <span class="btn btn-light-danger" style="transform: scale(0.7)">删除</span>
- </el-radio>
- <el-radio label="I">
- <span class="btn btn-light-warning" style="transform: scale(0.7)">导入</span>
- </el-radio>
- <el-radio label="E">
- <span class="btn btn-light-info" style="transform: scale(0.7)">导出</span>
- </el-radio>
- <el-radio label="H">
- <span class="btn btn-light" style="transform: scale(0.7)">隐藏</span>
- </el-radio>
- </el-radio-group>
- </el-form-item>
- </el-col>
- <el-col :span="12" v-if="form.menuType == 'F'">
- <el-form-item label="按钮样式" prop="btnClass">
- <template #label>
- <span>
- <vb-tooltip content="按钮在toolbar中的显示样式 class" placement="top">
- <span class="bi bi-question-circle-fill text-muted"></span>
- </vb-tooltip>
- 按钮样式
- </span>
- </template>
- <el-input v-model="form.btnClass" placeholder="请输入按钮样式" class="w-100" />
- </el-form-item>
- </el-col>
- <el-col :span="12" v-if="form.menuType == 'F'">
- <el-form-item label="按钮脚本" prop="btnScript">
- <template #label>
- <span>
- <vb-tooltip
- :width="320"
- content="点击按钮调用的方法名,前端需要向table组件内传此方法名为key的方法。[handleCreate]、[handleUpdate]、[handleDelete]、[handleExport]脚本在table组件内置了。注:设置为'hide'值会隐藏按钮。"
- placement="top">
- <span class="bi bi-question-circle-fill text-muted"></span>
- </vb-tooltip>
- 按钮脚本
- </span>
- </template>
- <el-input v-model="form.btnScript" placeholder="请输入按钮脚本" class="w-100" />
- </el-form-item>
- </el-col>
- <el-col :span="12" v-if="form.menuType != 'F'">
- <el-form-item label="路由地址" prop="path">
- <el-input v-model="form.path" placeholder="请输入路由地址" class="w-100" />
- </el-form-item>
- </el-col>
- <el-col :span="12" v-if="form.menuType != 'F'">
- <el-form-item label="是否为外链" prop="isFrame">
- <template #label>
- <span>
- <vb-tooltip content="选择是外链则路由地址需要以`http(s)://`开头" placement="top">
- <span class="bi bi-question-circle-fill text-muted"></span>
- </vb-tooltip>
- 是否外链
- </span>
- </template>
- <el-radio-group v-model="form.isFrame">
- <el-radio label="0">是</el-radio>
- <el-radio label="1">否</el-radio>
- </el-radio-group>
- </el-form-item>
- </el-col>
- <el-col :span="12" v-if="form.menuType == 'C'">
- <el-form-item label="组件路径" prop="component">
- <template #label>
- <span>
- <vb-tooltip
- :width="320"
- content="访问的组件路径,如:`system/user/index`,默认在`views`目录下"
- placement="top">
- <span class="bi bi-question-circle-fill text-muted"></span>
- </vb-tooltip>
- 组件路径
- </span>
- </template>
- <el-input v-model="form.component" placeholder="请输入组件路径" class="w-100" />
- </el-form-item>
- </el-col>
- <el-col :span="12" v-if="form.menuType == 'C'">
- <el-form-item>
- <el-input v-model="form.query" placeholder="请输入路由参数" maxlength="255" />
- <template #label>
- <span>
- <vb-tooltip
- content='访问路由的默认传递参数,如:`{"id": 1, "name": "ry"}`'
- placement="top">
- <span class="bi bi-question-circle-fill text-muted"></span>
- </vb-tooltip>
- 路由参数
- </span>
- </template>
- </el-form-item>
- </el-col>
- <el-col :span="12" v-if="form.menuType == 'C'">
- <el-form-item>
- <template #label>
- <span>
- <vb-tooltip
- content="选择是则会被`keep-alive`缓存,需要匹配组件的`name`和地址保持一致"
- placement="top">
- <span class="bi bi-question-circle-fill text-muted"></span>
- </vb-tooltip>
- 是否缓存
- </span>
- </template>
- <el-radio-group v-model="form.isCache">
- <el-radio label="0">缓存</el-radio>
- <el-radio label="1">不缓存</el-radio>
- </el-radio-group>
- </el-form-item>
- </el-col>
- <el-col :span="12" v-if="form.menuType != 'F'">
- <el-form-item>
- <template #label>
- <span>
- <vb-tooltip
- content="选择隐藏则路由将不会出现在侧边栏,但仍然可以访问"
- placement="top">
- <span class="bi bi-question-circle-fill text-muted"></span>
- </vb-tooltip>
- 显示状态
- </span>
- </template>
- <el-radio-group v-model="form.visible">
- <el-radio v-for="dict in sys_show_hide" :key="dict.value" :label="dict.value">
- {{ dict.label }}
- </el-radio>
- </el-radio-group>
- </el-form-item>
- </el-col>
- <el-col :span="12" v-if="form.menuType != 'F'">
- <el-form-item>
- <template #label>
- <span>
- <vb-tooltip
- content="选择停用则路由将不会出现在侧边栏,也不能被访问"
- placement="top">
- <span class="bi bi-question-circle-fill text-muted"></span>
- </vb-tooltip>
- 菜单状态
- </span>
- </template>
- <el-radio-group v-model="form.status">
- <el-radio v-for="dict in sys_normal_disable" :key="dict.value" :label="dict.value">
- {{ dict.label }}
- </el-radio>
- </el-radio-group>
- </el-form-item>
- </el-col>
- <el-col :span="24">
- <el-form-item label="备注" prop="remark">
- <el-input
- v-model="form.remark"
- type="textarea"
- placeholder="请输入内容"
- class="w-100" />
- </el-form-item>
- </el-col>
- </el-row>
- </template>
- </VbModal>
- </div>
- </template>
|