refactor: 样式优化

This commit is contained in:
meilin.huang
2025-08-19 19:44:14 +08:00
parent 82fd97e06a
commit c86f2ad412
11 changed files with 216 additions and 160 deletions

View File

@@ -10,9 +10,9 @@
"lint-fix": "eslint --fix --ext .js --ext .jsx --ext .vue src/"
},
"dependencies": {
"@element-plus/icons-vue": "^2.3.1",
"@logicflow/core": "^2.0.16",
"@logicflow/extension": "^2.0.21",
"@element-plus/icons-vue": "^2.3.2",
"@logicflow/core": "^2.1.1",
"@logicflow/extension": "^2.1.2",
"@vueuse/core": "^13.6.0",
"@xterm/addon-fit": "^0.10.0",
"@xterm/addon-search": "^0.15.0",
@@ -24,7 +24,7 @@
"crypto-js": "^4.2.0",
"dayjs": "^1.11.13",
"echarts": "^6.0.0",
"element-plus": "^2.10.5",
"element-plus": "^2.10.7",
"js-base64": "^3.7.7",
"jsencrypt": "^3.3.2",
"monaco-editor": "^0.52.2",
@@ -59,7 +59,7 @@
"eslint-plugin-vue": "^10.4.0",
"postcss": "^8.5.6",
"prettier": "^3.6.1",
"sass": "^1.89.2",
"sass": "^1.90.0",
"tailwindcss": "^4.1.11",
"typescript": "^5.9.2",
"vite": "npm:rolldown-vite@latest",

View File

@@ -1,6 +1,6 @@
<template>
<el-main class="layout-main !h-full">
<el-scrollbar ref="layoutScrollbarRef" view-class="!h-full">
<el-main class="layout-main h-full">
<el-scrollbar ref="layoutScrollbarRef" view-class="h-full">
<LayoutParentView />
</el-scrollbar>
@@ -13,7 +13,7 @@
</template>
<script setup lang="ts" name="layoutMain">
import { getCurrentInstance, watch, defineAsyncComponent } from 'vue';
import { watch, defineAsyncComponent, useTemplateRef, nextTick, onMounted } from 'vue';
import { useRoute } from 'vue-router';
import { storeToRefs } from 'pinia';
import { useThemeConfig } from '@/store/themeConfig';
@@ -21,22 +21,33 @@ import { useThemeConfig } from '@/store/themeConfig';
const LayoutParentView = defineAsyncComponent(() => import('@/layout/routerView/parent.vue'));
const Footer = defineAsyncComponent(() => import('@/layout/footer/index.vue'));
const { proxy } = getCurrentInstance() as any;
const layoutScrollbarRef = useTemplateRef('layoutScrollbarRef');
const { themeConfig } = storeToRefs(useThemeConfig());
const route = useRoute();
// 监听 themeConfig 配置文件的变化,更新菜单 el-scrollbar 的高度
watch(themeConfig.value, (val) => {
if (val.isFixedHeaderChange !== val.isFixedHeader) {
if (!proxy.$refs.layoutScrollbarRef) return false;
proxy.$refs.layoutScrollbarRef.update();
if (!layoutScrollbarRef.value) {
return;
}
layoutScrollbarRef.value.update();
}
});
// 监听路由的变化
watch(
() => route.path,
() => {
proxy.$refs.layoutScrollbarRef.wrapRef.scrollTop = 0;
nextTick(() => {
if (!layoutScrollbarRef.value) {
return;
}
setTimeout(() => {
layoutScrollbarRef.value.update();
}, 500);
layoutScrollbarRef.value.setScrollTop();
});
}
);
</script>

View File

@@ -1,5 +1,5 @@
<template>
<el-container class="layout-container flex-center layout-backtop">
<el-container class="layout-container layout-backtop !flex-col">
<Header />
<Main />
</el-container>

View File

@@ -34,13 +34,12 @@
</template>
<script lang="ts" setup name="navMenuHorizontal">
import { reactive, computed, onMounted, inject, defineAsyncComponent } from 'vue';
import { reactive, computed, onMounted, inject } from 'vue';
import { useRoute, onBeforeRouteUpdate } from 'vue-router';
import SubItem from '@/layout/navMenu/subItem.vue';
import { useRoutesList } from '@/store/routesList';
import { useThemeConfig } from '@/store/themeConfig';
const SubItem = defineAsyncComponent(() => import('@/layout/navMenu/subItem.vue'));
// 定义父组件传过来的值
const props = defineProps({
// 菜单列表
@@ -117,42 +116,29 @@ onBeforeRouteUpdate((to) => {
overflow: hidden;
margin-right: 30px;
.horizontal-menu {
border: none !important;
::v-deep(.el-scrollbar__bar.is-vertical) {
display: none;
}
::v-deep(a) {
width: 100%;
}
.el-menu.el-menu--horizontal {
display: flex;
height: 100%;
width: 100%;
box-sizing: border-box;
::v-deep(.el-menu-item) {
height: 42px;
line-height: 42px;
padding: 0 15px !important;
margin: 0 5px;
border-radius: 6px;
display: flex;
align-items: center;
border-bottom: none !important;
}
}
::v-deep(.el-sub-menu__title) {
height: 42px;
line-height: 42px;
padding: 0 25px 0 15px !important; /* 右边留出更多空间给箭头图标 */
margin: 0 5px;
border-radius: 6px;
display: flex;
align-items: center;
}
::v-deep(.el-sub-menu__icon-arrow) {
right: 5px !important;
margin-top: -5px !important;
}
::v-deep(.el-menu-item.is-active),
::v-deep(.el-sub-menu.is-active .el-sub-menu__title) {
color: #409eff;
background-color: rgba(64, 158, 255, 0.1);
}
}
// 菜单项基础样式
.horizontal-menu :deep(.el-menu-item),
.horizontal-menu :deep(.el-sub-menu__title) {
margin: 0 5px !important;
justify-content: center;
max-width: 160px;
min-width: 100px;
}
</style>

View File

@@ -131,35 +131,10 @@ $spacing: 8px;
// 横向菜单
.el-menu--horizontal {
background: var(--bg-topBar);
.el-menu-item,
.el-sub-menu {
height: $menuHeight;
line-height: $menuHeight;
color: var(--bg-topBarColor);
border-radius: $radius;
padding: 0 10px !important; // 减小内边距
.el-sub-menu__title {
height: $menuHeight;
line-height: $menuHeight;
color: var(--bg-topBarColor);
border-radius: $radius;
padding: 0 10px !important; // 减小内边距
}
}
.el-menu-item.is-active,
.el-sub-menu.is-active .el-sub-menu__title {
color: #409eff;
background-color: rgba(64, 158, 255, 0.1);
}
.el-menu-item:hover,
.el-sub-menu:not(.is-active):hover .el-sub-menu__title {
background-color: rgba(64, 158, 255, 0.05);
transform: translateY(-1px);
}
}
}
@@ -171,33 +146,15 @@ $spacing: 8px;
.el-menu-item,
.el-sub-menu__title {
height: $menuHeight;
line-height: $menuHeight;
color: var(--bg-topBarColor);
border-radius: $radius;
transition: all 0.2s ease;
padding: 0 10px !important; // 减小内边距
border-bottom: none !important;
}
.el-menu-item:not(.is-active):hover,
.el-sub-menu:not(.is-active):hover .el-sub-menu__title {
color: var(--bg-topBarColor);
background-color: rgba(0, 0, 0, 0.03);
}
.el-menu-item.is-active,
.el-sub-menu.is-active .el-sub-menu__title {
background-color: rgba(64, 158, 255, 0.1);
color: #409eff;
font-weight: 500;
border-bottom: none !important;
}
// 为水平菜单的子菜单项正确处理箭头图标位置
.el-sub-menu {
.el-sub-menu__title {
padding-right: 20px !important; // 调整箭头图标空间
padding-right: 22px !important; // 调整箭头图标空间
border-bottom: none !important;
}

View File

@@ -66,7 +66,7 @@ import { useI18nCreateTitle, useI18nDeleteConfirm, useI18nDeleteSuccessMsg, useI
import { tmplApi } from '../api';
import { TmplStatusEnum, TmplTypeEnum, ChannelTypeEnum } from '../enums';
import TmplEdit from './TmplEdit.vue';
import EnumValue from '../../../common/Enum';
import EnumValue from '@/common/Enum';
import AccountSelectFormItem from '@/views/system/account/components/AccountSelectFormItem.vue';
const perms = {

View File

@@ -1,6 +1,12 @@
<template>
<el-card class="h-full flex" body-class="!p-1 flex flex-col w-full">
<el-input v-model="filterText" :placeholder="$t('tag.tagFilterPlaceholder')" clearable size="small" class="!mb-1 w-full" />
<el-card class="h-full flex tag-tree-card" body-class="!p-0 flex flex-col w-full">
<div class="tag-tree-header">
<el-input v-model="filterText" :placeholder="$t('tag.tagFilterPlaceholder')" clearable size="small" class="tag-tree-search w-full">
<template #prefix>
<SvgIcon class="tag-tree-search-icon" name="search" />
</template>
</el-input>
</div>
<el-scrollbar>
<el-tree
class="min-w-full inline-block"
@@ -30,7 +36,7 @@
<slot v-else :node="node" :data="data" name="prefix"></slot>
<span class="ml-0.5" :title="data.labelRemark">
<span class="ml-1" :title="data.labelRemark">
<slot name="label" :data="data" v-if="!data.disabled"> {{ $t(data.label) }}</slot>
<!-- 禁用状态 -->
<slot name="disabledLabel" :data="data" v-else>
@@ -40,7 +46,7 @@
</slot>
</span>
<span class="absolute right-2.5 mt-0.5 text-[10px] text-gray-400">
<span class="ml-auto pr-1.5 text-[10px] text-gray-400">
<slot :node="node" :data="data" name="suffix"></slot>
</span>
</div>
@@ -59,6 +65,7 @@ import TagInfo from './TagInfo.vue';
import { Contextmenu } from '@/components/contextmenu';
import { tagApi } from '../tag/api';
import { isPrefixSubsequence } from '@/common/utils/string';
import SvgIcon from '@/components/svgIcon/index.vue';
const props = defineProps({
resourceType: {
@@ -248,4 +255,22 @@ defineExpose({
});
</script>
<style lang="scss" scoped></style>
<style lang="scss" scoped>
.tag-tree-card {
:deep(.el-card__body) {
padding: 0;
}
}
.tag-tree-header {
padding: 4px 6px;
border-bottom: 1px solid var(--el-border-color-light);
}
.tag-tree-search {
:deep(.el-input__wrapper) {
border-radius: 14px;
height: 24px;
}
}
</style>

View File

@@ -33,7 +33,7 @@
</div>
<!-- 字段名列 -->
<div v-else @contextmenu="headerContextmenuClick($event, column)" style="position: relative">
<div v-else style="position: relative" @mouseenter="showColumnAction(column)" @mouseleave="hideColumnAction">
<!-- 字段列的数据类型 -->
<div class="column-type">
<span v-if="column.dataTypeSubscript === 'icon-clock'">
@@ -65,9 +65,56 @@
<!-- 字段列右部分内容 -->
<div class="column-right">
<span v-if="column.title == nowSortColumn?.columnName">
<SvgIcon color="var(--el-color-primary)" :name="nowSortColumn?.order == 'asc' ? 'top' : 'bottom'"></SvgIcon>
<el-dropdown
@command="handleColumnCommand(column, $event)"
@visibleChange="onColumnActionVisibleChange(column, $event)"
trigger="click"
v-if="column.key !== rowNoColumn.key"
size="small"
>
<span class="column-actions-trigger">
<!-- 排序箭头图标 -->
<SvgIcon
v-if="
column.title == nowSortColumn?.columnName &&
!showColumnActions[column.key] &&
!columnActionVisible[column.key]
"
:color="'var(--el-color-primary)'"
:name="nowSortColumn?.order == 'asc' ? 'top' : 'bottom'"
:size="14"
/>
<!-- 更多操作图标 -->
<SvgIcon
v-if="columnActionVisible[column.key] || showColumnActions[column.key]"
name="MoreFilled"
:size="14"
:color="'var(--el-color-primary)'"
class="column-more-icon"
:class="{ 'column-more-icon-visible': columnActionVisible[column.key] || showColumnActions[column.key] }"
/>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="sort-asc">
<SvgIcon name="top" class="mr-1" />
{{ $t('db.asc') }}
</el-dropdown-item>
<el-dropdown-item command="sort-desc">
<SvgIcon name="bottom" class="mr-1" />
{{ $t('db.desc') }}
</el-dropdown-item>
<el-dropdown-item v-if="!column.fixed" command="fix">
<SvgIcon name="Paperclip" class="mr-1" />
{{ $t('db.fixed') }}
</el-dropdown-item>
<el-dropdown-item v-else command="unfix">
<SvgIcon name="Minus" class="mr-1" />
{{ $t('db.cancelFiexd') }}
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</div>
</div>
@@ -214,43 +261,9 @@ const props = defineProps({
const contextmenuRef = ref();
const tableRef = ref();
/** 表头 menu items **/
const cmHeaderAsc = new ContextmenuItem('asc', 'db.asc')
.withIcon('top')
.withOnClick((data: any) => {
onTableSortChange({ columnName: data.dataKey, order: 'asc' });
})
.withHideFunc(() => !props.showColumnTip);
const cmHeaderDesc = new ContextmenuItem('desc', 'db.desc')
.withIcon('bottom')
.withOnClick((data: any) => {
onTableSortChange({ columnName: data.dataKey, order: 'desc' });
})
.withHideFunc(() => !props.showColumnTip);
const cmHeaderFixed = new ContextmenuItem('fixed', 'db.fixed')
.withIcon('Paperclip')
.withOnClick((data: any) => {
state.columns.forEach((column: any) => {
if (column.dataKey == data.dataKey) {
column.fixed = true;
}
});
})
.withHideFunc((data: any) => data.fixed);
const cmHeaderCancelFixed = new ContextmenuItem('cancelFixed', 'db.cancelFiexd')
.withIcon('Minus')
.withOnClick((data: any) => {
state.columns.forEach((column: any) => {
if (column.dataKey == data.dataKey) {
column.fixed = false;
}
});
})
.withHideFunc((data: any) => !data.fixed);
// 用于控制列操作按钮的显示
const showColumnActions = ref({} as any);
const columnActionVisible = ref({} as any);
/** 表数据 contextmenu items **/
@@ -508,6 +521,55 @@ const cancelLoading = async () => {
endLoading();
};
/**
* 显示列操作按钮
*/
const showColumnAction = (column: any) => {
showColumnActions.value[column.key] = true;
};
/**
* 隐藏列操作按钮
*/
const hideColumnAction = () => {
showColumnActions.value = {};
};
/**
* 处理列操作命令
*/
const handleColumnCommand = (column: any, command: string) => {
switch (command) {
case 'sort-asc':
onTableSortChange({ columnName: column.dataKey, order: 'asc' });
break;
case 'sort-desc':
onTableSortChange({ columnName: column.dataKey, order: 'desc' });
break;
case 'fix':
state.columns.forEach((col: any) => {
if (col.dataKey == column.dataKey) {
col.fixed = true;
}
});
break;
case 'unfix':
state.columns.forEach((col: any) => {
if (col.dataKey == column.dataKey) {
col.fixed = false;
}
});
break;
}
// 点击了取消固定等操作后可能更多的icon还是显示在列上所以需要重新置为空对象。暂时不懂是组件bug还是啥
columnActionVisible.value = {};
};
const onColumnActionVisibleChange = (column: any, visible: boolean) => {
columnActionVisible.value = {}; // 只显示一个列的更多icon
columnActionVisible.value[column.key] = visible;
};
/**
* 当前单元格是否允许编辑
* @param rowIndex ri
@@ -570,16 +632,6 @@ const rowEventHandlers = {
},
};
const headerContextmenuClick = (event: any, data: any) => {
event.preventDefault(); // 阻止默认的右击菜单行为
const { clientX, clientY } = event;
state.contextmenu.dropdown.x = clientX;
state.contextmenu.dropdown.y = clientY;
state.contextmenu.items = [cmHeaderAsc, cmHeaderDesc, cmHeaderFixed, cmHeaderCancelFixed];
contextmenuRef.value.openContextmenu(data);
};
const dataContextmenuClick = (event: any, rowIndex: number, column: any, data: any) => {
event.preventDefault(); // 阻止默认的右击菜单行为
@@ -851,6 +903,31 @@ defineExpose({
top: 2px;
right: 0;
padding: 2px;
display: flex;
align-items: center;
}
.column-actions-trigger {
display: inline-flex;
align-items: center;
justify-content: center;
width: 16px;
height: 16px;
border-radius: 50%;
cursor: pointer;
&:hover {
background-color: var(--el-fill-color-light);
}
}
.column-more-icon {
opacity: 0;
transition: opacity 0.2s;
}
.column-more-icon-visible {
opacity: 1 !important;
}
}
</style>

View File

@@ -497,8 +497,8 @@ export class DbInst {
return;
}
// 获取列名称的长度 加上排序图标长度、abc为字段类型简称占位符、排序图标等
const columnWidth: number = getTextWidth(prop + 'abc') + 10;
// 获取列名称的长度 加上排序图标长度、abc为字段类型简称占位符、更多/排序图标等
const columnWidth: number = getTextWidth(prop + 'abc') + 25;
// prop为该列的字段名(传字符串);tableData为该表格的数据源(传变量);
if (!tableData || !tableData.length || tableData.length === 0 || tableData === undefined) {
return columnWidth;

View File

@@ -1,6 +1,6 @@
module mayfly-go
go 1.24
go 1.25
require (
gitee.com/chunanyong/dm v1.8.20
@@ -23,7 +23,7 @@ require (
github.com/pkg/errors v0.9.1
github.com/pkg/sftp v1.13.9
github.com/pquerna/otp v1.5.0
github.com/redis/go-redis/v9 v9.11.0
github.com/redis/go-redis/v9 v9.12.1
github.com/robfig/cron/v3 v3.0.1 //
github.com/sijms/go-ora/v2 v2.9.0
github.com/spf13/cast v1.9.2
@@ -31,7 +31,7 @@ require (
github.com/tidwall/gjson v1.18.0
github.com/veops/go-ansiterm v0.0.5
go.mongodb.org/mongo-driver/v2 v2.2.2 // mongo
golang.org/x/crypto v0.40.0 // ssh
golang.org/x/crypto v0.41.0 // ssh
golang.org/x/oauth2 v0.30.0
golang.org/x/sync v0.16.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
@@ -91,8 +91,8 @@ require (
golang.org/x/exp v0.0.0-20250718183923-645b1fa84792 // indirect
golang.org/x/image v0.29.0 // indirect
golang.org/x/net v0.42.0 // indirect
golang.org/x/sys v0.34.0 // indirect
golang.org/x/text v0.27.0 // indirect
golang.org/x/sys v0.35.0 // indirect
golang.org/x/text v0.28.0 // indirect
google.golang.org/protobuf v1.36.6 // indirect
modernc.org/libc v1.66.4 // indirect
modernc.org/mathutil v1.7.1 // indirect

View File

@@ -379,7 +379,7 @@ func (m *MachineFile) UploadFolder(rc *req.Ctx) {
isSuccess := true
for _, chunk := range chunks {
go func(files []FolderFile, wg *sync.WaitGroup) {
wg.Go(func() {
defer func() {
// 协程执行完成后调用Done方法
wg.Done()
@@ -397,7 +397,7 @@ func (m *MachineFile) UploadFolder(rc *req.Ctx) {
}
}()
for _, file := range files {
for _, file := range chunk {
fileHeader := file.Fileheader
dir := file.Dir
file, _ := fileHeader.Open()
@@ -410,7 +410,7 @@ func (m *MachineFile) UploadFolder(rc *req.Ctx) {
defer createfile.Close()
io.Copy(createfile, file)
}
}(chunk, &wg)
})
}
// 等待所有协程执行完成