gemercheung hai 7 meses
pai
achega
6b749ac681

+ 5 - 0
packages/backend/src/modules/menu/dto.ts

@@ -70,6 +70,11 @@ export class CreateMenuDto {
   @IsOptional()
   styleType?: number;
 
+  @ApiProperty({ required: false, default: 0 })
+  @IsNumber()
+  @IsOptional()
+  order?: number;
+
   @ApiProperty({ nullable: true, required: false, default: '' })
   @IsString()
   @IsOptional()

+ 3 - 0
packages/backend/src/modules/menu/menu.entity.ts

@@ -29,6 +29,9 @@ export class Menu {
   @Column({ default: 0 })
   styleType: number;
 
+  @Column({ default: 0 })
+  order: number;
+
   @Column({ default: '' })
   description: string;
 

+ 4 - 1
packages/backend/src/modules/web/web.controller.ts

@@ -5,7 +5,10 @@ import { ApiBearerAuth, ApiTags } from '@nestjs/swagger';
 @ApiTags('website(展示端)')
 @Controller('web')
 export class WebController {
-  constructor(private readonly webService: WebService) {}
+  constructor(
+    private readonly webService: WebService,
+
+  ) {}
 
   @Get('menu')
   getMenus() {

+ 12 - 4
packages/backend/src/modules/web/web.service.ts

@@ -2,20 +2,27 @@ import { Injectable } from '@nestjs/common';
 import { InjectRepository } from '@nestjs/typeorm';
 import { Like, Repository } from 'typeorm';
 import { Menu } from '@/modules/menu/menu.entity';
+import { SharedService } from '@/shared/shared.service';
 
 @Injectable()
 export class WebService {
   constructor(
     @InjectRepository(Menu)
     private menuRepo: Repository<Menu>,
-  ) {
-  }
+    private readonly sharedService: SharedService,
+  ) {}
 
-  findMenuTree() {
-    return this.menuRepo.find({
+  async findMenuTree() {
+    const menus = await this.menuRepo.find({
       where: {
         enable: true,
       },
+      order: {
+        order: 'ASC',
+        children: {
+          order: 'ASC',
+        },
+      },
       relations: {
         children: true,
         category: true,
@@ -27,5 +34,6 @@ export class WebService {
         },
       },
     });
+    return this.sharedService.handleTree(menus);
   }
 }

+ 26 - 24
packages/frontend/src/views/menu/index.vue

@@ -2,7 +2,7 @@
   <CommonPage>
     <template #action>
       <NButton type="primary" @click="handleAddMenu">
-        <i class="i-material-symbols:add mr-4 text-18"/>
+        <i class="i-material-symbols:add mr-4 text-18" />
         新增菜单
       </NButton>
     </template>
@@ -18,8 +18,10 @@
                   <n-card :bordered="false" size="small">
                     <template #cover>
                       <!-- <div style="width: 100%;height: 50px;overflow: hidden;"> -->
-                      <n-image preview-disabled :src="child.cover" object-fit="scale-down"
-                               style="width: 100%;height: 50px;overflow: hidden;"/>
+                      <n-image
+                        preview-disabled :src="child.cover" object-fit="scale-down"
+                        style="width: 100%;height: 50px;overflow: hidden;"
+                      />
                       <!-- </div> -->
                     </template>
                     <div class="text-center text-12">
@@ -41,7 +43,7 @@
               @select="(key) => handleSelect(key, item)"
             >
               <n-button text>
-                <i class="i-material-symbols:more-horiz text-24"/>
+                <i class="i-material-symbols:more-horiz text-24" />
               </n-button>
             </n-dropdown>
           </template>
@@ -58,7 +60,7 @@
             trigger: ['input', 'blur'],
           }"
         >
-          <n-input v-model:value="modalForm.title"/>
+          <n-input v-model:value="modalForm.title" />
         </n-form-item>
 
         <n-form-item
@@ -68,7 +70,7 @@
             trigger: ['input', 'blur'],
           }"
         >
-          <n-input v-model:value="modalForm.description" />
+          <n-input v-model:value="modalForm.description" type="textarea" />
         </n-form-item>
         <n-form-item
           label="样式类型" path="styleType" :rule="{
@@ -84,7 +86,7 @@
         </n-form-item>
 
         <n-form-item label="备注" path="remark">
-          <n-input v-model:value="modalForm.remark"/>
+          <n-input v-model:value="modalForm.remark" />
         </n-form-item>
         <n-form-item label="是否显示" path="isPublish">
           <NSwitch v-model:value="modalForm.isPublish">
@@ -112,32 +114,32 @@
 </template>
 
 <script setup>
-import {MeModal} from '@/components'
-import {useCrud} from '@/composables'
-import {useUserStore} from '@/store/index.js'
-import {styleEnum} from '@/utils/enum.js'
-import {onMounted} from 'vue'
-import {useRouter} from 'vue-router'
+import { MeModal } from '@/components'
+import { useCrud } from '@/composables'
+import { useUserStore } from '@/store/index.js'
+import { styleEnum } from '@/utils/enum.js'
+import { onMounted } from 'vue'
+import { useRouter } from 'vue-router'
 import MenuApi from './api.js'
 
 const router = useRouter()
 const topMenu = ref([])
-const {userId} = useUserStore()
+const { userId } = useUserStore()
 const $table = ref(null)
 
 console.log('styleEnum', styleEnum)
 /** QueryBar筛选参数(可选) */
 const queryItems = ref({})
 
-const {modalRef, modalFormRef, modalAction, modalForm, handleAdd, handleDelete, handleEdit}
+const { modalRef, modalFormRef, modalAction, modalForm, handleAdd, handleDelete, handleEdit }
   = useCrud({
-  name: '菜单',
-  doCreate: MenuApi.create,
-  doDelete: MenuApi.delete,
-  doUpdate: MenuApi.update,
-  initForm: {enable: true, isPublish: true},
-  refresh: (_, keepCurrentPage) => $table.value?.handleSearch(keepCurrentPage),
-})
+    name: '菜单',
+    doCreate: MenuApi.create,
+    doDelete: MenuApi.delete,
+    doUpdate: MenuApi.update,
+    initForm: { enable: true, isPublish: true },
+    refresh: (_, keepCurrentPage) => $table.value?.handleSearch(keepCurrentPage),
+  })
 onMounted(() => {
   $table.value?.handleSearch()
   getTopMenuList()
@@ -161,7 +163,7 @@ const options = [
 ]
 
 function handleSelect(key, item) {
-  const {id} = item
+  const { id } = item
   switch (key) {
     case 'edit':
       handleTopMenuEdit(id)
@@ -186,7 +188,7 @@ async function handleTopMenuEdit(id) {
 }
 
 function getTopMenuList() {
-  MenuApi.getLevel(0).then(({data = []}) => (topMenu.value = data))
+  MenuApi.getLevel(0).then(({ data = [] }) => (topMenu.value = data))
 }
 
 function handleAddMenu() {

+ 18 - 5
packages/frontend/src/views/menu/list.vue

@@ -47,7 +47,7 @@
             trigger: ['input', 'blur'],
           }"
         >
-          <n-input v-model:value="modalForm.description" />
+          <n-input v-model:value="modalForm.description" type="textarea" />
         </n-form-item>
         <n-form-item v-if="modalForm.level !== 0" label="封面" path="cover">
           <n-upload
@@ -116,6 +116,19 @@
           <n-input-number v-model:value="modalForm.grid" style="width:100%" />
         </n-form-item>
 
+        <n-form-item
+          label="排序"
+          path="order"
+          :rule="{
+            type: 'number',
+            required: true,
+            message: '此为必填项',
+            trigger: ['blur', 'change'],
+          }"
+        >
+          <n-input-number v-model:value="modalForm.order" />
+        </n-form-item>
+
         <n-form-item label="是否显示" path="isPublish">
           <NSwitch v-model:value="modalForm.isPublish">
             <template #checked>
@@ -146,12 +159,12 @@ import { MeCrud, MeModal, MeQueryItem } from '@/components'
 import { useCrud } from '@/composables'
 import { useUserStore } from '@/store/index.js'
 import { formatDateTime } from '@/utils'
+import { styleEnum } from '@/utils/enum.js'
 import { NButton, NImage, NSwitch } from 'naive-ui'
 import { useRoute, useRouter } from 'vue-router'
 import articleApi from '../article/api'
 import categoryApi from '../category/api'
 import api from './api.js'
-import {styleEnum} from "@/utils/enum.js";
 
 const $table = ref(null)
 
@@ -180,7 +193,7 @@ const { modalRef, modalFormRef, modalAction, modalForm, handleAdd, handleDelete,
     doCreate: api.create,
     doDelete: api.delete,
     doUpdate: api.update,
-    initForm: { enable: true, isPublish: true },
+    initForm: { enable: true, isPublish: true, order: 0 },
     refresh: (_, keepCurrentPage) => $table.value?.handleSearch(keepCurrentPage),
   })
 
@@ -281,7 +294,7 @@ async function handleEnable(row) {
     row.enableLoading = false
     $message.success('操作成功')
     $table.value?.handleSearch()
-  }
+  }
   catch (error) {
     console.error(error)
     row.enableLoading = false
@@ -333,7 +346,7 @@ async function handleFormEdit(data = {}) {
       status: 'finished',
       url: modalForm.value.cover,
     }]
-  }
+  }
   else {
     previewFileList.value = []
   }

+ 27 - 2
packages/web/src/pages/index.vue

@@ -74,15 +74,32 @@
                   </div>
 
                   <div
-                    class="text-size-base whitespace-nowrap text-ellipsis  w-[calc(100%-20px)] overflow-hidden color-[#909090]"
+                    class="text-size-base whitespace-nowrap text-ellipsis w-[calc(100%-20px)] overflow-hidden color-[#909090]"
                   >
-                    {{child.description}}
+                    {{ child.description }}
                   </div>
                 </div>
               </div>
             </n-gi>
           </n-grid>
         </template>
+
+        <template v-if="item.styleType === 3">
+          <n-grid x-gap="100" y-gap="100" :cols="3">
+            <n-gi v-for="child of item.children" :key="child.id">
+              <div
+                :class="{ [`style-${item.styleType}`]: true }"
+                class="show-item b-rd-3xl relative w-full h-full flex flex-col align-center items-center justify-center"
+              >
+                <img :src="child.cover" alt="" class="w-[40px] h-[40px]" />
+
+                <div class="font-size-[20px] font-bold my-[16px]">{{ child.title }}</div>
+
+                <div class="color-[#909090] w-[calc(100%-30px)] text-center">{{ child.description }}</div>
+              </div>
+            </n-gi>
+          </n-grid>
+        </template>
       </template>
     </div>
   </div>
@@ -146,5 +163,13 @@ const onSlideChange = () => {
     background: #f5f9ff;
     border-radius: 10px 10px 10px 10px;
   }
+
+  &.style-3 {
+    width: 411px;
+    height: 240px;
+    background: #ffffff;
+    box-shadow: 0px 0px 10px 0px rgba(6, 97, 201, 0.2);
+    border-radius: 10px 10px 10px 10px;
+  }
 }
 </style>