refactor: 标签资源重构

This commit is contained in:
meilin.huang
2024-04-06 18:19:17 +08:00
parent 582d879a77
commit 408bac09a1
52 changed files with 476 additions and 347 deletions

View File

@@ -22,6 +22,8 @@ export class EnumValue {
*/
tag: EnumValueTag;
extra: any;
constructor(value: any, label: string) {
this.value = value;
this.label = label;
@@ -53,6 +55,11 @@ export class EnumValue {
return this;
}
setExtra(extra: any): EnumValue {
this.extra = extra;
return this;
}
public static of(value: any, label: string): EnumValue {
return new EnumValue(value, label);
}
@@ -60,11 +67,12 @@ export class EnumValue {
/**
* 根据枚举值获取指定枚举值对象
*
* @param enumValues 所有枚举值
* @param enums 枚举对象
* @param value 需要匹配的枚举值
* @returns 枚举值对象
*/
static getEnumByValue(enumValues: EnumValue[], value: any): EnumValue | null {
static getEnumByValue(enums: any, value: any): EnumValue | null {
const enumValues = Object.values(enums) as any;
for (let enumValue of enumValues) {
if (enumValue.value == value) {
return enumValue;

View File

@@ -2,8 +2,9 @@ import EnumValue from './Enum';
// 标签关联的资源类型
export const TagResourceTypeEnum = {
Machine: EnumValue.of(1, '机器'),
Db: EnumValue.of(2, '数据库'),
Redis: EnumValue.of(3, 'redis'),
Mongo: EnumValue.of(4, 'mongo'),
Tag: EnumValue.of(-1, '标签').setExtra({ icon: 'CollectionTag' }),
Machine: EnumValue.of(1, '机器').setExtra({ icon: 'Monitor' }),
Db: EnumValue.of(2, '数据库').setExtra({ icon: 'Coin' }),
Redis: EnumValue.of(3, 'redis').setExtra({ icon: 'iconfont icon-redis' }),
Mongo: EnumValue.of(4, 'mongo').setExtra({ icon: 'iconfont icon-mongo' }),
};

View File

@@ -2,3 +2,8 @@ export const AccountUsernamePattern = {
pattern: /^[a-zA-Z0-9_]{5,20}$/g,
message: '只允许输入5-20位大小写字母、数字、下划线',
};
export const ResourceCodePattern = {
pattern: /^[a-zA-Z0-9_]{1,32}$/g,
message: '只允许输入1-32位大小写字母、数字、下划线',
};

View File

@@ -40,7 +40,7 @@ onMounted(() => {
});
const convert = (value: any) => {
const enumValue = EnumValue.getEnumByValue(Object.values(props.enums as any) as any, value) as any;
const enumValue = EnumValue.getEnumByValue(props.enums, value) as any;
if (!enumValue) {
state.enumLabel = '-';
state.type = 'danger';

View File

@@ -58,7 +58,7 @@ onMounted(async () => {
state.selectTags = props.selectTags;
}
state.tags = await tagApi.getTagTrees.request(null);
state.tags = await tagApi.getTagTrees.request({ type: -1 });
});
const changeTag = () => {

View File

@@ -47,6 +47,14 @@
</el-select>
</el-form-item>
<el-form-item prop="code" label="编号" required>
<el-input
:disabled="form.id"
v-model.trim="form.code"
placeholder="请输入机器编号 (数字字母下划线), 不可修改"
auto-complete="off"
></el-input>
</el-form-item>
<el-form-item prop="name" label="别名" required>
<el-input v-model.trim="form.name" placeholder="请输入数据库别名" auto-complete="off"></el-input>
</el-form-item>
@@ -96,6 +104,7 @@ import TagTreeSelect from '../component/TagTreeSelect.vue';
import type { CheckboxValueType } from 'element-plus';
import ProcdefSelectFormItem from '@/views/flow/components/ProcdefSelectFormItem.vue';
import { DbType } from '@/views/ops/db/dialect';
import { ResourceCodePattern } from '@/common/pattern';
const props = defineProps({
visible: {
@@ -128,7 +137,18 @@ const rules = {
trigger: ['change', 'blur'],
},
],
code: [
{
required: true,
message: '请输入编码',
trigger: ['change', 'blur'],
},
{
pattern: ResourceCodePattern.pattern,
message: ResourceCodePattern.message,
trigger: ['blur'],
},
],
name: [
{
required: true,

View File

@@ -239,10 +239,15 @@ const perms = {
restoreDb: 'db:restore',
};
const searchItems = [getTagPathSearchItem(TagResourceTypeEnum.Db.value), SearchItem.slot('instanceId', '实例', 'instanceSelect')];
const searchItems = [
getTagPathSearchItem(TagResourceTypeEnum.Db.value),
SearchItem.slot('instanceId', '实例', 'instanceSelect'),
SearchItem.input('code', '编号'),
];
const columns = ref([
TableColumn.new('tags[0].tagPath', '关联标签').isSlot('tagPath').setAddWidth(20),
TableColumn.new('code', '编号'),
TableColumn.new('name', '名称'),
TableColumn.new('type', '类型').isSlot().setAddWidth(-15).alignCenter(),
TableColumn.new('instanceName', '实例名'),

View File

@@ -1,6 +1,10 @@
<template>
<div>
<el-dialog :title="title" v-model="dialogVisible" :close-on-click-modal="false" :destroy-on-close="true" :before-close="cancel" width="650px">
<el-drawer :title="title" v-model="dialogVisible" :before-close="cancel" :destroy-on-close="true" :close-on-click-modal="false">
<template #header>
<DrawerHeader :header="title" :back="cancel" />
</template>
<el-form :model="form" ref="machineForm" :rules="rules" label-width="auto">
<el-tabs v-model="tabActiveName">
<el-tab-pane label="基础信息" name="basic">
@@ -18,19 +22,25 @@
style="width: 100%"
/>
</el-form-item>
<el-form-item prop="protocol" label="机器类型" required>
<el-radio-group v-model="form.protocol" @change="handleChangeProtocol">
<el-radio :value="1">SSH</el-radio>
<el-radio :value="2">RDP</el-radio>
<!-- <el-radio :value="3">VNC</el-radio> -->
</el-radio-group>
<el-form-item prop="code" label="编号" required>
<el-input
:disabled="form.id"
v-model.trim="form.code"
placeholder="请输入机器编号 (数字字母下划线), 不可修改"
auto-complete="off"
></el-input>
</el-form-item>
<el-form-item prop="name" label="名称" required>
<el-input v-model.trim="form.name" placeholder="请输入机器别名" auto-complete="off"></el-input>
</el-form-item>
<el-form-item prop="ip" label="ip" req uired>
<el-form-item prop="protocol" label="协议" required>
<el-radio-group v-model="form.protocol" @change="handleChangeProtocol">
<el-radio v-for="item in MachineProtocolEnum" :key="item.value" :label="item.label" :value="item.value"></el-radio>
</el-radio-group>
</el-form-item>
<el-form-item prop="ip" label="ip" required>
<el-col :span="18">
<el-input :disabled="form.id" v-model.trim="form.ip" placeholder="主机ip" auto-complete="off"> </el-input>
<el-input v-model.trim="form.ip" placeholder="主机ip" auto-complete="off"> </el-input>
</el-col>
<el-col style="text-align: center" :span="1">:</el-col>
<el-col :span="5">
@@ -81,7 +91,7 @@
<el-button type="primary" :loading="saveBtnLoading" @click="btnOk">确 定</el-button>
</div>
</template>
</el-dialog>
</el-drawer>
</div>
</template>
@@ -92,6 +102,9 @@ import { ElMessage } from 'element-plus';
import TagTreeSelect from '../component/TagTreeSelect.vue';
import SshTunnelSelect from '../component/SshTunnelSelect.vue';
import AuthCertSelect from './authcert/AuthCertSelect.vue';
import { MachineProtocolEnum } from './enums';
import DrawerHeader from '@/components/drawer-header/DrawerHeader.vue';
import { ResourceCodePattern } from '@/common/pattern';
const props = defineProps({
visible: {
@@ -116,6 +129,18 @@ const rules = {
trigger: ['change'],
},
],
code: [
{
required: true,
message: '请输入编码',
trigger: ['change', 'blur'],
},
{
pattern: ResourceCodePattern.pattern,
message: ResourceCodePattern.message,
trigger: ['blur'],
},
],
name: [
{
required: true,
@@ -134,7 +159,7 @@ const rules = {
{
required: true,
message: '请输入主机ip和端口',
trigger: ['change', 'blur'],
trigger: ['blur'],
},
],
authCertId: [
@@ -263,11 +288,11 @@ const getReqForm = () => {
};
const handleChangeProtocol = (val: any) => {
if (val == 1) {
if (val == MachineProtocolEnum.Ssh.value) {
state.form.port = 22;
} else if (val == 2) {
} else if (val == MachineProtocolEnum.Rdp.value) {
state.form.port = 3389;
} else if (val == 3) {
} else {
state.form.port = 5901;
}
};

View File

@@ -251,10 +251,16 @@ const perms = {
terminal: 'machine:terminal',
};
const searchItems = [getTagPathSearchItem(TagResourceTypeEnum.Machine.value), SearchItem.input('ip', 'IP'), SearchItem.input('name', '名称')];
const searchItems = [
getTagPathSearchItem(TagResourceTypeEnum.Machine.value),
SearchItem.input('code', '编号'),
SearchItem.input('ip', 'IP'),
SearchItem.input('name', '名称'),
];
const columns = [
TableColumn.new('tags[0].tagPath', '关联标签').isSlot('tagPath').setAddWidth(20),
TableColumn.new('code', '编号'),
TableColumn.new('name', '名称'),
TableColumn.new('ipPort', 'ip:port').isSlot().setAddWidth(50),
TableColumn.new('stat', '运行状态').isSlot().setAddWidth(55),

View File

@@ -1,5 +1,10 @@
import { EnumValue } from '@/common/Enum';
export const MachineProtocolEnum = {
Ssh: EnumValue.of(1, 'SSH'),
Rdp: EnumValue.of(2, 'RDP'),
};
// 脚本执行结果类型
export const ScriptResultEnum = {
Result: EnumValue.of(1, '有结果'),

View File

@@ -17,7 +17,14 @@
style="width: 100%"
/>
</el-form-item>
<el-form-item prop="code" label="编号" required>
<el-input
:disabled="form.id"
v-model.trim="form.code"
placeholder="请输入机器编号 (数字字母下划线), 不可修改"
auto-complete="off"
></el-input>
</el-form-item>
<el-form-item prop="name" label="名称" required>
<el-input v-model.trim="form.name" placeholder="请输入名称" auto-complete="off"></el-input>
</el-form-item>
@@ -57,6 +64,7 @@ import { mongoApi } from './api';
import { ElMessage } from 'element-plus';
import TagTreeSelect from '../component/TagTreeSelect.vue';
import SshTunnelSelect from '../component/SshTunnelSelect.vue';
import { ResourceCodePattern } from '@/common/pattern';
const props = defineProps({
visible: {
@@ -81,6 +89,18 @@ const rules = {
trigger: ['change', 'blur'],
},
],
code: [
{
required: true,
message: '请输入编码',
trigger: ['change', 'blur'],
},
{
pattern: ResourceCodePattern.pattern,
message: ResourceCodePattern.message,
trigger: ['blur'],
},
],
name: [
{
required: true,

View File

@@ -52,6 +52,7 @@ import { TableColumn } from '@/components/pagetable';
import { TagResourceTypeEnum } from '@/common/commonEnum';
import { useRoute } from 'vue-router';
import { getTagPathSearchItem } from '../component/tag';
import { SearchItem } from '@/components/SearchForm';
const MongoEdit = defineAsyncComponent(() => import('./MongoEdit.vue'));
const MongoDbs = defineAsyncComponent(() => import('./MongoDbs.vue'));
@@ -67,10 +68,11 @@ const props = defineProps({
const route = useRoute();
const pageTableRef: Ref<any> = ref(null);
const searchItems = [getTagPathSearchItem(TagResourceTypeEnum.Mongo.value)];
const searchItems = [getTagPathSearchItem(TagResourceTypeEnum.Mongo.value), SearchItem.input('code', '编号')];
const columns = [
TableColumn.new('tags[0].tagPath', '关联标签').isSlot('tagPath').setAddWidth(20),
TableColumn.new('code', '编号'),
TableColumn.new('name', '名称'),
TableColumn.new('uri', '连接uri'),
TableColumn.new('createTime', '创建时间').isTime(),

View File

@@ -1,6 +1,10 @@
<template>
<div>
<el-dialog :title="title" v-model="dialogVisible" :before-close="cancel" :close-on-click-modal="false" :destroy-on-close="true" width="38%">
<el-drawer :title="title" v-model="dialogVisible" :before-close="cancel" :destroy-on-close="true" :close-on-click-modal="false">
<template #header>
<DrawerHeader :header="title" :back="cancel" />
</template>
<el-form :model="form" ref="redisForm" :rules="rules" label-width="auto">
<el-tabs v-model="tabActiveName">
<el-tab-pane label="基础信息" name="basic">
@@ -17,6 +21,14 @@
style="width: 100%"
/>
</el-form-item>
<el-form-item prop="code" label="编号" required>
<el-input
:disabled="form.id"
v-model.trim="form.code"
placeholder="请输入机器编号 (数字字母下划线), 不可修改"
auto-complete="off"
></el-input>
</el-form-item>
<el-form-item prop="name" label="名称" required>
<el-input v-model.trim="form.name" placeholder="请输入redis名称" auto-complete="off"></el-input>
</el-form-item>
@@ -90,7 +102,7 @@
<el-button type="primary" :loading="saveBtnLoading" @click="btnOk">确 定</el-button>
</div>
</template>
</el-dialog>
</el-drawer>
</div>
</template>
@@ -102,6 +114,8 @@ import { RsaEncrypt } from '@/common/rsa';
import TagTreeSelect from '../component/TagTreeSelect.vue';
import SshTunnelSelect from '../component/SshTunnelSelect.vue';
import ProcdefSelectFormItem from '@/views/flow/components/ProcdefSelectFormItem.vue';
import { ResourceCodePattern } from '@/common/pattern';
import DrawerHeader from '@/components/drawer-header/DrawerHeader.vue';
const props = defineProps({
visible: {
@@ -125,6 +139,18 @@ const rules = {
trigger: ['blur', 'change'],
},
],
code: [
{
required: true,
message: '请输入编码',
trigger: ['change', 'blur'],
},
{
pattern: ResourceCodePattern.pattern,
message: ResourceCodePattern.message,
trigger: ['blur'],
},
],
name: [
{
required: true,

View File

@@ -161,6 +161,7 @@ import { TableColumn } from '@/components/pagetable';
import { TagResourceTypeEnum } from '@/common/commonEnum';
import { useRoute } from 'vue-router';
import { getTagPathSearchItem } from '../component/tag';
import { SearchItem } from '@/components/SearchForm';
const props = defineProps({
lazy: {
@@ -172,10 +173,11 @@ const props = defineProps({
const route = useRoute();
const pageTableRef: Ref<any> = ref(null);
const searchItems = [getTagPathSearchItem(TagResourceTypeEnum.Redis.value)];
const searchItems = [getTagPathSearchItem(TagResourceTypeEnum.Redis.value), SearchItem.input('code', '编号')];
const columns = ref([
TableColumn.new('tags[0].tagPath', '关联标签').isSlot('tagPath').setAddWidth(20),
TableColumn.new('code', '编号'),
TableColumn.new('name', '名称'),
TableColumn.new('host', 'host:port'),
TableColumn.new('mode', 'mode'),

View File

@@ -43,7 +43,9 @@
>
<template #default="{ data }">
<span class="custom-tree-node">
<span style="font-size: 13px">
<SvgIcon :name="EnumValue.getEnumByValue(TagResourceTypeEnum, data.type)?.extra.icon" />
<span class="ml5">
{{ data.code }}
<span style="color: #3c8dbc"></span>
{{ data.name }}
@@ -61,6 +63,10 @@
<el-tabs @tab-change="tabChange" v-model="state.activeTabName" v-if="currentTag">
<el-tab-pane label="标签详情" :name="TagDetail">
<el-descriptions :column="2" border>
<el-descriptions-item label="类型">
<EnumTag :enums="TagResourceTypeEnum" :value="currentTag.type" />
</el-descriptions-item>
<el-descriptions-item label="code">{{ currentTag.code }}</el-descriptions-item>
<el-descriptions-item label="code路径">{{ currentTag.codePath }}</el-descriptions-item>
<el-descriptions-item label="名称">{{ currentTag.name }}</el-descriptions-item>
@@ -73,19 +79,19 @@
</el-descriptions>
</el-tab-pane>
<el-tab-pane :label="`机器 (${resourceCount.machine})`" :name="MachineTag">
<el-tab-pane v-if="currentTag.type == TagResourceTypeEnum.Tag.value" :label="`机器 (${resourceCount.machine})`" :name="MachineTag">
<MachineList lazy ref="machineListRef" />
</el-tab-pane>
<el-tab-pane :label="`数据库 (${resourceCount.db})`" :name="DbTag">
<el-tab-pane v-if="currentTag.type == TagResourceTypeEnum.Tag.value" :label="`数据库 (${resourceCount.db})`" :name="DbTag">
<DbList lazy ref="dbListRef" />
</el-tab-pane>
<el-tab-pane :label="`Redis (${resourceCount.redis})`" :name="RedisTag">
<el-tab-pane v-if="currentTag.type == TagResourceTypeEnum.Tag.value" :label="`Redis (${resourceCount.redis})`" :name="RedisTag">
<RedisList lazy ref="redisListRef" />
</el-tab-pane>
<el-tab-pane :label="`Mongo (${resourceCount.mongo})`" :name="MongoTag">
<el-tab-pane v-if="currentTag.type == TagResourceTypeEnum.Tag.value" :label="`Mongo (${resourceCount.mongo})`" :name="MongoTag">
<MongoList lazy ref="mongoListRef" />
</el-tab-pane>
</el-tabs>
@@ -129,6 +135,9 @@ import MachineList from '../machine/MachineList.vue';
import RedisList from '../redis/RedisList.vue';
import MongoList from '../mongo/MongoList.vue';
import DbList from '../db/DbList.vue';
import { TagResourceTypeEnum } from '@/common/commonEnum';
import EnumTag from '@/components/enumtag/EnumTag.vue';
import EnumValue from '@/common/Enum';
interface Tree {
id: number;
@@ -155,6 +164,10 @@ const MongoTag = 'mongoTag';
const contextmenuAdd = new ContextmenuItem('addTag', '添加子标签')
.withIcon('circle-plus')
.withPermission('tag:save')
.withHideFunc((data: any) => {
// 非标签类型不可添加子标签
return data.type != -1;
})
.withOnClick((data: any) => showSaveTagDialog(data));
const contextmenuEdit = new ContextmenuItem('edit', '编辑')
@@ -167,7 +180,7 @@ const contextmenuDel = new ContextmenuItem('delete', '删除')
.withPermission('tag:del')
.withHideFunc((data: any) => {
// 存在子标签,则不允许删除
return data.children;
return data.children || data.type != -1;
})
.withOnClick((data: any) => deleteTag(data));

View File

@@ -66,7 +66,9 @@
>
<template #default="{ data }">
<span class="custom-tree-node">
<span style="font-size: 13px">
<SvgIcon :name="EnumValue.getEnumByValue(TagResourceTypeEnum, data.type)?.extra.icon" />
<span class="font13 ml5">
{{ data.code }}
<span style="color: #3c8dbc"></span>
{{ data.name }}
@@ -128,6 +130,8 @@ import PageTable from '@/components/pagetable/PageTable.vue';
import { TableColumn } from '@/components/pagetable';
import { SearchItem } from '@/components/SearchForm';
import AccountSelectFormItem from '@/views/system/account/components/AccountSelectFormItem.vue';
import { TagResourceTypeEnum } from '@/common/commonEnum';
import EnumValue from '@/common/Enum';
const teamForm: any = ref(null);
const tagTreeRef: any = ref(null);

View File

@@ -7,7 +7,6 @@ export const tagApi = {
delTagTree: Api.newDelete('/tag-trees/{id}'),
getResourceTagPaths: Api.newGet('/tag-trees/resources/{resourceType}/tag-paths'),
getTagResources: Api.newGet('/tag-trees/resources'),
countTagResource: Api.newGet('/tag-trees/resources/count'),
getTeams: Api.newGet('/teams'),