Procházet zdrojové kódy

feat:优化 system/menu 的实现代码

YunaiV před 4 měsíci
rodič
revize
020517c7c4

+ 7 - 0
src/pages-system/menu/components/breadcrumb.vue

@@ -80,6 +80,12 @@ function back(): boolean {
   return false
 }
 
+/** 重置面包屑 */
+function reset() {
+  breadcrumbs.value = []
+  currentParentId.value = 0
+}
+
 /** 监听外部 modelValue 变化,重置面包屑(用于外部重置场景) */
 watch(() => props.modelValue, (val) => {
   if (val === 0 && breadcrumbs.value.length > 0) {
@@ -90,6 +96,7 @@ watch(() => props.modelValue, (val) => {
 defineExpose({
   enter,
   back,
+  reset,
   breadcrumbs,
 })
 </script>

+ 117 - 0
src/pages-system/menu/components/search-form.vue

@@ -0,0 +1,117 @@
+<template>
+  <!-- 搜索框入口 -->
+  <wd-search
+    :placeholder="searchPlaceholder"
+    :hide-cancel="true"
+    disabled
+    @click="visible = true"
+  />
+
+  <!-- 搜索弹窗 -->
+  <wd-popup
+    v-model="visible"
+    position="top"
+    custom-style="border-radius: 0 0 24rpx 24rpx;"
+    safe-area-inset-top
+    @close="visible = false"
+  >
+    <view class="p-32rpx">
+      <view class="mb-24rpx text-32rpx text-[#333] font-semibold">
+        搜索菜单
+      </view>
+      <view class="mb-24rpx">
+        <view class="mb-12rpx text-28rpx text-[#666]">
+          菜单名称
+        </view>
+        <wd-input
+          v-model="formData.name"
+          placeholder="请输入菜单名称"
+          clearable
+        />
+      </view>
+      <view class="mb-24rpx">
+        <view class="mb-12rpx text-28rpx text-[#666]">
+          菜单状态
+        </view>
+        <wd-radio-group v-model="formData.status" shape="button">
+           <wd-radio v-for="dict in getIntDictOptions(DICT_TYPE.COMMON_STATUS)" :key="dict.value" :value="dict.value">
+            {{ dict.label }}
+          </wd-radio>
+        </wd-radio-group>
+      </view>
+      <view class="w-full flex justify-center gap-24rpx">
+        <wd-button class="flex-1" plain @click="handleReset">
+          重置
+        </wd-button>
+        <wd-button class="flex-1" type="primary" @click="handleSearch">
+          搜索
+        </wd-button>
+      </view>
+    </view>
+  </wd-popup>
+</template>
+
+<script lang="ts" setup>
+import { computed, reactive, ref, watch } from 'vue'
+import { DICT_TYPE } from '@/utils/constants'
+import { getIntDictOptions } from '@/hooks/useDict'
+
+/** 搜索表单数据 */
+export interface SearchFormData {
+  name?: string
+  status?: number
+}
+
+const props = defineProps<{
+  searchParams?: Partial<SearchFormData> // 初始搜索参数
+}>()
+
+const emit = defineEmits<{
+  'search': [data: SearchFormData]
+  'reset': []
+}>()
+
+const visible = ref(false)
+
+/** 搜索条件 placeholder 拼接 */
+const searchPlaceholder = computed(() => {
+  const conditions: string[] = []
+  if (props.searchParams?.name) {
+    conditions.push(`名称:${props.searchParams.name}`)
+  }
+  if (props.searchParams?.status !== undefined) {
+    const dict = getIntDictOptions(DICT_TYPE.COMMON_STATUS).find(d => d.value === props.searchParams?.status)
+    if (dict) {
+      conditions.push(`状态:${dict.label}`)
+    }
+  }
+  return conditions.length > 0 ? conditions.join(' | ') : '搜索菜单'
+})
+
+const formData = reactive<SearchFormData>({
+  name: undefined,
+  status: undefined,
+})
+
+/** 监听弹窗打开,同步外部参数 */
+watch(visible, (val) => {
+  if (val && props.searchParams) {
+    formData.name = props.searchParams.name
+    formData.status = props.searchParams.status
+  }
+})
+
+/** 搜索 */
+function handleSearch() {
+  visible.value = false
+  emit('search', { ...formData })
+}
+
+/** 重置 */
+function handleReset() {
+  formData.name = undefined
+  formData.status = undefined
+  visible.value = false
+  emit('reset')
+}
+</script>

+ 20 - 23
src/pages-system/menu/detail/index.vue

@@ -8,8 +8,8 @@
     />
 
     <!-- 详情内容 -->
-    <view class="p-24rpx pb-200rpx">
-      <wd-cell-group custom-class="cell-group" border>
+    <view>
+      <wd-cell-group border>
         <wd-cell title="菜单名称" :value="formData?.name || '-'" />
         <wd-cell title="菜单类型">
           <dict-tag :type="DICT_TYPE.SYSTEM_MENU_TYPE" :value="formData?.type" />
@@ -44,7 +44,7 @@
     </view>
 
     <!-- 底部操作按钮 -->
-    <view class="safe-area-inset-bottom fixed bottom-0 left-0 right-0 bg-white p-24rpx">
+    <view class="fixed bottom-0 left-0 right-0 bg-white p-24rpx">
       <view class="w-full flex gap-24rpx">
         <wd-button class="flex-1" type="warning" @click="handleEdit">
           编辑
@@ -62,11 +62,12 @@ import type { Menu } from '@/api/system/menu'
 import { onMounted, ref } from 'vue'
 import { useToast } from 'wot-design-uni'
 import { deleteMenu, getMenu, getSimpleMenuList } from '@/api/system/menu'
+import { navigateBackPlus } from '@/utils'
 import { DICT_TYPE, SystemMenuTypeEnum } from '@/utils/constants'
 import { formatDateTime } from '@/utils/date'
 
 const props = defineProps<{
-  id: number
+  id: number | any
 }>()
 
 definePage({
@@ -83,7 +84,7 @@ const parentMenuName = ref('-') // 上级菜单名称
 
 /** 返回上一页 */
 function handleBack() {
-  uni.navigateBack()
+  navigateBackPlus('/pages-system/menu/index')
 }
 
 /** 加载菜单详情 */
@@ -91,15 +92,20 @@ async function getDetail() {
   if (!props.id) {
     return
   }
-  formData.value = await getMenu(props.id)
-  // 获取上级菜单名称
-  if (formData.value?.parentId === 0) {
-    parentMenuName.value = '主类目'
-  } else if (formData.value?.parentId) {
-    // TODO @芋艿:后续这里可以优化,由后端返回 menuName;
-    const menuList = await getSimpleMenuList()
-    const parent = menuList.find(item => item.id === formData.value?.parentId)
-    parentMenuName.value = parent?.name || '-'
+  toast.loading('加载中...')
+  try {
+    formData.value = await getMenu(props.id)
+    // 获取上级菜单名称
+    if (formData.value?.parentId === 0) {
+      parentMenuName.value = '主类目'
+    } else if (formData.value?.parentId) {
+      // TODO @芋艿:后续这里可以优化,由后端返回 menuName;
+      const menuList = await getSimpleMenuList()
+      const parent = menuList.find(item => item.id === formData.value?.parentId)
+      parentMenuName.value = parent?.name || '-'
+    }
+  } finally {
+    toast.close()
   }
 }
 
@@ -143,13 +149,4 @@ onMounted(() => {
 </script>
 
 <style lang="scss" scoped>
-:deep(.cell-group) {
-  border-radius: 12rpx;
-  overflow: hidden;
-  box-shadow: 0 3rpx 8rpx rgba(24, 144, 255, 0.06);
-}
-
-.safe-area-inset-bottom {
-  padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
-}
 </style>

+ 5 - 10
src/pages-system/menu/form/index.vue

@@ -8,9 +8,9 @@
     />
 
     <!-- 表单区域 -->
-    <view class="p-24rpx pb-200rpx">
+    <view>
       <wd-form ref="formRef" :model="formData" :rules="formRules">
-        <wd-cell-group custom-class="cell-group" border>
+        <wd-cell-group border>
           <MenuPicker v-model="formData.parentId" />
           <wd-cell title="菜单类型" title-width="180rpx" prop="type">
             <wd-radio-group v-model="formData.type" shape="button" @change="handleTypeChange">
@@ -127,10 +127,11 @@ import { useToast } from 'wot-design-uni'
 import { createMenu, getMenu, updateMenu } from '@/api/system/menu'
 import { getIntDictOptions } from '@/hooks/useDict'
 import { CommonStatusEnum, DICT_TYPE, SystemMenuTypeEnum } from '@/utils/constants'
+import { navigateBackPlus } from '@/utils'
 import MenuPicker from './components/menu-picker.vue'
 
 const props = defineProps<{
-  id?: number
+  id?: number | any
   parentId?: number
 }>()
 
@@ -170,7 +171,7 @@ const formRef = ref()
 
 /** 返回上一页 */
 function handleBack() {
-  uni.navigateBack()
+  navigateBackPlus('/pages-system/menu/index')
 }
 
 /** 菜单类型变更: */
@@ -245,12 +246,6 @@ onMounted(() => {
 </script>
 
 <style lang="scss" scoped>
-:deep(.cell-group) {
-  border-radius: 12rpx;
-  overflow: hidden;
-  box-shadow: 0 3rpx 8rpx rgba(24, 144, 255, 0.06);
-}
-
 .safe-area-inset-bottom {
   padding-bottom: calc(24rpx + env(safe-area-inset-bottom));
 }

+ 38 - 8
src/pages-system/menu/index.vue

@@ -7,6 +7,13 @@
       @click-left="handleBack"
     />
 
+    <!-- 搜索组件 -->
+    <SearchForm
+      :search-params="queryParams"
+      @search="handleQuery"
+      @reset="handleReset"
+    />
+
     <!-- 面包屑导航 -->
     <Breadcrumb ref="breadcrumbRef" v-model="currentParentId" />
 
@@ -55,22 +62,25 @@
     </view>
 
     <!-- 新增按钮 -->
-    <view
-      class="fixed bottom-100rpx right-32rpx z-10 h-100rpx w-100rpx flex items-center justify-center rounded-full bg-[#1890ff] shadow-lg"
+    <wd-fab
+      position="right-bottom"
+      type="primary"
+      :expandable="false"
       @click="handleAdd"
-    >
-      <wd-icon name="add" size="24px" color="#fff" />
-    </view>
+    />
   </view>
 </template>
 
 <script lang="ts" setup>
+import type { SearchFormData } from './components/search-form.vue'
 import type { Menu } from '@/api/system/menu'
-import { computed, onMounted, ref } from 'vue'
+import { computed, onMounted, reactive, ref } from 'vue'
 import { getMenuList } from '@/api/system/menu'
+import { navigateBackPlus } from '@/utils'
 import { DICT_TYPE, SystemMenuTypeEnum } from '@/utils/constants'
 import { findChildren, handleTree } from '@/utils/tree'
 import Breadcrumb from './components/breadcrumb.vue'
+import SearchForm from './components/search-form.vue'
 
 definePage({
   style: {
@@ -91,10 +101,15 @@ const currentList = computed(() => {
 }) // 当前层级的菜单列表
 const breadcrumbRef = ref<InstanceType<typeof Breadcrumb>>()
 
+const queryParams = reactive<SearchFormData>({
+  name: undefined,
+  status: undefined,
+})
+
 /** 返回上一页或上一层级 */
 function handleBack() {
   if (!breadcrumbRef.value?.back()) {
-    uni.navigateBack()
+    navigateBackPlus()
   }
 }
 
@@ -149,13 +164,28 @@ function handleEnterChildren(item: Menu) {
 async function getList() {
   loading.value = true
   try {
-    const data = await getMenuList()
+    const data = await getMenuList(queryParams)
     list.value = handleTree(data)
   } finally {
     loading.value = false
   }
 }
 
+/** 搜索按钮操作 */
+function handleQuery(data?: SearchFormData) {
+  queryParams.name = data?.name
+  queryParams.status = data?.status
+  // 重置面包屑
+  currentParentId.value = 0
+  breadcrumbRef.value?.reset()
+  getList()
+}
+
+/** 重置按钮操作 */
+function handleReset() {
+  handleQuery()
+}
+
 /** 新增菜单 */
 function handleAdd() {
   uni.navigateTo({