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 | 1 | import request from '@/config/axios' |
| 2 | 2 | import type { MenuVO } from './types' |
| 3 | 3 | |
| 4 | -// 查询菜单(精简)列表 | |
| 4 | +// 查询菜单(精简)列表 | |
| 5 | 5 | export const listSimpleMenusApi = () => { |
| 6 | 6 | return request.get({ url: '/system/menu/list-all-simple' }) |
| 7 | 7 | } |
| 8 | + | |
| 8 | 9 | // 查询菜单列表 |
| 9 | 10 | export const getMenuListApi = (params) => { |
| 10 | 11 | return request.get({ url: '/system/menu/list', params }) | ... | ... |
yudao-ui-admin-vue3/src/views/system/menu/index.vue
| 1 | 1 | <template> |
| 2 | 2 | <ContentWrap> |
| 3 | + <!-- 搜索工作栏 --> | |
| 3 | 4 | <el-form :model="queryParams" ref="queryForm" :inline="true"> |
| 4 | 5 | <el-form-item label="菜单名称" prop="name"> |
| 5 | 6 | <el-input v-model="queryParams.name" placeholder="请输入菜单名称" /> |
| ... | ... | @@ -15,17 +16,20 @@ |
| 15 | 16 | </el-select> |
| 16 | 17 | </el-form-item> |
| 17 | 18 | <el-form-item> |
| 19 | + <!-- 操作:搜索 --> | |
| 18 | 20 | <XButton |
| 19 | 21 | type="primary" |
| 20 | 22 | preIcon="ep:search" |
| 21 | 23 | :title="t('common.query')" |
| 22 | 24 | @click="handleQuery()" |
| 23 | 25 | /> |
| 26 | + <!-- 操作:重置 --> | |
| 24 | 27 | <XButton preIcon="ep:refresh-right" :title="t('common.reset')" @click="resetQuery()" /> |
| 25 | 28 | </el-form-item> |
| 26 | 29 | </el-form> |
| 27 | 30 | <vxe-toolbar> |
| 28 | 31 | <template #buttons> |
| 32 | + <!-- 操作:新增 --> | |
| 29 | 33 | <XButton |
| 30 | 34 | type="primary" |
| 31 | 35 | preIcon="ep:zoom-in" |
| ... | ... | @@ -37,6 +41,8 @@ |
| 37 | 41 | <XButton title="关闭所有" @click="xTable?.clearTreeExpand()" /> |
| 38 | 42 | </template> |
| 39 | 43 | </vxe-toolbar> |
| 44 | + <!-- 列表 --> | |
| 45 | + <!-- TODO 星语:是不是也搞成 grid 会好点,后续代码就统一走 grid 风格? --> | |
| 40 | 46 | <vxe-table |
| 41 | 47 | show-overflow |
| 42 | 48 | keep-source |
| ... | ... | @@ -73,12 +79,14 @@ |
| 73 | 79 | <vxe-column title="创建时间" field="createTime" formatter="formatDate" /> |
| 74 | 80 | <vxe-column title="操作" width="200"> |
| 75 | 81 | <template #default="{ row }"> |
| 82 | + <!-- 操作:修改 --> | |
| 76 | 83 | <XTextButton |
| 77 | 84 | preIcon="ep:edit" |
| 78 | 85 | :title="t('action.edit')" |
| 79 | 86 | v-hasPermi="['system:menu:update']" |
| 80 | 87 | @click="handleUpdate(row)" |
| 81 | 88 | /> |
| 89 | + <!-- 操作:删除 --> | |
| 82 | 90 | <XTextButton |
| 83 | 91 | preIcon="ep:delete" |
| 84 | 92 | :title="t('action.del')" |
| ... | ... | @@ -89,6 +97,7 @@ |
| 89 | 97 | </vxe-column> |
| 90 | 98 | </vxe-table> |
| 91 | 99 | </ContentWrap> |
| 100 | + <!-- 添加或修改菜单对话框 --> | |
| 92 | 101 | <XModal v-model="dialogVisible" id="menuModel" :title="dialogTitle"> |
| 93 | 102 | <template #default> |
| 94 | 103 | <!-- 对话框(添加 / 修改) --> |
| ... | ... | @@ -227,7 +236,7 @@ |
| 227 | 236 | </el-form> |
| 228 | 237 | </template> |
| 229 | 238 | <template #footer> |
| 230 | - <!-- 操作按钮 --> | |
| 239 | + <!-- 按钮:保存 --> | |
| 231 | 240 | <XButton |
| 232 | 241 | v-if="['create', 'update'].includes(actionType)" |
| 233 | 242 | type="primary" |
| ... | ... | @@ -235,22 +244,17 @@ |
| 235 | 244 | @click="submitForm" |
| 236 | 245 | :title="t('action.save')" |
| 237 | 246 | /> |
| 247 | + <!-- 按钮:关闭 --> | |
| 238 | 248 | <XButton :loading="actionLoading" @click="dialogVisible = false" :title="t('dialog.close')" /> |
| 239 | 249 | </template> |
| 240 | 250 | </XModal> |
| 241 | 251 | </template> |
| 242 | 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 | 255 | import { useI18n } from '@/hooks/web/useI18n' |
| 246 | 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 | 258 | import { |
| 255 | 259 | ElRow, |
| 256 | 260 | ElCol, |
| ... | ... | @@ -264,17 +268,29 @@ import { |
| 264 | 268 | ElRadioGroup, |
| 265 | 269 | ElRadioButton |
| 266 | 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 | 280 | import { handleTree } from '@/utils/tree' |
| 281 | + | |
| 268 | 282 | const { t } = useI18n() // 国际化 |
| 269 | -const message = useMessage() | |
| 283 | +const message = useMessage() // 消息弹窗 | |
| 284 | +// 列表相关的变量 | |
| 270 | 285 | const xTable = ref<VxeTableInstance>() |
| 271 | 286 | const tableLoading = ref(false) |
| 272 | 287 | const tableData = ref() |
| 273 | -const actionLoading = ref(false) // 遮罩层 | |
| 274 | -const actionType = ref('') // 操作按钮的类型 | |
| 288 | +// 弹窗相关的变量 | |
| 275 | 289 | const dialogVisible = ref(false) // 是否显示弹出层 |
| 276 | 290 | const dialogTitle = ref('edit') // 弹出层标题 |
| 277 | -const statusOption = ref() // 状态选项 | |
| 291 | +const actionType = ref('') // 操作按钮的类型 | |
| 292 | +const actionLoading = ref(false) // 遮罩层 | |
| 293 | +// 新增和修改的表单值 | |
| 278 | 294 | const menuForm = ref<MenuVO>({ |
| 279 | 295 | id: 0, |
| 280 | 296 | name: '', |
| ... | ... | @@ -290,12 +306,24 @@ const menuForm = ref<MenuVO>({ |
| 290 | 306 | keepAlive: true, |
| 291 | 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 | 320 | const menuProps = { |
| 294 | 321 | checkStrictly: true, |
| 295 | 322 | children: 'children', |
| 296 | 323 | label: 'name', |
| 297 | 324 | value: 'id' |
| 298 | 325 | } |
| 326 | +// TODO @星语:Tree 可以是全局的 | |
| 299 | 327 | interface Tree { |
| 300 | 328 | id: number |
| 301 | 329 | name: string |
| ... | ... | @@ -309,6 +337,7 @@ const getTree = async () => { |
| 309 | 337 | menu.children = handleTree(res) |
| 310 | 338 | menuOptions.value.push(menu) |
| 311 | 339 | } |
| 340 | + | |
| 312 | 341 | // ========== 查询 ========== |
| 313 | 342 | const queryParams = reactive({ |
| 314 | 343 | name: null, |
| ... | ... | @@ -316,58 +345,47 @@ const queryParams = reactive({ |
| 316 | 345 | }) |
| 317 | 346 | const getList = async () => { |
| 318 | 347 | tableLoading.value = true |
| 319 | - statusOption.value = getIntDictOptions(DICT_TYPE.COMMON_STATUS) | |
| 320 | 348 | const res = await MenuApi.getMenuListApi(queryParams) |
| 321 | 349 | tableData.value = res |
| 322 | 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 | 368 | const setDialogTile = (type: string) => { |
| 326 | 369 | dialogTitle.value = t('action.' + type) |
| 327 | 370 | actionType.value = type |
| 328 | 371 | dialogVisible.value = true |
| 329 | 372 | } |
| 373 | + | |
| 330 | 374 | // 新建操作 |
| 331 | 375 | const handleCreate = () => { |
| 332 | 376 | setDialogTile('create') |
| 377 | + // TODO @星语:重置表单 | |
| 333 | 378 | } |
| 379 | + | |
| 334 | 380 | // 修改操作 |
| 335 | 381 | const handleUpdate = async (row: MenuVO) => { |
| 382 | + setDialogTile('update') | |
| 336 | 383 | // 设置数据 |
| 337 | 384 | const res = await MenuApi.getMenuApi(row.id) |
| 338 | - console.log(res) | |
| 339 | 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 | 389 | const submitForm = async () => { |
| 372 | 390 | actionLoading.value = true |
| 373 | 391 | // 提交请求 |
| ... | ... | @@ -400,8 +418,26 @@ const submitForm = async () => { |
| 400 | 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 | 438 | onMounted(async () => { |
| 404 | 439 | await getList() |
| 440 | + // TODO @星语:这个告警解决下;是不是改成新增和修改点击的时候,去加载下好一点哈? | |
| 405 | 441 | getTree() |
| 406 | 442 | }) |
| 407 | 443 | </script> | ... | ... |