mirror of
https://gitee.com/dromara/mayfly-go
synced 2025-11-03 16:00:25 +08:00
feat: 使用标签替代项目
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
export function dateFormat(fmt: string, date: Date) {
|
||||
export function dateFormat2(fmt: string, date: Date) {
|
||||
let ret;
|
||||
const opt = {
|
||||
"y+": date.getFullYear().toString(), // 年
|
||||
@@ -19,5 +19,9 @@ export function dateFormat(fmt: string, date: Date) {
|
||||
}
|
||||
|
||||
export function dateStrFormat(fmt: string, dateStr: string) {
|
||||
return dateFormat(fmt, new Date(dateStr))
|
||||
return dateFormat2(fmt, new Date(dateStr))
|
||||
}
|
||||
|
||||
export function dateFormat(dateStr: string) {
|
||||
return dateFormat2('yyyy-MM-dd HH:mm:ss',new Date(dateStr))
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
import { getUseWatermark4Session, getUserInfo4Session } from '@/common/utils/storage.ts';
|
||||
import { dateFormat } from '@/common/utils/date.ts'
|
||||
import { dateFormat2 } from '@/common/utils/date.ts'
|
||||
|
||||
// 页面添加水印效果
|
||||
const setWatermark = (str: any) => {
|
||||
@@ -46,7 +46,7 @@ const watermark = {
|
||||
setTimeout(() => {
|
||||
const userinfo = getUserInfo4Session()
|
||||
if (userinfo && getUseWatermark4Session()) {
|
||||
set(`${userinfo.username} ${dateFormat('yyyy-MM-dd HH:mm:ss', new Date())}`)
|
||||
set(`${userinfo.username} ${dateFormat2('yyyy-MM-dd HH:mm:ss', new Date())}`)
|
||||
} else {
|
||||
del();
|
||||
}
|
||||
|
||||
@@ -14,8 +14,9 @@ export const imports = {
|
||||
"SyslogList": () => import('@/views/system/syslog/SyslogList.vue'),
|
||||
"ConfigList": () => import('@/views/system/config/ConfigList.vue'),
|
||||
|
||||
// project
|
||||
"ProjectList": () => import('@/views/ops/project/ProjectList.vue'),
|
||||
// tag
|
||||
"TagTreeList": () => import('@/views/ops/tag/TagTreeList.vue'),
|
||||
"TeamList": () => import('@/views/ops/tag/TeamList.vue'),
|
||||
// db
|
||||
"DbList": () => import('@/views/ops/db/DbList.vue'),
|
||||
"SqlExec": () => import('@/views/ops/db'),
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { RouteRecordRaw } from 'vue-router';
|
||||
import Layout from '@/views/layout/index.vue'
|
||||
import RouterParent from '@/views/layout/routerView/parent.vue';
|
||||
// import RouterParent from '@/views/layout/routerView/parent.vue';
|
||||
|
||||
// 定义动态路由
|
||||
export const dynamicRoutes = [
|
||||
@@ -12,119 +12,108 @@ export const dynamicRoutes = [
|
||||
meta: {
|
||||
isKeepAlive: true,
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '/home',
|
||||
name: 'home',
|
||||
component: () => import('@/views/home/index.vue'),
|
||||
meta: {
|
||||
title: '首页',
|
||||
// iframe链接
|
||||
link: '',
|
||||
// 是否在菜单栏显示,默认显示
|
||||
isHide: false,
|
||||
isKeepAlive: true,
|
||||
// tag标签是否不可删除
|
||||
isAffix: true,
|
||||
// 是否为iframe
|
||||
isIframe: false,
|
||||
icon: 'el-icon-s-home',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/sys',
|
||||
name: 'Resource',
|
||||
redirect: '/sys/resources',
|
||||
meta: {
|
||||
title: '系统管理',
|
||||
// 资源code,用于校验用户是否拥有该资源权限
|
||||
code: 'sys',
|
||||
// isKeepAlive: true,
|
||||
icon: 'el-icon-monitor',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: 'sys/resources',
|
||||
name: 'ResourceList',
|
||||
component: () => import('@/views/system/resource'),
|
||||
meta: {
|
||||
title: '资源管理',
|
||||
code: 'resource:list',
|
||||
isKeepAlive: true,
|
||||
icon: 'el-icon-menu',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'sys/roles',
|
||||
name: 'RoleList',
|
||||
component: () => import('@/views/system/role'),
|
||||
meta: {
|
||||
title: '角色管理',
|
||||
code: 'role:list',
|
||||
isKeepAlive: true,
|
||||
icon: 'el-icon-menu',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: 'sys/accounts',
|
||||
name: 'ResourceList',
|
||||
component: () => import('@/views/system/account'),
|
||||
meta: {
|
||||
title: '账号管理',
|
||||
code: 'account:list',
|
||||
isKeepAlive: true,
|
||||
icon: 'el-icon-menu',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/machine',
|
||||
name: 'Machine',
|
||||
redirect: '/machine/list',
|
||||
meta: {
|
||||
title: '机器管理',
|
||||
// 资源code,用于校验用户是否拥有该资源权限
|
||||
code: 'machine',
|
||||
// isKeepAlive: true,
|
||||
icon: 'el-icon-monitor',
|
||||
},
|
||||
children: [
|
||||
{
|
||||
path: '/list',
|
||||
name: 'MachineList',
|
||||
component: () => import('@/views/ops/machine'),
|
||||
meta: {
|
||||
title: '机器列表',
|
||||
code: 'machine:list',
|
||||
isKeepAlive: true,
|
||||
icon: 'el-icon-menu',
|
||||
},
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
path: '/personal',
|
||||
name: 'personal',
|
||||
component: () => import('@/views/personal/index.vue'),
|
||||
meta: {
|
||||
title: '个人中心',
|
||||
isKeepAlive: true,
|
||||
icon: 'el-icon-user',
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/iframes',
|
||||
name: 'layoutIfameView',
|
||||
component: RouterParent,
|
||||
meta: {
|
||||
title: 'iframe',
|
||||
link: 'https://gitee.com/lyt-top/vue-next-admin',
|
||||
isIframe: true,
|
||||
icon: 'el-icon-menu',
|
||||
},
|
||||
},
|
||||
],
|
||||
// children: [
|
||||
// {
|
||||
// path: '/home',
|
||||
// name: 'home',
|
||||
// component: () => import('@/views/home/index.vue'),
|
||||
// meta: {
|
||||
// title: '首页',
|
||||
// // iframe链接
|
||||
// link: '',
|
||||
// // 是否在菜单栏显示,默认显示
|
||||
// isHide: false,
|
||||
// isKeepAlive: true,
|
||||
// // tag标签是否不可删除
|
||||
// isAffix: true,
|
||||
// // 是否为iframe
|
||||
// isIframe: false,
|
||||
// icon: 'el-icon-s-home',
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// path: '/sys',
|
||||
// name: 'Resource',
|
||||
// redirect: '/sys/resources',
|
||||
// meta: {
|
||||
// title: '系统管理',
|
||||
// // 资源code,用于校验用户是否拥有该资源权限
|
||||
// code: 'sys',
|
||||
// // isKeepAlive: true,
|
||||
// icon: 'el-icon-monitor',
|
||||
// },
|
||||
// children: [
|
||||
// {
|
||||
// path: 'sys/resources',
|
||||
// name: 'ResourceList',
|
||||
// component: () => import('@/views/system/resource'),
|
||||
// meta: {
|
||||
// title: '资源管理',
|
||||
// code: 'resource:list',
|
||||
// isKeepAlive: true,
|
||||
// icon: 'el-icon-menu',
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// path: 'sys/roles',
|
||||
// name: 'RoleList',
|
||||
// component: () => import('@/views/system/role'),
|
||||
// meta: {
|
||||
// title: '角色管理',
|
||||
// code: 'role:list',
|
||||
// isKeepAlive: true,
|
||||
// icon: 'el-icon-menu',
|
||||
// },
|
||||
// },
|
||||
// {
|
||||
// path: 'sys/accounts',
|
||||
// name: 'ResourceList',
|
||||
// component: () => import('@/views/system/account'),
|
||||
// meta: {
|
||||
// title: '账号管理',
|
||||
// code: 'account:list',
|
||||
// isKeepAlive: true,
|
||||
// icon: 'el-icon-menu',
|
||||
// },
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// {
|
||||
// path: '/machine',
|
||||
// name: 'Machine',
|
||||
// redirect: '/machine/list',
|
||||
// meta: {
|
||||
// title: '机器管理',
|
||||
// // 资源code,用于校验用户是否拥有该资源权限
|
||||
// code: 'machine',
|
||||
// // isKeepAlive: true,
|
||||
// icon: 'el-icon-monitor',
|
||||
// },
|
||||
// children: [
|
||||
// {
|
||||
// path: '/list',
|
||||
// name: 'MachineList',
|
||||
// component: () => import('@/views/ops/machine'),
|
||||
// meta: {
|
||||
// title: '机器列表',
|
||||
// code: 'machine:list',
|
||||
// isKeepAlive: true,
|
||||
// icon: 'el-icon-menu',
|
||||
// },
|
||||
// },
|
||||
// ],
|
||||
// },
|
||||
// {
|
||||
// path: '/personal',
|
||||
// name: 'personal',
|
||||
// component: () => import('@/views/personal/index.vue'),
|
||||
// meta: {
|
||||
// title: '个人中心',
|
||||
// isKeepAlive: true,
|
||||
// icon: 'el-icon-user',
|
||||
// },
|
||||
// },
|
||||
// ],
|
||||
},
|
||||
];
|
||||
|
||||
@@ -165,17 +154,6 @@ export const staticRoutes: Array<RouteRecordRaw> = [
|
||||
titleRename: true,
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/machine/terminal-rec',
|
||||
name: 'machineTerminalRec',
|
||||
component: () => import('@/views/ops/machine/MachineRec.vue'),
|
||||
meta: {
|
||||
// 将路径 'xxx?name=名字' 里的name字段值替换到title里
|
||||
title: '终端回放 | {name}',
|
||||
// 是否根据query对标题名进行参数替换,即最终显示为‘终端_机器名’
|
||||
titleRename: true,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
// 定义404界面
|
||||
|
||||
@@ -43,25 +43,25 @@ export default {
|
||||
const state = reactive({
|
||||
topCardItemList: [
|
||||
{
|
||||
title: '项目数',
|
||||
id: 'projectNum',
|
||||
color: '#FEBB50',
|
||||
},
|
||||
{
|
||||
title: 'Linux机器数',
|
||||
title: 'Linux机器',
|
||||
id: 'machineNum',
|
||||
color: '#F95959',
|
||||
},
|
||||
{
|
||||
title: '数据库总数',
|
||||
title: '数据库',
|
||||
id: 'dbNum',
|
||||
color: '#8595F4',
|
||||
},
|
||||
{
|
||||
title: 'redis总数',
|
||||
title: 'redis',
|
||||
id: 'redisNum',
|
||||
color: '#1abc9c',
|
||||
},
|
||||
{
|
||||
title: 'Mongo',
|
||||
id: 'mongoNum',
|
||||
color: '#FEBB50',
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
@@ -74,7 +74,7 @@ export default {
|
||||
const initNumCountUp = async () => {
|
||||
const res: any = await indexApi.getIndexCount.request();
|
||||
nextTick(() => {
|
||||
new CountUp('projectNum', res.projectNum).start();
|
||||
new CountUp('mongoNum', res.mongoNum).start();
|
||||
new CountUp('machineNum', res.machineNum).start();
|
||||
new CountUp('dbNum', res.dbNum).start();
|
||||
new CountUp('redisNum', res.redisNum).start();
|
||||
@@ -87,8 +87,8 @@ export default {
|
||||
router.push('/personal');
|
||||
break;
|
||||
}
|
||||
case 'projectNum': {
|
||||
router.push('/project/projects');
|
||||
case 'mongoNum': {
|
||||
router.push('/mongo/mongo-data-operation');
|
||||
break;
|
||||
}
|
||||
case 'machineNum': {
|
||||
|
||||
81
mayfly_go_web/src/views/ops/component/TagSelect.vue
Normal file
81
mayfly_go_web/src/views/ops/component/TagSelect.vue
Normal file
@@ -0,0 +1,81 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-tree-select
|
||||
@check="changeTag"
|
||||
style="width: 100%"
|
||||
v-model="selectTags"
|
||||
:data="tags"
|
||||
:render-after-expand="true"
|
||||
:default-expanded-keys="[selectTags]"
|
||||
show-checkbox
|
||||
check-strictly
|
||||
node-key="id"
|
||||
:props="{
|
||||
value: 'id',
|
||||
label: 'codePath',
|
||||
children: 'children',
|
||||
}"
|
||||
>
|
||||
<template #default="{ data }">
|
||||
<span class="custom-tree-node">
|
||||
<span style="font-size: 13px">
|
||||
{{ 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-select>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, defineComponent, onMounted } from 'vue';
|
||||
import { tagApi } from '../tag/api';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'TagSelect',
|
||||
props: {
|
||||
tagId: {
|
||||
type: Number,
|
||||
},
|
||||
tagPath: {
|
||||
type: String,
|
||||
},
|
||||
},
|
||||
setup(props: any, { emit }) {
|
||||
const state = reactive({
|
||||
tags: [],
|
||||
// 单选则为id,多选为id数组
|
||||
selectTags: null as any,
|
||||
});
|
||||
|
||||
onMounted(async () => {
|
||||
if (props.tagId) {
|
||||
state.selectTags = props.tagId;
|
||||
}
|
||||
state.tags = await tagApi.getTagTrees.request(null);
|
||||
});
|
||||
|
||||
const changeTag = (tag: any, checkInfo: any) => {
|
||||
if (checkInfo.checkedNodes.length > 0) {
|
||||
emit('update:tagId', tag.id);
|
||||
emit('update:tagPath', tag.codePath);
|
||||
emit('changeTag', tag);
|
||||
} else {
|
||||
emit('update:tagId', null);
|
||||
emit('update:tagPath', null);
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
changeTag,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="scss">
|
||||
</style>
|
||||
@@ -2,17 +2,10 @@
|
||||
<div>
|
||||
<el-dialog :title="title" v-model="dialogVisible" :before-close="cancel" :close-on-click-modal="false" :destroy-on-close="true" width="38%">
|
||||
<el-form :model="form" ref="dbForm" :rules="rules" label-width="95px">
|
||||
<el-form-item prop="projectId" label="项目:" required>
|
||||
<el-select style="width: 100%" v-model="form.projectId" placeholder="请选择项目" @change="changeProject" filterable>
|
||||
<el-option v-for="item in projects" :key="item.id" :label="`${item.name} [${item.remark}]`" :value="item.id"> </el-option>
|
||||
</el-select>
|
||||
<el-form-item prop="tagId" label="标签:" required>
|
||||
<tag-select v-model:tag-id="form.tagId" v-model:tag-path="form.tagPath" style="width: 100%" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item prop="envId" label="环境:" required>
|
||||
<el-select @change="changeEnv" style="width: 100%" v-model="form.envId" placeholder="请选择环境">
|
||||
<el-option v-for="item in envs" :key="item.id" :label="`${item.name} [${item.remark}]`" :value="item.id"> </el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item prop="name" label="别名:" required>
|
||||
<el-input v-model.trim="form.name" placeholder="请输入数据库别名" auto-complete="off"></el-input>
|
||||
</el-form-item>
|
||||
@@ -76,6 +69,10 @@
|
||||
</el-col>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item prop="remark" label="备注:">
|
||||
<el-input v-model.trim="form.remark" auto-complete="off" type="textarea"></el-input>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item prop="enableSshTunnel" label="SSH隧道:">
|
||||
<el-col :span="3">
|
||||
<el-checkbox @change="getSshTunnelMachines" v-model="form.enableSshTunnel" :true-label="1" :false-label="-1"></el-checkbox>
|
||||
@@ -108,14 +105,17 @@
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, watch, defineComponent, ref } from 'vue';
|
||||
import { dbApi } from './api';
|
||||
import { projectApi } from '../project/api.ts';
|
||||
import { machineApi } from '../machine/api.ts';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { notBlank } from '@/common/assert';
|
||||
import { RsaEncrypt } from '@/common/rsa';
|
||||
import TagSelect from '../component/TagSelect.vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'DbEdit',
|
||||
components: {
|
||||
TagSelect,
|
||||
},
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
@@ -139,10 +139,14 @@ export default defineComponent({
|
||||
envs: [],
|
||||
allDatabases: [] as any,
|
||||
databaseList: [] as any,
|
||||
sshTunnelMachineList: [],
|
||||
sshTunnelMachineList: [] as any,
|
||||
form: {
|
||||
id: null,
|
||||
tagId: null as any,
|
||||
tagPath: null as any,
|
||||
type: null,
|
||||
name: null,
|
||||
host: '',
|
||||
port: 3306,
|
||||
username: null,
|
||||
password: null,
|
||||
@@ -152,6 +156,7 @@ export default defineComponent({
|
||||
projectId: null,
|
||||
envId: null,
|
||||
env: null,
|
||||
remark: '',
|
||||
enableSshTunnel: null,
|
||||
sshTunnelMachineId: null,
|
||||
},
|
||||
@@ -218,7 +223,6 @@ export default defineComponent({
|
||||
}
|
||||
state.projects = newValue.projects;
|
||||
if (newValue.db) {
|
||||
getEnvs(newValue.db.projectId);
|
||||
state.form = { ...newValue.db };
|
||||
// 将数据库名使用空格切割,获取所有数据库列表
|
||||
state.databaseList = newValue.db.database.split(' ');
|
||||
@@ -244,22 +248,6 @@ export default defineComponent({
|
||||
}
|
||||
};
|
||||
|
||||
const getEnvs = async (projectId: any) => {
|
||||
state.envs = await projectApi.projectEnvs.request({ projectId });
|
||||
};
|
||||
|
||||
const changeProject = (projectId: number) => {
|
||||
for (let p of state.projects as any) {
|
||||
if (p.id == projectId) {
|
||||
state.form.project = p.name;
|
||||
}
|
||||
}
|
||||
state.form.envId = null;
|
||||
state.form.env = null;
|
||||
state.envs = [];
|
||||
getEnvs(projectId);
|
||||
};
|
||||
|
||||
const changeEnv = (envId: number) => {
|
||||
for (let p of state.envs as any) {
|
||||
if (p.id == envId) {
|
||||
@@ -272,7 +260,7 @@ export default defineComponent({
|
||||
const reqForm = { ...state.form };
|
||||
reqForm.password = await RsaEncrypt(reqForm.password);
|
||||
state.allDatabases = await dbApi.getAllDatabase.request(reqForm);
|
||||
ElMessage.success('获取成功, 请选择需要管理操作的数据库')
|
||||
ElMessage.success('获取成功, 请选择需要管理操作的数据库');
|
||||
};
|
||||
|
||||
const getDbPwd = async () => {
|
||||
@@ -324,7 +312,6 @@ export default defineComponent({
|
||||
getDbPwd,
|
||||
changeDatabase,
|
||||
getSshTunnelMachines,
|
||||
changeProject,
|
||||
changeEnv,
|
||||
btnOk,
|
||||
cancel,
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
>删除</el-button
|
||||
>
|
||||
<div style="float: right">
|
||||
<el-select @focus="getProjects" v-model="query.projectId" placeholder="请选择项目" filterable clearable>
|
||||
<el-option v-for="item in projects" :key="item.id" :label="`${item.name} [${item.remark}]`" :value="item.id"> </el-option>
|
||||
<el-select @focus="getTags" v-model="query.tagPath" placeholder="请选择标签" filterable clearable>
|
||||
<el-option v-for="item in tags" :key="item" :label="item" :value="item"> </el-option>
|
||||
</el-select>
|
||||
<el-button v-waves type="primary" icon="search" @click="search()" class="ml5">查询</el-button>
|
||||
</div>
|
||||
@@ -20,8 +20,7 @@
|
||||
</el-radio>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="project" label="项目" min-width="100" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="env" label="环境" min-width="100"></el-table-column>
|
||||
<el-table-column prop="tagPath" label="标签路径" min-width="150" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="name" label="名称" min-width="160" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column min-width="170" label="host:port" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
@@ -42,13 +41,14 @@
|
||||
size="small"
|
||||
v-for="db in scope.row.dbs"
|
||||
:key="db"
|
||||
style="cursor: pointer; margin-left: 3px; margin-bottom: 3px;"
|
||||
style="cursor: pointer; margin-left: 3px; margin-bottom: 3px"
|
||||
>{{ db }}</el-tag
|
||||
>
|
||||
</el-popover>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="username" label="用户名" min-width="100"></el-table-column>
|
||||
<el-table-column prop="remark" label="备注" min-width="150" show-overflow-tooltip></el-table-column>
|
||||
|
||||
<el-table-column min-width="115" prop="creator" label="创建账号"></el-table-column>
|
||||
<el-table-column min-width="160" prop="createTime" label="创建时间" show-overflow-tooltip>
|
||||
@@ -190,7 +190,7 @@
|
||||
<el-table-column prop="creator" label="执行人" min-width="60" show-overflow-tooltip> </el-table-column>
|
||||
<el-table-column prop="createTime" label="执行时间" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
{{ $filters.dateFormat(scope.row.createTime) }}
|
||||
{{ dateFormat(scope.row.createTime) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="remark" label="备注" min-width="60" show-overflow-tooltip> </el-table-column>
|
||||
@@ -249,7 +249,6 @@
|
||||
|
||||
<db-edit
|
||||
@val-change="valChange"
|
||||
:projects="projects"
|
||||
:title="dbEditDialog.title"
|
||||
v-model:visible="dbEditDialog.visible"
|
||||
v-model:db="dbEditDialog.data"
|
||||
@@ -266,11 +265,12 @@ import DbEdit from './DbEdit.vue';
|
||||
import CreateTable from './CreateTable.vue';
|
||||
import { dbApi } from './api';
|
||||
import enums from './enums';
|
||||
import { projectApi } from '../project/api.ts';
|
||||
import SqlExecBox from './component/SqlExecBox.ts';
|
||||
import config from '@/common/config';
|
||||
import { getSession } from '@/common/utils/storage';
|
||||
import { isTrue } from '@/common/assert';
|
||||
import { tagApi } from '../tag/api.ts';
|
||||
import { dateFormat } from '@/common/utils/date';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'DbList',
|
||||
@@ -286,7 +286,7 @@ export default defineComponent({
|
||||
saveDb: 'db:save',
|
||||
delDb: 'db:del',
|
||||
},
|
||||
projects: [],
|
||||
tags: [],
|
||||
chooseId: null,
|
||||
/**
|
||||
* 选中的数据
|
||||
@@ -296,6 +296,7 @@ export default defineComponent({
|
||||
* 查询条件
|
||||
*/
|
||||
query: {
|
||||
tagPath: null,
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
},
|
||||
@@ -406,12 +407,11 @@ export default defineComponent({
|
||||
search();
|
||||
};
|
||||
|
||||
const getProjects = async () => {
|
||||
state.projects = await projectApi.accountProjects.request(null);
|
||||
const getTags = async () => {
|
||||
state.tags = await tagApi.getAccountTags.request(null);
|
||||
};
|
||||
|
||||
const editDb = async (isAdd = false) => {
|
||||
await getProjects();
|
||||
if (isAdd) {
|
||||
state.dbEditDialog.data = null;
|
||||
state.dbEditDialog.title = '新增数据库资源';
|
||||
@@ -616,7 +616,8 @@ export default defineComponent({
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
getProjects,
|
||||
dateFormat,
|
||||
getTags,
|
||||
filterTableInfos,
|
||||
enums,
|
||||
search,
|
||||
|
||||
@@ -3,46 +3,56 @@
|
||||
<div class="toolbar">
|
||||
<el-row type="flex" justify="space-between">
|
||||
<el-col :span="24">
|
||||
<project-env-select @changeProjectEnv="changeProjectEnv">
|
||||
<template #default>
|
||||
<el-form-item label="资源">
|
||||
<el-select v-model="dbId" placeholder="请选择资源实例" @change="changeDbInstance" filterable style="width: 150px">
|
||||
<el-option v-for="item in dbs" :key="item.id" :label="item.name" :value="item.id">
|
||||
<span style="float: left">{{ item.name }}</span>
|
||||
<span style="float: right; color: #8492a6; margin-left: 6px; font-size: 13px">{{
|
||||
`${item.host}:${item.port} ${item.type}`
|
||||
}}</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form class="search-form" label-position="right" :inline="true">
|
||||
<el-form-item label="标签">
|
||||
<el-select @change="changeTag" @focus="getTags" v-model="params.tagPath" placeholder="请选择标签" filterable style="width: 220px">
|
||||
<el-option v-for="item in tags" :key="item" :label="item" :value="item"> </el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="数据库">
|
||||
<el-select
|
||||
v-model="db"
|
||||
placeholder="请选择数据库"
|
||||
@change="changeDb"
|
||||
@clear="clearDb"
|
||||
clearable
|
||||
filterable
|
||||
style="width: 150px"
|
||||
<el-form-item label="资源">
|
||||
<el-select v-model="dbId" placeholder="请选择资源实例" @change="changeDbInstance" filterable style="width: 220px">
|
||||
<el-option v-for="item in dbs" :key="item.id" :label="`${item.name} [${item.tagPath}]`" :value="item.id">
|
||||
<span style="float: left">{{ `${item.name} [${item.tagPath}]` }}</span>
|
||||
<span style="float: rignt; color: #8492a6; margin-left: 10px; font-size: 13px">{{
|
||||
`${item.host}:${item.port} ${item.type}`
|
||||
}}</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="数据库">
|
||||
<el-select
|
||||
v-model="db"
|
||||
placeholder="请选择数据库"
|
||||
@change="changeDb"
|
||||
@clear="clearDb"
|
||||
clearable
|
||||
filterable
|
||||
style="width: 150px"
|
||||
>
|
||||
<el-option v-for="item in databaseList" :key="item" :label="item" :value="item"> </el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label-width="20" label="表">
|
||||
<el-select v-model="tableName" placeholder="选择表查看表数据" @change="changeTable" filterable style="width: 250px">
|
||||
<el-option
|
||||
v-for="item in tableMetadata"
|
||||
:key="item.tableName"
|
||||
:label="item.tableName + (item.tableComment != '' ? `【${item.tableComment}】` : '')"
|
||||
:value="item.tableName"
|
||||
>
|
||||
<el-option v-for="item in databaseList" :key="item" :label="item" :value="item"> </el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
|
||||
<el-form-item label-width="20" label="表">
|
||||
<el-select v-model="tableName" placeholder="选择表查看表数据" @change="changeTable" filterable style="width: 250px">
|
||||
<el-option
|
||||
v-for="item in tableMetadata"
|
||||
:key="item.tableName"
|
||||
:label="item.tableName + (item.tableComment != '' ? `【${item.tableComment}】` : '')"
|
||||
:value="item.tableName"
|
||||
>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- <project-env-select @changeTag="changeTag">
|
||||
<template #default>
|
||||
|
||||
</template>
|
||||
</project-env-select>
|
||||
</project-env-select> -->
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
@@ -316,17 +326,15 @@ import 'codemirror/addon/hint/sql-hint.js';
|
||||
import { format as sqlFormatter } from 'sql-formatter';
|
||||
import { notBlank, notEmpty, isTrue } from '@/common/assert';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import ProjectEnvSelect from '../component/ProjectEnvSelect.vue';
|
||||
import config from '@/common/config';
|
||||
import { getSession } from '@/common/utils/storage';
|
||||
import SqlExecBox from './component/SqlExecBox';
|
||||
import { dateStrFormat } from '@/common/utils/date.ts';
|
||||
import { tagApi } from '../tag/api.ts';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'SqlExec',
|
||||
components: {
|
||||
ProjectEnvSelect,
|
||||
},
|
||||
components: {},
|
||||
setup() {
|
||||
const codeTextarea: any = ref(null);
|
||||
const token = getSession('token');
|
||||
@@ -336,11 +344,12 @@ export default defineComponent({
|
||||
const state = reactive({
|
||||
token: token,
|
||||
defalutLimit: 20, // 默认查询数量
|
||||
dbs: [], // 数据库实例列表
|
||||
tags: [],
|
||||
dbs: [] as any, // 数据库实例列表
|
||||
databaseList: [], // 数据库实例拥有的数据库列表,1数据库实例 -> 多数据库
|
||||
db: '', // 当前操作的数据库
|
||||
dbType: '',
|
||||
tables: [],
|
||||
tables: [] as any,
|
||||
dbId: null, // 当前选中操作的数据库实例
|
||||
tableName: '',
|
||||
tableMetadata: [],
|
||||
@@ -349,7 +358,7 @@ export default defineComponent({
|
||||
activeName: 'Query',
|
||||
queryTabName: 'Query',
|
||||
nowTableName: '', // 当前表格数据操作的数据库表名,用于双击编辑表内容使用
|
||||
dataTabs: {}, // 点击表信息后执行结果数据展示tabs
|
||||
dataTabs: {} as any, // 点击表信息后执行结果数据展示tabs
|
||||
dataTabsTableHeight: 600,
|
||||
// 查询tab
|
||||
queryTab: {
|
||||
@@ -366,8 +375,8 @@ export default defineComponent({
|
||||
},
|
||||
params: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
envId: null,
|
||||
pageSize: 100,
|
||||
tagPath: null,
|
||||
},
|
||||
conditionDialog: {
|
||||
title: '',
|
||||
@@ -440,16 +449,17 @@ export default defineComponent({
|
||||
/**
|
||||
* 项目及环境更改后的回调事件
|
||||
*/
|
||||
const changeProjectEnv = (projectId: any, envId: any) => {
|
||||
const changeTag = () => {
|
||||
state.dbs = [];
|
||||
state.dbId = null;
|
||||
state.db = '';
|
||||
state.databaseList = [];
|
||||
clearDb();
|
||||
if (envId != null) {
|
||||
state.params.envId = envId;
|
||||
search();
|
||||
}
|
||||
search();
|
||||
};
|
||||
|
||||
const getTags = async () => {
|
||||
state.tags = await tagApi.getAccountTags.request(null);
|
||||
};
|
||||
|
||||
const onBeforeChange = (instance: any, changeObj: any) => {
|
||||
@@ -1221,8 +1231,9 @@ export default defineComponent({
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
getTags,
|
||||
codeTextarea,
|
||||
changeProjectEnv,
|
||||
changeTag,
|
||||
changeTable,
|
||||
cellClick,
|
||||
onRunSql,
|
||||
|
||||
@@ -2,17 +2,15 @@
|
||||
<div>
|
||||
<el-dialog :title="title" v-model="dialogVisible" :close-on-click-modal="false" :destroy-on-close="true" :before-close="cancel" width="38%">
|
||||
<el-form :model="form" ref="machineForm" :rules="rules" label-width="85px">
|
||||
<el-form-item prop="projectId" label="项目:" required>
|
||||
<el-select style="width: 100%" v-model="form.projectId" placeholder="请选择项目" @change="changeProject" filterable>
|
||||
<el-option v-for="item in projects" :key="item.id" :label="`${item.name} [${item.remark}]`" :value="item.id"> </el-option>
|
||||
</el-select>
|
||||
<el-form-item prop="tagId" label="标签:" required>
|
||||
<tag-select v-model:tag-id="form.tagId" v-model:tag-path="form.tagPath" style="width: 100%" />
|
||||
</el-form-item>
|
||||
<el-form-item prop="name" label="名称:" required>
|
||||
<el-input v-model.trim="form.name" placeholder="请输入机器别名" auto-complete="off"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="ip" label="ip:" required>
|
||||
<el-col :span="18">
|
||||
<el-input :disabled="form.id" v-model.trim="form.ip" placeholder="主机ip" auto-complete="off"></el-input>
|
||||
<el-input :disabled="form.id != 0" v-model.trim="form.ip" placeholder="主机ip" auto-complete="off"></el-input>
|
||||
</el-col>
|
||||
<el-col style="text-align: center" :span="1">:</el-col>
|
||||
<el-col :span="5">
|
||||
@@ -91,9 +89,13 @@ import { machineApi } from './api';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { notBlank } from '@/common/assert';
|
||||
import { RsaEncrypt } from '@/common/rsa';
|
||||
import TagSelect from '../component/TagSelect.vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'MachineEdit',
|
||||
components: {
|
||||
TagSelect,
|
||||
},
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
@@ -112,12 +114,15 @@ export default defineComponent({
|
||||
const machineForm: any = ref(null);
|
||||
const state = reactive({
|
||||
dialogVisible: false,
|
||||
projects: [],
|
||||
sshTunnelMachineList: [],
|
||||
projects: [] as any,
|
||||
sshTunnelMachineList: [] as any,
|
||||
tags: [],
|
||||
selectTags: [],
|
||||
form: {
|
||||
id: null,
|
||||
projectId: null,
|
||||
projectName: null,
|
||||
tagId: null as any,
|
||||
tagPath: '',
|
||||
ip: null,
|
||||
name: null,
|
||||
authMethod: 1,
|
||||
port: 22,
|
||||
@@ -131,17 +136,10 @@ export default defineComponent({
|
||||
pwd: '',
|
||||
btnLoading: false,
|
||||
rules: {
|
||||
projectId: [
|
||||
tagId: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择项目',
|
||||
trigger: ['change', 'blur'],
|
||||
},
|
||||
],
|
||||
envId: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择环境',
|
||||
message: '请选择标签',
|
||||
trigger: ['change', 'blur'],
|
||||
},
|
||||
],
|
||||
@@ -206,14 +204,6 @@ export default defineComponent({
|
||||
state.pwd = await machineApi.getMachinePwd.request({ id: state.form.id });
|
||||
};
|
||||
|
||||
const changeProject = (projectId: number) => {
|
||||
for (let p of state.projects as any) {
|
||||
if (p.id == projectId) {
|
||||
state.form.projectName = p.name;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const btnOk = async () => {
|
||||
if (!state.form.id) {
|
||||
notBlank(state.form.password, '新增操作,密码不可为空');
|
||||
@@ -258,7 +248,6 @@ export default defineComponent({
|
||||
machineForm,
|
||||
getSshTunnelMachines,
|
||||
getPwd,
|
||||
changeProject,
|
||||
btnOk,
|
||||
cancel,
|
||||
};
|
||||
|
||||
@@ -3,21 +3,15 @@
|
||||
<el-card>
|
||||
<div>
|
||||
<el-button v-auth="'machine:add'" type="primary" icon="plus" @click="openFormDialog(false)" plain>添加</el-button>
|
||||
<el-button
|
||||
v-auth="'machine:update'"
|
||||
type="primary"
|
||||
icon="edit"
|
||||
:disabled="currentId == null"
|
||||
@click="openFormDialog(currentData)"
|
||||
plain
|
||||
<el-button v-auth="'machine:update'" type="primary" icon="edit" :disabled="!currentId" @click="openFormDialog(currentData)" plain
|
||||
>编辑</el-button
|
||||
>
|
||||
<el-button v-auth="'machine:del'" :disabled="currentId == null" @click="deleteMachine(currentId)" type="danger" icon="delete"
|
||||
<el-button v-auth="'machine:del'" :disabled="!currentId" @click="deleteMachine(currentId)" type="danger" icon="delete"
|
||||
>删除</el-button
|
||||
>
|
||||
<div style="float: right">
|
||||
<el-select @focus="getProjects" v-model="params.projectId" placeholder="请选择项目" @clear="search" filterable clearable>
|
||||
<el-option v-for="item in projects" :key="item.id" :label="`${item.name} [${item.remark}]`" :value="item.id"> </el-option>
|
||||
<el-select @focus="getTags" v-model="params.tagPath" placeholder="请选择标签" @clear="search" filterable clearable>
|
||||
<el-option v-for="item in tags" :key="item" :label="item" :value="item"> </el-option>
|
||||
</el-select>
|
||||
<el-input
|
||||
class="ml5"
|
||||
@@ -66,11 +60,11 @@
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="username" label="用户名" min-width="90"></el-table-column>
|
||||
<el-table-column prop="projectName" label="项目" min-width="120"></el-table-column>
|
||||
<el-table-column prop="tagPath" label="标签路径" min-width="150" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="remark" label="备注" min-width="250" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="createTime" label="创建时间" min-width="165">
|
||||
<template #default="scope">
|
||||
{{ $filters.dateFormat(scope.row.createTime) }}
|
||||
{{ dateFormat(scope.row.createTime) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="creator" label="创建者" min-width="80"></el-table-column>
|
||||
@@ -170,7 +164,6 @@
|
||||
|
||||
<machine-edit
|
||||
:title="machineEditDialog.title"
|
||||
:projects="projects"
|
||||
v-model:visible="machineEditDialog.visible"
|
||||
v-model:machine="machineEditDialog.data"
|
||||
@valChange="submitSuccess"
|
||||
@@ -187,6 +180,8 @@
|
||||
:machineId="machineStatsDialog.machineId"
|
||||
:title="machineStatsDialog.title"
|
||||
></machine-stats>
|
||||
|
||||
<machine-rec v-model:visible="machineRecDialog.visible" :machineId="machineRecDialog.machineId" :title="machineRecDialog.title"></machine-rec>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -195,12 +190,14 @@ import { toRefs, reactive, onMounted, defineComponent } from 'vue';
|
||||
import { useRouter } from 'vue-router';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { machineApi } from './api';
|
||||
import { projectApi } from '../project/api.ts';
|
||||
import { tagApi } from '../tag/api.ts';
|
||||
import ServiceManage from './ServiceManage.vue';
|
||||
import FileManage from './FileManage.vue';
|
||||
import MachineEdit from './MachineEdit.vue';
|
||||
import ProcessList from './ProcessList.vue';
|
||||
import MachineStats from './MachineStats.vue';
|
||||
import MachineRec from './MachineRec.vue';
|
||||
import { dateFormat } from '@/common/utils/date';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'MachineList',
|
||||
@@ -210,17 +207,19 @@ export default defineComponent({
|
||||
FileManage,
|
||||
MachineEdit,
|
||||
MachineStats,
|
||||
MachineRec,
|
||||
},
|
||||
setup() {
|
||||
const router = useRouter();
|
||||
const state = reactive({
|
||||
projects: [],
|
||||
tags: [] as any,
|
||||
stats: '',
|
||||
params: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
ip: null,
|
||||
name: null,
|
||||
tagPath: null,
|
||||
},
|
||||
// 列表数据
|
||||
data: {
|
||||
@@ -228,7 +227,7 @@ export default defineComponent({
|
||||
total: 10,
|
||||
},
|
||||
// 当前选中数据id
|
||||
currentId: null,
|
||||
currentId: 0,
|
||||
currentData: null,
|
||||
serviceDialog: {
|
||||
visible: false,
|
||||
@@ -252,7 +251,7 @@ export default defineComponent({
|
||||
},
|
||||
machineEditDialog: {
|
||||
visible: false,
|
||||
data: null,
|
||||
data: null as any,
|
||||
title: '新增机器',
|
||||
},
|
||||
machineRecDialog: {
|
||||
@@ -296,12 +295,11 @@ export default defineComponent({
|
||||
search();
|
||||
};
|
||||
|
||||
const getProjects = async () => {
|
||||
state.projects = await projectApi.accountProjects.request(null);
|
||||
const getTags = async () => {
|
||||
state.tags = await tagApi.getAccountTags.request(null);
|
||||
};
|
||||
|
||||
const openFormDialog = async (machine: any) => {
|
||||
await getProjects();
|
||||
let dialogTitle;
|
||||
if (machine) {
|
||||
state.machineEditDialog.data = state.currentData as any;
|
||||
@@ -324,7 +322,7 @@ export default defineComponent({
|
||||
});
|
||||
await machineApi.del.request({ id });
|
||||
ElMessage.success('操作成功');
|
||||
state.currentId = null;
|
||||
state.currentId = 0;
|
||||
state.currentData = null;
|
||||
search();
|
||||
} catch (err) {}
|
||||
@@ -353,7 +351,7 @@ export default defineComponent({
|
||||
};
|
||||
|
||||
const submitSuccess = () => {
|
||||
state.currentId = null;
|
||||
state.currentId = 0;
|
||||
state.currentData = null;
|
||||
search();
|
||||
};
|
||||
@@ -380,20 +378,16 @@ export default defineComponent({
|
||||
};
|
||||
|
||||
const showRec = (row: any) => {
|
||||
const { href } = router.resolve({
|
||||
path: `/machine/terminal-rec`,
|
||||
query: {
|
||||
id: row.id,
|
||||
name: `${row.name}[${row.ip}]-终端回放记录`,
|
||||
},
|
||||
});
|
||||
window.open(href, '_blank');
|
||||
state.machineRecDialog.title = `${row.name}[${row.ip}]-终端回放记录`;
|
||||
state.machineRecDialog.machineId = row.id;
|
||||
state.machineRecDialog.visible = true;
|
||||
};
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
dateFormat,
|
||||
choose,
|
||||
getProjects,
|
||||
getTags,
|
||||
showTerminal,
|
||||
openFormDialog,
|
||||
deleteMachine,
|
||||
|
||||
@@ -1,30 +1,37 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="toolbar">
|
||||
<span style="dispaly: inline-block" class="ml10">{{ title }}</span>
|
||||
<el-divider direction="vertical" border-style="dashed" />
|
||||
<el-select @change="getUsers" v-model="operateDate" placeholder="操作日期" filterable>
|
||||
<el-option v-for="item in operateDates" :key="item" :label="item" :value="item"> </el-option>
|
||||
</el-select>
|
||||
<el-select class="ml10" @change="getRecs" filterable v-model="user" placeholder="请选择操作人">
|
||||
<el-option v-for="item in users" :key="item" :label="item" :value="item"> </el-option>
|
||||
</el-select>
|
||||
<el-select class="ml10" @change="playRec" filterable v-model="rec" placeholder="请选择操作记录">
|
||||
<el-option v-for="item in recs" :key="item" :label="item" :value="item"> </el-option>
|
||||
</el-select>
|
||||
<el-divider direction="vertical" border-style="dashed" />
|
||||
快捷键-> space[空格键]: 暂停/播放 | f: 全屏/取消全屏
|
||||
</div>
|
||||
<div ref="playerRef" id="rc-player"></div>
|
||||
<div id="terminalRecDialog">
|
||||
<el-dialog
|
||||
:title="title"
|
||||
v-model="dialogVisible"
|
||||
:before-close="handleClose"
|
||||
:close-on-click-modal="false"
|
||||
:destroy-on-close="true"
|
||||
width="70%"
|
||||
>
|
||||
<div class="toolbar">
|
||||
<el-select @change="getUsers" v-model="operateDate" placeholder="操作日期" filterable>
|
||||
<el-option v-for="item in operateDates" :key="item" :label="item" :value="item"> </el-option>
|
||||
</el-select>
|
||||
<el-select class="ml10" @change="getRecs" filterable v-model="user" placeholder="请选择操作人">
|
||||
<el-option v-for="item in users" :key="item" :label="item" :value="item"> </el-option>
|
||||
</el-select>
|
||||
<el-select class="ml10" @change="playRec" filterable v-model="rec" placeholder="请选择操作记录">
|
||||
<el-option v-for="item in recs" :key="item" :label="item" :value="item"> </el-option>
|
||||
</el-select>
|
||||
<el-divider direction="vertical" border-style="dashed" />
|
||||
快捷键-> space[空格键]: 暂停/播放
|
||||
</div>
|
||||
<div ref="playerRef" id="rc-player"></div>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, onMounted, ref, reactive, defineComponent } from 'vue';
|
||||
import { toRefs, watch, ref, reactive, defineComponent } from 'vue';
|
||||
import { machineApi } from './api';
|
||||
import * as AsciinemaPlayer from 'asciinema-player';
|
||||
import 'asciinema-player/dist/bundle/asciinema-player.css';
|
||||
import { useRoute } from 'vue-router';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'MachineRec',
|
||||
components: {},
|
||||
@@ -34,7 +41,6 @@ export default defineComponent({
|
||||
title: { type: String },
|
||||
},
|
||||
setup(props: any, context) {
|
||||
const route = useRoute();
|
||||
const playerRef = ref(null);
|
||||
const state = reactive({
|
||||
dialogVisible: false,
|
||||
@@ -48,10 +54,14 @@ export default defineComponent({
|
||||
rec: '',
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
state.machineId = Number.parseInt(route.query.id as string);
|
||||
state.title = route.query.name as string;
|
||||
getOperateDate();
|
||||
watch(props, async (newValue) => {
|
||||
const visible = newValue.visible;
|
||||
if (visible) {
|
||||
state.machineId = newValue.machineId;
|
||||
state.title = newValue.title;
|
||||
await getOperateDate();
|
||||
}
|
||||
state.dialogVisible = visible;
|
||||
});
|
||||
|
||||
const getOperateDate = async () => {
|
||||
@@ -118,3 +128,10 @@ export default defineComponent({
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="scss">
|
||||
#terminalRecDialog {
|
||||
.el-overlay .el-overlay-dialog .el-dialog .el-dialog__body {
|
||||
padding: 0px !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -3,36 +3,45 @@
|
||||
<div class="toolbar">
|
||||
<el-row type="flex" justify="space-between">
|
||||
<el-col :span="24">
|
||||
<project-env-select @changeProjectEnv="changeProjectEnv">
|
||||
<template #default>
|
||||
<el-form-item label="实例" label-width="40px">
|
||||
<el-select v-model="mongoId" placeholder="请选择mongo" @change="changeMongo">
|
||||
<el-option v-for="item in mongoList" :key="item.id" :label="item.name" :value="item.id">
|
||||
<span style="float: left">{{ item.name }}</span>
|
||||
<span style="float: right; color: #8492a6; margin-left: 6px; font-size: 13px">{{ ` [${item.uri}]` }}</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form class="search-form" label-position="right" :inline="true">
|
||||
<el-form-item label="标签">
|
||||
<el-select
|
||||
@change="changeTag"
|
||||
@focus="getTags"
|
||||
v-model="query.tagPath"
|
||||
placeholder="请选择标签"
|
||||
filterable
|
||||
style="width: 250px"
|
||||
>
|
||||
<el-option v-for="item in tags" :key="item" :label="item" :value="item"> </el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="实例" label-width="40px">
|
||||
<el-select v-model="mongoId" placeholder="请选择mongo" @change="changeMongo">
|
||||
<el-option v-for="item in mongoList" :key="item.id" :label="item.name" :value="item.id">
|
||||
<span style="float: left">{{ item.name }}</span>
|
||||
<span style="float: right; color: #8492a6; margin-left: 6px; font-size: 13px">{{ ` [${item.uri}]` }}</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="库" label-width="20px">
|
||||
<el-select v-model="database" placeholder="请选择库" @change="changeDatabase" filterable>
|
||||
<el-option v-for="item in databases" :key="item.Name" :label="item.Name" :value="item.Name">
|
||||
<span style="float: left">{{ item.Name }}</span>
|
||||
<span style="float: right; color: #8492a6; margin-left: 4px; font-size: 13px">{{
|
||||
` [${formatByteSize(item.SizeOnDisk)}]`
|
||||
}}</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="库" label-width="20px">
|
||||
<el-select v-model="database" placeholder="请选择库" @change="changeDatabase" filterable>
|
||||
<el-option v-for="item in databases" :key="item.Name" :label="item.Name" :value="item.Name">
|
||||
<span style="float: left">{{ item.Name }}</span>
|
||||
<span style="float: right; color: #8492a6; margin-left: 4px; font-size: 13px">{{
|
||||
` [${formatByteSize(item.SizeOnDisk)}]`
|
||||
}}</span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item label="集合" label-width="40px">
|
||||
<el-select v-model="collection" placeholder="请选择集合" @change="changeCollection" filterable>
|
||||
<el-option v-for="item in collections" :key="item" :label="item" :value="item">
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</project-env-select>
|
||||
<el-form-item label="集合" label-width="40px">
|
||||
<el-select v-model="collection" placeholder="请选择集合" @change="changeCollection" filterable>
|
||||
<el-option v-for="item in collections" :key="item" :label="item" :value="item"> </el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
@@ -64,12 +73,7 @@
|
||||
|
||||
<el-divider direction="vertical" border-style="dashed" />
|
||||
|
||||
<el-link
|
||||
@click="onSaveDoc(item.value)"
|
||||
:underline="false"
|
||||
type="warning"
|
||||
icon="DocumentChecked"
|
||||
></el-link>
|
||||
<el-link @click="onSaveDoc(item.value)" :underline="false" type="warning" icon="DocumentChecked"></el-link>
|
||||
|
||||
<el-divider direction="vertical" border-style="dashed" />
|
||||
|
||||
@@ -132,36 +136,38 @@
|
||||
import { mongoApi } from './api';
|
||||
import { toRefs, ref, reactive, defineComponent } from 'vue';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import ProjectEnvSelect from '../component/ProjectEnvSelect.vue';
|
||||
|
||||
import { isTrue, notBlank, notNull } from '@/common/assert';
|
||||
import { formatByteSize } from '@/common/utils/format';
|
||||
import JsonEdit from '@/components/jsonedit/index.vue';
|
||||
import { tagApi } from '../tag/api.ts';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'MongoDataOp',
|
||||
components: {
|
||||
ProjectEnvSelect,
|
||||
JsonEdit,
|
||||
},
|
||||
setup() {
|
||||
const findParamInputRef: any = ref(null);
|
||||
const state = reactive({
|
||||
loading: false,
|
||||
mongoList: [],
|
||||
tags: [],
|
||||
mongoList: [] as any,
|
||||
query: {
|
||||
envId: 0,
|
||||
tagPath: null,
|
||||
},
|
||||
mongoId: null, // 当前选择操作的mongo
|
||||
database: '', // 当前选择操作的库
|
||||
collection: '', //当前选中的collection
|
||||
activeName: '', // 当前操作的tab
|
||||
databases: [],
|
||||
collections: [],
|
||||
dataTabs: {}, // 数据tabs
|
||||
databases: [] as any,
|
||||
collections: [] as any,
|
||||
dataTabs: {} as any, // 数据tabs
|
||||
findDialog: {
|
||||
visible: false,
|
||||
findParam: {
|
||||
limit: 0,
|
||||
skip: 0,
|
||||
filter: '',
|
||||
sort: '',
|
||||
},
|
||||
@@ -178,24 +184,27 @@ export default defineComponent({
|
||||
});
|
||||
|
||||
const searchMongo = async () => {
|
||||
notNull(state.query.envId, '请先选择项目环境');
|
||||
notNull(state.query.tagPath, '请先选择标签');
|
||||
const res = await mongoApi.mongoList.request(state.query);
|
||||
state.mongoList = res.list;
|
||||
};
|
||||
|
||||
const changeProjectEnv = (projectId: any, envId: any) => {
|
||||
const changeTag = (tagPath: string) => {
|
||||
state.databases = [];
|
||||
state.collections = [];
|
||||
state.mongoId = null;
|
||||
state.collection = '';
|
||||
state.database = '';
|
||||
state.dataTabs = {};
|
||||
if (envId != null) {
|
||||
state.query.envId = envId;
|
||||
if (tagPath != null) {
|
||||
searchMongo();
|
||||
}
|
||||
};
|
||||
|
||||
const getTags = async () => {
|
||||
state.tags = await tagApi.getAccountTags.request(null);
|
||||
};
|
||||
|
||||
const changeMongo = () => {
|
||||
state.databases = [];
|
||||
state.collections = [];
|
||||
@@ -419,7 +428,8 @@ export default defineComponent({
|
||||
return {
|
||||
...toRefs(state),
|
||||
findParamInputRef,
|
||||
changeProjectEnv,
|
||||
getTags,
|
||||
changeTag,
|
||||
changeMongo,
|
||||
changeDatabase,
|
||||
changeCollection,
|
||||
|
||||
@@ -2,17 +2,10 @@
|
||||
<div>
|
||||
<el-dialog :title="title" v-model="dialogVisible" :before-close="cancel" :close-on-click-modal="false" width="38%" :destroy-on-close="true">
|
||||
<el-form :model="form" ref="mongoForm" :rules="rules" label-width="85px">
|
||||
<el-form-item prop="projectId" label="项目" required>
|
||||
<el-select style="width: 100%" v-model="form.projectId" placeholder="请选择项目" @change="changeProject" filterable>
|
||||
<el-option v-for="item in projects" :key="item.id" :label="`${item.name} [${item.remark}]`" :value="item.id"> </el-option>
|
||||
</el-select>
|
||||
<el-form-item prop="tagId" label="标签:" required>
|
||||
<tag-select v-model:tag-id="form.tagId" v-model:tag-path="form.tagPath" style="width: 100%" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item prop="envId" label="环境" required>
|
||||
<el-select @change="changeEnv" style="width: 100%" v-model="form.envId" placeholder="请选择环境">
|
||||
<el-option v-for="item in envs" :key="item.id" :label="`${item.name} [${item.remark}]`" :value="item.id"> </el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item prop="name" label="名称" required>
|
||||
<el-input v-model.trim="form.name" placeholder="请输入名称" auto-complete="off"></el-input>
|
||||
</el-form-item>
|
||||
@@ -58,19 +51,19 @@
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, watch, defineComponent, ref } from 'vue';
|
||||
import { mongoApi } from './api';
|
||||
import { projectApi } from '../project/api.ts';
|
||||
import { machineApi } from '../machine/api.ts';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import TagSelect from '../component/TagSelect.vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'MongoEdit',
|
||||
components: {
|
||||
TagSelect,
|
||||
},
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
},
|
||||
projects: {
|
||||
type: Array,
|
||||
},
|
||||
mongo: {
|
||||
type: [Boolean, Object],
|
||||
},
|
||||
@@ -82,33 +75,22 @@ export default defineComponent({
|
||||
const mongoForm: any = ref(null);
|
||||
const state = reactive({
|
||||
dialogVisible: false,
|
||||
projects: [],
|
||||
envs: [],
|
||||
sshTunnelMachineList: [],
|
||||
sshTunnelMachineList: [] as any,
|
||||
form: {
|
||||
id: null,
|
||||
name: null,
|
||||
uri: null,
|
||||
enableSshTunnel: -1,
|
||||
sshTunnelMachineId: null,
|
||||
project: null,
|
||||
projectId: null,
|
||||
envId: null,
|
||||
env: null,
|
||||
tagId: null as any,
|
||||
tagPath: null as any,
|
||||
},
|
||||
btnLoading: false,
|
||||
rules: {
|
||||
projectId: [
|
||||
tagId: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择项目',
|
||||
trigger: ['change', 'blur'],
|
||||
},
|
||||
],
|
||||
envId: [
|
||||
{
|
||||
required: true,
|
||||
message: '请选择环境',
|
||||
message: '请选择标签',
|
||||
trigger: ['change', 'blur'],
|
||||
},
|
||||
],
|
||||
@@ -134,12 +116,9 @@ export default defineComponent({
|
||||
if (!state.dialogVisible) {
|
||||
return;
|
||||
}
|
||||
state.projects = newValue.projects;
|
||||
if (newValue.mongo) {
|
||||
getEnvs(newValue.mongo.projectId);
|
||||
state.form = { ...newValue.mongo };
|
||||
} else {
|
||||
state.envs = [];
|
||||
state.form = { db: 0 } as any;
|
||||
}
|
||||
getSshTunnelMachines();
|
||||
@@ -152,30 +131,6 @@ export default defineComponent({
|
||||
}
|
||||
};
|
||||
|
||||
const getEnvs = async (projectId: any) => {
|
||||
state.envs = await projectApi.projectEnvs.request({ projectId });
|
||||
};
|
||||
|
||||
const changeProject = (projectId: number) => {
|
||||
for (let p of state.projects as any) {
|
||||
if (p.id == projectId) {
|
||||
state.form.project = p.name;
|
||||
}
|
||||
}
|
||||
state.form.envId = null;
|
||||
state.form.env = null;
|
||||
state.envs = [];
|
||||
getEnvs(projectId);
|
||||
};
|
||||
|
||||
const changeEnv = (envId: number) => {
|
||||
for (let p of state.envs as any) {
|
||||
if (p.id == envId) {
|
||||
state.form.env = p.name;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const btnOk = async () => {
|
||||
mongoForm.value.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
@@ -206,9 +161,7 @@ export default defineComponent({
|
||||
return {
|
||||
...toRefs(state),
|
||||
mongoForm,
|
||||
changeProject,
|
||||
getSshTunnelMachines,
|
||||
changeEnv,
|
||||
btnOk,
|
||||
cancel,
|
||||
};
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
<el-button type="primary" icon="edit" :disabled="currentId == null" @click="editMongo(false)" plain>编辑</el-button>
|
||||
<el-button type="danger" icon="delete" :disabled="currentId == null" @click="deleteMongo" plain>删除</el-button>
|
||||
<div style="float: right">
|
||||
<el-select @focus="getProjects" v-model="query.projectId" placeholder="请选择项目" filterable clearable>
|
||||
<el-option v-for="item in projects" :key="item.id" :label="`${item.name} [${item.remark}]`" :value="item.id"> </el-option>
|
||||
<el-select @focus="getTags" v-model="query.tagPath" placeholder="请选择标签" filterable clearable>
|
||||
<el-option v-for="item in tags" :key="item" :label="item" :value="item"> </el-option>
|
||||
</el-select>
|
||||
<el-button class="ml5" @click="search" type="success" icon="search"></el-button>
|
||||
</div>
|
||||
@@ -18,8 +18,7 @@
|
||||
</el-radio>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="project" label="项目" width></el-table-column>
|
||||
<el-table-column prop="env" label="环境" width></el-table-column>
|
||||
<el-table-column prop="tagPath" label="标签路径" min-width="150" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="name" label="名称" width></el-table-column>
|
||||
<el-table-column prop="uri" label="连接uri" min-width="150" show-overflow-tooltip>
|
||||
<template #default="scope">
|
||||
@@ -28,7 +27,7 @@
|
||||
</el-table-column>
|
||||
<el-table-column prop="createTime" label="创建时间" min-width="150">
|
||||
<template #default="scope">
|
||||
{{ $filters.dateFormat(scope.row.createTime) }}
|
||||
{{ dateFormat(scope.row.createTime) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="creator" label="创建人"></el-table-column>
|
||||
@@ -180,7 +179,6 @@
|
||||
|
||||
<mongo-edit
|
||||
@val-change="valChange"
|
||||
:projects="projects"
|
||||
:title="mongoEditDialog.title"
|
||||
v-model:visible="mongoEditDialog.visible"
|
||||
v-model:mongo="mongoEditDialog.data"
|
||||
@@ -192,9 +190,10 @@
|
||||
import { mongoApi } from './api';
|
||||
import { toRefs, reactive, defineComponent, onMounted } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { projectApi } from '../project/api.ts';
|
||||
import { tagApi } from '../tag/api.ts';
|
||||
import MongoEdit from './MongoEdit.vue';
|
||||
import { formatByteSize } from '@/common/utils/format';
|
||||
import { dateFormat } from '@/common/utils/date';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'MongoList',
|
||||
@@ -203,7 +202,7 @@ export default defineComponent({
|
||||
},
|
||||
setup() {
|
||||
const state = reactive({
|
||||
projects: [],
|
||||
tags: [],
|
||||
list: [],
|
||||
total: 0,
|
||||
currentId: null,
|
||||
@@ -211,12 +210,11 @@ export default defineComponent({
|
||||
query: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
prjectId: null,
|
||||
clusterId: null,
|
||||
tagPath: null,
|
||||
},
|
||||
mongoEditDialog: {
|
||||
visible: false,
|
||||
data: null,
|
||||
data: null as any,
|
||||
title: '新增mongo',
|
||||
},
|
||||
databaseDialog: {
|
||||
@@ -225,7 +223,7 @@ export default defineComponent({
|
||||
title: '',
|
||||
statsDialog: {
|
||||
visible: false,
|
||||
data: {},
|
||||
data: {} as any,
|
||||
title: '',
|
||||
},
|
||||
},
|
||||
@@ -236,7 +234,7 @@ export default defineComponent({
|
||||
title: '',
|
||||
statsDialog: {
|
||||
visible: false,
|
||||
data: {},
|
||||
data: {} as any,
|
||||
title: '',
|
||||
},
|
||||
},
|
||||
@@ -370,12 +368,11 @@ export default defineComponent({
|
||||
state.total = res.total;
|
||||
};
|
||||
|
||||
const getProjects = async () => {
|
||||
state.projects = await projectApi.accountProjects.request(null);
|
||||
const getTags = async () => {
|
||||
state.tags = await tagApi.getAccountTags.request(null);
|
||||
};
|
||||
|
||||
const editMongo = async (isAdd = false) => {
|
||||
await getProjects();
|
||||
if (isAdd) {
|
||||
state.mongoEditDialog.data = null;
|
||||
state.mongoEditDialog.title = '新增mongo';
|
||||
@@ -394,7 +391,8 @@ export default defineComponent({
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
getProjects,
|
||||
dateFormat,
|
||||
getTags,
|
||||
search,
|
||||
handlePageChange,
|
||||
choose,
|
||||
|
||||
@@ -1,443 +0,0 @@
|
||||
<template>
|
||||
<div class="project-list">
|
||||
<el-card>
|
||||
<div>
|
||||
<el-button @click="showAddProjectDialog" v-auth="permissions.saveProject" type="primary" icon="plus">添加</el-button>
|
||||
<el-button
|
||||
@click="showAddProjectDialog(chooseData)"
|
||||
v-auth="permissions.saveProject"
|
||||
:disabled="chooseId == null"
|
||||
type="primary"
|
||||
icon="edit"
|
||||
>编辑</el-button
|
||||
>
|
||||
<el-button @click="showMembers(chooseData)" :disabled="chooseId == null" type="success" icon="user">成员管理</el-button>
|
||||
|
||||
<el-button @click="showEnv(chooseData)" :disabled="chooseId == null" type="info" icon="setting">环境管理</el-button>
|
||||
|
||||
<el-button v-auth="permissions.delProject" @click="delProject" :disabled="chooseId == null" type="danger" icon="delete"
|
||||
>删除</el-button
|
||||
>
|
||||
|
||||
<div style="float: right">
|
||||
<el-input class="mr2" placeholder="请输入项目名!" style="width: 200px" v-model="query.name" @clear="search" clearable></el-input>
|
||||
<el-button @click="search" type="success" icon="search"></el-button>
|
||||
</div>
|
||||
</div>
|
||||
<el-table :data="projects" @current-change="choose" ref="table" style="width: 100%">
|
||||
<el-table-column label="选择" width="55px">
|
||||
<template #default="scope">
|
||||
<el-radio v-model="chooseId" :label="scope.row.id">
|
||||
<i></i>
|
||||
</el-radio>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="name" label="项目名"></el-table-column>
|
||||
<el-table-column prop="remark" label="描述" min-width="180px" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="createTime" label="创建时间">
|
||||
<template #default="scope">
|
||||
{{ $filters.dateFormat(scope.row.createTime) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="creator" label="创建者"> </el-table-column>
|
||||
<!-- <el-table-column label="查看更多" min-width="80px">
|
||||
<template #default="scope">
|
||||
<el-link @click.prevent="showMembers(scope.row)" type="success">成员</el-link>
|
||||
|
||||
<el-link class="ml5" @click.prevent="showEnv(scope.row)" type="info">环境</el-link>
|
||||
</template>
|
||||
</el-table-column> -->
|
||||
</el-table>
|
||||
<el-row style="margin-top: 20px" type="flex" justify="end">
|
||||
<el-pagination
|
||||
style="text-align: right"
|
||||
@current-change="handlePageChange"
|
||||
:total="total"
|
||||
layout="prev, pager, next, total, jumper"
|
||||
v-model:current-page="query.pageNum"
|
||||
:page-size="query.pageSize"
|
||||
></el-pagination>
|
||||
</el-row>
|
||||
</el-card>
|
||||
|
||||
<el-dialog width="400px" title="项目编辑" :before-close="cancelAddProject" v-model="addProjectDialog.visible">
|
||||
<el-form :model="addProjectDialog.form" label-width="70px">
|
||||
<el-form-item prop="name" label="项目名:" required>
|
||||
<el-input :disabled="addProjectDialog.form.id ? true : false" v-model="addProjectDialog.form.name" auto-complete="off"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="描述:">
|
||||
<el-input v-model="addProjectDialog.form.remark" auto-complete="off"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="cancelAddProject()">取 消</el-button>
|
||||
<el-button @click="addProject" type="primary">确 定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog width="500px" :title="showEnvDialog.title" v-model="showEnvDialog.visible">
|
||||
<div class="toolbar">
|
||||
<el-button @click="showAddEnvDialog" v-auth="permissions.saveMember" type="primary" icon="plus">添加</el-button>
|
||||
<el-button @click="deleteEnv" v-auth="permissions.delProject" :disabled="showEnvDialog.chooseId == null" type="danger" icon="delete"
|
||||
>删除</el-button
|
||||
>
|
||||
</div>
|
||||
<el-table @current-change="chooseEnv" border :data="showEnvDialog.envs">
|
||||
<el-table-column label="选择" width="50px">
|
||||
<template #default="scope">
|
||||
<el-radio v-model="showEnvDialog.chooseId" :label="scope.row.id">
|
||||
<i></i>
|
||||
</el-radio>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column property="name" label="环境名" width="125"></el-table-column>
|
||||
<el-table-column property="remark" label="描述" width="125"></el-table-column>
|
||||
<el-table-column property="createTime" label="创建时间">
|
||||
<template #default="scope">
|
||||
{{ $filters.dateFormat(scope.row.createTime) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
|
||||
<el-dialog width="400px" title="添加环境" :before-close="cancelAddEnv" v-model="showEnvDialog.addVisible">
|
||||
<el-form :model="showEnvDialog.envForm" label-width="70px">
|
||||
<el-form-item prop="name" label="环境名:" required>
|
||||
<el-input v-model="showEnvDialog.envForm.name" auto-complete="off"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="描述:">
|
||||
<el-input v-model="showEnvDialog.envForm.remark" auto-complete="off"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="cancelAddEnv()">取 消</el-button>
|
||||
<el-button v-auth="permissions.saveEnv" @click="addEnv" type="primary" :loading="btnLoading">确 定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog width="500px" :title="showMemDialog.title" v-model="showMemDialog.visible">
|
||||
<div class="toolbar">
|
||||
<el-button v-auth="permissions.saveMember" @click="showAddMemberDialog()" type="primary" icon="plus">添加</el-button>
|
||||
<el-button v-auth="permissions.delMember" @click="deleteMember" :disabled="showMemDialog.chooseId == null" type="danger" icon="delete"
|
||||
>移除</el-button
|
||||
>
|
||||
</div>
|
||||
<el-table @current-change="chooseMember" border :data="showMemDialog.members.list">
|
||||
<el-table-column label="选择" width="50px">
|
||||
<template #default="scope">
|
||||
<el-radio v-model="showMemDialog.chooseId" :label="scope.row.id">
|
||||
<i></i>
|
||||
</el-radio>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column property="username" label="账号" width="125"></el-table-column>
|
||||
<el-table-column property="createTime" label="加入时间">
|
||||
<template #default="scope">
|
||||
{{ $filters.dateFormat(scope.row.createTime) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column property="creator" label="分配者" width="125"></el-table-column>
|
||||
</el-table>
|
||||
<el-pagination
|
||||
@current-change="setMemebers"
|
||||
style="text-align: center"
|
||||
background
|
||||
layout="prev, pager, next, total, jumper"
|
||||
:total="showMemDialog.members.total"
|
||||
v-model:current-page="showMemDialog.query.pageNum"
|
||||
:page-size="showMemDialog.query.pageSize"
|
||||
/>
|
||||
|
||||
<el-dialog width="400px" title="添加成员" :before-close="cancelAddMember" v-model="showMemDialog.addVisible">
|
||||
<el-form :model="showMemDialog.memForm" label-width="70px">
|
||||
<el-form-item label="账号:">
|
||||
<el-select
|
||||
style="width: 100%"
|
||||
remote
|
||||
:remote-method="getAccount"
|
||||
v-model="showMemDialog.memForm.accountId"
|
||||
filterable
|
||||
placeholder="请输入账号模糊搜索并选择"
|
||||
>
|
||||
<el-option v-for="item in showMemDialog.accounts" :key="item.id" :label="item.username" :value="item.id"> </el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item label="描述:">
|
||||
<el-input v-model="showEnvDialog.envForm.remark" auto-complete="off"></el-input>
|
||||
</el-form-item> -->
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="cancelAddMember()">取 消</el-button>
|
||||
<el-button v-auth="permissions.saveMember" @click="addMember" type="primary" :loading="btnLoading">确 定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, reactive, onMounted, defineComponent } from 'vue';
|
||||
import { projectApi } from './api';
|
||||
import { accountApi } from '../../system/api';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { notEmpty, notNull } from '@/common/assert';
|
||||
export default defineComponent({
|
||||
name: 'ProjectList',
|
||||
components: {},
|
||||
setup() {
|
||||
const state = reactive({
|
||||
permissions: {
|
||||
saveProject: 'project:save',
|
||||
delProject: 'project:del',
|
||||
saveMember: 'project:member:add',
|
||||
delMember: 'project:member:del',
|
||||
saveEnv: 'project:env:add',
|
||||
},
|
||||
query: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
name: null,
|
||||
},
|
||||
total: 0,
|
||||
projects: [],
|
||||
btnLoading: false,
|
||||
chooseId: null as any,
|
||||
chooseData: null as any,
|
||||
addProjectDialog: {
|
||||
title: '新增项目',
|
||||
visible: false,
|
||||
form: { name: '', remark: '' },
|
||||
},
|
||||
showEnvDialog: {
|
||||
visible: false,
|
||||
chooseId: null,
|
||||
chooseData: null,
|
||||
envs: [],
|
||||
title: '',
|
||||
addVisible: false,
|
||||
envForm: {
|
||||
name: '',
|
||||
remark: '',
|
||||
projectId: 0,
|
||||
},
|
||||
},
|
||||
showMemDialog: {
|
||||
visible: false,
|
||||
chooseId: null,
|
||||
chooseData: null,
|
||||
query: {
|
||||
pageSize: 8,
|
||||
pageNum: 1,
|
||||
projectId: null,
|
||||
},
|
||||
members: {
|
||||
list: [],
|
||||
total: null,
|
||||
},
|
||||
title: '',
|
||||
addVisible: false,
|
||||
memForm: {},
|
||||
accounts: [],
|
||||
},
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
search();
|
||||
});
|
||||
|
||||
const search = async () => {
|
||||
let res = await projectApi.projects.request(state.query);
|
||||
state.projects = res.list;
|
||||
state.total = res.total;
|
||||
};
|
||||
|
||||
const handlePageChange = (curPage: number) => {
|
||||
state.query.pageNum = curPage;
|
||||
search();
|
||||
};
|
||||
|
||||
const showAddProjectDialog = (data: any) => {
|
||||
if (data) {
|
||||
state.addProjectDialog.form = { ...data };
|
||||
} else {
|
||||
state.addProjectDialog.form = {} as any;
|
||||
}
|
||||
state.addProjectDialog.visible = true;
|
||||
};
|
||||
|
||||
const cancelAddProject = () => {
|
||||
state.addProjectDialog.visible = false;
|
||||
state.addProjectDialog.form = {} as any;
|
||||
};
|
||||
|
||||
const addProject = async () => {
|
||||
const form = state.addProjectDialog.form as any;
|
||||
notEmpty(form.name, '项目名不能为空');
|
||||
notEmpty(form.remark, '项目描述不能为空');
|
||||
|
||||
await projectApi.saveProject.request(form);
|
||||
ElMessage.success('保存成功');
|
||||
search();
|
||||
cancelAddProject();
|
||||
};
|
||||
|
||||
const delProject = async () => {
|
||||
try {
|
||||
await ElMessageBox.confirm(`确定删除该项目?`, '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
});
|
||||
await projectApi.delProject.request({ id: state.chooseId });
|
||||
ElMessage.success('删除成功');
|
||||
state.chooseData = null;
|
||||
state.chooseId = null;
|
||||
search();
|
||||
} catch (err) {}
|
||||
};
|
||||
|
||||
const choose = (item: any) => {
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
state.chooseId = item.id;
|
||||
state.chooseData = item;
|
||||
};
|
||||
|
||||
const showMembers = async (project: any) => {
|
||||
state.showMemDialog.query.projectId = project.id;
|
||||
await setMemebers();
|
||||
state.showMemDialog.title = `${project.name}的成员信息`;
|
||||
state.showMemDialog.visible = true;
|
||||
};
|
||||
|
||||
/**
|
||||
* 选中成员
|
||||
*/
|
||||
const chooseMember = (item: any) => {
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
state.showMemDialog.chooseData = item;
|
||||
state.showMemDialog.chooseId = item.id;
|
||||
};
|
||||
|
||||
const deleteMember = async () => {
|
||||
notNull(state.showMemDialog.chooseData, '请选选择成员');
|
||||
await projectApi.deleteProjectMem.request(state.showMemDialog.chooseData);
|
||||
ElMessage.success('移除成功');
|
||||
// 重新赋值成员列表
|
||||
setMemebers();
|
||||
};
|
||||
|
||||
/**
|
||||
* 设置成员列表信息
|
||||
*/
|
||||
const setMemebers = async () => {
|
||||
const res = await projectApi.projectMems.request(state.showMemDialog.query);
|
||||
state.showMemDialog.members.list = res.list;
|
||||
state.showMemDialog.members.total = res.total;
|
||||
};
|
||||
|
||||
const showEnv = async (project: any) => {
|
||||
state.showEnvDialog.envs = await projectApi.projectEnvs.request({ projectId: project.id });
|
||||
state.showEnvDialog.title = `${project.name}的环境信息`;
|
||||
state.showEnvDialog.visible = true;
|
||||
};
|
||||
|
||||
const chooseEnv = (item: any) => {
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
state.showEnvDialog.chooseData = item;
|
||||
state.showEnvDialog.chooseId = item.id;
|
||||
};
|
||||
|
||||
const deleteEnv = async () => {
|
||||
notNull(state.showEnvDialog.chooseData, '请选选择环境');
|
||||
await projectApi.delProjectEnvs.request({ id: state.showEnvDialog.chooseId });
|
||||
ElMessage.success('删除成功');
|
||||
state.showEnvDialog.envs = await projectApi.projectEnvs.request({ projectId: state.chooseId });
|
||||
};
|
||||
|
||||
const showAddMemberDialog = () => {
|
||||
state.showMemDialog.addVisible = true;
|
||||
};
|
||||
|
||||
const addMember = async () => {
|
||||
const memForm = state.showMemDialog.memForm as any;
|
||||
memForm.projectId = state.chooseData.id;
|
||||
notEmpty(memForm.accountId, '请先选择账号');
|
||||
|
||||
await projectApi.saveProjectMem.request(memForm);
|
||||
ElMessage.success('保存成功');
|
||||
setMemebers();
|
||||
cancelAddMember();
|
||||
};
|
||||
|
||||
const cancelAddMember = () => {
|
||||
state.showMemDialog.memForm = {};
|
||||
state.showMemDialog.addVisible = false;
|
||||
state.showMemDialog.chooseData = null;
|
||||
state.showMemDialog.chooseId = null;
|
||||
};
|
||||
|
||||
const getAccount = (username: any) => {
|
||||
accountApi.list.request({ username }).then((res) => {
|
||||
state.showMemDialog.accounts = res.list;
|
||||
});
|
||||
};
|
||||
|
||||
const showAddEnvDialog = () => {
|
||||
state.showEnvDialog.addVisible = true;
|
||||
};
|
||||
|
||||
const addEnv = async () => {
|
||||
const envForm = state.showEnvDialog.envForm;
|
||||
envForm.projectId = state.chooseData.id;
|
||||
await projectApi.saveProjectEnv.request(envForm);
|
||||
ElMessage.success('保存成功');
|
||||
state.showEnvDialog.envs = await projectApi.projectEnvs.request({ projectId: envForm.projectId });
|
||||
cancelAddEnv();
|
||||
};
|
||||
|
||||
const cancelAddEnv = () => {
|
||||
state.showEnvDialog.envForm = {} as any;
|
||||
state.showEnvDialog.addVisible = false;
|
||||
};
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
search,
|
||||
handlePageChange,
|
||||
choose,
|
||||
showAddProjectDialog,
|
||||
addProject,
|
||||
delProject,
|
||||
cancelAddProject,
|
||||
showMembers,
|
||||
setMemebers,
|
||||
showEnv,
|
||||
deleteEnv,
|
||||
showAddMemberDialog,
|
||||
addMember,
|
||||
chooseMember,
|
||||
deleteMember,
|
||||
cancelAddMember,
|
||||
showAddEnvDialog,
|
||||
chooseEnv,
|
||||
addEnv,
|
||||
cancelAddEnv,
|
||||
getAccount,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="scss">
|
||||
</style>
|
||||
@@ -1,17 +0,0 @@
|
||||
import Api from '@/common/Api';
|
||||
|
||||
export const projectApi = {
|
||||
// 获取账号可访问的项目列表
|
||||
accountProjects: Api.create("/accounts/projects", 'get'),
|
||||
projects: Api.create("/projects", 'get'),
|
||||
saveProject: Api.create("/projects", 'post'),
|
||||
delProject: Api.create("/projects", 'delete'),
|
||||
// 获取项目下的环境信息
|
||||
projectEnvs: Api.create("/projects/{projectId}/envs", 'get'),
|
||||
delProjectEnvs: Api.create("/projects/envs", 'delete'),
|
||||
saveProjectEnv: Api.create("/projects/{projectId}/envs", 'post'),
|
||||
// 获取项目下的成员信息
|
||||
projectMems: Api.create("/projects/{projectId}/members", 'get'),
|
||||
saveProjectMem: Api.create("/projects/{projectId}/members", 'post'),
|
||||
deleteProjectMem: Api.create("/projects/{projectId}/members/{accountId}", 'delete'),
|
||||
}
|
||||
@@ -4,34 +4,57 @@
|
||||
<div style="float: left">
|
||||
<el-row type="flex" justify="space-between">
|
||||
<el-col :span="24">
|
||||
<project-env-select @changeProjectEnv="changeProjectEnv" @clear="clearRedis">
|
||||
<template #default>
|
||||
<el-form-item label="redis" label-width="40px">
|
||||
<el-select v-model="scanParam.id" placeholder="请选择redis" @change="changeRedis" @clear="clearRedis" clearable>
|
||||
<el-option v-for="item in redisList" :key="item.id" :label="item.host" :value="item.id"> </el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="库" label-width="20px">
|
||||
<el-select v-model="scanParam.db" @change="changeDb" placeholder="库" style="width: 85px">
|
||||
<el-option v-for="db in dbList" :key="db" :label="db" :value="db"> </el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</template>
|
||||
</project-env-select>
|
||||
<el-form class="search-form" label-position="right" :inline="true">
|
||||
<el-form-item label="标签">
|
||||
<el-select
|
||||
@change="changeTag"
|
||||
@focus="getTags"
|
||||
v-model="query.tagPath"
|
||||
placeholder="请选择标签"
|
||||
filterable
|
||||
style="width: 250px"
|
||||
>
|
||||
<el-option v-for="item in tags" :key="item" :label="item" :value="item"> </el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="redis" label-width="40px">
|
||||
<el-select
|
||||
v-model="scanParam.id"
|
||||
placeholder="请选择redis"
|
||||
@change="changeRedis"
|
||||
@clear="clearRedis"
|
||||
clearable
|
||||
style="width: 250px"
|
||||
>
|
||||
<el-option
|
||||
v-for="item in redisList"
|
||||
:key="item.id"
|
||||
:label="`${item.name ? item.name : ''} [${item.host}]`"
|
||||
:value="item.id"
|
||||
>
|
||||
</el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
<el-form-item label="库" label-width="20px">
|
||||
<el-select v-model="scanParam.db" @change="changeDb" placeholder="库" style="width: 85px">
|
||||
<el-option v-for="db in dbList" :key="db" :label="db" :value="db"> </el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
</el-col>
|
||||
<el-col class="mt10">
|
||||
<el-form class="search-form" label-position="right" :inline="true" label-width="60px">
|
||||
<el-form-item label="key" label-width="40px">
|
||||
<el-input
|
||||
placeholder="match 支持*模糊key"
|
||||
style="width: 240px"
|
||||
style="width: 250px"
|
||||
v-model="scanParam.match"
|
||||
@clear="clear()"
|
||||
clearable
|
||||
></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="count" label-width="60px">
|
||||
<el-input placeholder="count" style="width: 62px" v-model.number="scanParam.count"></el-input>
|
||||
<el-form-item label="count" label-width="40px">
|
||||
<el-input placeholder="count" style="width: 70px" v-model.number="scanParam.count"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item>
|
||||
<el-button @click="searchKey()" type="success" icon="search" plain></el-button>
|
||||
@@ -127,12 +150,12 @@
|
||||
import { redisApi } from './api';
|
||||
import { toRefs, reactive, defineComponent } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import ProjectEnvSelect from '../component/ProjectEnvSelect.vue';
|
||||
import HashValue from './HashValue.vue';
|
||||
import StringValue from './StringValue.vue';
|
||||
import SetValue from './SetValue.vue';
|
||||
import ListValue from './ListValue.vue';
|
||||
import { isTrue, notBlank, notNull } from '@/common/assert';
|
||||
import { tagApi } from '../tag/api.ts';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'DataOperation',
|
||||
@@ -141,15 +164,15 @@ export default defineComponent({
|
||||
HashValue,
|
||||
SetValue,
|
||||
ListValue,
|
||||
ProjectEnvSelect,
|
||||
},
|
||||
setup() {
|
||||
const state = reactive({
|
||||
loading: false,
|
||||
redisList: [],
|
||||
tags: [],
|
||||
redisList: [] as any,
|
||||
dbList: [],
|
||||
query: {
|
||||
envId: 0,
|
||||
tagPath: null,
|
||||
},
|
||||
scanParam: {
|
||||
id: null,
|
||||
@@ -185,19 +208,22 @@ export default defineComponent({
|
||||
});
|
||||
|
||||
const searchRedis = async () => {
|
||||
notNull(state.query.envId, '请先选择项目环境');
|
||||
notBlank(state.query.tagPath, '请先选择标签');
|
||||
const res = await redisApi.redisList.request(state.query);
|
||||
state.redisList = res.list;
|
||||
};
|
||||
|
||||
const changeProjectEnv = (projectId: any, envId: any) => {
|
||||
const changeTag = (tagPath: string) => {
|
||||
clearRedis();
|
||||
if (envId != null) {
|
||||
state.query.envId = envId;
|
||||
if (tagPath != null) {
|
||||
searchRedis();
|
||||
}
|
||||
};
|
||||
|
||||
const getTags = async () => {
|
||||
state.tags = await tagApi.getAccountTags.request(null);
|
||||
};
|
||||
|
||||
const changeRedis = (id: number) => {
|
||||
resetScanParam(id);
|
||||
state.scanParam.db = null;
|
||||
@@ -385,7 +411,8 @@ export default defineComponent({
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
changeProjectEnv,
|
||||
getTags,
|
||||
changeTag,
|
||||
changeRedis,
|
||||
changeDb,
|
||||
clearRedis,
|
||||
|
||||
@@ -2,16 +2,11 @@
|
||||
<div>
|
||||
<el-dialog :title="title" v-model="dialogVisible" :before-close="cancel" :close-on-click-modal="false" :destroy-on-close="true" width="38%">
|
||||
<el-form :model="form" ref="redisForm" :rules="rules" label-width="85px">
|
||||
<el-form-item prop="projectId" label="项目:" required>
|
||||
<el-select style="width: 100%" v-model="form.projectId" placeholder="请选择项目" @change="changeProject" filterable>
|
||||
<el-option v-for="item in projects" :key="item.id" :label="`${item.name} [${item.remark}]`" :value="item.id"> </el-option>
|
||||
</el-select>
|
||||
<el-form-item prop="tagId" label="标签:" required>
|
||||
<tag-select v-model:tag-id="form.tagId" v-model:tag-path="form.tagPath" style="width: 100%" />
|
||||
</el-form-item>
|
||||
|
||||
<el-form-item prop="envId" label="环境:" required>
|
||||
<el-select @change="changeEnv" style="width: 100%" v-model="form.envId" placeholder="请选择环境">
|
||||
<el-option v-for="item in envs" :key="item.id" :label="`${item.name} [${item.remark}]`" :value="item.id"> </el-option>
|
||||
</el-select>
|
||||
<el-form-item prop="name" label="名称:" required>
|
||||
<el-input v-model.trim="form.name" placeholder="请输入redis名称" auto-complete="off"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="mode" label="mode:" required>
|
||||
<el-select style="width: 100%" v-model="form.mode" placeholder="请选择模式">
|
||||
@@ -88,9 +83,13 @@ import { projectApi } from '../project/api.ts';
|
||||
import { machineApi } from '../machine/api.ts';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { RsaEncrypt } from '@/common/rsa';
|
||||
import TagSelect from '../component/TagSelect.vue';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'RedisEdit',
|
||||
components: {
|
||||
TagSelect,
|
||||
},
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
@@ -114,6 +113,8 @@ export default defineComponent({
|
||||
sshTunnelMachineList: [],
|
||||
form: {
|
||||
id: null,
|
||||
tagId: null as any,
|
||||
tatPath: null as any,
|
||||
name: null,
|
||||
mode: 'standalone',
|
||||
host: '',
|
||||
@@ -176,7 +177,6 @@ export default defineComponent({
|
||||
}
|
||||
state.projects = newValue.projects;
|
||||
if (newValue.redis) {
|
||||
getEnvs(newValue.redis.projectId);
|
||||
state.form = { ...newValue.redis };
|
||||
convertDb(state.form.db);
|
||||
} else {
|
||||
@@ -205,34 +205,10 @@ export default defineComponent({
|
||||
}
|
||||
};
|
||||
|
||||
const getEnvs = async (projectId: any) => {
|
||||
state.envs = await projectApi.projectEnvs.request({ projectId });
|
||||
};
|
||||
|
||||
const getPwd = async () => {
|
||||
state.pwd = await redisApi.getRedisPwd.request({ id: state.form.id });
|
||||
};
|
||||
|
||||
const changeProject = (projectId: number) => {
|
||||
for (let p of state.projects as any) {
|
||||
if (p.id == projectId) {
|
||||
state.form.project = p.name;
|
||||
}
|
||||
}
|
||||
state.form.envId = null;
|
||||
state.form.env = null;
|
||||
state.envs = [];
|
||||
getEnvs(projectId);
|
||||
};
|
||||
|
||||
const changeEnv = (envId: number) => {
|
||||
for (let p of state.envs as any) {
|
||||
if (p.id == envId) {
|
||||
state.form.env = p.name;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const btnOk = async () => {
|
||||
redisForm.value.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
@@ -270,8 +246,6 @@ export default defineComponent({
|
||||
changeDb,
|
||||
getSshTunnelMachines,
|
||||
getPwd,
|
||||
changeProject,
|
||||
changeEnv,
|
||||
btnOk,
|
||||
cancel,
|
||||
};
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
<el-button type="primary" icon="edit" :disabled="currentId == null" @click="editRedis(false)" plain>编辑</el-button>
|
||||
<el-button type="danger" icon="delete" :disabled="currentId == null" @click="deleteRedis" plain>删除</el-button>
|
||||
<div style="float: right">
|
||||
<el-select @focus="getProjects" v-model="query.projectId" placeholder="请选择项目" filterable clearable>
|
||||
<el-option v-for="item in projects" :key="item.id" :label="`${item.name} [${item.remark}]`" :value="item.id"> </el-option>
|
||||
<el-select @focus="getTags" v-model="query.tagPath" placeholder="请选择标签" filterable clearable>
|
||||
<el-option v-for="item in tags" :key="item" :label="item" :value="item"> </el-option>
|
||||
</el-select>
|
||||
<el-button class="ml5" @click="search" type="success" icon="search"></el-button>
|
||||
</div>
|
||||
@@ -18,14 +18,14 @@
|
||||
</el-radio>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="project" label="项目" min-width="100"></el-table-column>
|
||||
<el-table-column prop="env" label="环境" min-width="100"></el-table-column>
|
||||
<el-table-column prop="tagPath" label="标签路径" min-width="150" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="name" label="名称" min-width="100"></el-table-column>
|
||||
<el-table-column prop="host" label="host:port" min-width="150" show-overflow-tooltip> </el-table-column>
|
||||
<el-table-column prop="mode" label="mode" min-width="100"></el-table-column>
|
||||
<el-table-column prop="remark" label="备注" min-width="120" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="createTime" label="创建时间" min-width="160">
|
||||
<template #default="scope">
|
||||
{{ $filters.dateFormat(scope.row.createTime) }}
|
||||
{{ dateFormat(scope.row.createTime) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="creator" label="创建人" min-width="100"></el-table-column>
|
||||
@@ -138,7 +138,7 @@
|
||||
|
||||
<redis-edit
|
||||
@val-change="valChange"
|
||||
:projects="projects"
|
||||
:tags="tags"
|
||||
:title="redisEditDialog.title"
|
||||
v-model:visible="redisEditDialog.visible"
|
||||
v-model:redis="redisEditDialog.data"
|
||||
@@ -151,8 +151,9 @@ import Info from './Info.vue';
|
||||
import { redisApi } from './api';
|
||||
import { toRefs, reactive, defineComponent, onMounted } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { projectApi } from '../project/api.ts';
|
||||
import { tagApi } from '../tag/api.ts';
|
||||
import RedisEdit from './RedisEdit.vue';
|
||||
import { dateFormat } from '@/common/utils/date';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'RedisList',
|
||||
@@ -162,15 +163,15 @@ export default defineComponent({
|
||||
},
|
||||
setup() {
|
||||
const state = reactive({
|
||||
projects: [],
|
||||
tags: [],
|
||||
redisTable: [],
|
||||
total: 0,
|
||||
currentId: null,
|
||||
currentData: null,
|
||||
query: {
|
||||
tagPath: null,
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
prjectId: null,
|
||||
clusterId: null,
|
||||
},
|
||||
redisInfo: {
|
||||
@@ -263,12 +264,11 @@ export default defineComponent({
|
||||
state.total = res.total;
|
||||
};
|
||||
|
||||
const getProjects = async () => {
|
||||
state.projects = await projectApi.accountProjects.request(null);
|
||||
const getTags = async () => {
|
||||
state.tags = await tagApi.getAccountTags.request(null);
|
||||
};
|
||||
|
||||
const editRedis = async (isAdd = false) => {
|
||||
await getProjects();
|
||||
if (isAdd) {
|
||||
state.redisEditDialog.data = null;
|
||||
state.redisEditDialog.title = '新增redis';
|
||||
@@ -287,7 +287,8 @@ export default defineComponent({
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
getProjects,
|
||||
dateFormat,
|
||||
getTags,
|
||||
search,
|
||||
handlePageChange,
|
||||
choose,
|
||||
|
||||
300
mayfly_go_web/src/views/ops/tag/TagTreeList.vue
Normal file
300
mayfly_go_web/src/views/ops/tag/TagTreeList.vue
Normal file
@@ -0,0 +1,300 @@
|
||||
<template>
|
||||
<div class="menu">
|
||||
<div class="toolbar">
|
||||
<el-button v-auth="'tag:save'" type="primary" icon="plus" @click="showSaveTabDialog(null)">添加</el-button>
|
||||
<div style="float: right">
|
||||
<el-tooltip effect="dark" placement="top">
|
||||
<template #content>
|
||||
1. 用于将资产进行归类
|
||||
<br />2. 可在团队管理中进行分配,用于资源隔离
|
||||
<br />3. 父标签可访问及操作所有子标签关联的资源
|
||||
</template>
|
||||
<span>标签作用<el-icon><question-filled /></el-icon></span>
|
||||
</el-tooltip>
|
||||
</div>
|
||||
</div>
|
||||
<el-tree
|
||||
class="none-select"
|
||||
:indent="38"
|
||||
node-key="id"
|
||||
:props="props"
|
||||
:data="data"
|
||||
@node-expand="handleNodeExpand"
|
||||
@node-collapse="handleNodeCollapse"
|
||||
:default-expanded-keys="defaultExpandedKeys"
|
||||
:expand-on-click-node="false"
|
||||
>
|
||||
<template #default="{ data }">
|
||||
<span class="custom-tree-node">
|
||||
<span style="font-size: 13px">
|
||||
{{ 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>
|
||||
|
||||
<el-link @click.prevent="info(data)" style="margin-left: 25px" icon="view" type="info" :underline="false" />
|
||||
|
||||
<el-link v-auth="'tag:save'" @click.prevent="showEditTagDialog(data)" class="ml5" type="primary" icon="edit" :underline="false" />
|
||||
|
||||
<el-link
|
||||
v-auth="'tag:save'"
|
||||
@click.prevent="showSaveTabDialog(data)"
|
||||
icon="circle-plus"
|
||||
:underline="false"
|
||||
type="success"
|
||||
class="ml5"
|
||||
/>
|
||||
|
||||
<!-- <el-link
|
||||
v-auth="'resource:changeStatus'"
|
||||
@click.prevent="changeStatus(data, -1)"
|
||||
v-if="data.status === 1 && data.type === enums.ResourceTypeEnum.PERMISSION.value"
|
||||
icon="circle-close"
|
||||
:underline="false"
|
||||
type="warning"
|
||||
class="ml5"
|
||||
/>
|
||||
|
||||
<el-link
|
||||
v-auth="'resource:changeStatus'"
|
||||
@click.prevent="changeStatus(data, 1)"
|
||||
v-if="data.status === -1 && data.type === enums.ResourceTypeEnum.PERMISSION.value"
|
||||
type="success"
|
||||
icon="circle-check"
|
||||
:underline="false"
|
||||
plain
|
||||
class="ml5"
|
||||
/> -->
|
||||
|
||||
<el-link
|
||||
v-auth="'tag:del'"
|
||||
@click.prevent="deleteTag(data)"
|
||||
v-if="data.children == null"
|
||||
type="danger"
|
||||
icon="delete"
|
||||
:underline="false"
|
||||
plain
|
||||
class="ml5"
|
||||
/>
|
||||
</span>
|
||||
</template>
|
||||
</el-tree>
|
||||
|
||||
<el-dialog width="500px" :title="saveTabDialog.title" :before-close="cancelSaveTag" v-model="saveTabDialog.visible">
|
||||
<el-form ref="tagForm" :rules="rules" :model="saveTabDialog.form" label-width="70px">
|
||||
<el-form-item prop="code" label="标识:" required>
|
||||
<el-input :disabled="saveTabDialog.form.id ? true : false" v-model="saveTabDialog.form.code" auto-complete="off"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="name" label="名称:" required>
|
||||
<el-input v-model="saveTabDialog.form.name" auto-complete="off"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注:">
|
||||
<el-input v-model="saveTabDialog.form.remark" auto-complete="off"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="cancelSaveTag()">取 消</el-button>
|
||||
<el-button @click="saveTag" type="primary">确 定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog v-model="infoDialog.visible">
|
||||
<el-descriptions title="节点信息" :column="2" border>
|
||||
<el-descriptions-item label="code">{{ infoDialog.data.code }}</el-descriptions-item>
|
||||
<el-descriptions-item label="code路径">{{ infoDialog.data.codePath }}</el-descriptions-item>
|
||||
<el-descriptions-item label="名称">{{ infoDialog.data.name }}</el-descriptions-item>
|
||||
<el-descriptions-item label="备注">{{ infoDialog.data.remark }}</el-descriptions-item>
|
||||
|
||||
<el-descriptions-item label="创建者">{{ infoDialog.data.creator }}</el-descriptions-item>
|
||||
<el-descriptions-item label="创建时间">{{ dateFormat(infoDialog.data.createTime) }}</el-descriptions-item>
|
||||
<el-descriptions-item label="修改者">{{ infoDialog.data.modifier }}</el-descriptions-item>
|
||||
<el-descriptions-item label="更新时间">{{ dateFormat(infoDialog.data.updateTime) }}</el-descriptions-item>
|
||||
</el-descriptions>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { toRefs, ref, reactive, onMounted, defineComponent } from 'vue';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { tagApi } from './api';
|
||||
import { dateFormat } from '@/common/utils/date';
|
||||
|
||||
export default defineComponent({
|
||||
name: 'TagTreeList',
|
||||
components: {},
|
||||
setup() {
|
||||
const tagForm: any = ref(null);
|
||||
const state = reactive({
|
||||
saveTabDialog: {
|
||||
title: '新增标签',
|
||||
visible: false,
|
||||
form: { id: 0, pid: 0, code: '', name: '', remark: '' },
|
||||
},
|
||||
//资源信息弹出框对象
|
||||
infoDialog: {
|
||||
title: '',
|
||||
visible: false,
|
||||
// 资源类型选择是否选
|
||||
data: null as any,
|
||||
},
|
||||
data: [],
|
||||
props: {
|
||||
label: 'name',
|
||||
children: 'children',
|
||||
},
|
||||
// 展开的节点
|
||||
defaultExpandedKeys: [] as any[],
|
||||
rules: {
|
||||
code: [
|
||||
{ required: true, message: '标识符不能为空', trigger: 'blur' },
|
||||
// {
|
||||
// pattern: /^\w+$/g,
|
||||
// message: '标识符只能为空数字字母下划线等',
|
||||
// trigger: 'blur',
|
||||
// },
|
||||
],
|
||||
name: [{ required: true, message: '名称不能为空', trigger: 'blur' }],
|
||||
},
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
search();
|
||||
});
|
||||
|
||||
const search = async () => {
|
||||
let res = await tagApi.getTagTrees.request(null);
|
||||
state.data = res;
|
||||
};
|
||||
|
||||
const info = async (data: any) => {
|
||||
state.infoDialog.data = data;
|
||||
state.infoDialog.visible = true;
|
||||
};
|
||||
|
||||
const showSaveTabDialog = (data: any) => {
|
||||
if (data) {
|
||||
state.saveTabDialog.form.pid = data.id;
|
||||
state.saveTabDialog.title = `新增 [${data.codePath}] 子标签信息`;
|
||||
} else {
|
||||
state.saveTabDialog.title = '新增根标签信息';
|
||||
}
|
||||
state.saveTabDialog.visible = true;
|
||||
};
|
||||
|
||||
const showEditTagDialog = (data: any) => {
|
||||
state.saveTabDialog.form.id = data.id;
|
||||
state.saveTabDialog.form.code = data.code;
|
||||
state.saveTabDialog.form.name = data.name;
|
||||
state.saveTabDialog.form.remark = data.remark;
|
||||
state.saveTabDialog.title = `修改 [${data.codePath}] 信息`;
|
||||
state.saveTabDialog.visible = true;
|
||||
};
|
||||
|
||||
const saveTag = async () => {
|
||||
tagForm.value.validate(async (valid: any) => {
|
||||
if (valid) {
|
||||
const form = state.saveTabDialog.form;
|
||||
await tagApi.saveTagTree.request(form);
|
||||
ElMessage.success('保存成功');
|
||||
search();
|
||||
cancelSaveTag();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const cancelSaveTag = () => {
|
||||
state.saveTabDialog.visible = false;
|
||||
state.saveTabDialog.form = {} as any;
|
||||
tagForm.value.resetFields();
|
||||
};
|
||||
|
||||
const deleteTag = (data: any) => {
|
||||
ElMessageBox.confirm(`此操作将删除 [${data.codePath}], 是否继续?`, '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}).then(async () => {
|
||||
await tagApi.delTagTree.request({ id: data.id });
|
||||
ElMessage.success('删除成功!');
|
||||
search();
|
||||
});
|
||||
};
|
||||
|
||||
// const changeStatus = async (data: any, status: any) => {
|
||||
// await resourceApi.changeStatus.request({
|
||||
// id: data.id,
|
||||
// status: status,
|
||||
// });
|
||||
// data.status = status;
|
||||
// ElMessage.success((status === 1 ? '启用' : '禁用') + '成功!');
|
||||
// };
|
||||
|
||||
// 节点被展开时触发的事件
|
||||
const handleNodeExpand = (data: any, node: any) => {
|
||||
const id: any = node.data.id;
|
||||
if (!state.defaultExpandedKeys.includes(id)) {
|
||||
state.defaultExpandedKeys.push(id);
|
||||
}
|
||||
};
|
||||
|
||||
// 关闭节点
|
||||
const handleNodeCollapse = (data: any, node: any) => {
|
||||
removeDeafultExpandId(node.data.id);
|
||||
|
||||
let childNodes = node.childNodes;
|
||||
for (let cn of childNodes) {
|
||||
if (cn.expanded) {
|
||||
removeDeafultExpandId(cn.data.id);
|
||||
}
|
||||
// 递归删除展开的子节点节点id
|
||||
handleNodeCollapse(data, cn);
|
||||
}
|
||||
};
|
||||
|
||||
const removeDeafultExpandId = (id: any) => {
|
||||
let index = state.defaultExpandedKeys.indexOf(id);
|
||||
if (index > -1) {
|
||||
state.defaultExpandedKeys.splice(index, 1);
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
dateFormat,
|
||||
tagForm,
|
||||
info,
|
||||
saveTag,
|
||||
showSaveTabDialog,
|
||||
showEditTagDialog,
|
||||
cancelSaveTag,
|
||||
deleteTag,
|
||||
handleNodeExpand,
|
||||
handleNodeCollapse,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="scss">
|
||||
.menu {
|
||||
height: 100%;
|
||||
.el-tree-node__content {
|
||||
height: 40px;
|
||||
line-height: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
.none-select {
|
||||
moz-user-select: -moz-none;
|
||||
-moz-user-select: none;
|
||||
-o-user-select: none;
|
||||
-khtml-user-select: none;
|
||||
-webkit-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
</style>
|
||||
440
mayfly_go_web/src/views/ops/tag/TeamList.vue
Executable file
440
mayfly_go_web/src/views/ops/tag/TeamList.vue
Executable file
@@ -0,0 +1,440 @@
|
||||
<template>
|
||||
<div class="role-list">
|
||||
<el-card>
|
||||
<el-button v-auth="'team:save'" type="primary" icon="plus" @click="showSaveTeamDialog(false)">添加</el-button>
|
||||
<el-button v-auth="'team:save'" :disabled="chooseId == null" @click="showSaveTeamDialog(chooseData)" type="primary" icon="edit"
|
||||
>编辑</el-button
|
||||
>
|
||||
<el-button v-auth="'team:del'" :disabled="chooseId == null" @click="deleteTeam(chooseData)" type="danger" icon="delete">删除</el-button>
|
||||
|
||||
<div style="float: right">
|
||||
<el-input placeholder="请输入团队名称" class="mr2" style="width: 200px" v-model="query.name" @clear="search" clearable></el-input>
|
||||
<el-button @click="search" type="success" icon="search"></el-button>
|
||||
</div>
|
||||
<el-table :data="data" @current-change="choose" ref="table" style="width: 100%">
|
||||
<el-table-column label="选择" width="55px">
|
||||
<template #default="scope">
|
||||
<el-radio v-model="chooseId" :label="scope.row.id">
|
||||
<i></i>
|
||||
</el-radio>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="name" label="团队名称"></el-table-column>
|
||||
<el-table-column prop="remark" label="备注" min-width="160px" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="createTime" label="创建时间">
|
||||
<template #default="scope">
|
||||
{{ dateFormat(scope.row.createTime) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="creator" label="创建者"> </el-table-column>
|
||||
<el-table-column label="操作" min-width="80px">
|
||||
<template #default="scope">
|
||||
<el-link @click.prevent="showMembers(scope.row)" :underline="false" type="primary">成员</el-link>
|
||||
<el-divider direction="vertical" border-style="dashed" />
|
||||
<el-link @click.prevent="showTags(scope.row)" :underline="false" type="success">标签</el-link>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-row style="margin-top: 20px" type="flex" justify="end">
|
||||
<el-pagination
|
||||
style="text-align: right"
|
||||
@current-change="handlePageChange"
|
||||
:total="total"
|
||||
layout="prev, pager, next, total, jumper"
|
||||
v-model:current-page="query.pageNum"
|
||||
:page-size="query.pageSize"
|
||||
></el-pagination>
|
||||
</el-row>
|
||||
</el-card>
|
||||
|
||||
<el-dialog width="400px" title="团队编辑" :before-close="cancelSaveTeam" v-model="addTeamDialog.visible">
|
||||
<el-form ref="teamForm" :model="addTeamDialog.form" label-width="70px">
|
||||
<el-form-item prop="name" label="团队名:" required>
|
||||
<el-input v-model="addTeamDialog.form.name" auto-complete="off"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item label="备注:">
|
||||
<el-input v-model="addTeamDialog.form.remark" auto-complete="off"></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="cancelSaveTeam()">取 消</el-button>
|
||||
<el-button @click="saveTeam" type="primary">确 定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog width="500px" :title="showTagDialog.title" :before-close="closeTagDialog" v-model="showTagDialog.visible">
|
||||
<el-form label-width="70px">
|
||||
<el-form-item prop="project" label="标签:">
|
||||
<el-tree-select
|
||||
ref="tagTreeRef"
|
||||
style="width: 100%"
|
||||
v-model="showTagDialog.tagTreeTeams"
|
||||
:data="showTagDialog.tags"
|
||||
:default-expanded-keys="showTagDialog.tagTreeTeams"
|
||||
multiple
|
||||
:render-after-expand="true"
|
||||
show-checkbox
|
||||
check-strictly
|
||||
node-key="id"
|
||||
:props="showTagDialog.props"
|
||||
@check="tagTreeNodeCheck"
|
||||
>
|
||||
<template #default="{ data }">
|
||||
<span class="custom-tree-node">
|
||||
<span style="font-size: 13px">
|
||||
{{ 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-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="closeTagDialog()">取 消</el-button>
|
||||
<el-button v-auth="'team:tag:save'" @click="saveTags()" type="primary">确 定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<el-dialog width="600px" :title="showMemDialog.title" v-model="showMemDialog.visible">
|
||||
<div class="toolbar">
|
||||
<el-button v-auth="'team:member:save'" @click="showAddMemberDialog()" type="primary" icon="plus">添加</el-button>
|
||||
<el-button v-auth="'team:member:del'" @click="deleteMember" :disabled="showMemDialog.chooseId == null" type="danger" icon="delete">移除</el-button>
|
||||
</div>
|
||||
<el-table @current-change="chooseMember" border :data="showMemDialog.members.list">
|
||||
<el-table-column label="选择" width="50px">
|
||||
<template #default="scope">
|
||||
<el-radio v-model="showMemDialog.chooseId" :label="scope.row.id">
|
||||
<i></i>
|
||||
</el-radio>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column property="username" label="账号" width="135"></el-table-column>
|
||||
<el-table-column property="createTime" label="加入时间">
|
||||
<template #default="scope">
|
||||
{{ dateFormat(scope.row.createTime) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column property="creator" label="分配者" width="135"></el-table-column>
|
||||
</el-table>
|
||||
<el-pagination
|
||||
@current-change="setMemebers"
|
||||
style="text-align: center"
|
||||
background
|
||||
layout="prev, pager, next, total, jumper"
|
||||
:total="showMemDialog.members.total"
|
||||
v-model:current-page="showMemDialog.query.pageNum"
|
||||
:page-size="showMemDialog.query.pageSize"
|
||||
/>
|
||||
|
||||
<el-dialog width="400px" title="添加成员" :before-close="cancelAddMember" v-model="showMemDialog.addVisible">
|
||||
<el-form :model="showMemDialog.memForm" label-width="70px">
|
||||
<el-form-item label="账号:">
|
||||
<el-select
|
||||
style="width: 100%"
|
||||
remote
|
||||
:remote-method="getAccount"
|
||||
v-model="showMemDialog.memForm.accountId"
|
||||
filterable
|
||||
placeholder="请输入账号模糊搜索并选择"
|
||||
>
|
||||
<el-option v-for="item in showMemDialog.accounts" :key="item.id" :label="item.username" :value="item.id"> </el-option>
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<div class="dialog-footer">
|
||||
<el-button @click="cancelAddMember()">取 消</el-button>
|
||||
<el-button @click="addMember" type="primary">确 定</el-button>
|
||||
</div>
|
||||
</template>
|
||||
</el-dialog>
|
||||
</el-dialog>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import { ref, toRefs, reactive, onMounted, defineComponent } from 'vue';
|
||||
import { tagApi } from './api';
|
||||
import { accountApi } from '../../system/api';
|
||||
import { ElMessage, ElMessageBox } from 'element-plus';
|
||||
import { dateFormat } from '@/common/utils/date';
|
||||
import { notBlank } from '@/common/assert';
|
||||
export default defineComponent({
|
||||
name: 'TeamList',
|
||||
components: {},
|
||||
setup() {
|
||||
const teamForm: any = ref(null);
|
||||
const tagTreeRef: any = ref(null);
|
||||
const state = reactive({
|
||||
dialogFormVisible: false,
|
||||
currentEditPermissions: false,
|
||||
addTeamDialog: {
|
||||
title: '新增团队',
|
||||
visible: false,
|
||||
form: { id: 0, name: '', remark: '' },
|
||||
},
|
||||
query: {
|
||||
pageNum: 1,
|
||||
pageSize: 10,
|
||||
name: null,
|
||||
},
|
||||
total: 0,
|
||||
data: [],
|
||||
chooseId: 0,
|
||||
chooseData: null,
|
||||
showMemDialog: {
|
||||
visible: false,
|
||||
chooseId: 0,
|
||||
chooseData: null,
|
||||
query: {
|
||||
pageSize: 8,
|
||||
pageNum: 1,
|
||||
teamId: null,
|
||||
},
|
||||
members: {
|
||||
list: [],
|
||||
total: null,
|
||||
},
|
||||
title: '',
|
||||
addVisible: false,
|
||||
memForm: {
|
||||
accountId: null as any,
|
||||
teamId: 0,
|
||||
},
|
||||
accounts: Array(),
|
||||
},
|
||||
showTagDialog: {
|
||||
title: '项目信息',
|
||||
visible: false,
|
||||
tags: [],
|
||||
teamId: 0,
|
||||
tagTreeTeams: [] as any,
|
||||
props: {
|
||||
value: 'id',
|
||||
label: 'codePath',
|
||||
children: 'children',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
onMounted(() => {
|
||||
search();
|
||||
});
|
||||
|
||||
const search = async () => {
|
||||
let res = await tagApi.getTeams.request(state.query);
|
||||
state.data = res.list;
|
||||
state.total = res.total;
|
||||
};
|
||||
|
||||
const handlePageChange = (curPage: number) => {
|
||||
state.query.pageNum = curPage;
|
||||
search();
|
||||
};
|
||||
|
||||
const choose = (item: any) => {
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
state.chooseId = item.id;
|
||||
state.chooseData = item;
|
||||
};
|
||||
|
||||
const showSaveTeamDialog = (data: any) => {
|
||||
if (data) {
|
||||
state.addTeamDialog.form.id = data.id;
|
||||
state.addTeamDialog.form.name = data.name;
|
||||
state.addTeamDialog.form.remark = data.remark;
|
||||
state.addTeamDialog.title = `修改 [${data.codePath}] 信息`;
|
||||
}
|
||||
state.addTeamDialog.visible = true;
|
||||
};
|
||||
|
||||
const saveTeam = async () => {
|
||||
teamForm.value.validate(async (valid: any) => {
|
||||
if (valid) {
|
||||
const form = state.addTeamDialog.form;
|
||||
await tagApi.saveTeam.request(form);
|
||||
ElMessage.success('保存成功');
|
||||
search();
|
||||
cancelSaveTeam();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
const cancelSaveTeam = () => {
|
||||
state.addTeamDialog.visible = false;
|
||||
state.addTeamDialog.form = {} as any;
|
||||
teamForm.value.resetFields();
|
||||
};
|
||||
|
||||
const deleteTeam = (data: any) => {
|
||||
ElMessageBox.confirm(`此操作将删除 [${data.name}], 是否继续?`, '提示', {
|
||||
confirmButtonText: '确定',
|
||||
cancelButtonText: '取消',
|
||||
type: 'warning',
|
||||
}).then(async () => {
|
||||
await tagApi.delTeam.request({ id: data.id });
|
||||
ElMessage.success('删除成功!');
|
||||
search();
|
||||
});
|
||||
};
|
||||
|
||||
/********** 团队成员相关 ***********/
|
||||
|
||||
const showMembers = async (team: any) => {
|
||||
state.showMemDialog.query.teamId = team.id;
|
||||
await setMemebers();
|
||||
state.showMemDialog.title = `[${team.name}] 成员信息`;
|
||||
state.showMemDialog.visible = true;
|
||||
};
|
||||
|
||||
const getAccount = (username: any) => {
|
||||
accountApi.list.request({ username }).then((res) => {
|
||||
state.showMemDialog.accounts = res.list;
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* 选中成员
|
||||
*/
|
||||
const chooseMember = (item: any) => {
|
||||
if (!item) {
|
||||
return;
|
||||
}
|
||||
state.showMemDialog.chooseData = item;
|
||||
state.showMemDialog.chooseId = item.id;
|
||||
};
|
||||
|
||||
const deleteMember = async () => {
|
||||
await tagApi.delTeamMem.request(state.showMemDialog.chooseData);
|
||||
ElMessage.success('移除成功');
|
||||
// 重新赋值成员列表
|
||||
setMemebers();
|
||||
};
|
||||
|
||||
/**
|
||||
* 设置成员列表信息
|
||||
*/
|
||||
const setMemebers = async () => {
|
||||
const res = await tagApi.getTeamMem.request(state.showMemDialog.query);
|
||||
state.showMemDialog.members.list = res.list;
|
||||
state.showMemDialog.members.total = res.total;
|
||||
};
|
||||
|
||||
const showAddMemberDialog = () => {
|
||||
state.showMemDialog.addVisible = true;
|
||||
};
|
||||
|
||||
const addMember = async () => {
|
||||
const memForm = state.showMemDialog.memForm;
|
||||
memForm.teamId = state.chooseId;
|
||||
notBlank(memForm.accountId, '请先选择账号');
|
||||
|
||||
await tagApi.saveTeamMem.request(memForm);
|
||||
ElMessage.success('保存成功');
|
||||
setMemebers();
|
||||
cancelAddMember();
|
||||
};
|
||||
|
||||
const cancelAddMember = () => {
|
||||
state.showMemDialog.memForm = {} as any;
|
||||
state.showMemDialog.addVisible = false;
|
||||
state.showMemDialog.chooseData = null;
|
||||
state.showMemDialog.chooseId = 0;
|
||||
};
|
||||
|
||||
/********** 标签相关 ***********/
|
||||
|
||||
const showTags = async (team: any) => {
|
||||
state.showTagDialog.tags = await tagApi.getTagTrees.request(null);
|
||||
state.showTagDialog.tagTreeTeams = await tagApi.getTeamTagIds.request({ teamId: team.id });
|
||||
state.showTagDialog.title = `[${team.name}] 项目信息`;
|
||||
state.showTagDialog.teamId = team.id;
|
||||
state.showTagDialog.visible = true;
|
||||
};
|
||||
|
||||
const closeTagDialog = () => {
|
||||
state.showTagDialog.visible = false;
|
||||
setTimeout(() => {
|
||||
state.showTagDialog.tagTreeTeams = [];
|
||||
}, 500);
|
||||
};
|
||||
|
||||
const saveTags = async () => {
|
||||
await tagApi.saveTeamTags.request({
|
||||
teamId: state.showTagDialog.teamId,
|
||||
tagIds: state.showTagDialog.tagTreeTeams,
|
||||
});
|
||||
ElMessage.success('保存成功');
|
||||
closeTagDialog();
|
||||
};
|
||||
|
||||
const tagTreeNodeCheck = (data: any, checkInfo: any) => {
|
||||
const node = tagTreeRef.value.getNode(data.id);
|
||||
console.log(node);
|
||||
// state.showTagDialog.tagTreeTeams = [16]
|
||||
if (node.checked) {
|
||||
if (node.parent) {
|
||||
console.log(node.parent);
|
||||
// removeCheckedTagId(node.parent.key);
|
||||
tagTreeRef.value.setChecked(node.parent, false, false);
|
||||
}
|
||||
// // parentNode = node.parent
|
||||
// for (let parentNode of node.parent) {
|
||||
// parentNode.setChecked(false);
|
||||
// }
|
||||
}
|
||||
console.log(data);
|
||||
console.log(checkInfo);
|
||||
};
|
||||
|
||||
// function removeCheckedTagId(id: any) {
|
||||
// console.log(state.showTagDialog.tagTreeTeams);
|
||||
// for (let i = 0; i < state.showTagDialog.tagTreeTeams.length; i++) {
|
||||
// if (state.showTagDialog.tagTreeTeams[i] == id) {
|
||||
// console.log('has id', id);
|
||||
// state.showTagDialog.tagTreeTeams.splice(i, 1);
|
||||
// }
|
||||
// }
|
||||
// console.log(state.showTagDialog.tagTreeTeams);
|
||||
// }
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
teamForm,
|
||||
tagTreeRef,
|
||||
dateFormat,
|
||||
choose,
|
||||
search,
|
||||
handlePageChange,
|
||||
showSaveTeamDialog,
|
||||
saveTeam,
|
||||
cancelSaveTeam,
|
||||
deleteTeam,
|
||||
showMembers,
|
||||
setMemebers,
|
||||
getAccount,
|
||||
showAddMemberDialog,
|
||||
addMember,
|
||||
cancelAddMember,
|
||||
chooseMember,
|
||||
deleteMember,
|
||||
showTags,
|
||||
closeTagDialog,
|
||||
saveTags,
|
||||
tagTreeNodeCheck,
|
||||
};
|
||||
},
|
||||
});
|
||||
</script>
|
||||
<style lang="scss">
|
||||
</style>
|
||||
19
mayfly_go_web/src/views/ops/tag/api.ts
Normal file
19
mayfly_go_web/src/views/ops/tag/api.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
import Api from '@/common/Api';
|
||||
|
||||
export const tagApi = {
|
||||
getAccountTags: Api.create("/tag-trees/account-has", 'get'),
|
||||
getTagTrees: Api.create("/tag-trees", 'get'),
|
||||
saveTagTree: Api.create("/tag-trees", 'post'),
|
||||
delTagTree: Api.create("/tag-trees/{id}", 'delete'),
|
||||
|
||||
getTeams: Api.create("/teams", 'get'),
|
||||
saveTeam: Api.create("/teams", 'post'),
|
||||
delTeam: Api.create("/teams/{id}", 'delete'),
|
||||
|
||||
getTeamMem: Api.create("/teams/{teamId}/members", 'get'),
|
||||
saveTeamMem: Api.create("/teams/{teamId}/members", 'post'),
|
||||
delTeamMem: Api.create("/teams/{teamId}/members/{accountId}", 'delete'),
|
||||
|
||||
getTeamTagIds: Api.create("/teams/{teamId}/tags", 'get'),
|
||||
saveTeamTags: Api.create("/teams/{teamId}/tags", 'post'),
|
||||
}
|
||||
@@ -73,6 +73,7 @@ export default defineComponent({
|
||||
state.form = { ...newValue.account };
|
||||
state.edit = true;
|
||||
} else {
|
||||
state.edit = false;
|
||||
state.form = {} as any;
|
||||
}
|
||||
state.dialogVisible = newValue.visible;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<template>
|
||||
<div>
|
||||
<el-dialog :title="title" v-model="dvisible" :show-close="false" :before-close="cancel" width="500px" :destroy-on-close="true">
|
||||
<el-dialog :title="title" v-model="dvisible" :show-close="false" :before-close="cancel" width="750px" :destroy-on-close="true">
|
||||
<el-form ref="configForm" :model="form" label-width="90px">
|
||||
<el-form-item prop="name" label="配置项:" required>
|
||||
<el-input v-model="form.name"></el-input>
|
||||
@@ -8,9 +8,28 @@
|
||||
<el-form-item prop="key" label="配置key:" required>
|
||||
<el-input :disabled="form.id != null" v-model="form.key"></el-input>
|
||||
</el-form-item>
|
||||
<el-form-item prop="value" label="配置值:" required>
|
||||
<el-input v-model="form.value"></el-input>
|
||||
|
||||
<el-row style="margin-left: 30px; margin-bottom: 5px">
|
||||
<el-button @click="onAddParam" size="small" type="success">新增配置项</el-button>
|
||||
</el-row>
|
||||
<el-form-item :key="param" v-for="(param, index) in params" prop="params" :label="`参数${index + 1}`">
|
||||
<el-row>
|
||||
<el-col :span="5"><el-input v-model="param.model" placeholder="model"></el-input></el-col>
|
||||
<el-divider :span="1" direction="vertical" border-style="dashed" />
|
||||
<el-col :span="4"><el-input v-model="param.name" placeholder="字段名"></el-input></el-col>
|
||||
<el-divider :span="1" direction="vertical" border-style="dashed" />
|
||||
<el-col :span="4"><el-input v-model="param.placeholder" placeholder="字段说明"></el-input></el-col>
|
||||
<el-divider :span="1" direction="vertical" border-style="dashed" />
|
||||
<el-col :span="4">
|
||||
<el-input v-model="param.options" placeholder="可选值 ,分割"></el-input>
|
||||
</el-col>
|
||||
<el-divider :span="1" direction="vertical" border-style="dashed" />
|
||||
<el-col :span="2"><el-button @click="onDeleteParam(index)" size="small" type="danger">删除</el-button></el-col>
|
||||
</el-row>
|
||||
</el-form-item>
|
||||
<!-- <el-form-item prop="value" label="配置值:" required>
|
||||
<el-input v-model="form.value"></el-input>
|
||||
</el-form-item> -->
|
||||
<el-form-item label="备注:">
|
||||
<el-input v-model="form.remark" type="textarea" :rows="2"></el-input>
|
||||
</el-form-item>
|
||||
@@ -46,10 +65,12 @@ export default defineComponent({
|
||||
const configForm: any = ref(null);
|
||||
const state = reactive({
|
||||
dvisible: false,
|
||||
params: [] as any,
|
||||
form: {
|
||||
id: null,
|
||||
name: '',
|
||||
key: '',
|
||||
params: '',
|
||||
value: '',
|
||||
remark: '',
|
||||
},
|
||||
@@ -60,11 +81,25 @@ export default defineComponent({
|
||||
state.dvisible = newValue.visible;
|
||||
if (newValue.data) {
|
||||
state.form = { ...newValue.data };
|
||||
if (state.form.params) {
|
||||
state.params = JSON.parse(state.form.params);
|
||||
} else {
|
||||
state.params = [];
|
||||
}
|
||||
} else {
|
||||
state.form = {} as any;
|
||||
state.params = [];
|
||||
}
|
||||
});
|
||||
|
||||
const onAddParam = () => {
|
||||
state.params.push({ name: '', model: '', placeholder: '' });
|
||||
};
|
||||
|
||||
const onDeleteParam = (idx: number) => {
|
||||
state.params.splice(idx, 1);
|
||||
};
|
||||
|
||||
const cancel = () => {
|
||||
// 更新父组件visible prop对应的值为false
|
||||
emit('update:visible', false);
|
||||
@@ -75,6 +110,9 @@ export default defineComponent({
|
||||
const btnOk = async () => {
|
||||
configForm.value.validate(async (valid: boolean) => {
|
||||
if (valid) {
|
||||
if (state.params) {
|
||||
state.form.params = JSON.stringify(state.params);
|
||||
}
|
||||
await configApi.save.request(state.form);
|
||||
emit('val-change', state.form);
|
||||
cancel();
|
||||
@@ -88,6 +126,8 @@ export default defineComponent({
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
onAddParam,
|
||||
onDeleteParam,
|
||||
configForm,
|
||||
btnOk,
|
||||
cancel,
|
||||
|
||||
@@ -16,12 +16,25 @@
|
||||
<el-table-column prop="key" label="配置key"></el-table-column>
|
||||
<el-table-column prop="value" label="配置值" min-width="100px" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="remark" label="备注" min-width="100px" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column prop="updateTime" label="更新时间">
|
||||
<el-table-column prop="updateTime" label="更新时间" min-width="100px">
|
||||
<template #default="scope">
|
||||
{{ $filters.dateFormat(scope.row.createTime) }}
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column prop="modifier" label="修改者" show-overflow-tooltip></el-table-column>
|
||||
<el-table-column label="操作" min-width="50" fixed="right">
|
||||
<template #default="scope">
|
||||
<el-link
|
||||
:disabled="scope.row.status == -1"
|
||||
type="warning"
|
||||
@click="showSetConfigDialog(scope.row)"
|
||||
plain
|
||||
size="small"
|
||||
:underline="false"
|
||||
>配置</el-link
|
||||
>
|
||||
</template>
|
||||
</el-table-column>
|
||||
</el-table>
|
||||
<el-row style="margin-top: 20px" type="flex" justify="end">
|
||||
<el-pagination
|
||||
@@ -35,6 +48,42 @@
|
||||
</el-row>
|
||||
</el-card>
|
||||
|
||||
<el-dialog :before-close="closeSetConfigDialog" title="配置项设置" v-model="paramsDialog.visible" width="500px">
|
||||
<el-form v-if="paramsDialog.paramsFormItem.length > 0" ref="paramsForm" :model="paramsDialog.params" label-width="90px">
|
||||
<el-form-item v-for="item in paramsDialog.paramsFormItem" :key="item.name" :prop="item.model" :label="item.name" required>
|
||||
<el-input
|
||||
v-if="!item.options"
|
||||
v-model="paramsDialog.params[item.model]"
|
||||
:placeholder="item.placeholder"
|
||||
autocomplete="off"
|
||||
clearable
|
||||
></el-input>
|
||||
<el-select
|
||||
v-else
|
||||
v-model="paramsDialog.params[item.model]"
|
||||
:placeholder="item.placeholder"
|
||||
filterable
|
||||
autocomplete="off"
|
||||
clearable
|
||||
style="width: 100%"
|
||||
>
|
||||
<el-option v-for="option in item.options.split(',')" :key="option" :label="option" :value="option" />
|
||||
</el-select>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<el-form v-else ref="paramsForm" label-width="90px">
|
||||
<el-form-item label="配置值" required>
|
||||
<el-input v-model="paramsDialog.params" :placeholder="paramsDialog.config.remark" autocomplete="off" clearable></el-input>
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="closeSetConfigDialog()">取 消</el-button>
|
||||
<el-button type="primary" @click="setConfig()">确 定</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
<config-edit :title="configEdit.title" v-model:visible="configEdit.visible" :data="configEdit.config" @val-change="configEditChange" />
|
||||
</div>
|
||||
</template>
|
||||
@@ -62,6 +111,12 @@ export default defineComponent({
|
||||
configs: [],
|
||||
chooseId: null,
|
||||
chooseData: null,
|
||||
paramsDialog: {
|
||||
visible: false,
|
||||
config: null as any,
|
||||
params: {},
|
||||
paramsFormItem: [] as any,
|
||||
},
|
||||
configEdit: {
|
||||
title: '配置修改',
|
||||
visible: false,
|
||||
@@ -84,6 +139,62 @@ export default defineComponent({
|
||||
search();
|
||||
};
|
||||
|
||||
const showSetConfigDialog = (row: any) => {
|
||||
state.paramsDialog.config = row;
|
||||
// 存在配置项则弹窗提示输入对应的配置项
|
||||
if (row.params) {
|
||||
state.paramsDialog.paramsFormItem = JSON.parse(row.params);
|
||||
if (state.paramsDialog.paramsFormItem && state.paramsDialog.paramsFormItem.length > 0) {
|
||||
if (row.value) {
|
||||
state.paramsDialog.params = JSON.parse(row.value);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
state.paramsDialog.params = row.value;
|
||||
}
|
||||
state.paramsDialog.visible = true;
|
||||
};
|
||||
|
||||
const closeSetConfigDialog = () => {
|
||||
state.paramsDialog.visible = false;
|
||||
setTimeout(() => {
|
||||
state.paramsDialog.config = {};
|
||||
state.paramsDialog.params = {};
|
||||
state.paramsDialog.paramsFormItem = [];
|
||||
}, 300);
|
||||
};
|
||||
|
||||
const setConfig = async () => {
|
||||
let paramsValue = state.paramsDialog.params;
|
||||
if (state.paramsDialog.paramsFormItem.length > 0) {
|
||||
// 如果配置项删除,则需要将value中对应的字段移除
|
||||
for (let paramKey in paramsValue) {
|
||||
if (!hasParam(paramKey, state.paramsDialog.paramsFormItem)) {
|
||||
delete paramsValue[paramKey];
|
||||
}
|
||||
}
|
||||
paramsValue = JSON.stringify(paramsValue);
|
||||
}
|
||||
await configApi.save.request({
|
||||
id: state.paramsDialog.config.id,
|
||||
key: state.paramsDialog.config.key,
|
||||
name: state.paramsDialog.config.name,
|
||||
value: paramsValue,
|
||||
});
|
||||
ElMessage.success('保存成功');
|
||||
closeSetConfigDialog();
|
||||
search();
|
||||
};
|
||||
|
||||
const hasParam = (paramKey: string, paramItems: any) => {
|
||||
for (let paramItem of paramItems) {
|
||||
if (paramItem.model == paramKey) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
const choose = (item: any) => {
|
||||
if (!item) {
|
||||
return;
|
||||
@@ -93,7 +204,7 @@ export default defineComponent({
|
||||
};
|
||||
|
||||
const configEditChange = () => {
|
||||
ElMessage.success('修改成功!');
|
||||
ElMessage.success('保存成功');
|
||||
state.chooseId = null;
|
||||
state.chooseData = null;
|
||||
search();
|
||||
@@ -111,6 +222,9 @@ export default defineComponent({
|
||||
|
||||
return {
|
||||
...toRefs(state),
|
||||
showSetConfigDialog,
|
||||
closeSetConfigDialog,
|
||||
setConfig,
|
||||
search,
|
||||
handlePageChange,
|
||||
choose,
|
||||
|
||||
Reference in New Issue
Block a user