From 31d4ed76f0d0abbe5d96d1637ecfa2ff0e6432bf Mon Sep 17 00:00:00 2001 From: zhonghua Date: Mon, 2 Mar 2026 14:01:51 +0800 Subject: [PATCH] =?UTF-8?q?feat(frontend):=20=E5=93=8D=E5=BA=94=E5=BC=8F?= =?UTF-8?q?=E5=B8=83=E5=B1=80=E4=B8=8E=E7=A7=BB=E5=8A=A8=E7=AB=AF=E9=80=82?= =?UTF-8?q?=E9=85=8D=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1) 新增 useBreakpoints 统一断点管理;2) 管理/教师/园校/家长端布局支持移动端抽屉菜单与顶部导航;3) 全局 html/body/#app overflow 与 safe-area 处理,避免横向滚动和刘海遮挡;4) 各端内容区仅内部滚动,提升大屏与小屏的浏览体验 Made-with: Cursor --- reading-platform-frontend/index.html | 2 +- reading-platform-frontend/src/App.vue | 20 +- reading-platform-frontend/src/components.d.ts | 44 ----- .../src/composables/useBreakpoints.ts | 47 +++++ .../src/views/admin/LayoutView.vue | 187 ++++++++++++++++-- .../src/views/parent/LayoutView.vue | 23 +-- .../src/views/school/LayoutView.vue | 141 ++++++++++++- .../src/views/teacher/LayoutView.vue | 117 ++++++++++- 8 files changed, 496 insertions(+), 85 deletions(-) create mode 100644 reading-platform-frontend/src/composables/useBreakpoints.ts diff --git a/reading-platform-frontend/index.html b/reading-platform-frontend/index.html index eb354c7..2ab6f25 100644 --- a/reading-platform-frontend/index.html +++ b/reading-platform-frontend/index.html @@ -3,7 +3,7 @@ - + 幼儿阅读教学服务平台 diff --git a/reading-platform-frontend/src/App.vue b/reading-platform-frontend/src/App.vue index b34dbbc..716ba23 100644 --- a/reading-platform-frontend/src/App.vue +++ b/reading-platform-frontend/src/App.vue @@ -12,16 +12,26 @@ const AConfigProvider = ConfigProvider; diff --git a/reading-platform-frontend/src/components.d.ts b/reading-platform-frontend/src/components.d.ts index fd768ff..06b8fad 100644 --- a/reading-platform-frontend/src/components.d.ts +++ b/reading-platform-frontend/src/components.d.ts @@ -7,75 +7,31 @@ export {} declare module 'vue' { export interface GlobalComponents { - AAlert: typeof import('ant-design-vue/es')['Alert'] AAvatar: typeof import('ant-design-vue/es')['Avatar'] ABadge: typeof import('ant-design-vue/es')['Badge'] AButton: typeof import('ant-design-vue/es')['Button'] - AButtonGroup: typeof import('ant-design-vue/es')['ButtonGroup'] ACard: typeof import('ant-design-vue/es')['Card'] - ACheckbox: typeof import('ant-design-vue/es')['Checkbox'] - ACheckboxGroup: typeof import('ant-design-vue/es')['CheckboxGroup'] ACol: typeof import('ant-design-vue/es')['Col'] - ACollapse: typeof import('ant-design-vue/es')['Collapse'] - ACollapsePanel: typeof import('ant-design-vue/es')['CollapsePanel'] - ADatePicker: typeof import('ant-design-vue/es')['DatePicker'] - ADescriptions: typeof import('ant-design-vue/es')['Descriptions'] - ADescriptionsItem: typeof import('ant-design-vue/es')['DescriptionsItem'] - ADivider: typeof import('ant-design-vue/es')['Divider'] ADrawer: typeof import('ant-design-vue/es')['Drawer'] ADropdown: typeof import('ant-design-vue/es')['Dropdown'] AEmpty: typeof import('ant-design-vue/es')['Empty'] AForm: typeof import('ant-design-vue/es')['Form'] AFormItem: typeof import('ant-design-vue/es')['FormItem'] - AImage: typeof import('ant-design-vue/es')['Image'] - AImagePreviewGroup: typeof import('ant-design-vue/es')['ImagePreviewGroup'] AInput: typeof import('ant-design-vue/es')['Input'] - AInputNumber: typeof import('ant-design-vue/es')['InputNumber'] AInputPassword: typeof import('ant-design-vue/es')['InputPassword'] - AInputSearch: typeof import('ant-design-vue/es')['InputSearch'] ALayout: typeof import('ant-design-vue/es')['Layout'] ALayoutContent: typeof import('ant-design-vue/es')['LayoutContent'] ALayoutHeader: typeof import('ant-design-vue/es')['LayoutHeader'] ALayoutSider: typeof import('ant-design-vue/es')['LayoutSider'] - AList: typeof import('ant-design-vue/es')['List'] - AListItem: typeof import('ant-design-vue/es')['ListItem'] - AListItemMeta: typeof import('ant-design-vue/es')['ListItemMeta'] AMenu: typeof import('ant-design-vue/es')['Menu'] AMenuDivider: typeof import('ant-design-vue/es')['MenuDivider'] AMenuItem: typeof import('ant-design-vue/es')['MenuItem'] - AModal: typeof import('ant-design-vue/es')['Modal'] - APageHeader: typeof import('ant-design-vue/es')['PageHeader'] - APagination: typeof import('ant-design-vue/es')['Pagination'] - APopconfirm: typeof import('ant-design-vue/es')['Popconfirm'] - AProgress: typeof import('ant-design-vue/es')['Progress'] - ARadio: typeof import('ant-design-vue/es')['Radio'] - ARadioButton: typeof import('ant-design-vue/es')['RadioButton'] - ARadioGroup: typeof import('ant-design-vue/es')['RadioGroup'] ARangePicker: typeof import('ant-design-vue/es')['RangePicker'] - ARate: typeof import('ant-design-vue/es')['Rate'] - AResult: typeof import('ant-design-vue/es')['Result'] ARow: typeof import('ant-design-vue/es')['Row'] - ASelect: typeof import('ant-design-vue/es')['Select'] - ASelectOptGroup: typeof import('ant-design-vue/es')['SelectOptGroup'] - ASelectOption: typeof import('ant-design-vue/es')['SelectOption'] - ASkeleton: typeof import('ant-design-vue/es')['Skeleton'] ASpace: typeof import('ant-design-vue/es')['Space'] ASpin: typeof import('ant-design-vue/es')['Spin'] - AStatistic: typeof import('ant-design-vue/es')['Statistic'] - AStep: typeof import('ant-design-vue/es')['Step'] - ASteps: typeof import('ant-design-vue/es')['Steps'] ASubMenu: typeof import('ant-design-vue/es')['SubMenu'] - ASwitch: typeof import('ant-design-vue/es')['Switch'] - ATable: typeof import('ant-design-vue/es')['Table'] - ATabPane: typeof import('ant-design-vue/es')['TabPane'] - ATabs: typeof import('ant-design-vue/es')['Tabs'] ATag: typeof import('ant-design-vue/es')['Tag'] - ATextarea: typeof import('ant-design-vue/es')['Textarea'] - ATimeRangePicker: typeof import('ant-design-vue/es')['TimeRangePicker'] - ATooltip: typeof import('ant-design-vue/es')['Tooltip'] - ATypographyText: typeof import('ant-design-vue/es')['TypographyText'] - AUpload: typeof import('ant-design-vue/es')['Upload'] - AUploadDragger: typeof import('ant-design-vue/es')['UploadDragger'] FilePreviewModal: typeof import('./components/FilePreviewModal.vue')['default'] FileUploader: typeof import('./components/course/FileUploader.vue')['default'] LessonConfigPanel: typeof import('./components/course/LessonConfigPanel.vue')['default'] diff --git a/reading-platform-frontend/src/composables/useBreakpoints.ts b/reading-platform-frontend/src/composables/useBreakpoints.ts new file mode 100644 index 0000000..32b5552 --- /dev/null +++ b/reading-platform-frontend/src/composables/useBreakpoints.ts @@ -0,0 +1,47 @@ +import { ref, onMounted, onUnmounted } from 'vue'; + +/** 统一断点(与 CSS 媒体查询一致) */ +export const BREAKPOINTS = { + /** 手机竖屏 */ + MOBILE: 768, + /** 平板 / 小屏 */ + TABLET: 1024, + /** 桌面 */ + DESKTOP: 1200, +} as const; + +export type BreakpointKey = keyof typeof BREAKPOINTS; + +/** + * 响应式断点:根据窗口宽度判断 isMobile / isTablet / isDesktop, + * 并随 resize 更新。全项目统一使用 768 为移动端分界。 + */ +export function useBreakpoints() { + const width = ref(typeof window !== 'undefined' ? window.innerWidth : 1024); + + const isMobile = ref(width.value < BREAKPOINTS.MOBILE); + const isTablet = ref( + width.value >= BREAKPOINTS.MOBILE && width.value < BREAKPOINTS.TABLET + ); + const isDesktop = ref(width.value >= BREAKPOINTS.TABLET); + + const update = () => { + if (typeof window === 'undefined') return; + const w = window.innerWidth; + width.value = w; + isMobile.value = w < BREAKPOINTS.MOBILE; + isTablet.value = w >= BREAKPOINTS.MOBILE && w < BREAKPOINTS.TABLET; + isDesktop.value = w >= BREAKPOINTS.TABLET; + }; + + onMounted(() => { + update(); + window.addEventListener('resize', update); + }); + + onUnmounted(() => { + window.removeEventListener('resize', update); + }); + + return { width, isMobile, isTablet, isDesktop, BREAKPOINTS }; +} diff --git a/reading-platform-frontend/src/views/admin/LayoutView.vue b/reading-platform-frontend/src/views/admin/LayoutView.vue index af809e2..35b0374 100644 --- a/reading-platform-frontend/src/views/admin/LayoutView.vue +++ b/reading-platform-frontend/src/views/admin/LayoutView.vue @@ -1,6 +1,8 @@ diff --git a/reading-platform-frontend/src/views/teacher/LayoutView.vue b/reading-platform-frontend/src/views/teacher/LayoutView.vue index 2ad6891..daccfb6 100644 --- a/reading-platform-frontend/src/views/teacher/LayoutView.vue +++ b/reading-platform-frontend/src/views/teacher/LayoutView.vue @@ -1,6 +1,7 @@