20260807230157
This commit is contained in:
1 parent
a07b1b6fdb
commit
fa510854b4
7 files changed
+135
-57
No files matched your search
@@ -876,7 +876,6 @@ defineExpose({
|
|||||||
min-height: 0;
|
min-height: 0;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border: 1px solid var(--fms-border, #e5e7eb);
|
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
background: var(--fms-surface, #fff);
|
background: var(--fms-surface, #fff);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -244,8 +244,7 @@ watch(() => props.queryItems, initializeDrafts, { immediate: true });
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
justify-content: space-between;
|
justify-content: space-between;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
padding: 8px 12px;
|
padding: 8px 0;
|
||||||
border-bottom: 1px solid var(--fms-border, #e5e7eb);
|
|
||||||
background: var(--fms-surface, #fff);
|
background: var(--fms-surface, #fff);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -253,6 +253,8 @@ const customVars = {
|
|||||||
"--header-row-height": `${headerRowHeight}px`,
|
"--header-row-height": `${headerRowHeight}px`,
|
||||||
"--row-height": `${rowHeight}px`,
|
"--row-height": `${rowHeight}px`,
|
||||||
"--footer-row-height": "36px",
|
"--footer-row-height": "36px",
|
||||||
|
"--th-bgc": "#fff",
|
||||||
|
"--tf-bgc": "#fff",
|
||||||
};
|
};
|
||||||
|
|
||||||
// 选择列:已选状态管理 + 渲染逻辑全部内聚在 createSelectionColumn 中
|
// 选择列:已选状态管理 + 渲染逻辑全部内聚在 createSelectionColumn 中
|
||||||
@@ -498,10 +500,10 @@ defineExpose({
|
|||||||
:data-source="dataSource"
|
:data-source="dataSource"
|
||||||
:drag-row-config="{ mode: rowDraggable ? 'insert' : 'none' }"
|
:drag-row-config="{ mode: rowDraggable ? 'insert' : 'none' }"
|
||||||
:row-key="rowKey"
|
:row-key="rowKey"
|
||||||
:bordered="props.editable ? true : 'body-h'"
|
|
||||||
:no-data-full="noDataFull"
|
:no-data-full="noDataFull"
|
||||||
:show-overflow="showOverflow"
|
:show-overflow="showOverflow"
|
||||||
:area-selection="editable"
|
:area-selection="editable"
|
||||||
|
:bordered="'h'"
|
||||||
@area-selection-change="handleAreaSelectionChange"
|
@area-selection-change="handleAreaSelectionChange"
|
||||||
@row-order-change="handleRowOrderChange"
|
@row-order-change="handleRowOrderChange"
|
||||||
>
|
>
|
||||||
@@ -529,6 +531,14 @@ defineExpose({
|
|||||||
<!-- 非 scoped 全局样式:选择列原生 checkbox 外观模拟 antdv-next Checkbox,
|
<!-- 非 scoped 全局样式:选择列原生 checkbox 外观模拟 antdv-next Checkbox,
|
||||||
避免每行创建 Checkbox 组件实例导致的滚动性能退化 -->
|
避免每行创建 Checkbox 组件实例导致的滚动性能退化 -->
|
||||||
<style lang="scss">
|
<style lang="scss">
|
||||||
|
/* 双类选择器 .fms-table__inner.stk-table 权重 (0,2,1) > 库默认 .stk-table (0,1,1) */
|
||||||
|
.fms-table__inner.stk-table {
|
||||||
|
th,
|
||||||
|
td {
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.fms-checkbox {
|
.fms-checkbox {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
|||||||
@@ -454,8 +454,8 @@ onMounted(() => {
|
|||||||
color: #3f3f46;
|
color: #3f3f46;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
box-shadow:
|
box-shadow:
|
||||||
0 1px 3px rgb(0 0 0 / 4%),
|
0 1px 2px rgb(0 0 0 / 2%),
|
||||||
0 2px 4px rgb(0 0 0 / 3%);
|
0 1px 1px rgb(0 0 0 / 1%);
|
||||||
}
|
}
|
||||||
|
|
||||||
&.collapsed {
|
&.collapsed {
|
||||||
|
|||||||
@@ -149,6 +149,20 @@ function handleTabKeydown(event, index) {
|
|||||||
focusAdjacentTab(index, event.key === "ArrowLeft" ? -1 : 1);
|
focusAdjacentTab(index, event.key === "ArrowLeft" ? -1 : 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function handleWheel(event) {
|
||||||
|
const track = trackRef.value;
|
||||||
|
if (!track || track.scrollWidth <= track.clientWidth) return;
|
||||||
|
const delta =
|
||||||
|
Math.abs(event.deltaY) >= Math.abs(event.deltaX) ? event.deltaY : event.deltaX;
|
||||||
|
if (!delta) return;
|
||||||
|
event.preventDefault();
|
||||||
|
track.scrollLeft += delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
function refreshTab(key) {
|
||||||
|
appStore.refreshRoute(key);
|
||||||
|
}
|
||||||
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@@ -166,12 +180,12 @@ function handleTabKeydown(event, index) {
|
|||||||
</button>
|
</button>
|
||||||
|
|
||||||
<!-- 标签轨道 -->
|
<!-- 标签轨道 -->
|
||||||
<div ref="trackRef" class="tab-track" role="tablist" @scroll.passive="updateScrollState">
|
<div ref="trackRef" class="tab-track" role="tablist" @scroll.passive="updateScrollState" @wheel="handleWheel">
|
||||||
<div
|
<div
|
||||||
v-for="(item, index) in tabItems"
|
v-for="(item, index) in tabItems"
|
||||||
:key="item.key"
|
:key="item.key"
|
||||||
class="tab-item"
|
class="tab-item"
|
||||||
:class="{ 'tab-item--active': item.key === activeTab }"
|
:class="{ 'tab-item--active': item.key === activeTab, 'tab-item--closable': item.closable }"
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
@@ -182,6 +196,7 @@ function handleTabKeydown(event, index) {
|
|||||||
:tabindex="item.key === activeTab ? 0 : -1"
|
:tabindex="item.key === activeTab ? 0 : -1"
|
||||||
@click="changeTab(item.key)"
|
@click="changeTab(item.key)"
|
||||||
@keydown="handleTabKeydown($event, index)"
|
@keydown="handleTabKeydown($event, index)"
|
||||||
|
@dblclick.prevent="refreshTab(item.key)"
|
||||||
>
|
>
|
||||||
<span>{{ getDisplayTabLabel(item) }}</span>
|
<span>{{ getDisplayTabLabel(item) }}</span>
|
||||||
</button>
|
</button>
|
||||||
@@ -274,8 +289,9 @@ function handleTabKeydown(event, index) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---- 标签项(应用 tab 药丸式) ---- */
|
/* ---- 标签项(浅灰高亮,与侧栏选中语言统一) ---- */
|
||||||
.tab-item {
|
.tab-item {
|
||||||
|
position: relative;
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: none;
|
flex: none;
|
||||||
min-width: 72px;
|
min-width: 72px;
|
||||||
@@ -283,32 +299,34 @@ function handleTabKeydown(event, index) {
|
|||||||
align-items: center;
|
align-items: center;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
background: transparent;
|
background: #f4f4f5;
|
||||||
color: #71717a;
|
color: #71717a;
|
||||||
transition:
|
transition:
|
||||||
background-color 0.15s ease,
|
background-color 0.15s ease,
|
||||||
color 0.15s ease;
|
color 0.15s ease;
|
||||||
|
|
||||||
background: #f4f4f5;
|
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: #ebebeb;
|
background: #f4f4f5;
|
||||||
color: #3f3f46;
|
color: #3f3f46;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 选中态:实心深色药丸,应用 tab 的醒目语言 */
|
/* 选中态:纯白凸起药丸(inset 描边 + 轻投影,边缘利落不脏);关闭 X 作为会话标签的强区分信号 */
|
||||||
&--active {
|
&--active {
|
||||||
color: #ffffff;
|
color: #27272a;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
background: #18181b;
|
background: #ffffff;
|
||||||
|
box-shadow:
|
||||||
|
inset 0 0 0 1px #e4e4e7,
|
||||||
|
0 1px 2px rgb(0 0 0 / 6%),
|
||||||
|
0 1px 1px rgb(0 0 0 / 4%);
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: #27272a;
|
background: #ffffff;
|
||||||
color: #ffffff;
|
color: #27272a;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tab-item__close:hover {
|
.tab-item__close:hover {
|
||||||
background: rgb(255 255 255 / 12%);
|
background: rgb(0 0 0 / 8%);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -330,6 +348,7 @@ function handleTabKeydown(event, index) {
|
|||||||
padding: 0 8px;
|
padding: 0 8px;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
|
transition: padding-right 0.15s ease;
|
||||||
|
|
||||||
span {
|
span {
|
||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
@@ -338,16 +357,30 @@ function handleTabKeydown(event, index) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* 仅可关闭标签预留右侧 X 空间;不可关闭(如工作台)保持对称居中 */
|
||||||
|
&--closable &__select {
|
||||||
|
padding-right: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
&__close {
|
&__close {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
right: 2px;
|
||||||
|
bottom: 0;
|
||||||
display: grid;
|
display: grid;
|
||||||
flex: none;
|
flex: none;
|
||||||
width: 22px;
|
width: 22px;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
place-items: center;
|
place-items: center;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
|
color: #a1a1aa;
|
||||||
|
transition:
|
||||||
|
background-color 0.15s ease,
|
||||||
|
color 0.15s ease;
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: rgb(0 0 0 / 8%);
|
background: rgb(0 0 0 / 8%);
|
||||||
|
color: #71717a;
|
||||||
}
|
}
|
||||||
|
|
||||||
.lucide {
|
.lucide {
|
||||||
|
|||||||
@@ -89,7 +89,9 @@ function handleClick() {
|
|||||||
background: #f4f4f5;
|
background: #f4f4f5;
|
||||||
color: #3f3f46;
|
color: #3f3f46;
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
box-shadow: 0 1px 3px rgb(0 0 0 / 4%), 0 2px 4px rgb(0 0 0 / 3%);
|
box-shadow:
|
||||||
|
0 1px 2px rgb(0 0 0 / 2%),
|
||||||
|
0 1px 1px rgb(0 0 0 / 1%);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---- 可展开的父级按钮 ---- */
|
/* ---- 可展开的父级按钮 ---- */
|
||||||
|
|||||||
@@ -1138,7 +1138,34 @@ void permissionStore
|
|||||||
<main class="mm-main">
|
<main class="mm-main">
|
||||||
<FmsLoadingOverlay :loading="configLoading">
|
<FmsLoadingOverlay :loading="configLoading">
|
||||||
<template v-if="selectedModule">
|
<template v-if="selectedModule">
|
||||||
<!-- 顶部栏:tabs + 开关 + 操作 -->
|
<!-- 顶部操作栏:模块级操作(保存 / 启用 / 显示菜单),独立于 tab 导航 -->
|
||||||
|
<div class="mm-action-bar">
|
||||||
|
<a-button
|
||||||
|
v-if="canManage('update')"
|
||||||
|
type="primary"
|
||||||
|
:loading="configSaving"
|
||||||
|
:disabled="configLoading || configSyncing"
|
||||||
|
@click="saveAll"
|
||||||
|
>
|
||||||
|
保存
|
||||||
|
</a-button>
|
||||||
|
<div class="mm-action-bar__switches">
|
||||||
|
<label class="mm-switch"><span>启用</span><a-switch
|
||||||
|
:checked="drafts.module?.b_canuse === 1"
|
||||||
|
:disabled="busy"
|
||||||
|
@update:checked="(v) => onSetField('b_canuse', v ? 1 : 0)"
|
||||||
|
/></label>
|
||||||
|
<template v-if="drafts.module?.b_module_type !== 'data'">
|
||||||
|
<label class="mm-switch"><span>显示菜单</span><a-switch
|
||||||
|
:checked="drafts.module?.b_canmenu === 1"
|
||||||
|
:disabled="busy || ['data', 'data_category'].includes(drafts.module?.b_module_type)"
|
||||||
|
@update:checked="(v) => onSetField('b_canmenu', v ? 1 : 0)"
|
||||||
|
/></label>
|
||||||
|
</template>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 顶部栏:tab 导航 -->
|
||||||
<div class="mm-tab-bar">
|
<div class="mm-tab-bar">
|
||||||
<nav class="mm-tab-nav">
|
<nav class="mm-tab-nav">
|
||||||
<button
|
<button
|
||||||
@@ -1152,29 +1179,6 @@ void permissionStore
|
|||||||
{{ tab.tab }}
|
{{ tab.tab }}
|
||||||
</button>
|
</button>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="mm-tab-bar__right">
|
|
||||||
<label class="mm-switch"><span>启用</span><a-checkbox
|
|
||||||
:checked="drafts.module?.b_canuse === 1"
|
|
||||||
:disabled="busy"
|
|
||||||
@update:checked="(v) => onSetField('b_canuse', v ? 1 : 0)"
|
|
||||||
/></label>
|
|
||||||
<template v-if="drafts.module?.b_module_type !== 'data'">
|
|
||||||
<label class="mm-switch"><span>显示菜单</span><a-checkbox
|
|
||||||
:checked="drafts.module?.b_canmenu === 1"
|
|
||||||
:disabled="busy || ['data', 'data_category'].includes(drafts.module?.b_module_type)"
|
|
||||||
@update:checked="(v) => onSetField('b_canmenu', v ? 1 : 0)"
|
|
||||||
/></label>
|
|
||||||
</template>
|
|
||||||
<a-button
|
|
||||||
v-if="canManage('update')"
|
|
||||||
type="primary"
|
|
||||||
:loading="configSaving"
|
|
||||||
:disabled="configLoading || configSyncing"
|
|
||||||
@click="saveAll"
|
|
||||||
>
|
|
||||||
保存
|
|
||||||
</a-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- 面板内容 -->
|
<!-- 面板内容 -->
|
||||||
@@ -1353,20 +1357,32 @@ void permissionStore
|
|||||||
background: var(--fms-surface);
|
background: var(--fms-surface);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ---- 顶部栏:tabs + 开关 + 操作 ---- */
|
/* ---- 顶部操作栏:模块级操作(启用 / 显示菜单 / 保存),独立于 tab 导航 ---- */
|
||||||
|
.mm-action-bar {
|
||||||
|
display: flex;
|
||||||
|
flex: none;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: flex-start;
|
||||||
|
gap: 16px;
|
||||||
|
min-height: 48px;
|
||||||
|
padding: 0 20px;
|
||||||
|
|
||||||
|
&__switches {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 16px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ---- 顶部栏:tab 导航 ---- */
|
||||||
.mm-tab-bar {
|
.mm-tab-bar {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex: none;
|
flex: none;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
height: 44px;
|
height: 44px;
|
||||||
padding: 0 20px;
|
padding: 0 20px;
|
||||||
|
border-bottom: 1px solid #e4e4e7;
|
||||||
&__right {
|
|
||||||
display: flex;
|
|
||||||
flex: none;
|
|
||||||
align-items: center;
|
|
||||||
gap: 12px;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.mm-switch {
|
.mm-switch {
|
||||||
@@ -1383,12 +1399,19 @@ void permissionStore
|
|||||||
display: flex;
|
display: flex;
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
|
align-self: stretch;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 2px;
|
gap: 2px;
|
||||||
|
overflow-x: auto;
|
||||||
|
scrollbar-width: none;
|
||||||
|
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
&__item {
|
&__item {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
height: 32px;
|
align-self: stretch;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
padding: 0 12px;
|
padding: 0 12px;
|
||||||
border: 0;
|
border: 0;
|
||||||
@@ -1397,6 +1420,7 @@ void permissionStore
|
|||||||
color: #71717a;
|
color: #71717a;
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
|
position: relative;
|
||||||
transition:
|
transition:
|
||||||
background-color 0.15s ease,
|
background-color 0.15s ease,
|
||||||
color 0.15s ease;
|
color 0.15s ease;
|
||||||
@@ -1407,13 +1431,24 @@ void permissionStore
|
|||||||
}
|
}
|
||||||
|
|
||||||
&.active {
|
&.active {
|
||||||
background: #f4f4f5;
|
color: #3f3f46;
|
||||||
color: #0a0a0a;
|
|
||||||
font-weight: 500;
|
font-weight: 500;
|
||||||
|
background: transparent;
|
||||||
|
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
left: 12px;
|
||||||
|
right: 12px;
|
||||||
|
bottom: -1px;
|
||||||
|
height: 2px;
|
||||||
|
border-radius: 2px;
|
||||||
|
background: #3f3f46;
|
||||||
|
}
|
||||||
|
|
||||||
&:hover {
|
&:hover {
|
||||||
background: #ebebeb;
|
background: #f4f4f5;
|
||||||
color: #0a0a0a;
|
color: #3f3f46;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user