From 61ef0c12140d436098ab1e1f031d5918ef1a8307 Mon Sep 17 00:00:00 2001 From: zhaichao Date: Sun, 23 Aug 2026 15:36:11 +0800 Subject: [PATCH] =?UTF-8?q?=E5=85=A8=E5=B1=80=E8=87=AA=E9=80=82=E5=BA=94?= =?UTF-8?q?=E4=BB=A5=E5=8F=8A=E8=A1=A8=E6=A0=BC=E6=A8=AA=E5=90=91=E6=8B=96?= =?UTF-8?q?=E5=8A=A8=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- frontend/src/assets/styles/index.scss | 33 ++++++++++++++- frontend/src/assets/styles/roomroot.scss | 21 +++++++++- frontend/src/directive/index.js | 5 ++- frontend/src/main.js | 6 ++- frontend/src/utils/adaptive.js | 52 ++++++++++++++++++++++++ 5 files changed, 113 insertions(+), 4 deletions(-) create mode 100644 frontend/src/utils/adaptive.js diff --git a/frontend/src/assets/styles/index.scss b/frontend/src/assets/styles/index.scss index c5090295..c2df7654 100644 --- a/frontend/src/assets/styles/index.scss +++ b/frontend/src/assets/styles/index.scss @@ -1,4 +1,4 @@ -@import './variables.scss'; +@import './variables.scss'; @import './mixin.scss'; @import './transition.scss'; @import './element-ui.scss'; @@ -41,6 +41,37 @@ html { height: 100%; } +/* ================= 全局自适应加固 ================= + 与 src/utils/adaptive.js 的视口等比缩放配合, + 兜底避免极端情况下出现页面级横向溢出与内容崩塌。 */ +html, +body { + width: 100%; +} + +body { + /* 禁止页面级横向滚动,防止因个别固定宽度元素把整页撑破; + 表格自身的横向滚动由 el-table 内部滚动容器承担,不受影响 */ + overflow-x: hidden; +} + +/* 弹性布局子项允许收缩,避免 flex 容器被子项固有宽度撑破 */ +#app, +.app-wrapper, +.main-container, +.lower-container { + min-width: 0; +} + +/* 通用长文本换行,避免 url/数字/英文长串将布局撑破 */ +a, +.el-button, +.el-table .cell, +.el-form-item__content, +.el-form-item__label { + word-break: break-word; +} + *, *:before, *:after { diff --git a/frontend/src/assets/styles/roomroot.scss b/frontend/src/assets/styles/roomroot.scss index bfc3a0c8..d05468dc 100644 --- a/frontend/src/assets/styles/roomroot.scss +++ b/frontend/src/assets/styles/roomroot.scss @@ -1,4 +1,4 @@ -/** +/** * 通用css样式布局处理 * Copyright (c) 2019 roomroot */ @@ -93,6 +93,11 @@ } .el-table { + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + .el-table__header-wrapper, .el-table__fixed-header-wrapper { th { word-break: break-word; @@ -108,6 +113,20 @@ margin-left: 1px; } } + + // 表格横向拖拽滚动:拖拽过程中光标切换为“抓手” + .el-table__body-wrapper.is-dragging, + .el-table__fixed-body-wrapper.is-dragging { + cursor: grabbing; + } +} + +/* 原生表格同样禁止选中文本,避免横向滑动/滚动时误触发复制前的文本选中 */ +table { + -webkit-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; } /** 表单布局 **/ diff --git a/frontend/src/directive/index.js b/frontend/src/directive/index.js index f51595dd..b05a40e5 100644 --- a/frontend/src/directive/index.js +++ b/frontend/src/directive/index.js @@ -1,9 +1,10 @@ -import hasRole from './permission/hasRole' +import hasRole from './permission/hasRole' import hasPermi from './permission/hasPermi' import dialogDrag from './dialog/drag' import dialogDragWidth from './dialog/dragWidth' import dialogDragHeight from './dialog/dragHeight' import clipboard from './module/clipboard' +import tableDrag from './tableDrag' const install = function(Vue) { Vue.directive('hasRole', hasRole) @@ -12,6 +13,8 @@ const install = function(Vue) { Vue.directive('dialogDrag', dialogDrag) Vue.directive('dialogDragWidth', dialogDragWidth) Vue.directive('dialogDragHeight', dialogDragHeight) + // 全局表格横向拖拽滚动:通过 document 事件委托自动作用于所有 el-table + tableDrag() } if (window.Vue) { diff --git a/frontend/src/main.js b/frontend/src/main.js index da778b9c..bcb172b5 100644 --- a/frontend/src/main.js +++ b/frontend/src/main.js @@ -1,4 +1,4 @@ -import Vue from 'vue' +import Vue from 'vue' import Cookies from 'js-cookie' @@ -13,6 +13,10 @@ import router from './router' import directive from './directive' // directive import plugins from './plugins' // plugins import { download } from '@/utils/request' +import { initAdaptive } from '@/utils/adaptive' // 全局视口等比缩放自适应 + +// 初始化全局自适应,保障不同大小屏幕下页面布局不崩塌 +initAdaptive() import './assets/icons' // icon import './permission' // permission control diff --git a/frontend/src/utils/adaptive.js b/frontend/src/utils/adaptive.js new file mode 100644 index 00000000..827b5dfb --- /dev/null +++ b/frontend/src/utils/adaptive.js @@ -0,0 +1,52 @@ +/** + * 全局视口等比缩放 —— 让整个系统在不同大小屏幕(桌面 1280~1920)下布局不崩塌。 + * + * 原理: + * 以 DESIGN_WIDTH 为设计基准宽度(视觉稿基准)。当视口宽度小于该值时, + * 对整个文档根节点 应用 zoom = 视口宽度 / DESIGN_WIDTH(不超过 1), + * 使页面内部所有元素(含 Element UI 挂载到 body 的弹窗/提示/下拉等)等比缩小, + * 从而在保持原有表格/表单固定布局不塌陷的前提下,整体适配到当前屏幕。 + * + * 说明: + * 1. 仅在“需要缩小”时写入 zoom,宽度 ≥ 设计宽时清除,保证大屏原生 1:1 显示; + * 2. zoom 挂在 documentElement 上,能覆盖包括 Element 弹层在内的所有内容; + * 3. modern Chromium / Edge / Safari 及 Firefox(≥126) 均支持 zoom。 + */ + +let initialized = false + +export const DEFAULT_DESIGN_WIDTH = 1920 + +/** + * 初始化全局自适应。可在任意入口调用,重复调用幂等。 + * @param {number} [designWidth] 设计基准宽度,默认 1920 + */ +export function initAdaptive(designWidth = DEFAULT_DESIGN_WIDTH) { + if (initialized) { + return + } + initialized = true + + let timer = null + + const applyScale = () => { + const root = document.documentElement + const width = (root && root.clientWidth) || window.innerWidth + const scale = Math.min(1, width / designWidth) + // 小于设计宽才缩放,否则清除,避免触发整页缩放动画/重置 + if (root) { + root.style.zoom = scale < 1 ? String(scale) : '' + } + } + + const onResize = () => { + // 简单防抖,避免 resize 高频触发引起抖动 + if (timer) { + clearTimeout(timer) + } + timer = setTimeout(applyScale, 100) + } + + applyScale() + window.addEventListener('resize', onResize) +} \ No newline at end of file