mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-03 16:00:25 +08:00
feat: 机器新增命令过滤配置、首页功能完善(操作记录与快捷操作)
This commit is contained in:
@@ -11,18 +11,17 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@element-plus/icons-vue": "^2.3.1",
|
"@element-plus/icons-vue": "^2.3.1",
|
||||||
"@vueuse/core": "^10.9.0",
|
"@vueuse/core": "^10.9.0",
|
||||||
"asciinema-player": "^3.7.0",
|
"asciinema-player": "^3.7.1",
|
||||||
"axios": "^1.6.2",
|
"axios": "^1.6.2",
|
||||||
"clipboard": "^2.0.11",
|
"clipboard": "^2.0.11",
|
||||||
"countup.js": "^2.8.0",
|
|
||||||
"cropperjs": "^1.6.1",
|
"cropperjs": "^1.6.1",
|
||||||
"echarts": "^5.5.0",
|
"echarts": "^5.5.0",
|
||||||
"element-plus": "^2.7.1",
|
"element-plus": "^2.7.2",
|
||||||
"js-base64": "^3.7.7",
|
"js-base64": "^3.7.7",
|
||||||
"jsencrypt": "^3.3.2",
|
"jsencrypt": "^3.3.2",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"mitt": "^3.0.1",
|
"mitt": "^3.0.1",
|
||||||
"monaco-editor": "^0.47.0",
|
"monaco-editor": "^0.48.0",
|
||||||
"monaco-sql-languages": "^0.11.0",
|
"monaco-sql-languages": "^0.11.0",
|
||||||
"monaco-themes": "^0.4.4",
|
"monaco-themes": "^0.4.4",
|
||||||
"nprogress": "^0.2.0",
|
"nprogress": "^0.2.0",
|
||||||
@@ -34,7 +33,7 @@
|
|||||||
"sql-formatter": "^15.0.2",
|
"sql-formatter": "^15.0.2",
|
||||||
"trzsz": "^1.1.5",
|
"trzsz": "^1.1.5",
|
||||||
"uuid": "^9.0.1",
|
"uuid": "^9.0.1",
|
||||||
"vue": "^3.4.23",
|
"vue": "^3.4.25",
|
||||||
"vue-router": "^4.3.2",
|
"vue-router": "^4.3.2",
|
||||||
"xterm": "^5.3.0",
|
"xterm": "^5.3.0",
|
||||||
"xterm-addon-fit": "^0.8.0",
|
"xterm-addon-fit": "^0.8.0",
|
||||||
|
|||||||
29
mayfly_go_web/src/store/autoOpenResource.ts
Normal file
29
mayfly_go_web/src/store/autoOpenResource.ts
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
import { defineStore } from 'pinia';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 自动打开资源
|
||||||
|
*/
|
||||||
|
export const useAutoOpenResource = defineStore('autoOpenResource', {
|
||||||
|
state: () => ({
|
||||||
|
autoOpenResource: {
|
||||||
|
machineCodePath: '',
|
||||||
|
dbCodePath: '',
|
||||||
|
redisCodePath: '',
|
||||||
|
mongoCodePath: '',
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
actions: {
|
||||||
|
setMachineCodePath(codePath: string) {
|
||||||
|
this.autoOpenResource.machineCodePath = codePath;
|
||||||
|
},
|
||||||
|
setDbCodePath(codePath: string) {
|
||||||
|
this.autoOpenResource.dbCodePath = codePath;
|
||||||
|
},
|
||||||
|
setRedisCodePath(codePath: string) {
|
||||||
|
this.autoOpenResource.redisCodePath = codePath;
|
||||||
|
},
|
||||||
|
setMongoCodePath(codePath: string) {
|
||||||
|
this.autoOpenResource.mongoCodePath = codePath;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
@@ -1,137 +1,541 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="home-container">
|
<div class="home-container personal">
|
||||||
<el-row :gutter="15">
|
<el-row :gutter="15">
|
||||||
<el-col :sm="6" class="mb15">
|
<!-- 个人信息 -->
|
||||||
<div @click="toPage({ id: 'personal' })" class="home-card-item home-card-first">
|
<el-col :xs="24" :sm="16">
|
||||||
<div class="flex-margin flex">
|
<el-card shadow="hover" header="个人信息">
|
||||||
<img :src="userInfo.photo" />
|
<div class="personal-user">
|
||||||
<div class="home-card-first-right ml15">
|
<div class="personal-user-left">
|
||||||
<div class="flex-margin">
|
<el-upload class="h100 personal-user-left-upload" action="" multiple :limit="1">
|
||||||
<div class="home-card-first-right-title">{{ `${currentTime}, ${userInfo.username}` }}</div>
|
<img :src="userInfo.photo" />
|
||||||
</div>
|
</el-upload>
|
||||||
|
</div>
|
||||||
|
<div class="personal-user-right">
|
||||||
|
<el-row>
|
||||||
|
<el-col :span="24" class="personal-title mb18"
|
||||||
|
>{{ currentTime }},{{ userInfo.name }},生活变的再糟糕,也不妨碍我变得更好!
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="24">
|
||||||
|
<el-row>
|
||||||
|
<el-col :xs="24" :sm="12" class="personal-item mb6">
|
||||||
|
<div class="personal-item-label">用户名:</div>
|
||||||
|
<div class="personal-item-value">{{ userInfo.username }}</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="24" :sm="12" class="personal-item mb6">
|
||||||
|
<div class="personal-item-label">角色:</div>
|
||||||
|
<div class="personal-item-value">{{ roleInfo }}</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-col>
|
||||||
|
<el-col :span="24">
|
||||||
|
<el-row>
|
||||||
|
<el-col :xs="24" :sm="12" class="personal-item mb6">
|
||||||
|
<div class="personal-item-label">上次登录IP:</div>
|
||||||
|
<div class="personal-item-value">{{ userInfo.lastLoginIp }}</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="24" :sm="12" class="personal-item mb6">
|
||||||
|
<div class="personal-item-label">上次登录时间:</div>
|
||||||
|
<div class="personal-item-value">{{ dateFormat(userInfo.lastLoginTime) }}</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</el-card>
|
||||||
</el-col>
|
</el-col>
|
||||||
<el-col :sm="3" class="mb15" v-for="(v, k) in topCardItemList as any" :key="k">
|
|
||||||
<div @click="toPage(v)" class="home-card-item home-card-item-box" :style="{ background: v.color }">
|
<!-- 消息通知 -->
|
||||||
<div class="home-card-item-flex">
|
<el-col :xs="24" :sm="8" class="pl15 personal-info">
|
||||||
<div class="home-card-item-title pb3">{{ v.title }}</div>
|
<el-card shadow="hover">
|
||||||
<div class="home-card-item-title-num pb6" :id="v.id"></div>
|
<template #header>
|
||||||
|
<span>消息通知</span>
|
||||||
|
<span @click="showMsgs" class="personal-info-more">更多</span>
|
||||||
|
</template>
|
||||||
|
<div class="personal-info-box">
|
||||||
|
<ul class="personal-info-ul">
|
||||||
|
<li v-for="(v, k) in state.msgs as any" :key="k" class="personal-info-li">
|
||||||
|
<a class="personal-info-li-title">{{ `[${getMsgTypeDesc(v.type)}] ${v.msg}` }}</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
<i :class="v.icon" :style="{ color: v.iconColor }"></i>
|
</el-card>
|
||||||
</div>
|
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
|
|
||||||
|
<el-row :gutter="20" class="mt20 resource-info">
|
||||||
|
<el-col :sm="12">
|
||||||
|
<el-card shadow="hover">
|
||||||
|
<template #header>
|
||||||
|
<div class="pointer-icon" @click="toPage('machine')">
|
||||||
|
<div class="resource-num">
|
||||||
|
<SvgIcon
|
||||||
|
class="mb5 mr5"
|
||||||
|
:size="28"
|
||||||
|
:name="TagResourceTypeEnum.Machine.extra.icon"
|
||||||
|
:color="TagResourceTypeEnum.Machine.extra.iconColor"
|
||||||
|
/>
|
||||||
|
<span>{{ state.machine.num }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-row>
|
||||||
|
<el-col :sm="24">
|
||||||
|
<el-table :data="state.machine.opLogs" :height="state.resourceOpTableHeight" stripe size="small">
|
||||||
|
<el-table-column prop="createTime" show-overflow-tooltip width="135">
|
||||||
|
<template #default="scope">
|
||||||
|
{{ dateFormat(scope.row.createTime) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="codePath" min-width="400" show-overflow-tooltip>
|
||||||
|
<template #default="scope">
|
||||||
|
<TagCodePath :path="scope.row.codePath" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column width="30">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-link @click="toPage('machine', scope.row.codePath)" type="primary" icon="Position"></el-link>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<el-col :sm="12">
|
||||||
|
<el-card shadow="hover">
|
||||||
|
<template #header>
|
||||||
|
<div class="pointer-icon" @click="toPage('db')">
|
||||||
|
<div class="resource-num">
|
||||||
|
<SvgIcon class="mb5 mr5" :size="28" :name="TagResourceTypeEnum.Db.extra.icon" :color="TagResourceTypeEnum.Db.extra.iconColor" />
|
||||||
|
<span>{{ state.db.num }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-row>
|
||||||
|
<el-col :sm="24">
|
||||||
|
<el-table :data="state.db.opLogs" :height="state.resourceOpTableHeight" stripe size="small">
|
||||||
|
<el-table-column prop="createTime" show-overflow-tooltip min-width="135">
|
||||||
|
<template #default="scope">
|
||||||
|
{{ dateFormat(scope.row.createTime) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="codePath" min-width="380" show-overflow-tooltip>
|
||||||
|
<template #default="scope">
|
||||||
|
<TagCodePath :path="scope.row.codePath" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column width="30">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-link @click="toPage('db', scope.row.codePath)" type="primary" icon="Position"></el-link>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-row :gutter="20" class="mt20 resource-info">
|
||||||
|
<el-col :sm="12">
|
||||||
|
<el-card shadow="hover">
|
||||||
|
<template #header>
|
||||||
|
<div class="pointer-icon" @click="toPage('redis')">
|
||||||
|
<div class="resource-num">
|
||||||
|
<SvgIcon
|
||||||
|
class="mb5 mr5"
|
||||||
|
:size="28"
|
||||||
|
:name="TagResourceTypeEnum.Redis.extra.icon"
|
||||||
|
:color="TagResourceTypeEnum.Redis.extra.iconColor"
|
||||||
|
/>
|
||||||
|
<span>{{ state.redis.num }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-row>
|
||||||
|
<el-col :sm="24">
|
||||||
|
<el-table :data="state.redis.opLogs" :height="state.resourceOpTableHeight" stripe size="small">
|
||||||
|
<el-table-column prop="createTime" show-overflow-tooltip min-width="135">
|
||||||
|
<template #default="scope">
|
||||||
|
{{ dateFormat(scope.row.createTime) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="codePath" min-width="380" show-overflow-tooltip>
|
||||||
|
<template #default="scope">
|
||||||
|
<TagCodePath :path="scope.row.codePath" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column width="30">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-link @click="toPage('redis', scope.row.codePath)" type="primary" icon="Position"></el-link>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
|
||||||
|
<el-col :sm="12">
|
||||||
|
<el-card shadow="hover">
|
||||||
|
<template #header>
|
||||||
|
<div class="pointer-icon" @click="toPage('mongo')">
|
||||||
|
<div class="resource-num">
|
||||||
|
<SvgIcon
|
||||||
|
class="mb5 mr5"
|
||||||
|
:size="28"
|
||||||
|
:name="TagResourceTypeEnum.Mongo.extra.icon"
|
||||||
|
:color="TagResourceTypeEnum.Mongo.extra.iconColor"
|
||||||
|
/>
|
||||||
|
<span>{{ state.mongo.num }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<el-row>
|
||||||
|
<el-col :sm="24">
|
||||||
|
<el-table :data="state.mongo.opLogs" :height="state.resourceOpTableHeight" stripe size="small">
|
||||||
|
<el-table-column prop="createTime" show-overflow-tooltip min-width="135">
|
||||||
|
<template #default="scope">
|
||||||
|
{{ dateFormat(scope.row.createTime) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="codePath" min-width="380" show-overflow-tooltip>
|
||||||
|
<template #default="scope">
|
||||||
|
<TagCodePath :path="scope.row.codePath" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column width="30">
|
||||||
|
<template #default="scope">
|
||||||
|
<el-link @click="toPage('mongo', scope.row.codePath)" type="primary" icon="Position"></el-link>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
</el-card>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<el-dialog width="900px" title="消息" v-model="msgDialog.visible">
|
||||||
|
<el-table border :data="msgDialog.msgs.list" size="small">
|
||||||
|
<el-table-column property="type" label="类型" width="60">
|
||||||
|
<template #default="scope">
|
||||||
|
{{ getMsgTypeDesc(scope.row.type) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column property="msg" label="消息"></el-table-column>
|
||||||
|
<el-table-column property="createTime" label="时间" width="150">
|
||||||
|
<template #default="scope">
|
||||||
|
{{ dateFormat(scope.row.createTime) }}
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
<el-row type="flex" class="mt5" justify="center">
|
||||||
|
<el-pagination
|
||||||
|
small
|
||||||
|
@current-change="searchMsg"
|
||||||
|
style="text-align: center"
|
||||||
|
background
|
||||||
|
layout="prev, pager, next, total, jumper"
|
||||||
|
:total="msgDialog.msgs.total"
|
||||||
|
v-model:current-page="msgDialog.query.pageNum"
|
||||||
|
:page-size="msgDialog.query.pageSize"
|
||||||
|
/>
|
||||||
|
</el-row>
|
||||||
|
</el-dialog>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { toRefs, reactive, onMounted, nextTick, computed } from 'vue';
|
import { toRefs, reactive, onMounted, computed } from 'vue';
|
||||||
// import * as echarts from 'echarts';
|
// import * as echarts from 'echarts';
|
||||||
import { CountUp } from 'countup.js';
|
|
||||||
import { formatAxis } from '@/common/utils/format';
|
import { formatAxis } from '@/common/utils/format';
|
||||||
import { indexApi } from './api';
|
import { indexApi } from './api';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import { storeToRefs } from 'pinia';
|
import { storeToRefs } from 'pinia';
|
||||||
import { useUserInfo } from '@/store/userInfo';
|
import { useUserInfo } from '@/store/userInfo';
|
||||||
|
import { personApi } from '../personal/api';
|
||||||
|
import { dateFormat } from '@/common/utils/date';
|
||||||
|
import SvgIcon from '@/components/svgIcon/index.vue';
|
||||||
|
import { TagResourceTypeEnum } from '@/common/commonEnum';
|
||||||
|
import { resourceOpLogApi } from '../ops/tag/api';
|
||||||
|
import TagCodePath from '../ops/component/TagCodePath.vue';
|
||||||
|
import { useAutoOpenResource } from '@/store/autoOpenResource';
|
||||||
|
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const { userInfo } = storeToRefs(useUserInfo());
|
const { userInfo } = storeToRefs(useUserInfo());
|
||||||
|
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
topCardItemList: [
|
accountInfo: {
|
||||||
{
|
roles: [],
|
||||||
title: 'Linux机器',
|
},
|
||||||
id: 'machineNum',
|
msgs: [],
|
||||||
color: '#F95959',
|
msgDialog: {
|
||||||
|
visible: false,
|
||||||
|
query: {
|
||||||
|
pageSize: 10,
|
||||||
|
pageNum: 1,
|
||||||
},
|
},
|
||||||
{
|
msgs: {
|
||||||
title: '数据库',
|
list: [],
|
||||||
id: 'dbNum',
|
total: null,
|
||||||
color: '#8595F4',
|
|
||||||
},
|
},
|
||||||
{
|
},
|
||||||
title: 'redis',
|
resourceOpTableHeight: 180,
|
||||||
id: 'redisNum',
|
defaultLogSize: 5,
|
||||||
color: '#1abc9c',
|
machine: {
|
||||||
},
|
num: 0,
|
||||||
{
|
opLogs: [],
|
||||||
title: 'Mongo',
|
},
|
||||||
id: 'mongoNum',
|
db: {
|
||||||
color: '#FEBB50',
|
num: 0,
|
||||||
},
|
opLogs: [],
|
||||||
],
|
},
|
||||||
|
redis: {
|
||||||
|
num: 0,
|
||||||
|
opLogs: [],
|
||||||
|
},
|
||||||
|
mongo: {
|
||||||
|
num: 0,
|
||||||
|
opLogs: [],
|
||||||
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const { topCardItemList } = toRefs(state);
|
const { msgDialog } = toRefs(state);
|
||||||
|
|
||||||
|
const roleInfo = computed(() => {
|
||||||
|
if (state.accountInfo.roles.length == 0) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
return state.accountInfo.roles.map((val: any) => val.roleName).join('、');
|
||||||
|
});
|
||||||
|
|
||||||
// 当前时间提示语
|
// 当前时间提示语
|
||||||
const currentTime = computed(() => {
|
const currentTime = computed(() => {
|
||||||
return formatAxis(new Date());
|
return formatAxis(new Date());
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// 页面加载时
|
||||||
|
onMounted(() => {
|
||||||
|
initData();
|
||||||
|
getAccountInfo();
|
||||||
|
|
||||||
|
getMsgs().then((res) => {
|
||||||
|
state.msgs = res.list;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
const showMsgs = async () => {
|
||||||
|
state.msgDialog.query.pageNum = 1;
|
||||||
|
searchMsg();
|
||||||
|
state.msgDialog.visible = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const searchMsg = async () => {
|
||||||
|
state.msgDialog.msgs = await getMsgs();
|
||||||
|
};
|
||||||
|
|
||||||
|
const getMsgTypeDesc = (type: number) => {
|
||||||
|
if (type == 1) {
|
||||||
|
return '登录';
|
||||||
|
}
|
||||||
|
if (type == 2) {
|
||||||
|
return '通知';
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const getAccountInfo = async () => {
|
||||||
|
state.accountInfo = await personApi.accountInfo.request();
|
||||||
|
};
|
||||||
|
|
||||||
|
const getMsgs = async () => {
|
||||||
|
return await personApi.getMsgs.request(state.msgDialog.query);
|
||||||
|
};
|
||||||
|
|
||||||
// 初始化数字滚动
|
// 初始化数字滚动
|
||||||
const initNumCountUp = async () => {
|
const initData = async () => {
|
||||||
indexApi.machineDashbord.request().then((res: any) => {
|
resourceOpLogApi.getAccountResourceOpLogs
|
||||||
nextTick(() => {
|
.request({ resourceType: TagResourceTypeEnum.MachineAuthCert.value, pageSize: state.defaultLogSize })
|
||||||
new CountUp('machineNum', res.machineNum).start();
|
.then((res: any) => {
|
||||||
|
state.machine.opLogs = res.list;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
resourceOpLogApi.getAccountResourceOpLogs.request({ resourceType: TagResourceTypeEnum.DbName.value, pageSize: state.defaultLogSize }).then((res: any) => {
|
||||||
|
state.db.opLogs = res.list;
|
||||||
|
});
|
||||||
|
|
||||||
|
resourceOpLogApi.getAccountResourceOpLogs.request({ resourceType: TagResourceTypeEnum.Redis.value, pageSize: state.defaultLogSize }).then((res: any) => {
|
||||||
|
state.redis.opLogs = res.list;
|
||||||
|
});
|
||||||
|
|
||||||
|
resourceOpLogApi.getAccountResourceOpLogs.request({ resourceType: TagResourceTypeEnum.Mongo.value, pageSize: state.defaultLogSize }).then((res: any) => {
|
||||||
|
state.mongo.opLogs = res.list;
|
||||||
|
});
|
||||||
|
|
||||||
|
indexApi.machineDashbord.request().then((res: any) => {
|
||||||
|
state.machine.num = res.machineNum;
|
||||||
});
|
});
|
||||||
|
|
||||||
indexApi.dbDashbord.request().then((res: any) => {
|
indexApi.dbDashbord.request().then((res: any) => {
|
||||||
nextTick(() => {
|
state.db.num = res.dbNum;
|
||||||
new CountUp('dbNum', res.dbNum).start();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
indexApi.redisDashbord.request().then((res: any) => {
|
indexApi.redisDashbord.request().then((res: any) => {
|
||||||
nextTick(() => {
|
state.redis.num = res.redisNum;
|
||||||
new CountUp('redisNum', res.redisNum).start();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
|
||||||
indexApi.mongoDashbord.request().then((res: any) => {
|
indexApi.mongoDashbord.request().then((res: any) => {
|
||||||
nextTick(() => {
|
state.mongo.num = res.mongoNum;
|
||||||
new CountUp('mongoNum', res.mongoNum).start();
|
|
||||||
});
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const toPage = (item: any) => {
|
const toPage = (item: any, codePath = '') => {
|
||||||
switch (item.id) {
|
let path;
|
||||||
|
switch (item) {
|
||||||
case 'personal': {
|
case 'personal': {
|
||||||
router.push('/personal');
|
router.push('/personal');
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'mongoNum': {
|
case 'mongo': {
|
||||||
router.push('/mongo/mongo-data-operation');
|
useAutoOpenResource().setMongoCodePath(codePath);
|
||||||
|
path = '/mongo/mongo-data-operation';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'machineNum': {
|
case 'machine': {
|
||||||
router.push('/machine/machines-op');
|
useAutoOpenResource().setMachineCodePath(codePath);
|
||||||
|
path = '/machine/machines-op';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'dbNum': {
|
case 'db': {
|
||||||
router.push('/dbms/sql-exec');
|
useAutoOpenResource().setDbCodePath(codePath);
|
||||||
|
path = '/dbms/sql-exec';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case 'redisNum': {
|
case 'redis': {
|
||||||
router.push('/redis/data-operation');
|
useAutoOpenResource().setRedisCodePath(codePath);
|
||||||
|
path = '/redis/data-operation';
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
// 页面加载时
|
router.push({ path });
|
||||||
onMounted(() => {
|
};
|
||||||
initNumCountUp();
|
|
||||||
// initHomeLaboratory();
|
|
||||||
// initHomeOvertime();
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
|
@import '@/theme/mixins/index.scss';
|
||||||
|
|
||||||
|
.personal {
|
||||||
|
.personal-user {
|
||||||
|
height: 130px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
|
||||||
|
.personal-user-left {
|
||||||
|
width: 100px;
|
||||||
|
height: 130px;
|
||||||
|
border-radius: 3px;
|
||||||
|
|
||||||
|
::v-deep(.el-upload) {
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.personal-user-left-upload {
|
||||||
|
img {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
img {
|
||||||
|
animation: logoAnimation 0.3s ease-in-out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.personal-user-right {
|
||||||
|
flex: 1;
|
||||||
|
padding: 0 15px;
|
||||||
|
|
||||||
|
.personal-title {
|
||||||
|
font-size: 18px;
|
||||||
|
@include text-ellipsis(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.personal-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
font-size: 13px;
|
||||||
|
|
||||||
|
.personal-item-label {
|
||||||
|
color: gray;
|
||||||
|
@include text-ellipsis(1);
|
||||||
|
}
|
||||||
|
|
||||||
|
.personal-item-value {
|
||||||
|
@include text-ellipsis(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.personal-info {
|
||||||
|
.personal-info-more {
|
||||||
|
float: right;
|
||||||
|
color: gray;
|
||||||
|
font-size: 13px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.personal-info-box {
|
||||||
|
height: 130px;
|
||||||
|
overflow: hidden;
|
||||||
|
|
||||||
|
.personal-info-ul {
|
||||||
|
list-style: none;
|
||||||
|
|
||||||
|
.personal-info-li {
|
||||||
|
font-size: 13px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
|
||||||
|
.personal-info-li-title {
|
||||||
|
display: inline-block;
|
||||||
|
@include text-ellipsis(1);
|
||||||
|
color: grey;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
& a:hover {
|
||||||
|
color: var(--el-color-primary);
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.resource-info {
|
||||||
|
text-align: center;
|
||||||
|
|
||||||
|
::v-deep(.el-card__header) {
|
||||||
|
padding: 2px 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.resource-num {
|
||||||
|
font-weight: 700;
|
||||||
|
font-size: 2vw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.home-container {
|
.home-container {
|
||||||
overflow-x: hidden;
|
overflow-x: hidden;
|
||||||
|
|
||||||
@@ -182,7 +586,7 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.home-card-item-title-num {
|
.home-card-item-title-num {
|
||||||
font-size: 18px;
|
font-size: 2vw;
|
||||||
}
|
}
|
||||||
|
|
||||||
.home-card-item-tip-num {
|
.home-card-item-tip-num {
|
||||||
@@ -190,124 +594,5 @@ onMounted(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.home-card-first {
|
|
||||||
background: var(--bg-main-color);
|
|
||||||
border: 1px solid var(--el-border-color-light, #ebeef5);
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
img {
|
|
||||||
width: 60px;
|
|
||||||
height: 60px;
|
|
||||||
border-radius: 100%;
|
|
||||||
border: 2px solid var(--el-color-primary-light-5);
|
|
||||||
}
|
|
||||||
|
|
||||||
.home-card-first-right {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
|
|
||||||
.home-card-first-right-msg {
|
|
||||||
font-size: 13px;
|
|
||||||
color: gray;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.home-monitor {
|
|
||||||
height: 200px;
|
|
||||||
|
|
||||||
.flex-warp-item {
|
|
||||||
width: 50%;
|
|
||||||
height: 100px;
|
|
||||||
display: flex;
|
|
||||||
|
|
||||||
.flex-warp-item-box {
|
|
||||||
margin: auto;
|
|
||||||
height: auto;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.home-warning-card {
|
|
||||||
height: 292px;
|
|
||||||
|
|
||||||
::v-deep(.el-card) {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.home-dynamic {
|
|
||||||
height: 200px;
|
|
||||||
|
|
||||||
.home-dynamic-item {
|
|
||||||
display: flex;
|
|
||||||
width: 100%;
|
|
||||||
height: 60px;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
&:first-of-type {
|
|
||||||
.home-dynamic-item-line {
|
|
||||||
i {
|
|
||||||
color: orange !important;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.home-dynamic-item-left {
|
|
||||||
text-align: right;
|
|
||||||
|
|
||||||
.home-dynamic-item-left-time1 {
|
|
||||||
}
|
|
||||||
|
|
||||||
.home-dynamic-item-left-time2 {
|
|
||||||
font-size: 13px;
|
|
||||||
color: gray;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.home-dynamic-item-line {
|
|
||||||
height: 60px;
|
|
||||||
border-right: 2px dashed #dfdfdf;
|
|
||||||
margin: 0 20px;
|
|
||||||
position: relative;
|
|
||||||
|
|
||||||
i {
|
|
||||||
color: var(--el-color-primary);
|
|
||||||
font-size: 12px;
|
|
||||||
position: absolute;
|
|
||||||
top: 1px;
|
|
||||||
left: -6px;
|
|
||||||
transform: rotate(46deg);
|
|
||||||
background: white;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.home-dynamic-item-right {
|
|
||||||
flex: 1;
|
|
||||||
|
|
||||||
.home-dynamic-item-right-title {
|
|
||||||
i {
|
|
||||||
margin-right: 5px;
|
|
||||||
border: 1px solid #dfdfdf;
|
|
||||||
width: 20px;
|
|
||||||
height: 20px;
|
|
||||||
border-radius: 100%;
|
|
||||||
padding: 3px 2px 2px;
|
|
||||||
text-align: center;
|
|
||||||
color: var(--el-color-primary);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.home-dynamic-item-right-label {
|
|
||||||
font-size: 13px;
|
|
||||||
color: gray;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -191,12 +191,13 @@ const showResourceEdit = computed(() => {
|
|||||||
return state.form.type != AuthCertTypeEnum.Public.value && !props.resourceEdit;
|
return state.form.type != AuthCertTypeEnum.Public.value && !props.resourceEdit;
|
||||||
});
|
});
|
||||||
|
|
||||||
watch(
|
watch(dialogVisible, (val: any) => {
|
||||||
() => props.authCert,
|
if (val) {
|
||||||
(val: any) => {
|
setForm(props.authCert);
|
||||||
setForm(val);
|
} else {
|
||||||
|
cancelEdit();
|
||||||
}
|
}
|
||||||
);
|
});
|
||||||
|
|
||||||
const setForm = (val: any) => {
|
const setForm = (val: any) => {
|
||||||
val = { ...val };
|
val = { ...val };
|
||||||
@@ -246,10 +247,11 @@ const getCiphertext = async () => {
|
|||||||
|
|
||||||
const cancelEdit = () => {
|
const cancelEdit = () => {
|
||||||
dialogVisible.value = false;
|
dialogVisible.value = false;
|
||||||
emit('cancel');
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
acForm.value?.resetFields();
|
|
||||||
state.form = { ...DefaultForm };
|
state.form = { ...DefaultForm };
|
||||||
|
acForm.value?.resetFields();
|
||||||
|
emit('cancel');
|
||||||
}, 300);
|
}, 300);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -113,9 +113,6 @@ const deleteRow = (idx: any) => {
|
|||||||
|
|
||||||
const cancelEdit = () => {
|
const cancelEdit = () => {
|
||||||
state.dvisible = false;
|
state.dvisible = false;
|
||||||
setTimeout(() => {
|
|
||||||
state.form = {};
|
|
||||||
}, 300);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const btnOk = async (authCert: any) => {
|
const btnOk = async (authCert: any) => {
|
||||||
|
|||||||
87
mayfly_go_web/src/views/ops/component/TagCodePath.vue
Normal file
87
mayfly_go_web/src/views/ops/component/TagCodePath.vue
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
<template>
|
||||||
|
<div v-if="paths">
|
||||||
|
<el-row v-for="(path, idx) in paths?.slice(0, 1)" :key="idx">
|
||||||
|
<span v-for="item in parseTagPath(path)" :key="item.code">
|
||||||
|
<SvgIcon
|
||||||
|
:name="EnumValue.getEnumByValue(TagResourceTypeEnum, item.type)?.extra.icon"
|
||||||
|
:color="EnumValue.getEnumByValue(TagResourceTypeEnum, item.type)?.extra.iconColor"
|
||||||
|
class="mr2"
|
||||||
|
/>
|
||||||
|
<span> {{ item.code }}</span>
|
||||||
|
<SvgIcon v-if="!item.isEnd" class="mr5 ml5" name="arrow-right" />
|
||||||
|
</span>
|
||||||
|
|
||||||
|
<!-- 展示剩余的标签信息 -->
|
||||||
|
<el-popover :show-after="300" v-if="paths.length > 1 && idx == 0" placement="bottom" width="500" trigger="hover">
|
||||||
|
<template #reference>
|
||||||
|
<SvgIcon class="mt5 ml5" color="var(--el-color-primary)" name="MoreFilled" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<el-row v-for="i in paths.slice(1)" :key="i">
|
||||||
|
<span v-for="item in parseTagPath(i)" :key="item.code">
|
||||||
|
<SvgIcon
|
||||||
|
:name="EnumValue.getEnumByValue(TagResourceTypeEnum, item.type)?.extra.icon"
|
||||||
|
:color="EnumValue.getEnumByValue(TagResourceTypeEnum, item.type)?.extra.iconColor"
|
||||||
|
class="mr2"
|
||||||
|
/>
|
||||||
|
<span> {{ item.code }}</span>
|
||||||
|
<SvgIcon v-if="!item.isEnd" class="mr5 ml5" name="arrow-right" />
|
||||||
|
</span>
|
||||||
|
</el-row>
|
||||||
|
</el-popover>
|
||||||
|
</el-row>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { TagResourceTypeEnum } from '@/common/commonEnum';
|
||||||
|
import EnumValue from '@/common/Enum';
|
||||||
|
import { computed } from 'vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
path: {
|
||||||
|
type: [String, Array<string>],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const paths = computed(() => {
|
||||||
|
if (Array.isArray(props.path)) {
|
||||||
|
return props.path;
|
||||||
|
}
|
||||||
|
|
||||||
|
return [props.path];
|
||||||
|
});
|
||||||
|
|
||||||
|
const parseTagPath = (tagPath: string = '') => {
|
||||||
|
if (!tagPath) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
const res = [] as any;
|
||||||
|
const codes = tagPath.split('/');
|
||||||
|
for (let code of codes) {
|
||||||
|
const typeAndCode = code.split('|');
|
||||||
|
|
||||||
|
if (typeAndCode.length == 1) {
|
||||||
|
const tagCode = typeAndCode[0];
|
||||||
|
if (!tagCode) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.push({
|
||||||
|
type: TagResourceTypeEnum.Tag.value,
|
||||||
|
code: typeAndCode[0],
|
||||||
|
});
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
res.push({
|
||||||
|
type: typeAndCode[0],
|
||||||
|
code: typeAndCode[1],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
res[res.length - 1].isEnd = true;
|
||||||
|
return res;
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style lang="scss"></style>
|
||||||
@@ -203,8 +203,14 @@ const getNode = (nodeKey: any) => {
|
|||||||
return node;
|
return node;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const setCurrentKey = (nodeKey: any) => {
|
||||||
|
treeRef.value.setCurrentKey(nodeKey);
|
||||||
|
};
|
||||||
|
|
||||||
defineExpose({
|
defineExpose({
|
||||||
reloadNode,
|
reloadNode,
|
||||||
|
getNode,
|
||||||
|
setCurrentKey,
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|||||||
153
mayfly_go_web/src/views/ops/component/TagTreeCheck.vue
Executable file
153
mayfly_go_web/src/views/ops/component/TagTreeCheck.vue
Executable file
@@ -0,0 +1,153 @@
|
|||||||
|
<template>
|
||||||
|
<div class="w100" style="border: 1px solid var(--el-border-color)">
|
||||||
|
<el-input v-model="filterTag" clearable placeholder="输入关键字过滤" size="small" />
|
||||||
|
<el-scrollbar :style="{ height: props.height }">
|
||||||
|
<el-tree
|
||||||
|
v-bind="$attrs"
|
||||||
|
ref="tagTreeRef"
|
||||||
|
style="width: 100%"
|
||||||
|
:data="state.tags"
|
||||||
|
:default-expanded-keys="checkedTags"
|
||||||
|
:default-checked-keys="checkedTags"
|
||||||
|
multiple
|
||||||
|
:render-after-expand="true"
|
||||||
|
show-checkbox
|
||||||
|
check-strictly
|
||||||
|
:node-key="$props.nodeKey"
|
||||||
|
:props="{
|
||||||
|
value: $props.nodeKey,
|
||||||
|
label: 'codePath',
|
||||||
|
children: 'children',
|
||||||
|
disabled: 'disabled',
|
||||||
|
}"
|
||||||
|
@check="tagTreeNodeCheck"
|
||||||
|
:filter-node-method="filterNode"
|
||||||
|
>
|
||||||
|
<template #default="{ data }">
|
||||||
|
<span class="custom-tree-node">
|
||||||
|
<SvgIcon
|
||||||
|
:name="EnumValue.getEnumByValue(TagResourceTypeEnum, data.type)?.extra.icon"
|
||||||
|
:color="EnumValue.getEnumByValue(TagResourceTypeEnum, data.type)?.extra.iconColor"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<span class="font13 ml5">
|
||||||
|
{{ data.code }}
|
||||||
|
<span style="color: #3c8dbc">【</span>
|
||||||
|
{{ data.name }}
|
||||||
|
<span style="color: #3c8dbc">】</span>
|
||||||
|
<el-tag v-if="data.children !== null" size="small">{{ data.children.length }} </el-tag>
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</template>
|
||||||
|
</el-tree>
|
||||||
|
</el-scrollbar>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, reactive, onMounted, watch } from 'vue';
|
||||||
|
import { tagApi } from '../tag/api';
|
||||||
|
import { TagResourceTypeEnum } from '@/common/commonEnum';
|
||||||
|
import EnumValue from '@/common/Enum';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
height: {
|
||||||
|
type: [String, Number],
|
||||||
|
default: 'calc(100vh - 330px)',
|
||||||
|
},
|
||||||
|
tagType: {
|
||||||
|
type: Number,
|
||||||
|
default: TagResourceTypeEnum.Tag.value,
|
||||||
|
},
|
||||||
|
nodeKey: {
|
||||||
|
type: String,
|
||||||
|
default: 'codePath',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const checkedTags = defineModel<Array<any>>('modelValue', {
|
||||||
|
default: () => [],
|
||||||
|
});
|
||||||
|
|
||||||
|
const tagTreeRef: any = ref(null);
|
||||||
|
const filterTag = ref('');
|
||||||
|
|
||||||
|
const state = reactive({
|
||||||
|
tags: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
search();
|
||||||
|
});
|
||||||
|
|
||||||
|
const search = async () => {
|
||||||
|
state.tags = await tagApi.getTagTrees.request({ type: props.tagType });
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
const checkedNodes = tagTreeRef.value.getCheckedNodes();
|
||||||
|
console.log('check nodes: ', checkedNodes);
|
||||||
|
// 禁用选中节点的所有父节点,不可选中
|
||||||
|
for (let checkNodeData of checkedNodes) {
|
||||||
|
disableParentNodes(tagTreeRef.value.getNode(checkNodeData.codePath).parent);
|
||||||
|
}
|
||||||
|
}, 200);
|
||||||
|
};
|
||||||
|
|
||||||
|
watch(filterTag, (val) => {
|
||||||
|
tagTreeRef.value!.filter(val);
|
||||||
|
});
|
||||||
|
|
||||||
|
const filterNode = (value: string, data: any) => {
|
||||||
|
if (!value) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return data.codePath.toLowerCase().includes(value) || data.name.includes(value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const tagTreeNodeCheck = (data: any) => {
|
||||||
|
const node = tagTreeRef.value.getNode(data.codePath);
|
||||||
|
console.log('check node: ', node);
|
||||||
|
|
||||||
|
if (node.checked) {
|
||||||
|
// 如果选中了子节点,则需要将父节点全部取消选中,并禁用父节点
|
||||||
|
unCheckParentNodes(node.parent);
|
||||||
|
disableParentNodes(node.parent);
|
||||||
|
} else {
|
||||||
|
// 如果取消了选中,则需要根据条件恢复父节点的选中状态
|
||||||
|
disableParentNodes(node.parent, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更新绑定的值
|
||||||
|
checkedTags.value = tagTreeRef.value.getCheckedKeys(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const unCheckParentNodes = (node: any) => {
|
||||||
|
if (!node) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
tagTreeRef.value.setChecked(node, false, false);
|
||||||
|
unCheckParentNodes(node.parent);
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 禁用该节点以及所有父节点
|
||||||
|
* @param node 节点
|
||||||
|
* @param disable 是否禁用
|
||||||
|
*/
|
||||||
|
const disableParentNodes = (node: any, disable = true) => {
|
||||||
|
if (!node) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!disable) {
|
||||||
|
// 恢复为非禁用状态时,若同层级存在一个选中状态或者禁用状态,则继续禁用 不恢复非禁用状态。
|
||||||
|
for (let oneLevelNodes of node.childNodes) {
|
||||||
|
if (oneLevelNodes.checked || oneLevelNodes.data.disabled) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
node.data.disabled = disable;
|
||||||
|
disableParentNodes(node.parent, disable);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style lang="scss" scoped></style>
|
||||||
@@ -6,11 +6,9 @@
|
|||||||
@change="changeTag"
|
@change="changeTag"
|
||||||
:data="tags"
|
:data="tags"
|
||||||
placeholder="请选择关联标签"
|
placeholder="请选择关联标签"
|
||||||
:render-after-expand="true"
|
:default-expanded-keys="defaultExpandedKeys"
|
||||||
:default-expanded-keys="[state.selectTags]"
|
|
||||||
show-checkbox
|
show-checkbox
|
||||||
node-key="codePath"
|
node-key="codePath"
|
||||||
:check-strictly="props.checkStrictly"
|
|
||||||
:props="{
|
:props="{
|
||||||
value: 'codePath',
|
value: 'codePath',
|
||||||
label: 'codePath',
|
label: 'codePath',
|
||||||
@@ -19,6 +17,7 @@
|
|||||||
>
|
>
|
||||||
<template #default="{ data }">
|
<template #default="{ data }">
|
||||||
<span class="custom-tree-node">
|
<span class="custom-tree-node">
|
||||||
|
<SvgIcon :name="EnumValue.getEnumByValue(TagResourceTypeEnum, data.type)?.extra.icon" class="mr2" />
|
||||||
<span style="font-size: 13px">
|
<span style="font-size: 13px">
|
||||||
{{ data.code }}
|
{{ data.code }}
|
||||||
<span style="color: #3c8dbc">【</span>
|
<span style="color: #3c8dbc">【</span>
|
||||||
@@ -33,25 +32,22 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { toRefs, reactive, onMounted } from 'vue';
|
import { toRefs, reactive, onMounted, computed } from 'vue';
|
||||||
import { tagApi } from '../tag/api';
|
import { tagApi } from '../tag/api';
|
||||||
import { TagResourceTypeEnum } from '@/common/commonEnum';
|
import { TagResourceTypeEnum } from '@/common/commonEnum';
|
||||||
|
import EnumValue from '@/common/Enum';
|
||||||
|
|
||||||
//定义事件
|
//定义事件
|
||||||
const emit = defineEmits(['update:modelValue', 'changeTag', 'input']);
|
const emit = defineEmits(['update:modelValue', 'changeTag', 'input']);
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
selectTags: {
|
selectTags: {
|
||||||
type: [Array<any>],
|
type: [Array<any>, Object],
|
||||||
},
|
},
|
||||||
tagType: {
|
tagType: {
|
||||||
type: Number,
|
type: Number,
|
||||||
default: TagResourceTypeEnum.Tag.value,
|
default: TagResourceTypeEnum.Tag.value,
|
||||||
},
|
},
|
||||||
checkStrictly: {
|
|
||||||
type: Boolean,
|
|
||||||
default: false,
|
|
||||||
},
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
@@ -62,6 +58,16 @@ const state = reactive({
|
|||||||
|
|
||||||
const { tags } = toRefs(state);
|
const { tags } = toRefs(state);
|
||||||
|
|
||||||
|
const defaultExpandedKeys = computed(() => {
|
||||||
|
if (Array.isArray(state.selectTags)) {
|
||||||
|
// 如果 state.selectTags 是数组,直接返回
|
||||||
|
return state.selectTags;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果 state.selectTags 不是数组,转换为包含 state.selectTags 的数组
|
||||||
|
return [state.selectTags];
|
||||||
|
});
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
state.selectTags = props.selectTags;
|
state.selectTags = props.selectTags;
|
||||||
state.tags = await tagApi.getTagTrees.request({ type: props.tagType });
|
state.tags = await tagApi.getTagTrees.request({ type: props.tagType });
|
||||||
|
|||||||
@@ -171,3 +171,31 @@ export function getTagPathSearchItem(resourceType: number) {
|
|||||||
})
|
})
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据标签路径获取对应的类型与编号数组
|
||||||
|
* @param codePath 编号路径 tag1/tag2/1|xxx/11|yyy/
|
||||||
|
* @returns {1: ['xxx'], 11: ['yyy']}
|
||||||
|
*/
|
||||||
|
export function getTagTypeCodeByPath(codePath: string) {
|
||||||
|
const result = {};
|
||||||
|
const parts = codePath.split('/'); // 切分字符串并保留数字和对应的值部分
|
||||||
|
|
||||||
|
for (let part of parts) {
|
||||||
|
if (!part) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let [key, value] = part.split('|'); // 分割数字和值部分
|
||||||
|
// 如果不存在第二个参数,则说明为标签类型
|
||||||
|
if (!value) {
|
||||||
|
value = key;
|
||||||
|
key = '-1';
|
||||||
|
}
|
||||||
|
if (!result[key]) {
|
||||||
|
result[key] = [];
|
||||||
|
}
|
||||||
|
result[key].push(value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|||||||
@@ -2,7 +2,12 @@
|
|||||||
<div class="db-sql-exec">
|
<div class="db-sql-exec">
|
||||||
<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.DbName.value" :tag-path-node-type="NodeTypeTagPath" ref="tagTreeRef">
|
<tag-tree
|
||||||
|
:default-expanded-keys="state.defaultExpendKey"
|
||||||
|
:resource-type="TagResourceTypeEnum.DbName.value"
|
||||||
|
:tag-path-node-type="NodeTypeTagPath"
|
||||||
|
ref="tagTreeRef"
|
||||||
|
>
|
||||||
<template #prefix="{ data }">
|
<template #prefix="{ data }">
|
||||||
<span v-if="data.type.value == SqlExecNodeType.DbInst">
|
<span v-if="data.type.value == SqlExecNodeType.DbInst">
|
||||||
<el-popover
|
<el-popover
|
||||||
@@ -167,11 +172,11 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { defineAsyncComponent, h, onBeforeUnmount, onMounted, reactive, ref, toRefs } from 'vue';
|
import { defineAsyncComponent, h, onBeforeUnmount, onMounted, reactive, ref, toRefs, watch } from 'vue';
|
||||||
import { ElCheckbox, ElMessage, ElMessageBox } from 'element-plus';
|
import { ElCheckbox, ElMessage, ElMessageBox } from 'element-plus';
|
||||||
import { formatByteSize } from '@/common/utils/format';
|
import { formatByteSize } from '@/common/utils/format';
|
||||||
import { DbInst, registerDbCompletionItemProvider, TabInfo, TabType } from './db';
|
import { DbInst, registerDbCompletionItemProvider, TabInfo, TabType } from './db';
|
||||||
import { NodeType, TagTreeNode } from '../component/tag';
|
import { NodeType, TagTreeNode, getTagTypeCodeByPath } from '../component/tag';
|
||||||
import TagTree from '../component/TagTree.vue';
|
import TagTree from '../component/TagTree.vue';
|
||||||
import { dbApi } from './api';
|
import { dbApi } from './api';
|
||||||
import { dispposeCompletionItemProvider } from '@/components/monaco/completionItemProvider';
|
import { dispposeCompletionItemProvider } from '@/components/monaco/completionItemProvider';
|
||||||
@@ -183,6 +188,8 @@ import { TagResourceTypeEnum } from '@/common/commonEnum';
|
|||||||
import { Pane, Splitpanes } from 'splitpanes';
|
import { Pane, Splitpanes } from 'splitpanes';
|
||||||
import { useEventListener } from '@vueuse/core';
|
import { useEventListener } from '@vueuse/core';
|
||||||
import SqlExecBox from '@/views/ops/db/component/sqleditor/SqlExecBox';
|
import SqlExecBox from '@/views/ops/db/component/sqleditor/SqlExecBox';
|
||||||
|
import { useAutoOpenResource } from '@/store/autoOpenResource';
|
||||||
|
import { storeToRefs } from 'pinia';
|
||||||
|
|
||||||
const DbTableOp = defineAsyncComponent(() => import('./component/table/DbTableOp.vue'));
|
const DbTableOp = defineAsyncComponent(() => import('./component/table/DbTableOp.vue'));
|
||||||
const DbSqlEditor = defineAsyncComponent(() => import('./component/sqleditor/DbSqlEditor.vue'));
|
const DbSqlEditor = defineAsyncComponent(() => import('./component/sqleditor/DbSqlEditor.vue'));
|
||||||
@@ -258,7 +265,7 @@ const NodeTypeTagPath = new NodeType(TagTreeNode.TagPath)
|
|||||||
await sleep(100);
|
await sleep(100);
|
||||||
return dbInfos?.map((x: any) => {
|
return dbInfos?.map((x: any) => {
|
||||||
x.tagPath = parentNode.key;
|
x.tagPath = parentNode.key;
|
||||||
return new TagTreeNode(`${parentNode.key}.${x.id}`, x.name, NodeTypeDbInst).withParams(x);
|
return new TagTreeNode(`${x.code}`, x.name, NodeTypeDbInst).withParams(x);
|
||||||
});
|
});
|
||||||
})
|
})
|
||||||
.withContextMenuItems([ContextmenuItemRefresh]);
|
.withContextMenuItems([ContextmenuItemRefresh]);
|
||||||
@@ -267,6 +274,7 @@ const NodeTypeTagPath = new NodeType(TagTreeNode.TagPath)
|
|||||||
const NodeTypeDbInst = new NodeType(SqlExecNodeType.DbInst).withLoadNodesFunc((parentNode: TagTreeNode) => {
|
const NodeTypeDbInst = new NodeType(SqlExecNodeType.DbInst).withLoadNodesFunc((parentNode: TagTreeNode) => {
|
||||||
const params = parentNode.params;
|
const params = parentNode.params;
|
||||||
const dbs = params.database.split(' ')?.sort();
|
const dbs = params.database.split(' ')?.sort();
|
||||||
|
|
||||||
return dbs.map((x: any) => {
|
return dbs.map((x: any) => {
|
||||||
return new TagTreeNode(`${parentNode.key}.${x}`, x, NodeTypeDb)
|
return new TagTreeNode(`${parentNode.key}.${x}`, x, NodeTypeDb)
|
||||||
.withParams({
|
.withParams({
|
||||||
@@ -418,6 +426,7 @@ const tagTreeRef: any = ref(null);
|
|||||||
|
|
||||||
const tabs: Map<string, TabInfo> = new Map();
|
const tabs: Map<string, TabInfo> = new Map();
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
|
defaultExpendKey: [] as any,
|
||||||
/**
|
/**
|
||||||
* 当前操作的数据库实例
|
* 当前操作的数据库实例
|
||||||
*/
|
*/
|
||||||
@@ -452,7 +461,11 @@ const serverInfoReqParam = ref({
|
|||||||
});
|
});
|
||||||
const { execute: getDbServerInfo, isFetching: loadingServerInfo, data: dbServerInfo } = dbApi.getInstanceServerInfo.useApi<any>(serverInfoReqParam);
|
const { execute: getDbServerInfo, isFetching: loadingServerInfo, data: dbServerInfo } = dbApi.getInstanceServerInfo.useApi<any>(serverInfoReqParam);
|
||||||
|
|
||||||
|
const autoOpenResourceStore = useAutoOpenResource();
|
||||||
|
const { autoOpenResource } = storeToRefs(autoOpenResourceStore);
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
|
autoOpenDb(autoOpenResource.value.dbCodePath);
|
||||||
setHeight();
|
setHeight();
|
||||||
// 监听浏览器窗口大小变化,更新对应组件高度
|
// 监听浏览器窗口大小变化,更新对应组件高度
|
||||||
useEventListener(window, 'resize', setHeight);
|
useEventListener(window, 'resize', setHeight);
|
||||||
@@ -462,6 +475,31 @@ onBeforeUnmount(() => {
|
|||||||
dispposeCompletionItemProvider('sql');
|
dispposeCompletionItemProvider('sql');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => autoOpenResource.value.dbCodePath,
|
||||||
|
(codePath: any) => {
|
||||||
|
autoOpenDb(codePath);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const autoOpenDb = (codePath: string) => {
|
||||||
|
if (!codePath) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const typeAndCodes = getTagTypeCodeByPath(codePath);
|
||||||
|
const tagPath = typeAndCodes[TagResourceTypeEnum.Tag.value].join('/') + '/';
|
||||||
|
|
||||||
|
const dbCode = typeAndCodes[TagResourceTypeEnum.DbName.value][0];
|
||||||
|
state.defaultExpendKey = [tagPath, dbCode];
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
// 置空
|
||||||
|
autoOpenResourceStore.setDbCodePath('');
|
||||||
|
tagTreeRef.value.setCurrentKey(dbCode);
|
||||||
|
}, 600);
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 设置editor高度和数据表高度
|
* 设置editor高度和数据表高度
|
||||||
*/
|
*/
|
||||||
@@ -807,7 +845,7 @@ const getNowDbInfo = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.db-op {
|
.db-op {
|
||||||
height: calc(100vh - 108px);
|
height: calc(100vh - 106px);
|
||||||
}
|
}
|
||||||
|
|
||||||
#data-exec {
|
#data-exec {
|
||||||
|
|||||||
@@ -106,7 +106,9 @@ const props = defineProps({
|
|||||||
});
|
});
|
||||||
|
|
||||||
//定义事件
|
//定义事件
|
||||||
const emit = defineEmits(['update:visible', 'cancel', 'val-change']);
|
const emit = defineEmits(['cancel', 'val-change']);
|
||||||
|
|
||||||
|
const dialogVisible = defineModel<boolean>('visible', { default: false });
|
||||||
|
|
||||||
const rules = {
|
const rules = {
|
||||||
tagCodePaths: [
|
tagCodePaths: [
|
||||||
@@ -170,23 +172,19 @@ const defaultForm = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
dialogVisible: false,
|
|
||||||
sshTunnelMachineList: [] as any,
|
sshTunnelMachineList: [] as any,
|
||||||
form: defaultForm,
|
form: defaultForm,
|
||||||
submitForm: {} as any,
|
submitForm: {} as any,
|
||||||
pwd: '',
|
pwd: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
const { dialogVisible, form, submitForm } = toRefs(state);
|
const { form, submitForm } = toRefs(state);
|
||||||
|
|
||||||
const { isFetching: testConnBtnLoading, execute: testConnExec } = machineApi.testConn.useApi(submitForm);
|
const { isFetching: testConnBtnLoading, execute: testConnExec } = machineApi.testConn.useApi(submitForm);
|
||||||
const { isFetching: saveBtnLoading, execute: saveMachineExec } = machineApi.saveMachine.useApi(submitForm);
|
const { isFetching: saveBtnLoading, execute: saveMachineExec } = machineApi.saveMachine.useApi(submitForm);
|
||||||
|
|
||||||
watchEffect(() => {
|
watchEffect(() => {
|
||||||
state.dialogVisible = props.visible;
|
if (!dialogVisible.value) {
|
||||||
if (!state.dialogVisible) {
|
|
||||||
state.form = { ...defaultForm };
|
|
||||||
state.form.authCerts = [];
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const machine: any = props.machine;
|
const machine: any = props.machine;
|
||||||
@@ -194,6 +192,9 @@ watchEffect(() => {
|
|||||||
state.form = { ...machine };
|
state.form = { ...machine };
|
||||||
state.form.tagCodePaths = machine.tags.map((t: any) => t.codePath);
|
state.form.tagCodePaths = machine.tags.map((t: any) => t.codePath);
|
||||||
state.form.authCerts = machine.authCerts || [];
|
state.form.authCerts = machine.authCerts || [];
|
||||||
|
} else {
|
||||||
|
state.form = { ...defaultForm };
|
||||||
|
state.form.authCerts = [];
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -250,7 +251,7 @@ const handleChangeProtocol = (val: any) => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const cancel = () => {
|
const cancel = () => {
|
||||||
emit('update:visible', false);
|
dialogVisible.value = false;
|
||||||
emit('cancel');
|
emit('cancel');
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
@@ -8,6 +8,7 @@
|
|||||||
ref="tagTreeRef"
|
ref="tagTreeRef"
|
||||||
:resource-type="TagResourceTypeEnum.MachineAuthCert.value"
|
:resource-type="TagResourceTypeEnum.MachineAuthCert.value"
|
||||||
:tag-path-node-type="NodeTypeTagPath"
|
:tag-path-node-type="NodeTypeTagPath"
|
||||||
|
:default-expanded-keys="state.defaultExpendKey"
|
||||||
>
|
>
|
||||||
<template #prefix="{ data }">
|
<template #prefix="{ data }">
|
||||||
<SvgIcon
|
<SvgIcon
|
||||||
@@ -153,13 +154,13 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { defineAsyncComponent, nextTick, reactive, ref, toRefs, watch } from 'vue';
|
import { defineAsyncComponent, nextTick, onMounted, reactive, ref, toRefs, watch } from 'vue';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import { getMachineTerminalSocketUrl, machineApi } from './api';
|
import { getMachineTerminalSocketUrl, machineApi } from './api';
|
||||||
import { dateFormat } from '@/common/utils/date';
|
import { dateFormat } from '@/common/utils/date';
|
||||||
import { hasPerms } from '@/components/auth/auth';
|
import { hasPerms } from '@/components/auth/auth';
|
||||||
import { TagResourceTypeEnum } from '@/common/commonEnum';
|
import { TagResourceTypeEnum } from '@/common/commonEnum';
|
||||||
import { NodeType, TagTreeNode } from '../component/tag';
|
import { NodeType, TagTreeNode, getTagTypeCodeByPath } from '../component/tag';
|
||||||
import TagTree from '../component/TagTree.vue';
|
import TagTree from '../component/TagTree.vue';
|
||||||
import { Pane, Splitpanes } from 'splitpanes';
|
import { Pane, Splitpanes } from 'splitpanes';
|
||||||
import { ContextmenuItem } from '@/components/contextmenu/index';
|
import { ContextmenuItem } from '@/components/contextmenu/index';
|
||||||
@@ -169,6 +170,8 @@ import MachineRdp from '@/components/terminal-rdp/MachineRdp.vue';
|
|||||||
import MachineFile from '@/views/ops/machine/file/MachineFile.vue';
|
import MachineFile from '@/views/ops/machine/file/MachineFile.vue';
|
||||||
import ResourceTags from '../component/ResourceTags.vue';
|
import ResourceTags from '../component/ResourceTags.vue';
|
||||||
import { MachineProtocolEnum } from './enums';
|
import { MachineProtocolEnum } from './enums';
|
||||||
|
import { useAutoOpenResource } from '@/store/autoOpenResource';
|
||||||
|
import { storeToRefs } from 'pinia';
|
||||||
|
|
||||||
// 组件
|
// 组件
|
||||||
const ScriptManage = defineAsyncComponent(() => import('./ScriptManage.vue'));
|
const ScriptManage = defineAsyncComponent(() => import('./ScriptManage.vue'));
|
||||||
@@ -196,6 +199,7 @@ class MachineNodeType {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
|
defaultExpendKey: [] as any,
|
||||||
params: {
|
params: {
|
||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
pageSize: 0,
|
pageSize: 0,
|
||||||
@@ -252,6 +256,9 @@ const { infoDialog, serviceDialog, processDialog, fileDialog, machineStatsDialog
|
|||||||
|
|
||||||
const tagTreeRef: any = ref(null);
|
const tagTreeRef: any = ref(null);
|
||||||
|
|
||||||
|
const autoOpenResourceStore = useAutoOpenResource();
|
||||||
|
const { autoOpenResource } = storeToRefs(autoOpenResourceStore);
|
||||||
|
|
||||||
let openIds = {};
|
let openIds = {};
|
||||||
|
|
||||||
const NodeTypeTagPath = new NodeType(TagTreeNode.TagPath).withLoadNodesFunc(async (node: TagTreeNode) => {
|
const NodeTypeTagPath = new NodeType(TagTreeNode.TagPath).withLoadNodesFunc(async (node: TagTreeNode) => {
|
||||||
@@ -263,7 +270,7 @@ const NodeTypeTagPath = new NodeType(TagTreeNode.TagPath).withLoadNodesFunc(asyn
|
|||||||
// 把list 根据name字段排序
|
// 把list 根据name字段排序
|
||||||
res.list = res.list.sort((a: any, b: any) => a.name.localeCompare(b.name));
|
res.list = res.list.sort((a: any, b: any) => a.name.localeCompare(b.name));
|
||||||
return res.list.map((x: any) =>
|
return res.list.map((x: any) =>
|
||||||
new TagTreeNode(x.id, x.name, NodeTypeMachine)
|
new TagTreeNode(x.code, x.name, NodeTypeMachine)
|
||||||
.withParams(x)
|
.withParams(x)
|
||||||
.withDisabled(x.status == -1 && x.protocol == MachineProtocolEnum.Ssh.value)
|
.withDisabled(x.status == -1 && x.protocol == MachineProtocolEnum.Ssh.value)
|
||||||
.withIcon({
|
.withIcon({
|
||||||
@@ -279,7 +286,7 @@ const NodeTypeMachine = new NodeType(MachineNodeType.Machine)
|
|||||||
// 获取授权凭证列表
|
// 获取授权凭证列表
|
||||||
const authCerts = machine.authCerts;
|
const authCerts = machine.authCerts;
|
||||||
return authCerts.map((x: any) =>
|
return authCerts.map((x: any) =>
|
||||||
new TagTreeNode(x.id, x.username, NodeTypeAuthCert)
|
new TagTreeNode(x.name, x.username, NodeTypeAuthCert)
|
||||||
.withParams({ ...machine, selectAuthCert: x })
|
.withParams({ ...machine, selectAuthCert: x })
|
||||||
.withDisabled(machine.status == -1 && machine.protocol == MachineProtocolEnum.Ssh.value)
|
.withDisabled(machine.status == -1 && machine.protocol == MachineProtocolEnum.Ssh.value)
|
||||||
.withIcon({
|
.withIcon({
|
||||||
@@ -323,6 +330,47 @@ const NodeTypeAuthCert = new NodeType(MachineNodeType.AuthCert)
|
|||||||
.withOnClick((node: any) => serviceManager(node.params)),
|
.withOnClick((node: any) => serviceManager(node.params)),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => autoOpenResource.value.machineCodePath,
|
||||||
|
(codePath: any) => {
|
||||||
|
autoOpenTerminal(codePath);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => state.activeTermName,
|
||||||
|
(newValue, oldValue) => {
|
||||||
|
oldValue && terminalRefs[oldValue]?.blur && terminalRefs[oldValue]?.blur();
|
||||||
|
terminalRefs[newValue]?.focus && terminalRefs[newValue]?.focus();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
autoOpenTerminal(autoOpenResource.value.machineCodePath);
|
||||||
|
});
|
||||||
|
|
||||||
|
const autoOpenTerminal = (codePath: string) => {
|
||||||
|
if (!codePath) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const typeAndCodes = getTagTypeCodeByPath(codePath);
|
||||||
|
const tagPath = typeAndCodes[TagResourceTypeEnum.Tag.value].join('/') + '/';
|
||||||
|
|
||||||
|
const machineCode = typeAndCodes[TagResourceTypeEnum.Machine.value][0];
|
||||||
|
state.defaultExpendKey = [tagPath, machineCode];
|
||||||
|
|
||||||
|
const authCertName = typeAndCodes[TagResourceTypeEnum.MachineAuthCert.value][0];
|
||||||
|
setTimeout(() => {
|
||||||
|
// 置空
|
||||||
|
autoOpenResourceStore.setMachineCodePath('');
|
||||||
|
tagTreeRef.value.setCurrentKey(authCertName);
|
||||||
|
|
||||||
|
const acNode = tagTreeRef.value.getNode(authCertName);
|
||||||
|
openTerminal(acNode.data.params);
|
||||||
|
}, 600);
|
||||||
|
};
|
||||||
|
|
||||||
const openTerminal = (machine: any, ex?: boolean) => {
|
const openTerminal = (machine: any, ex?: boolean) => {
|
||||||
// 授权凭证名
|
// 授权凭证名
|
||||||
const ac = machine.selectAuthCert.name;
|
const ac = machine.selectAuthCert.name;
|
||||||
@@ -465,15 +513,6 @@ const onRemoveTab = (targetName: string) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
watch(
|
|
||||||
() => state.activeTermName,
|
|
||||||
(newValue, oldValue) => {
|
|
||||||
console.log('oldValue', oldValue);
|
|
||||||
oldValue && terminalRefs[oldValue]?.blur && terminalRefs[oldValue]?.blur();
|
|
||||||
terminalRefs[newValue]?.focus && terminalRefs[newValue]?.focus();
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
const terminalStatusChange = (key: string, status: TerminalStatus) => {
|
const terminalStatusChange = (key: string, status: TerminalStatus) => {
|
||||||
state.tabs.get(key).status = status;
|
state.tabs.get(key).status = status;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -58,6 +58,12 @@ export const cronJobApi = {
|
|||||||
execList: Api.newGet('/machine-cronjobs/execs'),
|
execList: Api.newGet('/machine-cronjobs/execs'),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const cmdConfApi = {
|
||||||
|
list: Api.newGet('/machine/security/cmd-confs'),
|
||||||
|
save: Api.newPost('/machine/security/cmd-confs'),
|
||||||
|
delete: Api.newDelete('/machine/security/cmd-confs/{id}'),
|
||||||
|
};
|
||||||
|
|
||||||
export function getMachineTerminalSocketUrl(authCertName: any) {
|
export function getMachineTerminalSocketUrl(authCertName: any) {
|
||||||
return `${config.baseWsUrl}/machines/terminal/${authCertName}?${joinClientParams()}`;
|
return `${config.baseWsUrl}/machines/terminal/${authCertName}?${joinClientParams()}`;
|
||||||
}
|
}
|
||||||
|
|||||||
222
mayfly_go_web/src/views/ops/machine/security/CmdConfList.vue
Normal file
222
mayfly_go_web/src/views/ops/machine/security/CmdConfList.vue
Normal file
@@ -0,0 +1,222 @@
|
|||||||
|
<template>
|
||||||
|
<div>
|
||||||
|
<el-table :data="cmdConfs" stripe>
|
||||||
|
<el-table-column prop="name" label="名称" show-overflow-tooltip min-width="100px"> </el-table-column>
|
||||||
|
<el-table-column prop="cmds" label="过滤命令" min-width="320px" show-overflow-tooltip>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-tag class="ml2 mt2" v-for="cmd in scope.row.cmds" :key="cmd" type="danger">
|
||||||
|
{{ cmd }}
|
||||||
|
</el-tag>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="codePaths" label="关联机器" min-width="220px" show-overflow-tooltip>
|
||||||
|
<template #default="scope">
|
||||||
|
<TagCodePath :path="scope.row.tags.map((tag: any) => tag.codePath)" />
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
<el-table-column prop="remark" label="备注" show-overflow-tooltip width="120px"> </el-table-column>
|
||||||
|
<el-table-column prop="creator" label="创建者" show-overflow-tooltip width="100px"> </el-table-column>
|
||||||
|
|
||||||
|
<el-table-column label="操作" min-wdith="100px">
|
||||||
|
<template #header>
|
||||||
|
<el-text tag="b">操作</el-text>
|
||||||
|
<el-button v-auth="'cmdconf:save'" class="ml5" type="primary" circle size="small" icon="Plus" @click="openFormDialog(false)"> </el-button>
|
||||||
|
</template>
|
||||||
|
<template #default="scope">
|
||||||
|
<el-button v-auth="'cmdconf:save'" @click="openFormDialog(scope.row)" type="primary" link>编辑</el-button>
|
||||||
|
<el-button v-auth="'cmdconf:del'" @click="deleteCmdConf(scope.row)" type="danger" link>删除</el-button>
|
||||||
|
</template>
|
||||||
|
</el-table-column>
|
||||||
|
</el-table>
|
||||||
|
|
||||||
|
<el-drawer title="命令配置" v-model="dialogVisible" :show-close="false" width="600px" :destroy-on-close="true" :close-on-click-modal="false">
|
||||||
|
<template #header>
|
||||||
|
<DrawerHeader header="命令配置" :back="cancelEdit" />
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<el-form ref="formRef" :model="state.form" :rules="rules" label-width="auto">
|
||||||
|
<el-form-item prop="name" label="名称" required>
|
||||||
|
<el-input v-model="form.name" placeholder="名称"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item prop="cmds" label="过滤命令" required>
|
||||||
|
<el-row>
|
||||||
|
<el-tag
|
||||||
|
class="ml2 mt2"
|
||||||
|
v-for="tag in form.cmds"
|
||||||
|
:key="tag"
|
||||||
|
closable
|
||||||
|
:disable-transitions="false"
|
||||||
|
@close="handleCmdClose(tag)"
|
||||||
|
type="danger"
|
||||||
|
>
|
||||||
|
{{ tag }}
|
||||||
|
</el-tag>
|
||||||
|
<el-input
|
||||||
|
v-if="state.inputCmdVisible"
|
||||||
|
ref="cmdInputRef"
|
||||||
|
v-model="state.cmdInputValue"
|
||||||
|
class="mt3"
|
||||||
|
size="small"
|
||||||
|
@keyup.enter="handleCmdInputConfirm"
|
||||||
|
@blur="handleCmdInputConfirm"
|
||||||
|
placeholder="请输入命令正则表达式"
|
||||||
|
/>
|
||||||
|
<el-button v-else class="ml2 mt2" size="small" @click="showCmdInput"> + 新建命令 </el-button>
|
||||||
|
</el-row>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item label="备注">
|
||||||
|
<el-input v-model="form.remark" type="textarea" :rows="2"></el-input>
|
||||||
|
</el-form-item>
|
||||||
|
|
||||||
|
<el-form-item ref="tagSelectRef" prop="codePaths" label="关联机器">
|
||||||
|
<tag-tree-check height="calc(100vh - 430px)" :tag-type="TagResourceTypeEnum.MachineAuthCert.value" v-model="form.codePaths" />
|
||||||
|
</el-form-item>
|
||||||
|
</el-form>
|
||||||
|
<template #footer>
|
||||||
|
<div class="dialog-footer">
|
||||||
|
<el-button :loading="submiting" @click="cancelEdit">取 消</el-button>
|
||||||
|
<el-button v-auth="'cmdconf:save'" type="primary" :loading="submiting" @click="submitForm">确 定</el-button>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
</el-drawer>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { ref, toRefs, reactive, onMounted, nextTick } from 'vue';
|
||||||
|
import TagTreeCheck from '../../component/TagTreeCheck.vue';
|
||||||
|
import { TagResourceTypeEnum } from '@/common/commonEnum';
|
||||||
|
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||||
|
import { cmdConfApi } from '../api';
|
||||||
|
import DrawerHeader from '@/components/drawer-header/DrawerHeader.vue';
|
||||||
|
import TagCodePath from '../../component/TagCodePath.vue';
|
||||||
|
import _ from 'lodash';
|
||||||
|
|
||||||
|
const rules = {
|
||||||
|
tags: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请选择关联的机器',
|
||||||
|
trigger: ['change'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
cmds: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请创建命令',
|
||||||
|
trigger: ['change', 'blur'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
name: [
|
||||||
|
{
|
||||||
|
required: true,
|
||||||
|
message: '请输入名称',
|
||||||
|
trigger: ['change', 'blur'],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
const tagSelectRef: any = ref(null);
|
||||||
|
const formRef: any = ref(null);
|
||||||
|
const cmdInputRef: any = ref(null);
|
||||||
|
|
||||||
|
const DefaultForm = {
|
||||||
|
id: 0,
|
||||||
|
name: '',
|
||||||
|
codePaths: [],
|
||||||
|
cmds: [] as any,
|
||||||
|
remark: '',
|
||||||
|
};
|
||||||
|
|
||||||
|
const state = reactive({
|
||||||
|
cmdConfs: [],
|
||||||
|
dialogVisible: false,
|
||||||
|
form: DefaultForm,
|
||||||
|
submiting: false,
|
||||||
|
inputCmdVisible: false,
|
||||||
|
cmdInputValue: '',
|
||||||
|
});
|
||||||
|
|
||||||
|
const { cmdConfs, dialogVisible, form, submiting } = toRefs(state);
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
getCmdConfs();
|
||||||
|
});
|
||||||
|
|
||||||
|
const getCmdConfs = async () => {
|
||||||
|
state.cmdConfs = await cmdConfApi.list.request();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCmdClose = (tag: string) => {
|
||||||
|
state.form.cmds.splice(state.form.cmds.indexOf(tag), 1);
|
||||||
|
};
|
||||||
|
|
||||||
|
const showCmdInput = () => {
|
||||||
|
state.inputCmdVisible = true;
|
||||||
|
nextTick(() => {
|
||||||
|
cmdInputRef.value!.input!.focus();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCmdInputConfirm = () => {
|
||||||
|
if (state.cmdInputValue) {
|
||||||
|
state.form.cmds.push(state.cmdInputValue);
|
||||||
|
}
|
||||||
|
state.inputCmdVisible = false;
|
||||||
|
state.cmdInputValue = '';
|
||||||
|
};
|
||||||
|
|
||||||
|
const openFormDialog = (data: any) => {
|
||||||
|
if (!data) {
|
||||||
|
state.form = { ...DefaultForm };
|
||||||
|
} else {
|
||||||
|
state.form = _.cloneDeep(data);
|
||||||
|
state.form.codePaths = data.tags.map((tag: any) => tag.codePath);
|
||||||
|
}
|
||||||
|
state.dialogVisible = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const deleteCmdConf = async (data: any) => {
|
||||||
|
await ElMessageBox.confirm(`确定删除该[${data.name}]命令配置?`, '提示', {
|
||||||
|
confirmButtonText: '确定',
|
||||||
|
cancelButtonText: '取消',
|
||||||
|
type: 'warning',
|
||||||
|
});
|
||||||
|
|
||||||
|
await cmdConfApi.delete.request({ id: data.id });
|
||||||
|
ElMessage.success('操作成功');
|
||||||
|
getCmdConfs();
|
||||||
|
};
|
||||||
|
|
||||||
|
const cancelEdit = () => {
|
||||||
|
state.dialogVisible = false;
|
||||||
|
// 取消表单的校验
|
||||||
|
setTimeout(() => {
|
||||||
|
state.form = { ...DefaultForm };
|
||||||
|
formRef.value.resetFields();
|
||||||
|
}, 200);
|
||||||
|
};
|
||||||
|
|
||||||
|
const submitForm = () => {
|
||||||
|
formRef.value.validate(async (valid: boolean) => {
|
||||||
|
if (!valid) {
|
||||||
|
ElMessage.error('请正确填写信息');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
state.submiting = true;
|
||||||
|
await cmdConfApi.save.request(state.form);
|
||||||
|
ElMessage.success('操作成功');
|
||||||
|
|
||||||
|
cancelEdit();
|
||||||
|
getCmdConfs();
|
||||||
|
} finally {
|
||||||
|
state.submiting = false;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
<style></style>
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
<template>
|
||||||
|
<div class="card">
|
||||||
|
<el-tabs v-model="activeName" class="demo-tabs" @tab-change="handleTabChange">
|
||||||
|
<el-tab-pane label="命令配置" :name="CmdConfTab">
|
||||||
|
<CmdConfList />
|
||||||
|
</el-tab-pane>
|
||||||
|
</el-tabs>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script lang="ts" setup>
|
||||||
|
import { toRefs, reactive, onMounted, defineAsyncComponent } from 'vue';
|
||||||
|
|
||||||
|
const CmdConfList = defineAsyncComponent(() => import('./CmdConfList.vue'));
|
||||||
|
|
||||||
|
const CmdConfTab = 'cmdConf';
|
||||||
|
|
||||||
|
const state = reactive({
|
||||||
|
activeName: CmdConfTab,
|
||||||
|
cmdConfs: [],
|
||||||
|
});
|
||||||
|
|
||||||
|
const { activeName } = toRefs(state);
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
state.activeName = CmdConfTab;
|
||||||
|
});
|
||||||
|
|
||||||
|
const handleTabChange = (tabName: any) => {
|
||||||
|
if (tabName == CmdConfTab) {
|
||||||
|
console.log('get cmd confs');
|
||||||
|
}
|
||||||
|
console.log(tabName);
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style></style>
|
||||||
@@ -2,7 +2,12 @@
|
|||||||
<div class="flex-all-center">
|
<div class="flex-all-center">
|
||||||
<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.Mongo.value" :tag-path-node-type="NodeTypeTagPath">
|
<tag-tree
|
||||||
|
ref="tagTreeRef"
|
||||||
|
:default-expanded-keys="state.defaultExpendKey"
|
||||||
|
:resource-type="TagResourceTypeEnum.Mongo.value"
|
||||||
|
:tag-path-node-type="NodeTypeTagPath"
|
||||||
|
>
|
||||||
<template #prefix="{ data }">
|
<template #prefix="{ data }">
|
||||||
<span v-if="data.type.value == MongoNodeType.Mongo">
|
<span v-if="data.type.value == MongoNodeType.Mongo">
|
||||||
<el-popover :show-after="500" placement="right-start" title="mongo实例信息" trigger="hover" :width="250">
|
<el-popover :show-after="500" placement="right-start" title="mongo实例信息" trigger="hover" :width="250">
|
||||||
@@ -168,16 +173,18 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { mongoApi } from './api';
|
import { mongoApi } from './api';
|
||||||
import { computed, defineAsyncComponent, reactive, ref, toRefs } from 'vue';
|
import { computed, defineAsyncComponent, onMounted, reactive, ref, toRefs, watch } from 'vue';
|
||||||
import { ElMessage } from 'element-plus';
|
import { ElMessage } from 'element-plus';
|
||||||
|
|
||||||
import { isTrue, notBlank } from '@/common/assert';
|
import { isTrue, notBlank } from '@/common/assert';
|
||||||
import { TagTreeNode, NodeType } from '../component/tag';
|
import { TagTreeNode, NodeType, getTagTypeCodeByPath } from '../component/tag';
|
||||||
import TagTree from '../component/TagTree.vue';
|
import TagTree from '../component/TagTree.vue';
|
||||||
import { formatByteSize } from '@/common/utils/format';
|
import { formatByteSize } from '@/common/utils/format';
|
||||||
import { TagResourceTypeEnum } from '@/common/commonEnum';
|
import { TagResourceTypeEnum } from '@/common/commonEnum';
|
||||||
import { sleep } from '@/common/utils/loading';
|
import { sleep } from '@/common/utils/loading';
|
||||||
import { Splitpanes, Pane } from 'splitpanes';
|
import { Splitpanes, Pane } from 'splitpanes';
|
||||||
|
import { useAutoOpenResource } from '@/store/autoOpenResource';
|
||||||
|
import { storeToRefs } from 'pinia';
|
||||||
|
|
||||||
const MonacoEditor = defineAsyncComponent(() => import('@/components/monaco/MonacoEditor.vue'));
|
const MonacoEditor = defineAsyncComponent(() => import('@/components/monaco/MonacoEditor.vue'));
|
||||||
|
|
||||||
@@ -207,7 +214,7 @@ const NodeTypeTagPath = new NodeType(TagTreeNode.TagPath).withLoadNodesFunc(asyn
|
|||||||
await sleep(100);
|
await sleep(100);
|
||||||
return mongoInfos?.map((x: any) => {
|
return mongoInfos?.map((x: any) => {
|
||||||
x.tagPath = parentNode.key;
|
x.tagPath = parentNode.key;
|
||||||
return new TagTreeNode(`${parentNode.key}.${x.id}`, x.name, NodeTypeMongo).withParams(x);
|
return new TagTreeNode(`${x.code}`, x.name, NodeTypeMongo).withParams(x);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -250,7 +257,10 @@ const NodeTypeColl = new NodeType(MongoNodeType.Coll).withNodeClickFunc((nodeDat
|
|||||||
});
|
});
|
||||||
|
|
||||||
const findParamInputRef: any = ref(null);
|
const findParamInputRef: any = ref(null);
|
||||||
|
const tagTreeRef: any = ref(null);
|
||||||
|
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
|
defaultExpendKey: [] as any,
|
||||||
tags: [],
|
tags: [],
|
||||||
mongoList: [] as any,
|
mongoList: [] as any,
|
||||||
activeName: '', // 当前操作的tab
|
activeName: '', // 当前操作的tab
|
||||||
@@ -282,10 +292,42 @@ const state = reactive({
|
|||||||
|
|
||||||
const { findDialog, docEditDialog } = toRefs(state);
|
const { findDialog, docEditDialog } = toRefs(state);
|
||||||
|
|
||||||
|
const autoOpenResourceStore = useAutoOpenResource();
|
||||||
|
const { autoOpenResource } = storeToRefs(autoOpenResourceStore);
|
||||||
|
|
||||||
const nowColl = computed(() => {
|
const nowColl = computed(() => {
|
||||||
return getNowDataTab();
|
return getNowDataTab();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => autoOpenResource.value.mongoCodePath,
|
||||||
|
(codePath: any) => {
|
||||||
|
autoOpenMongo(codePath);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
autoOpenMongo(autoOpenResource.value.mongoCodePath);
|
||||||
|
});
|
||||||
|
|
||||||
|
const autoOpenMongo = (codePath: string) => {
|
||||||
|
if (!codePath) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const typeAndCodes = getTagTypeCodeByPath(codePath);
|
||||||
|
const tagPath = typeAndCodes[TagResourceTypeEnum.Tag.value].join('/') + '/';
|
||||||
|
|
||||||
|
const mongoCode = typeAndCodes[TagResourceTypeEnum.Mongo.value][0];
|
||||||
|
state.defaultExpendKey = [tagPath, mongoCode];
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
// 置空
|
||||||
|
autoOpenResourceStore.setMongoCodePath('');
|
||||||
|
tagTreeRef.value.setCurrentKey(mongoCode);
|
||||||
|
}, 600);
|
||||||
|
};
|
||||||
|
|
||||||
const changeCollection = async (id: any, schema: string, collection: string) => {
|
const changeCollection = async (id: any, schema: string, collection: string) => {
|
||||||
const label = `${id}:\`${schema}\`.${collection}`;
|
const label = `${id}:\`${schema}\`.${collection}`;
|
||||||
let dataTab = state.dataTabs[label];
|
let dataTab = state.dataTabs[label];
|
||||||
|
|||||||
@@ -2,7 +2,12 @@
|
|||||||
<div class="redis-data-op flex-all-center">
|
<div class="redis-data-op flex-all-center">
|
||||||
<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
|
||||||
|
ref="tagTreeRef"
|
||||||
|
:default-expanded-keys="state.defaultExpendKey"
|
||||||
|
:resource-type="TagResourceTypeEnum.Redis.value"
|
||||||
|
:tag-path-node-type="NodeTypeTagPath"
|
||||||
|
>
|
||||||
<template #prefix="{ data }">
|
<template #prefix="{ data }">
|
||||||
<span v-if="data.type.value == RedisNodeType.Redis">
|
<span v-if="data.type.value == RedisNodeType.Redis">
|
||||||
<el-popover :show-after="500" placement="right-start" title="redis实例信息" trigger="hover" :width="250">
|
<el-popover :show-after="500" placement="right-start" title="redis实例信息" trigger="hover" :width="250">
|
||||||
@@ -178,11 +183,11 @@
|
|||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { redisApi } from './api';
|
import { redisApi } from './api';
|
||||||
import { ref, defineAsyncComponent, toRefs, reactive, onMounted, nextTick, Ref } from 'vue';
|
import { ref, defineAsyncComponent, toRefs, reactive, onMounted, nextTick, Ref, watch } from 'vue';
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||||
import { isTrue, notBlank, notNull } from '@/common/assert';
|
import { isTrue, notBlank, notNull } from '@/common/assert';
|
||||||
import { copyToClipboard } from '@/common/utils/string';
|
import { copyToClipboard } from '@/common/utils/string';
|
||||||
import { TagTreeNode, NodeType } from '../component/tag';
|
import { TagTreeNode, NodeType, getTagTypeCodeByPath } 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';
|
||||||
@@ -190,6 +195,8 @@ 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 { RedisInst } from './redis';
|
import { RedisInst } from './redis';
|
||||||
|
import { useAutoOpenResource } from '@/store/autoOpenResource';
|
||||||
|
import { storeToRefs } from 'pinia';
|
||||||
|
|
||||||
const KeyDetail = defineAsyncComponent(() => import('./KeyDetail.vue'));
|
const KeyDetail = defineAsyncComponent(() => import('./KeyDetail.vue'));
|
||||||
|
|
||||||
@@ -230,7 +237,7 @@ const NodeTypeTagPath = new NodeType(TagTreeNode.TagPath).withLoadNodesFunc(asyn
|
|||||||
await sleep(100);
|
await sleep(100);
|
||||||
return redisInfos.map((x: any) => {
|
return redisInfos.map((x: any) => {
|
||||||
x.tagPath = parentNode.key;
|
x.tagPath = parentNode.key;
|
||||||
return new TagTreeNode(`${parentNode.key}.${x.id}`, x.name, NodeTypeRedis).withParams(x);
|
return new TagTreeNode(`${x.code}`, x.name, NodeTypeRedis).withParams(x);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -288,9 +295,11 @@ const treeProps = {
|
|||||||
const defaultCount = 250;
|
const defaultCount = 250;
|
||||||
|
|
||||||
const keyTreeRef: any = ref(null);
|
const keyTreeRef: any = ref(null);
|
||||||
|
const tagTreeRef: any = ref(null);
|
||||||
const redisInst: Ref<RedisInst> = ref(new RedisInst());
|
const redisInst: Ref<RedisInst> = ref(new RedisInst());
|
||||||
|
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
|
defaultExpendKey: [] as any,
|
||||||
tags: [],
|
tags: [],
|
||||||
redisList: [] as any,
|
redisList: [] as any,
|
||||||
dbList: [],
|
dbList: [],
|
||||||
@@ -331,7 +340,37 @@ const state = reactive({
|
|||||||
|
|
||||||
const { scanParam, keyTreeData, newKeyDialog } = toRefs(state);
|
const { scanParam, keyTreeData, newKeyDialog } = toRefs(state);
|
||||||
|
|
||||||
onMounted(async () => {});
|
const autoOpenResourceStore = useAutoOpenResource();
|
||||||
|
const { autoOpenResource } = storeToRefs(autoOpenResourceStore);
|
||||||
|
|
||||||
|
onMounted(async () => {
|
||||||
|
autoOpenRedis(autoOpenResource.value.redisCodePath);
|
||||||
|
});
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => autoOpenResource.value.redisCodePath,
|
||||||
|
(codePath: any) => {
|
||||||
|
autoOpenRedis(codePath);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const autoOpenRedis = (codePath: string) => {
|
||||||
|
if (!codePath) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const typeAndCodes = getTagTypeCodeByPath(codePath);
|
||||||
|
const tagPath = typeAndCodes[TagResourceTypeEnum.Tag.value].join('/') + '/';
|
||||||
|
|
||||||
|
const redisCode = typeAndCodes[TagResourceTypeEnum.Redis.value][0];
|
||||||
|
state.defaultExpendKey = [tagPath, redisCode];
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
// 置空
|
||||||
|
autoOpenResourceStore.setRedisCodePath('');
|
||||||
|
tagTreeRef.value.setCurrentKey(redisCode);
|
||||||
|
}, 600);
|
||||||
|
};
|
||||||
|
|
||||||
const scan = async (appendKey = false) => {
|
const scan = async (appendKey = false) => {
|
||||||
isTrue(state.scanParam.id != null, '请先选择redis');
|
isTrue(state.scanParam.id != null, '请先选择redis');
|
||||||
|
|||||||
@@ -75,11 +75,7 @@
|
|||||||
<el-descriptions-item label="code">{{ currentTag.code }}</el-descriptions-item>
|
<el-descriptions-item label="code">{{ currentTag.code }}</el-descriptions-item>
|
||||||
|
|
||||||
<el-descriptions-item label="路径" :span="2">
|
<el-descriptions-item label="路径" :span="2">
|
||||||
<span v-for="item in parseTagPath(currentTag.codePath)" :key="item.code">
|
<TagCodePath :path="currentTag.codePath" />
|
||||||
<SvgIcon :name="EnumValue.getEnumByValue(TagResourceTypeEnum, item.type)?.extra.icon" class="mr2" />
|
|
||||||
<span> {{ item.code }}</span>
|
|
||||||
<SvgIcon v-if="!item.isEnd" class="mr5 ml5" name="arrow-right" />
|
|
||||||
</span>
|
|
||||||
</el-descriptions-item>
|
</el-descriptions-item>
|
||||||
|
|
||||||
<el-descriptions-item label="名称">{{ currentTag.name }}</el-descriptions-item>
|
<el-descriptions-item label="名称">{{ currentTag.name }}</el-descriptions-item>
|
||||||
@@ -163,6 +159,7 @@ import { TagResourceTypeEnum } from '@/common/commonEnum';
|
|||||||
import EnumTag from '@/components/enumtag/EnumTag.vue';
|
import EnumTag from '@/components/enumtag/EnumTag.vue';
|
||||||
import EnumValue from '@/common/Enum';
|
import EnumValue from '@/common/Enum';
|
||||||
import InstanceList from '../db/InstanceList.vue';
|
import InstanceList from '../db/InstanceList.vue';
|
||||||
|
import TagCodePath from '../component/TagCodePath.vue';
|
||||||
|
|
||||||
interface Tree {
|
interface Tree {
|
||||||
id: number;
|
id: number;
|
||||||
@@ -345,38 +342,6 @@ const handleDrop = async (draggingNode: any, dropNode: any) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const parseTagPath = (tagPath: string) => {
|
|
||||||
if (!tagPath) {
|
|
||||||
return [];
|
|
||||||
}
|
|
||||||
const res = [] as any;
|
|
||||||
const codes = tagPath.split('/');
|
|
||||||
for (let code of codes) {
|
|
||||||
const typeAndCode = code.split('|');
|
|
||||||
|
|
||||||
if (typeAndCode.length == 1) {
|
|
||||||
const tagCode = typeAndCode[0];
|
|
||||||
if (!tagCode) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
res.push({
|
|
||||||
type: TagResourceTypeEnum.Tag.value,
|
|
||||||
code: typeAndCode[0],
|
|
||||||
});
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
res.push({
|
|
||||||
type: typeAndCode[0],
|
|
||||||
code: typeAndCode[1],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
res[res.length - 1].isEnd = true;
|
|
||||||
return res;
|
|
||||||
};
|
|
||||||
|
|
||||||
const tabChange = () => {
|
const tabChange = () => {
|
||||||
setNowTabData();
|
setNowTabData();
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -14,11 +14,8 @@
|
|||||||
<el-button v-auth="'team:del'" :disabled="selectionData.length < 1" @click="deleteTeam()" type="danger" icon="delete">删除</el-button>
|
<el-button v-auth="'team:del'" :disabled="selectionData.length < 1" @click="deleteTeam()" type="danger" icon="delete">删除</el-button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #tagPath="{ data }">
|
<template #tags="{ data }">
|
||||||
<tag-info :tag-path="data.tagPath" />
|
<TagCodePath :path="data.tags?.map((tag: any) => tag.codePath)" />
|
||||||
<span class="ml5">
|
|
||||||
{{ data.tagPath }}
|
|
||||||
</span>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template #action="{ data }">
|
<template #action="{ data }">
|
||||||
@@ -48,45 +45,7 @@
|
|||||||
</el-form-item>
|
</el-form-item>
|
||||||
|
|
||||||
<el-form-item prop="tag" label="标签">
|
<el-form-item prop="tag" label="标签">
|
||||||
<div class="w100" style="border: 1px solid var(--el-border-color)">
|
<TagTreeCheck v-model="state.addTeamDialog.form.codePaths" :tag-type="0" />
|
||||||
<el-input v-model="filterTag" clearable placeholder="输入关键字过滤" size="small" />
|
|
||||||
<el-scrollbar style="height: calc(100vh - 330px)">
|
|
||||||
<el-tree
|
|
||||||
ref="tagTreeRef"
|
|
||||||
style="width: 100%"
|
|
||||||
:data="state.tags"
|
|
||||||
:default-expanded-keys="state.addTeamDialog.form.tags"
|
|
||||||
:default-checked-keys="state.addTeamDialog.form.tags"
|
|
||||||
multiple
|
|
||||||
:render-after-expand="true"
|
|
||||||
show-checkbox
|
|
||||||
check-strictly
|
|
||||||
node-key="id"
|
|
||||||
:props="{
|
|
||||||
value: 'id',
|
|
||||||
label: 'codePath',
|
|
||||||
children: 'children',
|
|
||||||
disabled: 'disabled',
|
|
||||||
}"
|
|
||||||
@check="tagTreeNodeCheck"
|
|
||||||
:filter-node-method="filterNode"
|
|
||||||
>
|
|
||||||
<template #default="{ data }">
|
|
||||||
<span class="custom-tree-node">
|
|
||||||
<SvgIcon :name="EnumValue.getEnumByValue(TagResourceTypeEnum, data.type)?.extra.icon" />
|
|
||||||
|
|
||||||
<span class="font13 ml5">
|
|
||||||
{{ data.code }}
|
|
||||||
<span style="color: #3c8dbc">【</span>
|
|
||||||
{{ data.name }}
|
|
||||||
<span style="color: #3c8dbc">】</span>
|
|
||||||
<el-tag v-if="data.children !== null" size="small">{{ data.children.length }} </el-tag>
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</template>
|
|
||||||
</el-tree>
|
|
||||||
</el-scrollbar>
|
|
||||||
</div>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
</el-form>
|
</el-form>
|
||||||
<template #footer>
|
<template #footer>
|
||||||
@@ -131,7 +90,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref, toRefs, reactive, onMounted, Ref, watch } from 'vue';
|
import { ref, toRefs, reactive, onMounted, Ref } from 'vue';
|
||||||
import { tagApi } from './api';
|
import { tagApi } from './api';
|
||||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||||
import { notBlank } from '@/common/assert';
|
import { notBlank } from '@/common/assert';
|
||||||
@@ -139,20 +98,19 @@ import PageTable from '@/components/pagetable/PageTable.vue';
|
|||||||
import { TableColumn } from '@/components/pagetable';
|
import { TableColumn } from '@/components/pagetable';
|
||||||
import { SearchItem } from '@/components/SearchForm';
|
import { SearchItem } from '@/components/SearchForm';
|
||||||
import AccountSelectFormItem from '@/views/system/account/components/AccountSelectFormItem.vue';
|
import AccountSelectFormItem from '@/views/system/account/components/AccountSelectFormItem.vue';
|
||||||
import { TagResourceTypeEnum } from '@/common/commonEnum';
|
|
||||||
import EnumValue from '@/common/Enum';
|
|
||||||
import DrawerHeader from '@/components/drawer-header/DrawerHeader.vue';
|
import DrawerHeader from '@/components/drawer-header/DrawerHeader.vue';
|
||||||
|
import TagTreeCheck from '../component/TagTreeCheck.vue';
|
||||||
|
import TagCodePath from '../component/TagCodePath.vue';
|
||||||
|
|
||||||
const teamForm: any = ref(null);
|
const teamForm: any = ref(null);
|
||||||
const tagTreeRef: any = ref(null);
|
|
||||||
const pageTableRef: Ref<any> = ref(null);
|
const pageTableRef: Ref<any> = ref(null);
|
||||||
const showMemPageTableRef: Ref<any> = ref(null);
|
const showMemPageTableRef: Ref<any> = ref(null);
|
||||||
const filterTag = ref('');
|
|
||||||
|
|
||||||
const searchItems = [SearchItem.input('name', '团队名称')];
|
const searchItems = [SearchItem.input('name', '团队名称')];
|
||||||
const columns = [
|
const columns = [
|
||||||
TableColumn.new('name', '团队名称'),
|
TableColumn.new('name', '团队名称'),
|
||||||
TableColumn.new('remark', '备注'),
|
TableColumn.new('remark', '备注'),
|
||||||
|
TableColumn.new('tags', '分配标签').isSlot().setAddWidth(40),
|
||||||
TableColumn.new('creator', '创建者'),
|
TableColumn.new('creator', '创建者'),
|
||||||
TableColumn.new('createTime', '创建时间').isTime(),
|
TableColumn.new('createTime', '创建时间').isTime(),
|
||||||
TableColumn.new('modifier', '修改者'),
|
TableColumn.new('modifier', '修改者'),
|
||||||
@@ -162,10 +120,9 @@ const columns = [
|
|||||||
|
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
currentEditPermissions: false,
|
currentEditPermissions: false,
|
||||||
tags: [],
|
|
||||||
addTeamDialog: {
|
addTeamDialog: {
|
||||||
visible: false,
|
visible: false,
|
||||||
form: { id: 0, name: '', remark: '', tags: [] },
|
form: { id: 0, name: '', remark: '', codePaths: [] },
|
||||||
},
|
},
|
||||||
query: {
|
query: {
|
||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
@@ -211,34 +168,13 @@ const search = async () => {
|
|||||||
pageTableRef.value.search();
|
pageTableRef.value.search();
|
||||||
};
|
};
|
||||||
|
|
||||||
watch(filterTag, (val) => {
|
|
||||||
tagTreeRef.value!.filter(val);
|
|
||||||
});
|
|
||||||
|
|
||||||
const filterNode = (value: string, data: any) => {
|
|
||||||
if (!value) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return data.codePath.toLowerCase().includes(value) || data.name.includes(value);
|
|
||||||
};
|
|
||||||
|
|
||||||
const showSaveTeamDialog = async (data: any) => {
|
const showSaveTeamDialog = async (data: any) => {
|
||||||
state.tags = await tagApi.getTagTrees.request(null);
|
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
state.addTeamDialog.form.id = data.id;
|
state.addTeamDialog.form.id = data.id;
|
||||||
state.addTeamDialog.form.name = data.name;
|
state.addTeamDialog.form.name = data.name;
|
||||||
state.addTeamDialog.form.remark = data.remark;
|
state.addTeamDialog.form.remark = data.remark;
|
||||||
state.addTeamDialog.form.tags = await tagApi.getTeamTagIds.request({ teamId: data.id });
|
state.addTeamDialog.form.codePaths = data.tags?.map((tag: any) => tag.codePath);
|
||||||
|
// state.addTeamDialog.form.tags = await tagApi.getRelateTagIds.request({ relateType: TagTreeRelateTypeEnum.Team.value, relateId: data.id });
|
||||||
setTimeout(() => {
|
|
||||||
const checkedNodes = tagTreeRef.value.getCheckedNodes();
|
|
||||||
console.log('check nodes: ', checkedNodes);
|
|
||||||
// 禁用选中节点的所有父节点,不可选中
|
|
||||||
for (let checkNodeData of checkedNodes) {
|
|
||||||
disableParentNodes(tagTreeRef.value.getNode(checkNodeData.id).parent);
|
|
||||||
}
|
|
||||||
}, 200);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
state.addTeamDialog.visible = true;
|
state.addTeamDialog.visible = true;
|
||||||
@@ -248,7 +184,6 @@ const saveTeam = async () => {
|
|||||||
teamForm.value.validate(async (valid: any) => {
|
teamForm.value.validate(async (valid: any) => {
|
||||||
if (valid) {
|
if (valid) {
|
||||||
const form = state.addTeamDialog.form;
|
const form = state.addTeamDialog.form;
|
||||||
form.tags = tagTreeRef.value.getCheckedKeys(false);
|
|
||||||
await tagApi.saveTeam.request(form);
|
await tagApi.saveTeam.request(form);
|
||||||
ElMessage.success('保存成功');
|
ElMessage.success('保存成功');
|
||||||
search();
|
search();
|
||||||
@@ -318,48 +253,5 @@ const cancelAddMember = () => {
|
|||||||
state.showMemDialog.memForm = {} as any;
|
state.showMemDialog.memForm = {} as any;
|
||||||
state.showMemDialog.addVisible = false;
|
state.showMemDialog.addVisible = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
const tagTreeNodeCheck = (data: any) => {
|
|
||||||
const node = tagTreeRef.value.getNode(data.id);
|
|
||||||
console.log('check node: ', node);
|
|
||||||
|
|
||||||
if (node.checked) {
|
|
||||||
// 如果选中了子节点,则需要将父节点全部取消选中,并禁用父节点
|
|
||||||
unCheckParentNodes(node.parent);
|
|
||||||
disableParentNodes(node.parent);
|
|
||||||
} else {
|
|
||||||
// 如果取消了选中,则需要根据条件恢复父节点的选中状态
|
|
||||||
disableParentNodes(node.parent, false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const unCheckParentNodes = (node: any) => {
|
|
||||||
if (!node) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
tagTreeRef.value.setChecked(node, false, false);
|
|
||||||
unCheckParentNodes(node.parent);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 禁用该节点以及所有父节点
|
|
||||||
* @param node 节点
|
|
||||||
* @param disable 是否禁用
|
|
||||||
*/
|
|
||||||
const disableParentNodes = (node: any, disable = true) => {
|
|
||||||
if (!node) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!disable) {
|
|
||||||
// 恢复为非禁用状态时,若同层级存在一个选中状态或者禁用状态,则继续禁用 不恢复非禁用状态。
|
|
||||||
for (let oneLevelNodes of node.childNodes) {
|
|
||||||
if (oneLevelNodes.checked || oneLevelNodes.data.disabled) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
node.data.disabled = disable;
|
|
||||||
disableParentNodes(node.parent, disable);
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
<style lang="scss" scoped></style>
|
<style lang="scss" scoped></style>
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ export const tagApi = {
|
|||||||
|
|
||||||
getResourceTagPaths: Api.newGet('/tag-trees/resources/{resourceType}/tag-paths'),
|
getResourceTagPaths: Api.newGet('/tag-trees/resources/{resourceType}/tag-paths'),
|
||||||
countTagResource: Api.newGet('/tag-trees/resources/count'),
|
countTagResource: Api.newGet('/tag-trees/resources/count'),
|
||||||
|
getRelateTagIds: Api.newGet('/tag-trees/relate/{relateType}/{relateId}'),
|
||||||
|
|
||||||
getTeams: Api.newGet('/teams'),
|
getTeams: Api.newGet('/teams'),
|
||||||
saveTeam: Api.newPost('/teams'),
|
saveTeam: Api.newPost('/teams'),
|
||||||
@@ -17,8 +18,6 @@ export const tagApi = {
|
|||||||
getTeamMem: Api.newGet('/teams/{teamId}/members'),
|
getTeamMem: Api.newGet('/teams/{teamId}/members'),
|
||||||
saveTeamMem: Api.newPost('/teams/{teamId}/members'),
|
saveTeamMem: Api.newPost('/teams/{teamId}/members'),
|
||||||
delTeamMem: Api.newDelete('/teams/{teamId}/members/{accountId}'),
|
delTeamMem: Api.newDelete('/teams/{teamId}/members/{accountId}'),
|
||||||
|
|
||||||
getTeamTagIds: Api.newGet('/teams/{teamId}/tags'),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export const resourceAuthCertApi = {
|
export const resourceAuthCertApi = {
|
||||||
@@ -27,3 +26,7 @@ export const resourceAuthCertApi = {
|
|||||||
save: Api.newPost('/auth-certs'),
|
save: Api.newPost('/auth-certs'),
|
||||||
delete: Api.newDelete('/auth-certs/{id}'),
|
delete: Api.newDelete('/auth-certs/{id}'),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const resourceOpLogApi = {
|
||||||
|
getAccountResourceOpLogs: Api.newGet('/resource-op-logs/account'),
|
||||||
|
};
|
||||||
|
|||||||
@@ -14,3 +14,7 @@ export const AuthCertCiphertextTypeEnum = {
|
|||||||
PrivateKey: EnumValue.of(2, '秘钥').tagTypeSuccess(),
|
PrivateKey: EnumValue.of(2, '秘钥').tagTypeSuccess(),
|
||||||
Public: EnumValue.of(-1, '公共凭证').tagTypeSuccess(),
|
Public: EnumValue.of(-1, '公共凭证').tagTypeSuccess(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const TagTreeRelateTypeEnum = {
|
||||||
|
Team: EnumValue.of(1, '团队'),
|
||||||
|
};
|
||||||
|
|||||||
@@ -1,112 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<div class="personal">
|
<div class="personal">
|
||||||
<el-row>
|
<el-row>
|
||||||
<!-- 个人信息 -->
|
|
||||||
<el-col :xs="24" :sm="16">
|
|
||||||
<el-card shadow="hover" header="个人信息">
|
|
||||||
<div class="personal-user">
|
|
||||||
<div class="personal-user-left">
|
|
||||||
<el-upload class="h100 personal-user-left-upload" action="" multiple :limit="1">
|
|
||||||
<img :src="userInfo.photo" />
|
|
||||||
</el-upload>
|
|
||||||
</div>
|
|
||||||
<div class="personal-user-right">
|
|
||||||
<el-row>
|
|
||||||
<el-col :span="24" class="personal-title mb18"
|
|
||||||
>{{ currentTime }},{{ userInfo.name }},生活变的再糟糕,也不妨碍我变得更好!
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="24">
|
|
||||||
<el-row>
|
|
||||||
<el-col :xs="24" :sm="12" class="personal-item mb6">
|
|
||||||
<div class="personal-item-label">用户名:</div>
|
|
||||||
<div class="personal-item-value">{{ userInfo.username }}</div>
|
|
||||||
</el-col>
|
|
||||||
<el-col :xs="24" :sm="12" class="personal-item mb6">
|
|
||||||
<div class="personal-item-label">角色:</div>
|
|
||||||
<div class="personal-item-value">{{ roleInfo }}</div>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
</el-col>
|
|
||||||
<el-col :span="24">
|
|
||||||
<el-row>
|
|
||||||
<el-col :xs="24" :sm="12" class="personal-item mb6">
|
|
||||||
<div class="personal-item-label">上次登录IP:</div>
|
|
||||||
<div class="personal-item-value">{{ userInfo.lastLoginIp }}</div>
|
|
||||||
</el-col>
|
|
||||||
<el-col :xs="24" :sm="12" class="personal-item mb6">
|
|
||||||
<div class="personal-item-label">上次登录时间:</div>
|
|
||||||
<div class="personal-item-value">{{ dateFormat(userInfo.lastLoginTime) }}</div>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-card>
|
|
||||||
</el-col>
|
|
||||||
|
|
||||||
<!-- 消息通知 -->
|
|
||||||
<el-col :xs="24" :sm="8" class="pl15 personal-info">
|
|
||||||
<el-card shadow="hover">
|
|
||||||
<template #header>
|
|
||||||
<span>消息通知</span>
|
|
||||||
<span @click="showMsgs" class="personal-info-more">更多</span>
|
|
||||||
</template>
|
|
||||||
<div class="personal-info-box">
|
|
||||||
<ul class="personal-info-ul">
|
|
||||||
<li v-for="(v, k) in msgDialog.msgs.list as any" :key="k" class="personal-info-li">
|
|
||||||
<a class="personal-info-li-title">{{ `[${getMsgTypeDesc(v.type)}] ${v.msg}` }}</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
</el-card>
|
|
||||||
</el-col>
|
|
||||||
|
|
||||||
<el-dialog width="900px" title="消息" v-model="msgDialog.visible">
|
|
||||||
<el-table border :data="msgDialog.msgs.list" size="small">
|
|
||||||
<el-table-column property="type" label="类型" width="60">
|
|
||||||
<template #default="scope">
|
|
||||||
{{ getMsgTypeDesc(scope.row.type) }}
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
<el-table-column property="msg" label="消息"></el-table-column>
|
|
||||||
<el-table-column property="createTime" label="时间" width="150">
|
|
||||||
<template #default="scope">
|
|
||||||
{{ dateFormat(scope.row.createTime) }}
|
|
||||||
</template>
|
|
||||||
</el-table-column>
|
|
||||||
</el-table>
|
|
||||||
<el-row type="flex" class="mt5" justify="center">
|
|
||||||
<el-pagination
|
|
||||||
small
|
|
||||||
@current-change="getMsgs"
|
|
||||||
style="text-align: center"
|
|
||||||
background
|
|
||||||
layout="prev, pager, next, total, jumper"
|
|
||||||
:total="msgDialog.msgs.total"
|
|
||||||
v-model:current-page="msgDialog.query.pageNum"
|
|
||||||
:page-size="msgDialog.query.pageSize"
|
|
||||||
/>
|
|
||||||
</el-row>
|
|
||||||
</el-dialog>
|
|
||||||
|
|
||||||
<!-- 营销推荐 -->
|
|
||||||
<!-- <el-col :span="24">
|
|
||||||
<el-card shadow="hover" class="mt15" header="营销推荐">
|
|
||||||
<el-row :gutter="15" class="personal-recommend-row">
|
|
||||||
<el-col :sm="6" v-for="(v, k) in recommendList" :key="k" class="personal-recommend-col">
|
|
||||||
<div class="personal-recommend" :style="{ 'background-color': v.bg }">
|
|
||||||
<i :class="v.icon" :style="{ color: v.iconColor }"></i>
|
|
||||||
<div class="personal-recommend-auto">
|
|
||||||
<div>{{ v.title }}</div>
|
|
||||||
<div class="personal-recommend-msg">{{ v.msg }}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</el-col>
|
|
||||||
</el-row>
|
|
||||||
</el-card>
|
|
||||||
</el-col> -->
|
|
||||||
|
|
||||||
<!-- 更新信息 -->
|
<!-- 更新信息 -->
|
||||||
<el-col :span="24">
|
<el-col :span="24">
|
||||||
<el-card shadow="hover" class="mt15 personal-edit" header="更新信息">
|
<el-card shadow="hover" class="mt15 personal-edit" header="更新信息">
|
||||||
@@ -142,28 +36,6 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</span>
|
</span>
|
||||||
<!-- <div class="personal-edit-safe-box">
|
|
||||||
<div class="personal-edit-safe-item">
|
|
||||||
<div class="personal-edit-safe-item-left">
|
|
||||||
<div class="personal-edit-safe-item-left-label">密保手机</div>
|
|
||||||
<div class="personal-edit-safe-item-left-value">已绑定手机:132****4108</div>
|
|
||||||
</div>
|
|
||||||
<div class="personal-edit-safe-item-right">
|
|
||||||
<el-button type="text">立即修改</el-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="personal-edit-safe-box">
|
|
||||||
<div class="personal-edit-safe-item">
|
|
||||||
<div class="personal-edit-safe-item-left">
|
|
||||||
<div class="personal-edit-safe-item-left-label">密保问题</div>
|
|
||||||
<div class="personal-edit-safe-item-left-value">已设置密保问题,账号安全大幅度提升</div>
|
|
||||||
</div>
|
|
||||||
<div class="personal-edit-safe-item-right">
|
|
||||||
<el-button type="text">立即设置</el-button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div> -->
|
|
||||||
</el-card>
|
</el-card>
|
||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
@@ -171,33 +43,16 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { toRefs, reactive, computed, onMounted } from 'vue';
|
import { toRefs, reactive, onMounted } from 'vue';
|
||||||
import { ElMessage } from 'element-plus';
|
import { ElMessage } from 'element-plus';
|
||||||
import { formatAxis } from '@/common/utils/format';
|
|
||||||
import { personApi } from './api';
|
import { personApi } from './api';
|
||||||
import { dateFormat } from '@/common/utils/date';
|
|
||||||
import { storeToRefs } from 'pinia';
|
|
||||||
import { useUserInfo } from '@/store/userInfo';
|
|
||||||
import config from '@/common/config';
|
import config from '@/common/config';
|
||||||
import { joinClientParams } from '@/common/request';
|
import { joinClientParams } from '@/common/request';
|
||||||
|
|
||||||
const { userInfo } = storeToRefs(useUserInfo());
|
|
||||||
const state = reactive({
|
const state = reactive({
|
||||||
accountInfo: {
|
accountInfo: {
|
||||||
roles: [],
|
roles: [],
|
||||||
},
|
},
|
||||||
msgs: [],
|
|
||||||
msgDialog: {
|
|
||||||
visible: false,
|
|
||||||
query: {
|
|
||||||
pageSize: 10,
|
|
||||||
pageNum: 1,
|
|
||||||
},
|
|
||||||
msgs: {
|
|
||||||
list: [],
|
|
||||||
total: null,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
recommendList: [],
|
recommendList: [],
|
||||||
accountForm: {
|
accountForm: {
|
||||||
password: '',
|
password: '',
|
||||||
@@ -208,27 +63,10 @@ const state = reactive({
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
const { msgDialog, accountForm, authStatus } = toRefs(state);
|
const { accountForm, authStatus } = toRefs(state);
|
||||||
|
|
||||||
// 当前时间提示语
|
|
||||||
const currentTime = computed(() => {
|
|
||||||
return formatAxis(new Date());
|
|
||||||
});
|
|
||||||
|
|
||||||
const showMsgs = () => {
|
|
||||||
state.msgDialog.visible = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
const roleInfo = computed(() => {
|
|
||||||
if (state.accountInfo.roles.length == 0) {
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
return state.accountInfo.roles.map((val: any) => val.name).join('、');
|
|
||||||
});
|
|
||||||
|
|
||||||
onMounted(async () => {
|
onMounted(async () => {
|
||||||
getAccountInfo();
|
getAccountInfo();
|
||||||
getMsgs();
|
|
||||||
state.authStatus = await personApi.authStatus.request();
|
state.authStatus = await personApi.authStatus.request();
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -277,162 +115,11 @@ const unbindOAuth2 = async () => {
|
|||||||
ElMessage.success('解绑成功');
|
ElMessage.success('解绑成功');
|
||||||
state.authStatus = await personApi.authStatus.request();
|
state.authStatus = await personApi.authStatus.request();
|
||||||
};
|
};
|
||||||
|
|
||||||
const getMsgs = async () => {
|
|
||||||
const res = await personApi.getMsgs.request(state.msgDialog.query);
|
|
||||||
state.msgDialog.msgs = res;
|
|
||||||
};
|
|
||||||
|
|
||||||
const getMsgTypeDesc = (type: number) => {
|
|
||||||
if (type == 1) {
|
|
||||||
return '登录';
|
|
||||||
}
|
|
||||||
if (type == 2) {
|
|
||||||
return '通知';
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style scoped lang="scss">
|
<style scoped lang="scss">
|
||||||
@import '../../theme/mixins/index.scss';
|
@import '../../theme/mixins/index.scss';
|
||||||
|
|
||||||
.personal {
|
.personal {
|
||||||
.personal-user {
|
|
||||||
height: 130px;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
|
|
||||||
.personal-user-left {
|
|
||||||
width: 100px;
|
|
||||||
height: 130px;
|
|
||||||
border-radius: 3px;
|
|
||||||
|
|
||||||
::v-deep(.el-upload) {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.personal-user-left-upload {
|
|
||||||
img {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
border-radius: 3px;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
img {
|
|
||||||
animation: logoAnimation 0.3s ease-in-out;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.personal-user-right {
|
|
||||||
flex: 1;
|
|
||||||
padding: 0 15px;
|
|
||||||
|
|
||||||
.personal-title {
|
|
||||||
font-size: 18px;
|
|
||||||
@include text-ellipsis(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.personal-item {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
font-size: 13px;
|
|
||||||
|
|
||||||
.personal-item-label {
|
|
||||||
color: gray;
|
|
||||||
@include text-ellipsis(1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.personal-item-value {
|
|
||||||
@include text-ellipsis(1);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.personal-info {
|
|
||||||
.personal-info-more {
|
|
||||||
float: right;
|
|
||||||
color: gray;
|
|
||||||
font-size: 13px;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
color: var(--el-color-primary);
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.personal-info-box {
|
|
||||||
height: 130px;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
.personal-info-ul {
|
|
||||||
list-style: none;
|
|
||||||
|
|
||||||
.personal-info-li {
|
|
||||||
font-size: 13px;
|
|
||||||
padding-bottom: 10px;
|
|
||||||
|
|
||||||
.personal-info-li-title {
|
|
||||||
display: inline-block;
|
|
||||||
@include text-ellipsis(1);
|
|
||||||
color: grey;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
& a:hover {
|
|
||||||
color: var(--el-color-primary);
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.personal-recommend-row {
|
|
||||||
.personal-recommend-col {
|
|
||||||
.personal-recommend {
|
|
||||||
position: relative;
|
|
||||||
height: 100px;
|
|
||||||
color: #ffffff;
|
|
||||||
border-radius: 3px;
|
|
||||||
overflow: hidden;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
i {
|
|
||||||
right: 0px !important;
|
|
||||||
bottom: 0px !important;
|
|
||||||
transition: all ease 0.3s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
i {
|
|
||||||
position: absolute;
|
|
||||||
right: -10px;
|
|
||||||
bottom: -10px;
|
|
||||||
font-size: 70px;
|
|
||||||
transform: rotate(-30deg);
|
|
||||||
transition: all ease 0.3s;
|
|
||||||
}
|
|
||||||
|
|
||||||
.personal-recommend-auto {
|
|
||||||
padding: 15px;
|
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
top: 5%;
|
|
||||||
|
|
||||||
.personal-recommend-msg {
|
|
||||||
font-size: 12px;
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.personal-edit {
|
.personal-edit {
|
||||||
.personal-edit-title {
|
.personal-edit-title {
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|||||||
@@ -128,7 +128,7 @@
|
|||||||
</el-col>
|
</el-col>
|
||||||
</el-row>
|
</el-row>
|
||||||
</el-form>
|
</el-form>
|
||||||
e
|
|
||||||
<template #footer>
|
<template #footer>
|
||||||
<div>
|
<div>
|
||||||
<el-button @click="cancel()">取 消</el-button>
|
<el-button @click="cancel()">取 消</el-button>
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ require (
|
|||||||
github.com/pquerna/otp v1.4.0
|
github.com/pquerna/otp v1.4.0
|
||||||
github.com/redis/go-redis/v9 v9.5.1
|
github.com/redis/go-redis/v9 v9.5.1
|
||||||
github.com/robfig/cron/v3 v3.0.1 // 定时任务
|
github.com/robfig/cron/v3 v3.0.1 // 定时任务
|
||||||
github.com/sijms/go-ora/v2 v2.8.12
|
github.com/sijms/go-ora/v2 v2.8.13
|
||||||
github.com/stretchr/testify v1.8.4
|
github.com/stretchr/testify v1.8.4
|
||||||
github.com/veops/go-ansiterm v0.0.5
|
github.com/veops/go-ansiterm v0.0.5
|
||||||
go.mongodb.org/mongo-driver v1.15.0 // mongo
|
go.mongodb.org/mongo-driver v1.15.0 // mongo
|
||||||
@@ -39,7 +39,7 @@ require (
|
|||||||
gopkg.in/yaml.v3 v3.0.1
|
gopkg.in/yaml.v3 v3.0.1
|
||||||
// gorm
|
// gorm
|
||||||
gorm.io/driver/mysql v1.5.6
|
gorm.io/driver/mysql v1.5.6
|
||||||
gorm.io/gorm v1.25.9
|
gorm.io/gorm v1.25.10
|
||||||
)
|
)
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
|||||||
@@ -20,7 +20,4 @@ const (
|
|||||||
ResourceTypeDb int8 = 2
|
ResourceTypeDb int8 = 2
|
||||||
ResourceTypeRedis int8 = 3
|
ResourceTypeRedis int8 = 3
|
||||||
ResourceTypeMongo int8 = 4
|
ResourceTypeMongo int8 = 4
|
||||||
|
|
||||||
// 删除机器的事件主题名
|
|
||||||
DeleteMachineEventTopic = "machine:delete"
|
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -12,11 +12,13 @@ import (
|
|||||||
"mayfly-go/internal/db/config"
|
"mayfly-go/internal/db/config"
|
||||||
"mayfly-go/internal/db/dbm/dbi"
|
"mayfly-go/internal/db/dbm/dbi"
|
||||||
"mayfly-go/internal/db/domain/entity"
|
"mayfly-go/internal/db/domain/entity"
|
||||||
|
"mayfly-go/internal/event"
|
||||||
msgapp "mayfly-go/internal/msg/application"
|
msgapp "mayfly-go/internal/msg/application"
|
||||||
msgdto "mayfly-go/internal/msg/application/dto"
|
msgdto "mayfly-go/internal/msg/application/dto"
|
||||||
tagapp "mayfly-go/internal/tag/application"
|
tagapp "mayfly-go/internal/tag/application"
|
||||||
tagentity "mayfly-go/internal/tag/domain/entity"
|
tagentity "mayfly-go/internal/tag/domain/entity"
|
||||||
"mayfly-go/pkg/biz"
|
"mayfly-go/pkg/biz"
|
||||||
|
"mayfly-go/pkg/global"
|
||||||
"mayfly-go/pkg/logx"
|
"mayfly-go/pkg/logx"
|
||||||
"mayfly-go/pkg/model"
|
"mayfly-go/pkg/model"
|
||||||
"mayfly-go/pkg/req"
|
"mayfly-go/pkg/req"
|
||||||
@@ -97,6 +99,8 @@ func (d *Db) ExecSql(rc *req.Ctx) {
|
|||||||
biz.ErrIsNil(err)
|
biz.ErrIsNil(err)
|
||||||
biz.ErrIsNilAppendErr(d.TagApp.CanAccess(rc.GetLoginAccount().Id, dbConn.Info.TagPath...), "%s")
|
biz.ErrIsNilAppendErr(d.TagApp.CanAccess(rc.GetLoginAccount().Id, dbConn.Info.TagPath...), "%s")
|
||||||
|
|
||||||
|
global.EventBus.Publish(rc.MetaCtx, event.EventTopicResourceOp, dbConn.Info.TagPath[0])
|
||||||
|
|
||||||
sqlBytes, err := base64.StdEncoding.DecodeString(form.Sql)
|
sqlBytes, err := base64.StdEncoding.DecodeString(form.Sql)
|
||||||
biz.ErrIsNilAppendErr(err, "sql解码失败: %s")
|
biz.ErrIsNilAppendErr(err, "sql解码失败: %s")
|
||||||
// 去除前后空格及换行符
|
// 去除前后空格及换行符
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ type InstanceDbNamesForm struct {
|
|||||||
Host string `binding:"required" json:"host"`
|
Host string `binding:"required" json:"host"`
|
||||||
Port int `json:"port"`
|
Port int `json:"port"`
|
||||||
Params string `json:"params"`
|
Params string `json:"params"`
|
||||||
|
Extra string `json:"extra"`
|
||||||
SshTunnelMachineId int `json:"sshTunnelMachineId"`
|
SshTunnelMachineId int `json:"sshTunnelMachineId"`
|
||||||
AuthCert *tagentity.ResourceAuthCert `json:"authCert" binding:"required"` // 资产授权凭证信息
|
AuthCert *tagentity.ResourceAuthCert `json:"authCert" binding:"required"` // 资产授权凭证信息
|
||||||
}
|
}
|
||||||
|
|||||||
6
server/internal/event/topic.go
Normal file
6
server/internal/event/topic.go
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
package event
|
||||||
|
|
||||||
|
const (
|
||||||
|
EventTopicDeleteMachine = "machine:delete" // 删除机器的事件主题名
|
||||||
|
EventTopicResourceOp = "resource:op" // 资源操作主题
|
||||||
|
)
|
||||||
@@ -44,3 +44,14 @@ type MachineCronJobForm struct {
|
|||||||
MachineIds []uint64 `json:"machineIds"`
|
MachineIds []uint64 `json:"machineIds"`
|
||||||
Remark string `json:"remark"`
|
Remark string `json:"remark"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MachineCmdConfForm struct {
|
||||||
|
Id uint64 `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
Cmds []string `json:"cmds"` // 命令配置
|
||||||
|
Status int8 `json:"execCmds"` // 状态
|
||||||
|
Stratege string `json:"stratege"` // 策略,空禁用
|
||||||
|
Remark string `json:"remark"` // 备注
|
||||||
|
|
||||||
|
CodePaths []string `json:"codePaths"`
|
||||||
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"fmt"
|
"fmt"
|
||||||
"mayfly-go/internal/common/consts"
|
"mayfly-go/internal/common/consts"
|
||||||
|
"mayfly-go/internal/event"
|
||||||
"mayfly-go/internal/machine/api/form"
|
"mayfly-go/internal/machine/api/form"
|
||||||
"mayfly-go/internal/machine/api/vo"
|
"mayfly-go/internal/machine/api/vo"
|
||||||
"mayfly-go/internal/machine/application"
|
"mayfly-go/internal/machine/application"
|
||||||
@@ -15,6 +16,7 @@ import (
|
|||||||
tagentity "mayfly-go/internal/tag/domain/entity"
|
tagentity "mayfly-go/internal/tag/domain/entity"
|
||||||
"mayfly-go/pkg/biz"
|
"mayfly-go/pkg/biz"
|
||||||
"mayfly-go/pkg/errorx"
|
"mayfly-go/pkg/errorx"
|
||||||
|
"mayfly-go/pkg/global"
|
||||||
"mayfly-go/pkg/logx"
|
"mayfly-go/pkg/logx"
|
||||||
"mayfly-go/pkg/model"
|
"mayfly-go/pkg/model"
|
||||||
"mayfly-go/pkg/req"
|
"mayfly-go/pkg/req"
|
||||||
@@ -192,7 +194,9 @@ func (m *Machine) WsSSH(g *gin.Context) {
|
|||||||
cli, err := m.MachineApp.NewCli(GetMachineAc(rc))
|
cli, err := m.MachineApp.NewCli(GetMachineAc(rc))
|
||||||
biz.ErrIsNilAppendErr(err, mcm.GetErrorContentRn("获取客户端连接失败: %s"))
|
biz.ErrIsNilAppendErr(err, mcm.GetErrorContentRn("获取客户端连接失败: %s"))
|
||||||
defer cli.Close()
|
defer cli.Close()
|
||||||
biz.ErrIsNilAppendErr(m.TagApp.CanAccess(rc.GetLoginAccount().Id, cli.Info.TagPath...), "%s")
|
biz.ErrIsNilAppendErr(m.TagApp.CanAccess(rc.GetLoginAccount().Id, cli.Info.TagPath...), mcm.GetErrorContentRn("%s"))
|
||||||
|
|
||||||
|
global.EventBus.Publish(rc.MetaCtx, event.EventTopicResourceOp, cli.Info.TagPath[0])
|
||||||
|
|
||||||
cols := rc.QueryIntDefault("cols", 80)
|
cols := rc.QueryIntDefault("cols", 80)
|
||||||
rows := rc.QueryIntDefault("rows", 32)
|
rows := rc.QueryIntDefault("rows", 32)
|
||||||
|
|||||||
49
server/internal/machine/api/machine_cmd_conf.go
Normal file
49
server/internal/machine/api/machine_cmd_conf.go
Normal file
@@ -0,0 +1,49 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"mayfly-go/internal/machine/api/form"
|
||||||
|
"mayfly-go/internal/machine/api/vo"
|
||||||
|
"mayfly-go/internal/machine/application"
|
||||||
|
"mayfly-go/internal/machine/domain/entity"
|
||||||
|
|
||||||
|
tagapp "mayfly-go/internal/tag/application"
|
||||||
|
tagentity "mayfly-go/internal/tag/domain/entity"
|
||||||
|
"mayfly-go/pkg/biz"
|
||||||
|
"mayfly-go/pkg/req"
|
||||||
|
"mayfly-go/pkg/utils/collx"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MachineCmdConf struct {
|
||||||
|
MachineCmdConfApp application.MachineCmdConf `inject:""`
|
||||||
|
TagTreeRelateApp tagapp.TagTreeRelate `inject:"TagTreeRelateApp"`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MachineCmdConf) MachineCmdConfs(rc *req.Ctx) {
|
||||||
|
cond := req.BindQuery(rc, new(entity.MachineCmdConf))
|
||||||
|
|
||||||
|
var vos []*vo.MachineCmdConfVO
|
||||||
|
err := m.MachineCmdConfApp.ListByCond(cond, &vos)
|
||||||
|
biz.ErrIsNil(err)
|
||||||
|
|
||||||
|
m.TagTreeRelateApp.FillTagInfo(tagentity.TagRelateTypeMachineCmd, collx.ArrayMap(vos, func(mvo *vo.MachineCmdConfVO) tagentity.IRelateTag {
|
||||||
|
return mvo
|
||||||
|
})...)
|
||||||
|
|
||||||
|
rc.ResData = vos
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MachineCmdConf) Save(rc *req.Ctx) {
|
||||||
|
cmdForm := new(form.MachineCmdConfForm)
|
||||||
|
mcj := req.BindJsonAndCopyTo[*entity.MachineCmdConf](rc, cmdForm, new(entity.MachineCmdConf))
|
||||||
|
rc.ReqParam = cmdForm
|
||||||
|
|
||||||
|
err := m.MachineCmdConfApp.SaveCmdConf(rc.MetaCtx, &application.SaveMachineCmdConfParam{
|
||||||
|
CmdConf: mcj,
|
||||||
|
CodePaths: cmdForm.CodePaths,
|
||||||
|
})
|
||||||
|
biz.ErrIsNil(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *MachineCmdConf) Delete(rc *req.Ctx) {
|
||||||
|
m.MachineCmdConfApp.DeleteCmdConf(rc.MetaCtx, uint64(rc.PathParamInt("id")))
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ package vo
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
tagentity "mayfly-go/internal/tag/domain/entity"
|
tagentity "mayfly-go/internal/tag/domain/entity"
|
||||||
|
"mayfly-go/pkg/model"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -85,3 +86,18 @@ func (s MachineFileInfos) Less(i, j int) bool {
|
|||||||
}
|
}
|
||||||
return s[i].Name < s[j].Name
|
return s[i].Name < s[j].Name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MachineCmdConfVO struct {
|
||||||
|
tagentity.RelateTags // 标签信息
|
||||||
|
model.Model
|
||||||
|
|
||||||
|
Name string `json:"name"`
|
||||||
|
Cmds model.Slice[string] `json:"cmds"` // 命令配置
|
||||||
|
Status int8 `json:"execCmds"` // 状态
|
||||||
|
Stratege string `json:"stratege"` // 策略,空禁用
|
||||||
|
Remark string `json:"remark"` // 备注
|
||||||
|
}
|
||||||
|
|
||||||
|
func (mcc *MachineCmdConfVO) GetRelateId() uint64 {
|
||||||
|
return mcc.Id
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ func InitIoc() {
|
|||||||
ioc.Register(new(machineScriptAppImpl), ioc.WithComponentName("MachineScriptApp"))
|
ioc.Register(new(machineScriptAppImpl), ioc.WithComponentName("MachineScriptApp"))
|
||||||
ioc.Register(new(machineCronJobAppImpl), ioc.WithComponentName("MachineCronJobApp"))
|
ioc.Register(new(machineCronJobAppImpl), ioc.WithComponentName("MachineCronJobApp"))
|
||||||
ioc.Register(new(machineTermOpAppImpl), ioc.WithComponentName("MachineTermOpApp"))
|
ioc.Register(new(machineTermOpAppImpl), ioc.WithComponentName("MachineTermOpApp"))
|
||||||
|
ioc.Register(new(machineCmdConfAppImpl), ioc.WithComponentName("MachineCmdConfApp"))
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetMachineApp() Machine {
|
func GetMachineApp() Machine {
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package application
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"mayfly-go/internal/common/consts"
|
"mayfly-go/internal/event"
|
||||||
"mayfly-go/internal/machine/api/vo"
|
"mayfly-go/internal/machine/api/vo"
|
||||||
"mayfly-go/internal/machine/domain/entity"
|
"mayfly-go/internal/machine/domain/entity"
|
||||||
"mayfly-go/internal/machine/domain/repository"
|
"mayfly-go/internal/machine/domain/repository"
|
||||||
@@ -210,7 +210,7 @@ func (m *machineAppImpl) Delete(ctx context.Context, id uint64) error {
|
|||||||
mcm.DeleteCli(id)
|
mcm.DeleteCli(id)
|
||||||
|
|
||||||
// 发布机器删除事件
|
// 发布机器删除事件
|
||||||
global.EventBus.Publish(ctx, consts.DeleteMachineEventTopic, machine)
|
global.EventBus.Publish(ctx, event.EventTopicDeleteMachine, machine)
|
||||||
|
|
||||||
resourceType := tagentity.TagTypeMachine
|
resourceType := tagentity.TagTypeMachine
|
||||||
return m.Tx(ctx,
|
return m.Tx(ctx,
|
||||||
|
|||||||
98
server/internal/machine/application/machine_cmd_conf.go
Normal file
98
server/internal/machine/application/machine_cmd_conf.go
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
package application
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"mayfly-go/internal/machine/domain/entity"
|
||||||
|
"mayfly-go/internal/machine/domain/repository"
|
||||||
|
tagapp "mayfly-go/internal/tag/application"
|
||||||
|
tagentity "mayfly-go/internal/tag/domain/entity"
|
||||||
|
"mayfly-go/pkg/base"
|
||||||
|
"mayfly-go/pkg/errorx"
|
||||||
|
"mayfly-go/pkg/logx"
|
||||||
|
"regexp"
|
||||||
|
)
|
||||||
|
|
||||||
|
type SaveMachineCmdConfParam struct {
|
||||||
|
CmdConf *entity.MachineCmdConf
|
||||||
|
CodePaths []string
|
||||||
|
}
|
||||||
|
|
||||||
|
type MachineCmd struct {
|
||||||
|
CmdRegexp *regexp.Regexp // 命令正则表达式
|
||||||
|
Stratege string // 策略(拒绝或审批等)
|
||||||
|
}
|
||||||
|
|
||||||
|
type MachineCmdConf interface {
|
||||||
|
base.App[*entity.MachineCmdConf]
|
||||||
|
|
||||||
|
SaveCmdConf(ctx context.Context, cmdConf *SaveMachineCmdConfParam) error
|
||||||
|
|
||||||
|
DeleteCmdConf(ctx context.Context, id uint64) error
|
||||||
|
|
||||||
|
GetCmdConfsByMachineTags(tagPaths ...string) []*MachineCmd
|
||||||
|
}
|
||||||
|
|
||||||
|
type machineCmdConfAppImpl struct {
|
||||||
|
base.AppImpl[*entity.MachineCmdConf, repository.MachineCmdConf]
|
||||||
|
|
||||||
|
tagTreeRelateApp tagapp.TagTreeRelate `inject:"TagTreeRelateApp"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ (MachineCmdConf) = (*machineCmdConfAppImpl)(nil)
|
||||||
|
|
||||||
|
// 注入MachineCmdConfRepo
|
||||||
|
func (m *machineCmdConfAppImpl) InjectMachineCmdConfRepo(repo repository.MachineCmdConf) {
|
||||||
|
m.Repo = repo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *machineCmdConfAppImpl) SaveCmdConf(ctx context.Context, cmdConfParam *SaveMachineCmdConfParam) error {
|
||||||
|
cmdConf := cmdConfParam.CmdConf
|
||||||
|
|
||||||
|
return m.Tx(ctx, func(ctx context.Context) error {
|
||||||
|
return m.Save(ctx, cmdConf)
|
||||||
|
}, func(ctx context.Context) error {
|
||||||
|
return m.tagTreeRelateApp.RelateTag(ctx, tagentity.TagRelateTypeMachineCmd, cmdConf.Id, cmdConfParam.CodePaths...)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *machineCmdConfAppImpl) DeleteCmdConf(ctx context.Context, id uint64) error {
|
||||||
|
_, err := m.GetById(new(entity.MachineCmdConf), id)
|
||||||
|
if err != nil {
|
||||||
|
return errorx.NewBiz("该命令配置不存在")
|
||||||
|
}
|
||||||
|
|
||||||
|
return m.Tx(ctx, func(ctx context.Context) error {
|
||||||
|
return m.DeleteById(ctx, id)
|
||||||
|
}, func(ctx context.Context) error {
|
||||||
|
return m.tagTreeRelateApp.DeleteByCond(ctx, &tagentity.TagTreeRelate{
|
||||||
|
RelateType: tagentity.TagRelateTypeMachineCmd,
|
||||||
|
RelateId: id,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (m *machineCmdConfAppImpl) GetCmdConfsByMachineTags(tagPaths ...string) []*MachineCmd {
|
||||||
|
var cmds []*MachineCmd
|
||||||
|
cmdConfIds, err := m.tagTreeRelateApp.GetRelateIds(tagentity.TagRelateTypeMachineCmd, tagPaths...)
|
||||||
|
if err != nil {
|
||||||
|
logx.Errorf("获取命令配置信息失败: %s", err.Error())
|
||||||
|
return cmds
|
||||||
|
}
|
||||||
|
if len(cmdConfIds) == 0 {
|
||||||
|
return cmds
|
||||||
|
}
|
||||||
|
|
||||||
|
var cmdConfs []*entity.MachineCmdConf
|
||||||
|
m.GetByIdIn(&cmdConfs, cmdConfIds)
|
||||||
|
|
||||||
|
for _, cmdConf := range cmdConfs {
|
||||||
|
for _, cmd := range cmdConf.Cmds {
|
||||||
|
if p, err := regexp.Compile(cmd); err != nil {
|
||||||
|
logx.Errorf("命令配置[%s],正则编译失败", cmd)
|
||||||
|
} else {
|
||||||
|
cmds = append(cmds, &MachineCmd{CmdRegexp: p})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return cmds
|
||||||
|
}
|
||||||
@@ -36,6 +36,8 @@ type MachineTermOp interface {
|
|||||||
|
|
||||||
type machineTermOpAppImpl struct {
|
type machineTermOpAppImpl struct {
|
||||||
base.AppImpl[*entity.MachineTermOp, repository.MachineTermOp]
|
base.AppImpl[*entity.MachineTermOp, repository.MachineTermOp]
|
||||||
|
|
||||||
|
machineCmdConfApp MachineCmdConf `inject:"MachineCmdConfApp"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// 注入MachineTermOpRepo
|
// 注入MachineTermOpRepo
|
||||||
@@ -87,12 +89,17 @@ func (m *machineTermOpAppImpl) TermConn(ctx context.Context, cli *mcm.Cli, wsCon
|
|||||||
LogCmd: cli.Info.EnableRecorder == 1,
|
LogCmd: cli.Info.EnableRecorder == 1,
|
||||||
}
|
}
|
||||||
|
|
||||||
// createTsParam.CmdFilterFuncs = []mcm.CmdFilterFunc{func(cmd string) error {
|
cmdConfs := m.machineCmdConfApp.GetCmdConfsByMachineTags(cli.Info.TagPath...)
|
||||||
// if strings.HasPrefix(cmd, "rm") {
|
if len(cmdConfs) > 0 {
|
||||||
// return errorx.NewBiz("该命令已被禁用...")
|
createTsParam.CmdFilterFuncs = []mcm.CmdFilterFunc{func(cmd string) error {
|
||||||
// }
|
for _, cmdConf := range cmdConfs {
|
||||||
// return nil
|
if cmdConf.CmdRegexp.Match([]byte(cmd)) {
|
||||||
// }}
|
return errorx.NewBiz("该命令已被禁用...")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
mts, err := mcm.NewTerminalSession(createTsParam)
|
mts, err := mcm.NewTerminalSession(createTsParam)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
16
server/internal/machine/domain/entity/machine_cmd_conf.go
Normal file
16
server/internal/machine/domain/entity/machine_cmd_conf.go
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
package entity
|
||||||
|
|
||||||
|
import (
|
||||||
|
"mayfly-go/pkg/model"
|
||||||
|
)
|
||||||
|
|
||||||
|
// 机器命令过滤配置
|
||||||
|
type MachineCmdConf struct {
|
||||||
|
model.Model
|
||||||
|
|
||||||
|
Name string `json:"name"`
|
||||||
|
Cmds model.Slice[string] `json:"cmds"` // 命令配置
|
||||||
|
Status int8 `json:"execCmds"` // 状态
|
||||||
|
Stratege string `json:"stratege"` // 策略,空禁用
|
||||||
|
Remark string `json:"remark"` // 备注
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"mayfly-go/internal/machine/domain/entity"
|
||||||
|
"mayfly-go/pkg/base"
|
||||||
|
)
|
||||||
|
|
||||||
|
type MachineCmdConf interface {
|
||||||
|
base.Repo[*entity.MachineCmdConf]
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package persistence
|
||||||
|
|
||||||
|
import (
|
||||||
|
"mayfly-go/internal/machine/domain/entity"
|
||||||
|
"mayfly-go/internal/machine/domain/repository"
|
||||||
|
"mayfly-go/pkg/base"
|
||||||
|
)
|
||||||
|
|
||||||
|
type machineCmdConfRepoImpl struct {
|
||||||
|
base.RepoImpl[*entity.MachineCmdConf]
|
||||||
|
}
|
||||||
|
|
||||||
|
func newMachineCmdConfRepo() repository.MachineCmdConf {
|
||||||
|
return &machineCmdConfRepoImpl{base.RepoImpl[*entity.MachineCmdConf]{M: new(entity.MachineCmdConf)}}
|
||||||
|
}
|
||||||
@@ -12,4 +12,5 @@ func InitIoc() {
|
|||||||
ioc.Register(newMachineCronJobExecRepo(), ioc.WithComponentName("MachineCronJobExecRepo"))
|
ioc.Register(newMachineCronJobExecRepo(), ioc.WithComponentName("MachineCronJobExecRepo"))
|
||||||
ioc.Register(newMachineCronJobRelateRepo(), ioc.WithComponentName("MachineCronJobRelateRepo"))
|
ioc.Register(newMachineCronJobRelateRepo(), ioc.WithComponentName("MachineCronJobRelateRepo"))
|
||||||
ioc.Register(newMachineTermOpRepoImpl(), ioc.WithComponentName("MachineTermOpRepo"))
|
ioc.Register(newMachineTermOpRepoImpl(), ioc.WithComponentName("MachineTermOpRepo"))
|
||||||
|
ioc.Register(newMachineCmdConfRepo(), ioc.WithComponentName("MachineCmdConfRepo"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ package init
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"mayfly-go/initialize"
|
"mayfly-go/initialize"
|
||||||
"mayfly-go/internal/common/consts"
|
"mayfly-go/internal/event"
|
||||||
"mayfly-go/internal/machine/application"
|
"mayfly-go/internal/machine/application"
|
||||||
"mayfly-go/internal/machine/domain/entity"
|
"mayfly-go/internal/machine/domain/entity"
|
||||||
"mayfly-go/internal/machine/infrastructure/persistence"
|
"mayfly-go/internal/machine/infrastructure/persistence"
|
||||||
@@ -28,17 +28,17 @@ func Init() {
|
|||||||
|
|
||||||
application.GetMachineTermOpApp().TimerDeleteTermOp()
|
application.GetMachineTermOpApp().TimerDeleteTermOp()
|
||||||
|
|
||||||
global.EventBus.Subscribe(consts.DeleteMachineEventTopic, "machineFile", func(ctx context.Context, event *eventbus.Event) error {
|
global.EventBus.Subscribe(event.EventTopicDeleteMachine, "machineFile", func(ctx context.Context, event *eventbus.Event) error {
|
||||||
me := event.Val.(*entity.Machine)
|
me := event.Val.(*entity.Machine)
|
||||||
return application.GetMachineFileApp().DeleteByCond(ctx, &entity.MachineFile{MachineId: me.Id})
|
return application.GetMachineFileApp().DeleteByCond(ctx, &entity.MachineFile{MachineId: me.Id})
|
||||||
})
|
})
|
||||||
|
|
||||||
global.EventBus.Subscribe(consts.DeleteMachineEventTopic, "machineScript", func(ctx context.Context, event *eventbus.Event) error {
|
global.EventBus.Subscribe(event.EventTopicDeleteMachine, "machineScript", func(ctx context.Context, event *eventbus.Event) error {
|
||||||
me := event.Val.(*entity.Machine)
|
me := event.Val.(*entity.Machine)
|
||||||
return application.GetMachineScriptApp().DeleteByCond(ctx, &entity.MachineScript{MachineId: me.Id})
|
return application.GetMachineScriptApp().DeleteByCond(ctx, &entity.MachineScript{MachineId: me.Id})
|
||||||
})
|
})
|
||||||
|
|
||||||
global.EventBus.Subscribe(consts.DeleteMachineEventTopic, "machineCronJob", func(ctx context.Context, event *eventbus.Event) error {
|
global.EventBus.Subscribe(event.EventTopicDeleteMachine, "machineCronJob", func(ctx context.Context, event *eventbus.Event) error {
|
||||||
me := event.Val.(*entity.Machine)
|
me := event.Val.(*entity.Machine)
|
||||||
var jobIds []uint64
|
var jobIds []uint64
|
||||||
application.GetMachineCronJobApp().MachineRelateCronJobs(ctx, me.Id, jobIds)
|
application.GetMachineCronJobApp().MachineRelateCronJobs(ctx, me.Id, jobIds)
|
||||||
|
|||||||
27
server/internal/machine/router/machine_cmd_conf.go
Normal file
27
server/internal/machine/router/machine_cmd_conf.go
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
package router
|
||||||
|
|
||||||
|
import (
|
||||||
|
"mayfly-go/internal/machine/api"
|
||||||
|
"mayfly-go/pkg/biz"
|
||||||
|
"mayfly-go/pkg/ioc"
|
||||||
|
"mayfly-go/pkg/req"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func InitMachineCmdConfRouter(router *gin.RouterGroup) {
|
||||||
|
mccs := router.Group("machine/security/cmd-confs")
|
||||||
|
|
||||||
|
mcc := new(api.MachineCmdConf)
|
||||||
|
biz.ErrIsNil(ioc.Inject(mcc))
|
||||||
|
|
||||||
|
reqs := [...]*req.Conf{
|
||||||
|
req.NewGet("", mcc.MachineCmdConfs),
|
||||||
|
|
||||||
|
req.NewPost("", mcc.Save).Log(req.NewLogSave("机器命令配置-保存")).RequiredPermissionCode("cmdconf:save"),
|
||||||
|
|
||||||
|
req.NewDelete(":id", mcc.Delete).Log(req.NewLogSave("机器命令配置-删除")).RequiredPermissionCode("cmdconf:del"),
|
||||||
|
}
|
||||||
|
|
||||||
|
req.BatchSetGroup(mccs, reqs[:])
|
||||||
|
}
|
||||||
@@ -7,4 +7,5 @@ func Init(router *gin.RouterGroup) {
|
|||||||
InitMachineFileRouter(router)
|
InitMachineFileRouter(router)
|
||||||
InitMachineScriptRouter(router)
|
InitMachineScriptRouter(router)
|
||||||
InitMachineCronJobRouter(router)
|
InitMachineCronJobRouter(router)
|
||||||
|
InitMachineCmdConfRouter(router)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package api
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"mayfly-go/internal/common/consts"
|
"mayfly-go/internal/common/consts"
|
||||||
|
"mayfly-go/internal/event"
|
||||||
"mayfly-go/internal/mongo/api/form"
|
"mayfly-go/internal/mongo/api/form"
|
||||||
"mayfly-go/internal/mongo/api/vo"
|
"mayfly-go/internal/mongo/api/vo"
|
||||||
"mayfly-go/internal/mongo/application"
|
"mayfly-go/internal/mongo/application"
|
||||||
@@ -10,6 +11,7 @@ import (
|
|||||||
tagapp "mayfly-go/internal/tag/application"
|
tagapp "mayfly-go/internal/tag/application"
|
||||||
tagentity "mayfly-go/internal/tag/domain/entity"
|
tagentity "mayfly-go/internal/tag/domain/entity"
|
||||||
"mayfly-go/pkg/biz"
|
"mayfly-go/pkg/biz"
|
||||||
|
"mayfly-go/pkg/global"
|
||||||
"mayfly-go/pkg/model"
|
"mayfly-go/pkg/model"
|
||||||
"mayfly-go/pkg/req"
|
"mayfly-go/pkg/req"
|
||||||
"mayfly-go/pkg/utils/collx"
|
"mayfly-go/pkg/utils/collx"
|
||||||
@@ -93,6 +95,9 @@ func (m *Mongo) Databases(rc *req.Ctx) {
|
|||||||
func (m *Mongo) Collections(rc *req.Ctx) {
|
func (m *Mongo) Collections(rc *req.Ctx) {
|
||||||
conn, err := m.MongoApp.GetMongoConn(m.GetMongoId(rc))
|
conn, err := m.MongoApp.GetMongoConn(m.GetMongoId(rc))
|
||||||
biz.ErrIsNil(err)
|
biz.ErrIsNil(err)
|
||||||
|
|
||||||
|
global.EventBus.Publish(rc.MetaCtx, event.EventTopicResourceOp, conn.Info.TagPath[0])
|
||||||
|
|
||||||
db := rc.Query("database")
|
db := rc.Query("database")
|
||||||
biz.NotEmpty(db, "database不能为空")
|
biz.NotEmpty(db, "database不能为空")
|
||||||
ctx := context.TODO()
|
ctx := context.TODO()
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"mayfly-go/internal/event"
|
||||||
"mayfly-go/internal/redis/api/form"
|
"mayfly-go/internal/redis/api/form"
|
||||||
"mayfly-go/internal/redis/application"
|
"mayfly-go/internal/redis/application"
|
||||||
"mayfly-go/pkg/biz"
|
"mayfly-go/pkg/biz"
|
||||||
|
"mayfly-go/pkg/global"
|
||||||
"mayfly-go/pkg/req"
|
"mayfly-go/pkg/req"
|
||||||
"mayfly-go/pkg/utils/collx"
|
"mayfly-go/pkg/utils/collx"
|
||||||
)
|
)
|
||||||
@@ -14,8 +16,11 @@ func (r *Redis) RunCmd(rc *req.Ctx) {
|
|||||||
biz.IsTrue(len(cmdReq.Cmd) > 0, "redis命令不能为空")
|
biz.IsTrue(len(cmdReq.Cmd) > 0, "redis命令不能为空")
|
||||||
|
|
||||||
redisConn := r.getRedisConn(rc)
|
redisConn := r.getRedisConn(rc)
|
||||||
|
biz.ErrIsNilAppendErr(r.TagApp.CanAccess(rc.GetLoginAccount().Id, redisConn.Info.TagPath...), "%s")
|
||||||
rc.ReqParam = collx.Kvs("redis", redisConn.Info, "cmd", cmdReq.Cmd)
|
rc.ReqParam = collx.Kvs("redis", redisConn.Info, "cmd", cmdReq.Cmd)
|
||||||
|
|
||||||
|
global.EventBus.Publish(rc.MetaCtx, event.EventTopicResourceOp, redisConn.Info.TagPath[0])
|
||||||
|
|
||||||
res, err := r.RedisApp.RunCmd(rc.MetaCtx, redisConn, runCmdParam)
|
res, err := r.RedisApp.RunCmd(rc.MetaCtx, redisConn, runCmdParam)
|
||||||
biz.ErrIsNil(err)
|
biz.ErrIsNil(err)
|
||||||
rc.ResData = res
|
rc.ResData = res
|
||||||
|
|||||||
24
server/internal/tag/api/resource_op_log.go
Normal file
24
server/internal/tag/api/resource_op_log.go
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package api
|
||||||
|
|
||||||
|
import (
|
||||||
|
"mayfly-go/internal/tag/application"
|
||||||
|
"mayfly-go/internal/tag/domain/entity"
|
||||||
|
"mayfly-go/pkg/biz"
|
||||||
|
"mayfly-go/pkg/req"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ResourceOpLog struct {
|
||||||
|
ResourceOpLogApp application.ResourceOpLog `inject:""`
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *ResourceOpLog) PageAccountOpLog(rc *req.Ctx) {
|
||||||
|
cond := new(entity.ResourceOpLog)
|
||||||
|
cond.ResourceCode = rc.Query("resourceCode")
|
||||||
|
cond.ResourceType = int8(rc.QueryInt("resourceType"))
|
||||||
|
cond.CreatorId = rc.GetLoginAccount().Id
|
||||||
|
|
||||||
|
var rols []*entity.ResourceOpLog
|
||||||
|
res, err := r.ResourceOpLogApp.PageQuery(cond, rc.GetPageParam(), &rols)
|
||||||
|
biz.ErrIsNil(err)
|
||||||
|
rc.ResData = res
|
||||||
|
}
|
||||||
@@ -15,7 +15,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type TagTree struct {
|
type TagTree struct {
|
||||||
TagTreeApp application.TagTree `inject:""`
|
TagTreeApp application.TagTree `inject:""`
|
||||||
|
TagTreeRelateApp application.TagTreeRelate `inject:""`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *TagTree) GetTagTree(rc *req.Ctx) {
|
func (p *TagTree) GetTagTree(rc *req.Ctx) {
|
||||||
@@ -123,3 +124,8 @@ func (p *TagTree) CountTagResource(rc *req.Ctx) {
|
|||||||
"mongo": len(p.TagTreeApp.GetAccountTagCodes(accountId, consts.ResourceTypeMongo, tagPath)),
|
"mongo": len(p.TagTreeApp.GetAccountTagCodes(accountId, consts.ResourceTypeMongo, tagPath)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 获取关联的标签id
|
||||||
|
func (p *TagTree) GetRelateTagIds(rc *req.Ctx) {
|
||||||
|
rc.ResData = p.TagTreeRelateApp.GetTagPathsByRelate(entity.TagRelateType(rc.PathParamInt("relateType")), uint64(rc.PathParamInt("relateId")))
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,22 +10,29 @@ import (
|
|||||||
"mayfly-go/internal/tag/domain/entity"
|
"mayfly-go/internal/tag/domain/entity"
|
||||||
"mayfly-go/pkg/biz"
|
"mayfly-go/pkg/biz"
|
||||||
"mayfly-go/pkg/req"
|
"mayfly-go/pkg/req"
|
||||||
|
"mayfly-go/pkg/utils/collx"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/may-fly/cast"
|
"github.com/may-fly/cast"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Team struct {
|
type Team struct {
|
||||||
TeamApp application.Team `inject:""`
|
TeamApp application.Team `inject:""`
|
||||||
TagTreeApp application.TagTree `inject:""`
|
TagTreeApp application.TagTree `inject:""`
|
||||||
AccountApp sys_applicaiton.Account `inject:""`
|
TagTreeRelateApp application.TagTreeRelate `inject:""`
|
||||||
|
AccountApp sys_applicaiton.Account `inject:""`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *Team) GetTeams(rc *req.Ctx) {
|
func (p *Team) GetTeams(rc *req.Ctx) {
|
||||||
queryCond, page := req.BindQueryAndPage(rc, new(entity.TeamQuery))
|
queryCond, page := req.BindQueryAndPage(rc, new(entity.TeamQuery))
|
||||||
teams := &[]entity.Team{}
|
var teams []*vo.Team
|
||||||
res, err := p.TeamApp.GetPageList(queryCond, page, teams)
|
res, err := p.TeamApp.GetPageList(queryCond, page, &teams)
|
||||||
biz.ErrIsNil(err)
|
biz.ErrIsNil(err)
|
||||||
|
|
||||||
|
p.TagTreeRelateApp.FillTagInfo(entity.TagRelateTypeTeam, collx.ArrayMap(teams, func(mvo *vo.Team) entity.IRelateTag {
|
||||||
|
return mvo
|
||||||
|
})...)
|
||||||
|
|
||||||
rc.ResData = res
|
rc.ResData = res
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -89,8 +96,3 @@ func (p *Team) DelTeamMember(rc *req.Ctx) {
|
|||||||
|
|
||||||
p.TeamApp.DeleteMember(rc.MetaCtx, uint64(tid), uint64(aid))
|
p.TeamApp.DeleteMember(rc.MetaCtx, uint64(tid), uint64(aid))
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取团队关联的标签id
|
|
||||||
func (p *Team) GetTagIds(rc *req.Ctx) {
|
|
||||||
rc.ResData = p.TeamApp.ListTagIds(uint64(rc.PathParamInt("id")))
|
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,6 +1,22 @@
|
|||||||
package vo
|
package vo
|
||||||
|
|
||||||
import "time"
|
import (
|
||||||
|
"mayfly-go/internal/tag/domain/entity"
|
||||||
|
"mayfly-go/pkg/model"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Team struct {
|
||||||
|
model.Model
|
||||||
|
entity.RelateTags // 标签信息
|
||||||
|
|
||||||
|
Name string `json:"name"` // 名称
|
||||||
|
Remark string `json:"remark"` // 备注说明
|
||||||
|
}
|
||||||
|
|
||||||
|
func (t *Team) GetRelateId() uint64 {
|
||||||
|
return t.Id
|
||||||
|
}
|
||||||
|
|
||||||
// 团队成员信息
|
// 团队成员信息
|
||||||
type TeamMember struct {
|
type TeamMember struct {
|
||||||
|
|||||||
@@ -8,4 +8,10 @@ func InitIoc() {
|
|||||||
ioc.Register(new(tagTreeAppImpl), ioc.WithComponentName("TagTreeApp"))
|
ioc.Register(new(tagTreeAppImpl), ioc.WithComponentName("TagTreeApp"))
|
||||||
ioc.Register(new(teamAppImpl), ioc.WithComponentName("TeamApp"))
|
ioc.Register(new(teamAppImpl), ioc.WithComponentName("TeamApp"))
|
||||||
ioc.Register(new(resourceAuthCertAppImpl), ioc.WithComponentName("ResourceAuthCertApp"))
|
ioc.Register(new(resourceAuthCertAppImpl), ioc.WithComponentName("ResourceAuthCertApp"))
|
||||||
|
ioc.Register(new(resourceOpLogAppImpl), ioc.WithComponentName("ResourceOpLogApp"))
|
||||||
|
ioc.Register(new(tagTreeRelateAppImpl), ioc.WithComponentName("TagTreeRelateApp"))
|
||||||
|
}
|
||||||
|
|
||||||
|
func GetResourceOpLogApp() ResourceOpLog {
|
||||||
|
return ioc.Get[ResourceOpLog]("ResourceOpLogApp")
|
||||||
}
|
}
|
||||||
|
|||||||
58
server/internal/tag/application/resource_op_log.go
Normal file
58
server/internal/tag/application/resource_op_log.go
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
package application
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"mayfly-go/internal/tag/domain/entity"
|
||||||
|
"mayfly-go/internal/tag/domain/repository"
|
||||||
|
"mayfly-go/pkg/base"
|
||||||
|
"mayfly-go/pkg/contextx"
|
||||||
|
"mayfly-go/pkg/errorx"
|
||||||
|
"mayfly-go/pkg/utils/collx"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ResourceOpLog interface {
|
||||||
|
base.App[*entity.ResourceOpLog]
|
||||||
|
|
||||||
|
// AddResourceOpLog 新增资源操作记录
|
||||||
|
AddResourceOpLog(ctx context.Context, codePath string) error
|
||||||
|
}
|
||||||
|
|
||||||
|
type resourceOpLogAppImpl struct {
|
||||||
|
base.AppImpl[*entity.ResourceOpLog, repository.ResourceOpLog]
|
||||||
|
|
||||||
|
tagTreeApp TagTree `inject:"TagTreeApp"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ (ResourceOpLog) = (*resourceOpLogAppImpl)(nil)
|
||||||
|
|
||||||
|
// 注入ResourceOpLogRepo
|
||||||
|
func (rol *resourceOpLogAppImpl) InjectResourceOpLogRepo(resourceOpLogRepo repository.ResourceOpLog) {
|
||||||
|
rol.Repo = resourceOpLogRepo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rol *resourceOpLogAppImpl) AddResourceOpLog(ctx context.Context, codePath string) error {
|
||||||
|
loginAccount := contextx.GetLoginAccount(ctx)
|
||||||
|
if loginAccount == nil {
|
||||||
|
return errorx.NewBiz("当前上下文不存在登录信息")
|
||||||
|
}
|
||||||
|
|
||||||
|
var logs []*entity.ResourceOpLog
|
||||||
|
if err := rol.ListByWheres(collx.Kvs("create_time > ?", time.Now().Add(-5*time.Minute), "creator_id = ?", loginAccount.Id, "code_path = ?", codePath), &logs); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
// 指定时间内多次操作则不记录
|
||||||
|
if len(logs) > 0 {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
tagTree := &entity.TagTree{CodePath: codePath}
|
||||||
|
if err := rol.tagTreeApp.GetBy(tagTree); err != nil {
|
||||||
|
return errorx.NewBiz("资源不存在")
|
||||||
|
}
|
||||||
|
|
||||||
|
return rol.Save(ctx, &entity.ResourceOpLog{
|
||||||
|
ResourceCode: tagTree.Code,
|
||||||
|
ResourceType: int8(tagTree.Type),
|
||||||
|
CodePath: tagTree.CodePath,
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -99,7 +99,7 @@ type TagTree interface {
|
|||||||
type tagTreeAppImpl struct {
|
type tagTreeAppImpl struct {
|
||||||
base.AppImpl[*entity.TagTree, repository.TagTree]
|
base.AppImpl[*entity.TagTree, repository.TagTree]
|
||||||
|
|
||||||
tagTreeTeamRepo repository.TagTreeTeam `inject:"TagTreeTeamRepo"`
|
tagTreeRelateApp TagTreeRelate `inject:"TagTreeRelateApp"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// 注入TagTreeRepo
|
// 注入TagTreeRepo
|
||||||
@@ -450,7 +450,7 @@ func (p *tagTreeAppImpl) ListTagPathByTypeAndCode(resourceType int8, resourceCod
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (p *tagTreeAppImpl) ListTagByAccountId(accountId uint64) []string {
|
func (p *tagTreeAppImpl) ListTagByAccountId(accountId uint64) []string {
|
||||||
return p.tagTreeTeamRepo.SelectTagPathsByAccountId(accountId)
|
return p.tagTreeRelateApp.GetTagPathsByAccountId(accountId)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *tagTreeAppImpl) CanAccess(accountId uint64, tagPath ...string) error {
|
func (p *tagTreeAppImpl) CanAccess(accountId uint64, tagPath ...string) error {
|
||||||
@@ -486,7 +486,7 @@ func (p *tagTreeAppImpl) FillTagInfo(resourceTagType entity.TagType, resources .
|
|||||||
|
|
||||||
for _, tr := range tagResources {
|
for _, tr := range tagResources {
|
||||||
// 赋值标签信息
|
// 赋值标签信息
|
||||||
resourceCode2Resouce[tr.Code].SetTagInfo(entity.ResourceTag{CodePath: tr.GetTagPath()})
|
resourceCode2Resouce[tr.Code].SetTagInfo(entity.ResourceTag{TagId: tr.Id, CodePath: tr.GetTagPath()})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -552,6 +552,8 @@ func (p *tagTreeAppImpl) deleteByIds(ctx context.Context, tagIds []uint64) error
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// 删除team关联的标签
|
// 删除与标签有关联信息的记录(如团队关联的标签等)
|
||||||
return p.tagTreeTeamRepo.DeleteByWheres(ctx, collx.M{"tag_id in ?": tagIds})
|
return p.tagTreeRelateApp.DeleteByWheres(ctx, collx.M{
|
||||||
|
"tag_id in ?": tagIds,
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|||||||
130
server/internal/tag/application/tag_tree_relate.go
Normal file
130
server/internal/tag/application/tag_tree_relate.go
Normal file
@@ -0,0 +1,130 @@
|
|||||||
|
package application
|
||||||
|
|
||||||
|
import (
|
||||||
|
"context"
|
||||||
|
"mayfly-go/internal/tag/domain/entity"
|
||||||
|
"mayfly-go/internal/tag/domain/repository"
|
||||||
|
"mayfly-go/pkg/base"
|
||||||
|
"mayfly-go/pkg/errorx"
|
||||||
|
"mayfly-go/pkg/utils/collx"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TagTreeRelate interface {
|
||||||
|
base.App[*entity.TagTreeRelate]
|
||||||
|
|
||||||
|
// RelateTag 关联标签
|
||||||
|
RelateTag(ctx context.Context, relateType entity.TagRelateType, relateId uint64, tagCodePaths ...string) error
|
||||||
|
|
||||||
|
// GetRelateIds 根据标签路径获取对应关联的id
|
||||||
|
GetRelateIds(relateType entity.TagRelateType, tagPaths ...string) ([]uint64, error)
|
||||||
|
|
||||||
|
// GetTagPathsByAccountId 根据账号id获取该账号可操作的标签code路径
|
||||||
|
GetTagPathsByAccountId(accountId uint64) []string
|
||||||
|
|
||||||
|
// GetTagPathsByRelate 根据关联信息获取关联的标签codePaths
|
||||||
|
GetTagPathsByRelate(relateType entity.TagRelateType, relateId uint64) []string
|
||||||
|
|
||||||
|
// FillTagInfo 填充关联的标签信息
|
||||||
|
FillTagInfo(relateType entity.TagRelateType, relates ...entity.IRelateTag)
|
||||||
|
}
|
||||||
|
|
||||||
|
type tagTreeRelateAppImpl struct {
|
||||||
|
base.AppImpl[*entity.TagTreeRelate, repository.TagTreeRelate]
|
||||||
|
|
||||||
|
tagTreeRelateRepo repository.TagTreeRelate `inject:"TagTreeRelateRepo"`
|
||||||
|
|
||||||
|
tagTreeApp TagTree `inject:"TagTreeApp"`
|
||||||
|
}
|
||||||
|
|
||||||
|
var _ (TagTreeRelate) = (*tagTreeRelateAppImpl)(nil)
|
||||||
|
|
||||||
|
// 注入TagTreeRelateRepo
|
||||||
|
func (p *tagTreeRelateAppImpl) InjectTagTreeRelateRepo(tagTreeRelateRepo repository.TagTreeRelate) {
|
||||||
|
p.Repo = tagTreeRelateRepo
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tr *tagTreeRelateAppImpl) RelateTag(ctx context.Context, relateType entity.TagRelateType, relateId uint64, tagCodePaths ...string) error {
|
||||||
|
var tags []*entity.TagTree
|
||||||
|
tr.tagTreeApp.ListByQuery(&entity.TagTreeQuery{CodePaths: tagCodePaths}, &tags)
|
||||||
|
if len(tags) != len(tagCodePaths) {
|
||||||
|
return errorx.NewBiz("存在错误标签路径")
|
||||||
|
}
|
||||||
|
|
||||||
|
var oldRelates []*entity.TagTreeRelate
|
||||||
|
tr.ListByCond(&entity.TagTreeRelate{RelateType: relateType, RelateId: relateId}, &oldRelates)
|
||||||
|
oldTagIds := collx.ArrayMap[*entity.TagTreeRelate, uint64](oldRelates, func(val *entity.TagTreeRelate) uint64 {
|
||||||
|
return val.TagId
|
||||||
|
})
|
||||||
|
newTagIds := collx.ArrayMap[*entity.TagTree, uint64](tags, func(val *entity.TagTree) uint64 {
|
||||||
|
return val.Id
|
||||||
|
})
|
||||||
|
addTagIds, delTagIds, _ := collx.ArrayCompare(newTagIds, oldTagIds)
|
||||||
|
|
||||||
|
if len(addTagIds) > 0 {
|
||||||
|
trs := make([]*entity.TagTreeRelate, 0)
|
||||||
|
for _, tagId := range addTagIds {
|
||||||
|
trs = append(trs, &entity.TagTreeRelate{
|
||||||
|
TagId: tagId,
|
||||||
|
RelateType: relateType,
|
||||||
|
RelateId: relateId,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if err := tr.BatchInsert(ctx, trs); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if len(delTagIds) > 0 {
|
||||||
|
if err := tr.DeleteByWheres(ctx, collx.Kvs("relate_type=?", relateType, "relate_id=?", relateId, "tag_id in ?", delTagIds)); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tr *tagTreeRelateAppImpl) GetRelateIds(relateType entity.TagRelateType, tagPaths ...string) ([]uint64, error) {
|
||||||
|
poisibleTagPaths := make([]string, 0)
|
||||||
|
for _, tagPath := range tagPaths {
|
||||||
|
// 追加可能关联的标签路径,如tagPath = tag1/tag2/1|xxx/,需要获取所有关联的自身及父标签(tag1/ tag1/tag2/ tag1/tag2/1|xxx)
|
||||||
|
poisibleTagPaths = append(poisibleTagPaths, entity.GetAllCodePath(tagPath)...)
|
||||||
|
}
|
||||||
|
return tr.tagTreeRelateRepo.SelectRelateIdsByTagPaths(relateType, poisibleTagPaths...)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tr *tagTreeRelateAppImpl) GetTagPathsByAccountId(accountId uint64) []string {
|
||||||
|
return tr.tagTreeRelateRepo.SelectTagPathsByAccountId(accountId)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tr *tagTreeRelateAppImpl) GetTagPathsByRelate(relateType entity.TagRelateType, relateId uint64) []string {
|
||||||
|
return tr.tagTreeRelateRepo.SelectTagPathsByRelate(relateType, relateId)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tr *tagTreeRelateAppImpl) FillTagInfo(relateType entity.TagRelateType, relates ...entity.IRelateTag) {
|
||||||
|
if len(relates) == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关联id -> 关联信息
|
||||||
|
relateIds2Relate := collx.ArrayToMap(relates, func(rt entity.IRelateTag) uint64 {
|
||||||
|
return rt.GetRelateId()
|
||||||
|
})
|
||||||
|
|
||||||
|
var relateTags []*entity.TagTreeRelate
|
||||||
|
tr.ListByWheres(collx.Kvs("relate_type=?", relateType, "relate_id in ?", collx.MapKeys(relateIds2Relate)), &relateTags)
|
||||||
|
|
||||||
|
tagIds := collx.ArrayMap(relateTags, func(rt *entity.TagTreeRelate) uint64 {
|
||||||
|
return rt.TagId
|
||||||
|
})
|
||||||
|
var tags []*entity.TagTree
|
||||||
|
tr.tagTreeApp.GetByIdIn(&tags, tagIds)
|
||||||
|
|
||||||
|
tagId2Tag := collx.ArrayToMap(tags, func(t *entity.TagTree) uint64 {
|
||||||
|
return t.Id
|
||||||
|
})
|
||||||
|
for _, rt := range relateTags {
|
||||||
|
// 赋值标签信息
|
||||||
|
tag := tagId2Tag[rt.TagId]
|
||||||
|
relateIds2Relate[rt.RelateId].SetTagInfo(entity.ResourceTag{CodePath: tag.CodePath, TagId: tag.Id})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,7 +10,6 @@ import (
|
|||||||
"mayfly-go/pkg/gormx"
|
"mayfly-go/pkg/gormx"
|
||||||
"mayfly-go/pkg/logx"
|
"mayfly-go/pkg/logx"
|
||||||
"mayfly-go/pkg/model"
|
"mayfly-go/pkg/model"
|
||||||
"mayfly-go/pkg/utils/collx"
|
|
||||||
|
|
||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
@@ -20,7 +19,7 @@ type SaveTeamParam struct {
|
|||||||
Name string `json:"name" binding:"required"` // 名称
|
Name string `json:"name" binding:"required"` // 名称
|
||||||
Remark string `json:"remark"` // 备注说明
|
Remark string `json:"remark"` // 备注说明
|
||||||
|
|
||||||
Tags []uint64 `json:"tags"` // 关联标签信息
|
CodePaths []string `json:"codePaths"` // 关联标签信息
|
||||||
}
|
}
|
||||||
|
|
||||||
type Team interface {
|
type Team interface {
|
||||||
@@ -42,17 +41,14 @@ type Team interface {
|
|||||||
|
|
||||||
IsExistMember(teamId, accounId uint64) bool
|
IsExistMember(teamId, accounId uint64) bool
|
||||||
|
|
||||||
//--------------- 关联项目相关接口 ---------------
|
|
||||||
|
|
||||||
ListTagIds(teamId uint64) []uint64
|
|
||||||
|
|
||||||
DeleteTag(tx context.Context, teamId, tagId uint64) error
|
DeleteTag(tx context.Context, teamId, tagId uint64) error
|
||||||
}
|
}
|
||||||
|
|
||||||
type teamAppImpl struct {
|
type teamAppImpl struct {
|
||||||
teamRepo repository.Team `inject:"TeamRepo"`
|
teamRepo repository.Team `inject:"TeamRepo"`
|
||||||
teamMemberRepo repository.TeamMember `inject:"TeamMemberRepo"`
|
teamMemberRepo repository.TeamMember `inject:"TeamMemberRepo"`
|
||||||
tagTreeTeamRepo repository.TagTreeTeam `inject:"TagTreeTeamRepo"`
|
|
||||||
|
tagTreeRelateApp TagTreeRelate `inject:"TagTreeRelateApp"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *teamAppImpl) GetPageList(condition *entity.TeamQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) {
|
func (p *teamAppImpl) GetPageList(condition *entity.TeamQuery, pageParam *model.PageParam, toEntity any, orderBy ...string) (*model.PageResult[any], error) {
|
||||||
@@ -89,35 +85,7 @@ func (p *teamAppImpl) Save(ctx context.Context, saveParam *SaveTeamParam) error
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 保存团队关联的标签信息
|
// 保存团队关联的标签信息
|
||||||
teamId := team.Id
|
return p.tagTreeRelateApp.RelateTag(ctx, entity.TagRelateTypeTeam, team.Id, saveParam.CodePaths...)
|
||||||
var addIds, delIds []uint64
|
|
||||||
if saveParam.Id == 0 {
|
|
||||||
addIds = saveParam.Tags
|
|
||||||
} else {
|
|
||||||
// 将[]uint64转为[]any
|
|
||||||
oIds := p.ListTagIds(team.Id)
|
|
||||||
// 比较新旧两合集
|
|
||||||
addIds, delIds, _ = collx.ArrayCompare(saveParam.Tags, oIds)
|
|
||||||
}
|
|
||||||
|
|
||||||
addTeamTags := make([]*entity.TagTreeTeam, 0)
|
|
||||||
for _, v := range addIds {
|
|
||||||
ptt := &entity.TagTreeTeam{TeamId: teamId, TagId: v}
|
|
||||||
addTeamTags = append(addTeamTags, ptt)
|
|
||||||
}
|
|
||||||
if len(addTeamTags) > 0 {
|
|
||||||
logx.DebugfContext(ctx, "团队[%s]新增关联的标签信息: [%v]", team.Name, addTeamTags)
|
|
||||||
p.tagTreeTeamRepo.BatchInsert(ctx, addTeamTags)
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, v := range delIds {
|
|
||||||
p.DeleteTag(ctx, teamId, v)
|
|
||||||
}
|
|
||||||
if len(delIds) > 0 {
|
|
||||||
logx.DebugfContext(ctx, "团队[%s]删除关联的标签信息: [%v]", team.Name, delIds)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *teamAppImpl) Delete(ctx context.Context, id uint64) error {
|
func (p *teamAppImpl) Delete(ctx context.Context, id uint64) error {
|
||||||
@@ -129,7 +97,7 @@ func (p *teamAppImpl) Delete(ctx context.Context, id uint64) error {
|
|||||||
return p.teamMemberRepo.DeleteByCondWithDb(ctx, db, &entity.TeamMember{TeamId: id})
|
return p.teamMemberRepo.DeleteByCondWithDb(ctx, db, &entity.TeamMember{TeamId: id})
|
||||||
},
|
},
|
||||||
func(db *gorm.DB) error {
|
func(db *gorm.DB) error {
|
||||||
return p.tagTreeTeamRepo.DeleteByCondWithDb(ctx, db, &entity.TagTreeTeam{TeamId: id})
|
return p.tagTreeRelateApp.DeleteByCondWithDb(ctx, db, &entity.TagTreeRelate{RelateType: entity.TagRelateTypeTeam, RelateId: id})
|
||||||
},
|
},
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -158,17 +126,7 @@ func (p *teamAppImpl) IsExistMember(teamId, accounId uint64) bool {
|
|||||||
|
|
||||||
//--------------- 标签相关接口 ---------------
|
//--------------- 标签相关接口 ---------------
|
||||||
|
|
||||||
func (p *teamAppImpl) ListTagIds(teamId uint64) []uint64 {
|
// 删除关联标签信息
|
||||||
tags := &[]entity.TagTreeTeam{}
|
|
||||||
p.tagTreeTeamRepo.ListByCondOrder(&entity.TagTreeTeam{TeamId: teamId}, tags)
|
|
||||||
ids := make([]uint64, 0)
|
|
||||||
for _, v := range *tags {
|
|
||||||
ids = append(ids, v.TagId)
|
|
||||||
}
|
|
||||||
return ids
|
|
||||||
}
|
|
||||||
|
|
||||||
// 删除关联项目信息
|
|
||||||
func (p *teamAppImpl) DeleteTag(ctx context.Context, teamId, tagId uint64) error {
|
func (p *teamAppImpl) DeleteTag(ctx context.Context, teamId, tagId uint64) error {
|
||||||
return p.tagTreeTeamRepo.DeleteByCond(ctx, &entity.TagTreeTeam{TeamId: teamId, TagId: tagId})
|
return p.tagTreeRelateApp.DeleteByCond(ctx, &entity.TagTreeRelate{RelateType: entity.TagRelateTypeTeam, RelateId: teamId, TagId: tagId})
|
||||||
}
|
}
|
||||||
|
|||||||
12
server/internal/tag/domain/entity/resource_op_log.go
Normal file
12
server/internal/tag/domain/entity/resource_op_log.go
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
package entity
|
||||||
|
|
||||||
|
import "mayfly-go/pkg/model"
|
||||||
|
|
||||||
|
// 资源操作日志记录
|
||||||
|
type ResourceOpLog struct {
|
||||||
|
model.CreateModel
|
||||||
|
|
||||||
|
CodePath string `json:"codePath"` // 标签路径
|
||||||
|
ResourceCode string `json:"resourceCode"` // 资源编号
|
||||||
|
ResourceType int8 `json:"relateType"` // 资源类型
|
||||||
|
}
|
||||||
@@ -89,11 +89,13 @@ type ITagResource interface {
|
|||||||
|
|
||||||
// 资源关联的标签信息
|
// 资源关联的标签信息
|
||||||
type ResourceTag struct {
|
type ResourceTag struct {
|
||||||
|
TagId uint64 `json:"tagId" gorm:"-"`
|
||||||
CodePath string `json:"codePath" gorm:"-"` // 标签路径
|
CodePath string `json:"codePath" gorm:"-"` // 标签路径
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *ResourceTag) SetTagInfo(rt ResourceTag) {
|
func (r *ResourceTag) SetTagInfo(rt ResourceTag) {
|
||||||
r.CodePath = rt.CodePath
|
r.CodePath = rt.CodePath
|
||||||
|
r.TagId = rt.TagId
|
||||||
}
|
}
|
||||||
|
|
||||||
// 资源标签列表
|
// 资源标签列表
|
||||||
|
|||||||
40
server/internal/tag/domain/entity/tag_tree_relate.go
Normal file
40
server/internal/tag/domain/entity/tag_tree_relate.go
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
package entity
|
||||||
|
|
||||||
|
import "mayfly-go/pkg/model"
|
||||||
|
|
||||||
|
// 与标签树有关联关系的实体
|
||||||
|
type TagTreeRelate struct {
|
||||||
|
model.Model
|
||||||
|
|
||||||
|
TagId uint64 `json:"tagId"`
|
||||||
|
RelateId uint64 `json:"relateId"` // 关联的id
|
||||||
|
RelateType TagRelateType `json:"relateType"` // 关联的类型
|
||||||
|
}
|
||||||
|
|
||||||
|
type TagRelateType int8
|
||||||
|
|
||||||
|
const (
|
||||||
|
TagRelateTypeTeam TagRelateType = 1 // 关联团队
|
||||||
|
TagRelateTypeMachineCmd TagRelateType = 2 // 关联机器命令配置
|
||||||
|
)
|
||||||
|
|
||||||
|
// 关联标签信息,如果要实现填充关联标签信息,则结构体需要实现该接口
|
||||||
|
type IRelateTag interface {
|
||||||
|
// 获取关联id
|
||||||
|
GetRelateId() uint64
|
||||||
|
|
||||||
|
// 赋值标签路径
|
||||||
|
SetTagInfo(tag ResourceTag)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 关联的标签信息
|
||||||
|
type RelateTags struct {
|
||||||
|
Tags []ResourceTag `json:"tags" gorm:"-"` // 标签路径
|
||||||
|
}
|
||||||
|
|
||||||
|
func (r *RelateTags) SetTagInfo(rt ResourceTag) {
|
||||||
|
if r.Tags == nil {
|
||||||
|
r.Tags = make([]ResourceTag, 0)
|
||||||
|
}
|
||||||
|
r.Tags = append(r.Tags, rt)
|
||||||
|
}
|
||||||
@@ -1,11 +0,0 @@
|
|||||||
package entity
|
|
||||||
|
|
||||||
import "mayfly-go/pkg/model"
|
|
||||||
|
|
||||||
// 标签树与团队关联信息
|
|
||||||
type TagTreeTeam struct {
|
|
||||||
model.Model
|
|
||||||
|
|
||||||
TagId uint64 `json:"tagId"`
|
|
||||||
TeamId uint64 `json:"teamId"`
|
|
||||||
}
|
|
||||||
10
server/internal/tag/domain/repository/resource_op_log.go
Normal file
10
server/internal/tag/domain/repository/resource_op_log.go
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"mayfly-go/internal/tag/domain/entity"
|
||||||
|
"mayfly-go/pkg/base"
|
||||||
|
)
|
||||||
|
|
||||||
|
type ResourceOpLog interface {
|
||||||
|
base.Repo[*entity.ResourceOpLog]
|
||||||
|
}
|
||||||
19
server/internal/tag/domain/repository/tag_tree_relate.go
Normal file
19
server/internal/tag/domain/repository/tag_tree_relate.go
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
package repository
|
||||||
|
|
||||||
|
import (
|
||||||
|
"mayfly-go/internal/tag/domain/entity"
|
||||||
|
"mayfly-go/pkg/base"
|
||||||
|
)
|
||||||
|
|
||||||
|
type TagTreeRelate interface {
|
||||||
|
base.Repo[*entity.TagTreeRelate]
|
||||||
|
|
||||||
|
// SelectRelateIdsByTagPaths 根据标签路径查询相关联的id
|
||||||
|
SelectRelateIdsByTagPaths(relateType entity.TagRelateType, tagPaths ...string) ([]uint64, error)
|
||||||
|
|
||||||
|
// SelectTagPathsByAccountId 根据账号id获取该账号可访问操作的标签codePaths(该方法调用较频繁,故不使用下列方法获取)
|
||||||
|
SelectTagPathsByAccountId(accountId uint64) []string
|
||||||
|
|
||||||
|
// SelectTagPathsByRelate 根据关联信息查询对应的关联的标签路径
|
||||||
|
SelectTagPathsByRelate(relateType entity.TagRelateType, relateId uint64) []string
|
||||||
|
}
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
package repository
|
|
||||||
|
|
||||||
import (
|
|
||||||
"mayfly-go/internal/tag/domain/entity"
|
|
||||||
"mayfly-go/pkg/base"
|
|
||||||
)
|
|
||||||
|
|
||||||
type TagTreeTeam interface {
|
|
||||||
base.Repo[*entity.TagTreeTeam]
|
|
||||||
|
|
||||||
SelectTagPathsByAccountId(accountId uint64) []string
|
|
||||||
}
|
|
||||||
@@ -6,8 +6,9 @@ import (
|
|||||||
|
|
||||||
func InitIoc() {
|
func InitIoc() {
|
||||||
ioc.Register(newTagTreeRepo(), ioc.WithComponentName("TagTreeRepo"))
|
ioc.Register(newTagTreeRepo(), ioc.WithComponentName("TagTreeRepo"))
|
||||||
ioc.Register(newTagTreeTeamRepo(), ioc.WithComponentName("TagTreeTeamRepo"))
|
|
||||||
ioc.Register(newTeamRepo(), ioc.WithComponentName("TeamRepo"))
|
ioc.Register(newTeamRepo(), ioc.WithComponentName("TeamRepo"))
|
||||||
ioc.Register(newTeamMemberRepo(), ioc.WithComponentName("TeamMemberRepo"))
|
ioc.Register(newTeamMemberRepo(), ioc.WithComponentName("TeamMemberRepo"))
|
||||||
ioc.Register(newResourceAuthCertRepoImpl(), ioc.WithComponentName("ResourceAuthCertRepo"))
|
ioc.Register(newResourceAuthCertRepoImpl(), ioc.WithComponentName("ResourceAuthCertRepo"))
|
||||||
|
ioc.Register(newResourceOpLogRepo(), ioc.WithComponentName("ResourceOpLogRepo"))
|
||||||
|
ioc.Register(newTagTreeRelateRepo(), ioc.WithComponentName("TagTreeRelateRepo"))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package persistence
|
||||||
|
|
||||||
|
import (
|
||||||
|
"mayfly-go/internal/tag/domain/entity"
|
||||||
|
"mayfly-go/internal/tag/domain/repository"
|
||||||
|
"mayfly-go/pkg/base"
|
||||||
|
)
|
||||||
|
|
||||||
|
type resourceOpLogRepoImpl struct {
|
||||||
|
base.RepoImpl[*entity.ResourceOpLog]
|
||||||
|
}
|
||||||
|
|
||||||
|
func newResourceOpLogRepo() repository.ResourceOpLog {
|
||||||
|
return &resourceOpLogRepoImpl{base.RepoImpl[*entity.ResourceOpLog]{M: new(entity.ResourceOpLog)}}
|
||||||
|
}
|
||||||
@@ -0,0 +1,86 @@
|
|||||||
|
package persistence
|
||||||
|
|
||||||
|
import (
|
||||||
|
"mayfly-go/internal/tag/domain/entity"
|
||||||
|
"mayfly-go/internal/tag/domain/repository"
|
||||||
|
"mayfly-go/pkg/base"
|
||||||
|
"mayfly-go/pkg/gormx"
|
||||||
|
)
|
||||||
|
|
||||||
|
type tagTreeRelateRepoImpl struct {
|
||||||
|
base.RepoImpl[*entity.TagTreeRelate]
|
||||||
|
}
|
||||||
|
|
||||||
|
func newTagTreeRelateRepo() repository.TagTreeRelate {
|
||||||
|
return &tagTreeRelateRepoImpl{base.RepoImpl[*entity.TagTreeRelate]{M: new(entity.TagTreeRelate)}}
|
||||||
|
}
|
||||||
|
|
||||||
|
// SelectRelateIdsByTagPaths 根据标签路径查询相关联的id
|
||||||
|
func (tr *tagTreeRelateRepoImpl) SelectRelateIdsByTagPaths(relateType entity.TagRelateType, tagPaths ...string) ([]uint64, error) {
|
||||||
|
var res []uint64
|
||||||
|
sql := `
|
||||||
|
SELECT
|
||||||
|
t1.relate_id
|
||||||
|
FROM
|
||||||
|
t_tag_tree_relate t1
|
||||||
|
JOIN t_tag_tree t ON
|
||||||
|
t.id = t1.tag_id
|
||||||
|
WHERE
|
||||||
|
t1.relate_type = ?
|
||||||
|
AND t.code_path in ?
|
||||||
|
AND t.is_deleted = 0
|
||||||
|
AND t1.is_deleted = 0
|
||||||
|
ORDER BY
|
||||||
|
t.code_path
|
||||||
|
`
|
||||||
|
if err := gormx.GetListBySql2Model(sql, &res, relateType, tagPaths); err != nil {
|
||||||
|
return res, err
|
||||||
|
}
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (tr *tagTreeRelateRepoImpl) SelectTagPathsByAccountId(accountId uint64) []string {
|
||||||
|
var res []string
|
||||||
|
sql := `
|
||||||
|
SELECT
|
||||||
|
DISTINCT(t.code_path)
|
||||||
|
FROM
|
||||||
|
t_tag_tree_relate t1
|
||||||
|
JOIN t_team_member t2 ON
|
||||||
|
t1.relate_id = t2.team_id
|
||||||
|
JOIN t_tag_tree t ON
|
||||||
|
t.id = t1.tag_id
|
||||||
|
WHERE
|
||||||
|
t1.relate_type = ?
|
||||||
|
AND t2.account_id = ?
|
||||||
|
AND t1.is_deleted = 0
|
||||||
|
AND t2.is_deleted = 0
|
||||||
|
AND t.is_deleted = 0
|
||||||
|
ORDER BY
|
||||||
|
t.code_path
|
||||||
|
`
|
||||||
|
gormx.GetListBySql2Model(sql, &res, entity.TagRelateTypeTeam, accountId)
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
// SelectTagPathsByRelate 根据关联信息查询对应的关联的标签路径
|
||||||
|
func (tr *tagTreeRelateRepoImpl) SelectTagPathsByRelate(relateType entity.TagRelateType, relateId uint64) []string {
|
||||||
|
var res []string
|
||||||
|
sql := `
|
||||||
|
SELECT
|
||||||
|
DISTINCT(t.code_path)
|
||||||
|
FROM
|
||||||
|
t_tag_tree_relate t1
|
||||||
|
JOIN t_tag_tree t ON
|
||||||
|
t.id = t1.tag_id
|
||||||
|
WHERE
|
||||||
|
t1.relate_id = ?
|
||||||
|
AND t1.relate_type = ?
|
||||||
|
AND t.is_deleted = 0
|
||||||
|
AND t1.is_deleted = 0
|
||||||
|
ORDER BY
|
||||||
|
t.code_path
|
||||||
|
`
|
||||||
|
gormx.GetListBySql2Model(sql, &res, relateId, relateType)
|
||||||
|
return res
|
||||||
|
}
|
||||||
@@ -1,39 +0,0 @@
|
|||||||
package persistence
|
|
||||||
|
|
||||||
import (
|
|
||||||
"mayfly-go/internal/tag/domain/entity"
|
|
||||||
"mayfly-go/internal/tag/domain/repository"
|
|
||||||
"mayfly-go/pkg/base"
|
|
||||||
"mayfly-go/pkg/gormx"
|
|
||||||
)
|
|
||||||
|
|
||||||
type tagTreeTeamRepoImpl struct {
|
|
||||||
base.RepoImpl[*entity.TagTreeTeam]
|
|
||||||
}
|
|
||||||
|
|
||||||
func newTagTreeTeamRepo() repository.TagTreeTeam {
|
|
||||||
return &tagTreeTeamRepoImpl{base.RepoImpl[*entity.TagTreeTeam]{M: new(entity.TagTreeTeam)}}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p *tagTreeTeamRepoImpl) SelectTagPathsByAccountId(accountId uint64) []string {
|
|
||||||
var res []string
|
|
||||||
sql := `
|
|
||||||
SELECT
|
|
||||||
DISTINCT(t.code_path)
|
|
||||||
FROM
|
|
||||||
t_tag_tree_team t1
|
|
||||||
JOIN t_team_member t2 ON
|
|
||||||
t1.team_id = t2.team_id
|
|
||||||
JOIN t_tag_tree t ON
|
|
||||||
t.id = t1.tag_id
|
|
||||||
WHERE
|
|
||||||
t2.account_id = ?
|
|
||||||
AND t1.is_deleted = 0
|
|
||||||
AND t2.is_deleted = 0
|
|
||||||
AND t.is_deleted = 0
|
|
||||||
ORDER BY
|
|
||||||
t.code_path
|
|
||||||
`
|
|
||||||
gormx.GetListBySql2Model(sql, &res, accountId)
|
|
||||||
return res
|
|
||||||
}
|
|
||||||
@@ -1,10 +1,14 @@
|
|||||||
package init
|
package init
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"context"
|
||||||
"mayfly-go/initialize"
|
"mayfly-go/initialize"
|
||||||
|
"mayfly-go/internal/event"
|
||||||
"mayfly-go/internal/tag/application"
|
"mayfly-go/internal/tag/application"
|
||||||
"mayfly-go/internal/tag/infrastructure/persistence"
|
"mayfly-go/internal/tag/infrastructure/persistence"
|
||||||
"mayfly-go/internal/tag/router"
|
"mayfly-go/internal/tag/router"
|
||||||
|
"mayfly-go/pkg/eventbus"
|
||||||
|
"mayfly-go/pkg/global"
|
||||||
)
|
)
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
@@ -13,4 +17,14 @@ func init() {
|
|||||||
application.InitIoc()
|
application.InitIoc()
|
||||||
})
|
})
|
||||||
initialize.AddInitRouterFunc(router.Init)
|
initialize.AddInitRouterFunc(router.Init)
|
||||||
|
initialize.AddInitFunc(Init)
|
||||||
|
}
|
||||||
|
|
||||||
|
func Init() {
|
||||||
|
|
||||||
|
global.EventBus.SubscribeAsync(event.EventTopicResourceOp, "ResourceOpLogApp", func(ctx context.Context, event *eventbus.Event) error {
|
||||||
|
codePath := event.Val.(string)
|
||||||
|
return application.GetResourceOpLogApp().AddResourceOpLog(ctx, codePath)
|
||||||
|
}, false)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
24
server/internal/tag/router/resource_op_log.go
Normal file
24
server/internal/tag/router/resource_op_log.go
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
package router
|
||||||
|
|
||||||
|
import (
|
||||||
|
"mayfly-go/internal/tag/api"
|
||||||
|
"mayfly-go/pkg/biz"
|
||||||
|
"mayfly-go/pkg/ioc"
|
||||||
|
"mayfly-go/pkg/req"
|
||||||
|
|
||||||
|
"github.com/gin-gonic/gin"
|
||||||
|
)
|
||||||
|
|
||||||
|
func InitResourceOpLogRouter(router *gin.RouterGroup) {
|
||||||
|
m := new(api.ResourceOpLog)
|
||||||
|
biz.ErrIsNil(ioc.Inject(m))
|
||||||
|
|
||||||
|
resourceOpLog := router.Group("/resource-op-logs")
|
||||||
|
{
|
||||||
|
reqs := [...]*req.Conf{
|
||||||
|
req.NewGet("/account", m.PageAccountOpLog),
|
||||||
|
}
|
||||||
|
|
||||||
|
req.BatchSetGroup(resourceOpLog, reqs[:])
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,4 +6,5 @@ func Init(router *gin.RouterGroup) {
|
|||||||
InitTagTreeRouter(router)
|
InitTagTreeRouter(router)
|
||||||
InitTeamRouter(router)
|
InitTeamRouter(router)
|
||||||
InitResourceAuthCertRouter(router)
|
InitResourceAuthCertRouter(router)
|
||||||
|
InitResourceOpLogRouter(router)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -31,6 +31,9 @@ func InitTagTreeRouter(router *gin.RouterGroup) {
|
|||||||
req.NewGet("/resources/:rtype/tag-paths", m.TagResources),
|
req.NewGet("/resources/:rtype/tag-paths", m.TagResources),
|
||||||
|
|
||||||
req.NewGet("/resources/count", m.CountTagResource),
|
req.NewGet("/resources/count", m.CountTagResource),
|
||||||
|
|
||||||
|
// 获取关联的标签id列表
|
||||||
|
req.NewGet("/relate/:relateType/:relateId", m.GetRelateTagIds),
|
||||||
}
|
}
|
||||||
|
|
||||||
req.BatchSetGroup(tagTree, reqs[:])
|
req.BatchSetGroup(tagTree, reqs[:])
|
||||||
|
|||||||
@@ -29,9 +29,6 @@ func InitTeamRouter(router *gin.RouterGroup) {
|
|||||||
req.NewPost("/:id/members", m.SaveTeamMember).Log(req.NewLogSave("团队-新增成员")).RequiredPermissionCode("team:member:save"),
|
req.NewPost("/:id/members", m.SaveTeamMember).Log(req.NewLogSave("团队-新增成员")).RequiredPermissionCode("team:member:save"),
|
||||||
|
|
||||||
req.NewDelete("/:id/members/:accountId", m.DelTeamMember).Log(req.NewLogSave("团队-删除成员")).RequiredPermissionCode("team:member:del"),
|
req.NewDelete("/:id/members/:accountId", m.DelTeamMember).Log(req.NewLogSave("团队-删除成员")).RequiredPermissionCode("team:member:del"),
|
||||||
|
|
||||||
// 获取团队关联的标签id列表
|
|
||||||
req.NewGet("/:id/tags", m.GetTagIds),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
req.BatchSetGroup(team, reqs[:])
|
req.BatchSetGroup(team, reqs[:])
|
||||||
|
|||||||
@@ -89,9 +89,6 @@ func T2022() *gormigrate.Migration {
|
|||||||
if err := tx.AutoMigrate(&entity7.TagTree{}); err != nil {
|
if err := tx.AutoMigrate(&entity7.TagTree{}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if err := tx.AutoMigrate(&entity7.TagTreeTeam{}); err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
if err := tx.AutoMigrate(&entity7.Team{}); err != nil {
|
if err := tx.AutoMigrate(&entity7.Team{}); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -138,9 +138,18 @@ type Map[K comparable, V any] map[K]V
|
|||||||
|
|
||||||
func (m *Map[K, V]) Scan(value any) error {
|
func (m *Map[K, V]) Scan(value any) error {
|
||||||
return json.Unmarshal(value.([]byte), m)
|
return json.Unmarshal(value.([]byte), m)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m Map[K, V]) Value() (driver.Value, error) {
|
func (m Map[K, V]) Value() (driver.Value, error) {
|
||||||
return json.Marshal(m)
|
return json.Marshal(m)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Slice[T int | string | Map[string, any]] []T
|
||||||
|
|
||||||
|
func (s *Slice[T]) Scan(value any) error {
|
||||||
|
return json.Unmarshal(value.([]byte), s)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s Slice[T]) Value() (driver.Value, error) {
|
||||||
|
return json.Marshal(s)
|
||||||
|
}
|
||||||
|
|||||||
@@ -503,6 +503,25 @@ CREATE TABLE `t_machine_term_op` (
|
|||||||
PRIMARY KEY (`id`)
|
PRIMARY KEY (`id`)
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='机器终端操作记录表';
|
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='机器终端操作记录表';
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `t_machine_cmd_conf`;
|
||||||
|
CREATE TABLE `t_machine_cmd_conf` (
|
||||||
|
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
|
`name` varchar(100) COLLATE utf8_bin DEFAULT NULL COMMENT '名称',
|
||||||
|
`cmds` varchar(500) COLLATE utf8_bin DEFAULT NULL COMMENT '命令配置',
|
||||||
|
`status` tinyint(4) DEFAULT NULL COMMENT '状态',
|
||||||
|
`stratege` varchar(100) COLLATE utf8_bin DEFAULT NULL COMMENT '策略',
|
||||||
|
`remark` varchar(50) COLLATE utf8_bin DEFAULT NULL COMMENT '备注',
|
||||||
|
`create_time` datetime NOT NULL,
|
||||||
|
`creator_id` bigint(20) NOT NULL,
|
||||||
|
`creator` varchar(36) COLLATE utf8_bin NOT NULL,
|
||||||
|
`update_time` datetime NOT NULL,
|
||||||
|
`modifier_id` bigint(20) NOT NULL,
|
||||||
|
`modifier` varchar(36) COLLATE utf8_bin NOT NULL,
|
||||||
|
`is_deleted` tinyint(4) DEFAULT '0',
|
||||||
|
`delete_time` datetime DEFAULT NULL,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=1 COMMENT='机器命令配置';
|
||||||
|
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
-- Table structure for t_mongo
|
-- Table structure for t_mongo
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
@@ -826,7 +845,9 @@ INSERT INTO `t_sys_resource` (`id`, `pid`, `type`, `status`, `name`, `code`, `we
|
|||||||
INSERT INTO `t_sys_resource` (`id`, `pid`, `type`, `status`, `name`, `code`, `weight`, `meta`, `creator_id`, `creator`, `modifier_id`, `modifier`, `create_time`, `update_time`, `ui_path`, `is_deleted`, `delete_time`) VALUES(1709196723, 1709194669, 2, 1, '启停', 'db:transfer:status', 1709196723, 'null', 12, 'liuzongyang', 12, 'liuzongyang', '2024-02-29 16:52:04', '2024-02-29 16:52:04', 'SmLcpu6c/hGiLN1VT/', 0, NULL);
|
INSERT INTO `t_sys_resource` (`id`, `pid`, `type`, `status`, `name`, `code`, `weight`, `meta`, `creator_id`, `creator`, `modifier_id`, `modifier`, `create_time`, `update_time`, `ui_path`, `is_deleted`, `delete_time`) VALUES(1709196723, 1709194669, 2, 1, '启停', 'db:transfer:status', 1709196723, 'null', 12, 'liuzongyang', 12, 'liuzongyang', '2024-02-29 16:52:04', '2024-02-29 16:52:04', 'SmLcpu6c/hGiLN1VT/', 0, NULL);
|
||||||
INSERT INTO `t_sys_resource` (`id`, `pid`, `type`, `status`, `name`, `code`, `weight`, `meta`, `creator_id`, `creator`, `modifier_id`, `modifier`, `create_time`, `update_time`, `ui_path`, `is_deleted`, `delete_time`) VALUES(1709196737, 1709194669, 2, 1, '日志', 'db:transfer:log', 1709196737, 'null', 12, 'liuzongyang', 12, 'liuzongyang', '2024-02-29 16:52:17', '2024-02-29 16:52:17', 'SmLcpu6c/CZhNIbWg/', 0, NULL);
|
INSERT INTO `t_sys_resource` (`id`, `pid`, `type`, `status`, `name`, `code`, `weight`, `meta`, `creator_id`, `creator`, `modifier_id`, `modifier`, `create_time`, `update_time`, `ui_path`, `is_deleted`, `delete_time`) VALUES(1709196737, 1709194669, 2, 1, '日志', 'db:transfer:log', 1709196737, 'null', 12, 'liuzongyang', 12, 'liuzongyang', '2024-02-29 16:52:17', '2024-02-29 16:52:17', 'SmLcpu6c/CZhNIbWg/', 0, NULL);
|
||||||
INSERT INTO `t_sys_resource` (`id`, `pid`, `type`, `status`, `name`, `code`, `weight`, `meta`, `creator_id`, `creator`, `modifier_id`, `modifier`, `create_time`, `update_time`, `ui_path`, `is_deleted`, `delete_time`) VALUES(1709196755, 1709194669, 2, 1, '运行', 'db:transfer:run', 1709196755, 'null', 12, 'liuzongyang', 12, 'liuzongyang', '2024-02-29 16:52:36', '2024-02-29 16:52:36', 'SmLcpu6c/b6yHt6V2/', 0, NULL);
|
INSERT INTO `t_sys_resource` (`id`, `pid`, `type`, `status`, `name`, `code`, `weight`, `meta`, `creator_id`, `creator`, `modifier_id`, `modifier`, `create_time`, `update_time`, `ui_path`, `is_deleted`, `delete_time`) VALUES(1709196755, 1709194669, 2, 1, '运行', 'db:transfer:run', 1709196755, 'null', 12, 'liuzongyang', 12, 'liuzongyang', '2024-02-29 16:52:36', '2024-02-29 16:52:36', 'SmLcpu6c/b6yHt6V2/', 0, NULL);
|
||||||
|
INSERT INTO t_sys_resource (id, pid, ui_path, `type`, status, name, code, weight, meta, creator_id, creator, modifier_id, modifier, create_time, update_time, is_deleted, delete_time) VALUES(1714032002, 1713875842, '12sSjal1/UnWIUhW0/0tJwC3Gf/', 2, 1, '命令配置-删除', 'cmdconf:del', 1714032002, 'null', 1, 'admin', 1, 'admin', '2024-04-25 16:00:02', '2024-04-25 16:00:02', 0, NULL);
|
||||||
|
INSERT INTO t_sys_resource (id, pid, ui_path, `type`, status, name, code, weight, meta, creator_id, creator, modifier_id, modifier, create_time, update_time, is_deleted, delete_time) VALUES(1714031981, 1713875842, '12sSjal1/UnWIUhW0/tEzIKecl/', 2, 1, '命令配置-保存', 'cmdconf:save', 1714031981, 'null', 1, 'admin', 1, 'admin', '2024-04-25 15:59:41', '2024-04-25 15:59:41', 0, NULL);
|
||||||
|
INSERT INTO t_sys_resource (id, pid, ui_path, `type`, status, name, code, weight, meta, creator_id, creator, modifier_id, modifier, create_time, update_time, is_deleted, delete_time) VALUES(1713875842, 2, '12sSjal1/UnWIUhW0/', 1, 1, '安全配置', 'security', 1713875842, '{"component":"ops/machine/security/SecurityConfList","icon":"Setting","isKeepAlive":true,"routeName":"SecurityConfList"}', 1, 'admin', 1, 'admin', '2024-04-23 20:37:22', '2024-04-23 20:37:22', 0, NULL);
|
||||||
COMMIT;
|
COMMIT;
|
||||||
|
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
@@ -913,31 +934,29 @@ BEGIN;
|
|||||||
INSERT INTO `t_tag_tree` VALUES (1, -1, 'default', 'default/', '默认', '默认标签', '2022-10-26 20:04:19', 1, 'admin', '2022-10-26 20:04:19', 1, 'admin', 0, NULL);
|
INSERT INTO `t_tag_tree` VALUES (1, -1, 'default', 'default/', '默认', '默认标签', '2022-10-26 20:04:19', 1, 'admin', '2022-10-26 20:04:19', 1, 'admin', 0, NULL);
|
||||||
COMMIT;
|
COMMIT;
|
||||||
|
|
||||||
-- ----------------------------
|
DROP TABLE IF EXISTS `t_tag_tree_relate`;
|
||||||
-- Table structure for t_tag_tree_team
|
CREATE TABLE `t_tag_tree_relate` (
|
||||||
-- ----------------------------
|
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
|
||||||
DROP TABLE IF EXISTS `t_tag_tree_team`;
|
`tag_id` bigint NOT NULL COMMENT '标签树id',
|
||||||
CREATE TABLE `t_tag_tree_team` (
|
`relate_id` bigint NOT NULL COMMENT '关联',
|
||||||
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
`relate_type` tinyint NOT NULL COMMENT '关联类型',
|
||||||
`tag_id` bigint(20) NOT NULL COMMENT '项目树id',
|
|
||||||
`team_id` bigint(20) NOT NULL COMMENT '团队id',
|
|
||||||
`create_time` datetime NOT NULL,
|
`create_time` datetime NOT NULL,
|
||||||
`creator_id` bigint(20) NOT NULL,
|
`creator_id` bigint NOT NULL,
|
||||||
`creator` varchar(36) NOT NULL,
|
`creator` varchar(36) COLLATE utf8mb4_bin NOT NULL,
|
||||||
`update_time` datetime NOT NULL,
|
`update_time` datetime NOT NULL,
|
||||||
`modifier_id` bigint(20) NOT NULL,
|
`modifier_id` bigint NOT NULL,
|
||||||
`modifier` varchar(36) NOT NULL,
|
`modifier` varchar(36) COLLATE utf8mb4_bin NOT NULL,
|
||||||
`is_deleted` tinyint(8) NOT NULL DEFAULT 0,
|
`is_deleted` tinyint NOT NULL DEFAULT '0',
|
||||||
`delete_time` datetime DEFAULT NULL,
|
`delete_time` datetime DEFAULT NULL,
|
||||||
PRIMARY KEY (`id`),
|
PRIMARY KEY (`id`),
|
||||||
KEY `idx_tag_id` (`tag_id`) USING BTREE
|
KEY `idx_tag_id` (`tag_id`) USING BTREE
|
||||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='标签树团队关联信息';
|
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='与标签树有关联关系的表';
|
||||||
|
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
-- Records of t_tag_tree_team
|
-- Records of t_tag_tree_relate
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
BEGIN;
|
BEGIN;
|
||||||
INSERT INTO `t_tag_tree_team` VALUES (1, 1, 1, '2022-10-26 20:04:45', 1, 'admin', '2022-10-26 20:04:45', 1, 'admin', 0, NULL);
|
INSERT INTO `t_tag_tree_relate` VALUES (1, 1, 1, 1, '2022-10-26 20:04:45', 1, 'admin', '2022-10-26 20:04:45', 1, 'admin', 0, NULL);
|
||||||
COMMIT;
|
COMMIT;
|
||||||
|
|
||||||
-- ----------------------------
|
-- ----------------------------
|
||||||
@@ -1019,6 +1038,21 @@ CREATE TABLE `t_resource_auth_cert` (
|
|||||||
KEY `idx_name` (`name`) USING BTREE
|
KEY `idx_name` (`name`) USING BTREE
|
||||||
) COMMENT='资源授权凭证表';
|
) COMMENT='资源授权凭证表';
|
||||||
|
|
||||||
|
DROP TABLE IF EXISTS `t_resource_op_log`;
|
||||||
|
CREATE TABLE `t_resource_op_log` (
|
||||||
|
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
|
||||||
|
`code_path` varchar(600) NOT NULL COMMENT '资源标签路径',
|
||||||
|
`resource_code` varchar(32) NOT NULL COMMENT '资源编号',
|
||||||
|
`resource_type` tinyint NOT NULL COMMENT '资源类型',
|
||||||
|
`create_time` datetime NOT NULL,
|
||||||
|
`creator_id` bigint NOT NULL,
|
||||||
|
`creator` varchar(36) NOT NULL,
|
||||||
|
`is_deleted` tinyint NOT NULL DEFAULT '0',
|
||||||
|
`delete_time` datetime DEFAULT NULL,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `idx_resource_code` (`resource_code`) USING BTREE
|
||||||
|
) ENGINE=InnoDB COMMENT='资源操作记录';
|
||||||
|
|
||||||
DROP TABLE IF EXISTS `t_flow_procdef`;
|
DROP TABLE IF EXISTS `t_flow_procdef`;
|
||||||
-- 工单流程相关表
|
-- 工单流程相关表
|
||||||
CREATE TABLE `t_flow_procdef` (
|
CREATE TABLE `t_flow_procdef` (
|
||||||
|
|||||||
84
server/resources/script/sql/v1.8/v1.8.2.sql
Normal file
84
server/resources/script/sql/v1.8/v1.8.2.sql
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
CREATE TABLE `t_tag_tree_relate` (
|
||||||
|
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
|
||||||
|
`tag_id` bigint NOT NULL COMMENT '标签树id',
|
||||||
|
`relate_id` bigint NOT NULL COMMENT '关联',
|
||||||
|
`relate_type` tinyint NOT NULL COMMENT '关联类型',
|
||||||
|
`create_time` datetime NOT NULL,
|
||||||
|
`creator_id` bigint NOT NULL,
|
||||||
|
`creator` varchar(36) COLLATE utf8mb4_bin NOT NULL,
|
||||||
|
`update_time` datetime NOT NULL,
|
||||||
|
`modifier_id` bigint NOT NULL,
|
||||||
|
`modifier` varchar(36) COLLATE utf8mb4_bin NOT NULL,
|
||||||
|
`is_deleted` tinyint NOT NULL DEFAULT '0',
|
||||||
|
`delete_time` datetime DEFAULT NULL,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `idx_tag_id` (`tag_id`) USING BTREE
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin COMMENT='与标签树有关联关系的表';
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE `t_machine_cmd_conf` (
|
||||||
|
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
|
||||||
|
`name` varchar(100) COLLATE utf8_bin DEFAULT NULL COMMENT '名称',
|
||||||
|
`cmds` varchar(500) COLLATE utf8_bin DEFAULT NULL COMMENT '命令配置',
|
||||||
|
`status` tinyint(4) DEFAULT NULL COMMENT '状态',
|
||||||
|
`stratege` varchar(100) COLLATE utf8_bin DEFAULT NULL COMMENT '策略',
|
||||||
|
`remark` varchar(50) COLLATE utf8_bin DEFAULT NULL COMMENT '备注',
|
||||||
|
`create_time` datetime NOT NULL,
|
||||||
|
`creator_id` bigint(20) NOT NULL,
|
||||||
|
`creator` varchar(36) COLLATE utf8_bin NOT NULL,
|
||||||
|
`update_time` datetime NOT NULL,
|
||||||
|
`modifier_id` bigint(20) NOT NULL,
|
||||||
|
`modifier` varchar(36) COLLATE utf8_bin NOT NULL,
|
||||||
|
`is_deleted` tinyint(4) DEFAULT '0',
|
||||||
|
`delete_time` datetime DEFAULT NULL,
|
||||||
|
PRIMARY KEY (`id`)
|
||||||
|
) ENGINE=InnoDB AUTO_INCREMENT=1 COMMENT='机器命令配置';
|
||||||
|
|
||||||
|
INSERT INTO t_sys_resource (id, pid, ui_path, `type`, status, name, code, weight, meta, creator_id, creator, modifier_id, modifier, create_time, update_time, is_deleted, delete_time) VALUES(1714032002, 1713875842, '12sSjal1/UnWIUhW0/0tJwC3Gf/', 2, 1, '命令配置-删除', 'cmdconf:del', 1714032002, 'null', 1, 'admin', 1, 'admin', '2024-04-25 16:00:02', '2024-04-25 16:00:02', 0, NULL);
|
||||||
|
INSERT INTO t_sys_resource (id, pid, ui_path, `type`, status, name, code, weight, meta, creator_id, creator, modifier_id, modifier, create_time, update_time, is_deleted, delete_time) VALUES(1714031981, 1713875842, '12sSjal1/UnWIUhW0/tEzIKecl/', 2, 1, '命令配置-保存', 'cmdconf:save', 1714031981, 'null', 1, 'admin', 1, 'admin', '2024-04-25 15:59:41', '2024-04-25 15:59:41', 0, NULL);
|
||||||
|
INSERT INTO t_sys_resource (id, pid, ui_path, `type`, status, name, code, weight, meta, creator_id, creator, modifier_id, modifier, create_time, update_time, is_deleted, delete_time) VALUES(1713875842, 2, '12sSjal1/UnWIUhW0/', 1, 1, '安全配置', 'security', 1713875842, '{"component":"ops/machine/security/SecurityConfList","icon":"Setting","isKeepAlive":true,"routeName":"SecurityConfList"}', 1, 'admin', 1, 'admin', '2024-04-23 20:37:22', '2024-04-23 20:37:22', 0, NULL);
|
||||||
|
|
||||||
|
INSERT
|
||||||
|
INTO
|
||||||
|
t_tag_tree_relate (tag_id,
|
||||||
|
relate_id,
|
||||||
|
relate_type,
|
||||||
|
create_time,
|
||||||
|
creator_id,
|
||||||
|
creator,
|
||||||
|
update_time,
|
||||||
|
modifier_id,
|
||||||
|
modifier,
|
||||||
|
is_deleted )
|
||||||
|
SELECT
|
||||||
|
tt.tag_id ,
|
||||||
|
tt.team_id ,
|
||||||
|
1,
|
||||||
|
DATE_FORMAT( NOW(), '%Y-%m-%d %H:%i:%s' ),
|
||||||
|
1,
|
||||||
|
'admin',
|
||||||
|
DATE_FORMAT( NOW(), '%Y-%m-%d %H:%i:%s' ),
|
||||||
|
1,
|
||||||
|
'admin',
|
||||||
|
0
|
||||||
|
FROM
|
||||||
|
`t_tag_tree_team` tt
|
||||||
|
WHERE
|
||||||
|
tt.`is_deleted` = 0;
|
||||||
|
|
||||||
|
DROP TABLE t_tag_tree_team;
|
||||||
|
|
||||||
|
|
||||||
|
CREATE TABLE `t_resource_op_log` (
|
||||||
|
`id` bigint unsigned NOT NULL AUTO_INCREMENT,
|
||||||
|
`code_path` varchar(600) NOT NULL COMMENT '资源标签路径',
|
||||||
|
`resource_code` varchar(32) NOT NULL COMMENT '资源编号',
|
||||||
|
`resource_type` tinyint NOT NULL COMMENT '资源类型',
|
||||||
|
`create_time` datetime NOT NULL,
|
||||||
|
`creator_id` bigint NOT NULL,
|
||||||
|
`creator` varchar(36) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
|
||||||
|
`is_deleted` tinyint NOT NULL DEFAULT '0',
|
||||||
|
`delete_time` datetime DEFAULT NULL,
|
||||||
|
PRIMARY KEY (`id`),
|
||||||
|
KEY `idx_resource_code` (`resource_code`) USING BTREE
|
||||||
|
) ENGINE=InnoDB COMMENT='资源操作记录';
|
||||||
Reference in New Issue
Block a user