Commit 7cbd6cfb68b9230d2be9ba90871d5cf9bf0d5741
1 parent
42452049
vue3:【menu】
1. 添加 TODO 修改建议 2. 增加注释 3. 调整一些 import、const 的顺序
Showing
2 changed files
with
84 additions
and
47 deletions
yudao-ui-admin-vue3/src/api/system/menu/index.ts
| 1 | import request from '@/config/axios' | 1 | import request from '@/config/axios' |
| 2 | import type { MenuVO } from './types' | 2 | import type { MenuVO } from './types' |
| 3 | 3 | ||
| 4 | -// 查询菜单(精简)列表 | 4 | +// 查询菜单(精简)列表 |
| 5 | export const listSimpleMenusApi = () => { | 5 | export const listSimpleMenusApi = () => { |
| 6 | return request.get({ url: '/system/menu/list-all-simple' }) | 6 | return request.get({ url: '/system/menu/list-all-simple' }) |
| 7 | } | 7 | } |
| 8 | + | ||
| 8 | // 查询菜单列表 | 9 | // 查询菜单列表 |
| 9 | export const getMenuListApi = (params) => { | 10 | export const getMenuListApi = (params) => { |
| 10 | return request.get({ url: '/system/menu/list', params }) | 11 | return request.get({ url: '/system/menu/list', params }) |
yudao-ui-admin-vue3/src/views/system/menu/index.vue
| 1 | <template> | 1 | <template> |
| 2 | <ContentWrap> | 2 | <ContentWrap> |
| 3 | + <!-- 搜索工作栏 --> | ||
| 3 | <el-form :model="queryParams" ref="queryForm" :inline="true"> | 4 | <el-form :model="queryParams" ref="queryForm" :inline="true"> |
| 4 | <el-form-item label="菜单名称" prop="name"> | 5 | <el-form-item label="菜单名称" prop="name"> |
| 5 | <el-input v-model="queryParams.name" placeholder="请输入菜单名称" /> | 6 | <el-input v-model="queryParams.name" placeholder="请输入菜单名称" /> |
| @@ -15,17 +16,20 @@ | @@ -15,17 +16,20 @@ | ||
| 15 | </el-select> | 16 | </el-select> |
| 16 | </el-form-item> | 17 | </el-form-item> |
| 17 | <el-form-item> | 18 | <el-form-item> |
| 19 | + <!-- 操作:搜索 --> | ||
| 18 | <XButton | 20 | <XButton |
| 19 | type="primary" | 21 | type="primary" |
| 20 | preIcon="ep:search" | 22 | preIcon="ep:search" |
| 21 | :title="t('common.query')" | 23 | :title="t('common.query')" |
| 22 | @click="handleQuery()" | 24 | @click="handleQuery()" |
| 23 | /> | 25 | /> |
| 26 | + <!-- 操作:重置 --> | ||
| 24 | <XButton preIcon="ep:refresh-right" :title="t('common.reset')" @click="resetQuery()" /> | 27 | <XButton preIcon="ep:refresh-right" :title="t('common.reset')" @click="resetQuery()" /> |
| 25 | </el-form-item> | 28 | </el-form-item> |
| 26 | </el-form> | 29 | </el-form> |
| 27 | <vxe-toolbar> | 30 | <vxe-toolbar> |
| 28 | <template #buttons> | 31 | <template #buttons> |
| 32 | + <!-- 操作:新增 --> | ||
| 29 | <XButton | 33 | <XButton |
| 30 | type="primary" | 34 | type="primary" |
| 31 | preIcon="ep:zoom-in" | 35 | preIcon="ep:zoom-in" |
| @@ -37,6 +41,8 @@ | @@ -37,6 +41,8 @@ | ||
| 37 | <XButton title="关闭所有" @click="xTable?.clearTreeExpand()" /> | 41 | <XButton title="关闭所有" @click="xTable?.clearTreeExpand()" /> |
| 38 | </template> | 42 | </template> |
| 39 | </vxe-toolbar> | 43 | </vxe-toolbar> |
| 44 | + <!-- 列表 --> | ||
| 45 | + <!-- TODO 星语:是不是也搞成 grid 会好点,后续代码就统一走 grid 风格? --> | ||
| 40 | <vxe-table | 46 | <vxe-table |
| 41 | show-overflow | 47 | show-overflow |
| 42 | keep-source | 48 | keep-source |
| @@ -73,12 +79,14 @@ | @@ -73,12 +79,14 @@ | ||
| 73 | <vxe-column title="创建时间" field="createTime" formatter="formatDate" /> | 79 | <vxe-column title="创建时间" field="createTime" formatter="formatDate" /> |
| 74 | <vxe-column title="操作" width="200"> | 80 | <vxe-column title="操作" width="200"> |
| 75 | <template #default="{ row }"> | 81 | <template #default="{ row }"> |
| 82 | + <!-- 操作:修改 --> | ||
| 76 | <XTextButton | 83 | <XTextButton |
| 77 | preIcon="ep:edit" | 84 | preIcon="ep:edit" |
| 78 | :title="t('action.edit')" | 85 | :title="t('action.edit')" |
| 79 | v-hasPermi="['system:menu:update']" | 86 | v-hasPermi="['system:menu:update']" |
| 80 | @click="handleUpdate(row)" | 87 | @click="handleUpdate(row)" |
| 81 | /> | 88 | /> |
| 89 | + <!-- 操作:删除 --> | ||
| 82 | <XTextButton | 90 | <XTextButton |
| 83 | preIcon="ep:delete" | 91 | preIcon="ep:delete" |
| 84 | :title="t('action.del')" | 92 | :title="t('action.del')" |
| @@ -89,6 +97,7 @@ | @@ -89,6 +97,7 @@ | ||
| 89 | </vxe-column> | 97 | </vxe-column> |
| 90 | </vxe-table> | 98 | </vxe-table> |
| 91 | </ContentWrap> | 99 | </ContentWrap> |
| 100 | + <!-- 添加或修改菜单对话框 --> | ||
| 92 | <XModal v-model="dialogVisible" id="menuModel" :title="dialogTitle"> | 101 | <XModal v-model="dialogVisible" id="menuModel" :title="dialogTitle"> |
| 93 | <template #default> | 102 | <template #default> |
| 94 | <!-- 对话框(添加 / 修改) --> | 103 | <!-- 对话框(添加 / 修改) --> |
| @@ -227,7 +236,7 @@ | @@ -227,7 +236,7 @@ | ||
| 227 | </el-form> | 236 | </el-form> |
| 228 | </template> | 237 | </template> |
| 229 | <template #footer> | 238 | <template #footer> |
| 230 | - <!-- 操作按钮 --> | 239 | + <!-- 按钮:保存 --> |
| 231 | <XButton | 240 | <XButton |
| 232 | v-if="['create', 'update'].includes(actionType)" | 241 | v-if="['create', 'update'].includes(actionType)" |
| 233 | type="primary" | 242 | type="primary" |
| @@ -235,22 +244,17 @@ | @@ -235,22 +244,17 @@ | ||
| 235 | @click="submitForm" | 244 | @click="submitForm" |
| 236 | :title="t('action.save')" | 245 | :title="t('action.save')" |
| 237 | /> | 246 | /> |
| 247 | + <!-- 按钮:关闭 --> | ||
| 238 | <XButton :loading="actionLoading" @click="dialogVisible = false" :title="t('dialog.close')" /> | 248 | <XButton :loading="actionLoading" @click="dialogVisible = false" :title="t('dialog.close')" /> |
| 239 | </template> | 249 | </template> |
| 240 | </XModal> | 250 | </XModal> |
| 241 | </template> | 251 | </template> |
| 242 | <script setup lang="ts"> | 252 | <script setup lang="ts"> |
| 243 | -import * as MenuApi from '@/api/system/menu' | ||
| 244 | -import { MenuVO } from '@/api/system/menu/types' | 253 | +// 全局相关的 import |
| 254 | +import { onMounted, reactive, ref } from 'vue' | ||
| 245 | import { useI18n } from '@/hooks/web/useI18n' | 255 | import { useI18n } from '@/hooks/web/useI18n' |
| 246 | import { useMessage } from '@/hooks/web/useMessage' | 256 | import { useMessage } from '@/hooks/web/useMessage' |
| 247 | -import { IconSelect } from '@/components/Icon' | ||
| 248 | -import { Tooltip } from '@/components/Tooltip' | ||
| 249 | -import { required } from '@/utils/formRules.js' | ||
| 250 | -import { onMounted, reactive, ref } from 'vue' | ||
| 251 | -import { VxeTableInstance } from 'vxe-table' | ||
| 252 | -import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' | ||
| 253 | -import { SystemMenuTypeEnum, CommonStatusEnum } from '@/utils/constants' | 257 | +// TODO @星语:是不是 'element-plus' 和 '@/components/Tooltip' 和 '@/components/Icon' 全局直接引入 |
| 254 | import { | 258 | import { |
| 255 | ElRow, | 259 | ElRow, |
| 256 | ElCol, | 260 | ElCol, |
| @@ -264,17 +268,29 @@ import { | @@ -264,17 +268,29 @@ import { | ||
| 264 | ElRadioGroup, | 268 | ElRadioGroup, |
| 265 | ElRadioButton | 269 | ElRadioButton |
| 266 | } from 'element-plus' | 270 | } from 'element-plus' |
| 271 | +import { Tooltip } from '@/components/Tooltip' | ||
| 272 | +import { IconSelect } from '@/components/Icon' | ||
| 273 | +import { VxeTableInstance } from 'vxe-table' | ||
| 274 | +// 业务相关的 import | ||
| 275 | +import * as MenuApi from '@/api/system/menu' | ||
| 276 | +import { MenuVO } from '@/api/system/menu/types' | ||
| 277 | +import { required } from '@/utils/formRules.js' | ||
| 278 | +import { DICT_TYPE, getIntDictOptions } from '@/utils/dict' | ||
| 279 | +import { SystemMenuTypeEnum, CommonStatusEnum } from '@/utils/constants' | ||
| 267 | import { handleTree } from '@/utils/tree' | 280 | import { handleTree } from '@/utils/tree' |
| 281 | + | ||
| 268 | const { t } = useI18n() // 国际化 | 282 | const { t } = useI18n() // 国际化 |
| 269 | -const message = useMessage() | 283 | +const message = useMessage() // 消息弹窗 |
| 284 | +// 列表相关的变量 | ||
| 270 | const xTable = ref<VxeTableInstance>() | 285 | const xTable = ref<VxeTableInstance>() |
| 271 | const tableLoading = ref(false) | 286 | const tableLoading = ref(false) |
| 272 | const tableData = ref() | 287 | const tableData = ref() |
| 273 | -const actionLoading = ref(false) // 遮罩层 | ||
| 274 | -const actionType = ref('') // 操作按钮的类型 | 288 | +// 弹窗相关的变量 |
| 275 | const dialogVisible = ref(false) // 是否显示弹出层 | 289 | const dialogVisible = ref(false) // 是否显示弹出层 |
| 276 | const dialogTitle = ref('edit') // 弹出层标题 | 290 | const dialogTitle = ref('edit') // 弹出层标题 |
| 277 | -const statusOption = ref() // 状态选项 | 291 | +const actionType = ref('') // 操作按钮的类型 |
| 292 | +const actionLoading = ref(false) // 遮罩层 | ||
| 293 | +// 新增和修改的表单值 | ||
| 278 | const menuForm = ref<MenuVO>({ | 294 | const menuForm = ref<MenuVO>({ |
| 279 | id: 0, | 295 | id: 0, |
| 280 | name: '', | 296 | name: '', |
| @@ -290,12 +306,24 @@ const menuForm = ref<MenuVO>({ | @@ -290,12 +306,24 @@ const menuForm = ref<MenuVO>({ | ||
| 290 | keepAlive: true, | 306 | keepAlive: true, |
| 291 | createTime: '' | 307 | createTime: '' |
| 292 | }) | 308 | }) |
| 309 | +// 新增和修改的表单校验 | ||
| 310 | +const rules = reactive({ | ||
| 311 | + name: [required], | ||
| 312 | + sort: [required], | ||
| 313 | + path: [required], | ||
| 314 | + status: [required] | ||
| 315 | +}) | ||
| 316 | + | ||
| 317 | +// ========== 下拉框[上级菜单] ========== | ||
| 318 | +// 下拉框[上级菜单]的配置项目 | ||
| 319 | +// TODO @星语:menuProps 貌似也可以抽到全局? | ||
| 293 | const menuProps = { | 320 | const menuProps = { |
| 294 | checkStrictly: true, | 321 | checkStrictly: true, |
| 295 | children: 'children', | 322 | children: 'children', |
| 296 | label: 'name', | 323 | label: 'name', |
| 297 | value: 'id' | 324 | value: 'id' |
| 298 | } | 325 | } |
| 326 | +// TODO @星语:Tree 可以是全局的 | ||
| 299 | interface Tree { | 327 | interface Tree { |
| 300 | id: number | 328 | id: number |
| 301 | name: string | 329 | name: string |
| @@ -309,6 +337,7 @@ const getTree = async () => { | @@ -309,6 +337,7 @@ const getTree = async () => { | ||
| 309 | menu.children = handleTree(res) | 337 | menu.children = handleTree(res) |
| 310 | menuOptions.value.push(menu) | 338 | menuOptions.value.push(menu) |
| 311 | } | 339 | } |
| 340 | + | ||
| 312 | // ========== 查询 ========== | 341 | // ========== 查询 ========== |
| 313 | const queryParams = reactive({ | 342 | const queryParams = reactive({ |
| 314 | name: null, | 343 | name: null, |
| @@ -316,58 +345,47 @@ const queryParams = reactive({ | @@ -316,58 +345,47 @@ const queryParams = reactive({ | ||
| 316 | }) | 345 | }) |
| 317 | const getList = async () => { | 346 | const getList = async () => { |
| 318 | tableLoading.value = true | 347 | tableLoading.value = true |
| 319 | - statusOption.value = getIntDictOptions(DICT_TYPE.COMMON_STATUS) | ||
| 320 | const res = await MenuApi.getMenuListApi(queryParams) | 348 | const res = await MenuApi.getMenuListApi(queryParams) |
| 321 | tableData.value = res | 349 | tableData.value = res |
| 322 | tableLoading.value = false | 350 | tableLoading.value = false |
| 323 | } | 351 | } |
| 352 | + | ||
| 353 | +// 查询操作 | ||
| 354 | +const handleQuery = async () => { | ||
| 355 | + await getList() | ||
| 356 | +} | ||
| 357 | + | ||
| 358 | +// 重置操作 | ||
| 359 | +const resetQuery = async () => { | ||
| 360 | + queryParams.name = null | ||
| 361 | + queryParams.status = null | ||
| 362 | + await getList() | ||
| 363 | +} | ||
| 364 | + | ||
| 365 | +// ========== 新增/修改 ========== | ||
| 366 | + | ||
| 324 | // 设置标题 | 367 | // 设置标题 |
| 325 | const setDialogTile = (type: string) => { | 368 | const setDialogTile = (type: string) => { |
| 326 | dialogTitle.value = t('action.' + type) | 369 | dialogTitle.value = t('action.' + type) |
| 327 | actionType.value = type | 370 | actionType.value = type |
| 328 | dialogVisible.value = true | 371 | dialogVisible.value = true |
| 329 | } | 372 | } |
| 373 | + | ||
| 330 | // 新建操作 | 374 | // 新建操作 |
| 331 | const handleCreate = () => { | 375 | const handleCreate = () => { |
| 332 | setDialogTile('create') | 376 | setDialogTile('create') |
| 377 | + // TODO @星语:重置表单 | ||
| 333 | } | 378 | } |
| 379 | + | ||
| 334 | // 修改操作 | 380 | // 修改操作 |
| 335 | const handleUpdate = async (row: MenuVO) => { | 381 | const handleUpdate = async (row: MenuVO) => { |
| 382 | + setDialogTile('update') | ||
| 336 | // 设置数据 | 383 | // 设置数据 |
| 337 | const res = await MenuApi.getMenuApi(row.id) | 384 | const res = await MenuApi.getMenuApi(row.id) |
| 338 | - console.log(res) | ||
| 339 | menuForm.value = res | 385 | menuForm.value = res |
| 340 | - setDialogTile('update') | ||
| 341 | -} | ||
| 342 | -// 删除操作 | ||
| 343 | -const handleDelete = async (row: MenuVO) => { | ||
| 344 | - message.confirm(t('common.delDataMessage'), t('common.confirmTitle')).then(async () => { | ||
| 345 | - await MenuApi.deleteMenuApi(row.id) | ||
| 346 | - message.success(t('common.delSuccess')) | ||
| 347 | - await getList() | ||
| 348 | - }) | ||
| 349 | -} | ||
| 350 | -// 表单校验 | ||
| 351 | -const rules = reactive({ | ||
| 352 | - name: [required], | ||
| 353 | - sort: [required], | ||
| 354 | - path: [required], | ||
| 355 | - status: [required] | ||
| 356 | -}) | ||
| 357 | -// 查询操作 | ||
| 358 | -const handleQuery = async () => { | ||
| 359 | - await getList() | ||
| 360 | -} | ||
| 361 | -// 重置操作 | ||
| 362 | -const resetQuery = async () => { | ||
| 363 | - queryParams.name = null | ||
| 364 | - queryParams.status = null | ||
| 365 | - await getList() | ||
| 366 | -} | ||
| 367 | -// 保存操作 | ||
| 368 | -const isExternal = (path: string) => { | ||
| 369 | - return /^(https?:|mailto:|tel:)/.test(path) | ||
| 370 | } | 386 | } |
| 387 | + | ||
| 388 | +// 提交新增/修改的表单 | ||
| 371 | const submitForm = async () => { | 389 | const submitForm = async () => { |
| 372 | actionLoading.value = true | 390 | actionLoading.value = true |
| 373 | // 提交请求 | 391 | // 提交请求 |
| @@ -400,8 +418,26 @@ const submitForm = async () => { | @@ -400,8 +418,26 @@ const submitForm = async () => { | ||
| 400 | actionLoading.value = false | 418 | actionLoading.value = false |
| 401 | } | 419 | } |
| 402 | } | 420 | } |
| 421 | + | ||
| 422 | +// 判断 path 是不是外部的 HTTP 等链接 | ||
| 423 | +const isExternal = (path: string) => { | ||
| 424 | + return /^(https?:|mailto:|tel:)/.test(path) | ||
| 425 | +} | ||
| 426 | + | ||
| 427 | +// ========== 删除 ========== | ||
| 428 | +// 删除操作 | ||
| 429 | +const handleDelete = async (row: MenuVO) => { | ||
| 430 | + message.confirm(t('common.delDataMessage'), t('common.confirmTitle')).then(async () => { | ||
| 431 | + await MenuApi.deleteMenuApi(row.id) | ||
| 432 | + message.success(t('common.delSuccess')) | ||
| 433 | + await getList() | ||
| 434 | + }) | ||
| 435 | +} | ||
| 436 | + | ||
| 437 | +// ========== 初始化 ========== | ||
| 403 | onMounted(async () => { | 438 | onMounted(async () => { |
| 404 | await getList() | 439 | await getList() |
| 440 | + // TODO @星语:这个告警解决下;是不是改成新增和修改点击的时候,去加载下好一点哈? | ||
| 405 | getTree() | 441 | getTree() |
| 406 | }) | 442 | }) |
| 407 | </script> | 443 | </script> |