Эх сурвалжийг харах

fix: 新增侧边栏和首页

MTrun 3 жил өмнө
parent
commit
c1daa231b6

+ 1 - 1
package.json

@@ -8,7 +8,6 @@
     "lint": "eslint \"{src}/**/*.{vue,ts,tsx}\" --fix --ext"
   },
   "dependencies": {
-    "@vicons/ionicons5": "~0.11.0",
     "axios": "^0.23.0",
     "mockjs": "^1.1.0",
     "naive-ui": "^2.19.9",
@@ -18,6 +17,7 @@
     "vue-router": "4.0.12"
   },
   "devDependencies": {
+    "@vicons/ionicons5": "~0.11.0",
     "@types/node": "^16.11.1",
     "@typescript-eslint/eslint-plugin": "^5.6.0",
     "@typescript-eslint/parser": "^5.6.0",

BIN
src/assets/images/project/project-top-bg.png


+ 0 - 7
src/components/Application/Application.vue

@@ -12,13 +12,6 @@
   </n-loading-bar-provider>
 </template>
 
-<script lang="ts">
-import { defineComponent } from 'vue'
-export default defineComponent({
-  name: 'Application'
-})
-</script>
-
 <script lang="ts" setup>
 import {
   NDialogProvider,

+ 24 - 9
src/components/UserInfo/index.vue

@@ -5,18 +5,17 @@
     :show-arrow="true"
     :options="options"
   >
-    <n-button quaternary circle>
-      <n-icon size="20" :depth="1">
-        <PersonOutlineIcon v-show="fallback" />
-      </n-icon>
+    <div class="user-info-box">
+      <PersonIcon v-if="fallback" />
       <n-avatar
-        v-show="!fallback"
+        v-if="!fallback"
         round
-        size="small"
+        object-fit="cover"
+        size="medium"
         :src="imageUrl"
         @error="errorHandle"
       />
-    </n-button>
+    </div>
   </n-dropdown>
 </template>
 
@@ -26,9 +25,10 @@ import { NAvatar, NText } from 'naive-ui'
 import { renderIcon } from '@/utils/index'
 import { openDoc, logout } from '@/utils/page'
 import {
-  Person as PersonOutlineIcon,
+  Person as PersonIcon,
   LogOutOutline as LogOutOutlineIcon,
-  DocumentText as DocumentTextIcon
+  DocumentText as DocumentTextIcon,
+  ChatboxEllipses as ChatboxEllipsesIcon
 } from '@vicons/ionicons5'
 
 const imageUrl = 'https://www.naiveui.com/assets/naivelogo.93278402.svg'
@@ -74,6 +74,11 @@ const options = [
     key: 'doc',
     icon: renderIcon(DocumentTextIcon)
   },
+  {
+    label: '联系我们',
+    key: 'contact',
+    icon: renderIcon(ChatboxEllipsesIcon)
+  },
   {
     type: 'divider',
     key: 'd2'
@@ -95,9 +100,19 @@ const handleSelect = (key: string) => {
     case 'doc':
       openDoc()
       break
+    case 'contact':
+      openDoc()
+      break
     case 'logout':
       logout()
       break
   }
 }
 </script>
+
+<style lang="scss" scoped>
+.user-info-box {
+  cursor: pointer;
+  transform: scale(.7);
+}
+</style>

+ 10 - 2
src/enums/pageEnum.ts

@@ -13,9 +13,17 @@ export enum PageEnum {
   BASE_HOME = '/project',
   BASE_HOME_NAME = 'Project',
 
+  // 我的项目
+  BASE_HOME_ITEMS = '/project/items',
+  BASE_HOME_ITEMS_NAME = 'Project-Ttems',
+
+  // 我的模板
+  BASE_HOME_TEMPLATE = '/project/my-template',
+  BASE_HOME_TEMPLATE_NAME = 'Project-My-Template',
+
   // 模板市场
-  BASE_HOME_Template_Market = '/project/templateMarket',
-  BASE_HOME_Template_Market_NAME = 'Project-TemplateMarket',
+  BASE_HOME_TEMPLATE_MARKET = '/project/template-market',
+  BASE_HOME_TEMPLATE_MARKET_NAME = 'Project-Template-Market',
 
   // 错误
   ERROR_PAGE_NAME_403 = 'ErrorPage403',

+ 0 - 4
src/layout/components/Header/index.vue

@@ -32,9 +32,5 @@ import { LangSelect } from '@/components/LangSelect'
     padding: 0 60px;
     height: $--header-height;
   }
-  &-divider {
-    margin: 0;
-    padding-top: 0;
-  }
 }
 </style>

+ 1 - 5
src/layout/components/Main/index.vue

@@ -1,11 +1,7 @@
 <template>
   <router-view>
     <template #default="{ Component, route }">
-      <transition name="list-complete" mode="out-in" appear>
-        <keep-alive>
-          <component :is="Component" :key="route.fullPath" />
-        </keep-alive>
-      </transition>
+      <component :is="Component" :key="route.fullPath" />
     </template>
   </router-view>
 </template>

+ 3 - 0
src/layout/components/TransitionMain/index.ts

@@ -0,0 +1,3 @@
+import TransitionMain from './index.vue';
+
+export { TransitionMain };

+ 18 - 0
src/layout/components/TransitionMain/index.vue

@@ -0,0 +1,18 @@
+<template>
+  <router-view #default="{ Component, route }">
+    <n-collapse-transition appear>
+      <component :is="Component" :key="route.fullPath" />
+    </n-collapse-transition>
+    <!-- todo 暂时不生效,待处理 -->
+    <!-- <transition name="v-modal" mode="out-in" appear>
+      <component
+        v-if="route.noKeepAlive"
+        :is="Component"
+        :key="route.fullPath"
+      />
+      <keep-alive v-else>
+        <component :is="Component" :key="route.fullPath" />
+      </keep-alive>
+    </transition> -->
+  </router-view>
+</template>

+ 0 - 9
src/layout/index.vue

@@ -6,17 +6,8 @@
 import { ref, onMounted } from 'vue'
 import { useLoadingBar } from 'naive-ui'
 import { MainView } from '@/layout/components/Main/index'
-const collapsed = ref<boolean>(false)
-
-const watchWidth = () => {
-  const Width = document.body.clientWidth
-  if (Width <= 950) {
-    collapsed.value = true
-  } else collapsed.value = false
-}
 
 onMounted(() => {
-  window.addEventListener('resize', watchWidth)
   //挂载在 window 方便与在js中使用
   window['$loading'] = useLoadingBar()
   window['$loading'].finish()

+ 10 - 0
src/plugins/naive.ts

@@ -2,6 +2,11 @@ import type { App } from 'vue';
 import {
   create,
   NA,
+  NP,
+  NH1,
+  NH2,
+  NH3,
+  NH4,
   NText,
   NConfigProvider,
   NMessageProvider,
@@ -76,6 +81,11 @@ import {
 const naive = create({
   components: [
     NA,
+    NP,
+    NH1,
+    NH2,
+    NH3,
+    NH4,
     NText,
     NMessageProvider,
     NDialogProvider,

+ 0 - 1
src/router/base.ts

@@ -40,7 +40,6 @@ export const HttpErrorPage: RouteRecordRaw[] = [
   },
 ]
 
-// 404 on a page
 export const ErrorPageRoute: AppRouteRecordRaw = {
   path: '/:path(.*)*',
   name: 'ErrorPage',

+ 38 - 2
src/router/modules/project.router.ts

@@ -1,14 +1,50 @@
 import { RouteRecordRaw } from 'vue-router'
 import { PageEnum } from '@/enums/pageEnum'
 
+// 引入路径
+const importPath = {
+  'PageEnum.BASE_HOME_NAME': () => import('@/views/project/index.vue'),
+  'PageEnum.BASE_HOME_ITEMS_NAME': () => import('@/views/project/items/index.vue'),
+  'PageEnum.BASE_HOME_TEMPLATE_NAME': () => import('@/views/project/mtTemplate/index.vue'),
+  'PageEnum.BASE_HOME_TEMPLATE_MARKET_NAME': () => import('@/views/project/templateMarket/index.vue')
+}
+
 const projectRoutes: RouteRecordRaw = {
   path: PageEnum.BASE_HOME,
   name: PageEnum.BASE_HOME_NAME,
-  component: () => import('@/views/project/index.vue'),
+  component: importPath['PageEnum.BASE_HOME_NAME'],
+  redirect: PageEnum.BASE_HOME_ITEMS,
   meta: {
     title: '项目',
     isRoot: true
-  }
+  },
+  // todo 可采用循环动态插入
+  children: [
+    {
+      path: PageEnum.BASE_HOME_ITEMS,
+      name: PageEnum.BASE_HOME_ITEMS_NAME,
+      component: importPath['PageEnum.BASE_HOME_ITEMS_NAME'],
+      meta: {
+        title: PageEnum.BASE_HOME_ITEMS_NAME
+      }
+    },
+    {
+      path: PageEnum.BASE_HOME_TEMPLATE,
+      name: PageEnum.BASE_HOME_TEMPLATE_NAME,
+      component: importPath['PageEnum.BASE_HOME_TEMPLATE_NAME'],
+      meta: {
+        title: PageEnum.BASE_HOME_TEMPLATE_NAME
+      }
+    },
+    {
+      path: PageEnum.BASE_HOME_TEMPLATE_MARKET,
+      name: PageEnum.BASE_HOME_TEMPLATE_MARKET_NAME,
+      component: importPath['PageEnum.BASE_HOME_TEMPLATE_MARKET_NAME'],
+      meta: {
+        title: PageEnum.BASE_HOME_TEMPLATE_MARKET_NAME
+      }
+    }
+  ]
 }
 
 export default projectRoutes

+ 1 - 1
src/settings/designSetting.ts

@@ -47,7 +47,7 @@ export const theme = {
 }
 
 // 侧边栏宽度
-export const asideWidth = '240'
+export const asideWidth = '270'
 
 // 侧边栏缩小后的宽度
 export const asideCollapsedWidth = '60'

+ 2 - 0
src/styles/common/_dark.scss

@@ -8,4 +8,6 @@ $dark: (
     linear-gradient(120deg, $--color-dark-bg-1 0%, $--color-dark-bg-1 100%),
   //毛玻璃
     filter-color: $--filter-color-login-dark,
+  // 物料市场背景
+    items-top-bg: linear-gradient(180deg, $--color-dark-bg-1, rgba(23, 23, 26, 0))
 );

+ 0 - 3
src/styles/common/_light.scss

@@ -8,7 +8,4 @@ $light: (
     linear-gradient(120deg, $--color-text-1 0%, $--color-text-1 100%),
   //毛玻璃
     filter-color: $--filter-color-login-light,
-  // 侧边栏
-    aside-bg: #fff,
-    aside-color: rgb(239, 239, 245)
 );

+ 1 - 1
src/styles/common/animation.scss

@@ -34,4 +34,4 @@
 }
 .list-complete-leave-active {
   position: absolute;
-}
+}

+ 4 - 1
src/views/project/index.vue

@@ -12,7 +12,9 @@
           :native-scrollbar="false"
         >
           <n-layout-content>
-            <router-view></router-view>
+            <TransitionMain>
+              <router-view />
+            </TransitionMain>
           </n-layout-content>
         </n-layout>
       </n-layout>
@@ -23,6 +25,7 @@
 <script setup lang="ts">
 import { Sider } from './layout/components/Sider'
 import { Header } from './layout/components/Header/index'
+import { TransitionMain } from '@/layout/components/TransitionMain/index'
 </script>
 
 <style lang="scss" scoped>

+ 3 - 0
src/views/project/items/List/index.ts

@@ -0,0 +1,3 @@
+import List from './index.vue'
+
+export { List }

+ 105 - 0
src/views/project/items/List/index.vue

@@ -0,0 +1,105 @@
+<template>
+  <div class="go-items-list">
+    <n-grid
+      :x-gap="20"
+      :y-gap="20"
+      cols="2 s:2 m:3 l:4 xl:4 xxl:4"
+      responsive="screen"
+    >
+      <n-grid-item v-for="item in list" :key="item.id">
+        <n-card hoverable size="small">
+          <div class="list-content">
+            <n-space>
+              <n-button size="small">
+                <template #icon>
+                  <n-icon>
+                    <EllipsisHorizontalCircleSharpIcon />
+                  </n-icon>
+                </template>
+              </n-button>
+            </n-space>
+            <div class="list-content-top"></div>
+            <n-image
+              object-fit="cover"
+              width="100"
+              src="https://gw.alipayobjects.com/zos/antfincdn/aPkFc8Sj7n/method-draw-image.svg"
+              :alt="item.title"
+            />
+          </div>
+          <template #action>
+            <n-skeleton v-if="loading" round size="medium" />
+            <n-space v-else justify="space-between">
+              <n-text>
+                奔跑的模板
+              </n-text>
+              <!-- 工具 -->
+              <n-space>
+                <n-button size="small">
+                  <template #icon>
+                    <n-icon>
+                      <EllipsisHorizontalCircleSharpIcon />
+                    </n-icon>
+                  </template>
+                </n-button>
+                <n-button size="small">
+                  <template #icon>
+                    <n-icon>
+                      <CopyIcon />
+                    </n-icon>
+                  </template>
+                </n-button>
+              </n-space>
+              <!-- end -->
+            </n-space>
+          </template>
+        </n-card>
+      </n-grid-item>
+    </n-grid>
+  </div>
+</template>
+
+<script setup lang="ts">
+import { reactive, ref } from 'vue'
+import {
+  EllipsisHorizontalSharp as EllipsisHorizontalCircleSharpIcon,
+  CopyOutline as CopyIcon
+} from '@vicons/ionicons5'
+const loading = ref<boolean>(true)
+
+setTimeout(() => {
+  loading.value = false
+}, 200)
+
+const list = reactive([
+  {
+    id: 1,
+    title: '物料1'
+  },
+  {
+    id: 2,
+    title: '物料1'
+  },
+  {
+    id: 3,
+    title: '物料1'
+  },
+  {
+    id: 4,
+    title: '物料1'
+  },
+  {
+    id: 5,
+    title: '物料1'
+  }
+])
+</script>
+
+<style lang="scss" scoped>
+$contentHeight: 250px;
+@include go('items-list') {
+  .list-content {
+    position: relative;
+    height: $contentHeight;
+  }
+}
+</style>

+ 15 - 0
src/views/project/items/index.vue

@@ -0,0 +1,15 @@
+<template>
+  <div class="go-project-items">
+    <List />
+  </div>
+</template>
+
+<script setup lang="ts">
+import { List } from './List'
+</script>
+
+<style lang="scss" scoped>
+@include go(project-items) {
+  padding: 30px 20px;
+}
+</style>

+ 40 - 4
src/views/project/layout/components/AsideFooter/index.vue

@@ -1,8 +1,24 @@
 <template>
   <div class="go-aside-footer">
     <n-divider class="go-mt-0" />
-    <n-space justify="space-around" :wrap="false">
-      <n-button secondary @click="handleDoc">
+    <n-space justify="space-around">
+      
+      <n-tooltip v-if="collapsed" placement="right" trigger="hover">
+        <template #trigger>
+          <n-button secondary @click="handleDoc">
+            <template #icon>
+              <n-icon size="18">
+                <HelpOutlineIcon />
+              </n-icon>
+            </template>
+          </n-button>
+        </template>
+        <n-text>
+          帮助中心
+        </n-text>
+      </n-tooltip>
+      
+      <n-button v-else secondary @click="handleDoc">
         <template #icon>
           <n-icon size="18">
             <HelpOutlineIcon />
@@ -10,13 +26,29 @@
         </template>
         <n-text>帮助中心</n-text>
       </n-button>
-      <n-button secondary @click="handleCode">
+
+      <n-tooltip v-if="collapsed" placement="right" trigger="hover">
+        <template #trigger>
+          <n-button secondary @click="handleDoc">
+            <template #icon>
+              <n-icon size="18">
+                <CodeSlashIcon />
+              </n-icon>
+            </template>
+          </n-button>
+        </template>
+        <n-text>
+          仓库地址
+        </n-text>
+      </n-tooltip>
+
+      <n-button v-else secondary @click="handleCode">
         <template #icon>
           <n-icon size="18">
             <CodeSlashIcon />
           </n-icon>
         </template>
-        <n-text>仓库地址</n-text>
+        <n-text v-show="!collapsed">仓库地址</n-text>
       </n-button>
     </n-space>
   </div>
@@ -29,6 +61,10 @@ import {
   CodeSlash as CodeSlashIcon
 } from '@vicons/ionicons5'
 
+const props = defineProps({
+  collapsed: Boolean
+})
+
 const handleDoc = () => {
   openDoc()
 }

+ 16 - 9
src/views/project/layout/components/Create/index.vue

@@ -1,20 +1,27 @@
 <template>
-  <n-button v-if="collapsed" ghost type="primary" size="small">
-    <template #icon>
-      <n-icon>
-        <DuplicateIcon />
-      </n-icon>
+  <n-tooltip v-if="collapsed" placement="right" trigger="hover">
+    <template #trigger>
+      <n-button ghost type="primary" size="small">
+        <template #icon>
+          <n-icon>
+            <DuplicateIcon />
+          </n-icon>
+        </template>
+      </n-button>
     </template>
-  </n-button>
+    <n-text>
+      新建
+    </n-text>
+  </n-tooltip>
   <n-button v-else ghost type="primary" size="large">
     <template #icon>
       <n-icon>
         <DuplicateIcon />
       </n-icon>
     </template>
-    <span>
-      新建项目
-    </span>
+    <n-text>
+      新建
+    </n-text>
   </n-button>
 </template>
 <script setup lang="ts">

+ 19 - 7
src/views/project/layout/components/Sider/index.vue

@@ -2,8 +2,8 @@
   <n-layout-sider
     class="go-project-layout-sider"
     bordered
-    inverted
     collapse-mode="width"
+    show-trigger="bar"
     :collapsed="collapsed"
     :native-scrollbar="false"
     :collapsed-width="asideCollapsedWidth"
@@ -21,32 +21,46 @@
           :options="menuOptions"
           :collapsed-width="asideCollapsedWidth"
           :collapsed-icon-size="22"
+          :default-expanded-keys="defaultExpandedKeys"
           @update:value="handleUpdateValue"
         />
       </aside>
       <!-- 底部提示 -->
       <div class="sider-bottom">
-        <AsideFooter />
+        <AsideFooter :collapsed="collapsed" />
       </div>
     </div>
   </n-layout-sider>
 </template>
 
 <script setup lang="ts">
-import { ref, computed } from 'vue'
+import { ref, computed, onMounted } from 'vue'
 import { Create } from '../Create/index'
 import { AsideFooter } from '../AsideFooter/index'
 import { asideWidth, asideCollapsedWidth } from '@/settings/designSetting'
-import { menuOptionsInit } from './menu'
+import { menuOptionsInit, expandedKeys } from './menu'
 import { useRoute } from 'vue-router'
 
-const collapsed = ref(false)
+const collapsed = ref<boolean>(false)
 
 const route = useRoute()
 const routeRame = computed(() => route.name)
 const menuValue = ref(routeRame)
 
 const menuOptions = menuOptionsInit()
+
+const defaultExpandedKeys = expandedKeys()
+
+const watchWidth = () => {
+  const Width = document.body.clientWidth
+  if (Width <= 950) {
+    collapsed.value = true
+  } else collapsed.value = false
+}
+
+onMounted(() => {
+  window.addEventListener('resize', watchWidth)
+})
 </script>
 
 <style lang="scss" scoped>
@@ -71,8 +85,6 @@ $siderHeight: 100vh;
   }
   &-layout-sider {
     height: $siderHeight;
-    @include filter-bg-color('aside-bg');
-    @include filter-border-color('aside-color');
   }
   .content-top {
     top: $--header-height;

+ 49 - 20
src/views/project/layout/components/Sider/menu.ts

@@ -3,12 +3,19 @@ import { renderIcon } from '@/utils'
 import { RouterLink } from 'vue-router'
 import { PageEnum } from '@/enums/pageEnum'
 import { MenuOption, MenuGroupOption } from 'naive-ui'
-import { FolderOpen as FolderOpenIcon, LogoAppleAppstore as LogoAppleAppstoreIcon, } from '@vicons/ionicons5'
+import {
+  Grid as GridIcon,
+  LogoAppleAppstore as LogoAppleAppstoreIcon,
+  TvOutline as DesktopIcon,
+  LaptopOutline as LaptopOutlineIcon,
+} from '@vicons/ionicons5'
 
 export const renderMenuLabel = (option: MenuOption | MenuGroupOption) => {
   return option.label
 }
 
+export const expandedKeys = () => ['all-project']
+
 export const menuOptionsInit = () => {
   return reactive([
     {
@@ -16,28 +23,50 @@ export const menuOptionsInit = () => {
       type: 'divider'
     },
     {
-      type: 'group',
-      label: '我的',
-      key: 'people',
+      label: '项目',
+      key: 'all-project',
+      icon: renderIcon(GridIcon),
       children: [
         {
-          label: () =>
-            h(
-              RouterLink,
-              {
-                to: {
-                  name: PageEnum.BASE_HOME_NAME
-                }
-              },
-              { default: () => '全部项目' }
-            ),
-          key: PageEnum.BASE_HOME_NAME,
-          icon: renderIcon(FolderOpenIcon)
+          type: 'group',
+          label: '我的',
+          key: 'my-project',
+          children: [
+            {
+              label: () =>
+                h(
+                  RouterLink,
+                  {
+                    to: {
+                      name: PageEnum.BASE_HOME_ITEMS_NAME
+                    }
+                  },
+                  { default: () => '全部项目' }
+                ),
+              key: PageEnum.BASE_HOME_ITEMS_NAME,
+              icon: renderIcon(DesktopIcon)
+            },
+            {
+              label: () =>
+                h(
+                  RouterLink,
+                  {
+                    to: {
+                      name: PageEnum.BASE_HOME_TEMPLATE_NAME
+                    }
+                  },
+                  { default: () => '我的物料' }
+                ),
+              key: PageEnum.BASE_HOME_TEMPLATE_NAME,
+              icon: renderIcon(LaptopOutlineIcon)
+            }
+          ]
         }
       ]
     },
+
     {
-      key: 'divider-1',
+      key: 'divider-2',
       type: 'divider'
     },
     {
@@ -46,12 +75,12 @@ export const menuOptionsInit = () => {
           RouterLink,
           {
             to: {
-              name: PageEnum.BASE_HOME_NAME
+              name: PageEnum.BASE_HOME_TEMPLATE_MARKET_NAME
             }
           },
-          { default: () => '模板市场' }
+          { default: () => '物料市场' }
         ),
-      key: 'store',
+      key: PageEnum.BASE_HOME_TEMPLATE_MARKET_NAME,
       icon: renderIcon(LogoAppleAppstoreIcon)
     }
   ])

+ 14 - 0
src/views/project/mtTemplate/index.vue

@@ -0,0 +1,14 @@
+<template>
+  <div class="go-project-my-template">
+    <h1>
+      我的模板
+    </h1>
+  </div>
+</template>
+
+<script setup lang="ts"></script>
+
+<style lang="scss" scoped>
+@include go('project-my-template') {
+}
+</style>

+ 3 - 0
src/views/project/templateMarket/components/TopCarousel/index.ts

@@ -0,0 +1,3 @@
+import TopCarousel from './index.vue'
+
+export { TopCarousel }

+ 12 - 0
src/views/project/templateMarket/components/TopCarousel/index.vue

@@ -0,0 +1,12 @@
+<template>
+  <div class="go-template-market-top-carousel">
+    <h1>轮播图</h1>
+  </div>
+</template>
+
+<script setup lang="ts"></script>
+
+<style lang="scss" scoped>
+@include go('project-template-market-top-carousel') {
+}
+</style>

+ 62 - 0
src/views/project/templateMarket/index.vue

@@ -0,0 +1,62 @@
+<template>
+  <div class="go-project-template-market">
+    <header class="top-box">
+      <div class="top-box-bg"></div>
+      <div class="top-content">
+        <n-carousel class="top-content-carousel" show-arrow>
+          <n-card title="模板1" hoverable>
+            <img
+              class="carousel-img"
+              src="https://naive-ui.oss-cn-beijing.aliyuncs.com/carousel-img/carousel1.jpeg"
+            />
+          </n-card>
+          <n-card title="模板2" hoverable>
+            <img
+              class="carousel-img"
+              src="https://naive-ui.oss-cn-beijing.aliyuncs.com/carousel-img/carousel2.jpeg"
+            />
+          </n-card>
+          <n-card title="模板2" hoverable>
+            <img
+              class="carousel-img"
+              src="https://naive-ui.oss-cn-beijing.aliyuncs.com/carousel-img/carousel3.jpeg"
+            />
+          </n-card>
+          <n-card title="模板2" hoverable>
+            <img
+              class="carousel-img"
+              src="https://naive-ui.oss-cn-beijing.aliyuncs.com/carousel-img/carousel4.jpeg"
+            />
+          </n-card>
+        </n-carousel>
+      </div>
+      <n-divider />
+    </header>
+  </div>
+</template>
+
+<script setup lang="ts"></script>
+
+<style lang="scss" scoped>
+@include go('project-template-market') {
+  .top-box {
+    background: url('@/assets/images/project/project-top-bg.png');
+    background-position: top -185px left -1560px;
+    &-bg {
+      position: absolute;
+      width: 100%;
+      height: 100%;
+      @include background-image('items-top-bg');
+    }
+    .top-content {
+      display: flex;
+      justify-content: center;
+      position: relative;
+      padding-top: 30px;
+      &-carousel {
+        width: 70%;
+      }
+    }
+  }
+}
+</style>