mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-01 23:10:26 +08:00
refactor: 标签不可移动,资源选择优化等
This commit is contained in:
@@ -11,23 +11,23 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@element-plus/icons-vue": "^2.3.2",
|
||||
"@logicflow/core": "^2.1.1",
|
||||
"@logicflow/extension": "^2.1.2",
|
||||
"@logicflow/core": "^2.1.2",
|
||||
"@logicflow/extension": "^2.1.3",
|
||||
"@vueuse/core": "^13.9.0",
|
||||
"@xterm/addon-fit": "^0.10.0",
|
||||
"@xterm/addon-search": "^0.15.0",
|
||||
"@xterm/addon-web-links": "^0.11.0",
|
||||
"@xterm/xterm": "^5.5.0",
|
||||
"asciinema-player": "^3.10.0",
|
||||
"asciinema-player": "^3.11.0",
|
||||
"axios": "^1.6.2",
|
||||
"clipboard": "^2.0.11",
|
||||
"crypto-js": "^4.2.0",
|
||||
"dayjs": "^1.11.18",
|
||||
"echarts": "^6.0.0",
|
||||
"element-plus": "^2.11.2",
|
||||
"element-plus": "^2.11.4",
|
||||
"js-base64": "^3.7.8",
|
||||
"jsencrypt": "^3.5.4",
|
||||
"monaco-editor": "^0.53.0",
|
||||
"monaco-editor": "^0.54.0",
|
||||
"monaco-sql-languages": "^0.15.1",
|
||||
"monaco-themes": "^0.4.7",
|
||||
"nprogress": "^0.2.0",
|
||||
@@ -38,13 +38,13 @@
|
||||
"sql-formatter": "^15.6.8",
|
||||
"trzsz": "^1.1.5",
|
||||
"uuid": "^13.0.0",
|
||||
"vue": "^v3.6.0-alpha.2",
|
||||
"vue": "^v3.5.22",
|
||||
"vue-i18n": "^11.1.12",
|
||||
"vue-router": "^4.5.1",
|
||||
"vuedraggable": "^4.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/vite": "^4.1.13",
|
||||
"@tailwindcss/vite": "^4.1.14",
|
||||
"@types/crypto-js": "^4.2.2",
|
||||
"@types/node": "^22.13.14",
|
||||
"@types/nprogress": "^0.2.0",
|
||||
@@ -56,11 +56,11 @@
|
||||
"autoprefixer": "^10.4.21",
|
||||
"code-inspector-plugin": "^1.0.4",
|
||||
"eslint": "^9.29.0",
|
||||
"eslint-plugin-vue": "^10.4.0",
|
||||
"eslint-plugin-vue": "^10.5.0",
|
||||
"postcss": "^8.5.6",
|
||||
"prettier": "^3.6.1",
|
||||
"sass": "^1.92.1",
|
||||
"tailwindcss": "^4.1.13",
|
||||
"sass": "^1.93.2",
|
||||
"tailwindcss": "^4.1.14",
|
||||
"typescript": "^5.9.2",
|
||||
"vite": "npm:rolldown-vite@latest",
|
||||
"vite-plugin-progress": "0.0.7",
|
||||
|
||||
@@ -15,7 +15,7 @@ const config = {
|
||||
baseWsUrl: `${(window as any).globalConfig.BaseWsUrl || `${location.protocol == 'https:' ? 'wss:' : 'ws:'}//${getBaseApiUrl()}`}/api`,
|
||||
|
||||
// 系统版本
|
||||
version: 'v1.10.3',
|
||||
version: 'v1.10.4',
|
||||
};
|
||||
|
||||
export default config;
|
||||
|
||||
@@ -34,7 +34,6 @@ const props = defineProps({
|
||||
},
|
||||
placement: {
|
||||
type: String,
|
||||
required: true,
|
||||
default: 'top',
|
||||
},
|
||||
});
|
||||
|
||||
@@ -7,6 +7,7 @@ export default {
|
||||
tagTips1: '1. Used to group assets',
|
||||
tagTips2: '2. Can be allocated in team management for resource isolation',
|
||||
tagTips3: '3. Team members who own a parent tag have access to resources that manipulate their own or child tag associations',
|
||||
tagTips4: '4. Right-click nodes to edit or add child tags',
|
||||
machine: 'Machine',
|
||||
db: 'Db',
|
||||
code: 'Code',
|
||||
|
||||
@@ -7,6 +7,7 @@ export default {
|
||||
tagTips1: '1. 用于将资产进行归类',
|
||||
tagTips2: '2. 可在团队管理中进行分配,用于资源隔离',
|
||||
tagTips3: '3. 拥有父标签的团队成员可访问操作其自身或子标签关联的资源',
|
||||
tagTips4: '4. 右击节点可进行编辑或添加子标签操作',
|
||||
machine: '机器',
|
||||
db: '数据库',
|
||||
es: 'ES',
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<template>
|
||||
<el-form :model="bizForm" ref="formRef" :rules="rules" label-width="auto">
|
||||
<el-form-item prop="id" label="DB" required>
|
||||
<TagTreeResourceSelect
|
||||
<ResourceSelect
|
||||
v-bind="$attrs"
|
||||
v-model="selectRedis"
|
||||
@change="changeRedis"
|
||||
@@ -9,7 +9,7 @@
|
||||
:tag-path-node-type="NodeTypeTagPath"
|
||||
:placeholder="$t('flow.selectRedisPlaceholder')"
|
||||
>
|
||||
</TagTreeResourceSelect>
|
||||
</ResourceSelect>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item prop="cmd" label="CMD" required>
|
||||
@@ -21,12 +21,13 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref } from 'vue';
|
||||
import { TagResourceTypeEnum } from '@/common/commonEnum';
|
||||
import TagTreeResourceSelect from '@/views/ops/component/TagTreeResourceSelect.vue';
|
||||
import ResourceSelect from '@/views/ops/resource/ResourceSelect.vue';
|
||||
import { NodeType, TagTreeNode } from '@/views/ops/component/tag';
|
||||
import { redisApi } from '@/views/ops/redis/api';
|
||||
import { sleep } from '@/common/utils/loading';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { Rules } from '@/common/rule';
|
||||
import { RedisIcon } from '@/views/ops/redis/resource';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
@@ -52,7 +53,7 @@ const NodeTypeTagPath = new NodeType(TagTreeNode.TagPath).withLoadNodesFunc(asyn
|
||||
await sleep(100);
|
||||
return redisInfos.map((x: any) => {
|
||||
x.tagPath = parentNode.key;
|
||||
return new TagTreeNode(`${x.code}`, x.name, NodeTypeRedis).withParams(x);
|
||||
return new TagTreeNode(`${x.code}`, x.name, NodeTypeRedis).withParams(x).withIcon(RedisIcon);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -61,15 +62,18 @@ const NodeTypeRedis = new NodeType(1).withLoadNodesFunc(async (parentNode: TagTr
|
||||
const redisInfo = parentNode.params;
|
||||
|
||||
let dbs: TagTreeNode[] = redisInfo.db.split(',').map((x: string) => {
|
||||
return new TagTreeNode(x, `db${x}`, 2 as any).withIsLeaf(true).withParams({
|
||||
id: redisInfo.id,
|
||||
db: x,
|
||||
name: `db${x}`,
|
||||
keys: 0,
|
||||
tagPath: redisInfo.tagPath,
|
||||
redisName: redisInfo.name,
|
||||
code: redisInfo.code,
|
||||
});
|
||||
return new TagTreeNode(x, `db${x}`, 2 as any)
|
||||
.withIsLeaf(true)
|
||||
.withParams({
|
||||
id: redisInfo.id,
|
||||
db: x,
|
||||
name: `db${x}`,
|
||||
keys: 0,
|
||||
tagPath: redisInfo.tagPath,
|
||||
redisName: redisInfo.name,
|
||||
code: redisInfo.code,
|
||||
})
|
||||
.withIcon({ name: 'Coin', color: '#67c23a' });
|
||||
});
|
||||
|
||||
if (redisInfo.mode == 'cluster') {
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<el-form :model="form" ref="dbForm" :rules="rules" label-position="top" label-width="auto">
|
||||
<el-tabs v-model="tabActiveName">
|
||||
<el-tab-pane :label="$t('common.basic')" :name="basicTab">
|
||||
<el-row gutter="10">
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="taskName" :label="$t('db.taskName')" required>
|
||||
<el-input v-model.trim="form.taskName" auto-complete="off" />
|
||||
@@ -59,7 +59,7 @@
|
||||
<monaco-editor height="200px" class="task-sql" language="sql" v-model="form.dataSql" />
|
||||
</el-form-item>
|
||||
|
||||
<el-row gutter="10">
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="12">
|
||||
<el-form-item prop="targetTableName" :label="$t('db.targetDbTable')" required>
|
||||
<el-select v-model="form.targetTableName" filterable>
|
||||
@@ -80,7 +80,7 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row gutter="10">
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="12">
|
||||
<FormItemTooltip :label="$t('db.updateField')" prop="updField" :tooltip="$t('db.updateFieldTips')">
|
||||
<el-input v-model.trim="form.updField" :placeholder="$t('db.updateFiledPlaceholder')" auto-complete="off" />
|
||||
@@ -94,7 +94,7 @@
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<el-row gutter="10">
|
||||
<el-row :gutter="10">
|
||||
<el-col :span="12">
|
||||
<FormItemTooltip :label="$t('db.fieldValueSrc')" prop="updFieldSrc" :tooltip="$t('db.fieldValueSrcTips')">
|
||||
<el-input v-model.trim="form.updFieldSrc" :placeholder="$t('db.fieldValueSrcPlaceholder')" auto-complete="off" />
|
||||
@@ -320,7 +320,7 @@ watch(dialogVisible, async (newValue: boolean) => {
|
||||
state.tabActiveName = 'basic';
|
||||
const propsData = props.data as any;
|
||||
if (!propsData?.id) {
|
||||
let d = {} as FormData;
|
||||
let d = { taskCron: '' } as FormData;
|
||||
Object.assign(d, basicFormData);
|
||||
state.form = d;
|
||||
return;
|
||||
@@ -416,6 +416,7 @@ const refreshPreviewInsertSql = () => {
|
||||
const onSelectSrcDb = async (params: any) => {
|
||||
// 初始化数据源
|
||||
params.databases = params.dbs; // 数据源里需要这个值
|
||||
console.log(params.dbs);
|
||||
state.srcDbInst = await DbInst.getOrNewInst(params);
|
||||
registerDbCompletionItemProvider(params.id, params.db, params.dbs, params.type);
|
||||
};
|
||||
|
||||
@@ -1,31 +1,23 @@
|
||||
<template>
|
||||
<TagTreeResourceSelect
|
||||
v-bind="$attrs"
|
||||
v-model="selectNode"
|
||||
@change="changeNode"
|
||||
:resource-type="TagResourceTypePath.Db"
|
||||
:tag-path-node-type="NodeTypeTagPath"
|
||||
>
|
||||
<ResourceSelect v-bind="$attrs" v-model="selectNode" @change="changeNode" :resource-type="TagResourceTypePath.Db" :tag-path-node-type="NodeTypeDbInst">
|
||||
<template #iconPrefix>
|
||||
<SvgIcon v-if="dbType && getDbDialect(dbType)" :name="getDbDialect(dbType).getInfo().icon" :size="18" />
|
||||
</template>
|
||||
<template #prefix="{ data }">
|
||||
<SvgIcon v-if="data.type.value == SqlExecNodeType.DbInst" :name="getDbDialect(data.params.type).getInfo().icon" :size="18" />
|
||||
<SvgIcon v-if="data.icon" :name="data.icon.name" :color="data.icon.color" />
|
||||
</template>
|
||||
</TagTreeResourceSelect>
|
||||
</ResourceSelect>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { TagResourceTypePath } from '@/common/commonEnum';
|
||||
import { computed } from 'vue';
|
||||
import { TagResourceTypeEnum, TagResourceTypePath } from '@/common/commonEnum';
|
||||
import { NodeType, TagTreeNode } from '@/views/ops/component/tag';
|
||||
import { dbApi } from '@/views/ops/db/api';
|
||||
import { sleep } from '@/common/utils/loading';
|
||||
import SvgIcon from '@/components/svgIcon/index.vue';
|
||||
import { getDbDialect, noSchemaTypes } from '@/views/ops/db/dialect';
|
||||
import TagTreeResourceSelect from '../../component/TagTreeResourceSelect.vue';
|
||||
import { computed } from 'vue';
|
||||
import { DbInst } from '../db';
|
||||
import { getDbDialect, schemaDbTypes } from '@/views/ops/db/dialect';
|
||||
import ResourceSelect from '@/views/ops/resource/ResourceSelect.vue';
|
||||
import NodeDbInst from '@/views/ops/db/resource/NodeDbInst.vue';
|
||||
import NodeDb from '@/views/ops/db/resource/NodeDb.vue';
|
||||
import { DbIcon, SchemaIcon } from '@/views/ops/db/resource';
|
||||
import { DbInst } from '@/views/ops/db/db';
|
||||
|
||||
const dbId = defineModel<number>('dbId');
|
||||
const instName = defineModel<string>('instName');
|
||||
@@ -35,20 +27,6 @@ const dbType = defineModel<string>('dbType');
|
||||
|
||||
const emits = defineEmits(['selectDb']);
|
||||
|
||||
/**
|
||||
* 树节点类型
|
||||
*/
|
||||
class SqlExecNodeType {
|
||||
static DbInst = 1;
|
||||
static Db = 2;
|
||||
static TableMenu = 3;
|
||||
static SqlMenu = 4;
|
||||
static Table = 5;
|
||||
static Sql = 6;
|
||||
static PgSchemaMenu = 7;
|
||||
static PgSchema = 8;
|
||||
}
|
||||
|
||||
const selectNode = computed({
|
||||
get: () => {
|
||||
return dbName.value ? `${tagPath.value} > ${instName.value} > ${dbName.value}` : '';
|
||||
@@ -58,90 +36,94 @@ const selectNode = computed({
|
||||
},
|
||||
});
|
||||
|
||||
const DbIcon = {
|
||||
name: 'Coin',
|
||||
color: '#67c23a',
|
||||
};
|
||||
const NodeTypeDbInst = new NodeType(TagResourceTypeEnum.DbInstance.value).withLoadNodesFunc(async (parentNode: TagTreeNode) => {
|
||||
const tagPath = parentNode.key;
|
||||
|
||||
// pgsql schema icon
|
||||
const SchemaIcon = {
|
||||
name: 'List',
|
||||
color: '#67c23a',
|
||||
};
|
||||
|
||||
const NodeTypeTagPath = new NodeType(TagTreeNode.TagPath).withLoadNodesFunc(async (parentNode: TagTreeNode) => {
|
||||
const dbInfoRes = await dbApi.dbs.request({ tagPath: parentNode.key });
|
||||
const dbInfos = dbInfoRes.list;
|
||||
if (!dbInfos) {
|
||||
const dbInstancesRes = await dbApi.instances.request({ tagPath, pageSize: 100 });
|
||||
const dbInstances = dbInstancesRes.list;
|
||||
if (!dbInstances) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// 防止过快加载会出现一闪而过,对眼睛不好
|
||||
await sleep(100);
|
||||
return dbInfos?.map((x: any) => {
|
||||
x.tagPath = parentNode.key;
|
||||
return new TagTreeNode(`${parentNode.key}.${x.id}`, x.name, NodeTypeDbInst).withParams(x);
|
||||
return dbInstances?.map((x: any) => {
|
||||
x.tagPath = tagPath;
|
||||
return TagTreeNode.new(parentNode, `${x.code}`, x.name, NodeTypeDbConf).withParams(x).withNodeComponent(NodeDbInst);
|
||||
});
|
||||
});
|
||||
|
||||
/** mysql类型的数据库,没有schema层 */
|
||||
const noSchemaType = (type: string) => {
|
||||
return noSchemaTypes.includes(type);
|
||||
};
|
||||
const NodeTypeDbConf = new NodeType(TagResourceTypeEnum.Db.value).withLoadNodesFunc(async (parentNode: TagTreeNode) => {
|
||||
const params = parentNode.params;
|
||||
|
||||
// 数据库实例节点类型
|
||||
const NodeTypeDbInst = new NodeType(SqlExecNodeType.DbInst).withLoadNodesFunc(async (parentNode: TagTreeNode) => {
|
||||
const tagPath = params.tagPath;
|
||||
const authCerts = {} as any;
|
||||
for (let authCert of params.authCerts) {
|
||||
authCerts[authCert.name] = authCert;
|
||||
}
|
||||
|
||||
const dbInfoRes = await dbApi.dbs.request({
|
||||
tagPath: `${tagPath}${TagResourceTypeEnum.DbInstance.value}|${params.code}`,
|
||||
});
|
||||
const dbInfos = dbInfoRes.list;
|
||||
if (!dbInfos) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return dbInfos?.map((x: any) => {
|
||||
x.tagPath = tagPath;
|
||||
x.username = authCerts[x.authCertName]?.username;
|
||||
return TagTreeNode.new(parentNode, `${x.code}`, x.name, NodeTypeDbs).withParams(x).withIcon(DbIcon).withNodeComponent(NodeDb);
|
||||
});
|
||||
});
|
||||
|
||||
// 数据库列表名类型
|
||||
const NodeTypeDbs = new NodeType(222).withLoadNodesFunc(async (parentNode: TagTreeNode) => {
|
||||
const params = parentNode.params;
|
||||
const dbs = (await DbInst.getDbNames(params))?.sort();
|
||||
let fn: NodeType;
|
||||
if (noSchemaType(params.type)) {
|
||||
fn = MysqlNodeTypes;
|
||||
} else {
|
||||
fn = PgNodeTypes;
|
||||
}
|
||||
const hasSchema = schemaDbTypes.includes(params.type);
|
||||
const nodeType = hasSchema ? NodeTypeDbSchema : NodeTypeNoSchemaDb;
|
||||
|
||||
return dbs.map((x: any) => {
|
||||
let tagTreeNode = new TagTreeNode(`${parentNode.key}.${x}`, `${x}`, fn)
|
||||
return TagTreeNode.new(parentNode, `${parentNode.key}.${x}`, x, nodeType)
|
||||
.withParams({
|
||||
tagPath: params.tagPath,
|
||||
id: params.id,
|
||||
code: params.code,
|
||||
instanceId: params.instanceId,
|
||||
name: params.name,
|
||||
type: params.type,
|
||||
host: `${params.host}:${params.port}`,
|
||||
dbs: dbs,
|
||||
db: x,
|
||||
code: params.code,
|
||||
})
|
||||
.withIcon(DbIcon);
|
||||
if (noSchemaType(params.type)) {
|
||||
tagTreeNode.isLeaf = true;
|
||||
}
|
||||
return tagTreeNode;
|
||||
.withIcon(DbIcon)
|
||||
.withIsLeaf(!hasSchema);
|
||||
});
|
||||
});
|
||||
|
||||
// 数据库节点
|
||||
const PgNodeTypes = new NodeType(SqlExecNodeType.Db).withLoadNodesFunc(async (parentNode: TagTreeNode) => {
|
||||
// pg类数据库会多一层schema
|
||||
const NodeTypeDbSchema = new NodeType(2).withLoadNodesFunc(async (parentNode: TagTreeNode) => {
|
||||
const params = parentNode.params;
|
||||
params.parentKey = parentNode.key;
|
||||
const { id, db } = params;
|
||||
const schemaNames = await dbApi.pgSchemas.request({ id, db });
|
||||
const dbs = schemaNames.map((x: any) => `${db}/${x}`);
|
||||
return schemaNames.map((sn: any) => {
|
||||
// 将db变更为 db/schema;
|
||||
const nParams = { ...params };
|
||||
nParams.schema = sn;
|
||||
nParams.db = nParams.db + '/' + sn;
|
||||
nParams.dbs = schemaNames;
|
||||
let tagTreeNode = new TagTreeNode(`${params.id}.${params.db}.schema.${sn}`, sn, NodeTypePostgresSchema).withParams(nParams).withIcon(SchemaIcon);
|
||||
tagTreeNode.isLeaf = true;
|
||||
return tagTreeNode;
|
||||
nParams.dbs = dbs;
|
||||
return TagTreeNode.new(parentNode, `${params.id}.${params.db}.schema.${sn}`, sn, NodeTypePostgresSchema)
|
||||
.withParams(nParams)
|
||||
.withIcon(SchemaIcon)
|
||||
.withIsLeaf(true);
|
||||
});
|
||||
});
|
||||
|
||||
const MysqlNodeTypes = new NodeType(SqlExecNodeType.Db);
|
||||
|
||||
// postgres schema模式
|
||||
const NodeTypePostgresSchema = new NodeType(SqlExecNodeType.PgSchema);
|
||||
const NodeTypePostgresSchema = new NodeType(99);
|
||||
const NodeTypeNoSchemaDb = new NodeType(99);
|
||||
|
||||
const changeNode = (nodeData: TagTreeNode) => {
|
||||
const params = nodeData.params;
|
||||
|
||||
@@ -16,18 +16,18 @@ const NodeDbInst = defineAsyncComponent(() => import('./NodeDbInst.vue'));
|
||||
const NodeDb = defineAsyncComponent(() => import('./NodeDb.vue'));
|
||||
const NodeDbTable = defineAsyncComponent(() => import('./NodeDbTable.vue'));
|
||||
|
||||
const DbIcon = {
|
||||
export const DbIcon = {
|
||||
name: ResourceTypeEnum.Db.extra.icon,
|
||||
color: ResourceTypeEnum.Db.extra.iconColor,
|
||||
};
|
||||
|
||||
// pgsql schema icon
|
||||
const SchemaIcon = {
|
||||
export const SchemaIcon = {
|
||||
name: 'List',
|
||||
color: '#67c23a',
|
||||
};
|
||||
|
||||
const TableIcon = {
|
||||
export const TableIcon = {
|
||||
name: 'icon db/table',
|
||||
color: '#409eff',
|
||||
};
|
||||
|
||||
@@ -33,13 +33,9 @@
|
||||
|
||||
<el-descriptions-item :span="3" :label="$t('tag.relateTag')"><ResourceTags :tags="detailDialog.data.tags" /></el-descriptions-item>
|
||||
|
||||
<el-descriptions-item :span="3" label="Host">{{ detailDialog.data.host }}</el-descriptions-item>
|
||||
<el-descriptions-item :span="3" :label="$t('docker.addr')">{{ detailDialog.data.addr }}</el-descriptions-item>
|
||||
|
||||
<el-descriptions-item :span="3" label="DB">{{ detailDialog.data.db }}</el-descriptions-item>
|
||||
<el-descriptions-item :span="3" :label="$t('common.remark')">{{ detailDialog.data.remark }}</el-descriptions-item>
|
||||
<el-descriptions-item :span="3" :label="$t('machine.sshTunnel')">
|
||||
{{ detailDialog.data.sshTunnelMachineId > 0 ? $t('common.yes') : $t('common.no') }}
|
||||
</el-descriptions-item>
|
||||
|
||||
<el-descriptions-item :span="2" :label="$t('common.createTime')">{{ formatDate(detailDialog.data.createTime) }} </el-descriptions-item>
|
||||
<el-descriptions-item :span="1" :label="$t('common.creator')">{{ detailDialog.data.creator }}</el-descriptions-item>
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
<el-row class="mb-2 ml-4">
|
||||
<el-breadcrumb separator-icon="ArrowRight">
|
||||
<el-breadcrumb-item v-for="path in filePathNav" :key="path">
|
||||
<el-link @click="setFiles(path.path)" style="font-weight: bold">{{ path.name }}</el-link>
|
||||
<el-link @click="setFiles(path.path)" class="!cursor-pointer !font-bold">{{ path.name }}</el-link>
|
||||
</el-breadcrumb-item>
|
||||
</el-breadcrumb>
|
||||
</el-row>
|
||||
|
||||
@@ -4,7 +4,7 @@ import { ResourceTypeEnum, TagResourceTypeEnum } from '@/common/commonEnum';
|
||||
import { redisApi } from '../api';
|
||||
import { sleep } from '@/common/utils/loading';
|
||||
|
||||
const RedisIcon = {
|
||||
export const RedisIcon = {
|
||||
name: ResourceTypeEnum.Redis.extra.icon,
|
||||
color: ResourceTypeEnum.Redis.extra.iconColor,
|
||||
};
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<div
|
||||
:id="props.node.key"
|
||||
class="w-full node-container flex items-center cursor-pointer select-none"
|
||||
:class="props.data.type.nodeDblclickFunc ? 'select-none' : ''"
|
||||
:class="props.data.type?.nodeDblclickFunc ? 'select-none' : ''"
|
||||
@mouseenter="showActions = true"
|
||||
@mouseleave="showActions = false"
|
||||
>
|
||||
@@ -55,7 +55,7 @@ import { ContextmenuItem } from '@/components/contextmenu';
|
||||
import { ResourceOpCtx, TagTreeNode } from '@/views/ops/component/tag';
|
||||
import { ResourceOpCtxKey } from '@/views/ops/resource/resource';
|
||||
|
||||
const resourceOpCtx: ResourceOpCtx | undefined = inject(ResourceOpCtxKey);
|
||||
const resourceOpCtx: ResourceOpCtx | undefined = inject(ResourceOpCtxKey, undefined);
|
||||
|
||||
const props = defineProps({
|
||||
node: {
|
||||
|
||||
@@ -18,28 +18,18 @@
|
||||
<slot name="iconPrefix" :node="node" :data="data" />
|
||||
</template>
|
||||
<template #default="{ node, data }">
|
||||
<span>
|
||||
<span v-if="data.type.value == TagTreeNode.TagPath">
|
||||
<tag-info :tag-path="data.label" />
|
||||
</span>
|
||||
|
||||
<slot v-else :node="node" :data="data" name="prefix"></slot>
|
||||
|
||||
<span class="ml-0.5" :title="data.labelRemark">
|
||||
<slot name="label" :data="data"> {{ data.label }}</slot>
|
||||
</span>
|
||||
|
||||
<slot :node="node" :data="data" name="suffix"></slot>
|
||||
</span>
|
||||
<component v-if="data.nodeComponent" :is="data.nodeComponent" :node="node" :data="data" />
|
||||
<BaseTreeNode v-else :node="node" :data="data" />
|
||||
</template>
|
||||
</el-tree-select>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { onMounted, reactive, ref, toRefs, watch } from 'vue';
|
||||
import { NodeType, TagTreeNode } from './tag';
|
||||
import TagInfo from './TagInfo.vue';
|
||||
import { tagApi } from '../tag/api';
|
||||
|
||||
import { NodeType, TagTreeNode } from '@/views/ops/component/tag';
|
||||
import { tagApi } from '@/views/ops/tag/api';
|
||||
import BaseTreeNode from '@/views/ops/resource/BaseTreeNode.vue';
|
||||
|
||||
const props = defineProps({
|
||||
resourceType: {
|
||||
@@ -16,8 +16,11 @@
|
||||
<template #content>
|
||||
{{ $t('tag.tagTips1') }}
|
||||
<br />
|
||||
{{ $t('tag.tagTips2') }} <br />
|
||||
{{ $t('tag.tagTips2') }}
|
||||
<br />
|
||||
{{ $t('tag.tagTips3') }}
|
||||
<br />
|
||||
{{ $t('tag.tagTips4') }}
|
||||
</template>
|
||||
<SvgIcon class="ml-1" name="question-filled" />
|
||||
</el-tooltip>
|
||||
@@ -37,10 +40,6 @@
|
||||
@node-contextmenu="onNodeContextmenu"
|
||||
@node-click="onTreeNodeClick"
|
||||
:default-expanded-keys="defaultExpandedKeys"
|
||||
draggable
|
||||
:allow-drop="allowDrop"
|
||||
:allow-drag="allowDrag"
|
||||
@node-drop="onNodeDrop"
|
||||
:expand-on-click-node="false"
|
||||
:filter-node-method="filterNode"
|
||||
>
|
||||
@@ -279,7 +278,7 @@ watch(filterTag, (val) => {
|
||||
watch(
|
||||
() => state.currentTag,
|
||||
(val: any) => {
|
||||
if (val.type == TagResourceTypeEnum.Tag.value) {
|
||||
if (val?.type == TagResourceTypeEnum.Tag.value) {
|
||||
tagApi.countTagResource.request({ tagPath: val.codePath }).then((res: any) => {
|
||||
state.resourceCount = res;
|
||||
});
|
||||
@@ -289,82 +288,6 @@ watch(
|
||||
}
|
||||
);
|
||||
|
||||
const allowDrop = (draggingNode: any, dropNode: any, type: any) => {
|
||||
// 不允许同层级移动
|
||||
if (type != 'inner') {
|
||||
return false;
|
||||
}
|
||||
|
||||
const dropNodeData = dropNode.data;
|
||||
const draggingNodeData = draggingNode.data;
|
||||
const dropTagType = dropNodeData.type;
|
||||
const draggingTagType = draggingNodeData.type;
|
||||
|
||||
// 目标节点只允许为标签类型
|
||||
if (dropTagType != TagResourceTypeEnum.Tag.value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 目标节点下没有子节点
|
||||
if (!dropNodeData.children) {
|
||||
// 都为标签类型允许移动
|
||||
if (dropTagType == draggingTagType && dropTagType == TagResourceTypeEnum.Tag.value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 目标节点为标签,允许移动
|
||||
if (dropTagType == TagResourceTypeEnum.Tag.value) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
for (let child of dropNodeData.children) {
|
||||
// 当前移动节点若在目标节点下有相同code,则不允许移动
|
||||
if (draggingNodeData.code == child.code) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const childType = child.type;
|
||||
// 移动节点非标签类型时(资源标签),并且子节点存在标签类型,则不允许移动,因为资源只允许放在叶子标签类型下
|
||||
if (draggingTagType != TagResourceTypeEnum.Tag.value && childType == TagResourceTypeEnum.Tag.value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 移动节点为标签类型时(资源标签),并且子节点存在资源类型,则不允许移动
|
||||
if (draggingTagType == TagResourceTypeEnum.Tag.value && childType != TagResourceTypeEnum.Tag.value) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
const allowDrag = (node: any) => {
|
||||
const tagType = node.data.type;
|
||||
return (
|
||||
tagType == TagResourceTypeEnum.Tag.value ||
|
||||
tagType == TagResourceTypeEnum.DbInstance.value ||
|
||||
tagType == TagResourceTypeEnum.Redis.value ||
|
||||
tagType == TagResourceTypeEnum.Machine.value ||
|
||||
tagType == TagResourceTypeEnum.Mongo.value
|
||||
);
|
||||
};
|
||||
|
||||
const onNodeDrop = async (draggingNode: any, dropNode: any) => {
|
||||
const draggingData = draggingNode.data;
|
||||
const dropData = dropNode.data;
|
||||
|
||||
try {
|
||||
await tagApi.movingTag.request({
|
||||
fromPath: draggingData.codePath,
|
||||
toPath: dropData.codePath,
|
||||
});
|
||||
} finally {
|
||||
search();
|
||||
}
|
||||
};
|
||||
|
||||
const onTabChange = () => {
|
||||
setNowTabData();
|
||||
};
|
||||
|
||||
@@ -6,21 +6,21 @@ require (
|
||||
gitee.com/chunanyong/dm v1.8.20
|
||||
gitee.com/liuzongyang/libpq v1.10.11
|
||||
github.com/antlr4-go/antlr/v4 v4.13.1
|
||||
github.com/docker/docker v28.4.0+incompatible
|
||||
github.com/docker/docker v28.5.0+incompatible
|
||||
github.com/docker/go-connections v0.6.0
|
||||
github.com/gin-gonic/gin v1.10.1
|
||||
github.com/gin-gonic/gin v1.11.0
|
||||
github.com/glebarez/sqlite v1.11.0
|
||||
github.com/go-gormigrate/gormigrate/v2 v2.1.5
|
||||
github.com/go-ldap/ldap/v3 v3.4.11
|
||||
github.com/go-ldap/ldap/v3 v3.4.12
|
||||
github.com/go-playground/locales v0.14.1
|
||||
github.com/go-playground/universal-translator v0.18.1
|
||||
github.com/go-playground/validator/v10 v10.27.0
|
||||
github.com/go-playground/validator/v10 v10.28.0
|
||||
github.com/go-sql-driver/mysql v1.9.3
|
||||
github.com/golang-jwt/jwt/v5 v5.3.0
|
||||
github.com/google/uuid v1.6.0
|
||||
github.com/gorilla/websocket v1.5.3
|
||||
github.com/lionsoul2014/ip2region/binding/golang v0.0.0-20250630080345-f9402614f6ba
|
||||
github.com/microsoft/go-mssqldb v1.9.2
|
||||
github.com/lionsoul2014/ip2region/binding/golang v0.0.0-20250930013652-2d71241a3bb9
|
||||
github.com/microsoft/go-mssqldb v1.9.3
|
||||
github.com/mojocn/base64Captcha v1.3.8 // 验证码
|
||||
github.com/opencontainers/image-spec v1.1.1
|
||||
github.com/pkg/errors v0.9.1
|
||||
@@ -49,10 +49,12 @@ require (
|
||||
github.com/Azure/go-ntlmssp v0.0.0-20221128193559-754e69321358 // indirect
|
||||
github.com/Microsoft/go-winio v0.6.2 // indirect
|
||||
github.com/boombuler/barcode v1.1.0 // indirect
|
||||
github.com/bytedance/sonic v1.14.0 // indirect
|
||||
github.com/bytedance/gopkg v0.1.3 // indirect
|
||||
github.com/bytedance/sonic v1.14.1 // indirect
|
||||
github.com/bytedance/sonic/loader v0.3.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/cloudwego/base64x v0.1.5 // indirect
|
||||
github.com/clipperhouse/uax29/v2 v2.2.0 // indirect
|
||||
github.com/cloudwego/base64x v0.1.6 // indirect
|
||||
github.com/containerd/errdefs v1.0.0 // indirect
|
||||
github.com/containerd/errdefs/pkg v0.3.0 // indirect
|
||||
github.com/containerd/log v0.1.0 // indirect
|
||||
@@ -62,13 +64,14 @@ require (
|
||||
github.com/docker/go-units v0.5.0 // indirect
|
||||
github.com/dustin/go-humanize v1.0.1 // indirect
|
||||
github.com/felixge/httpsnoop v1.0.4 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.9 // indirect
|
||||
github.com/gabriel-vasile/mimetype v1.4.10 // indirect
|
||||
github.com/gin-contrib/sse v1.1.0 // indirect
|
||||
github.com/glebarez/go-sqlite v1.22.0 // indirect
|
||||
github.com/go-asn1-ber/asn1-ber v1.5.8-0.20250403174932-29230038a667 // indirect
|
||||
github.com/go-logr/logr v1.4.3 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/goccy/go-json v0.10.5 // indirect
|
||||
github.com/goccy/go-yaml v1.18.0 // indirect
|
||||
github.com/golang-sql/civil v0.0.0-20220223132316-b832511892a9 // indirect
|
||||
github.com/golang-sql/sqlexp v0.1.0 // indirect
|
||||
github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 // indirect
|
||||
@@ -81,7 +84,7 @@ require (
|
||||
github.com/kr/fs v0.1.0 // indirect
|
||||
github.com/leodido/go-urn v1.4.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.19 // indirect
|
||||
github.com/moby/docker-image-spec v1.3.1 // indirect
|
||||
github.com/moby/sys/atomicwriter v0.1.0 // indirect
|
||||
github.com/moby/term v0.5.2 // indirect
|
||||
@@ -92,9 +95,10 @@ require (
|
||||
github.com/opencontainers/go-digest v1.0.0 // indirect
|
||||
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.0 // indirect
|
||||
github.com/quic-go/qpack v0.5.1 // indirect
|
||||
github.com/quic-go/quic-go v0.55.0 // indirect
|
||||
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/tidwall/match v1.1.1 // indirect
|
||||
github.com/tidwall/match v1.2.0 // indirect
|
||||
github.com/tidwall/pretty v1.2.1 // indirect
|
||||
github.com/tjfoc/gmsm v1.4.1 // indirect
|
||||
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
|
||||
@@ -103,24 +107,26 @@ require (
|
||||
github.com/xdg-go/scram v1.1.2 // indirect
|
||||
github.com/xdg-go/stringprep v1.0.4 // indirect
|
||||
github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect
|
||||
go.opentelemetry.io/otel v1.37.0 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect
|
||||
go.opentelemetry.io/otel v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.37.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.37.0 // indirect
|
||||
go.opentelemetry.io/otel/sdk v1.37.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.37.0 // indirect
|
||||
golang.org/x/arch v0.19.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20250718183923-645b1fa84792 // indirect
|
||||
golang.org/x/image v0.29.0 // indirect
|
||||
golang.org/x/net v0.43.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.38.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.38.0 // indirect
|
||||
go.uber.org/mock v0.6.0 // indirect
|
||||
golang.org/x/arch v0.21.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20251002181428-27f1f14c8bb9 // indirect
|
||||
golang.org/x/image v0.31.0 // indirect
|
||||
golang.org/x/mod v0.28.0 // indirect
|
||||
golang.org/x/net v0.44.0 // indirect
|
||||
golang.org/x/sys v0.36.0 // indirect
|
||||
golang.org/x/text v0.29.0 // indirect
|
||||
google.golang.org/protobuf v1.36.6 // indirect
|
||||
modernc.org/libc v1.66.4 // indirect
|
||||
golang.org/x/tools v0.37.0 // indirect
|
||||
google.golang.org/protobuf v1.36.10 // indirect
|
||||
modernc.org/libc v1.66.10 // indirect
|
||||
modernc.org/mathutil v1.7.1 // indirect
|
||||
modernc.org/memory v1.11.0 // indirect
|
||||
modernc.org/sqlite v1.38.1 // indirect
|
||||
modernc.org/sqlite v1.39.0 // indirect
|
||||
)
|
||||
|
||||
replace google.golang.org/genproto => google.golang.org/genproto v0.0.0-20250603155806-513f23925822
|
||||
|
||||
@@ -370,9 +370,11 @@ func (app *dataSyncAppImpl) saveLog(log *entity.DataSyncLog) {
|
||||
}
|
||||
|
||||
func (app *dataSyncAppImpl) InitCronJob() {
|
||||
ctx := contextx.NewTraceId()
|
||||
|
||||
defer func() {
|
||||
if err := recover(); err != nil {
|
||||
logx.ErrorTrace("the data synchronization task failed to initialize", err)
|
||||
logx.ErrorTraceContext(ctx, "the data synchronization task failed to initialize", err)
|
||||
}
|
||||
}()
|
||||
|
||||
@@ -380,11 +382,11 @@ func (app *dataSyncAppImpl) InitCronJob() {
|
||||
_ = app.UpdateByCond(context.TODO(), &entity.DataSyncTask{RunningState: entity.DataSyncTaskRunStateReady}, &entity.DataSyncTask{RunningState: entity.DataSyncTaskRunStateRunning})
|
||||
|
||||
if err := app.CursorByCond(&entity.DataSyncTaskQuery{Status: entity.DataSyncTaskStatusEnable}, func(dst *entity.DataSyncTask) error {
|
||||
app.addCronJob(contextx.NewTraceId(), dst)
|
||||
app.MarkStop(dst.Id)
|
||||
app.addCronJob(ctx, dst)
|
||||
return nil
|
||||
}); err != nil {
|
||||
logx.ErrorTrace("the db data sync task failed to initialize: %v", err)
|
||||
logx.ErrorTraceContext(ctx, "the db data sync task failed to initialize: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -415,13 +417,13 @@ func (app *dataSyncAppImpl) addCronJob(ctx context.Context, taskEntity *entity.D
|
||||
// 根据状态添加新的任务
|
||||
if taskEntity.Status == entity.DataSyncTaskStatusEnable {
|
||||
taskId := taskEntity.Id
|
||||
logx.Infof("start add the data sync task job: %s, cron[%s]", taskEntity.TaskName, taskEntity.TaskCron)
|
||||
logx.InfofContext(ctx, "start add the data sync task job: %s, cron[%s]", taskEntity.TaskName, taskEntity.TaskCron)
|
||||
if err := scheduler.AddFunByKey(key, taskEntity.TaskCron, func() {
|
||||
if err := app.Run(context.Background(), taskId); err != nil {
|
||||
logx.Errorf("the data sync task failed to execute at a scheduled time: %s", err.Error())
|
||||
logx.ErrorfContext(ctx, "the data sync task failed to execute at a scheduled time: %s", err.Error())
|
||||
}
|
||||
}); err != nil {
|
||||
logx.ErrorTrace("add db data sync job failed", err)
|
||||
logx.ErrorTraceContext(ctx, "add db data sync job failed", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@ import "fmt"
|
||||
|
||||
const (
|
||||
AppName = "mayfly-go"
|
||||
Version = "v1.10.3"
|
||||
Version = "v1.10.4"
|
||||
)
|
||||
|
||||
func GetAppInfo() string {
|
||||
|
||||
@@ -87,6 +87,7 @@ func (a *Account) GetPermissions(rc *req.Ctx) {
|
||||
var resources vo.AccountResourceVOList
|
||||
// 获取账号菜单资源
|
||||
biz.ErrIsNil(a.resourceApp.GetAccountResources(account.Id, &resources))
|
||||
biz.IsTrue(len(resources) > 0, "no permission")
|
||||
// 菜单树与权限code数组
|
||||
var menus vo.AccountResourceVOList
|
||||
var permissions []string
|
||||
|
||||
@@ -121,8 +121,8 @@ func Errorf(format string, args ...any) {
|
||||
Log(context.Background(), slog.LevelError, fmt.Sprintf(format, args...))
|
||||
}
|
||||
|
||||
// 错误记录,并将堆栈信息添加至msg里,默认记录10个堆栈信息
|
||||
func ErrorTrace(msg string, err any) {
|
||||
// ErrorTraceContext 错误记录,并将堆栈信息添加至msg里,默认记录10个堆栈信息
|
||||
func ErrorTraceContext(ctx context.Context, msg string, err any) {
|
||||
errMsg := ""
|
||||
switch t := err.(type) {
|
||||
case error:
|
||||
@@ -132,7 +132,12 @@ func ErrorTrace(msg string, err any) {
|
||||
default:
|
||||
errMsg = fmt.Sprintf("%v", t)
|
||||
}
|
||||
Log(context.Background(), slog.LevelError, fmt.Sprintf(msg+"\n%s\n%s", errMsg, runtimex.StackStr(2, 20)))
|
||||
Log(ctx, slog.LevelError, fmt.Sprintf(msg+"\n%s\n%s", errMsg, runtimex.StackStr(2, 20)))
|
||||
}
|
||||
|
||||
// ErrorTrace 错误记录,并将堆栈信息添加至msg里,默认记录10个堆栈信息
|
||||
func ErrorTrace(msg string, err any) {
|
||||
ErrorTraceContext(context.Background(), msg, err)
|
||||
}
|
||||
|
||||
func ErrorWithFields(ctx context.Context, msg string, mapFields map[string]any) {
|
||||
|
||||
@@ -49,8 +49,9 @@ func Error(bizerr *errorx.BizError) *Result {
|
||||
}
|
||||
|
||||
// 返回服务器错误Result
|
||||
func ServerError() *Result {
|
||||
return Error(errorx.ServerError)
|
||||
func ServerError(msg string) *Result {
|
||||
serverErr := errorx.NewBizCode(errorx.ServerError.Code(), msg)
|
||||
return Error(serverErr)
|
||||
}
|
||||
|
||||
func TokenError() *Result {
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
package req
|
||||
|
||||
import (
|
||||
"cmp"
|
||||
"context"
|
||||
"fmt"
|
||||
"mayfly-go/pkg/biz"
|
||||
"mayfly-go/pkg/contextx"
|
||||
"mayfly-go/pkg/errorx"
|
||||
@@ -109,8 +111,8 @@ func (rc *Ctx) res() {
|
||||
case *errorx.BizError:
|
||||
rc.JSONRes(http.StatusOK, model.Error(t))
|
||||
default:
|
||||
logx.ErrorTrace("服务器错误", t)
|
||||
rc.JSONRes(http.StatusOK, model.ServerError())
|
||||
logx.ErrorTrace("server error", t)
|
||||
rc.JSONRes(http.StatusOK, model.ServerError(fmt.Sprintf("server error [%d-%s]", errorx.ServerError.Code(), cmp.Or(contextx.GetTraceId(rc.MetaCtx), "none"))))
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -53,7 +53,7 @@ func Ip2Region(ip string) string {
|
||||
}
|
||||
|
||||
// 2、用全局的 vIndex 创建带 VectorIndex 缓存的查询对象。
|
||||
searcher, err := xdb.NewWithVectorIndex(ip2RegionDbPath, vectorIndex)
|
||||
searcher, err := xdb.NewWithVectorIndex(xdb.IPv4, ip2RegionDbPath, vectorIndex)
|
||||
if err != nil {
|
||||
logx.Errorf("failed to create searcher with vector index: %s\n", err)
|
||||
return ""
|
||||
|
||||
Reference in New Issue
Block a user