gemercheung 7 mēneši atpakaļ
vecāks
revīzija
519e6eab3c

+ 1 - 7
packages/backend/src/modules/auth/auth.service.ts

@@ -1,10 +1,4 @@
-/**********************************
- * @Author: Ronnie Zhang
- * @LastEditor: Ronnie Zhang
- * @LastEditTime: 2023/12/07 20:25:48
- * @Email: zclzone@outlook.com
- * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
- **********************************/
+
 
 import { Injectable } from '@nestjs/common';
 import { JwtService } from '@nestjs/jwt';

+ 0 - 7
packages/backend/src/modules/auth/dto.ts

@@ -1,10 +1,3 @@
-/**********************************
- * @Author: Ronnie Zhang
- * @LastEditor: Ronnie Zhang
- * @LastEditTime: 2023/12/07 20:25:55
- * @Email: zclzone@outlook.com
- * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
- **********************************/
 
 import { ApiProperty } from '@nestjs/swagger';
 import { IsBoolean, IsNotEmpty, IsOptional, IsString, Length } from 'class-validator';

+ 0 - 7
packages/backend/src/modules/auth/jwt.strategy.ts

@@ -1,10 +1,3 @@
-/**********************************
- * @Author: Ronnie Zhang
- * @LastEditor: Ronnie Zhang
- * @LastEditTime: 2023/12/07 20:26:02
- * @Email: zclzone@outlook.com
- * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
- **********************************/
 
 import { CustomException, ErrorCode } from '@/common/exceptions/custom.exception';
 import { ACCESS_TOKEN_EXPIRATION_TIME, USER_ACCESS_TOKEN_KEY } from '@/constants/redis.contant';

+ 1 - 7
packages/backend/src/modules/auth/local.strategy.ts

@@ -1,10 +1,4 @@
-/**********************************
- * @Author: Ronnie Zhang
- * @LastEditor: Ronnie Zhang
- * @LastEditTime: 2023/12/07 20:26:09
- * @Email: zclzone@outlook.com
- * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
- **********************************/
+
 
 import { Injectable } from '@nestjs/common';
 import { PassportStrategy } from '@nestjs/passport';

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

@@ -64,6 +64,11 @@ export class CreateMenuDto {
   @IsNumber()
   @IsOptional()
   articleId?: number;
+
+  @ApiProperty({ nullable: true, required: false, default: 0 })
+  @IsNumber()
+  @IsOptional()
+  styleType?: number;
 }
 
 export class GetMenuDto {

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

@@ -25,6 +25,9 @@ export class Menu {
 
   @Column({ default: true })
   enable: boolean;
+  
+  @Column({ default: 0 })
+  styleType: number;
 
   @Column({ default: 0 })
   level: number;

+ 0 - 8
packages/backend/src/modules/permission/permission.entity.ts

@@ -1,11 +1,3 @@
-/**********************************
- * @Author: Ronnie Zhang
- * @LastEditor: Ronnie Zhang
- * @LastEditTime: 2023/12/07 20:26:30
- * @Email: zclzone@outlook.com
- * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
- **********************************/
-
 import { Column, Entity, ManyToMany, ManyToOne, OneToMany, PrimaryGeneratedColumn } from 'typeorm';
 import { Role } from '@/modules/role/role.entity';
 import { MethodType, PermissionType } from '@/types';

+ 0 - 8
packages/backend/src/modules/permission/permission.module.ts

@@ -1,11 +1,3 @@
-/**********************************
- * @Author: Ronnie Zhang
- * @LastEditor: Ronnie Zhang
- * @LastEditTime: 2023/12/07 20:26:36
- * @Email: zclzone@outlook.com
- * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
- **********************************/
-
 import { Module } from '@nestjs/common';
 import { PermissionService } from './permission.service';
 import { PermissionController } from './permission.controller';

+ 0 - 8
packages/backend/src/modules/role/role.controller.ts

@@ -1,11 +1,3 @@
-/**********************************
- * @Author: Ronnie Zhang
- * @LastEditor: Ronnie Zhang
- * @LastEditTime: 2023/12/07 20:27:04
- * @Email: zclzone@outlook.com
- * Copyright © 2023 Ronnie Zhang(大脸怪) | https://isme.top
- **********************************/
-
 import {
   Controller,
   Get,

+ 22 - 0
packages/frontend/src/utils/enum.js

@@ -0,0 +1,22 @@
+export const styleEnum = [
+  {
+    value: 0,
+    label: '二宫格',
+  },
+  {
+    value: 1,
+    label: '横向幻灯片',
+  },
+  {
+    value: 2,
+    label: '二宫格',
+  },
+  {
+    value: 3,
+    label: '二宫格图文',
+  },
+  {
+    value: 4,
+    label: '其他',
+  },
+]

+ 22 - 21
packages/frontend/src/views/article/index.vue

@@ -2,7 +2,7 @@
   <CommonPage>
     <template #action>
       <NButton type="primary" @click="router.push('article/add')">
-        <i class="i-material-symbols:add mr-4 text-18"/>
+        <i class="i-material-symbols:add mr-4 text-18" />
         新增文章
       </NButton>
     </template>
@@ -10,7 +10,7 @@
     <MeCrud ref="$table" v-model:query-items="queryItems" :scroll-x="1200" :columns="columns" :get-data="api.read">
       <MeQueryItem label="标题" :label-width="50">
         <n-input v-model:value="queryItems.title" type="text" placeholder="请输入标题名" clearable>
-          <template #password-visible-icon/>
+          <template #password-visible-icon />
         </n-input>
       </MeQueryItem>
       <MeQueryItem label="状态" :label-width="50">
@@ -26,13 +26,13 @@
 </template>
 
 <script setup>
-import {MeCrud, MeQueryItem} from '@/components'
-import {useCrud} from '@/composables'
-import {formatDateTime} from '@/utils'
-import {NButton, NSwitch} from 'naive-ui'
+import { MeCrud, MeQueryItem } from '@/components'
+import { useCrud } from '@/composables'
+import { formatDateTime } from '@/utils'
+import { NButton, NSwitch } from 'naive-ui'
 import api from './api'
 
-defineOptions({name: 'RoleMgt'})
+defineOptions({ name: 'RoleMgt' })
 
 const router = useRouter()
 
@@ -44,19 +44,19 @@ onMounted(() => {
   $table.value?.handleSearch()
 })
 
-const {handleDelete}
+const { handleDelete }
   = useCrud({
-  name: '文章',
-  doCreate: api.create,
-  doDelete: api.delete,
-  doUpdate: api.update,
-  initForm: {enable: true},
-  refresh: (_, keepCurrentPage) => $table.value?.handleSearch(keepCurrentPage),
-})
+    name: '文章',
+    doCreate: api.create,
+    doDelete: api.delete,
+    doUpdate: api.update,
+    initForm: { enable: true },
+    refresh: (_, keepCurrentPage) => $table.value?.handleSearch(keepCurrentPage),
+  })
 
 const columns = [
-  {title: '标题名', key: 'title', width: '200'},
-  {title: '分类', key: 'category.title'},
+  { title: '标题名', key: 'title', width: '200' },
+  { title: '分类', key: 'category.title' },
   {
     title: '内容',
     key: 'content',
@@ -107,7 +107,7 @@ const columns = [
           },
           {
             default: () => '编辑',
-            icon: () => h('i', {class: 'i-material-symbols:edit-outline text-14'}),
+            icon: () => h('i', { class: 'i-material-symbols:edit-outline text-14' }),
           },
         ),
 
@@ -122,7 +122,7 @@ const columns = [
           },
           {
             default: () => '删除',
-            icon: () => h('i', {class: 'i-material-symbols:delete-outline text-14'}),
+            icon: () => h('i', { class: 'i-material-symbols:delete-outline text-14' }),
           },
         ),
       ]
@@ -133,11 +133,12 @@ const columns = [
 async function handleEnable(row) {
   row.enableLoading = true
   try {
-    await api.update({id: row.id, enable: !row.enable})
+    await api.update({ id: row.id, enable: !row.enable })
     row.enableLoading = false
     $message.success('操作成功')
     $table.value?.handleSearch()
-  } catch (error) {
+  }
+  catch (error) {
     console.error(error)
     row.enableLoading = false
   }

+ 16 - 0
packages/frontend/src/views/menu/index.vue

@@ -60,6 +60,19 @@
           <n-input v-model:value="modalForm.title" />
         </n-form-item>
 
+        <n-form-item
+          label="样式类型" path="styleType" :rule="{
+            required: true,
+            type: 'number',
+            message: '请输入样式类型',
+            trigger: ['input', 'blur'],
+          }"
+        >
+          <n-select
+            v-model:value="modalForm.styleType" :options="styleEnum" clearable filterable tag
+          />
+        </n-form-item>
+
         <n-form-item label="备注" path="remark">
           <n-input v-model:value="modalForm.remark" />
         </n-form-item>
@@ -92,6 +105,7 @@
 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'
@@ -100,6 +114,8 @@ const router = useRouter()
 const topMenu = ref([])
 const { userId } = useUserStore()
 const $table = ref(null)
+
+console.log('styleEnum', styleEnum)
 /** QueryBar筛选参数(可选) */
 const queryItems = ref({})
 

+ 16 - 0
packages/frontend/src/views/menu/list.vue

@@ -73,6 +73,20 @@
         </n-form-item>
 
         <n-form-item
+          v-if="modalForm.level === 0"
+          label="样式类型" path="styleType" :rule="{
+            required: true,
+            type: 'number',
+            message: '请输入样式类型',
+            trigger: ['input', 'blur'],
+          }"
+        >
+          <n-select
+            v-model:value="modalForm.styleType" :options="styleEnum" clearable filterable tag
+          />
+        </n-form-item>
+
+        <n-form-item
           label="文章链接" path="articleId" :rule="{
             required: false,
             type: 'number',
@@ -127,6 +141,7 @@ 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)
 
@@ -323,6 +338,7 @@ function handleCoverRemove() {
 
 function handleSubAdd() {
   modalForm.value.level = 1
+  modalForm.value.cover = ''
   handleAdd({ level: 1 })
 }
 

+ 1 - 0
packages/web/package.json

@@ -28,6 +28,7 @@
     "@vicons/ionicons5": "^0.13.0",
     "axios": "^1.7.9",
     "naive-ui": "^2.40.3",
+    "swiper": "^11.2.1",
     "unocss": "^0.65.1",
     "vue": "^3.5.13",
     "vue-i18n": "^11.0.1",

+ 1 - 0
packages/web/src/api/menu.ts

@@ -7,5 +7,6 @@ export type MenuItem = {
   cover: string
   children: MenuItem[]
   grid: number
+  styleType: number
 }
 export const getMenuList = (): Promise<ResultData<MenuItem[]>> => request.get('web/menu')

BIN
packages/web/src/assets/img/banner_bg1.png


BIN
packages/web/src/assets/img/logo.png


BIN
packages/web/src/assets/img/logo_4dge_cn.png


+ 54 - 1
packages/web/src/layouts/footer.vue

@@ -1,4 +1,57 @@
 <template>
-  <div  class="grid min-h-sm w-full bg-gray-800 text-white flex flex-col">footer</div>
+  <div class="footer grid min-h-sm w-full bg-gray-800 text-white">
+    <div class="w-full h-full flex flex-col justify-between max-w-screen-xl my-0 mx-auto">
+      <div class="top w-full h-full mt-[60px] flex flex-row">
+        <div class="left flex flex-col w-auto">
+          <img src="@/assets/img/logo.png" alt="logo" class="h-[43px] w-[160px] mb-5" />
+          <span class="font-size-[16px]" > 联系我们</span>
+          <span class="font-size-[24px] my-[10px]"> 400-669-8025</span>
+          <span class="font-size-[16px] my-1"> 销售合作:sales@4dage.com</span>
+          <span class="font-size-[16px] my-1"> 媒体采访:pr@4dage.com</span>
+        </div>
+        <div class=" flex-1 middle">
+
+          xxxxx
+        </div>
+        <div>xxxxx</div>
+      </div>
+      <div class="bottom h-[82px] flex flex-row flex items-center justify-between">
+        <div class="label">Copyright © 2022 4DAGE Co., Ltd. All rights reserved.</div>
+        <div class="label icp">
+          <a
+            target="_blank"
+            href="http://www.beian.gov.cn/portal/registerSystemInfo?recordcode=44049102496647"
+            class="text-white min-h-[20px] inline-flex items-center justify-center mr-3 decoration-none"
+          >
+            <img
+              src="https://4dscene.4dage.com/new4dkk/v2/images/src/assets/images/baicon.d0289dc.png"
+              alt=""
+            />
+            粤公网安备 44049102496647号
+          </a>
+          <a
+            target="_blank"
+            href="https://beian.miit.gov.cn/"
+            class="text-white min-h-[20px] inline-flex items-center justify-center mr-3 decoration-none"
+            ><img
+              src="https://4dscene.4dage.com/new4dkk/v2/images/src/assets/images/baicon.d0289dc.png"
+              alt=""
+            />
+            粤ICP备14078495号
+          </a>
+        </div>
+      </div>
+    </div>
+  </div>
 </template>
 <script setup lang="ts"></script>
+
+<style scoped>
+.footer {
+  background: linear-gradient(180deg, #0661c9 0%, #033063 100%);
+}
+
+.bottom {
+  border-top: 1px solid rgba(255, 255, 255, 0.1);
+}
+</style>

Failā izmaiņas netiks attēlotas, jo tās ir par lielu
+ 14 - 10
packages/web/src/layouts/header.vue


+ 70 - 18
packages/web/src/pages/index.vue

@@ -1,27 +1,58 @@
 <template>
   <div class="max-w-screen-xl content my-0 mx-auto">
-    <div v-for="(item, index) in list" :key="index" class="m-y-[140px]">
-      <n-h1 class="text-center mb-[160px]">{{ item.title }}</n-h1>
+    <div v-for="(item, index) in list" :key="index" class="m-b-[80px]">
+      <template v-if="item.children?.length">
+        <n-h1 class="text-center mb-[80px] font-size-[40px] font-500">{{ item.title }}</n-h1>
 
-      <n-grid x-gap="100" y-gap="100" :cols="item.grid || 3">
-        <n-gi v-for="child of item.children" :key="child.id">
-          <div
-            class="show-item b-rd-3xl relative"
-            :class="{ [`grid-${item.grid}`]: true }"
-            :style="{ backgroundImage: `url(${child.cover})` }"
+        <template v-if="item.styleType === 0">
+          <n-grid x-gap="100" y-gap="100" :cols="2">
+            <n-gi v-for="child of item.children" :key="child.id">
+              <div
+                class="show-item b-rd-3xl relative"
+                :class="{ [`grid-${item.grid}`]: true }"
+                :style="{ backgroundImage: `url(${child.cover})` }"
+              >
+                <div
+                  class="w-full h-full flex flex-col absolute top-0 left-0 -mx-auto justify-end overflow-hidden shadow-blueGray"
+                >
+                  <div
+                    class="w-full h-[60px] title text-black font-size-[20px] flex justify-center items-center bg-white bg-op-50"
+                  >
+                    {{ child.title }}
+                  </div>
+                </div>
+              </div>
+            </n-gi>
+          </n-grid>
+        </template>
+
+        <template v-if="item.styleType === 1">
+          <swiper
+            :slides-per-view="item.grid"
+            :space-between="50"
+            @swiper="onSwiper"
+            @slideChange="onSlideChange"
           >
-            <div
-              class="w-full h-full flex flex-col absolute top-0 left-0 -mx-auto justify-end overflow-hidden shadow-blueGray"
-            >
+            <swiper-slide v-for="child of item.children" :key="child.id">
               <div
-                class="w-full h-[60px] title text-white text-size-2xl flex items-center p-x-10 bg-white bg-op-50"
+                class="cover w-full h-[400px] overflow-hidden b-rd-xl"
+                :style="{ backgroundImage: `url(${child.cover})` }"
+              ></div>
+              <div
+                class="w-full h-full flex flex-col absolute top-0 left-0 -mx-auto justify-end overflow-hidden shadow-blueGray"
               >
-                {{ child.title }}
+                <div
+                  class="w-full h-[50px] title text-black font-size-[20px] flex justify-center items-center bg-white bg-op-50"
+                >
+                  {{ child.title }}
+                </div>
               </div>
-            </div>
-          </div>
-        </n-gi>
-      </n-grid>
+            </swiper-slide>
+
+            ...
+          </swiper>
+        </template>
+      </template>
     </div>
   </div>
 </template>
@@ -32,9 +63,13 @@ title: About
 </route>
 
 <script setup lang="ts">
-import { NH1, NCard, NGrid, NGi } from 'naive-ui'
+import { NH1, NImage, NGrid, NGi } from 'naive-ui'
 import { getMenuList } from '@/api'
 import type { MenuItem } from '@/api'
+import { Swiper, SwiperSlide } from 'swiper/vue'
+
+// Import Swiper styles
+import 'swiper/css'
 
 const list = ref<MenuItem[]>([])
 
@@ -43,15 +78,32 @@ getMenuList().then((data) => {
     list.value = data.data
   }
 })
+
+const onSwiper = (swiper) => {
+  console.log(swiper)
+}
+const onSlideChange = () => {
+  console.log('slide change')
+}
 </script>
 
 <style lang="scss" scoped>
+.trans-white-50 {
+  background: rgba(255, 255, 255, 0.5);
+}
+.cover {
+  background-repeat: no-repeat;
+  background-position: center top;
+  background-size: cover;
+}
 .show-item {
   cursor: pointer;
   background-repeat: no-repeat;
   background-position: center bottom;
   background-size: cover;
 
+
+
   &.grid-2 {
     width: 614px;
     height: 346px;

+ 0 - 1
packages/web/src/pages/showdoc.vue

@@ -12,7 +12,6 @@ title: showdoc
 
 <script setup lang="ts">
 
-
 </script>
 
 <style lang="scss" scoped></style>

+ 9 - 0
pnpm-lock.yaml

@@ -541,6 +541,9 @@ importers:
       naive-ui:
         specifier: ^2.40.3
         version: 2.40.4(vue@3.5.13(typescript@5.7.3))
+      swiper:
+        specifier: ^11.2.1
+        version: 11.2.1
       unocss:
         specifier: ^0.65.1
         version: 0.65.3(postcss@8.4.49)(rollup@4.29.1)(vite@6.0.7(@types/node@22.10.6)(jiti@2.4.2)(sass@1.83.1)(terser@5.37.0)(tsx@4.19.2)(yaml@2.7.0))(vue@3.5.13(typescript@5.7.3))
@@ -7616,6 +7619,10 @@ packages:
   swagger-ui-dist@5.18.2:
     resolution: {integrity: sha512-J+y4mCw/zXh1FOj5wGJvnAajq6XgHOyywsa9yITmwxIlJbMqITq3gYRZHaeqLVH/eV/HOPphE6NjF+nbSNC5Zw==}
 
+  swiper@11.2.1:
+    resolution: {integrity: sha512-62G69+iQRIfUqTmJkWpZDcX891Ra8O9050ckt1/JI2H+0483g+gq0m7gINecDqMtDh2zt5dK+uzBRxGhGOOvQA==}
+    engines: {node: '>= 4.7.0'}
+
   symbol-observable@4.0.0:
     resolution: {integrity: sha512-b19dMThMV4HVFynSAM1++gBHAbk2Tc/osgLIBZMKsyqh34jb2e8Os7T6ZW/Bt3pJFdBTd2JwAnAAEQV7rSNvcQ==}
     engines: {node: '>=0.10'}
@@ -17000,6 +17007,8 @@ snapshots:
     dependencies:
       '@scarf/scarf': 1.4.0
 
+  swiper@11.2.1: {}
+
   symbol-observable@4.0.0: {}
 
   symbol-tree@3.2.4: {}