feat: adapted mobile screen

This commit is contained in:
chansee97 2025-08-02 01:06:47 +08:00
parent 1b4639d5d8
commit 3144acbc39
10 changed files with 417 additions and 315 deletions

6
pnpm-workspace.yaml Normal file
View File

@ -0,0 +1,6 @@
ignoredBuiltDependencies:
- '@parcel/watcher'
- esbuild
- simple-git-hooks
- vue-demi
- unrs-resolver

View File

@ -0,0 +1,38 @@
<script setup lang="ts">
import Search from '../header/Search.vue'
import Notices from '../header/Notices.vue'
import UserCenter from '../header/UserCenter.vue'
import Setting from './Setting.vue'
const showDrawer = defineModel<boolean>('show', { default: false })
</script>
<template>
<n-drawer
v-model:show="showDrawer"
:width="280"
placement="right"
:mask-closable="true"
:close-on-esc="true"
>
<n-drawer-content :native-scrollbar="false" :body-content-style="{ padding: '0' }">
<template #header>
<div class="flex">
<UserCenter />
<div class="ml-auto" />
<Search />
<Notices />
</div>
</template>
<slot />
<template #footer>
<DarkModeSwitch />
<LangsSwitch />
<div class="ml-auto" />
<Setting />
</template>
</n-drawer-content>
</n-drawer>
</template>

View File

@ -1,7 +1,8 @@
<script setup lang="ts"> <script setup lang="ts">
import { useBoolean } from '@/hooks' import { useBoolean } from '@/hooks'
import { useRouteStore } from '@/store' import { useAppStore, useRouteStore } from '@/store'
const appStore = useAppStore()
const routeStore = useRouteStore() const routeStore = useRouteStore()
// //
@ -143,13 +144,14 @@ function handleMouseEnter(index: number) {
<template> <template>
<CommonWrapper @click="openModal"> <CommonWrapper @click="openModal">
<icon-park-outline-search /><n-tag round size="small" class="font-mono cursor-pointer"> <icon-park-outline-search />
<n-tag v-if="!appStore.isMobile" round size="small" class="font-mono cursor-pointer">
CtrlK CtrlK
</n-tag> </n-tag>
</CommonWrapper> </CommonWrapper>
<n-modal <n-modal
v-model:show="showModal" v-model:show="showModal"
class="w-560px fixed top-60px inset-x-0" class="w-560px fixed top-60px inset-x-0 max-w-full"
size="small" size="small"
preset="card" preset="card"
:segmented="{ :segmented="{

View File

@ -2,6 +2,7 @@ import BackTop from './common/BackTop.vue'
import Setting from './common/Setting.vue' import Setting from './common/Setting.vue'
import SettingDrawer from './common/SettingDrawer.vue' import SettingDrawer from './common/SettingDrawer.vue'
import Logo from './common/Logo.vue' import Logo from './common/Logo.vue'
import MobileDrawer from './common/MobileDrawer.vue'
import Breadcrumb from './header/Breadcrumb.vue' import Breadcrumb from './header/Breadcrumb.vue'
import CollapaseButton from './header/CollapaseButton.vue' import CollapaseButton from './header/CollapaseButton.vue'
@ -18,6 +19,7 @@ export {
CollapaseButton, CollapaseButton,
FullScreen, FullScreen,
Logo, Logo,
MobileDrawer,
Notices, Notices,
Search, Search,
Setting, Setting,

View File

@ -20,7 +20,7 @@ function exitFullContent() {
</script> </script>
<template> <template>
<n-tooltip placement="bottom" trigger="hover"> <n-tooltip v-if="!appStore.isMobile" placement="bottom" trigger="hover">
<template #trigger> <template #trigger>
<CommonWrapper @click="enterFullContent"> <CommonWrapper @click="enterFullContent">
<icon-park-outline-full-screen-one /> <icon-park-outline-full-screen-one />

View File

@ -6,6 +6,7 @@ import {
CollapaseButton, CollapaseButton,
FullScreen, FullScreen,
Logo, Logo,
MobileDrawer,
Notices, Notices,
Search, Search,
Setting, Setting,
@ -14,12 +15,12 @@ import {
UserCenter, UserCenter,
} from './components' } from './components'
import Content from './Content.vue' import Content from './Content.vue'
import { ProLayout, useLayoutMenu } from 'pro-naive-ui' import { ProLayout, useLayoutMenu } from 'pro-naive-ui'
const route = useRoute() const route = useRoute()
const appStore = useAppStore() const appStore = useAppStore()
const routeStore = useRouteStore() const routeStore = useRouteStore()
const { layoutMode } = storeToRefs(useAppStore()) const { layoutMode } = storeToRefs(useAppStore())
const { const {
@ -31,16 +32,19 @@ const {
menus: routeStore.menus, menus: routeStore.menus,
}) })
watch(() => route.path, (value) => { watch(() => route.path, (value: string) => {
activeKey.value = value activeKey.value = value
}, { immediate: true }) }, { immediate: true })
//
const showMobileDrawer = ref(false)
const sidebarWidth = ref(240) const sidebarWidth = ref(240)
const sidebarCollapsedWidth = ref(64) const sidebarCollapsedWidth = ref(64)
const hasHorizontalMenu = computed(() => ['horizontal', 'mixed-two-column', 'mixed-sidebar'].includes(layoutMode.value)) const hasHorizontalMenu = computed(() => ['horizontal', 'mixed-two-column', 'mixed-sidebar'].includes(layoutMode.value))
const hidenCollapaseButton = computed(() => ['horizontal'].includes(layoutMode.value)) const hidenCollapaseButton = computed(() => ['horizontal'].includes(layoutMode.value) || appStore.isMobile)
</script> </script>
<template> <template>
@ -48,7 +52,8 @@ const hidenCollapaseButton = computed(() => ['horizontal'].includes(layoutMode.v
<ProLayout <ProLayout
v-model:collapsed="appStore.collapsed" v-model:collapsed="appStore.collapsed"
:mode="layoutMode" :mode="layoutMode"
:show-logo="appStore.showLogo" :is-mobile="appStore.isMobile"
:show-logo="appStore.showLogo && !appStore.isMobile"
:show-footer="appStore.showFooter" :show-footer="appStore.showFooter"
:show-tabbar="appStore.showTabs" :show-tabbar="appStore.showTabs"
nav-fixed nav-fixed
@ -79,6 +84,22 @@ const hidenCollapaseButton = computed(() => ['horizontal'].includes(layoutMode.v
<template #nav-right> <template #nav-right>
<div class="h-full flex-y-center gap-1 p-x-xl"> <div class="h-full flex-y-center gap-1 p-x-xl">
<!-- 移动端只显示菜单按钮 -->
<template v-if="appStore.isMobile">
<n-button
quaternary
@click="showMobileDrawer = true"
>
<template #icon>
<n-icon size="18">
<icon-park-outline-hamburger-button />
</n-icon>
</template>
</n-button>
</template>
<!-- 桌面端显示完整功能组件 -->
<template v-else>
<Search /> <Search />
<Notices /> <Notices />
<FullScreen /> <FullScreen />
@ -86,6 +107,7 @@ const hidenCollapaseButton = computed(() => ['horizontal'].includes(layoutMode.v
<LangsSwitch /> <LangsSwitch />
<Setting /> <Setting />
<UserCenter /> <UserCenter />
</template>
</div> </div>
</template> </template>
@ -111,5 +133,10 @@ const hidenCollapaseButton = computed(() => ['horizontal'].includes(layoutMode.v
<Content /> <Content />
<BackTop /> <BackTop />
<SettingDrawer /> <SettingDrawer />
<!-- 移动端功能抽屉 -->
<MobileDrawer v-model:show="showMobileDrawer">
<n-menu v-bind="layout.verticalMenuProps" />
</MobileDrawer>
</ProLayout> </ProLayout>
</template> </template>

View File

@ -17,6 +17,8 @@ const { system, store } = useColorMode({
emitAuto: true, emitAuto: true,
}) })
const isMobile = useMediaQuery('(max-width: 700px)')
export const useAppStore = defineStore('app-store', { export const useAppStore = defineStore('app-store', {
state: () => { state: () => {
return { return {
@ -50,6 +52,9 @@ export const useAppStore = defineStore('app-store', {
fullScreen() { fullScreen() {
return isFullscreen.value return isFullscreen.value
}, },
isMobile() {
return isMobile.value
},
}, },
actions: { actions: {
// 重置所有设置 // 重置所有设置

View File

@ -1,8 +1,11 @@
<script setup lang="ts"> <script setup lang="ts">
import { useAppStore } from '@/store'
import Chart from './components/chart.vue' import Chart from './components/chart.vue'
import Chart2 from './components/chart2.vue' import Chart2 from './components/chart2.vue'
import Chart3 from './components/chart3.vue' import Chart3 from './components/chart3.vue'
const appStore = useAppStore()
const tableData = [ const tableData = [
{ {
id: 0, id: 0,
@ -32,12 +35,15 @@ const tableData = [
</script> </script>
<template> <template>
<div>
<n-grid <n-grid
:x-gap="16" :x-gap="16"
:y-gap="16" :y-gap="16"
:cols="12"
item-responsive
responsive="screen"
> >
<n-gi :span="6"> <!-- 统计卡片 - 移动端每行2个桌面端每行4个 -->
<n-gi span="6 m:3">
<n-card> <n-card>
<n-space <n-space
justify="space-between" justify="space-between"
@ -69,7 +75,7 @@ const tableData = [
</template> </template>
</n-card> </n-card>
</n-gi> </n-gi>
<n-gi :span="6"> <n-gi span="6 m:3">
<n-card> <n-card>
<n-space <n-space
justify="space-between" justify="space-between"
@ -101,7 +107,7 @@ const tableData = [
</template> </template>
</n-card> </n-card>
</n-gi> </n-gi>
<n-gi :span="6"> <n-gi span="6 m:3">
<n-card> <n-card>
<n-space <n-space
justify="space-between" justify="space-between"
@ -133,7 +139,7 @@ const tableData = [
</template> </template>
</n-card> </n-card>
</n-gi> </n-gi>
<n-gi :span="6"> <n-gi span="6 m:3">
<n-card> <n-card>
<n-space <n-space
justify="space-between" justify="space-between"
@ -165,7 +171,8 @@ const tableData = [
</template> </template>
</n-card> </n-card>
</n-gi> </n-gi>
<n-gi :span="24"> <!-- 图表区域 - 全宽显示 -->
<n-gi :span="12">
<n-card content-style="padding: 0;"> <n-card content-style="padding: 0;">
<n-tabs <n-tabs
type="line" type="line"
@ -182,7 +189,9 @@ const tableData = [
</n-tabs> </n-tabs>
</n-card> </n-card>
</n-gi> </n-gi>
<n-gi :span="8">
<!-- 访问来源 - 移动端全宽桌面端1/3 -->
<n-gi span="12 m:4">
<n-card <n-card
title="访问来源" title="访问来源"
:segmented="{ :segmented="{
@ -192,7 +201,9 @@ const tableData = [
<Chart3 /> <Chart3 />
</n-card> </n-card>
</n-gi> </n-gi>
<n-gi :span="16">
<!-- 成交记录 - 移动端全宽桌面端2/3 -->
<n-gi span="12 m:8">
<n-card <n-card
title="成交记录" title="成交记录"
:segmented="{ :segmented="{
@ -210,6 +221,7 @@ const tableData = [
<n-table <n-table
:bordered="false" :bordered="false"
:single-line="false" :single-line="false"
:scroll-x="appStore.isMobile ? 600 : undefined"
> >
<thead> <thead>
<tr> <tr>
@ -243,7 +255,4 @@ const tableData = [
</n-card> </n-card>
</n-gi> </n-gi>
</n-grid> </n-grid>
</div>
</template> </template>
<style scoped></style>

View File

@ -9,21 +9,30 @@ const { userInfo } = useAuthStore()
<n-grid <n-grid
:x-gap="16" :x-gap="16"
:y-gap="16" :y-gap="16"
:cols="3"
item-responsive
responsive="screen"
> >
<n-gi :span="16"> <!-- 左侧主要内容区 - 移动端全宽桌面端2/3 -->
<n-gi span="3 m:2">
<n-space <n-space
vertical vertical
:size="16" :size="16"
> >
<!-- 图表区域 -->
<n-card style="--n-padding-left: 0;"> <n-card style="--n-padding-left: 0;">
<Chart /> <Chart />
</n-card> </n-card>
<n-card>
<!-- 统计卡片区域 -->
<n-grid <n-grid
:x-gap="8" :x-gap="16"
:y-gap="8" :y-gap="16"
:cols="4"
item-responsive
responsive="screen"
> >
<n-gi :span="6"> <n-gi span="2 l:1">
<n-card> <n-card>
<n-thing> <n-thing>
<template #avatar> <template #avatar>
@ -41,7 +50,7 @@ const { userInfo } = useAuthStore()
</n-thing> </n-thing>
</n-card> </n-card>
</n-gi> </n-gi>
<n-gi :span="6"> <n-gi span="2 l:1">
<n-card> <n-card>
<n-thing> <n-thing>
<template #avatar> <template #avatar>
@ -59,7 +68,7 @@ const { userInfo } = useAuthStore()
</n-thing> </n-thing>
</n-card> </n-card>
</n-gi> </n-gi>
<n-gi :span="6"> <n-gi span="2 l:1">
<n-card> <n-card>
<n-thing> <n-thing>
<template #avatar> <template #avatar>
@ -77,7 +86,7 @@ const { userInfo } = useAuthStore()
</n-thing> </n-thing>
</n-card> </n-card>
</n-gi> </n-gi>
<n-gi :span="6"> <n-gi span="2 l:1">
<n-card> <n-card>
<n-thing> <n-thing>
<template #avatar> <template #avatar>
@ -96,7 +105,6 @@ const { userInfo } = useAuthStore()
</n-card> </n-card>
</n-gi> </n-gi>
</n-grid> </n-grid>
</n-card>
<n-card title="动态"> <n-card title="动态">
<template #header-extra> <template #header-extra>
<n-button <n-button
@ -167,7 +175,9 @@ const { userInfo } = useAuthStore()
</n-card> </n-card>
</n-space> </n-space>
</n-gi> </n-gi>
<n-gi :span="8">
<!-- 右侧边栏 - 移动端全宽桌面端1/3 -->
<n-gi span="3 m:1">
<n-space <n-space
vertical vertical
:size="16" :size="16"
@ -226,11 +236,14 @@ const { userInfo } = useAuthStore()
</n-list-item> </n-list-item>
</n-list> </n-list>
</n-card> </n-card>
<!-- 订单和待办统计 -->
<n-grid <n-grid
:x-gap="8" :x-gap="16"
:y-gap="8" :y-gap="16"
:cols="2"
> >
<n-gi :span="12"> <!-- 移动端和桌面端都是每行2个 -->
<n-gi :span="1">
<n-card> <n-card>
<n-flex vertical align="center"> <n-flex vertical align="center">
<n-text depth="3"> <n-text depth="3">
@ -245,7 +258,7 @@ const { userInfo } = useAuthStore()
</n-flex> </n-flex>
</n-card> </n-card>
</n-gi> </n-gi>
<n-gi :span="12"> <n-gi :span="1">
<n-card> <n-card>
<n-flex vertical align="center"> <n-flex vertical align="center">
<n-text depth="3"> <n-text depth="3">

View File

@ -1,7 +1,7 @@
<script setup lang="ts"> <script setup lang="ts">
import { Login, Register, ResetPwd } from './components' import { Login, Register, ResetPwd } from './components'
type IformType = 'login' | 'register' | 'resetPwd' type IformType = 'login' | 'register' | 'resetPwd'
const formType: Ref<IformType> = ref('login') const formType: Ref<IformType> = ref('login')
const formComponets = { const formComponets = {
login: Login, login: Login,
@ -18,7 +18,7 @@ const appName = import.meta.env.VITE_APP_NAME
<DarkModeSwitch /> <DarkModeSwitch />
<LangsSwitch /> <LangsSwitch />
</div> </div>
<n-el <div
class="p-4xl h-full w-full sm:w-450px sm:h-unset" class="p-4xl h-full w-full sm:w-450px sm:h-unset"
style="background: var(--card-color);box-shadow: var(--box-shadow-1);" style="background: var(--card-color);box-shadow: var(--box-shadow-1);"
> >
@ -36,7 +36,7 @@ const appName = import.meta.env.VITE_APP_NAME
/> />
</transition> </transition>
</div> </div>
</n-el> </div>
<div /> <div />
</n-el> </n-el>