refactor: 样式调整

This commit is contained in:
meilin.huang
2023-12-14 13:05:21 +08:00
parent 5c72b1de57
commit 18cf2e54c4
17 changed files with 391 additions and 337 deletions

View File

@@ -2,7 +2,7 @@
<div> <div>
<transition name="el-zoom-in-top"> <transition name="el-zoom-in-top">
<!-- 查询表单 --> <!-- 查询表单 -->
<SearchForm v-show="isShowSearch" :items="searchItems" v-model="queryForm_" :search="queryData" :reset="reset" :search-col="searchCol"> <SearchForm v-if="isShowSearch" :items="searchItems" v-model="queryForm_" :search="queryData" :reset="reset" :search-col="searchCol">
<!-- 遍历父组件传入的 solts 透传给子组件 --> <!-- 遍历父组件传入的 solts 透传给子组件 -->
<template v-for="(_, key) in useSlots()" v-slot:[key]> <template v-for="(_, key) in useSlots()" v-slot:[key]>
<slot :name="key"></slot> <slot :name="key"></slot>
@@ -10,7 +10,7 @@
</SearchForm> </SearchForm>
</transition> </transition>
<el-card> <div class="card">
<div class="table-main"> <div class="table-main">
<!-- 表格头部 操作按钮 --> <!-- 表格头部 操作按钮 -->
<div class="table-header"> <div class="table-header">
@@ -121,7 +121,7 @@
:page-sizes="pageSizes" :page-sizes="pageSizes"
/> />
</el-row> </el-row>
</el-card> </div>
</div> </div>
</template> </template>

View File

@@ -190,6 +190,18 @@ body,
} }
} }
.flex-justify-between {
display: flex;
align-items: center; // 垂直方向水平居中
justify-content: space-between; // 使第一个子元素靠近父容器的起始位置,最后一个子元素靠近终止位置,而其他子元素均匀分布在它们之间
}
.flex-align-center {
display: flex;
align-items: center;
}
/* 宽高 100% /* 宽高 100%
------------------------------- */ ------------------------------- */
.w100 { .w100 {
@@ -276,6 +288,10 @@ body,
.pl#{$i} { .pl#{$i} {
padding-left: #{$i}px !important; padding-left: #{$i}px !important;
} }
.pd#{$i} {
padding: #{$i}px !important;
}
} }
@@ -335,12 +351,15 @@ body,
user-select: none; user-select: none;
} }
.toolbar { /* custom card */
width: 100%; .card {
padding: 4px; box-sizing: border-box;
overflow: hidden; padding: 20px;
line-height: 24px; overflow-x: hidden;
border: 1px solid var(--el-border-color-light, #ebeef5); background-color: var(--el-bg-color);
border: 1px solid var(--el-border-color-light);
border-radius: 6px;
box-shadow: 0 0 12px rgb(0 0 0 / 5%);
} }
.fl { .fl {
@@ -361,10 +380,6 @@ body,
z-index: inherit !important; z-index: inherit !important;
} }
.f12 {
font-size: 12px
}
.pointer { .pointer {
cursor: pointer; cursor: pointer;
} }
@@ -396,6 +411,7 @@ body,
.default-theme.splitpanes--horizontal>.splitpanes__splitter { .default-theme.splitpanes--horizontal>.splitpanes__splitter {
border-top: 1px solid var(--bg-main-color); border-top: 1px solid var(--bg-main-color);
} }
// 竖线样式 // 竖线样式
.splitpanes.default-theme .splitpanes__splitter::before, .splitpanes.default-theme .splitpanes__splitter::before,
.splitpanes.default-theme .splitpanes__splitter::after { .splitpanes.default-theme .splitpanes__splitter::after {

View File

@@ -1,7 +1,7 @@
<template> <template>
<div class="tag-tree"> <div class="tag-tree card pd5">
<el-input v-model="filterText" placeholder="输入关键字->搜索已展开节点信息" clearable size="small" class="mb5" /> <el-scrollbar>
<el-scrollbar :style="{ height: state.height, maxHeight: state.height, backgroundColor: 'var(--el-fill-color-blank)' }"> <el-input v-model="filterText" placeholder="输入关键字->搜索已展开节点信息" clearable size="small" class="mb5 w100" />
<el-tree <el-tree
ref="treeRef" ref="treeRef"
:highlight-current="true" :highlight-current="true"
@@ -32,8 +32,9 @@
</span> </span>
</template> </template>
</el-tree> </el-tree>
<contextmenu :dropdown="state.dropdown" :items="state.contextmenuItems" ref="contextmenuRef" @currentContextmenuClick="onCurrentContextmenuClick" />
</el-scrollbar> </el-scrollbar>
<contextmenu :dropdown="state.dropdown" :items="state.contextmenuItems" ref="contextmenuRef" @currentContextmenuClick="onCurrentContextmenuClick" />
</div> </div>
</template> </template>
@@ -43,7 +44,6 @@ import { NodeType, TagTreeNode } from './tag';
import TagInfo from './TagInfo.vue'; import TagInfo from './TagInfo.vue';
import { Contextmenu } from '@/components/contextmenu'; import { Contextmenu } from '@/components/contextmenu';
import { tagApi } from '../tag/api'; import { tagApi } from '../tag/api';
import { useEventListener, useWindowSize } from '@vueuse/core';
const props = defineProps({ const props = defineProps({
resourceType: { resourceType: {
@@ -74,8 +74,6 @@ const emit = defineEmits(['nodeClick', 'currentContextmenuClick']);
const treeRef: any = ref(null); const treeRef: any = ref(null);
const contextmenuRef = ref(); const contextmenuRef = ref();
const { height: vh } = useWindowSize();
const state = reactive({ const state = reactive({
height: 600 as any, height: 600 as any,
filterText: '', filterText: '',
@@ -88,14 +86,7 @@ const state = reactive({
}); });
const { filterText } = toRefs(state); const { filterText } = toRefs(state);
onMounted(async () => { onMounted(async () => {});
setHeight();
useEventListener(window, 'resize', setHeight);
});
const setHeight = () => {
state.height = vh.value - 138 + 'px';
};
watch(filterText, (val) => { watch(filterText, (val) => {
treeRef.value?.filter(val); treeRef.value?.filter(val);
@@ -195,8 +186,7 @@ defineExpose({
<style lang="scss" scoped> <style lang="scss" scoped>
.tag-tree { .tag-tree {
border-radius: var(--el-input-border-radius, var(--el-border-radius-base)); height: calc(100vh - 108px);
border: 1px solid var(--el-border-color-light, #ebeef5);
.el-tree { .el-tree {
display: inline-block; display: inline-block;

View File

@@ -21,7 +21,7 @@
</el-col> </el-col>
<el-col style="text-align: center" :span="1">:</el-col> <el-col style="text-align: center" :span="1">:</el-col>
<el-col :span="5"> <el-col :span="5">
<el-input type="number" v-model.number="form.port" placeholder="请输入端口"></el-input> <el-input type="number" v-model.number="form.port" placeholder="端口"></el-input>
</el-col> </el-col>
</el-form-item> </el-form-item>
<el-form-item prop="username" label="用户名" required> <el-form-item prop="username" label="用户名" required>
@@ -49,7 +49,7 @@
<el-tab-pane label="其他配置" name="other"> <el-tab-pane label="其他配置" name="other">
<el-form-item prop="params" label="连接参数"> <el-form-item prop="params" label="连接参数">
<el-input v-model.trim="form.params" placeholder="其他连接参数,形如: key1=value1&key2=value2"> <el-input v-model.trim="form.params" placeholder="其他连接参数,形如: key1=value1&key2=value2">
<template #suffix> <!-- <template #suffix>
<el-link <el-link
target="_blank" target="_blank"
href="https://github.com/go-sql-driver/mysql#parameters" href="https://github.com/go-sql-driver/mysql#parameters"
@@ -58,7 +58,7 @@
class="mr5" class="mr5"
>参数参考</el-link >参数参考</el-link
> >
</template> </template> -->
</el-input> </el-input>
</el-form-item> </el-form-item>

View File

@@ -47,101 +47,103 @@
</Pane> </Pane>
<Pane> <Pane>
<el-row> <div class="card db-op pd5">
<el-col :span="24" v-if="state.db"> <el-row>
<el-descriptions :column="4" size="small" border class="ml5"> <el-col :span="24" v-if="state.db">
<el-descriptions-item label-align="right" label="操作" <el-descriptions :column="4" size="small" border>
><el-button <el-descriptions-item label-align="right" label="操作"
:disabled="!state.db || !nowDbInst.id" ><el-button
type="primary" :disabled="!state.db || !nowDbInst.id"
icon="Search" type="primary"
@click="addQueryTab({ id: nowDbInst.id, dbs: nowDbInst.databases }, state.db)" icon="Search"
size="small" @click="addQueryTab({ id: nowDbInst.id, dbs: nowDbInst.databases }, state.db)"
>新建查询</el-button size="small"
></el-descriptions-item >新建查询</el-button
> ></el-descriptions-item
>
<el-descriptions-item label-align="right" label="tag">{{ nowDbInst.tagPath }}</el-descriptions-item> <el-descriptions-item label-align="right" label="tag">{{ nowDbInst.tagPath }}</el-descriptions-item>
<el-descriptions-item label-align="right"> <el-descriptions-item label-align="right">
<template #label> <template #label>
<div> <div>
<SvgIcon :name="getDbDialect(nowDbInst.type).getInfo().icon" :size="18" /> <SvgIcon :name="getDbDialect(nowDbInst.type).getInfo().icon" :size="18" />
实例 实例
</div> </div>
</template>
{{ nowDbInst.id }}
<el-divider direction="vertical" border-style="dashed" />
{{ nowDbInst.name }}
<el-divider direction="vertical" border-style="dashed" />
{{ nowDbInst.host }}
</el-descriptions-item>
<el-descriptions-item label="库名" label-align="right">{{ state.db }}</el-descriptions-item>
</el-descriptions>
</el-col>
</el-row>
<div id="data-exec" class="mt5 ml5">
<el-tabs
v-if="state.tabs.size > 0"
type="card"
@tab-remove="onRemoveTab"
@tab-change="onTabChange"
style="width: 100%"
v-model="state.activeName"
class="h100"
>
<el-tab-pane class="h100" closable v-for="dt in state.tabs.values()" :label="dt.label" :name="dt.key" :key="dt.key">
<template #label>
<el-popover :show-after="1000" placement="bottom-start" trigger="hover" :width="250">
<template #reference> {{ dt.label }} </template>
<template #default>
<el-descriptions :column="1" size="small">
<el-descriptions-item label="tagPath">
{{ dt.params.tagPath }}
</el-descriptions-item>
<el-descriptions-item label="名称">
{{ dt.params.name }}
</el-descriptions-item>
<el-descriptions-item label="host">
<SvgIcon :name="getDbDialect(dt.params.type).getInfo().icon" :size="18" />
{{ dt.params.host }}
</el-descriptions-item>
<el-descriptions-item label="库名">
{{ dt.params.dbName }}
</el-descriptions-item>
</el-descriptions>
</template> </template>
</el-popover> {{ nowDbInst.id }}
</template> <el-divider direction="vertical" border-style="dashed" />
{{ nowDbInst.name }}
<el-divider direction="vertical" border-style="dashed" />
{{ nowDbInst.host }}
</el-descriptions-item>
<db-table-data-op <el-descriptions-item label="库名" label-align="right">{{ state.db }}</el-descriptions-item>
v-if="dt.type === TabType.TableData" </el-descriptions>
:db-id="dt.dbId" </el-col>
:db-name="dt.db" </el-row>
:table-name="dt.params.table"
:table-height="state.dataTabsTableHeight"
></db-table-data-op>
<db-sql-editor <div id="data-exec" class="mt5">
v-if="dt.type === TabType.Query" <el-tabs
:db-id="dt.dbId" v-if="state.tabs.size > 0"
:db-name="dt.db" type="card"
:sql-name="dt.params.sqlName" @tab-remove="onRemoveTab"
@save-sql-success="reloadSqls" @tab-change="onTabChange"
> style="width: 100%"
</db-sql-editor> v-model="state.activeName"
class="h100"
>
<el-tab-pane class="h100" closable v-for="dt in state.tabs.values()" :label="dt.label" :name="dt.key" :key="dt.key">
<template #label>
<el-popover :show-after="1000" placement="bottom-start" trigger="hover" :width="250">
<template #reference> {{ dt.label }} </template>
<template #default>
<el-descriptions :column="1" size="small">
<el-descriptions-item label="tagPath">
{{ dt.params.tagPath }}
</el-descriptions-item>
<el-descriptions-item label="名称">
{{ dt.params.name }}
</el-descriptions-item>
<el-descriptions-item label="host">
<SvgIcon :name="getDbDialect(dt.params.type).getInfo().icon" :size="18" />
{{ dt.params.host }}
</el-descriptions-item>
<el-descriptions-item label="库名">
{{ dt.params.dbName }}
</el-descriptions-item>
</el-descriptions>
</template>
</el-popover>
</template>
<db-tables-op <db-table-data-op
v-if="dt.type == TabType.TablesOp" v-if="dt.type === TabType.TableData"
:db-id="dt.params.id" :db-id="dt.dbId"
:db="dt.params.db" :db-name="dt.db"
:db-type="dt.params.type" :table-name="dt.params.table"
:height="state.tablesOpHeight" :table-height="state.dataTabsTableHeight"
/> ></db-table-data-op>
</el-tab-pane>
</el-tabs> <db-sql-editor
v-if="dt.type === TabType.Query"
:db-id="dt.dbId"
:db-name="dt.db"
:sql-name="dt.params.sqlName"
@save-sql-success="reloadSqls"
>
</db-sql-editor>
<db-tables-op
v-if="dt.type == TabType.TablesOp"
:db-id="dt.params.id"
:db="dt.params.db"
:db-type="dt.params.type"
:height="state.tablesOpHeight"
/>
</el-tab-pane>
</el-tabs>
</div>
</div> </div>
</Pane> </Pane>
</Splitpanes> </Splitpanes>
@@ -397,8 +399,8 @@ onBeforeUnmount(() => {
* 设置editor高度和数据表高度 * 设置editor高度和数据表高度
*/ */
const setHeight = () => { const setHeight = () => {
state.dataTabsTableHeight = window.innerHeight - 255 + 'px'; state.dataTabsTableHeight = window.innerHeight - 270 + 'px';
state.tablesOpHeight = window.innerHeight - 212 + 'px'; state.tablesOpHeight = window.innerHeight - 225 + 'px';
}; };
// 选择数据库,改变当前正在操作的数据库信息 // 选择数据库,改变当前正在操作的数据库信息
@@ -607,6 +609,10 @@ const getNowDbInfo = () => {
font-size: 9px; font-size: 9px;
} }
.db-op {
height: calc(100vh - 108px);
}
#data-exec { #data-exec {
.el-tabs { .el-tabs {
--el-tabs-header-height: 30px; --el-tabs-header-height: 30px;

View File

@@ -1,8 +1,8 @@
<template> <template>
<div> <div>
<div> <div>
<div class="toolbar"> <div class="card pd5 flex-justify-between">
<div class="fl"> <div>
<el-link @click="onRunSql()" :underline="false" class="ml15" icon="VideoPlay"> </el-link> <el-link @click="onRunSql()" :underline="false" class="ml15" icon="VideoPlay"> </el-link>
<el-divider direction="vertical" border-style="dashed" /> <el-divider direction="vertical" border-style="dashed" />
@@ -33,7 +33,7 @@
</el-upload> </el-upload>
</div> </div>
<div class="fr"> <div>
<el-button @click="saveSql()" type="primary" icon="document-add" plain size="small">保存SQL</el-button> <el-button @click="saveSql()" type="primary" icon="document-add" plain size="small">保存SQL</el-button>
</div> </div>
</div> </div>
@@ -44,7 +44,7 @@
@resize="resizeTableHeight" @resize="resizeTableHeight"
horizontal horizontal
class="default-theme" class="default-theme"
style="height: calc(100vh - 220px)" style="height: calc(100vh - 233px)"
> >
<Pane :size="state.editorSize" max-size="80"> <Pane :size="state.editorSize" max-size="80">
<MonacoEditor ref="monacoEditorRef" class="mt5" v-model="state.sql" language="sql" height="100%" :id="'MonacoTextarea-' + getKey()" /> <MonacoEditor ref="monacoEditorRef" class="mt5" v-model="state.sql" language="sql" height="100%" :id="'MonacoTextarea-' + getKey()" />
@@ -128,7 +128,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { h, nextTick, onMounted, reactive, toRefs, ref } from 'vue'; import { h, nextTick, onMounted, reactive, toRefs, ref, unref } from 'vue';
import { getToken } from '@/common/utils/storage'; import { getToken } from '@/common/utils/storage';
import { notBlank } from '@/common/assert'; import { notBlank } from '@/common/assert';
import { format as sqlFormatter } from 'sql-formatter'; import { format as sqlFormatter } from 'sql-formatter';
@@ -276,7 +276,7 @@ const onRemoveTab = (targetId: number) => {
const resizeTableHeight = (e: any) => { const resizeTableHeight = (e: any) => {
const vh = window.innerHeight; const vh = window.innerHeight;
state.editorSize = e[0].size; state.editorSize = e[0].size;
const plitpaneHeight = vh - 210; const plitpaneHeight = vh - 223;
const editorHeight = plitpaneHeight * (state.editorSize / 100); const editorHeight = plitpaneHeight * (state.editorSize / 100);
state.tableDataHeight = plitpaneHeight - editorHeight - 40 + 'px'; state.tableDataHeight = plitpaneHeight - editorHeight - 40 + 'px';
}; };
@@ -336,7 +336,7 @@ const onRunSql = async (newTab = false) => {
// 不是新建tab执行则在当前激活的tab上执行sql // 不是新建tab执行则在当前激活的tab上执行sql
i = state.execResTabs.findIndex((x) => x.id == state.activeTab); i = state.execResTabs.findIndex((x) => x.id == state.activeTab);
execRes = state.execResTabs[i]; execRes = state.execResTabs[i];
if (execRes.loading?.value) { if (unref(execRes.loading)) {
ElMessage.error('当前结果集tab正在执行, 请使用新标签执行'); ElMessage.error('当前结果集tab正在执行, 请使用新标签执行');
return; return;
} }

View File

@@ -47,11 +47,11 @@
<el-divider direction="vertical" border-style="dashed" /> <el-divider direction="vertical" border-style="dashed" />
<el-tooltip :show-after="500" v-if="hasUpdatedFileds" class="box-item" effect="dark" content="提交修改" placement="top"> <el-tooltip :show-after="500" v-if="hasUpdatedFileds" class="box-item" effect="dark" content="提交修改" placement="top">
<el-link @click="submitUpdateFields()" type="success" :underline="false" class="f12">提交</el-link> <el-link @click="submitUpdateFields()" type="success" :underline="false" class="font12">提交</el-link>
</el-tooltip> </el-tooltip>
<el-divider v-if="hasUpdatedFileds" direction="vertical" border-style="dashed" /> <el-divider v-if="hasUpdatedFileds" direction="vertical" border-style="dashed" />
<el-tooltip :show-after="500" v-if="hasUpdatedFileds" class="box-item" effect="dark" content="取消修改" placement="top"> <el-tooltip :show-after="500" v-if="hasUpdatedFileds" class="box-item" effect="dark" content="取消修改" placement="top">
<el-link @click="cancelUpdateFields" type="warning" :underline="false" class="f12">取消</el-link> <el-link @click="cancelUpdateFields" type="warning" :underline="false" class="font12">取消</el-link>
</el-tooltip> </el-tooltip>
</div> </div>
</el-col> </el-col>

View File

@@ -1,19 +1,19 @@
<template> <template>
<div class="file-manage"> <div class="file-manage">
<el-dialog title="进程信息" v-model="dialogVisible" :destroy-on-close="true" :show-close="true" :before-close="handleClose" width="65%"> <el-dialog title="进程信息" v-model="dialogVisible" :destroy-on-close="true" :show-close="true" :before-close="handleClose" width="65%">
<div class="toolbar"> <div class="card pd5">
<el-row> <el-row>
<el-col :span="4"> <el-col :span="4">
<el-input size="small" placeholder="进程名" v-model="params.name" plain clearable></el-input> <el-input size="small" placeholder="进程名" v-model="params.name" plain clearable></el-input>
</el-col> </el-col>
<el-col :span="4" class="ml5"> <el-col :span="4" class="ml5">
<el-select @change="getProcess" size="small" v-model="params.sortType" placeholder="请选择排序类型"> <el-select class="w100" @change="getProcess" size="small" v-model="params.sortType" placeholder="请选择排序类型">
<el-option key="cpu" label="cpu降序" value="1"> </el-option> <el-option key="cpu" label="cpu降序" value="1"> </el-option>
<el-option key="cpu" label="mem降序" value="2"> </el-option> <el-option key="cpu" label="mem降序" value="2"> </el-option>
</el-select> </el-select>
</el-col> </el-col>
<el-col :span="4" class="ml5"> <el-col :span="4" class="ml5">
<el-select @change="getProcess" size="small" v-model="params.count" placeholder="请选择进程个数"> <el-select class="w100" @change="getProcess" size="small" v-model="params.count" placeholder="请选择进程个数">
<el-option key="10" label="10" value="10"> </el-option> <el-option key="10" label="10" value="10"> </el-option>
<el-option key="15" label="15" value="15"> </el-option> <el-option key="15" label="15" value="15"> </el-option>
<el-option key="20" label="20" value="20"> </el-option> <el-option key="20" label="20" value="20"> </el-option>

View File

@@ -44,9 +44,9 @@
</Pane> </Pane>
<Pane> <Pane>
<div id="mongo-tab" class="ml5" style="border: 1px solid var(--el-border-color-light, #ebeef5); margin-top: 1px"> <div class="mongo-data-tab card pd5">
<el-row v-if="nowColl"> <el-row v-if="nowColl">
<el-descriptions :column="10" size="small" border> <el-descriptions class="w100" :column="10" size="small" border>
<!-- <el-descriptions-item label-align="right" label="tag">xxx</el-descriptions-item> --> <!-- <el-descriptions-item label-align="right" label="tag">xxx</el-descriptions-item> -->
<el-descriptions-item label="ns" label-align="right"> <el-descriptions-item label="ns" label-align="right">
@@ -74,7 +74,7 @@
</el-row> </el-row>
<el-row type="flex"> <el-row type="flex">
<el-tabs @tab-remove="removeDataTab" style="width: 100%; margin-left: 5px" v-model="state.activeName"> <el-tabs @tab-remove="removeDataTab" class="w100 ml5" v-model="state.activeName">
<el-tab-pane closable v-for="dt in state.dataTabs" :key="dt.key" :label="dt.label" :name="dt.key"> <el-tab-pane closable v-for="dt in state.dataTabs" :key="dt.key" :label="dt.label" :name="dt.key">
<el-row> <el-row>
<el-col :span="2"> <el-col :span="2">
@@ -96,27 +96,29 @@
</el-input> </el-input>
</el-col> </el-col>
</el-row> </el-row>
<el-row :style="`height: ${dataHeight}; overflow: auto;`"> <el-scrollbar class="mongo-data-tab-data">
<el-col :span="6" v-for="item in dt.datas" :key="item"> <el-row>
<el-card :body-style="{ padding: '0px', position: 'relative' }"> <el-col :span="6" v-for="item in dt.datas" :key="item">
<el-input type="textarea" v-model="item.value" :rows="10" /> <el-card :body-style="{ padding: '0px', position: 'relative' }">
<div style="padding: 3px; float: right" class="mr5 mongo-doc-btns"> <el-input type="textarea" v-model="item.value" :rows="10" />
<div> <div style="padding: 3px; float: right" class="mr5 mongo-doc-btns">
<el-link @click="onEditDoc(item)" :underline="false" type="success" icon="MagicStick"></el-link> <div>
<el-link @click="onEditDoc(item)" :underline="false" type="success" icon="MagicStick"></el-link>
<el-divider direction="vertical" border-style="dashed" /> <el-divider direction="vertical" border-style="dashed" />
<el-popconfirm @confirm="onDeleteDoc(item.value)" title="确定删除该文档?" width="160"> <el-popconfirm @confirm="onDeleteDoc(item.value)" title="确定删除该文档?" width="160">
<template #reference> <template #reference>
<el-link v-auth="perms.delData" :underline="false" type="danger" icon="DocumentDelete"> <el-link v-auth="perms.delData" :underline="false" type="danger" icon="DocumentDelete">
</el-link> </el-link>
</template> </template>
</el-popconfirm> </el-popconfirm>
</div>
</div> </div>
</div> </el-card>
</el-card> </el-col>
</el-col> </el-row>
</el-row> </el-scrollbar>
</el-tab-pane> </el-tab-pane>
</el-tabs> </el-tabs>
</el-row> </el-row>
@@ -253,7 +255,6 @@ const NodeTypeColl = new NodeType(MongoNodeType.Coll).withNodeClickFunc((nodeDat
const findParamInputRef: any = ref(null); const findParamInputRef: any = ref(null);
const state = reactive({ const state = reactive({
tags: [], tags: [],
dataHeight: `${window.innerHeight - 220}px`,
mongoList: [] as any, mongoList: [] as any,
activeName: '', // 当前操作的tab activeName: '', // 当前操作的tab
dataTabs: {} as any, // 数据tabs dataTabs: {} as any, // 数据tabs
@@ -282,7 +283,7 @@ const state = reactive({
}, },
}); });
const { dataHeight, findDialog, docEditDialog } = toRefs(state); const { findDialog, docEditDialog } = toRefs(state);
const nowColl = computed(() => { const nowColl = computed(() => {
return getNowDataTab(); return getNowDataTab();
@@ -506,7 +507,17 @@ const getNowDataTab = () => {
max-width: 120px; max-width: 120px;
} }
#mongo-tab { .mongo-data-tab {
height: calc(100vh - 108px);
}
.mongo-data-tab {
margin-top: 1px;
.mongo-data-tab-data {
height: calc(100vh - 230px);
}
.el-tabs__header { .el-tabs__header {
margin: 0 0 5px; margin: 0 0 5px;

View File

@@ -1,5 +1,5 @@
<template> <template>
<div> <div class="redis-data-op">
<Splitpanes class="default-theme"> <Splitpanes class="default-theme">
<Pane size="20" max-size="30"> <Pane size="20" max-size="30">
<tag-tree :resource-type="TagResourceTypeEnum.Redis.value" :tag-path-node-type="NodeTypeTagPath"> <tag-tree :resource-type="TagResourceTypeEnum.Redis.value" :tag-path-node-type="NodeTypeTagPath">
@@ -34,68 +34,68 @@
</Pane> </Pane>
<Pane min-size="20" size="30"> <Pane min-size="20" size="30">
<div class="key-list-vtree"> <div class="key-list-vtree card pd5">
<el-row> <el-scrollbar>
<el-col :span="2"> <el-row>
<el-input v-model="state.keySeparator" placeholder="分割符" size="small" class="ml5" /> <el-col :span="2">
</el-col> <el-input v-model="state.keySeparator" placeholder="分割符" size="small" class="ml5" />
<el-col :span="18"> </el-col>
<el-input @clear="clear" v-model="scanParam.match" placeholder="match 支持*模糊key" clearable size="small" class="ml10" /> <el-col :span="18">
</el-col> <el-input @clear="clear" v-model="scanParam.match" placeholder="match 支持*模糊key" clearable size="small" class="ml10" />
<el-col :span="4"> </el-col>
<el-button <el-col :span="4">
class="ml15" <el-button
:disabled="!scanParam.id || !scanParam.db" class="ml15"
@click="searchKey()" :disabled="!scanParam.id || !scanParam.db"
type="success" @click="searchKey()"
icon="search" type="success"
size="small" icon="search"
plain size="small"
></el-button> plain
</el-col> ></el-button>
</el-row> </el-col>
</el-row>
<el-row class="mb5 mt5"> <el-row class="mb5 mt5">
<el-col :span="19"> <el-col :span="19">
<el-button class="ml5" :disabled="!scanParam.id || !scanParam.db" @click="scan(true)" type="success" icon="more" size="small" plain <el-button
>加载更多</el-button class="ml5"
> :disabled="!scanParam.id || !scanParam.db"
@click="scan(true)"
type="success"
icon="more"
size="small"
plain
>加载更多</el-button
>
<el-button <el-button
v-auth="'redis:data:save'" v-auth="'redis:data:save'"
:disabled="!scanParam.id || !scanParam.db" :disabled="!scanParam.id || !scanParam.db"
@click="showNewKeyDialog" @click="showNewKeyDialog"
type="primary" type="primary"
icon="plus" icon="plus"
size="small" size="small"
plain plain
>新增key</el-button >新增key</el-button
> >
<el-button <el-button
:disabled="!scanParam.id || !scanParam.db" :disabled="!scanParam.id || !scanParam.db"
@click="flushDb" @click="flushDb"
type="danger" type="danger"
plain plain
v-auth="'redis:data:del'" v-auth="'redis:data:del'"
size="small" size="small"
icon="delete" icon="delete"
>flush</el-button >flush</el-button
> >
</el-col> </el-col>
<el-col :span="5"> <el-col :span="5">
<span style="display: inline-block" class="mt5">keys:{{ state.dbsize }}</span> <span style="display: inline-block" class="mt5">keys:{{ state.dbsize }}</span>
</el-col> </el-col>
</el-row> </el-row>
<el-scrollbar
:style="{
maxHeight: state.keyTreeHeight,
height: state.keyTreeHeight,
backgroundColor: 'var(--el-fill-color-blank)',
border: '1px solid var(--el-border-color-light, #ebeef5)',
}"
>
<el-tree <el-tree
ref="keyTreeRef" ref="keyTreeRef"
:highlight-current="true" :highlight-current="true"
@@ -130,7 +130,7 @@
</Pane> </Pane>
<Pane min-size="40"> <Pane min-size="40">
<div class=""> <div class="key-detail card pd5">
<el-tabs @tab-remove="removeDataTab" v-model="state.activeName"> <el-tabs @tab-remove="removeDataTab" v-model="state.activeName">
<el-tab-pane closable v-for="dt in state.dataTabs" :key="dt.key" :label="dt.label" :name="dt.key"> <el-tab-pane closable v-for="dt in state.dataTabs" :key="dt.key" :label="dt.label" :name="dt.key">
<key-detail :redisId="scanParam.id" :db="scanParam.db" :key-info="dt.keyInfo" @change-key="searchKey()" @del-key="delKey" /> <key-detail :redisId="scanParam.id" :db="scanParam.db" :key-info="dt.keyInfo" @change-key="searchKey()" @del-key="delKey" />
@@ -178,10 +178,9 @@ import { TagTreeNode, NodeType } from '../component/tag';
import TagTree from '../component/TagTree.vue'; import TagTree from '../component/TagTree.vue';
import { keysToTree, sortByTreeNodes, keysToList } from './utils'; import { keysToTree, sortByTreeNodes, keysToList } from './utils';
import { Contextmenu, ContextmenuItem } from '@/components/contextmenu'; import { Contextmenu, ContextmenuItem } from '@/components/contextmenu';
import { sleep } from '../../../common/utils/loading'; import { sleep } from '@/common/utils/loading';
import { TagResourceTypeEnum } from '@/common/commonEnum'; import { TagResourceTypeEnum } from '@/common/commonEnum';
import { Splitpanes, Pane } from 'splitpanes'; import { Splitpanes, Pane } from 'splitpanes';
import { useEventListener } from '@vueuse/core';
const KeyDetail = defineAsyncComponent(() => import('./KeyDetail.vue')); const KeyDetail = defineAsyncComponent(() => import('./KeyDetail.vue'));
@@ -316,15 +315,7 @@ const state = reactive({
const { scanParam, keyTreeData, newKeyDialog } = toRefs(state); const { scanParam, keyTreeData, newKeyDialog } = toRefs(state);
onMounted(async () => { onMounted(async () => {});
setHeight();
// 监听浏览器窗口大小变化,更新对应组件高度
useEventListener(window, 'resize', setHeight);
});
const setHeight = () => {
state.keyTreeHeight = window.innerHeight - 165 + 'px';
};
const scan = async (appendKey = false) => { const scan = async (appendKey = false) => {
isTrue(state.scanParam.id != null, '请先选择redis'); isTrue(state.scanParam.id != null, '请先选择redis');
@@ -581,24 +572,27 @@ const delKey = (key: string) => {
</script> </script>
<style lang="scss"> <style lang="scss">
.key-list-vtree { .redis-data-op {
height: 100%; .key-list-vtree,
} .key-detail {
height: calc(100vh - 108px);
}
.key-list-vtree .folder-label { .key-list-vtree .folder-label {
font-weight: bold; font-weight: bold;
} }
.key-list-vtree .key-label { .key-list-vtree .key-label {
color: #67c23a; color: #67c23a;
} }
.key-list-vtree .key-list-custom-node { .key-list-vtree .key-list-custom-node {
width: 100%; width: 100%;
overflow: hidden; overflow: hidden;
text-overflow: ellipsis; text-overflow: ellipsis;
/*note the following 2 items should be same value, may not consist with itemSize*/ /*note the following 2 items should be same value, may not consist with itemSize*/
height: 22px; height: 22px;
line-height: 22px; line-height: 22px;
}
} }
</style> </style>

View File

@@ -107,10 +107,10 @@ defineExpose({ getContent });
.format-viewer-container .el-textarea textarea { .format-viewer-container .el-textarea textarea {
font-size: 14px; font-size: 14px;
height: calc(100vh - 536px + v-bind(height)); height: calc(100vh - 546px + v-bind(height));
} }
.format-viewer-container .monaco-editor-content { .format-viewer-container .monaco-editor-content {
height: calc(100vh - 550px + v-bind(height)) !important; height: calc(100vh - 560px + v-bind(height)) !important;
} }
</style> </style>

View File

@@ -1,8 +1,10 @@
<template> <template>
<div class="menu"> <div class="tag-tree-list card">
<div class="toolbar"> <div class="card pd10">
<el-input v-model="filterTag" placeholder="输入关键字过滤(右击进行操作)" style="width: 220px; margin-right: 10px" /> <el-input v-model="filterTag" clearable placeholder="输入关键字过滤(右击进行操作)" style="width: 220px; margin-right: 10px" />
<el-button v-auth="'tag:save'" type="primary" icon="plus" @click="showSaveTagDialog(null)">添加</el-button> <el-button v-if="useUserInfo().userInfo.username == 'admin'" v-auth="'tag:save'" type="primary" icon="plus" @click="showSaveTagDialog(null)"
>添加</el-button
>
<div style="float: right"> <div style="float: right">
<el-tooltip placement="top"> <el-tooltip placement="top">
<template #content> <template #content>
@@ -17,33 +19,34 @@
</el-tooltip> </el-tooltip>
</div> </div>
</div> </div>
<el-tree <el-scrollbar class="tag-tree-data">
ref="tagTreeRef" <el-tree
class="none-select" ref="tagTreeRef"
:indent="38" class="none-select"
node-key="id" node-key="id"
:props="props" :props="props"
:data="data" :data="data"
@node-expand="handleNodeExpand" @node-expand="handleNodeExpand"
@node-collapse="handleNodeCollapse" @node-collapse="handleNodeCollapse"
@node-contextmenu="nodeContextmenu" @node-contextmenu="nodeContextmenu"
@node-click="treeNodeClick" @node-click="treeNodeClick"
:default-expanded-keys="defaultExpandedKeys" :default-expanded-keys="defaultExpandedKeys"
:expand-on-click-node="true" :expand-on-click-node="true"
:filter-node-method="filterNode" :filter-node-method="filterNode"
> >
<template #default="{ data }"> <template #default="{ data }">
<span class="custom-tree-node"> <span class="custom-tree-node">
<span style="font-size: 13px"> <span style="font-size: 13px">
{{ data.code }} {{ data.code }}
<span style="color: #3c8dbc"></span> <span style="color: #3c8dbc"></span>
{{ data.name }} {{ data.name }}
<span style="color: #3c8dbc"></span> <span style="color: #3c8dbc"></span>
<el-tag v-if="data.children !== null" size="small">{{ data.children.length }}</el-tag> <el-tag v-if="data.children !== null" size="small">{{ data.children.length }}</el-tag>
</span>
</span> </span>
</span> </template>
</template> </el-tree>
</el-tree> </el-scrollbar>
<el-dialog width="500px" :title="saveTabDialog.title" :before-close="cancelSaveTag" v-model="saveTabDialog.visible"> <el-dialog width="500px" :title="saveTabDialog.title" :before-close="cancelSaveTag" v-model="saveTabDialog.visible">
<el-form ref="tagForm" :rules="rules" :model="saveTabDialog.form" label-width="auto"> <el-form ref="tagForm" :rules="rules" :model="saveTabDialog.form" label-width="auto">
@@ -113,6 +116,7 @@ import { TagResourceTypeEnum } from '../../../common/commonEnum';
import EnumValue from '@/common/Enum'; import EnumValue from '@/common/Enum';
import { useRouter } from 'vue-router'; import { useRouter } from 'vue-router';
import { hasPerm } from '@/components/auth/auth'; import { hasPerm } from '@/components/auth/auth';
import { useUserInfo } from '@/store/userInfo';
interface Tree { interface Tree {
id: number; id: number;
@@ -390,12 +394,14 @@ const removeDeafultExpandId = (id: any) => {
}; };
</script> </script>
<style lang="scss"> <style lang="scss">
.menu { .tag-tree-list {
height: 100%; .tag-tree-data {
height: calc(100vh - 200px);
.el-tree-node__content { .el-tree-node__content {
height: 40px; height: 40px;
line-height: 40px; line-height: 40px;
}
} }
} }

View File

@@ -1,8 +1,8 @@
<template> <template>
<div class="account-dialog"> <div class="account-dialog">
<el-dialog :title="account == null ? '' : '分配“' + account.username + '”的角色'" v-model="dialogVisible" :before-close="cancel" :show-close="false"> <el-dialog :title="account == null ? '' : '分配“' + account.username + '”的角色'" v-model="dialogVisible" :before-close="cancel" :show-close="false">
<div class="toolbar"> <div class="card pd5">
<div style="float: left"> <div>
<el-input placeholder="请输入角色名" style="width: 150px" v-model="query.name" @clear="clear()" clearable> </el-input> <el-input placeholder="请输入角色名" style="width: 150px" v-model="query.name" @clear="clear()" clearable> </el-input>
<el-button class="ml5" @click="search" type="success" icon="search"></el-button> <el-button class="ml5" @click="search" type="success" icon="search"></el-button>
</div> </div>
@@ -17,15 +17,17 @@
</template> </template>
</el-table-column> </el-table-column>
</el-table> </el-table>
<el-pagination <el-row type="flex" justify="end">
@current-change="handlePageChange" <el-pagination
style="text-align: center; margin-top: 20px" @current-change="handlePageChange"
background style="text-align: center; margin-top: 20px"
layout="prev, pager, next, total, jumper" background
:total="total" layout="prev, pager, next, total, jumper"
v-model:current-page="query.pageNum" :total="total"
:page-size="query.pageSize" v-model:current-page="query.pageNum"
></el-pagination> :page-size="query.pageSize"
></el-pagination>
</el-row>
<template #footer> <template #footer>
<div class="dialog-footer"> <div class="dialog-footer">

View File

@@ -1,44 +1,52 @@
<template> <template>
<div class="menu"> <div class="card system-resouce-list">
<div class="toolbar"> <div class="card pd10 flex-justify-between">
<div> <div>
<span style="font-size: 14px"> <SvgIcon name="info-filled" />红色橙色字体表示禁用状态 (右击资源进行操作) </span> <el-input v-model="filterResource" clearable placeholder="输入关键字过滤(右击进行操作)" style="width: 220px; margin-right: 10px" />
<el-button v-auth="perms.addResource" type="primary" icon="plus" @click="addResource(false)">添加</el-button>
</div>
<div>
<span> <SvgIcon name="info-filled" />红色橙色字体表示禁用状态 (右击资源进行操作) </span>
</div> </div>
<el-button v-auth="perms.addResource" type="primary" icon="plus" @click="addResource(false)">添加</el-button>
</div> </div>
<el-tree <el-scrollbar class="tree-data">
class="none-select" <el-tree
:indent="38" ref="resourceTreeRef"
node-key="id" class="none-select"
:props="props" :indent="38"
:data="data" node-key="id"
@node-expand="handleNodeExpand" :props="props"
@node-collapse="handleNodeCollapse" :data="data"
@node-contextmenu="nodeContextmenu" @node-expand="handleNodeExpand"
@node-click="treeNodeClick" @node-collapse="handleNodeCollapse"
:default-expanded-keys="defaultExpandedKeys" @node-contextmenu="nodeContextmenu"
:expand-on-click-node="true" @node-click="treeNodeClick"
draggable :default-expanded-keys="defaultExpandedKeys"
:allow-drop="allowDrop" :expand-on-click-node="true"
@node-drop="handleDrop" draggable
> :allow-drop="allowDrop"
<template #default="{ data }"> @node-drop="handleDrop"
<span class="custom-tree-node"> :filter-node-method="filterNode"
<span style="font-size: 13px" v-if="data.type === menuTypeValue"> >
<span style="color: #3c8dbc"></span> <template #default="{ data }">
<span v-if="data.status == 1">{{ data.name }}</span> <span class="custom-tree-node">
<span v-if="data.status == -1" style="color: #e6a23c">{{ data.name }}</span> <span style="font-size: 13px" v-if="data.type === menuTypeValue">
<span style="color: #3c8dbc"></span> <span style="color: #3c8dbc"></span>
<el-tag v-if="data.children !== null" size="small">{{ data.children.length }}</el-tag> <span v-if="data.status == 1">{{ data.name }}</span>
<span v-if="data.status == -1" style="color: #e6a23c">{{ data.name }}</span>
<span style="color: #3c8dbc"></span>
<el-tag v-if="data.children !== null" size="small">{{ data.children.length }}</el-tag>
</span>
<span style="font-size: 13px" v-if="data.type === permissionTypeValue">
<span style="color: #3c8dbc"></span>
<span :style="data.status == 1 ? 'color: #67c23a;' : 'color: #f67c6c;'">{{ data.name }}</span>
<span style="color: #3c8dbc"></span>
</span>
</span> </span>
<span style="font-size: 13px" v-if="data.type === permissionTypeValue"> </template>
<span style="color: #3c8dbc"></span> </el-tree>
<span :style="data.status == 1 ? 'color: #67c23a;' : 'color: #f67c6c;'">{{ data.name }}</span> </el-scrollbar>
<span style="color: #3c8dbc"></span>
</span>
</span>
</template>
</el-tree>
<ResourceEdit <ResourceEdit
:title="dialogForm.title" :title="dialogForm.title"
@@ -94,7 +102,7 @@
</template> </template>
<script lang="ts" setup> <script lang="ts" setup>
import { ref, toRefs, reactive, onMounted } from 'vue'; import { ref, toRefs, reactive, onMounted, watch } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus'; import { ElMessage, ElMessageBox } from 'element-plus';
import ResourceEdit from './ResourceEdit.vue'; import ResourceEdit from './ResourceEdit.vue';
import { ResourceTypeEnum } from '../enums'; import { ResourceTypeEnum } from '../enums';
@@ -119,6 +127,8 @@ const props = {
}; };
const contextmenuRef = ref(); const contextmenuRef = ref();
const filterResource = ref();
const resourceTreeRef = ref();
const contextmenuInfo = new ContextmenuItem('info', '详情').withIcon('View').withOnClick((data: any) => info(data)); const contextmenuInfo = new ContextmenuItem('info', '详情').withIcon('View').withOnClick((data: any) => info(data));
@@ -195,6 +205,17 @@ onMounted(() => {
search(); search();
}); });
watch(filterResource, (val) => {
resourceTreeRef.value!.filter(val);
});
const filterNode = (value: string, data: any) => {
if (!value) {
return true;
}
return data.name.includes(value);
};
const search = async () => { const search = async () => {
let res = await resourceApi.list.request(null); let res = await resourceApi.list.request(null);
state.data = res; state.data = res;
@@ -380,11 +401,15 @@ const info = async (data: any) => {
}; };
</script> </script>
<style lang="scss"> <style lang="scss">
.menu { .system-resouce-list {
.el-tree-node__content { .el-tree-node__content {
height: 40px; height: 40px;
line-height: 40px; line-height: 40px;
} }
.tree-data {
height: calc(100vh - 200px);
}
} }
.none-select { .none-select {

View File

@@ -18,7 +18,7 @@ require (
github.com/gorilla/websocket v1.5.1 github.com/gorilla/websocket v1.5.1
github.com/kanzihuang/vitess/go/vt/sqlparser v0.0.0-20231018071450-ac8d9f0167e9 github.com/kanzihuang/vitess/go/vt/sqlparser v0.0.0-20231018071450-ac8d9f0167e9
github.com/lionsoul2014/ip2region/binding/golang v0.0.0-20230712084735-068dc2aee82d github.com/lionsoul2014/ip2region/binding/golang v0.0.0-20230712084735-068dc2aee82d
github.com/mojocn/base64Captcha v1.3.5 // github.com/mojocn/base64Captcha v1.3.6 //
github.com/pkg/errors v0.9.1 github.com/pkg/errors v0.9.1
github.com/pkg/sftp v1.13.6 github.com/pkg/sftp v1.13.6
github.com/pquerna/otp v1.4.0 github.com/pquerna/otp v1.4.0
@@ -78,7 +78,7 @@ require (
github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d // indirect
golang.org/x/arch v0.3.0 // indirect golang.org/x/arch v0.3.0 // indirect
golang.org/x/exp v0.0.0-20230519143937-03e91628a987 golang.org/x/exp v0.0.0-20230519143937-03e91628a987
golang.org/x/image v0.0.0-20220302094943-723b81ca9867 // indirect golang.org/x/image v0.13.0 // indirect
golang.org/x/net v0.18.0 // indirect golang.org/x/net v0.18.0 // indirect
golang.org/x/sync v0.1.0 // indirect golang.org/x/sync v0.1.0 // indirect
golang.org/x/sys v0.15.0 // indirect golang.org/x/sys v0.15.0 // indirect

View File

@@ -88,9 +88,12 @@ func (p *tagTreeAppImpl) Save(ctx context.Context, tag *entity.TagTree) error {
tag.CodePath = parentTag.CodePath + tag.Code + entity.CodePathSeparator tag.CodePath = parentTag.CodePath + tag.Code + entity.CodePathSeparator
} else { } else {
if accountId != consts.AdminId {
return errorx.NewBiz("非管理员无法添加根标签")
}
tag.CodePath = tag.Code + entity.CodePathSeparator tag.CodePath = tag.Code + entity.CodePathSeparator
} }
if err := p.CanAccess(accountId, tag.CodePath); err != nil { if p.CanAccess(accountId, tag.CodePath) != nil {
return errorx.NewBiz("无权添加该标签") return errorx.NewBiz("无权添加该标签")
} }

View File

@@ -22,7 +22,8 @@ func Generate() (string, string, error) {
c := base64Captcha.NewCaptcha(driver, store) c := base64Captcha.NewCaptcha(driver, store)
// 获取 // 获取
return c.Generate() id, b64s, _, err := c.Generate()
return id, b64s, err
} }
// 验证验证码 // 验证验证码