diff --git a/code/fms/fms-vue/package.json b/code/fms/fms-vue/package.json index b7e90418..5341dc6a 100644 --- a/code/fms/fms-vue/package.json +++ b/code/fms/fms-vue/package.json @@ -24,6 +24,7 @@ "axios": "^1.19.0", "dayjs": "^1.11.21", "nprogress": "^0.2.0", + "overlayscrollbars": "^2.16.0", "pinia": "^4.0.3", "pinia-plugin-persistedstate": "^4.7.1", "sortablejs": "^1.15.7", diff --git a/code/fms/fms-vue/pnpm-lock.yaml b/code/fms/fms-vue/pnpm-lock.yaml index 259ec618..585df71c 100644 --- a/code/fms/fms-vue/pnpm-lock.yaml +++ b/code/fms/fms-vue/pnpm-lock.yaml @@ -23,6 +23,9 @@ importers: nprogress: specifier: ^0.2.0 version: 0.2.0 + overlayscrollbars: + specifier: ^2.16.0 + version: 2.16.0 pinia: specifier: ^4.0.3 version: 4.0.3(@vue/devtools-api@8.2.1)(vue@3.5.41) @@ -1256,6 +1259,9 @@ packages: resolution: {integrity: sha512-4a+OsYv9UktOJKE+l1A4OufDgdRF9PifWj+tJnHURo/P+WOxpG4GzUFL9qCalmWauao6ogiG+QvnCovwPoyAWA==} engines: {node: '>=12.20.0'} + overlayscrollbars@2.16.0: + resolution: {integrity: sha512-N03oje/q7j93D0aLZtoCdsDSYLmhheSsv8H7oSLE7HhdV9P/bmCURtLV/KbPye7P/bpfyt/obSfDpGUYoJ0OWg==} + oxfmt@0.63.0: resolution: {integrity: sha512-kgdDwv35wvVf6554U2Ab8Jnd0zTM+TsEQWwaB70RAjK3gICFAFGO+2Hd3Be27GMoXj3XRL9IKSNRVl7KBQL6iw==} engines: {node: ^20.19.0 || >=22.12.0} @@ -2656,6 +2662,8 @@ snapshots: obug@2.1.4: {} + overlayscrollbars@2.16.0: {} + oxfmt@0.63.0: dependencies: tinypool: 2.1.0 diff --git a/code/fms/fms-vue/src/components/ui/input/index.scss b/code/fms/fms-vue/src/components/ui/input/index.scss index 0b551572..f72abf60 100644 --- a/code/fms/fms-vue/src/components/ui/input/index.scss +++ b/code/fms/fms-vue/src/components/ui/input/index.scss @@ -28,6 +28,8 @@ .input-wrapper.input-focused { border-color: var(--fms-primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--fms-primary) 20%, transparent); + /* 聚焦光环向外扩散 3px,提升层级避免被相邻表单项/后续兄弟元素遮挡 */ + z-index: 1; } /* 多行模式:紧凑的垂直布局,footer 只保留计数和手柄需要的空间 */ diff --git a/code/fms/fms-vue/src/components/ui/scrollbar/scrollbar.vue b/code/fms/fms-vue/src/components/ui/scrollbar/scrollbar.vue new file mode 100644 index 00000000..16d92bab --- /dev/null +++ b/code/fms/fms-vue/src/components/ui/scrollbar/scrollbar.vue @@ -0,0 +1,68 @@ + + + diff --git a/code/fms/fms-vue/src/components/ui/select/index.scss b/code/fms/fms-vue/src/components/ui/select/index.scss index 87efe5c3..bcaf024e 100644 --- a/code/fms/fms-vue/src/components/ui/select/index.scss +++ b/code/fms/fms-vue/src/components/ui/select/index.scss @@ -28,6 +28,8 @@ font-size: var(--fms-font-size); line-height: var(--fms-line-height); cursor: pointer; + position: relative; + z-index: 0; transition: border-color 150ms ease, box-shadow 150ms ease; @@ -46,6 +48,8 @@ .select-trigger-focused { border-color: var(--fms-primary); box-shadow: 0 0 0 3px color-mix(in srgb, var(--fms-primary) 20%, transparent); + /* 聚焦光环向外扩散 3px,提升层级避免被相邻表单项/后续兄弟元素遮挡 */ + z-index: 1; } /* 触发器输入框:透明内嵌,撑满框体;筛选直接在输入框内键入 */ diff --git a/code/fms/fms-vue/src/components/ui/tabs/index.scss b/code/fms/fms-vue/src/components/ui/tabs/index.scss index 08b5e90a..f5d38862 100644 --- a/code/fms/fms-vue/src/components/ui/tabs/index.scss +++ b/code/fms/fms-vue/src/components/ui/tabs/index.scss @@ -30,8 +30,9 @@ flex-shrink: 0; } -/* tab 列表:承担方向排列、间距与滚动,flex: 1 占满剩余空间, - * 将 leftExtra / rightExtra 推到标签栏两端而非紧贴标签 */ +/* tab 列表:承担方向排列、间距,flex: 1 占满剩余空间, + * 将 leftExtra / rightExtra 推到标签栏两端而非紧贴标签; + * 滚动由外层 Scrollbar 组件(.tabs-nav-scroll)接管,列表本身不设 overflow */ .tabs-nav-list { display: flex; flex: 1 1 auto; @@ -39,10 +40,20 @@ min-height: 0; } +/* Scrollbar 容器:占满 nav 剩余空间,由组件自绘悬浮滚动条 */ +.tabs-nav-scroll { + flex: 1 1 auto; + min-width: 0; + min-height: 0; +} + .tabs-top .tabs-nav { flex-direction: row; align-items: flex-end; - overflow-x: auto; +} + +/* line 类型有底部分割线;pill 类型不需要——胶囊背景已足够区分 */ +.tabs-line.tabs-top .tabs-nav { border-bottom: 1px solid var(--fms-border); } @@ -54,7 +65,6 @@ .tabs-left .tabs-nav { flex-direction: column; - overflow-y: auto; border-right: 1px solid var(--fms-border); } @@ -65,7 +75,6 @@ .tabs-right .tabs-nav { flex-direction: column; - overflow-y: auto; border-left: 1px solid var(--fms-border); } @@ -154,45 +163,69 @@ outline-offset: 2px; } -/* 激活:指示条 + 主色文字 + 字重,不只依赖颜色表达选中 */ +/* 激活:主色文字 + 字重,不只依赖颜色表达选中 */ .tabs-tab-active { color: var(--fms-primary); font-weight: 600; } +/* ==================== line 类型(默认):下划线指示条 ==================== */ /* line 指示条:top 为下边框,left/right 为侧边框,无需 JS 测量 */ -.tabs-top .tabs-tab { +.tabs-line.tabs-top .tabs-tab { border-bottom: 2px solid transparent; } -.tabs-top .tabs-tab-active { +.tabs-line.tabs-top .tabs-tab-active { border-bottom-color: var(--fms-primary); } -.tabs-left .tabs-tab, -.tabs-right .tabs-tab { +.tabs-line.tabs-left .tabs-tab, +.tabs-line.tabs-right .tabs-tab { border-top: none; border-bottom: none; } -.tabs-left .tabs-tab { +.tabs-line.tabs-left .tabs-tab { border-right: 2px solid transparent; } -.tabs-left .tabs-tab-active { +.tabs-line.tabs-left .tabs-tab-active { border-right-color: var(--fms-primary); background: var(--fms-secondary-hover); } -.tabs-right .tabs-tab { +.tabs-line.tabs-right .tabs-tab { border-left: 2px solid transparent; } -.tabs-right .tabs-tab-active { +.tabs-line.tabs-right .tabs-tab-active { border-left-color: var(--fms-primary); background: var(--fms-secondary-hover); } +/* ==================== pill 类型(胶囊):圆角背景式激活 ==================== */ +.tabs-pill .tabs-nav-list { + gap: 4px; +} + +.tabs-pill .tabs-tab { + border-radius: 999px; + padding: 0 12px; + transition: + color 150ms ease, + background 150ms ease; +} + +.tabs-pill .tabs-tab:hover:not(:disabled) { + background: var(--fms-secondary); +} + +.tabs-pill .tabs-tab-active { + background: color-mix(in srgb, var(--fms-primary) 10%, transparent); + border-bottom: none; + font-weight: 500; +} + /* 禁用:降透明 + not-allowed(原生 disabled 同时保证不可交互) */ .tabs-tab:disabled { color: var(--fms-disabled-text); diff --git a/code/fms/fms-vue/src/components/ui/tabs/tabs.vue b/code/fms/fms-vue/src/components/ui/tabs/tabs.vue index 7c449f0f..88645687 100644 --- a/code/fms/fms-vue/src/components/ui/tabs/tabs.vue +++ b/code/fms/fms-vue/src/components/ui/tabs/tabs.vue @@ -1,5 +1,6 @@ @@ -1518,11 +1475,7 @@ onBeforeUnmount(() => { -
+