feat: 小功能优化&前端基于setup语法糖重构

This commit is contained in:
meilin.huang
2022-10-29 20:08:15 +08:00
committed by 刘宗洋
parent a6d9a4b5ae
commit 03291594b1
147 changed files with 9089 additions and 9951 deletions

View File

@@ -4,15 +4,12 @@
</div>
</template>
<script lang="ts">
import { ref, toRefs, reactive, nextTick, watch, onMounted, onUnmounted, defineComponent } from 'vue';
<script lang="ts" setup>
import { ref, toRefs, reactive, nextTick, watch, onMounted, onUnmounted } from 'vue';
import JSONEditor from 'jsoneditor';
import 'jsoneditor/dist/jsoneditor.min.css';
export default defineComponent({
name: 'JsonEdit',
components: {},
props: {
const props = defineProps({
modelValue: {
type: [String, Object],
},
@@ -38,8 +35,11 @@ export default defineComponent({
return ['tree', 'code', 'form', 'text', 'view'];
},
},
},
setup(props: any, { emit }) {
})
//定义事件
const emit = defineEmits(['update:visible', 'update:modelValue', 'onChange'])
let { modelValue, options, modeList, currentMode } = toRefs(props);
const jsoneditorVue = ref(null)
@@ -60,7 +60,7 @@ export default defineComponent({
state.height = props.height;
init();
setJson(modelValue.value);
setJson(modelValue!.value);
});
onUnmounted(() => {
@@ -117,13 +117,6 @@ export default defineComponent({
};
editor = new JSONEditor(jsoneditorVue.value, finalOptions);
};
return {
...toRefs(state),
jsoneditorVue,
};
},
});
</script>
<style lang="scss">

View File

@@ -3,7 +3,7 @@ import RouterParent from '@/views/layout/routerView/parent.vue';
export const imports = {
'RouterParent': RouterParent,
"Home": () => import('@/views/home/index.vue'),
"Home": () => import('@/views/home/Home.vue'),
'Personal': () => import('@/views/personal/index.vue'),
// machine
"MachineList": () => import('@/views/ops/machine'),

View File

@@ -7,13 +7,14 @@
<img :src="getUserInfos.photo" />
<div class="home-card-first-right ml15">
<div class="flex-margin">
<div class="home-card-first-right-title">{{ `${currentTime}, ${getUserInfos.username}` }}</div>
<div class="home-card-first-right-title">{{ `${currentTime}, ${getUserInfos.username}`
}}</div>
</div>
</div>
</div>
</div>
</el-col>
<el-col :sm="3" class="mb15" v-for="(v, k) in topCardItemList" :key="k">
<el-col :sm="3" class="mb15" v-for="(v, k) in topCardItemList as any" :key="k">
<div @click="toPage(v)" class="home-card-item home-card-item-box" :style="{ background: v.color }">
<div class="home-card-item-flex">
<div class="home-card-item-title pb3">{{ v.title }}</div>
@@ -26,7 +27,7 @@
</div>
</template>
<script lang="ts">
<script lang="ts" setup>
import { toRefs, reactive, onMounted, nextTick, computed } from 'vue';
import { useStore } from '@/store/index.ts';
// import * as echarts from 'echarts';
@@ -34,10 +35,7 @@ import { CountUp } from 'countup.js';
import { formatAxis } from '@/common/utils/formatTime.ts';
import { indexApi } from './api';
import { useRouter } from 'vue-router';
export default {
name: 'HomePage',
setup() {
// const { proxy } = getCurrentInstance() as any;
const router = useRouter();
const store = useStore();
const state = reactive({
@@ -65,6 +63,10 @@ export default {
],
});
const {
topCardItemList,
} = toRefs(state)
//
const currentTime = computed(() => {
return formatAxis(new Date());
@@ -117,20 +119,12 @@ export default {
const getUserInfos = computed(() => {
return store.state.userInfos.userInfos;
});
return {
getUserInfos,
currentTime,
toPage,
...toRefs(state),
};
},
};
</script>
<style scoped lang="scss">
.home-container {
overflow-x: hidden;
.home-card-item {
width: 100%;
height: 103px;
@@ -138,16 +132,19 @@ export default {
border-radius: 4px;
transition: all ease 0.3s;
cursor: pointer;
&:hover {
box-shadow: 0 2px 12px 0 rgb(0 0 0 / 10%);
transition: all ease 0.3s;
}
}
.home-card-item-box {
display: flex;
align-items: center;
position: relative;
overflow: hidden;
&:hover {
i {
right: 0px !important;
@@ -155,6 +152,7 @@ export default {
transition: all ease 0.3s;
}
}
i {
position: absolute;
right: -10px;
@@ -163,48 +161,59 @@ export default {
transform: rotate(-30deg);
transition: all ease 0.3s;
}
.home-card-item-flex {
padding: 0 20px;
color: white;
.home-card-item-title,
.home-card-item-tip {
font-size: 13px;
}
.home-card-item-title-num {
font-size: 18px;
}
.home-card-item-tip-num {
font-size: 13px;
}
}
}
.home-card-first {
background: white;
border: 1px solid #ebeef5;
display: flex;
align-items: center;
img {
width: 60px;
height: 60px;
border-radius: 100%;
border: 2px solid var(--color-primary-light-5);
}
.home-card-first-right {
flex: 1;
display: flex;
flex-direction: column;
.home-card-first-right-msg {
font-size: 13px;
color: gray;
}
}
}
.home-monitor {
height: 200px;
.flex-warp-item {
width: 50%;
height: 100px;
display: flex;
.flex-warp-item-box {
margin: auto;
height: auto;
@@ -212,19 +221,24 @@ export default {
}
}
}
.home-warning-card {
height: 292px;
::v-deep(.el-card) {
height: 100%;
}
}
.home-dynamic {
height: 200px;
.home-dynamic-item {
display: flex;
width: 100%;
height: 60px;
overflow: hidden;
&:first-of-type {
.home-dynamic-item-line {
i {
@@ -232,20 +246,24 @@ export default {
}
}
}
.home-dynamic-item-left {
text-align: right;
.home-dynamic-item-left-time1 {
}
.home-dynamic-item-left-time1 {}
.home-dynamic-item-left-time2 {
font-size: 13px;
color: gray;
}
}
.home-dynamic-item-line {
height: 60px;
border-right: 2px dashed #dfdfdf;
margin: 0 20px;
position: relative;
i {
color: var(--color-primary);
font-size: 12px;
@@ -256,8 +274,10 @@ export default {
background: white;
}
}
.home-dynamic-item-right {
flex: 1;
.home-dynamic-item-right-title {
i {
margin-right: 5px;
@@ -270,6 +290,7 @@ export default {
color: var(--color-primary);
}
}
.home-dynamic-item-right-label {
font-size: 13px;
color: gray;

View File

@@ -2,37 +2,25 @@
<div>
<el-form ref="loginFormRef" :model="loginForm" :rules="rules" class="login-content-form" size="large">
<el-form-item prop="username">
<el-input type="text" placeholder="请输入用户名" prefix-icon="user" v-model="loginForm.username" clearable autocomplete="off">
<el-input type="text" placeholder="请输入用户名" prefix-icon="user" v-model="loginForm.username" clearable
autocomplete="off">
</el-input>
</el-form-item>
<el-form-item prop="password">
<el-input type="password" placeholder="请输入密码" prefix-icon="lock" v-model="loginForm.password" autocomplete="off" show-password>
<el-input type="password" placeholder="请输入密码" prefix-icon="lock" v-model="loginForm.password"
autocomplete="off" show-password>
</el-input>
</el-form-item>
<el-form-item v-if="useLoginCaptcha" prop="captcha">
<el-form-item v-if="isUseLoginCaptcha" prop="captcha">
<el-row :gutter="15">
<el-col :span="16">
<el-input
type="text"
maxlength="6"
placeholder="请输入验证码"
prefix-icon="position"
v-model="loginForm.captcha"
clearable
autocomplete="off"
@keyup.enter="login"
></el-input>
<el-input type="text" maxlength="6" placeholder="请输入验证码" prefix-icon="position"
v-model="loginForm.captcha" clearable autocomplete="off" @keyup.enter="login"></el-input>
</el-col>
<el-col :span="8">
<div class="login-content-code">
<img
class="login-content-code-img"
@click="getCaptcha"
width="130px"
height="40px"
:src="captchaImage"
style="cursor: pointer"
/>
<img class="login-content-code-img" @click="getCaptcha" width="130px" height="40px"
:src="captchaImage" style="cursor: pointer" />
</div>
</el-col>
</el-row>
@@ -44,21 +32,20 @@
</el-form-item>
</el-form>
<el-dialog title="修改密码" v-model="changePwdDialog.visible" :close-on-click-modal="false" width="450px" :destroy-on-close="true">
<el-form :model="changePwdDialog.form" :rules="changePwdDialog.rules" ref="changePwdFormRef" label-width="65px">
<el-dialog title="修改密码" v-model="changePwdDialog.visible" :close-on-click-modal="false" width="450px"
:destroy-on-close="true">
<el-form :model="changePwdDialog.form" :rules="changePwdDialog.rules" ref="changePwdFormRef"
label-width="65px">
<el-form-item prop="username" label="用户名" required>
<el-input v-model.trim="changePwdDialog.form.username" disabled></el-input>
</el-form-item>
<el-form-item prop="oldPassword" label="旧密码" required>
<el-input v-model.trim="changePwdDialog.form.oldPassword" autocomplete="new-password" type="password"></el-input>
<el-input v-model.trim="changePwdDialog.form.oldPassword" autocomplete="new-password"
type="password"></el-input>
</el-form-item>
<el-form-item prop="newPassword" label="新密码" required>
<el-input
v-model.trim="changePwdDialog.form.newPassword"
placeholder="须为8位以上且包含字⺟⼤⼩写+数字+特殊符号"
type="password"
autocomplete="new-password"
></el-input>
<el-input v-model.trim="changePwdDialog.form.newPassword" placeholder="须为8位以上且包含字⺟⼤⼩写+数字+特殊符号"
type="password" autocomplete="new-password"></el-input>
</el-form-item>
</el-form>
@@ -72,8 +59,8 @@
</div>
</template>
<script lang="ts">
import { nextTick, onMounted, ref, toRefs, reactive, defineComponent, computed } from 'vue';
<script lang="ts" setup>
import { nextTick, onMounted, ref, toRefs, reactive, computed } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { ElMessage } from 'element-plus';
import { initBackEndControlRoutesFun } from '@/router/index.ts';
@@ -85,9 +72,12 @@ import { RsaEncrypt } from '@/common/rsa';
import { useLoginCaptcha, useWartermark } from '@/common/sysconfig';
import { letterAvatar } from '@/common/utils/string';
export default defineComponent({
name: 'AccountLogin',
setup() {
const rules = {
username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
password: [{ required: true, message: '请输入密码', trigger: 'blur' }],
captcha: [{ required: true, message: '请输入验证码', trigger: 'blur' }],
}
const store = useStore();
const route = useRoute();
const router = useRouter();
@@ -95,7 +85,7 @@ export default defineComponent({
const changePwdFormRef: any = ref(null);
const state = reactive({
useLoginCaptcha: false,
isUseLoginCaptcha: false,
captchaImage: '',
loginForm: {
username: '',
@@ -121,20 +111,23 @@ export default defineComponent({
],
},
},
rules: {
username: [{ required: true, message: '请输入用户名', trigger: 'blur' }],
password: [{ required: true, message: '请输入密码', trigger: 'blur' }],
captcha: [{ required: true, message: '请输入验证码', trigger: 'blur' }],
},
loading: {
signIn: false,
changePwd: false,
},
});
const {
isUseLoginCaptcha,
captchaImage,
loginForm,
changePwdDialog,
loading,
} = toRefs(state)
onMounted(async () => {
nextTick(async () => {
state.useLoginCaptcha = await useLoginCaptcha();
state.isUseLoginCaptcha = await useLoginCaptcha();
getCaptcha();
});
// 移除公钥, 方便后续重新获取
@@ -142,7 +135,7 @@ export default defineComponent({
});
const getCaptcha = async () => {
if (!state.useLoginCaptcha) {
if (!state.isUseLoginCaptcha) {
return;
}
let res: any = await openApi.captcha();
@@ -271,28 +264,17 @@ export default defineComponent({
state.changePwdDialog.form.username = '';
getCaptcha();
};
return {
getCaptcha,
currentTime,
loginFormRef,
changePwdFormRef,
login,
changePwd,
cancelChangePwd,
...toRefs(state),
};
},
});
</script>
<style scoped lang="scss">
.login-content-form {
margin-top: 20px;
.login-content-code {
display: flex;
align-items: center;
justify-content: space-around;
.login-content-code-img {
width: 100%;
height: 40px;
@@ -309,12 +291,14 @@ export default defineComponent({
transition: all ease 0.2s;
border-radius: 4px;
user-select: none;
&:hover {
border-color: #c0c4cc;
transition: all ease 0.2s;
}
}
}
.login-content-submit {
width: 100%;
letter-spacing: 2px;

View File

@@ -31,34 +31,31 @@
</div>
</template>
<script lang="ts">
<script lang="ts" setup>
import { toRefs, reactive, computed } from 'vue';
import Account from '@/views/login/component/AccountLogin.vue';
import { useStore } from '@/store/index.ts';
export default {
name: 'LoginPage',
components: { Account },
setup() {
const store = useStore();
const state = reactive({
tabsActiveName: 'account',
isTabPaneShow: true,
});
const {
isTabPaneShow,
tabsActiveName,
} = toRefs(state)
// 获取布局配置信息
const getThemeConfig = computed(() => {
return store.state.themeConfig.themeConfig;
});
// 切换密码、手机登录
const onTabsClick = () => {
state.isTabPaneShow = !state.isTabPaneShow;
};
return {
onTabsClick,
getThemeConfig,
...toRefs(state),
};
},
};
</script>
<style scoped lang="scss">
@@ -67,6 +64,7 @@ export default {
height: 100%;
background: url('@/assets/image/bg-login.png') no-repeat;
background-size: 100% 100%;
.login-logo {
position: absolute;
top: 30px;
@@ -80,6 +78,7 @@ export default {
width: 90%;
transform: translateX(-50%);
}
.login-content {
width: 500px;
padding: 20px;
@@ -94,9 +93,11 @@ export default {
height: 480px;
overflow: hidden;
z-index: 1;
.login-content-main {
margin: 0 auto;
width: 80%;
.login-content-title {
color: #333;
font-weight: 500;
@@ -108,9 +109,11 @@ export default {
}
}
}
.login-content-mobile {
height: 418px;
}
.login-copyright {
position: absolute;
left: 50%;
@@ -120,9 +123,11 @@ export default {
color: white;
font-size: 12px;
opacity: 0.8;
.login-copyright-company {
white-space: nowrap;
}
.login-copyright-msg {
@extend .login-copyright-company;
}

View File

@@ -1,100 +0,0 @@
<template>
<div>
<el-form class="search-form" label-position="right" :inline="true">
<el-form-item prop="project" label="项目" label-width="40px">
<el-select v-model="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>
<el-form-item prop="env" label="env" label-width="33px">
<el-select style="width: 85px" v-model="envId" placeholder="环境" @change="changeEnv" filterable>
<el-option v-for="item in envs" :key="item.id" :label="item.name" :value="item.id">
<span style="float: left">{{ item.name }}</span>
<span style="float: right; color: #8492a6; font-size: 13px">{{ item.remark }}</span>
</el-option>
</el-select>
</el-form-item>
<slot></slot>
</el-form>
</div>
</template>
<script lang="ts">
import {toRefs, reactive, defineComponent, onMounted, watch} from 'vue';
import { projectApi } from '../project/api';
export default defineComponent({
name: 'ProjectEnvSelect',
props: {
visible: {
type: Boolean,
},
data: {
type: Object,
},
title: {
type: String,
},
machineId: {
type: Number,
},
isCommon: {
type: Boolean,
},
},
setup(props: any, { emit }) {
const state = reactive({
projects: [] as any,
envs: [] as any,
projectId: null,
envId: null,
});
// 动态选中项目和环境
const setData = async (projectId: null, envId: null) => {
if (projectId) {
state.projectId = projectId;
if (envId) {
state.envs = await projectApi.projectEnvs.request({projectId});
state.envId = envId;
}
}
}
watch(() => props.data, (newValue)=>{
setData(newValue.projectId, newValue.envId)
})
onMounted(async () => {
state.projects = await projectApi.accountProjects.request(null);
// 初始化容器时可能会选中项目和环境
if(props.data?.projectId && props.data?.envId){
await setData(props.data.projectId, props.data.envId)
}
});
const changeProject = async (projectId: any) => {
emit('update:projectId', projectId);
emit('changeProjectEnv', state.projectId, null);
state.envId = null;
state.envs = await projectApi.projectEnvs.request({ projectId });
};
const changeEnv = (envId: any) => {
emit('update:envId', envId);
emit('changeProjectEnv', state.projectId, envId);
};
return {
...toRefs(state),
changeProject,
changeEnv,
setData,
};
},
});
</script>
<style lang="scss">
</style>

View File

@@ -1,21 +1,12 @@
<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"
<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">
@@ -31,27 +22,33 @@
</div>
</template>
<script lang="ts">
import { toRefs, reactive, defineComponent, onMounted } from 'vue';
<script lang="ts" setup>
import { toRefs, reactive, onMounted } from 'vue';
import { tagApi } from '../tag/api';
export default defineComponent({
name: 'TagSelect',
props: {
const props = defineProps({
tagId: {
type: Number,
},
tagPath: {
type: String,
},
},
setup(props: any, { emit }) {
})
//定义事件
const emit = defineEmits(['changeTag', 'update:tagId', 'update:tagPath'])
const state = reactive({
tags: [],
// 单选则为id多选为id数组
selectTags: null as any,
});
const {
tags,
selectTags,
} = toRefs(state)
onMounted(async () => {
if (props.tagId) {
state.selectTags = props.tagId;
@@ -69,13 +66,7 @@ export default defineComponent({
emit('update:tagPath', null);
}
};
return {
...toRefs(state),
changeTag,
};
},
});
</script>
<style lang="scss">
</style>

View File

@@ -16,14 +16,18 @@
<el-col :span="12">
<el-form-item prop="characterSet" label="charset">
<el-select filterable style="width: 80%" v-model="tableData.characterSet" size="small">
<el-option v-for="item in characterSetNameList" :key="item" :label="item" :value="item"> </el-option>
<el-option v-for="item in characterSetNameList" :key="item" :label="item" :value="item">
</el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item prop="characterSet" label="collation">
<el-select filterable style="width: 80%" v-model="tableData.collation" size="small">
<el-option v-for="item in collationNameList" :key="item" :label="tableData.characterSet+'_'+item" :value="tableData.characterSet+'_'+item"> </el-option>
<el-option v-for="item in collationNameList" :key="item"
:label="tableData.characterSet + '_' + item"
:value="tableData.characterSet + '_' + item">
</el-option>
</el-select>
</el-form-item>
</el-col>
@@ -32,27 +36,38 @@
<el-tabs v-model="activeName">
<el-tab-pane label="字段" name="1">
<el-table :data="tableData.fields.res" :max-height="tableData.height">
<el-table-column :prop="item.prop" :label="item.label" v-for="item in tableData.fields.colNames" :key="item.prop">
<el-table-column :prop="item.prop" :label="item.label"
v-for="item in tableData.fields.colNames" :key="item.prop">
<template #default="scope">
<el-input v-if="item.prop === 'name'" size="small" v-model="scope.row.name"></el-input>
<el-input v-if="item.prop === 'name'" size="small" v-model="scope.row.name">
</el-input>
<el-select v-if="item.prop === 'type'" filterable size="small" v-model="scope.row.type">
<el-option v-for="typeValue in columnTypeList" :key="typeValue" :value="typeValue">{{ typeValue }}</el-option>
<el-select v-if="item.prop === 'type'" filterable size="small"
v-model="scope.row.type">
<el-option v-for="typeValue in columnTypeList" :key="typeValue"
:value="typeValue">{{ typeValue }}</el-option>
</el-select>
<el-input v-if="item.prop === 'value'" size="small" v-model="scope.row.value"> </el-input>
<el-input v-if="item.prop === 'value'" size="small" v-model="scope.row.value">
</el-input>
<el-input v-if="item.prop === 'length'" size="small" v-model="scope.row.length"> </el-input>
<el-input v-if="item.prop === 'length'" size="small" v-model="scope.row.length">
</el-input>
<el-checkbox v-if="item.prop === 'notNull'" size="small" v-model="scope.row.notNull"> </el-checkbox>
<el-checkbox v-if="item.prop === 'notNull'" size="small"
v-model="scope.row.notNull"> </el-checkbox>
<el-checkbox v-if="item.prop === 'pri'" size="small" v-model="scope.row.pri"> </el-checkbox>
<el-checkbox v-if="item.prop === 'pri'" size="small" v-model="scope.row.pri">
</el-checkbox>
<el-checkbox v-if="item.prop === 'auto_increment'" size="small" v-model="scope.row.auto_increment"> </el-checkbox>
<el-checkbox v-if="item.prop === 'auto_increment'" size="small"
v-model="scope.row.auto_increment"> </el-checkbox>
<el-input v-if="item.prop === 'remark'" size="small" v-model="scope.row.remark"> </el-input>
<el-input v-if="item.prop === 'remark'" size="small" v-model="scope.row.remark">
</el-input>
<el-link v-if="item.prop === 'action'" type="danger" plain size="small" :underline="false" @click.prevent="deleteRow(scope.$index)">删除</el-link>
<el-link v-if="item.prop === 'action'" type="danger" plain size="small"
:underline="false" @click.prevent="deleteRow(scope.$index)">删除</el-link>
</template>
</el-table-column>
</el-table>
@@ -63,35 +78,36 @@
</el-tab-pane>
<el-tab-pane label="索引" name="2">
<el-table :data="tableData.indexs.res" :max-height="tableData.height">
<el-table-column :prop="item.prop" :label="item.label" v-for="item in tableData.indexs.colNames" :key="item.prop">
<el-table-column :prop="item.prop" :label="item.label"
v-for="item in tableData.indexs.colNames" :key="item.prop">
<template #default="scope">
<el-input v-if="item.prop === 'indexName'" size="small" v-model="scope.row.indexName"></el-input>
<el-input v-if="item.prop === 'indexName'" size="small"
v-model="scope.row.indexName"></el-input>
<el-select
v-if="item.prop === 'columnNames'"
v-model="scope.row.columnNames"
multiple
collapse-tags
collapse-tags-tooltip
filterable
placeholder="请选择字段"
style="width: 100%"
>
<el-option v-for="cl in tableData.indexs.columns" :key="cl.name" :label="cl.name" :value="cl.name" >
<el-select v-if="item.prop === 'columnNames'" v-model="scope.row.columnNames"
multiple collapse-tags collapse-tags-tooltip filterable placeholder="请选择字段"
style="width: 100%">
<el-option v-for="cl in tableData.indexs.columns" :key="cl.name"
:label="cl.name" :value="cl.name">
{{ cl.name + ' - ' + (cl.remark || '') }}
</el-option>
</el-select>
<el-checkbox v-if="item.prop === 'unique'" size="small" v-model="scope.row.unique"> </el-checkbox>
<el-checkbox v-if="item.prop === 'unique'" size="small" v-model="scope.row.unique">
</el-checkbox>
<el-select v-if="item.prop === 'indexType'" filterable size="small" v-model="scope.row.indexType">
<el-option v-for="typeValue in indexTypeList" :key="typeValue" :value="typeValue">{{ typeValue }}</el-option>
<el-select v-if="item.prop === 'indexType'" filterable size="small"
v-model="scope.row.indexType">
<el-option v-for="typeValue in indexTypeList" :key="typeValue"
:value="typeValue">{{ typeValue }}</el-option>
</el-select>
<el-input v-if="item.prop === 'indexComment'" size="small" v-model="scope.row.indexComment"> </el-input>
<el-input v-if="item.prop === 'indexComment'" size="small"
v-model="scope.row.indexComment"> </el-input>
<el-link v-if="item.prop === 'action'" type="danger" plain size="small" :underline="false" @click.prevent="deleteIndex(scope.$index)">删除</el-link>
<el-link v-if="item.prop === 'action'" type="danger" plain size="small"
:underline="false" @click.prevent="deleteIndex(scope.$index)">删除</el-link>
</template>
</el-table-column>
</el-table>
@@ -111,15 +127,13 @@
</template>
<script lang="ts">
import { watch, toRefs, reactive, defineComponent, ref, getCurrentInstance } from 'vue';
<script lang="ts" setup>
import { watch, toRefs, reactive, ref, getCurrentInstance } from 'vue';
import { TYPE_LIST, CHARACTER_SET_NAME_LIST, COLLATION_SUFFIX_LIST } from './service.ts';
import { ElMessage } from 'element-plus';
import SqlExecBox from './component/SqlExecBox.ts';
export default defineComponent({
name: 'createTable',
props: {
const props = defineProps({
visible: {
type: Boolean,
},
@@ -135,8 +149,11 @@ export default defineComponent({
db: {
type: String,
}
},
setup(props: any, { emit }) {
})
//定义事件
const emit = defineEmits(['update:visible', 'cancel', 'val-change'])
const formRef: any = ref();
const { proxy } = getCurrentInstance() as any;
@@ -248,13 +265,26 @@ export default defineComponent({
},
});
const {
dialogVisible,
btnloading,
activeName,
columnTypeList,
indexTypeList,
characterSetNameList,
collationNameList,
tableData,
} = toRefs(state)
watch(props, async (newValue) => {
state.dialogVisible = newValue.visible;
});
const cancel = () => {
emit('update:visible', false);
reset();
};
const addRow = () => {
state.tableData.fields.res.push({
name: '',
@@ -267,6 +297,7 @@ export default defineComponent({
remark: '',
});
};
const addIndex = () => {
state.tableData.indexs.res.push({
indexName: '',
@@ -276,6 +307,7 @@ export default defineComponent({
indexComment: '',
});
};
const addDefaultRows = () => {
state.tableData.fields.res.push(
{ name: 'id', type: 'bigint', length: '20', value: '', notNull: true, pri: true, auto_increment: true, remark: '主键ID' },
@@ -287,12 +319,15 @@ export default defineComponent({
{ name: 'update_time', type: 'datetime', length: '', value: '', notNull: true, pri: false, auto_increment: false, remark: '修改时间' },
);
};
const deleteRow = (index: any) => {
state.tableData.fields.res.splice(index, 1);
};
const deleteIndex = (index: any) => {
state.tableData.indexs.res.splice(index, 1);
};
const submit = async () => {
let sql = genSql();
if (!sql) {
@@ -379,7 +414,7 @@ export default defineComponent({
let data = state.tableData;
// 创建表
if(!props.data.edit){
if (!props.data?.edit) {
if (state.activeName === '1') {// 创建表结构
let primary_key = '';
let fields: string[] = [];
@@ -514,7 +549,7 @@ export default defineComponent({
};
const oldData = { indexs: [] as any[], fields: [] as any[] }
watch(()=>props.data, (newValue)=>{
watch(() => props.data, (newValue: any) => {
const { row, indexs, columns } = newValue;
// 回显表名表注释
state.tableData.tableName = row.tableName
@@ -563,20 +598,5 @@ export default defineComponent({
})
}
})
return {
...toRefs(state),
formRef,
cancel,
reset,
addDefaultRows,
addRow,
deleteRow,
addIndex,
deleteIndex,
submit,
};
},
});
</script>

View File

@@ -1,6 +1,7 @@
<template>
<div>
<el-dialog :title="title" v-model="dialogVisible" :before-close="cancel" :close-on-click-modal="false" :destroy-on-close="true" width="38%">
<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="tagId" label="标签:" required>
<tag-select v-model:tag-id="form.tagId" v-model:tag-path="form.tagPath" style="width: 100%" />
@@ -17,7 +18,8 @@
</el-form-item>
<el-form-item prop="host" label="host:" required>
<el-col :span="18">
<el-input :disabled="form.id!==undefined" v-model.trim="form.host" placeholder="请输入主机ip" auto-complete="off"></el-input>
<el-input :disabled="form.id !== undefined" v-model.trim="form.host" placeholder="请输入主机ip"
auto-complete="off"></el-input>
</el-col>
<el-col style="text-align: center" :span="1">:</el-col>
<el-col :span="5">
@@ -28,17 +30,14 @@
<el-input v-model.trim="form.username" placeholder="请输入用户名"></el-input>
</el-form-item>
<el-form-item prop="password" label="密码:">
<el-input
type="password"
show-password
v-model.trim="form.password"
placeholder="请输入密码,修改操作可不填"
autocomplete="new-password"
>
<el-input type="password" show-password v-model.trim="form.password" placeholder="请输入密码,修改操作可不填"
autocomplete="new-password">
<template v-if="form.id && form.id != 0" #suffix>
<el-popover @hide="pwd = ''" placement="right" title="原密码" :width="200" trigger="click" :content="pwd">
<el-popover @hide="pwd = ''" placement="right" title="原密码" :width="200" trigger="click"
:content="pwd">
<template #reference>
<el-link @click="getDbPwd" :underline="false" type="primary" class="mr5">原密码</el-link>
<el-link @click="getDbPwd" :underline="false" type="primary" class="mr5">原密码
</el-link>
</template>
</el-popover>
</template>
@@ -47,28 +46,22 @@
<el-form-item prop="params" label="连接参数:">
<el-input v-model.trim="form.params" placeholder="其他连接参数,形如: key1=value1&key2=value2">
<template v-if="form.id && form.id != 0" #suffix>
<el-link target="_blank" href="https://github.com/go-sql-driver/mysql#dsn-data-source-name" :underline="false" type="primary" class="mr5">参数参考</el-link>
<el-link target="_blank" href="https://github.com/go-sql-driver/mysql#dsn-data-source-name"
:underline="false" type="primary" class="mr5">参数参考</el-link>
</template>
</el-input>
</el-form-item>
<el-form-item prop="database" label="数据库名:" required>
<el-col :span="19">
<el-select
@change="changeDatabase"
v-model="databaseList"
multiple
clearable
collapse-tags
collapse-tags-tooltip
filterable
allow-create
placeholder="请确保数据库实例信息填写完整后获取库名"
style="width: 100%"
>
<el-select @change="changeDatabase" v-model="databaseList" multiple clearable collapse-tags
collapse-tags-tooltip filterable allow-create placeholder="请确保数据库实例信息填写完整后获取库名"
style="width: 100%">
<el-option v-for="db in allDatabases" :key="db" :label="db" :value="db" />
</el-select>
</el-col>
<el-col style="text-align: center" :span="1"><el-divider direction="vertical" border-style="dashed" /></el-col>
<el-col style="text-align: center" :span="1">
<el-divider direction="vertical" border-style="dashed" />
</el-col>
<el-col :span="4">
<el-link @click="getAllDatabase" :underline="false" type="success">获取库名</el-link>
</el-col>
@@ -80,17 +73,14 @@
<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>
<el-checkbox @change="getSshTunnelMachines" v-model="form.enableSshTunnel" :true-label="1"
:false-label="-1"></el-checkbox>
</el-col>
<el-col :span="5" v-if="form.enableSshTunnel == 1"> 机器: </el-col>
<el-col :span="16" v-if="form.enableSshTunnel == 1">
<el-select style="width: 100%" v-model="form.sshTunnelMachineId" placeholder="请选择SSH隧道机器">
<el-option
v-for="item in sshTunnelMachineList"
:key="item.id"
:label="`${item.ip}:${item.port} [${item.name}]`"
:value="item.id"
>
<el-option v-for="item in sshTunnelMachineList" :key="item.id"
:label="`${item.ip}:${item.port} [${item.name}]`" :value="item.id">
</el-option>
</el-select>
</el-col>
@@ -107,8 +97,8 @@
</div>
</template>
<script lang="ts">
import { toRefs, reactive, watch, defineComponent, ref } from 'vue';
<script lang="ts" setup>
import { toRefs, reactive, watch, ref } from 'vue';
import { dbApi } from './api';
import { machineApi } from '../machine/api.ts';
import { ElMessage } from 'element-plus';
@@ -116,70 +106,26 @@ import { notBlank } from '@/common/assert';
import { RsaEncrypt } from '@/common/rsa';
import TagSelect from '../component/TagSelect.vue';
export default defineComponent({
name: 'DbEdit',
components: {
TagSelect,
},
props: {
const props = defineProps({
visible: {
type: Boolean,
},
projects: {
type: Array,
},
db: {
type: [Boolean, Object],
},
title: {
type: String,
},
},
setup(props: any, { emit }) {
const dbForm: any = ref(null);
})
const state = reactive({
dialogVisible: false,
projects: [],
envs: [],
allDatabases: [] as any,
databaseList: [] as any,
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,
params: null,
database: '',
project: null,
projectId: null,
envId: null,
env: null,
remark: '',
enableSshTunnel: null,
sshTunnelMachineId: null,
},
// 原密码
pwd: '',
btnLoading: false,
rules: {
projectId: [
//定义事件
const emit = defineEmits(['update:visible', 'cancel', 'val-change'])
const rules = {
tagId: [
{
required: true,
message: '请选择项目',
trigger: ['change', 'blur'],
},
],
envId: [
{
required: true,
message: '请选择环境',
message: '请选择标签',
trigger: ['change', 'blur'],
},
],
@@ -218,21 +164,60 @@ export default defineComponent({
trigger: ['change', 'blur'],
},
],
}
const dbForm: any = ref(null);
const state = reactive({
dialogVisible: false,
allDatabases: [] as any,
databaseList: [] as any,
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,
params: null,
database: '',
project: null,
projectId: null,
envId: null,
env: null,
remark: '',
enableSshTunnel: null,
sshTunnelMachineId: null,
},
// 原密码
pwd: '',
btnLoading: false,
});
watch(props, (newValue) => {
const {
dialogVisible,
allDatabases,
databaseList,
sshTunnelMachineList,
form,
pwd,
btnLoading,
} = toRefs(state)
watch(props, (newValue: any) => {
state.dialogVisible = newValue.visible;
if (!state.dialogVisible) {
return;
}
state.projects = newValue.projects;
if (newValue.db) {
state.form = { ...newValue.db };
// 将数据库名使用空格切割,获取所有数据库列表
state.databaseList = newValue.db.database.split(' ');
} else {
state.envs = [];
state.form = { port: 3306, enableSshTunnel: -1 } as any;
state.databaseList = [];
}
@@ -253,14 +238,6 @@ export default defineComponent({
}
};
const changeEnv = (envId: number) => {
for (let p of state.envs as any) {
if (p.id == envId) {
state.form.env = p.name;
}
}
};
const getAllDatabase = async () => {
const reqForm = { ...state.form };
reqForm.password = await RsaEncrypt(reqForm.password);
@@ -309,20 +286,7 @@ export default defineComponent({
resetInputDb();
}, 500);
};
return {
...toRefs(state),
dbForm,
getAllDatabase,
getDbPwd,
changeDatabase,
getSshTunnelMachines,
changeEnv,
btnOk,
cancel,
};
},
});
</script>
<style lang="scss">
</style>

View File

@@ -2,8 +2,10 @@
<div class="db-list">
<el-card>
<el-button v-auth="permissions.saveDb" type="primary" icon="plus" @click="editDb(true)">添加</el-button>
<el-button v-auth="permissions.saveDb" :disabled="chooseId == null" @click="editDb(false)" type="primary" icon="edit">编辑</el-button>
<el-button v-auth="permissions.delDb" :disabled="chooseId == null" @click="deleteDb(chooseId)" type="danger" icon="delete">删除</el-button>
<el-button v-auth="permissions.saveDb" :disabled="chooseId == null" @click="editDb(false)" type="primary"
icon="edit">编辑</el-button>
<el-button v-auth="permissions.delDb" :disabled="chooseId == null" @click="deleteDb(chooseId)" type="danger"
icon="delete">删除</el-button>
<div style="float: right">
<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>
@@ -30,19 +32,24 @@
<template #default="scope">
<el-popover placement="right" trigger="click" :width="300">
<template #reference>
<el-link type="primary" :underline="false" plain @click="selectDb(scope.row.dbs)">查看</el-link>
<el-link type="primary" :underline="false" plain @click="selectDb(scope.row.dbs)">查看
</el-link>
</template>
<el-input v-model="filterDb.param" @keyup="filterSchema" class="w-50 m-2" placeholder="搜索" size="small" >
<el-input v-model="filterDb.param" @keyup="filterSchema" class="w-50 m-2" placeholder="搜索"
size="small">
<template #prefix>
<el-icon class="el-input__icon"><search-icon /></el-icon>
<el-icon class="el-input__icon">
<search-icon />
</el-icon>
</template>
</el-input>
<div class="el-tag--plain el-tag--success"
v-for="db in filterDb.list" :key="db"
style="border:1px var(--color-success-light-3) solid; margin-top: 3px;border-radius: 5px; padding: 2px;position: relative"
>
<el-link type="success" plain size="small" :underline="false" @click="showTableInfo(scope.row, db)">{{ db }}</el-link>
<el-link type="primary" plain size="small" :underline="false" @click="openSqlExec(scope.row, db)" style="position: absolute; right: 4px">数据操作</el-link>
<div class="el-tag--plain el-tag--success" v-for="db in filterDb.list" :key="db"
style="border:1px var(--color-success-light-3) solid; margin-top: 3px;border-radius: 5px; padding: 2px;position: relative">
<el-link type="success" plain size="small" :underline="false"
@click="showTableInfo(scope.row, db)">{{ db }}</el-link>
<el-link type="primary" plain size="small" :underline="false"
@click="openSqlExec(scope.row, db)" style="position: absolute; right: 4px">数据操作
</el-link>
</div>
</el-popover>
</template>
@@ -53,25 +60,21 @@
<el-table-column min-width="115" prop="creator" label="创建账号"></el-table-column>
<el-table-column min-width="160" 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 label="操作" min-width="120" fixed="right">
<template #default="scope">
<el-link type="primary" plain size="small" :underline="false" @click="onShowSqlExec(scope.row)">SQL执行记录</el-link>
<el-link type="primary" plain size="small" :underline="false" @click="onShowSqlExec(scope.row)">
SQL执行记录</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-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>
@@ -90,10 +93,13 @@
</el-form-item>
<el-form-item label="导出表: ">
<el-table @selection-change="handleDumpTableSelectionChange" max-height="300" size="small" :data="tableInfoDialog.infos">
<el-table @selection-change="handleDumpTableSelectionChange" max-height="300" size="small"
:data="tableInfoDialog.infos">
<el-table-column type="selection" width="45" />
<el-table-column property="tableName" label="表名" min-width="150" show-overflow-tooltip> </el-table-column>
<el-table-column property="tableComment" label="备注" min-width="150" show-overflow-tooltip></el-table-column>
<el-table-column property="tableName" label="表名" min-width="150" show-overflow-tooltip>
</el-table-column>
<el-table-column property="tableComment" label="备注" min-width="150" show-overflow-tooltip>
</el-table-column>
</el-table>
</el-form-item>
@@ -105,40 +111,30 @@
<el-button type="primary" size="small" @click="openEditTable(false)">创建表</el-button>
</el-row>
<el-table v-loading="tableInfoDialog.loading" border stripe :data="filterTableInfos" size="small" max-height="680">
<el-table v-loading="tableInfoDialog.loading" border stripe :data="filterTableInfos" size="small"
max-height="680">
<el-table-column property="tableName" label="表名" min-width="150" show-overflow-tooltip>
<template #header>
<el-input v-model="tableInfoDialog.tableNameSearch" size="small" placeholder="表名: 输入可过滤" clearable />
<el-input v-model="tableInfoDialog.tableNameSearch" size="small" placeholder="表名: 输入可过滤"
clearable />
</template>
</el-table-column>
<el-table-column property="tableComment" label="备注" min-width="150" show-overflow-tooltip>
<template #header>
<el-input v-model="tableInfoDialog.tableCommentSearch" size="small" placeholder="备注: 输入可过滤" clearable />
<el-input v-model="tableInfoDialog.tableCommentSearch" size="small" placeholder="备注: 输入可过滤"
clearable />
</template>
</el-table-column>
<el-table-column
prop="tableRows"
label="Rows"
min-width="70"
sortable
:sort-method="(a, b) => parseInt(a.tableRows) - parseInt(b.tableRows)"
></el-table-column>
<el-table-column
property="dataLength"
label="数据大小"
sortable
:sort-method="(a, b) => parseInt(a.dataLength) - parseInt(b.dataLength)"
>
<el-table-column prop="tableRows" label="Rows" min-width="70" sortable
:sort-method="(a: any, b: any) => parseInt(a.tableRows) - parseInt(b.tableRows)"></el-table-column>
<el-table-column property="dataLength" label="数据大小" sortable
:sort-method="(a: any, b: any) => parseInt(a.dataLength) - parseInt(b.dataLength)">
<template #default="scope">
{{ formatByteSize(scope.row.dataLength) }}
</template>
</el-table-column>
<el-table-column
property="indexLength"
label="索引大小"
sortable
:sort-method="(a, b) => parseInt(a.indexLength) - parseInt(b.indexLength)"
>
<el-table-column property="indexLength" label="索引大小" sortable
:sort-method="(a: any, b: any) => parseInt(a.indexLength) - parseInt(b.indexLength)">
<template #default="scope">
{{ formatByteSize(scope.row.indexLength) }}
</template>
@@ -160,19 +156,18 @@
</el-table>
</el-dialog>
<el-dialog
width="90%"
:title="`${sqlExecLogDialog.title} - SQL执行记录`"
:before-close="onBeforeCloseSqlExecDialog"
v-model="sqlExecLogDialog.visible"
>
<el-dialog width="90%" :title="`${sqlExecLogDialog.title} - SQL执行记录`" :before-close="onBeforeCloseSqlExecDialog"
v-model="sqlExecLogDialog.visible">
<div class="toolbar">
<el-select v-model="sqlExecLogDialog.query.db" placeholder="请选择数据库" filterable clearable>
<el-option v-for="item in sqlExecLogDialog.dbs" :key="item" :label="`${item}`" :value="item"> </el-option>
<el-option v-for="item in sqlExecLogDialog.dbs" :key="item" :label="`${item}`" :value="item">
</el-option>
</el-select>
<el-input v-model="sqlExecLogDialog.query.table" placeholder="请输入表名" clearable class="ml5" style="width: 180px" />
<el-input v-model="sqlExecLogDialog.query.table" placeholder="请输入表名" clearable class="ml5"
style="width: 180px" />
<el-select v-model="sqlExecLogDialog.query.type" placeholder="请选择操作类型" clearable class="ml5">
<el-option v-for="item in enums.DbSqlExecTypeEnum" :key="item.value" :label="item.label" :value="item.value"> </el-option>
<el-option v-for="item in enums.DbSqlExecTypeEnum as any" :key="item.value" :label="item.label"
:value="item.value"> </el-option>
</el-select>
<el-button class="ml5" @click="searchSqlExecLog" type="success" icon="search"></el-button>
</div>
@@ -181,9 +176,12 @@
<el-table-column prop="table" label="" min-width="60" show-overflow-tooltip> </el-table-column>
<el-table-column prop="type" label="类型" width="85" show-overflow-tooltip>
<template #default="scope">
<el-tag v-if="scope.row.type == enums.DbSqlExecTypeEnum.UPDATE.value" color="#E4F5EB" size="small">UPDATE</el-tag>
<el-tag v-if="scope.row.type == enums.DbSqlExecTypeEnum.DELETE.value" color="#F9E2AE" size="small">DELETE</el-tag>
<el-tag v-if="scope.row.type == enums.DbSqlExecTypeEnum.INSERT.value" color="#A8DEE0" size="small">INSERT</el-tag>
<el-tag v-if="scope.row.type == enums.DbSqlExecTypeEnum['UPDATE'].value" color="#E4F5EB"
size="small">UPDATE</el-tag>
<el-tag v-if="scope.row.type == enums.DbSqlExecTypeEnum['DELETE'].value" color="#F9E2AE"
size="small">DELETE</el-tag>
<el-tag v-if="scope.row.type == enums.DbSqlExecTypeEnum['INSERT'].value" color="#A8DEE0"
size="small">INSERT</el-tag>
</template>
</el-table-column>
<el-table-column prop="sql" label="SQL" min-width="230" show-overflow-tooltip> </el-table-column>
@@ -198,31 +196,23 @@
<el-table-column label="操作" min-width="50" fixed="right">
<template #default="scope">
<el-link
v-if="scope.row.type == enums.DbSqlExecTypeEnum.UPDATE.value || scope.row.type == enums.DbSqlExecTypeEnum.DELETE.value"
type="primary"
plain
size="small"
:underline="false"
@click="onShowRollbackSql(scope.row)"
>还原SQL</el-link
>
v-if="scope.row.type == enums.DbSqlExecTypeEnum['UPDATE'].value || scope.row.type == enums.DbSqlExecTypeEnum['DELETE'].value"
type="primary" plain size="small" :underline="false" @click="onShowRollbackSql(scope.row)">
还原SQL</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="handleSqlExecPageChange"
:total="sqlExecLogDialog.total"
layout="prev, pager, next, total, jumper"
v-model:current-page="sqlExecLogDialog.query.pageNum"
:page-size="sqlExecLogDialog.query.pageSize"
></el-pagination>
<el-pagination style="text-align: right" @current-change="handleSqlExecPageChange"
:total="sqlExecLogDialog.total" layout="prev, pager, next, total, jumper"
v-model:current-page="sqlExecLogDialog.query.pageNum" :page-size="sqlExecLogDialog.query.pageSize">
</el-pagination>
</el-row>
</el-dialog>
<el-dialog width="55%" :title="`还原SQL`" v-model="rollbackSqlDialog.visible">
<el-input type="textarea" :autosize="{ minRows: 15, maxRows: 30 }" v-model="rollbackSqlDialog.sql" size="small"> </el-input>
<el-input type="textarea" :autosize="{ minRows: 15, maxRows: 30 }" v-model="rollbackSqlDialog.sql"
size="small"> </el-input>
</el-dialog>
<el-dialog width="40%" :title="`${chooseTableName} 字段信息`" v-model="columnDialog.visible">
@@ -236,30 +226,29 @@
<el-dialog width="40%" :title="`${chooseTableName} 索引信息`" v-model="indexDialog.visible">
<el-table border stripe :data="indexDialog.indexs" size="small">
<el-table-column prop="indexName" label="索引名" show-overflow-tooltip> </el-table-column>
<el-table-column prop="columnName" label="列名" show-overflow-tooltip> </el-table-column>
<el-table-column prop="indexName" label="索引名" min-width="120" show-overflow-tooltip> </el-table-column>
<el-table-column prop="columnName" label="列名" min-width="120" show-overflow-tooltip> </el-table-column>
<el-table-column prop="seqInIndex" label="列序列号" show-overflow-tooltip> </el-table-column>
<el-table-column prop="indexType" label="类型"> </el-table-column>
<el-table-column prop="indexComment" label="备注" min-width="230" show-overflow-tooltip> </el-table-column>
<el-table-column prop="indexComment" label="备注" min-width="130" show-overflow-tooltip>
</el-table-column>
</el-table>
</el-dialog>
<el-dialog width="55%" :title="`${chooseTableName} Create-DDL`" v-model="ddlDialog.visible">
<el-input disabled type="textarea" :autosize="{ minRows: 15, maxRows: 30 }" v-model="ddlDialog.ddl" size="small"> </el-input>
<el-input disabled type="textarea" :autosize="{ minRows: 15, maxRows: 30 }" v-model="ddlDialog.ddl"
size="small"> </el-input>
</el-dialog>
<db-edit
@val-change="valChange"
:title="dbEditDialog.title"
v-model:visible="dbEditDialog.visible"
v-model:db="dbEditDialog.data"
></db-edit>
<create-table :title="tableCreateDialog.title" :active-name="tableCreateDialog.activeName" :dbId="dbId" :db="db" :data="tableCreateDialog.data" v-model:visible="tableCreateDialog.visible"></create-table>
<db-edit @val-change="valChange" :title="dbEditDialog.title" v-model:visible="dbEditDialog.visible"
v-model:db="dbEditDialog.data"></db-edit>
<create-table :title="tableCreateDialog.title" :active-name="tableCreateDialog.activeName" :dbId="dbId" :db="db"
:data="tableCreateDialog.data" v-model:visible="tableCreateDialog.visible"></create-table>
</div>
</template>
<script lang='ts'>
import { toRefs, reactive, computed, onMounted, defineComponent } from 'vue';
<script lang='ts' setup>
import { toRefs, reactive, computed, onMounted } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { formatByteSize } from '@/common/utils/format';
import DbEdit from './DbEdit.vue';
@@ -276,24 +265,17 @@ import {store} from '@/store';
import { tagApi } from '../tag/api.ts';
import { dateFormat } from '@/common/utils/date';
export default defineComponent({
name: 'DbList',
components: {
DbEdit,
CreateTable,
SearchIcon,
},
setup() {
const permissions = {
saveDb: 'db:save',
delDb: 'db:del',
}
const state = reactive({
row: {},
dbId: 0,
db: '',
permissions: {
saveDb: 'db:save',
delDb: 'db:del',
},
tags: [],
chooseId: null,
chooseId: null as any,
/**
* 选中的数据
*/
@@ -358,7 +340,7 @@ export default defineComponent({
},
dbEditDialog: {
visible: false,
data: null,
data: null as any,
title: '新增数据库',
},
tableCreateDialog: {
@@ -379,6 +361,29 @@ export default defineComponent({
}
});
const {
dbId,
db,
tags,
chooseId,
query,
datas,
total,
showDumpInfo,
dumpInfo,
sqlExecLogDialog,
rollbackSqlDialog,
chooseTableName,
tableInfoDialog,
columnDialog,
indexDialog,
ddlDialog,
dbEditDialog,
tableCreateDialog,
filterDb,
} = toRefs(state)
onMounted(async () => {
search();
});
@@ -695,41 +700,7 @@ export default defineComponent({
state.tableCreateDialog.data = { edit: true, row, indexs, columns }
}
}
return {
...toRefs(state),
dateFormat,
getTags,
filterTableInfos,
enums,
search,
choose,
handlePageChange,
editDb,
valChange,
deleteDb,
onShowSqlExec,
handleDumpTableSelectionChange,
dump,
onBeforeCloseSqlExecDialog,
handleSqlExecPageChange,
searchSqlExecLog,
onShowRollbackSql,
showTableInfo,
refreshTableInfo,
closeTableInfo,
showColumns,
showTableIndex,
showCreateDdl,
dropTable,
formatByteSize,
openSqlExec,
selectDb,
filterSchema,
openEditTable,
};
},
});
</script>
<style lang="scss">
</style>

View File

@@ -5,21 +5,17 @@
<el-col :span="24">
<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-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="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">
<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: right; color: #8492a6; margin-left: 10px; font-size: 13px">{{
`${item.host}:${item.port} ${item.type}`
@@ -29,27 +25,19 @@
</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 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"
<el-select v-model="tableName" placeholder="选择表查看表数据" @change="changeTable" filterable
style="width: 250px">
<el-option v-for="item in tableMetadata as any" :key="item.tableName"
:label="item.tableName + (item.tableComment != '' ? `【${item.tableComment}】` : '')"
:value="item.tableName"
>
:value="item.tableName">
</el-option>
</el-select>
</el-form-item>
@@ -71,33 +59,27 @@
<div>
<div class="toolbar">
<div class="fl">
<el-link @click="onRunSql" :underline="false" class="ml15" icon="VideoPlay"></el-link>
<el-link @click="onRunSql" :underline="false" class="ml15" icon="VideoPlay">
</el-link>
<el-divider direction="vertical" border-style="dashed" />
<el-tooltip class="box-item" effect="dark" content="format sql" placement="top">
<el-link @click="formatSql" type="primary" :underline="false" icon="MagicStick"></el-link>
<el-link @click="formatSql" type="primary" :underline="false" icon="MagicStick">
</el-link>
</el-tooltip>
<el-divider direction="vertical" border-style="dashed" />
<el-tooltip class="box-item" effect="dark" content="commit" placement="top">
<el-link @click="onCommit" type="success" :underline="false" icon="CircleCheck"></el-link>
<el-link @click="onCommit" type="success" :underline="false" icon="CircleCheck">
</el-link>
</el-tooltip>
<el-divider direction="vertical" border-style="dashed" />
<el-upload
style="display: inline-block"
:before-upload="beforeUpload"
:on-success="execSqlFileSuccess"
:headers="{ Authorization: token }"
:data="{
<el-upload style="display: inline-block" :before-upload="beforeUpload"
:on-success="execSqlFileSuccess" :headers="{ Authorization: token }" :data="{
dbId: 1,
}"
:action="getUploadSqlFileUrl()"
:show-file-list="false"
name="file"
multiple
:limit="100"
>
}" :action="getUploadSqlFileUrl()" :show-file-list="false" name="file" multiple
:limit="100">
<el-tooltip class="box-item" effect="dark" content="SQL脚本执行" placement="top">
<el-link type="success" :underline="false" icon="Document"></el-link>
</el-tooltip>
@@ -105,23 +87,18 @@
</div>
<div style="float: right" class="fl">
<el-select
v-model="sqlName"
placeholder="选择or输入SQL模板名"
@change="changeSqlTemplate"
filterable
allow-create
default-first-option
size="small"
class="mr10"
>
<el-option v-for="item in sqlNames" :key="item" :label="item.database" :value="item">
<el-select v-model="sqlName" placeholder="选择or输入SQL模板名" @change="changeSqlTemplate"
filterable allow-create default-first-option size="small" class="mr10">
<el-option v-for="item in sqlNames as any" :key="item" :label="item.database"
:value="item">
{{ item }}
</el-option>
</el-select>
<el-button @click="saveSql" type="primary" icon="document-add" plain size="small">保存</el-button>
<el-button @click="deleteSql" type="danger" icon="delete" plain size="small">删除</el-button>
<el-button @click="saveSql" type="primary" icon="document-add" plain size="small">保存
</el-button>
<el-button @click="deleteSql" type="danger" icon="delete" plain size="small">删除
</el-button>
</div>
</div>
</div>
@@ -134,48 +111,24 @@
</div>
<div class="mt5">
<el-row>
<el-link
v-if="queryTab.nowTableName"
@click="onDeleteData"
class="ml5"
type="danger"
icon="delete"
:underline="false"
></el-link>
<el-link v-if="queryTab.nowTableName" @click="onDeleteData" class="ml5" type="danger"
icon="delete" :underline="false"></el-link>
<span v-if="queryTab.execRes.data.length > 0">
<el-divider direction="vertical" border-style="dashed" />
<el-link type="success" :underline="false" @click="exportData"><span style="font-size: 12px">导出</span></el-link>
<el-link type="success" :underline="false" @click="exportData"><span
style="font-size: 12px">导出</span></el-link>
</span>
</el-row>
<el-table
@cell-dblclick="cellClick"
@selection-change="onDataSelectionChange"
:data="queryTab.execRes.data"
v-loading="queryTab.loading"
element-loading-text="查询中..."
size="small"
max-height="800"
empty-text="tips: select *开头的单表查询或点击表名默认查询的数据,可双击数据在线修改"
stripe
border
class="mt5"
>
<el-table-column
v-if="queryTab.execRes.tableColumn.length > 0 && queryTab.nowTableName"
type="selection"
width="35"
/>
<el-table-column
min-width="100"
:width="flexColumnWidth(item, queryTab.execRes.data)"
align="center"
v-for="item in queryTab.execRes.tableColumn"
:key="item"
:prop="item"
:label="item"
show-overflow-tooltip
>
<el-table @cell-dblclick="cellClick" @selection-change="onDataSelectionChange"
:data="queryTab.execRes.data" v-loading="queryTab.loading" element-loading-text="查询中..."
size="small" max-height="250" empty-text="tips: select *开头的单表查询或点击表名默认查询的数据,可双击数据在线修改"
stripe border class="mt5">
<el-table-column v-if="queryTab.execRes.tableColumn.length > 0 && queryTab.nowTableName"
type="selection" width="35" />
<el-table-column min-width="100" :width="flexColumnWidth(item, queryTab.execRes.data)"
align="center" v-for="item in queryTab.execRes.tableColumn" :key="item" :prop="item"
:label="item" show-overflow-tooltip>
</el-table-column>
</el-table>
</div>
@@ -186,7 +139,8 @@
<el-tab-pane closable v-for="dt in dataTabs" :key="dt.name" :label="dt.label" :name="dt.name">
<el-row v-if="dbId">
<el-col :span="8">
<el-link @click="onRefresh(dt.name)" icon="refresh" :underline="false" class="ml5"></el-link>
<el-link @click="onRefresh(dt.name)" icon="refresh" :underline="false" class="ml5">
</el-link>
<el-divider direction="vertical" border-style="dashed" />
<el-link @click="addRow" type="primary" icon="plus" :underline="false"></el-link>
@@ -196,7 +150,8 @@
<el-divider direction="vertical" border-style="dashed" />
<el-tooltip class="box-item" effect="dark" content="commit" placement="top">
<el-link @click="onCommit" type="success" icon="CircleCheck" :underline="false"></el-link>
<el-link @click="onCommit" type="success" icon="CircleCheck" :underline="false">
</el-link>
</el-tooltip>
<el-divider direction="vertical" border-style="dashed" />
@@ -205,67 +160,42 @@
</el-tooltip>
</el-col>
<el-col :span="16">
<el-input
v-model="dt.condition"
placeholder="若需条件过滤,可选择列并点击对应的字段并输入需要过滤的内容点击查询按钮即可"
clearable
size="small"
style="width: 100%"
>
<el-input v-model="dt.condition" placeholder="若需条件过滤,可选择列并点击对应的字段并输入需要过滤的内容点击查询按钮即可"
clearable size="small" style="width: 100%">
<template #prepend>
<el-popover trigger="click" :width="320" placement="right">
<template #reference>
<el-link type="success" :underline="false">选择列</el-link>
</template>
<el-table
:data="getColumns4Map(dt.name)"
max-height="500"
size="small"
<el-table :data="getColumns4Map(dt.name)" max-height="500" size="small"
@row-click="
(...event) => {
(...event: any) => {
onConditionRowClick(event, dt);
}
"
style="cursor: pointer"
>
<el-table-column property="columnName" label="列名" show-overflow-tooltip> </el-table-column>
<el-table-column property="columnComment" label="备注" show-overflow-tooltip> </el-table-column>
" style="cursor: pointer">
<el-table-column property="columnName" label="列名" show-overflow-tooltip>
</el-table-column>
<el-table-column property="columnComment" label="备注" show-overflow-tooltip>
</el-table-column>
</el-table>
</el-popover>
</template>
<template #append>
<el-button @click="selectByCondition(dt.name, dt.condition)" icon="search" size="small"></el-button>
<el-button @click="selectByCondition(dt.name, dt.condition)" icon="search"
size="small"></el-button>
</template>
</el-input>
</el-col>
</el-row>
<el-table
@cell-dblclick="cellClick"
@sort-change="onTableSortChange"
@selection-change="onDataSelectionChange"
:data="dt.datas"
size="small"
:max-height="dataTabsTableHeight"
v-loading="dt.loading"
element-loading-text="查询中..."
empty-text="暂无数据"
stripe
border
class="mt5"
>
<el-table @cell-dblclick="cellClick" @sort-change="onTableSortChange"
@selection-change="onDataSelectionChange" :data="dt.datas" size="small"
:max-height="dataTabsTableHeight" v-loading="dt.loading" element-loading-text="查询中..."
empty-text="暂无数据" stripe border class="mt5">
<el-table-column v-if="dt.datas.length > 0" type="selection" width="35" />
<el-table-column
min-width="100"
:width="flexColumnWidth(item, dt.datas)"
align="center"
v-for="item in dt.columnNames"
:key="item"
:prop="item"
:label="item"
show-overflow-tooltip
:sortable="nowTableName != '' ? 'custom' : false"
>
<el-table-column min-width="100" :width="flexColumnWidth(item, dt.datas)" align="center"
v-for="item in dt.columnNames" :key="item" :prop="item" :label="item" show-overflow-tooltip
:sortable="nowTableName != '' ? 'custom' : false">
<template #header>
<el-tooltip raw-content placement="top" effect="customized">
<template #content> {{ getColumnTip(dt.name, item) }} </template>
@@ -275,14 +205,9 @@
</el-table-column>
</el-table>
<el-row type="flex" class="mt5" justify="center">
<el-pagination
small
:total="dt.count"
@current-change="handlePageChange(dt)"
layout="prev, pager, next, total, jumper"
v-model:current-page="dt.pageNum"
:page-size="defalutLimit"
></el-pagination>
<el-pagination small :total="dt.count" @current-change="handlePageChange(dt)"
layout="prev, pager, next, total, jumper" v-model:current-page="dt.pageNum"
:page-size="defalutLimit"></el-pagination>
</el-row>
</el-tab-pane>
</el-tabs>
@@ -318,8 +243,8 @@
</div>
</template>
<script lang="ts">
import { onMounted, toRefs, reactive, defineComponent, ref, watch } from 'vue';
<script lang="ts" setup>
import { onMounted, toRefs, reactive, ref, watch } from 'vue';
import { dbApi } from './api';
import 'codemirror/addon/hint/show-hint.css';
@@ -350,10 +275,6 @@ import { editor, languages, Position} from 'monaco-editor';
import ITextModel = editor.ITextModel;
import CompletionItem = languages.CompletionItem;
export default defineComponent({
name: 'SqlExec',
components: {},
setup() {
const store = useStore();
const codeTextarea: any = ref(null);
const monacoTextarea: any = ref(null);
@@ -361,9 +282,29 @@ export default defineComponent({
let codemirror = null as any;
const tableMap = new Map();
const defalutLimit = 20
const cmOptions = {
tabSize: 4,
mode: 'text/x-sql',
lineNumbers: true,
line: true,
indentWithTabs: true,
smartIndent: true,
matchBrackets: true,
theme: 'base16-light',
autofocus: true,
extraKeys: { Tab: 'autocomplete' }, // 自定义快捷键
hintOptions: {
completeSingle: false,
// 自定义提示选项
tables: {},
},
// more CodeMirror options...
}
const state = reactive({
token: token,
defalutLimit: 20, // 默认查询数量
tags: [],
dbs: [] as any, // 数据库实例列表
databaseList: [], // 数据库实例拥有的数据库列表1数据库实例 -> 多数据库
@@ -376,7 +317,6 @@ export default defineComponent({
sqlName: '', // 当前sql模板名
sqlNames: [], // 所有sql模板名
activeName: 'Query',
queryTabName: 'Query',
nowTableName: '', // 当前表格数据操作的数据库表名,用于双击编辑表内容使用
dataTabs: {} as any, // 点击表信息后执行结果数据展示tabs
dataTabsTableHeight: 600,
@@ -420,9 +360,29 @@ export default defineComponent({
}
});
const {
tags,
dbs,
databaseList,
db,
dbId,
tableName,
tableMetadata,
sqlName,
sqlNames,
activeName,
nowTableName,
dataTabs,
dataTabsTableHeight,
queryTab,
params,
conditionDialog,
genSqlDialog,
} = toRefs(state)
const initCodemirror = () => {
// 初始化编辑器实例,传入需要被实例化的文本域对象和默认配置
codemirror = _CodeMirror.fromTextArea(codeTextarea.value, state.cmOptions);
codemirror = _CodeMirror.fromTextArea(codeTextarea.value, cmOptions);
codemirror.on('inputRead', (instance: any, changeObj: any) => {
if (/^[a-zA-Z]/.test(changeObj.text[0])) {
instance.showHint();
@@ -663,7 +623,7 @@ onMounted(() => {
};
/**
* 项目及环境更改后的回调事件
* 标签更改后的回调事件
*/
const changeTag = () => {
state.dbs = [];
@@ -678,11 +638,11 @@ onMounted(() => {
state.tags = await tagApi.getAccountTags.request(null);
};
const onBeforeChange = (instance: any, changeObj: any) => {
var text = changeObj.text[0];
// 将sql提示去除
changeObj.text[0] = text.split(' ')[0];
};
// const onBeforeChange = (instance: any, changeObj: any) => {
// var text = changeObj.text[0];
// // 将sql提示去除
// changeObj.text[0] = text.split(' ')[0];
// };
/**
* 执行sql
@@ -966,7 +926,7 @@ onMounted(() => {
db,
})
.then((res) => {
state.cmOptions.hintOptions.tables = res;
cmOptions.hintOptions.tables = res;
});
getSqlNames();
};
@@ -1128,10 +1088,10 @@ const addTableSuggestions = (tables: any[]) => {
const getDefaultSelectSql = (tableName: string, where: string = '', orderBy: string = '', pageNum: number = 1) => {
const baseSql = `SELECT * FROM ${tableName} ${where ? 'WHERE ' + where : ''} ${orderBy ? orderBy : ''}`;
if (state.dbType == 'mysql') {
return `${baseSql} LIMIT ${(pageNum - 1) * state.defalutLimit}, ${state.defalutLimit};`;
return `${baseSql} LIMIT ${(pageNum - 1) * defalutLimit}, ${defalutLimit};`;
}
if (state.dbType == 'postgres') {
return `${baseSql} OFFSET ${(pageNum - 1) * state.defalutLimit} LIMIT ${state.defalutLimit};`;
return `${baseSql} OFFSET ${(pageNum - 1) * defalutLimit} LIMIT ${defalutLimit};`;
}
return baseSql;
};
@@ -1257,7 +1217,7 @@ const addTableSuggestions = (tables: any[]) => {
state.activeName = state.queryTab.name;
state.queryTab.execRes.data = [];
state.queryTab.execRes.tableColumn = [];
state.cmOptions.hintOptions.tables = [];
cmOptions.hintOptions.tables = [];
tableMap.clear();
};
@@ -1471,48 +1431,6 @@ const addTableSuggestions = (tables: any[]) => {
watch(store.state.sqlExecInfo, async (newValue) => {
await setSelects(newValue);
});
return {
...toRefs(state),
getTags,
codeTextarea,
monacoTextarea,
changeTag,
changeTable,
cellClick,
onRunSql,
exportData,
removeDataTab,
onDataTabClick,
beforeUpload,
getUploadSqlFileUrl,
execSqlFileSuccess,
flexColumnWidth,
getColumnTip,
getColumns4Map,
onConditionRowClick,
onConfirmCondition,
onCancelCondition,
changeSqlTemplate,
deleteSql,
saveSql,
changeDbInstance,
changeDb,
clearDb,
formatSql,
onBeforeChange,
onRefresh,
handlePageChange,
selectByCondition,
onCommit,
addRow,
onDataSelectionChange,
onDeleteData,
onTableSortChange,
onGenerateInsertSql,
};
},
});
</script>
<style lang="scss">
@@ -1520,15 +1438,19 @@ const addTableSuggestions = (tables: any[]) => {
font-size: 8pt;
font-weight: 600;
border: 1px solid #ccc;
.CodeMirror {
flex-grow: 1;
z-index: 1;
.CodeMirror-code {
line-height: 19px;
}
font-family: 'JetBrainsMono';
}
}
.el-tabs__header {
padding: 0 10px;
background-color: #fff;

View File

@@ -2,19 +2,18 @@
<div>
<el-dialog :title="`${title} 详情`" v-model="dialogVisible" :before-close="cancel" width="90%">
<el-table @cell-click="cellClick" :data="data.res">
<el-table-column :width="200" :prop="item" :label="item" v-for="item in data.colNames" :key="item"> </el-table-column>
<el-table-column :width="200" :prop="item" :label="item" v-for="item in data.colNames" :key="item">
</el-table-column>
</el-table>
</el-dialog>
</div>
</template>
<script lang="ts">
import { watch, toRefs, reactive, defineComponent } from 'vue';
<script lang="ts" setup>
import { watch, toRefs, reactive } from 'vue';
export default defineComponent({
name: 'tableEdit',
props: {
const props = defineProps({
visible: {
type: Boolean,
},
@@ -24,8 +23,11 @@ export default defineComponent({
data: {
type: Object,
},
},
setup(props: any, { emit }) {
})
//定义事件
const emit = defineEmits(['update:visible'])
const state = reactive({
dialogVisible: false,
data: {
@@ -34,12 +36,18 @@ export default defineComponent({
},
});
watch(props, async (newValue) => {
const {
dialogVisible,
data,
} = toRefs(state)
watch(props, async (newValue: any) => {
state.dialogVisible = newValue.visible;
state.data.res = newValue.data.res;
state.data.colNames = newValue.data.colNames;
});
const cellClick = (row: any, column: any, cell: any, event: any) => {
const cellClick = (row: any, column: any, cell: any) => {
let isDiv = cell.children[0].tagName === 'DIV';
let text = cell.children[0].innerText;
let div = cell.children[0];
@@ -54,15 +62,10 @@ export default defineComponent({
});
}
};
const cancel = () => {
emit('update:visible', false);
};
return {
...toRefs(state),
cancel,
cellClick,
};
},
});
</script>

View File

@@ -2,7 +2,8 @@
<div>
<el-dialog title="待执行SQL" v-model="dialogVisible" :show-close="false" width="600px">
如需执行多条sql需要在数据库管理配置连接参数multiStatements=true
<codemirror height="350px" class="codesql" ref="cmEditor" language="sql" v-model="sqlValue" :options="cmOptions" />
<codemirror height="350px" class="codesql" ref="cmEditor" language="sql" v-model="sqlValue"
:options="cmOptions" />
<el-input ref="remarkInputRef" v-model="remark" placeholder="请输入执行备注" class="mt5" />
<template #footer>
<span class="dialog-footer">
@@ -14,8 +15,8 @@
</div>
</template>
<script lang="ts">
import { toRefs, ref, nextTick, reactive, defineComponent } from 'vue';
<script lang="ts" setup>
import { toRefs, ref, nextTick, reactive } from 'vue';
import { dbApi } from '../api';
import { ElDialog, ElButton, ElInput, ElMessage, InputInstance } from 'element-plus';
// import base style
@@ -28,15 +29,7 @@ import { format as sqlFormatter } from 'sql-formatter';
import { SqlExecProps } from './SqlExecBox';
export default defineComponent({
name: 'SqlExecDialog',
components: {
codemirror,
ElButton,
ElDialog,
ElInput,
},
props: {
const props = defineProps({
visible: {
type: Boolean,
},
@@ -49,17 +42,9 @@ export default defineComponent({
sql: {
type: String,
},
},
setup(props: any) {
const remarkInputRef = ref<InputInstance>();
const state = reactive({
dialogVisible: false,
sqlValue: '',
dbId: 0,
db: '',
remark: '',
btnLoading: false,
cmOptions: {
})
const cmOptions = {
tabSize: 4,
mode: 'text/x-sql',
lineNumbers: true,
@@ -70,10 +55,26 @@ export default defineComponent({
theme: 'base16-light',
autofocus: true,
extraKeys: { Tab: 'autocomplete' }, // 自定义快捷键
},
});
state.sqlValue = props.sql;
}
const remarkInputRef = ref<InputInstance>();
const state = reactive({
dialogVisible: false,
sqlValue: '',
dbId: 0,
db: '',
remark: '',
btnLoading: false,
});
const {
dialogVisible,
sqlValue,
remark,
btnLoading
} = toRefs(state)
state.sqlValue = props.sql as any;
let runSuccessCallback: any;
let cancelCallback: any;
let runSuccess: boolean = false;
@@ -138,16 +139,6 @@ export default defineComponent({
});
});
};
return {
...toRefs(state),
remarkInputRef,
open,
runSql,
cancel,
};
},
});
</script>
<style lang="scss">
.codesql {

View File

@@ -3,79 +3,56 @@
<el-dialog :title="title" v-model="dialogVisible" :show-close="true" :before-close="handleClose" width="800px">
<div class="toolbar">
<div style="float: right">
<el-button v-auth="'machine:file:add'" type="primary" @click="add" icon="plus" size="small" plain>添加</el-button>
<el-button v-auth="'machine:file:add'" type="primary" @click="add" icon="plus" size="small" plain>添加
</el-button>
</div>
</div>
<el-table :data="fileTable" stripe style="width: 100%">
<el-table-column prop="name" label="名称" width>
<template #default="scope">
<el-input v-model="scope.row.name" size="small" :disabled="scope.row.id != null" clearable></el-input>
<el-input v-model="scope.row.name" size="small" :disabled="scope.row.id != null" clearable>
</el-input>
</template>
</el-table-column>
<el-table-column prop="name" label="类型" min-width="50px">
<template #default="scope">
<el-select :disabled="scope.row.id != null" size="small" v-model="scope.row.type" style="width: 100px" placeholder="请选择">
<el-option v-for="item in enums.FileTypeEnum" :key="item.value" :label="item.label" :value="item.value"></el-option>
<el-select :disabled="scope.row.id != null" size="small" v-model="scope.row.type"
style="width: 100px" placeholder="请选择">
<el-option v-for="item in enums.FileTypeEnum as any" :key="item.value" :label="item.label"
:value="item.value"></el-option>
</el-select>
</template>
</el-table-column>
<el-table-column prop="path" label="路径" width>
<template #default="scope">
<el-input v-model="scope.row.path" :disabled="scope.row.id != null" size="small" clearable></el-input>
<el-input v-model="scope.row.path" :disabled="scope.row.id != null" size="small" clearable>
</el-input>
</template>
</el-table-column>
<el-table-column label="操作" width>
<template #default="scope">
<el-button v-if="scope.row.id == null" @click="addFiles(scope.row)" type="success" icon="success-filled" size="small" plain
>确定</el-button
>
<el-button v-if="scope.row.id != null" @click="getConf(scope.row)" type="primary" icon="tickets" size="small" plain
>查看</el-button
>
<el-button
v-auth="'machine:file:del'"
type="danger"
@click="deleteRow(scope.$index, scope.row)"
icon="delete"
size="small"
plain
>删除</el-button
>
<el-button v-if="scope.row.id == null" @click="addFiles(scope.row)" type="success"
icon="success-filled" size="small" plain>确定</el-button>
<el-button v-if="scope.row.id != null" @click="getConf(scope.row)" type="primary" icon="tickets"
size="small" plain>查看</el-button>
<el-button v-auth="'machine:file:del'" type="danger" @click="deleteRow(scope.$index, scope.row)"
icon="delete" size="small" plain>删除</el-button>
</template>
</el-table-column>
</el-table>
<el-row style="margin-top: 10px" type="flex" justify="end">
<el-pagination
small
style="text-align: center"
:total="total"
layout="prev, pager, next, total, jumper"
v-model:current-page="query.pageNum"
:page-size="query.pageSize"
@current-change="handlePageChange"
></el-pagination>
<el-pagination small style="text-align: center" :total="total" layout="prev, pager, next, total, jumper"
v-model:current-page="query.pageNum" :page-size="query.pageSize" @current-change="handlePageChange">
</el-pagination>
</el-row>
</el-dialog>
<el-dialog :title="tree.title" v-model="tree.visible" :close-on-click-modal="false" width="70%">
<el-progress
v-if="uploadProgressShow"
style="width: 90%; margin-left: 20px"
:text-inside="true"
:stroke-width="20"
:percentage="progressNum"
/>
<el-progress v-if="uploadProgressShow" style="width: 90%; margin-left: 20px" :text-inside="true"
:stroke-width="20" :percentage="progressNum" />
<div style="height: 45vh; overflow: auto">
<el-tree
v-if="tree.visible"
ref="fileTree"
:highlight-current="true"
:load="loadNode"
:props="props"
lazy
node-key="id"
:expand-on-click-node="true"
>
<el-tree v-if="tree.visible" ref="fileTree" :highlight-current="true" :load="loadNode"
:props="treeProps" lazy node-key="id" :expand-on-click-node="true">
<template #default="{ node, data }">
<span class="custom-tree-node">
<el-dropdown size="small" @visible-change="getFilePath(data, $event)" trigger="contextmenu">
@@ -97,31 +74,25 @@
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item
@click="getFileContent(tree.folder.id, data.path)"
v-if="data.type == '-' && data.size < 1 * 1024 * 1024"
>
<el-dropdown-item @click="getFileContent(tree.folder.id, data.path)"
v-if="data.type == '-' && data.size < 1 * 1024 * 1024">
<el-link type="info" icon="view" :underline="false">查看</el-link>
</el-dropdown-item>
<span v-auth="'machine:file:write'">
<el-dropdown-item @click="showCreateFileDialog(node, data)" v-if="data.type == 'd'">
<el-link type="primary" icon="document" :underline="false" style="margin-left: 2px">新建</el-link>
<el-dropdown-item @click="showCreateFileDialog(node)"
v-if="data.type == 'd'">
<el-link type="primary" icon="document" :underline="false"
style="margin-left: 2px">新建</el-link>
</el-dropdown-item>
</span>
<span v-auth="'machine:file:upload'">
<el-dropdown-item v-if="data.type == 'd'">
<el-upload
:before-upload="beforeUpload"
:on-success="uploadSuccess"
action=""
:http-request="getUploadFile"
:headers="{ token }"
:show-file-list="false"
name="file"
style="display: inline-block; margin-left: 2px"
>
<el-upload :before-upload="beforeUpload" :on-success="uploadSuccess"
action="" :http-request="getUploadFile" :headers="{ token }"
:show-file-list="false" name="file"
style="display: inline-block; margin-left: 2px">
<el-link icon="upload" :underline="false">上传</el-link>
</el-upload>
</el-dropdown-item>
@@ -129,21 +100,25 @@
<span v-auth="'machine:file:write'">
<el-dropdown-item @click="downloadFile(node, data)" v-if="data.type == '-'">
<el-link type="primary" icon="download" :underline="false" style="margin-left: 2px">下载</el-link>
<el-link type="primary" icon="download" :underline="false"
style="margin-left: 2px">下载</el-link>
</el-dropdown-item>
</span>
<span v-auth="'machine:file:rm'">
<el-dropdown-item @click="deleteFile(node, data)" v-if="!dontOperate(data)">
<el-link type="danger" icon="delete" :underline="false" style="margin-left: 2px">删除</el-link>
<el-link type="danger" icon="delete" :underline="false"
style="margin-left: 2px">删除</el-link>
</el-dropdown-item>
</span>
</el-dropdown-menu>
</template>
</el-dropdown>
<span style="display: inline-block" class="ml15">
<span style="color: #67c23a" v-if="data.type == '-'">[{{ formatFileSize(data.size) }}]</span>
<span v-if="data.mode" style="color: #67c23a">&nbsp;[{{ data.mode }} {{ data.modTime }}]</span>
<span style="color: #67c23a" v-if="data.type == '-'">[{{ formatFileSize(data.size)
}}]</span>
<span v-if="data.mode" style="color: #67c23a">&nbsp;[{{ data.mode }} {{ data.modTime
}}]</span>
</span>
</span>
</template>
@@ -151,15 +126,8 @@
</div>
</el-dialog>
<el-dialog
:destroy-on-close="true"
title="新建文件"
v-model="createFileDialog.visible"
:before-close="closeCreateFileDialog"
:close-on-click-modal="false"
top="5vh"
width="400px"
>
<el-dialog :destroy-on-close="true" title="新建文件" v-model="createFileDialog.visible"
:before-close="closeCreateFileDialog" :close-on-click-modal="false" top="5vh" width="400px">
<div>
<el-form-item prop="name" label="名称:">
<el-input v-model.trim="createFileDialog.name" placeholder="请输入名称" auto-complete="off"></el-input>
@@ -180,16 +148,11 @@
</template>
</el-dialog>
<el-dialog
:destroy-on-close="true"
:title="fileContent.dialogTitle"
v-model="fileContent.contentVisible"
:close-on-click-modal="false"
top="5vh"
width="70%"
>
<el-dialog :destroy-on-close="true" :title="fileContent.dialogTitle" v-model="fileContent.contentVisible"
:close-on-click-modal="false" top="5vh" width="70%">
<div>
<codemirror :can-change-mode="true" ref="cmEditor" v-model="fileContent.content" :language="fileContent.type" />
<codemirror :can-change-mode="true" ref="cmEditor" v-model="fileContent.content"
:language="fileContent.type" />
</div>
<template #footer>
@@ -202,8 +165,8 @@
</div>
</template>
<script lang="ts">
import { ref, toRefs, reactive, watch, defineComponent } from 'vue';
<script lang="ts" setup>
import { ref, toRefs, reactive, watch } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { machineApi } from './api';
@@ -213,18 +176,20 @@ import enums from './enums';
import config from '@/common/config';
import { isTrue } from '@/common/assert';
export default defineComponent({
name: 'FileManage',
components: {
codemirror,
},
props: {
const props = defineProps({
visible: { type: Boolean },
machineId: { type: Number },
title: { type: String },
},
})
const emit = defineEmits(['update:visible', 'cancel', 'update:machineId'])
const treeProps = {
label: 'name',
children: 'zones',
isLeaf: 'leaf',
}
setup(props: any, { emit }) {
const addFile = machineApi.addConf;
const delFile = machineApi.delConf;
const updateFileContent = machineApi.updateFileContent;
@@ -268,18 +233,13 @@ export default defineComponent({
},
resolve: {},
},
props: {
label: 'name',
children: 'zones',
isLeaf: 'leaf',
},
progressNum: 0,
uploadProgressShow: false,
dataObj: {
name: '',
path: '',
type: '',
},
progressNum: 0,
uploadProgressShow: false,
createFileDialog: {
visible: false,
name: '',
@@ -289,6 +249,18 @@ export default defineComponent({
file: null as any,
});
const {
dialogVisible,
query,
total,
fileTable,
fileContent,
tree,
progressNum,
uploadProgressShow,
createFileDialog,
} = toRefs(state)
watch(props, async (newValue) => {
if (newValue.machineId && newValue.visible) {
await getFiles();
@@ -297,7 +269,7 @@ export default defineComponent({
});
const getFiles = async () => {
state.query.id = props.machineId;
state.query.id = props.machineId as any;
const res = await files.request(state.query);
state.fileTable = res.list;
state.total = res.total;
@@ -536,7 +508,7 @@ export default defineComponent({
const params = new FormData();
params.append('file', content.file);
params.append('path', state.dataObj.path);
params.append('machineId', props.machineId);
params.append('machineId', props.machineId as any);
params.append('fileId', state.tree.folder.id as any);
params.append('token', token);
machineApi.uploadFile
@@ -615,36 +587,7 @@ export default defineComponent({
}
return '-';
};
return {
...toRefs(state),
fileTree,
enums,
token,
add,
getFiles,
handlePageChange,
addFiles,
deleteRow,
getConf,
getFileContent,
updateContent,
handleClose,
loadNode,
showCreateFileDialog,
closeCreateFileDialog,
createFile,
deleteFile,
downloadFile,
getUploadFile,
beforeUpload,
getFilePath,
uploadSuccess,
dontOperate,
formatFileSize,
};
},
});
</script>
<style lang="scss">
</style>

View File

@@ -1,6 +1,7 @@
<template>
<div>
<el-dialog :title="title" v-model="dialogVisible" :close-on-click-modal="false" :destroy-on-close="true" :before-close="cancel" width="38%">
<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="tagId" label="标签:" required>
<tag-select v-model:tag-id="form.tagId" v-model:tag-path="form.tagPath" style="width: 100%" />
@@ -10,7 +11,8 @@
</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" 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">
@@ -27,15 +29,11 @@
</el-select>
</el-form-item>
<el-form-item v-if="form.authMethod == 1" prop="password" label="密码:">
<el-input
type="password"
show-password
v-model.trim="form.password"
placeholder="请输入密码,修改操作可不填"
autocomplete="new-password"
>
<el-input type="password" show-password v-model.trim="form.password" placeholder="请输入密码,修改操作可不填"
autocomplete="new-password">
<template v-if="form.id && form.id != 0" #suffix>
<el-popover @hide="pwd = ''" placement="right" title="原密码" :width="200" trigger="click" :content="pwd">
<el-popover @hide="pwd = ''" placement="right" title="原密码" :width="200" trigger="click"
:content="pwd">
<template #reference>
<el-link @click="getPwd" :underline="false" type="primary" class="mr5">原密码</el-link>
</template>
@@ -44,7 +42,8 @@
</el-input>
</el-form-item>
<el-form-item v-if="form.authMethod == 2" prop="password" label="秘钥:">
<el-input type="textarea" :rows="3" v-model="form.password" placeholder="请将私钥文件内容拷贝至此,修改操作可不填"></el-input>
<el-input type="textarea" :rows="3" v-model="form.password" placeholder="请将私钥文件内容拷贝至此,修改操作可不填">
</el-input>
</el-form-item>
<el-form-item prop="remark" label="备注:">
<el-input type="textarea" v-model="form.remark"></el-input>
@@ -56,17 +55,14 @@
<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>
<el-checkbox @change="getSshTunnelMachines" v-model="form.enableSshTunnel" :true-label="1"
:false-label="-1"></el-checkbox>
</el-col>
<el-col :span="2" v-if="form.enableSshTunnel == 1"> 机器: </el-col>
<el-col :span="19" v-if="form.enableSshTunnel == 1">
<el-select style="width: 100%" v-model="form.sshTunnelMachineId" placeholder="请选择SSH隧道机器">
<el-option
v-for="item in sshTunnelMachineList"
:key="item.id"
:label="`${item.ip}:${item.port} [${item.name}]`"
:value="item.id"
>
<el-option v-for="item in sshTunnelMachineList" :key="item.id"
:label="`${item.ip}:${item.port} [${item.name}]`" :value="item.id">
</el-option>
</el-select>
</el-col>
@@ -83,20 +79,15 @@
</div>
</template>
<script lang="ts">
import { toRefs, reactive, watch, defineComponent, ref } from 'vue';
<script lang="ts" setup>
import { toRefs, reactive, watch, ref } from 'vue';
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: {
const props = defineProps({
visible: {
type: Boolean,
},
@@ -109,33 +100,12 @@ export default defineComponent({
title: {
type: String,
},
},
setup(props: any, { emit }) {
const machineForm: any = ref(null);
const state = reactive({
dialogVisible: false,
projects: [] as any,
sshTunnelMachineList: [] as any,
tags: [],
selectTags: [],
form: {
id: null,
tagId: null as any,
tagPath: '',
ip: null,
name: null,
authMethod: 1,
port: 22,
username: '',
password: '',
remark: '',
enableSshTunnel: null,
sshTunnelMachineId: null,
enableRecorder: -1,
},
pwd: '',
btnLoading: false,
rules: {
})
//定义事件
const emit = defineEmits(['update:visible', 'cancel', 'val-change'])
const rules = {
tagId: [
{
required: true,
@@ -171,15 +141,44 @@ export default defineComponent({
trigger: ['change', 'blur'],
},
],
}
const machineForm: any = ref(null);
const state = reactive({
dialogVisible: false,
sshTunnelMachineList: [] as any,
form: {
id: null,
tagId: null as any,
tagPath: '',
ip: null,
name: null,
authMethod: 1,
port: 22,
username: '',
password: '',
remark: '',
enableSshTunnel: null,
sshTunnelMachineId: null,
enableRecorder: -1,
},
pwd: '',
btnLoading: false,
});
watch(props, async (newValue) => {
const {
dialogVisible,
sshTunnelMachineList,
form,
pwd,
btnLoading,
} = toRefs(state)
watch(props, async (newValue: any) => {
state.dialogVisible = newValue.visible;
if (!state.dialogVisible) {
return;
}
state.projects = newValue.projects;
if (newValue.machine) {
state.form = { ...newValue.machine };
} else {
@@ -242,17 +241,7 @@ export default defineComponent({
emit('update:visible', false);
emit('cancel');
};
return {
...toRefs(state),
machineForm,
getSshTunnelMachines,
getPwd,
btnOk,
cancel,
};
},
});
</script>
<style lang="scss">
</style>

View File

@@ -2,27 +2,21 @@
<div>
<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" @click="openFormDialog(currentData)" plain
>编辑</el-button
>
<el-button v-auth="'machine:del'" :disabled="!currentId" @click="deleteMachine(currentId)" type="danger" icon="delete"
>删除</el-button
>
<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"
@click="openFormDialog(currentData)" plain>编辑</el-button>
<el-button v-auth="'machine:del'" :disabled="!currentId" @click="deleteMachine(currentId)" type="danger"
icon="delete">删除</el-button>
<div style="float: right">
<el-select @focus="getTags" v-model="params.tagPath" placeholder="请选择标签" @clear="search" filterable clearable>
<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"
placeholder="请输入名称"
style="width: 150px"
v-model="params.name"
@clear="search"
plain
clearable
></el-input>
<el-input class="ml5" placeholder="请输入ip" style="width: 150px" v-model="params.ip" @clear="search" plain clearable></el-input>
<el-input class="ml5" placeholder="请输入名称" style="width: 150px" v-model="params.name" @clear="search"
plain clearable></el-input>
<el-input class="ml5" placeholder="请输入ip" style="width: 150px" v-model="params.ip" @clear="search"
plain clearable></el-input>
<el-button class="ml5" @click="search" type="success" icon="search"></el-button>
</div>
</div>
@@ -35,32 +29,25 @@
</el-radio>
</template>
</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="140" show-overflow-tooltip></el-table-column>
<el-table-column prop="ip" label="ip:port" min-width="150">
<template #default="scope">
<el-link :disabled="scope.row.status == -1" @click="showMachineStats(scope.row)" type="primary" :underline="false">{{
<el-link :disabled="scope.row.status == -1" @click="showMachineStats(scope.row)" type="primary"
:underline="false">{{
`${scope.row.ip}:${scope.row.port}`
}}</el-link>
</template>
</el-table-column>
<el-table-column prop="status" label="状态" min-width="75">
<template #default="scope">
<el-switch
v-auth:disabled="'machine:update'"
:width="47"
v-model="scope.row.status"
:active-value="1"
:inactive-value="-1"
inline-prompt
active-text="启用"
inactive-text="停用"
<el-switch v-auth:disabled="'machine:update'" :width="47" v-model="scope.row.status"
:active-value="1" :inactive-value="-1" inline-prompt active-text="启用" inactive-text="停用"
style="--el-switch-on-color: #13ce66; --el-switch-off-color: #ff4949"
@change="changeStatus(scope.row)"
></el-switch>
@change="changeStatus(scope.row)"></el-switch>
</template>
</el-table-column>
<el-table-column prop="username" label="用户名" min-width="90"></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">
@@ -71,40 +58,19 @@
<el-table-column label="操作" min-width="235" fixed="right">
<template #default="scope">
<span v-auth="'machine:terminal'">
<el-link
:disabled="scope.row.status == -1"
type="primary"
@click="showTerminal(scope.row)"
plain
size="small"
:underline="false"
>终端</el-link
>
<el-link :disabled="scope.row.status == -1" type="primary" @click="showTerminal(scope.row)"
plain size="small" :underline="false">终端</el-link>
<el-divider direction="vertical" border-style="dashed" />
</span>
<span v-auth="'machine:file'">
<el-link
type="success"
:disabled="scope.row.status == -1"
@click="fileManage(scope.row)"
plain
size="small"
:underline="false"
>文件</el-link
>
<el-link type="success" :disabled="scope.row.status == -1"
@click="showFileManage(scope.row)" plain size="small" :underline="false">文件</el-link>
<el-divider direction="vertical" border-style="dashed" />
</span>
<el-link
:disabled="scope.row.status == -1"
type="warning"
@click="serviceManager(scope.row)"
plain
size="small"
:underline="false"
>脚本</el-link
>
<el-link :disabled="scope.row.status == -1" type="warning" @click="serviceManager(scope.row)"
plain size="small" :underline="false">脚本</el-link>
<el-divider direction="vertical" border-style="dashed" />
<el-dropdown>
@@ -116,34 +82,21 @@
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item
><el-link
@click="showProcess(scope.row)"
:disabled="scope.row.status == -1"
plain
:underline="false"
size="small"
>进程</el-link
></el-dropdown-item
>
<el-dropdown-item>
<el-link @click="showProcess(scope.row)" :disabled="scope.row.status == -1"
plain :underline="false" size="small">进程</el-link>
</el-dropdown-item>
<el-dropdown-item v-if="scope.row.enableRecorder == 1"
><el-link v-auth="'machine:update'" @click="showRec(scope.row)" plain :underline="false" size="small"
>终端回放</el-link
></el-dropdown-item
>
<el-dropdown-item v-if="scope.row.enableRecorder == 1">
<el-link v-auth="'machine:update'" @click="showRec(scope.row)" plain
:underline="false" size="small">终端回放</el-link>
</el-dropdown-item>
<el-dropdown-item
><el-link
:disabled="!scope.row.hasCli || scope.row.status == -1"
type="danger"
@click="closeCli(scope.row)"
plain
size="small"
:underline="false"
>关闭连接</el-link
></el-dropdown-item
>
<el-dropdown-item>
<el-link :disabled="!scope.row.hasCli || scope.row.status == -1" type="danger"
@click="closeCli(scope.row)" plain size="small" :underline="false">关闭连接
</el-link>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
@@ -151,42 +104,33 @@
</el-table-column>
</el-table>
<el-row style="margin-top: 20px" type="flex" justify="end">
<el-pagination
style="text-align: right"
:total="data.total"
layout="prev, pager, next, total, jumper"
v-model:current-page="params.pageNum"
:page-size="params.pageSize"
@current-change="handlePageChange"
></el-pagination>
<el-pagination style="text-align: right" :total="data.total" layout="prev, pager, next, total, jumper"
v-model:current-page="params.pageNum" :page-size="params.pageSize"
@current-change="handlePageChange"></el-pagination>
</el-row>
</el-card>
<machine-edit
:title="machineEditDialog.title"
v-model:visible="machineEditDialog.visible"
v-model:machine="machineEditDialog.data"
@valChange="submitSuccess"
></machine-edit>
<machine-edit :title="machineEditDialog.title" v-model:visible="machineEditDialog.visible"
v-model:machine="machineEditDialog.data" @valChange="submitSuccess"></machine-edit>
<process-list v-model:visible="processDialog.visible" v-model:machineId="processDialog.machineId" />
<service-manage :title="serviceDialog.title" v-model:visible="serviceDialog.visible" v-model:machineId="serviceDialog.machineId" />
<service-manage :title="serviceDialog.title" v-model:visible="serviceDialog.visible"
v-model:machineId="serviceDialog.machineId" />
<file-manage :title="fileDialog.title" v-model:visible="fileDialog.visible" v-model:machineId="fileDialog.machineId" />
<file-manage :title="fileDialog.title" v-model:visible="fileDialog.visible"
v-model:machineId="fileDialog.machineId" />
<machine-stats
v-model:visible="machineStatsDialog.visible"
:machineId="machineStatsDialog.machineId"
:title="machineStatsDialog.title"
></machine-stats>
<machine-stats v-model:visible="machineStatsDialog.visible" :machineId="machineStatsDialog.machineId"
:title="machineStatsDialog.title"></machine-stats>
<machine-rec v-model:visible="machineRecDialog.visible" :machineId="machineRecDialog.machineId" :title="machineRecDialog.title"></machine-rec>
<machine-rec v-model:visible="machineRecDialog.visible" :machineId="machineRecDialog.machineId"
:title="machineRecDialog.title"></machine-rec>
</div>
</template>
<script lang="ts">
import { toRefs, reactive, onMounted, defineComponent } from 'vue';
<script lang="ts" setup>
import { toRefs, reactive, onMounted } from 'vue';
import { useRouter } from 'vue-router';
import { ElMessage, ElMessageBox } from 'element-plus';
import { machineApi } from './api';
@@ -199,21 +143,9 @@ import MachineStats from './MachineStats.vue';
import MachineRec from './MachineRec.vue';
import { dateFormat } from '@/common/utils/date';
export default defineComponent({
name: 'MachineList',
components: {
ServiceManage,
ProcessList,
FileManage,
MachineEdit,
MachineStats,
MachineRec,
},
setup() {
const router = useRouter();
const state = reactive({
tags: [] as any,
stats: '',
params: {
pageNum: 1,
pageSize: 10,
@@ -261,6 +193,20 @@ export default defineComponent({
},
});
const {
tags,
params,
data,
currentId,
currentData,
serviceDialog,
processDialog,
fileDialog,
machineStatsDialog,
machineEditDialog,
machineRecDialog,
} = toRefs(state)
onMounted(async () => {
search();
});
@@ -356,7 +302,7 @@ export default defineComponent({
search();
};
const fileManage = (currentData: any) => {
const showFileManage = (currentData: any) => {
state.fileDialog.visible = true;
state.fileDialog.machineId = currentData.id;
state.fileDialog.title = `${currentData.name} => ${currentData.ip}`;
@@ -382,34 +328,13 @@ export default defineComponent({
state.machineRecDialog.machineId = row.id;
state.machineRecDialog.visible = true;
};
return {
...toRefs(state),
dateFormat,
choose,
getTags,
showTerminal,
openFormDialog,
deleteMachine,
closeCli,
serviceManager,
showMachineStats,
showProcess,
changeStatus,
submitSuccess,
fileManage,
search,
showRec,
handlePageChange,
};
},
});
</script>
<style>
.el-dialog__body {
padding: 2px 2px;
}
.el-dropdown-link-machine-list {
cursor: pointer;
color: var(--el-color-primary);

View File

@@ -1,13 +1,7 @@
<template>
<div id="terminalRecDialog">
<el-dialog
:title="title"
v-model="dialogVisible"
:before-close="handleClose"
:close-on-click-modal="false"
:destroy-on-close="true"
width="70%"
>
<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>
@@ -26,21 +20,20 @@
</div>
</template>
<script lang="ts">
import { toRefs, watch, ref, reactive, defineComponent } from 'vue';
<script lang="ts" setup>
import { toRefs, watch, ref, reactive } from 'vue';
import { machineApi } from './api';
import * as AsciinemaPlayer from 'asciinema-player';
import 'asciinema-player/dist/bundle/asciinema-player.css';
export default defineComponent({
name: 'MachineRec',
components: {},
props: {
const props = defineProps({
visible: { type: Boolean },
machineId: { type: Number },
title: { type: String },
},
setup(props: any, context) {
})
const emit = defineEmits(['update:visible', 'cancel', 'update:machineId'])
const playerRef = ref(null);
const state = reactive({
dialogVisible: false,
@@ -54,7 +47,18 @@ export default defineComponent({
rec: '',
});
watch(props, async (newValue) => {
const {
dialogVisible,
title,
operateDates,
operateDate,
users,
recs,
user,
rec,
} = toRefs(state)
watch(props, async (newValue: any) => {
const visible = newValue.visible;
if (visible) {
state.machineId = newValue.machineId;
@@ -106,9 +110,9 @@ export default defineComponent({
* 关闭取消按钮触发的事件
*/
const handleClose = () => {
context.emit('update:visible', false);
context.emit('update:machineId', null);
context.emit('cancel');
emit('update:visible', false);
emit('update:machineId', null);
emit('cancel');
state.operateDates = [];
state.users = [];
state.recs = [];
@@ -116,17 +120,6 @@ export default defineComponent({
state.user = '';
state.rec = '';
};
return {
...toRefs(state),
playerRef,
getUsers,
getRecs,
playRec,
handleClose,
};
},
});
</script>
<style lang="scss">
#terminalRecDialog {

View File

@@ -1,6 +1,7 @@
<template>
<div>
<el-dialog :title="title" v-model="dialogVisible" :close-on-click-modal="true" :destroy-on-close="true" :before-close="cancel" width="1050px">
<el-dialog :title="title" v-model="dialogVisible" :close-on-click-modal="true" :destroy-on-close="true"
:before-close="cancel" width="1050px">
<el-row :gutter="20">
<el-col :lg="12" :md="12">
<el-descriptions size="small" title="基础信息" :column="2" border>
@@ -19,7 +20,8 @@
<el-descriptions-item label="运行中任务">
{{ stats.RunningProcs }}
</el-descriptions-item>
<el-descriptions-item label="负载"> {{ stats.Load1 }} {{ stats.Load5 }} {{ stats.Load10 }} </el-descriptions-item>
<el-descriptions-item label="负载"> {{ stats.Load1 }} {{ stats.Load5 }} {{ stats.Load10 }}
</el-descriptions-item>
</el-descriptions>
</el-col>
@@ -36,7 +38,8 @@
<el-col :lg="8" :md="8">
<span style="font-size: 16px; font-weight: 700">磁盘</span>
<el-table :data="stats.FSInfos" stripe max-height="250" style="width: 100%" border>
<el-table-column prop="MountPoint" label="挂载点" min-width="100" show-overflow-tooltip></el-table-column>
<el-table-column prop="MountPoint" label="挂载点" min-width="100" show-overflow-tooltip>
</el-table-column>
<el-table-column prop="Used" label="可使用" min-width="70" show-overflow-tooltip>
<template #default="scope">
{{ formatByteSize(scope.row.Free) }}
@@ -54,8 +57,10 @@
<span style="font-size: 16px; font-weight: 700">网卡</span>
<el-table :data="netInter" stripe max-height="250" style="width: 100%" border>
<el-table-column prop="name" label="网卡" min-width="120" show-overflow-tooltip></el-table-column>
<el-table-column prop="IPv4" label="IPv4" min-width="130" show-overflow-tooltip></el-table-column>
<el-table-column prop="IPv6" label="IPv6" min-width="130" show-overflow-tooltip></el-table-column>
<el-table-column prop="IPv4" label="IPv4" min-width="130" show-overflow-tooltip>
</el-table-column>
<el-table-column prop="IPv6" label="IPv6" min-width="130" show-overflow-tooltip>
</el-table-column>
<el-table-column prop="Rx" label="接收(rx)" min-width="110" show-overflow-tooltip>
<template #default="scope">
{{ formatByteSize(scope.row.Rx) }}
@@ -73,17 +78,14 @@
</div>
</template>
<script lang="ts">
import { toRefs, reactive, watch, defineComponent, ref, nextTick } from 'vue';
<script lang="ts" setup>
import { toRefs, reactive, watch, ref, nextTick } from 'vue';
import useEcharts from '@/common/echarts/useEcharts.ts';
import tdTheme from '@/common/echarts/theme.json';
import { formatByteSize } from '@/common/utils/format';
import { machineApi } from './api';
export default defineComponent({
name: 'MachineStats',
components: {},
props: {
const props = defineProps({
visible: {
type: Boolean,
},
@@ -96,8 +98,10 @@ export default defineComponent({
title: {
type: String,
},
},
setup(props: any, { emit }) {
})
const emit = defineEmits(['update:visible', 'cancel', 'update:machineId'])
const cpuRef: any = ref();
const memRef: any = ref();
@@ -106,12 +110,19 @@ export default defineComponent({
const state = reactive({
dialogVisible: false,
charts: [] as any,
stats: {} as any,
netInter: [] as any,
});
watch(props, async (newValue) => {
const {
dialogVisible,
stats,
netInter,
} = toRefs(state)
let charts = [] as any
watch(props, async (newValue: any) => {
const visible = newValue.visible;
if (visible) {
await setStats();
@@ -186,7 +197,7 @@ export default defineComponent({
}
const chart: any = useEcharts(memRef.value, tdTheme, option);
memChart = chart;
state.charts.push(chart);
charts.push(chart);
};
const initCpuStats = () => {
@@ -253,7 +264,7 @@ export default defineComponent({
}
const chart: any = useEcharts(cpuRef.value, tdTheme, option);
cpuChart = chart;
state.charts.push(chart);
charts.push(chart);
};
const initCharts = () => {
@@ -267,9 +278,9 @@ export default defineComponent({
const initEchartResizeFun = () => {
nextTick(() => {
for (let i = 0; i < state.charts.length; i++) {
for (let i = 0; i < charts.length; i++) {
setTimeout(() => {
state.charts[i].resize();
charts[i].resize();
}, i * 1000);
}
});
@@ -301,17 +312,6 @@ export default defineComponent({
memChart = null;
}, 200);
};
return {
...toRefs(state),
cpuRef,
memRef,
cancel,
formatByteSize,
onRefresh,
};
},
});
</script>
<style lang="scss">
.card-item-chart {

View File

@@ -1,6 +1,7 @@
<template>
<div class="file-manage">
<el-dialog title="进程信息" v-model="dialogVisible" :destroy-on-close="true" :show-close="true" :before-close="handleClose" width="65%">
<el-dialog title="进程信息" v-model="dialogVisible" :destroy-on-close="true" :show-close="true"
:before-close="handleClose" width="65%">
<div class="toolbar">
<el-row>
<el-col :span="4">
@@ -21,7 +22,8 @@
</el-select>
</el-col>
<el-col :span="6">
<el-button class="ml5" @click="getProcess" type="primary" icon="tickets" size="small" plain>刷新</el-button>
<el-button class="ml5" @click="getProcess" type="primary" icon="tickets" size="small" plain>刷新
</el-button>
</el-col>
</el-row>
</div>
@@ -35,7 +37,9 @@
<template #header>
VSZ
<el-tooltip class="box-item" effect="dark" content="虚拟内存" placement="top">
<el-icon><question-filled /></el-icon>
<el-icon>
<question-filled />
</el-icon>
</el-tooltip>
</template>
</el-table-column>
@@ -43,7 +47,9 @@
<template #header>
RSS
<el-tooltip class="box-item" effect="dark" content="固定内存" placement="top">
<el-icon><question-filled /></el-icon>
<el-icon>
<question-filled />
</el-icon>
</el-tooltip>
</template>
</el-table-column>
@@ -51,7 +57,9 @@
<template #header>
STAT
<el-tooltip class="box-item" effect="dark" content="进程状态" placement="top">
<el-icon><question-filled /></el-icon>
<el-icon>
<question-filled />
</el-icon>
</el-tooltip>
</template>
</el-table-column>
@@ -59,7 +67,9 @@
<template #header>
START
<el-tooltip class="box-item" effect="dark" content="启动时间" placement="top">
<el-icon><question-filled /></el-icon>
<el-icon>
<question-filled />
</el-icon>
</el-tooltip>
</template>
</el-table-column>
@@ -67,17 +77,21 @@
<template #header>
TIME
<el-tooltip class="box-item" effect="dark" content="该进程实际使用CPU运作的时间" placement="top">
<el-icon><question-filled /></el-icon>
<el-icon>
<question-filled />
</el-icon>
</el-tooltip>
</template>
</el-table-column>
<el-table-column prop="command" label="command" :min-width="120" show-overflow-tooltip> </el-table-column>
<el-table-column prop="command" label="command" :min-width="120" show-overflow-tooltip>
</el-table-column>
<el-table-column label="操作">
<template #default="scope">
<el-popconfirm title="确定终止该进程?" @confirm="confirmKillProcess(scope.row.pid)">
<template #reference>
<el-button v-auth="'machine:killprocess'" type="danger" icon="delete" size="small" plain>终止</el-button>
<el-button v-auth="'machine:killprocess'" type="danger" icon="delete" size="small"
plain>终止</el-button>
</template>
</el-popconfirm>
<!-- <el-button @click="addFiles(scope.row)" type="danger" icon="delete" size="small" plain>终止</el-button> -->
@@ -88,20 +102,19 @@
</div>
</template>
<script lang="ts">
import { toRefs, reactive, watch, defineComponent } from 'vue';
<script lang="ts" setup>
import { toRefs, reactive, watch } from 'vue';
import { ElMessage } from 'element-plus';
import { machineApi } from './api';
import enums from './enums';
export default defineComponent({
name: 'ProcessList',
components: {},
props: {
const props = defineProps({
visible: { type: Boolean },
machineId: { type: Number },
title: { type: String },
},
setup(props: any, context) {
})
const emit = defineEmits(['update:visible', 'cancel', 'update:machineId'])
const state = reactive({
dialogVisible: false,
params: {
@@ -113,6 +126,13 @@ export default defineComponent({
processList: [],
});
const {
dialogVisible,
params,
processList,
} = toRefs(state)
watch(props, (newValue) => {
if (props.machineId) {
state.params.id = props.machineId;
@@ -181,9 +201,9 @@ export default defineComponent({
* 关闭取消按钮触发的事件
*/
const handleClose = () => {
context.emit('update:visible', false);
context.emit('update:machineId', null);
context.emit('cancel');
emit('update:visible', false);
emit('update:machineId', null);
emit('cancel');
state.params = {
name: '',
sortType: '1',
@@ -192,14 +212,4 @@ export default defineComponent({
};
state.processList = [];
};
return {
...toRefs(state),
getProcess,
confirmKillProcess,
enums,
handleClose,
};
},
});
</script>

View File

@@ -1,14 +1,7 @@
<template>
<div class="mock-data-dialog">
<el-dialog
:title="title"
v-model="dialogVisible"
:close-on-click-modal="false"
:before-close="cancel"
:show-close="true"
:destroy-on-close="true"
width="900px"
>
<el-dialog :title="title" v-model="dialogVisible" :close-on-click-modal="false" :before-close="cancel"
:show-close="true" :destroy-on-close="true" width="900px">
<el-form :model="form" ref="scriptForm" label-width="50px" size="small">
<el-form-item prop="method" label="名称">
<el-input v-model.trim="form.name" placeholder="请输入名称"></el-input>
@@ -20,7 +13,8 @@
<el-form-item prop="type" label="类型">
<el-select v-model="form.type" default-first-option style="width: 100%" placeholder="请选择类型">
<el-option v-for="item in enums.scriptTypeEnum" :key="item.value" :label="item.label" :value="item.value"></el-option>
<el-option v-for="item in enums.scriptTypeEnum as any" :key="item.value" :label="item.label"
:value="item.value"></el-option>
</el-select>
</el-form-item>
@@ -29,17 +23,25 @@
</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-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-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-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-col :span="2">
<el-button @click="onDeleteParam(index)" size="small" type="danger">删除</el-button>
</el-col>
</el-row>
</el-form-item>
@@ -51,22 +53,16 @@
<template #footer>
<div class="dialog-footer">
<el-button @click="cancel()" :disabled="submitDisabled"> </el-button>
<el-button
v-auth="'machine:script:save'"
type="primary"
:loading="btnLoading"
@click="btnOk"
:disabled="submitDisabled"
> </el-button
>
<el-button v-auth="'machine:script:save'" type="primary" :loading="btnLoading" @click="btnOk"
:disabled="submitDisabled"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script lang="ts">
import { ref, toRefs, reactive, watch, defineComponent } from 'vue';
<script lang="ts" setup>
import { ref, toRefs, reactive, watch } from 'vue';
import { ElMessage } from 'element-plus';
import { machineApi } from './api';
import enums from './enums';
@@ -74,12 +70,7 @@ import { notEmpty } from '@/common/assert';
import { codemirror } from '@/components/codemirror';
export default defineComponent({
name: 'ScriptEdit',
components: {
codemirror,
},
props: {
const props = defineProps({
visible: {
type: Boolean,
},
@@ -95,8 +86,10 @@ export default defineComponent({
isCommon: {
type: Boolean,
},
},
setup(props: any, { emit }) {
})
const emit = defineEmits(['update:visible', 'cancel', 'submitSuccess'])
const { isCommon, machineId } = toRefs(props);
const scriptForm: any = ref(null);
@@ -116,7 +109,15 @@ export default defineComponent({
btnLoading: false,
});
watch(props, (newValue) => {
const {
dialogVisible,
submitDisabled,
params,
form,
btnLoading,
} = toRefs(state)
watch(props, (newValue: any) => {
state.dialogVisible = newValue.visible;
if (!newValue.visible) {
return;
@@ -141,7 +142,7 @@ export default defineComponent({
};
const btnOk = () => {
state.form.machineId = isCommon.value ? 9999999 : (machineId.value as any);
state.form.machineId = isCommon.value ? 9999999 : (machineId?.value as any);
console.log('machineid:', machineId);
scriptForm.value.validate((valid: any) => {
if (valid) {
@@ -173,18 +174,6 @@ export default defineComponent({
emit('cancel');
state.params = [];
};
return {
...toRefs(state),
enums,
onAddParam,
onDeleteParam,
scriptForm,
btnOk,
cancel,
};
},
});
</script>
<style lang="scss">
#content {

View File

@@ -1,6 +1,7 @@
<template>
<div class="file-manage">
<el-dialog :title="title" v-model="dialogVisible" :destroy-on-close="true" :show-close="true" :before-close="handleClose" width="60%">
<el-dialog :title="title" v-model="dialogVisible" :destroy-on-close="true" :show-close="true"
:before-close="handleClose" width="60%">
<div class="toolbar">
<div style="float: left">
<el-select v-model="type" @change="getScripts" size="small" placeholder="请选择">
@@ -9,20 +10,12 @@
</el-select>
</div>
<div style="float: right">
<el-button @click="editScript(currentData)" :disabled="currentId == null" type="primary" icon="tickets" size="small" plain
>查看</el-button
>
<el-button v-auth="'machine:script:save'" type="primary" @click="editScript(null)" icon="plus" size="small" plain>添加</el-button>
<el-button
v-auth="'machine:script:del'"
:disabled="currentId == null"
type="danger"
@click="deleteRow(currentData)"
icon="delete"
size="small"
plain
>删除</el-button
>
<el-button @click="editScript(currentData)" :disabled="currentId == null" type="primary"
icon="tickets" size="small" plain>查看</el-button>
<el-button v-auth="'machine:script:save'" type="primary" @click="editScript(null)" icon="plus"
size="small" plain>添加</el-button>
<el-button v-auth="'machine:script:del'" :disabled="currentId == null" type="danger"
@click="deleteRow(currentData)" icon="delete" size="small" plain>删除</el-button>
</div>
</div>
@@ -43,56 +36,32 @@
</el-table-column>
<el-table-column label="操作">
<template #default="scope">
<el-button v-if="scope.row.id == null" @click="addFiles(scope.row)" type="success" icon="el-icon-success" size="small" plain
>确定</el-button
>
<el-button v-if="scope.row.id == null" type="success" icon="el-icon-success" size="small" plain>
确定</el-button>
<el-button
v-auth="'machine:script:run'"
v-if="scope.row.id != null"
@click="runScript(scope.row)"
type="primary"
icon="video-play"
size="small"
plain
>执行</el-button
>
<el-button v-auth="'machine:script:run'" v-if="scope.row.id != null"
@click="runScript(scope.row)" type="primary" icon="video-play" size="small" plain>执行
</el-button>
</template>
</el-table-column>
</el-table>
<el-row style="margin-top: 10px" type="flex" justify="end">
<el-pagination
small
style="text-align: center"
:total="total"
layout="prev, pager, next, total, jumper"
v-model:current-page="query.pageNum"
:page-size="query.pageSize"
@current-change="handlePageChange"
></el-pagination>
<el-pagination small style="text-align: center" :total="total" layout="prev, pager, next, total, jumper"
v-model:current-page="query.pageNum" :page-size="query.pageSize" @current-change="handlePageChange">
</el-pagination>
</el-row>
</el-dialog>
<el-dialog title="脚本参数" v-model="scriptParamsDialog.visible" width="400px">
<el-form ref="paramsForm" :model="scriptParamsDialog.params" label-width="70px" size="small">
<el-form-item v-for="item in scriptParamsDialog.paramsFormItem" :key="item.name" :prop="item.model" :label="item.name" required>
<el-input
v-if="!item.options"
v-model="scriptParamsDialog.params[item.model]"
:placeholder="item.placeholder"
autocomplete="off"
clearable
></el-input>
<el-select
v-else
v-model="scriptParamsDialog.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-form-item v-for="item in scriptParamsDialog.paramsFormItem as any" :key="item.name"
:prop="item.model" :label="item.name" required>
<el-input v-if="!item.options" v-model="scriptParamsDialog.params[item.model]"
:placeholder="item.placeholder" autocomplete="off" clearable></el-input>
<el-select v-else v-model="scriptParamsDialog.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>
@@ -109,49 +78,33 @@
</div>
</el-dialog>
<el-dialog
v-if="terminalDialog.visible"
title="终端"
v-model="terminalDialog.visible"
width="80%"
:close-on-click-modal="false"
:modal="false"
@close="closeTermnial"
>
<ssh-terminal ref="terminal" :cmd="terminalDialog.cmd" :machineId="terminalDialog.machineId" height="560px" />
<el-dialog v-if="terminalDialog.visible" title="终端" v-model="terminalDialog.visible" width="80%"
:close-on-click-modal="false" :modal="false" @close="closeTermnial">
<ssh-terminal ref="terminal" :cmd="terminalDialog.cmd" :machineId="terminalDialog.machineId"
height="560px" />
</el-dialog>
<script-edit
v-model:visible="editDialog.visible"
v-model:data="editDialog.data"
:title="editDialog.title"
v-model:machineId="editDialog.machineId"
:isCommon="type == 1"
@submitSuccess="submitSuccess"
/>
<script-edit v-model:visible="editDialog.visible" v-model:data="editDialog.data" :title="editDialog.title"
v-model:machineId="editDialog.machineId" :isCommon="type == 1" @submitSuccess="submitSuccess" />
</div>
</template>
<script lang="ts">
import { ref, toRefs, reactive, watch, defineComponent } from 'vue';
<script lang="ts" setup>
import { ref, toRefs, reactive, watch } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import SshTerminal from './SshTerminal.vue';
import { machineApi } from './api';
import enums from './enums';
import ScriptEdit from './ScriptEdit.vue';
export default defineComponent({
name: 'ServiceManage',
components: {
ScriptEdit,
SshTerminal,
},
props: {
const props = defineProps({
visible: { type: Boolean },
machineId: { type: Number },
title: { type: String },
},
setup(props: any, context) {
})
const emit = defineEmits(['update:visible', 'cancel', 'update:machineId'])
const paramsForm: any = ref(null);
const state = reactive({
dialogVisible: false,
@@ -159,13 +112,13 @@ export default defineComponent({
currentId: null,
currentData: null,
query: {
machineId: 0,
machineId: 0 as any,
pageNum: 1,
pageSize: 8,
},
editDialog: {
visible: false,
data: null,
data: null as any,
title: '',
machineId: 9999999,
},
@@ -187,6 +140,20 @@ export default defineComponent({
},
});
const {
dialogVisible,
type,
currentId,
currentData,
query,
editDialog,
total,
scriptTable,
scriptParamsDialog,
resultDialog,
terminalDialog,
} = toRefs(state)
watch(props, async (newValue) => {
if (props.machineId && newValue.visible) {
await getScripts();
@@ -264,7 +231,7 @@ export default defineComponent({
}
state.terminalDialog.cmd = script;
state.terminalDialog.visible = true;
state.terminalDialog.machineId = props.machineId;
state.terminalDialog.machineId = props.machineId as any;
return;
}
};
@@ -300,7 +267,7 @@ export default defineComponent({
};
const editScript = (data: any) => {
state.editDialog.machineId = props.machineId;
state.editDialog.machineId = props.machineId as any;
state.editDialog.data = data;
if (data) {
state.editDialog.title = '查看编辑脚本';
@@ -336,30 +303,12 @@ export default defineComponent({
* 关闭取消按钮触发的事件
*/
const handleClose = () => {
context.emit('update:visible', false);
context.emit('update:machineId', null);
context.emit('cancel');
emit('update:visible', false);
emit('update:machineId', null);
emit('cancel');
state.scriptTable = [];
state.scriptParamsDialog.paramsFormItem = [];
};
return {
...toRefs(state),
paramsForm,
enums,
getScripts,
handlePageChange,
runScript,
hasParamsRun,
closeTermnial,
choose,
editScript,
submitSuccess,
deleteRow,
handleClose,
};
},
});
</script>
<style lang="sass">
</style>

View File

@@ -2,23 +2,21 @@
<div :style="{ height: height }" id="xterm" class="xterm" />
</template>
<script lang="ts">
<script lang="ts" setup>
import 'xterm/css/xterm.css';
import { Terminal } from 'xterm';
import { FitAddon } from 'xterm-addon-fit';
import { getSession } from '@/common/utils/storage.ts';
import config from '@/common/config';
import { useStore } from '@/store/index.ts';
import { nextTick, toRefs, watch, computed, reactive, defineComponent, onMounted, onBeforeUnmount } from 'vue';
import { nextTick, toRefs, watch, computed, reactive, onMounted, onBeforeUnmount } from 'vue';
export default defineComponent({
name: 'SshTerminal',
props: {
const props = defineProps({
machineId: { type: Number },
cmd: { type: String },
height: { type: String },
},
setup(props: any) {
})
const state = reactive({
machineId: 0,
cmd: '',
@@ -27,20 +25,24 @@ export default defineComponent({
socket: null as any,
});
const {
height,
} = toRefs(state)
const resize = 1;
const data = 2;
const ping = 3;
watch(props, (newValue) => {
watch(props, (newValue: any) => {
state.machineId = newValue.machineId;
state.cmd = newValue.cmd;
state.height = newValue.height;
});
onMounted(() => {
state.machineId = props.machineId;
state.height = props.height;
state.cmd = props.cmd;
state.machineId = props.machineId as any;
state.height = props.height as any;
state.cmd = props.cmd as any;
});
onBeforeUnmount(() => {
@@ -120,8 +122,7 @@ export default defineComponent({
let pingInterval: any;
function initSocket() {
state.socket = new WebSocket(
`${config.baseWsUrl}/machines/${state.machineId}/terminal?token=${getSession('token')}&cols=${state.term.cols}&rows=${
state.term.rows
`${config.baseWsUrl}/machines/${state.machineId}/terminal?token=${getSession('token')}&cols=${state.term.cols}&rows=${state.term.rows
}`
);
@@ -188,10 +189,4 @@ export default defineComponent({
state.term = null;
}
}
return {
...toRefs(state),
};
},
});
</script>

View File

@@ -4,36 +4,27 @@
</div>
</template>
<script lang="ts">
<script lang="ts" setup>
import SshTerminal from './SshTerminal.vue';
import { reactive, toRefs, defineComponent, onMounted } from 'vue';
import { reactive, toRefs, onMounted } from 'vue';
import { useRoute } from 'vue-router';
export default defineComponent({
name: 'SshTerminalPage',
components: {
SshTerminal,
},
props: {
machineId: { type: Number },
},
setup() {
const route = useRoute();
const state = reactive({
machineId: 0,
height: 700,
});
const {
machineId,
height,
} = toRefs(state)
onMounted(() => {
state.height = window.innerHeight + 5;
state.machineId = Number.parseInt(route.query.id as string);
});
return {
...toRefs(state),
};
},
});
</script>
<style lang="scss">
</style>

View File

@@ -5,14 +5,8 @@
<el-col :span="24">
<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-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>
@@ -20,14 +14,17 @@
<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>
<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">
<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)}]`
@@ -38,7 +35,8 @@
<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-option v-for="item in collections" :key="item" :label="item" :value="item">
</el-option>
</el-select>
</el-form-item>
</el-form>
@@ -47,19 +45,18 @@
</div>
<el-container id="data-exec" style="border: 1px solid #eee; margin-top: 1px">
<el-tabs @tab-remove="removeDataTab" @tab-click="onDataTabClick" style="width: 100%; margin-left: 5px" v-model="activeName">
<el-tabs @tab-remove="removeDataTab" @tab-click="onDataTabClick" style="width: 100%; margin-left: 5px"
v-model="activeName">
<el-tab-pane closable v-for="dt in dataTabs" :key="dt.name" :label="dt.name" :name="dt.name">
<el-row v-if="mongoId">
<el-link @click="findCommand(activeName)" icon="refresh" :underline="false" class="ml5"></el-link>
<el-link @click="showInsertDocDialog" class="ml5" type="primary" icon="plus" :underline="false"></el-link>
<el-link @click="findCommand(activeName)" icon="refresh" :underline="false" class="ml5">
</el-link>
<el-link @click="showInsertDocDialog" class="ml5" type="primary" icon="plus" :underline="false">
</el-link>
</el-row>
<el-row class="mt5 mb5">
<el-input
ref="findParamInputRef"
v-model="dt.findParamStr"
placeholder="点击输入相应查询条件"
@focus="showFindDialog(dt.name)"
>
<el-input ref="findParamInputRef" v-model="dt.findParamStr" placeholder="点击输入相应查询条件"
@focus="showFindDialog(dt.name)">
<template #prepend>查询参数</template>
</el-input>
</el-row>
@@ -69,17 +66,20 @@
<el-input type="textarea" v-model="item.value" :rows="12" />
<div style="padding: 3px; float: right" class="mr5 mongo-doc-btns">
<div>
<el-link @click="onJsonEditor(item)" :underline="false" type="success" icon="MagicStick"></el-link>
<el-link @click="onJsonEditor(item)" :underline="false" type="success"
icon="MagicStick"></el-link>
<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" />
<el-popconfirm @confirm="onDeleteDoc(item.value)" title="确定删除该文档?">
<template #reference>
<el-link :underline="false" type="danger" icon="DocumentDelete"></el-link>
<el-link :underline="false" type="danger" icon="DocumentDelete">
</el-link>
</template>
</el-popconfirm>
</div>
@@ -94,10 +94,12 @@
<el-dialog width="600px" title="find参数" v-model="findDialog.visible">
<el-form label-width="70px">
<el-form-item label="filter">
<el-input v-model="findDialog.findParam.filter" type="textarea" :rows="6" clearable auto-complete="off"></el-input>
<el-input v-model="findDialog.findParam.filter" type="textarea" :rows="6" clearable
auto-complete="off"></el-input>
</el-form-item>
<el-form-item label="sort">
<el-input v-model="findDialog.findParam.sort" type="textarea" :rows="3" clearable auto-complete="off"></el-input>
<el-input v-model="findDialog.findParam.sort" type="textarea" :rows="3" clearable
auto-complete="off"></el-input>
</el-form-item>
<el-form-item label="limit">
<el-input v-model.number="findDialog.findParam.limit" type="number" auto-complete="off"></el-input>
@@ -114,7 +116,8 @@
</template>
</el-dialog>
<el-dialog width="800px" :title="`新增'${activeName}'集合文档`" v-model="insertDocDialog.visible" :close-on-click-modal="false">
<el-dialog width="800px" :title="`新增'${activeName}'集合文档`" v-model="insertDocDialog.visible"
:close-on-click-modal="false">
<json-edit currentMode="code" v-model="insertDocDialog.doc" />
<template #footer>
<div>
@@ -124,7 +127,8 @@
</template>
</el-dialog>
<el-dialog width="70%" title="json编辑器" v-model="jsoneditorDialog.visible" @close="onCloseJsonEditDialog" :close-on-click-modal="false">
<el-dialog width="70%" title="json编辑器" v-model="jsoneditorDialog.visible" @close="onCloseJsonEditDialog"
:close-on-click-modal="false">
<json-edit v-model="jsoneditorDialog.doc" />
</el-dialog>
@@ -132,9 +136,9 @@
</div>
</template>
<script lang="ts">
<script lang="ts" setup>
import { mongoApi } from './api';
import {toRefs, ref, reactive, defineComponent, watch} from 'vue';
import { toRefs, ref, reactive, watch } from 'vue';
import { ElMessage } from 'element-plus';
import { isTrue, notBlank, notNull } from '@/common/assert';
@@ -143,16 +147,9 @@ import JsonEdit from '@/components/jsonedit/index.vue';
import { tagApi } from '../tag/api.ts';
import { useStore } from '@/store/index.ts';
export default defineComponent({
name: 'MongoDataOp',
components: {
JsonEdit,
},
setup() {
const store = useStore();
const findParamInputRef: any = ref(null);
const state = reactive({
loading: false,
tags: [],
mongoList: [] as any,
query: {
@@ -185,6 +182,22 @@ export default defineComponent({
},
});
const {
tags,
mongoList,
query,
mongoId,
database,
collection,
activeName,
databases,
collections,
dataTabs,
findDialog,
insertDocDialog,
jsoneditorDialog,
} = toRefs(state)
const searchMongo = async () => {
notNull(state.query.tagPath, '请先选择标签');
const res = await mongoApi.mongoList.request(state.query);
@@ -452,30 +465,6 @@ export default defineComponent({
watch(store.state.mongoDbOptInfo, async (newValue) => {
await setSelects(newValue)
})
return {
...toRefs(state),
findParamInputRef,
getTags,
changeTag,
changeMongo,
changeDatabase,
changeCollection,
onDataTabClick,
removeDataTab,
showFindDialog,
confirmFindDialog,
findCommand,
showInsertDocDialog,
onInsertDoc,
onSaveDoc,
onDeleteDoc,
onJsonEditor,
onCloseJsonEditDialog,
formatByteSize,
};
},
});
</script>
<style>

View File

@@ -1,6 +1,7 @@
<template>
<div>
<el-dialog :title="title" v-model="dialogVisible" :before-close="cancel" :close-on-click-modal="false" width="38%" :destroy-on-close="true">
<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="tagId" label="标签:" required>
<tag-select v-model:tag-id="form.tagId" v-model:tag-path="form.tagPath" style="width: 100%" />
@@ -10,28 +11,20 @@
<el-input v-model.trim="form.name" placeholder="请输入名称" auto-complete="off"></el-input>
</el-form-item>
<el-form-item prop="uri" label="uri" required>
<el-input
type="textarea"
:rows="2"
v-model.trim="form.uri"
placeholder="形如 mongodb://username:password@host1:port1"
auto-complete="off"
></el-input>
<el-input type="textarea" :rows="2" v-model.trim="form.uri"
placeholder="形如 mongodb://username:password@host1:port1" auto-complete="off"></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>
<el-checkbox @change="getSshTunnelMachines" v-model="form.enableSshTunnel" :true-label="1"
:false-label="-1"></el-checkbox>
</el-col>
<el-col :span="2" v-if="form.enableSshTunnel == 1"> 机器: </el-col>
<el-col :span="19" v-if="form.enableSshTunnel == 1">
<el-select style="width: 100%" v-model="form.sshTunnelMachineId" placeholder="请选择SSH隧道机器">
<el-option
v-for="item in sshTunnelMachineList"
:key="item.id"
:label="`${item.ip}:${item.port} [${item.name}]`"
:value="item.id"
>
<el-option v-for="item in sshTunnelMachineList" :key="item.id"
:label="`${item.ip}:${item.port} [${item.name}]`" :value="item.id">
</el-option>
</el-select>
</el-col>
@@ -48,19 +41,14 @@
</div>
</template>
<script lang="ts">
import { toRefs, reactive, watch, defineComponent, ref } from 'vue';
<script lang="ts" setup>
import { toRefs, reactive, watch, ref } from 'vue';
import { mongoApi } from './api';
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: {
const props = defineProps({
visible: {
type: Boolean,
},
@@ -70,23 +58,12 @@ export default defineComponent({
title: {
type: String,
},
},
setup(props: any, { emit }) {
const mongoForm: any = ref(null);
const state = reactive({
dialogVisible: false,
sshTunnelMachineList: [] as any,
form: {
id: null,
name: null,
uri: null,
enableSshTunnel: -1,
sshTunnelMachineId: null,
tagId: null as any,
tagPath: null as any,
},
btnLoading: false,
rules: {
})
//定义事件
const emit = defineEmits(['update:visible', 'cancel', 'val-change'])
const rules = {
tagId: [
{
required: true,
@@ -108,10 +85,32 @@ export default defineComponent({
trigger: ['change', 'blur'],
},
],
}
const mongoForm: any = ref(null);
const state = reactive({
dialogVisible: false,
sshTunnelMachineList: [] as any,
form: {
id: null,
name: null,
uri: null,
enableSshTunnel: -1,
sshTunnelMachineId: null,
tagId: null as any,
tagPath: null as any,
},
btnLoading: false,
});
watch(props, async (newValue) => {
const {
dialogVisible,
sshTunnelMachineList,
form,
btnLoading,
} = toRefs(state)
watch(props, async (newValue: any) => {
state.dialogVisible = newValue.visible;
if (!state.dialogVisible) {
return;
@@ -157,16 +156,7 @@ export default defineComponent({
emit('update:visible', false);
emit('cancel');
};
return {
...toRefs(state),
mongoForm,
getSshTunnelMachines,
btnOk,
cancel,
};
},
});
</script>
<style lang="scss">
</style>

View File

@@ -2,8 +2,10 @@
<div>
<el-card>
<el-button type="primary" icon="plus" @click="editMongo(true)" plain>添加</el-button>
<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>
<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="getTags" v-model="query.tagPath" placeholder="请选择标签" filterable clearable>
<el-option v-for="item in tags" :key="item" :label="item" :value="item"> </el-option>
@@ -34,19 +36,15 @@
<el-table-column label="操作" width>
<template #default="scope">
<el-link type="primary" @click="showDatabases(scope.row.id, scope.row)" plain size="small" :underline="false">数据库</el-link>
<el-link type="primary" @click="showDatabases(scope.row.id, 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
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-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>
@@ -62,16 +60,20 @@
<el-table-column min-width="150" label="操作">
<template #default="scope">
<el-link type="success" @click="showDatabaseStats(scope.row.Name)" plain size="small" :underline="false">stats</el-link>
<el-link type="success" @click="showDatabaseStats(scope.row.Name)" plain size="small"
:underline="false">stats</el-link>
<el-divider direction="vertical" border-style="dashed" />
<el-link type="primary" @click="showCollections(scope.row.Name)" plain size="small" :underline="false">集合</el-link>
<el-link type="primary" @click="showCollections(scope.row.Name)" plain size="small"
:underline="false">集合</el-link>
<el-divider direction="vertical" border-style="dashed" />
<el-link type="primary" @click="openDataOps(scope.row)" plain size="small" :underline="false">数据操作</el-link>
<el-link type="primary" @click="openDataOps(scope.row)" plain size="small" :underline="false">
数据操作</el-link>
</template>
</el-table-column>
</el-table>
<el-dialog width="700px" :title="databaseDialog.statsDialog.title" v-model="databaseDialog.statsDialog.visible">
<el-dialog width="700px" :title="databaseDialog.statsDialog.title"
v-model="databaseDialog.statsDialog.visible">
<el-descriptions title="库状态信息" :column="3" border size="small">
<el-descriptions-item label="db" label-align="right" align="center">
{{ databaseDialog.statsDialog.data.db }}
@@ -120,7 +122,8 @@
<el-table-column prop="name" label="名称" show-overflow-tooltip> </el-table-column>
<el-table-column min-width="80" label="操作">
<template #default="scope">
<el-link type="success" @click="showCollectionStats(scope.row.name)" plain size="small" :underline="false">stats</el-link>
<el-link type="success" @click="showCollectionStats(scope.row.name)" plain size="small"
:underline="false">stats</el-link>
<el-divider direction="vertical" border-style="dashed" />
<el-popconfirm @confirm="onDeleteCollection(scope.row.name)" title="确定删除该集合?">
<template #reference>
@@ -131,7 +134,8 @@
</el-table-column>
</el-table>
<el-dialog width="700px" :title="collectionsDialog.statsDialog.title" v-model="collectionsDialog.statsDialog.visible">
<el-dialog width="700px" :title="collectionsDialog.statsDialog.title"
v-model="collectionsDialog.statsDialog.visible">
<el-descriptions title="集合状态信息" :column="3" border size="small">
<el-descriptions-item label="ns" label-align="right" :span="2" align="center">
{{ collectionsDialog.statsDialog.data.ns }}
@@ -179,18 +183,14 @@
</template>
</el-dialog>
<mongo-edit
@val-change="valChange"
:title="mongoEditDialog.title"
v-model:visible="mongoEditDialog.visible"
v-model:mongo="mongoEditDialog.data"
></mongo-edit>
<mongo-edit @val-change="valChange" :title="mongoEditDialog.title" v-model:visible="mongoEditDialog.visible"
v-model:mongo="mongoEditDialog.data"></mongo-edit>
</div>
</template>
<script lang="ts">
<script lang="ts" setup>
import { mongoApi } from './api';
import { toRefs, reactive, defineComponent, onMounted } from 'vue';
import { toRefs, reactive, onMounted } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { tagApi } from '../tag/api.ts';
import MongoEdit from './MongoEdit.vue';
@@ -199,19 +199,12 @@ import {store} from '@/store';
import router from '@/router';
import { dateFormat } from '@/common/utils/date';
export default defineComponent({
name: 'MongoList',
components: {
MongoEdit,
},
setup() {
const state = reactive({
tags: [],
dbOps: {
dbId: 0,
db: '',
},
projects: [],
list: [],
total: 0,
currentId: null,
@@ -255,6 +248,18 @@ export default defineComponent({
},
});
const {
tags,
list,
total,
currentId,
query,
mongoEditDialog,
databaseDialog,
collectionsDialog,
createCollectionDialog,
} = toRefs(state)
onMounted(async () => {
search();
});
@@ -419,29 +424,8 @@ export default defineComponent({
router.push({ name: 'MongoDataOp' });
}
return {
...toRefs(state),
dateFormat,
getTags,
search,
handlePageChange,
choose,
showDatabases,
showDatabaseStats,
showCollections,
showCollectionStats,
onDeleteCollection,
showCreateCollectionDialog,
onCreateCollection,
formatByteSize,
deleteMongo,
editMongo,
valChange,
openDataOps,
};
},
});
</script>
<style>
</style>

View File

@@ -6,37 +6,23 @@
<el-col :span="24">
<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 @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-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-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>
@@ -45,16 +31,12 @@
<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: 250px"
v-model="scanParam.match"
@clear="clear()"
clearable
></el-input>
<el-input placeholder="match 支持*模糊key" style="width: 250px" v-model="scanParam.match"
@clear="clear()" clearable></el-input>
</el-form-item>
<el-form-item label="count" label-width="40px">
<el-input placeholder="count" style="width: 70px" v-model.number="scanParam.count"></el-input>
<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>
@@ -63,9 +45,12 @@
<template #reference>
<el-button type="primary" icon="plus" plain></el-button>
</template>
<el-tag @click="onAddData('string')" :color="getTypeColor('string')" style="cursor: pointer">string</el-tag>
<el-tag @click="onAddData('hash')" :color="getTypeColor('hash')" class="ml5" style="cursor: pointer">hash</el-tag>
<el-tag @click="onAddData('set')" :color="getTypeColor('set')" class="ml5" style="cursor: pointer">set</el-tag>
<el-tag @click="onAddData('string')" :color="getTypeColor('string')"
style="cursor: pointer">string</el-tag>
<el-tag @click="onAddData('hash')" :color="getTypeColor('hash')" class="ml5"
style="cursor: pointer">hash</el-tag>
<el-tag @click="onAddData('set')" :color="getTypeColor('set')" class="ml5"
style="cursor: pointer">set</el-tag>
<!-- <el-tag @click="onAddData('list')" :color="getTypeColor('list')" class="ml5" style="cursor: pointer">list</el-tag> -->
</el-popover>
</el-form-item>
@@ -91,8 +76,10 @@
</el-table-column>
<el-table-column label="操作">
<template #default="scope">
<el-button @click="getValue(scope.row)" type="success" icon="search" plain size="small">查看</el-button>
<el-button @click="del(scope.row.key)" type="danger" icon="delete" plain size="small">删除</el-button>
<el-button @click="getValue(scope.row)" type="success" icon="search" plain size="small">查看
</el-button>
<el-button @click="del(scope.row.key)" type="danger" icon="delete" plain size="small">删除
</el-button>
</template>
</el-table-column>
</el-table>
@@ -100,55 +87,27 @@
<div style="text-align: center; margin-top: 10px"></div>
<hash-value
v-model:visible="hashValueDialog.visible"
:operationType="dataEdit.operationType"
:title="dataEdit.title"
:keyInfo="dataEdit.keyInfo"
:redisId="scanParam.id"
:db="scanParam.db"
@cancel="onCancelDataEdit"
@valChange="searchKey"
/>
<hash-value v-model:visible="hashValueDialog.visible" :operationType="dataEdit.operationType"
:title="dataEdit.title" :keyInfo="dataEdit.keyInfo" :redisId="scanParam.id" :db="scanParam.db"
@cancel="onCancelDataEdit" @valChange="searchKey" />
<string-value
v-model:visible="stringValueDialog.visible"
:operationType="dataEdit.operationType"
:title="dataEdit.title"
:keyInfo="dataEdit.keyInfo"
:redisId="scanParam.id"
:db="scanParam.db"
@cancel="onCancelDataEdit"
@valChange="searchKey"
/>
<string-value v-model:visible="stringValueDialog.visible" :operationType="dataEdit.operationType"
:title="dataEdit.title" :keyInfo="dataEdit.keyInfo" :redisId="scanParam.id" :db="scanParam.db"
@cancel="onCancelDataEdit" @valChange="searchKey" />
<set-value
v-model:visible="setValueDialog.visible"
:title="dataEdit.title"
:keyInfo="dataEdit.keyInfo"
:redisId="scanParam.id"
:db="scanParam.db"
:operationType="dataEdit.operationType"
@valChange="searchKey"
@cancel="onCancelDataEdit"
/>
<set-value v-model:visible="setValueDialog.visible" :title="dataEdit.title" :keyInfo="dataEdit.keyInfo"
:redisId="scanParam.id" :db="scanParam.db" :operationType="dataEdit.operationType" @valChange="searchKey"
@cancel="onCancelDataEdit" />
<list-value
v-model:visible="listValueDialog.visible"
:title="dataEdit.title"
:keyInfo="dataEdit.keyInfo"
:redisId="scanParam.id"
:db="scanParam.db"
:operationType="dataEdit.operationType"
@valChange="searchKey"
@cancel="onCancelDataEdit"
/>
<list-value v-model:visible="listValueDialog.visible" :title="dataEdit.title" :keyInfo="dataEdit.keyInfo"
:redisId="scanParam.id" :db="scanParam.db" :operationType="dataEdit.operationType" @valChange="searchKey"
@cancel="onCancelDataEdit" />
</div>
</template>
<script lang="ts">
<script lang="ts" setup>
import { redisApi } from './api';
import { toRefs, reactive, defineComponent, watch } from 'vue';
import { toRefs, reactive, watch } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import HashValue from './HashValue.vue';
import StringValue from './StringValue.vue';
@@ -159,18 +118,8 @@ import { isTrue, notBlank, notNull } from '@/common/assert';
import { useStore } from '@/store/index.ts';
import { tagApi } from '../tag/api.ts';
export default defineComponent({
name: 'DataOperation',
components: {
StringValue,
HashValue,
SetValue,
ListValue,
},
setup() {
let store = useStore();
const state = reactive({
projectId: null,
loading: false,
tags: [],
redisList: [] as any,
@@ -179,7 +128,7 @@ export default defineComponent({
tagPath: null,
},
scanParam: {
id: null,
id: null as any,
db: '',
match: null,
count: 10,
@@ -211,6 +160,22 @@ export default defineComponent({
dbsize: 0,
});
const {
loading,
tags,
redisList,
dbList,
query,
scanParam,
dataEdit,
hashValueDialog,
stringValueDialog,
setValueDialog,
listValueDialog,
keys,
dbsize,
} = toRefs(state)
const searchRedis = async () => {
notBlank(state.query.tagPath, '请先选择标签');
const res = await redisApi.redisList.request(state.query);
@@ -436,26 +401,8 @@ export default defineComponent({
await setSelects(newValue);
});
return {
...toRefs(state),
getTags,
changeTag,
changeRedis,
changeDb,
clearRedis,
searchKey,
scan,
clear,
getValue,
del,
ttlConveter,
getTypeColor,
onAddData,
onCancelDataEdit,
};
},
});
</script>
<style>
</style>

View File

@@ -14,14 +14,18 @@
<el-row class="mt10">
<el-form label-position="right" :inline="true">
<el-form-item label="field" label-width="40px" v-if="operationType == 2">
<el-input placeholder="支持*模糊field" style="width: 140px" v-model="scanParam.match" clearable size="small"></el-input>
<el-input placeholder="支持*模糊field" style="width: 140px" v-model="scanParam.match" clearable
size="small"></el-input>
</el-form-item>
<el-form-item label="count" v-if="operationType == 2">
<el-input placeholder="count" style="width: 62px" v-model.number="scanParam.count" size="small"></el-input>
<el-input placeholder="count" style="width: 62px" v-model.number="scanParam.count" size="small">
</el-input>
</el-form-item>
<el-form-item>
<el-button v-if="operationType == 2" @click="reHscan()" type="success" icon="search" plain size="small"></el-button>
<el-button v-if="operationType == 2" @click="hscan()" icon="bottom" plain size="small">scan</el-button>
<el-button v-if="operationType == 2" @click="reHscan()" type="success" icon="search" plain
size="small"></el-button>
<el-button v-if="operationType == 2" @click="hscan()" icon="bottom" plain size="small">scan
</el-button>
<el-button @click="onAddHashValue" icon="plus" size="small" plain>添加</el-button>
</el-form-item>
<div v-if="operationType == 2" class="mt10" style="float: right">
@@ -37,13 +41,16 @@
</el-table-column>
<el-table-column prop="value" label="value" min-width="200">
<template #default="scope">
<el-input v-model="scope.row.value" clearable type="textarea" :autosize="{ minRows: 2, maxRows: 10 }" size="small"></el-input>
<el-input v-model="scope.row.value" clearable type="textarea"
:autosize="{ minRows: 2, maxRows: 10 }" size="small"></el-input>
</template>
</el-table-column>
<el-table-column label="操作" width="120">
<template #default="scope">
<el-button v-if="operationType == 2" type="success" @click="hset(scope.row)" icon="check" size="small" plain></el-button>
<el-button type="danger" @click="hdel(scope.row.field, scope.$index)" icon="delete" size="small" plain></el-button>
<el-button v-if="operationType == 2" type="success" @click="hset(scope.row)" icon="check"
size="small" plain></el-button>
<el-button type="danger" @click="hdel(scope.row.field, scope.$index)" icon="delete" size="small"
plain></el-button>
</template>
</el-table-column>
</el-table>
@@ -56,16 +63,13 @@
</template>
</el-dialog>
</template>
<script lang="ts">
import { defineComponent, reactive, watch, toRefs } from 'vue';
<script lang="ts" setup>
import { reactive, watch, toRefs } from 'vue';
import { redisApi } from './api';
import { ElMessage, ElMessageBox } from 'element-plus';
import { isTrue, notEmpty } from '@/common/assert';
export default defineComponent({
name: 'HashValue',
components: {},
props: {
const props = defineProps({
visible: {
type: Boolean,
},
@@ -91,9 +95,10 @@ export default defineComponent({
hashValue: {
type: [Array, Object],
},
},
emits: ['valChange', 'cancel', 'update:visible'],
setup(props: any, { emit }) {
})
const emit = defineEmits(['update:visible', 'cancel', 'valChange'])
const state = reactive({
dialogVisible: false,
operationType: 1,
@@ -121,6 +126,15 @@ export default defineComponent({
],
});
const {
dialogVisible,
operationType,
key,
scanParam,
keySize,
hashValues,
} = toRefs(state)
const cancel = () => {
emit('update:visible', false);
emit('cancel');
@@ -130,7 +144,7 @@ export default defineComponent({
}, 500);
};
watch(props, async (newValue) => {
watch(props, async (newValue: any) => {
const visible = newValue.visible;
state.redisId = newValue.redisId;
state.db = newValue.db;
@@ -138,7 +152,7 @@ export default defineComponent({
state.operationType = newValue.operationType;
if (visible && state.operationType == 2) {
state.scanParam.id = props.redisId;
state.scanParam.id = props.redisId as any;
state.scanParam.key = state.key.key;
await reHscan();
}
@@ -233,19 +247,6 @@ export default defineComponent({
cancel();
emit('valChange');
};
return {
...toRefs(state),
reHscan,
hscan,
cancel,
hdel,
hset,
onAddHashValue,
saveValue,
};
},
});
</script>
<style lang="scss">
#string-value-text {

View File

@@ -145,12 +145,10 @@
</div>
</template>
<script lang="ts">
import { defineComponent, reactive, watch, toRefs } from 'vue';
<script lang="ts" setup>
import { reactive, watch, toRefs } from 'vue';
export default defineComponent({
name: 'Info',
props: {
const props = defineProps({
visible: {
type: Boolean,
},
@@ -160,12 +158,18 @@ export default defineComponent({
info: {
type: [Boolean, Object],
},
},
setup(props: any, { emit }) {
})
const emit = defineEmits(['update:visible', 'close'])
const state = reactive({
dialogVisible: false,
});
const {
dialogVisible,
} = toRefs(state)
watch(
() => props.visible,
(val) => {
@@ -177,13 +181,6 @@ export default defineComponent({
emit('update:visible', false);
emit('close');
};
return {
...toRefs(state),
close,
};
},
});
</script>
<style>

View File

@@ -18,32 +18,22 @@
<el-table :data="value" stripe style="width: 100%">
<el-table-column prop="value" label="value" min-width="200">
<template #default="scope">
<el-input v-model="scope.row.value" clearable type="textarea" :autosize="{ minRows: 2, maxRows: 10 }" size="small"></el-input>
<el-input v-model="scope.row.value" clearable type="textarea"
:autosize="{ minRows: 2, maxRows: 10 }" size="small"></el-input>
</template>
</el-table-column>
<el-table-column label="操作" width="140">
<template #default="scope">
<el-button
v-if="operationType == 2"
type="success"
@click="lset(scope.row, scope.$index)"
icon="check"
size="small"
plain
></el-button>
<el-button v-if="operationType == 2" type="success" @click="lset(scope.row, scope.$index)"
icon="check" size="small" plain></el-button>
<!-- <el-button type="danger" @click="set.value.splice(scope.$index, 1)" icon="delete" size="small" plain></el-button> -->
</template>
</el-table-column>
</el-table>
<el-row style="margin-top: 20px" type="flex" justify="end">
<el-pagination
style="text-align: right"
:total="len"
layout="prev, pager, next, total"
@current-change="handlePageChange"
v-model:current-page="pageNum"
:page-size="pageSize"
></el-pagination>
<el-pagination style="text-align: right" :total="len" layout="prev, pager, next, total"
@current-change="handlePageChange" v-model:current-page="pageNum" :page-size="pageSize">
</el-pagination>
</el-row>
</el-form>
<!-- <template #footer>
@@ -54,16 +44,12 @@
</template> -->
</el-dialog>
</template>
<script lang="ts">
import { defineComponent, reactive, watch, toRefs } from 'vue';
<script lang="ts" setup>
import { reactive, watch, toRefs } from 'vue';
import { redisApi } from './api';
import { ElMessage } from 'element-plus';
import { isTrue, notEmpty } from '@/common/assert';
export default defineComponent({
name: 'ListValue',
components: {},
props: {
const props = defineProps({
visible: {
type: Boolean,
},
@@ -88,9 +74,10 @@ export default defineComponent({
listValue: {
type: [Array, Object],
},
},
emits: ['valChange', 'cancel', 'update:visible'],
setup(props: any, { emit }) {
})
const emit = defineEmits(['update:visible', 'cancel', 'valChange'])
const state = reactive({
dialogVisible: false,
operationType: 1,
@@ -109,6 +96,16 @@ export default defineComponent({
pageSize: 10,
});
const {
dialogVisible,
operationType,
key,
value,
len,
pageNum,
pageSize,
} = toRefs(state)
const cancel = () => {
emit('update:visible', false);
emit('cancel');
@@ -122,7 +119,7 @@ export default defineComponent({
}, 500);
};
watch(props, async (newValue) => {
watch(props, async (newValue: any) => {
state.dialogVisible = newValue.visible;
state.key = newValue.key;
state.redisId = newValue.redisId;
@@ -164,37 +161,26 @@ export default defineComponent({
ElMessage.success('数据保存成功');
};
const saveValue = async () => {
notEmpty(state.key.key, 'key不能为空');
isTrue(state.value.length > 0, 'list内容不能为空');
// const sv = { value: state.value.map((x) => x.value), id: state.redisId };
// Object.assign(sv, state.key);
// await redisApi.saveSetValue.request(sv);
// const saveValue = async () => {
// notEmpty(state.key.key, 'key不能为空');
// isTrue(state.value.length > 0, 'list内容不能为空');
// // const sv = { value: state.value.map((x) => x.value), id: state.redisId };
// // Object.assign(sv, state.key);
// // await redisApi.saveSetValue.request(sv);
ElMessage.success('数据保存成功');
cancel();
emit('valChange');
};
// ElMessage.success('数据保存成功');
// cancel();
// emit('valChange');
// };
const onAddListValue = () => {
state.value.unshift({ value: '' });
};
// const onAddListValue = () => {
// state.value.unshift({ value: '' });
// };
const handlePageChange = (curPage: number) => {
state.pageNum = curPage;
getListValue();
};
return {
...toRefs(state),
saveValue,
handlePageChange,
cancel,
lset,
onAddListValue,
};
},
});
</script>
<style lang="scss">
#string-value-text {

View File

@@ -1,6 +1,7 @@
<template>
<div>
<el-dialog :title="title" v-model="dialogVisible" :before-close="cancel" :close-on-click-modal="false" :destroy-on-close="true" width="38%">
<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="tagId" label="标签:" required>
<tag-select v-model:tag-id="form.tagId" v-model:tag-path="form.tagPath" style="width: 100%" />
@@ -16,32 +17,25 @@
</el-select>
</el-form-item>
<el-form-item prop="host" label="host:" required>
<el-input
v-model.trim="form.host"
<el-input v-model.trim="form.host"
placeholder="请输入host:portsentinel模式为: mastername=sentinelhost:port若集群或哨兵需设多个节点可使用','分割"
auto-complete="off"
type="textarea"
></el-input>
auto-complete="off" type="textarea"></el-input>
</el-form-item>
<el-form-item prop="password" label="密码:">
<el-input
type="password"
show-password
v-model.trim="form.password"
placeholder="请输入密码, 修改操作可不填"
autocomplete="new-password"
><template v-if="form.id && form.id != 0" #suffix>
<el-popover @hide="pwd = ''" placement="right" title="原密码" :width="200" trigger="click" :content="pwd">
<el-input type="password" show-password v-model.trim="form.password" placeholder="请输入密码, 修改操作可不填"
autocomplete="new-password"><template v-if="form.id && form.id != 0" #suffix>
<el-popover @hide="pwd = ''" placement="right" title="原密码" :width="200" trigger="click"
:content="pwd">
<template #reference>
<el-link @click="getPwd" :underline="false" type="primary" class="mr5">原密码</el-link>
</template>
</el-popover>
</template></el-input
>
</template></el-input>
</el-form-item>
<el-form-item prop="db" label="库号:" required>
<el-select @change="changeDb" v-model="dbList" multiple placeholder="请选择可操作库号" style="width: 100%">
<el-option v-for="db in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]" :key="db" :label="db" :value="db" />
<el-option v-for="db in [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15]" :key="db"
:label="db" :value="db" />
</el-select>
</el-form-item>
<el-form-item prop="remark" label="备注:">
@@ -49,17 +43,14 @@
</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>
<el-checkbox @change="getSshTunnelMachines" v-model="form.enableSshTunnel" :true-label="1"
:false-label="-1"></el-checkbox>
</el-col>
<el-col :span="2" v-if="form.enableSshTunnel == 1"> 机器: </el-col>
<el-col :span="19" v-if="form.enableSshTunnel == 1">
<el-select style="width: 100%" v-model="form.sshTunnelMachineId" placeholder="请选择SSH隧道机器">
<el-option
v-for="item in sshTunnelMachineList"
:key="item.id"
:label="`${item.ip}:${item.port} [${item.name}]`"
:value="item.id"
>
<el-option v-for="item in sshTunnelMachineList as any" :key="item.id"
:label="`${item.ip}:${item.port} [${item.name}]`" :value="item.id">
</el-option>
</el-select>
</el-col>
@@ -76,62 +67,29 @@
</div>
</template>
<script lang="ts">
import { toRefs, reactive, watch, defineComponent, ref } from 'vue';
<script lang="ts" setup>
import { toRefs, reactive, watch, ref } from 'vue';
import { redisApi } from './api';
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: {
const props = defineProps({
visible: {
type: Boolean,
},
projects: {
type: Array,
},
redis: {
type: [Boolean, Object],
},
title: {
type: String,
},
},
setup(props: any, { emit }) {
const redisForm: any = ref(null);
const state = reactive({
dialogVisible: false,
projects: [],
envs: [],
sshTunnelMachineList: [],
form: {
id: null,
tagId: null as any,
tatPath: null as any,
name: null,
mode: 'standalone',
host: '',
password: null,
db: '',
project: null,
projectId: null,
envId: null,
env: null,
remark: '',
enableSshTunnel: null,
sshTunnelMachineId: null,
},
dbList: [0],
pwd: '',
btnLoading: false,
rules: {
})
const emit = defineEmits(['update:visible', 'val-change', 'cancel'])
const rules = {
projectId: [
{
required: true,
@@ -167,20 +125,53 @@ export default defineComponent({
trigger: ['change', 'blur'],
},
],
}
const redisForm: any = ref(null);
const state = reactive({
dialogVisible: false,
sshTunnelMachineList: [],
form: {
id: null,
tagId: null as any,
tagPath: null as any,
name: null,
mode: 'standalone',
host: '',
password: null,
db: '',
project: null,
projectId: null,
envId: null,
env: null,
remark: '',
enableSshTunnel: null,
sshTunnelMachineId: null,
},
dbList: [0],
pwd: '',
btnLoading: false,
});
watch(props, async (newValue) => {
const {
dialogVisible,
sshTunnelMachineList,
form,
dbList,
pwd,
btnLoading,
} = toRefs(state)
watch(props, async (newValue: any) => {
state.dialogVisible = newValue.visible;
if (!state.dialogVisible) {
return;
}
state.projects = newValue.projects;
if (newValue.redis) {
state.form = { ...newValue.redis };
convertDb(state.form.db);
} else {
state.envs = [];
state.form = { db: '0', enableSshTunnel: -1 } as any;
state.dbList = [];
}
@@ -239,18 +230,7 @@ export default defineComponent({
emit('update:visible', false);
emit('cancel');
};
return {
...toRefs(state),
redisForm,
changeDb,
getSshTunnelMachines,
getPwd,
btnOk,
cancel,
};
},
});
</script>
<style lang="scss">
</style>

View File

@@ -2,8 +2,10 @@
<div>
<el-card>
<el-button type="primary" icon="plus" @click="editRedis(true)" plain>添加</el-button>
<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>
<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="getTags" v-model="query.tagPath" placeholder="请选择标签" filterable clearable>
<el-option v-for="item in tags" :key="item" :label="item" :value="item"> </el-option>
@@ -31,34 +33,27 @@
<el-table-column prop="creator" label="创建人" min-width="100"></el-table-column>
<el-table-column label="更多" min-width="155" fixed="right">
<template #default="scope">
<el-link
v-if="scope.row.mode === 'standalone' || scope.row.mode === 'sentinel'"
type="primary"
@click="info(scope.row)"
:underline="false"
>单机信息</el-link>
<el-link @click="onShowClusterInfo(scope.row)" v-if="scope.row.mode === 'cluster'" type="success" :underline="false">集群信息</el-link>
<el-link v-if="scope.row.mode === 'standalone' || scope.row.mode === 'sentinel'" type="primary"
@click="showInfoDialog(scope.row)" :underline="false">单机信息</el-link>
<el-link @click="onShowClusterInfo(scope.row)" v-if="scope.row.mode === 'cluster'"
type="success" :underline="false">集群信息</el-link>
<el-divider direction="vertical" border-style="dashed" />
<el-link @click="openDataOpt(scope.row)" type="success" :underline="false">数据操作</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-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>
<info v-model:visible="infoDialog.visible" :title="infoDialog.title" :info="infoDialog.info"></info>
<el-dialog width="1000px" title="集群信息" v-model="clusterInfoDialog.visible">
<el-input type="textarea" :autosize="{ minRows: 12, maxRows: 12 }" v-model="clusterInfoDialog.info"> </el-input>
<el-input type="textarea" :autosize="{ minRows: 12, maxRows: 12 }" v-model="clusterInfoDialog.info">
</el-input>
<el-divider content-position="left">节点信息</el-divider>
<el-table :data="clusterInfoDialog.nodes" stripe size="small" border>
@@ -66,44 +61,36 @@
<template #header>
nodeId
<el-tooltip class="box-item" effect="dark" content="节点id" placement="top">
<el-icon><question-filled /></el-icon>
<el-icon>
<question-filled />
</el-icon>
</el-tooltip>
</template>
</el-table-column>
<el-table-column prop="ip" label="ip" min-width="180">
<template #header>
ip
<el-tooltip
class="box-item"
effect="dark"
content="ip:port1@port2port1指redis服务器与客户端通信的端口port2则是集群内部节点间通信的端口"
placement="top"
>
<el-icon><question-filled /></el-icon>
<el-tooltip class="box-item" effect="dark"
content="ip:port1@port2port1指redis服务器与客户端通信的端口port2则是集群内部节点间通信的端口" placement="top">
<el-icon>
<question-filled />
</el-icon>
</el-tooltip>
</template>
<template #default="scope">
<el-tag
@click="info({ id: clusterInfoDialog.redisId, ip: scope.row.ip })"
effect="plain"
type="success"
size="small"
style="cursor: pointer"
>{{ scope.row.ip }}</el-tag
>
<el-tag @click="showInfoDialog({ id: clusterInfoDialog.redisId, ip: scope.row.ip })" effect="plain"
type="success" size="small" style="cursor: pointer">{{ scope.row.ip }}</el-tag>
</template>
</el-table-column>
<el-table-column prop="flags" label="flags" min-width="110"></el-table-column>
<el-table-column prop="masterSlaveRelation" label="masterSlaveRelation" min-width="300">
<template #header>
masterSlaveRelation
<el-tooltip
class="box-item"
effect="dark"
content="如果节点是slave并且已知master节点则为master节点ID否则为符号'-'"
placement="top"
>
<el-icon><question-filled /></el-icon>
<el-tooltip class="box-item" effect="dark"
content="如果节点是slave并且已知master节点则为master节点ID否则为符号'-'" placement="top">
<el-icon>
<question-filled />
</el-icon>
</el-tooltip>
</template>
</el-table-column>
@@ -120,13 +107,12 @@
<el-table-column prop="configEpoch" label="configEpoch" min-width="130">
<template #header>
configEpoch
<el-tooltip
class="box-item"
effect="dark"
<el-tooltip class="box-item" effect="dark"
content="节点的epoch值如果该节点是从节点则为其主节点的epoch值。每当节点发生失败切换时都会创建一个新的独特的递增的epoch。"
placement="top"
>
<el-icon><question-filled /></el-icon>
placement="top">
<el-icon>
<question-filled />
</el-icon>
</el-tooltip>
</template>
</el-table-column>
@@ -135,20 +121,15 @@
</el-table>
</el-dialog>
<redis-edit
@val-change="valChange"
:tags="tags"
:title="redisEditDialog.title"
v-model:visible="redisEditDialog.visible"
v-model:redis="redisEditDialog.data"
></redis-edit>
<redis-edit @val-change="valChange" :tags="tags" :title="redisEditDialog.title"
v-model:visible="redisEditDialog.visible" v-model:redis="redisEditDialog.data"></redis-edit>
</div>
</template>
<script lang="ts">
<script lang="ts" setup>
import Info from './Info.vue';
import { redisApi } from './api';
import { toRefs, reactive, defineComponent, onMounted } from 'vue';
import { toRefs, reactive, onMounted } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { tagApi } from '../tag/api.ts';
import RedisEdit from './RedisEdit.vue';
@@ -156,13 +137,6 @@ import { dateFormat } from '@/common/utils/date';
import { store } from '@/store';
import router from '@/router';
export default defineComponent({
name: 'RedisList',
components: {
Info,
RedisEdit,
},
setup() {
const state = reactive({
tags: [],
redisTable: [],
@@ -175,21 +149,12 @@ export default defineComponent({
pageSize: 10,
clusterId: null,
},
redisInfo: {
url: '',
},
clusterInfoDialog: {
visible: false,
redisId: 0,
info: '',
nodes: [],
},
clusters: [
{
id: 0,
name: '单机',
},
],
infoDialog: {
title: '',
visible: false,
@@ -203,11 +168,22 @@ export default defineComponent({
},
redisEditDialog: {
visible: false,
data: null,
data: null as any,
title: '新增redis',
},
});
const {
tags,
redisTable,
total,
currentId,
query,
clusterInfoDialog,
infoDialog,
redisEditDialog,
} = toRefs(state)
onMounted(async () => {
search();
});
@@ -240,7 +216,7 @@ export default defineComponent({
} catch (err) { }
};
const info = async (redis: any) => {
const showInfoDialog = async (redis: any) => {
var host = redis.host;
if (redis.ip) {
host = redis.ip.split('@')[0];
@@ -300,24 +276,8 @@ export default defineComponent({
}
router.push({ name: 'DataOperation' });
}
return {
...toRefs(state),
dateFormat,
getTags,
search,
handlePageChange,
choose,
info,
onShowClusterInfo,
deleteRedis,
editRedis,
valChange,
openDataOpt,
};
},
});
</script>
<style>
</style>

View File

@@ -15,12 +15,14 @@
<el-table :data="value" stripe style="width: 100%">
<el-table-column prop="value" label="value" min-width="200">
<template #default="scope">
<el-input v-model="scope.row.value" clearable type="textarea" :autosize="{ minRows: 2, maxRows: 10 }" size="small"></el-input>
<el-input v-model="scope.row.value" clearable type="textarea"
:autosize="{ minRows: 2, maxRows: 10 }" size="small"></el-input>
</template>
</el-table-column>
<el-table-column label="操作" width="90">
<template #default="scope">
<el-button type="danger" @click="value.splice(scope.$index, 1)" icon="delete" size="small" plain>删除</el-button>
<el-button type="danger" @click="value.splice(scope.$index, 1)" icon="delete" size="small"
plain>删除</el-button>
</template>
</el-table-column>
</el-table>
@@ -33,16 +35,13 @@
</template>
</el-dialog>
</template>
<script lang="ts">
import { defineComponent, reactive, watch, toRefs } from 'vue';
<script lang="ts" setup>
import { reactive, watch, toRefs } from 'vue';
import { redisApi } from './api';
import { ElMessage } from 'element-plus';
import { isTrue, notEmpty } from '@/common/assert';
export default defineComponent({
name: 'SetValue',
components: {},
props: {
const props = defineProps({
visible: {
type: Boolean,
},
@@ -67,9 +66,10 @@ export default defineComponent({
setValue: {
type: [Array, Object],
},
},
emits: ['valChange', 'cancel', 'update:visible'],
setup(props: any, { emit }) {
})
const emit = defineEmits(['update:visible', 'cancel', 'valChange'])
const state = reactive({
dialogVisible: false,
operationType: 1,
@@ -83,6 +83,13 @@ export default defineComponent({
value: [{ value: '' }],
});
const {
dialogVisible,
operationType,
key,
value,
} = toRefs(state)
const cancel = () => {
emit('update:visible', false);
emit('cancel');
@@ -96,7 +103,7 @@ export default defineComponent({
}, 500);
};
watch(props, async (newValue) => {
watch(props, async (newValue: any) => {
state.dialogVisible = newValue.visible;
state.key = newValue.key;
state.redisId = newValue.redisId;
@@ -137,15 +144,6 @@ export default defineComponent({
const onAddSetValue = () => {
state.value.unshift({ value: '' });
};
return {
...toRefs(state),
saveValue,
cancel,
onAddSetValue,
};
},
});
</script>
<style lang="scss">
#string-value-text {

View File

@@ -12,7 +12,8 @@
</el-form-item>
<div id="string-value-text" style="width: 100%">
<el-input class="json-text" v-model="string.value" type="textarea" :autosize="{ minRows: 10, maxRows: 20 }"></el-input>
<el-input class="json-text" v-model="string.value" type="textarea"
:autosize="{ minRows: 10, maxRows: 20 }"></el-input>
<el-select class="text-type-select" @change="onChangeTextType" v-model="string.type">
<el-option key="text" label="text" value="text"> </el-option>
<el-option key="json" label="json" value="json"> </el-option>
@@ -27,17 +28,14 @@
</template>
</el-dialog>
</template>
<script lang="ts">
import { defineComponent, reactive, watch, toRefs } from 'vue';
<script lang="ts" setup>
import { reactive, watch, toRefs } from 'vue';
import { redisApi } from './api';
import { ElMessage } from 'element-plus';
import { notEmpty } from '@/common/assert';
import { formatJsonString } from '@/common/utils/format';
export default defineComponent({
name: 'StringValue',
components: {},
props: {
const props = defineProps({
visible: {
type: Boolean,
},
@@ -59,9 +57,10 @@ export default defineComponent({
operationType: {
type: [Number],
},
},
emits: ['valChange', 'cancel', 'update:visible'],
setup(props: any, { emit }) {
})
const emit = defineEmits(['update:visible', 'cancel', 'valChange'])
const state = reactive({
dialogVisible: false,
operationType: 1,
@@ -78,6 +77,13 @@ export default defineComponent({
},
});
const {
dialogVisible,
operationType,
key,
string,
} = toRefs(state)
const cancel = () => {
emit('update:visible', false);
emit('cancel');
@@ -102,18 +108,18 @@ export default defineComponent({
watch(
() => props.redisId,
(val) => {
state.redisId = val;
state.redisId = val as any;
}
);
watch(
() => props.db,
(val) => {
state.db = val;
state.db = val as any;
}
);
watch(props, async (newValue) => {
watch(props, async (newValue: any) => {
state.dialogVisible = newValue.visible;
state.key = newValue.key;
state.redisId = newValue.redisId;
@@ -156,15 +162,6 @@ export default defineComponent({
state.string.value = formatJsonString(state.string.value, true);
}
};
return {
...toRefs(state),
saveValue,
cancel,
onChangeTextType,
};
},
});
</script>
<style lang="scss">
#string-value-text {

View File

@@ -1,88 +0,0 @@
<template>
<el-dialog :title="keyValue.key" v-model="dialogVisible" :before-close="cancel" :show-close="false" width="900px">
<el-form>
<el-form-item>
<el-input class="json-text" v-model="keyValue2.jsonValue" type="textarea" :autosize="{ minRows: 10, maxRows: 20 }"></el-input>
</el-form-item>
<!-- <vue3-json-editor v-model="keyValue2.jsonValue" @json-change="valueChange" :show-btns="false" :expandedOnStart="true" /> -->
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="cancel()"> </el-button>
<el-button @click="saveValue" type="primary"> </el-button>
</div>
</template>
</el-dialog>
</template>
<script lang="ts">
import { defineComponent, reactive, watch, toRefs } from 'vue';
import { redisApi } from './api';
import { ElMessage } from 'element-plus';
import { isTrue } from '@/common/assert';
export default defineComponent({
name: 'ValueDialog',
components: {},
props: {
visible: {
type: Boolean,
},
title: {
type: String,
},
keyValue: {
type: [String, Object],
},
},
setup(props: any, { emit }) {
const state = reactive({
dialogVisible: false,
keyValue2: {} as any,
});
const cancel = () => {
emit('update:visible', false);
emit('cancel');
};
watch(
() => props.visible,
(val) => {
state.dialogVisible = val;
}
);
watch(
() => props.keyValue,
(val) => {
state.keyValue2 = val;
if (typeof val.value == 'string') {
state.keyValue2.jsonValue = JSON.stringify(JSON.parse(val.value), null, 2);
} else {
state.keyValue2.jsonValue = JSON.stringify(val.value, null, 2);
}
}
);
const saveValue = async () => {
isTrue(state.keyValue2.type == 'string', '暂不支持除string外其他类型修改');
state.keyValue2.value = state.keyValue2.jsonValue;
await redisApi.saveStringValue.request(state.keyValue2);
ElMessage.success('保存成功');
cancel();
};
const valueChange = (val: any) => {
state.keyValue2.value = JSON.stringify(val);
};
return {
...toRefs(state),
saveValue,
valueChange,
cancel,
};
},
});
</script>

View File

@@ -1,29 +1,24 @@
<template>
<div class="menu">
<div class="toolbar">
<el-input v-model="filterTag" placeholder="输入标签关键字过滤" style="width: 200px; margin-right: 10px" />
<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. 父标签可访问及操作所有子标签关联的资源
<br />2. 可在团队管理中进行分配用于资源隔离 <br />3. 拥有父标签的团队成员可访问操作其自身或子标签关联的资源
</template>
<span>标签作用<el-icon><question-filled /></el-icon></span>
<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"
>
<el-tree ref="tagTreeRef" 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" :filter-node-method="filterNode">
<template #default="{ data }">
<span class="custom-tree-node">
<span style="font-size: 13px">
@@ -34,18 +29,14 @@
<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 @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="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="'tag:save'" @click.prevent="showSaveTabDialog(data)" icon="circle-plus"
:underline="false" type="success" class="ml5" />
<!-- <el-link
v-auth="'resource:changeStatus'"
@@ -68,24 +59,18 @@
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"
/>
<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-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-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>
@@ -118,38 +103,53 @@
</div>
</template>
<script lang="ts">
import { toRefs, ref, reactive, onMounted, defineComponent } from 'vue';
<script lang="ts" setup>
import { toRefs, ref, watch, reactive, onMounted } 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() {
interface Tree {
id: number;
codePath: string;
name: string;
children?: Tree[];
}
const tagForm: any = ref(null);
const tagTreeRef: any = ref(null);
const filterTag = ref('');
const state = reactive({
data: [],
saveTabDialog: {
title: '新增标签',
visible: false,
form: { id: 0, pid: 0, code: '', name: '', remark: '' },
},
//资源信息弹出框对象
infoDialog: {
title: '',
visible: false,
// 资源类型选择是否选
data: null as any,
},
data: [],
props: {
// 展开的节点
defaultExpandedKeys: [] as any
});
const {
data,
saveTabDialog,
infoDialog,
defaultExpandedKeys,
} = toRefs(state)
const props = {
label: 'name',
children: 'children',
},
// 展开的节点
defaultExpandedKeys: [] as any[],
rules: {
};
const rules = {
code: [
{ required: true, message: '标识符不能为空', trigger: 'blur' },
// {
@@ -159,13 +159,21 @@ export default defineComponent({
// },
],
name: [{ required: true, message: '名称不能为空', trigger: 'blur' }],
},
});
};
onMounted(() => {
search();
});
watch(filterTag, (val) => {
tagTreeRef.value!.filter(val);
});
const filterNode = (value: string, data: Tree) => {
if (!value) return true;
return data.codePath.includes(value) || data.name.includes(value);
};
const search = async () => {
let res = await tagApi.getTagTrees.request(null);
state.data = res;
@@ -262,26 +270,11 @@ export default defineComponent({
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;

View File

@@ -2,13 +2,14 @@
<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>
<el-button v-auth="'team:save'" :disabled="!chooseId" @click="showSaveTeamDialog(chooseData)"
type="primary" icon="edit">编辑</el-button>
<el-button v-auth="'team:del'" :disabled="!chooseId" @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-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%">
@@ -36,14 +37,9 @@
</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-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>
@@ -64,23 +60,14 @@
</template>
</el-dialog>
<el-dialog width="500px" :title="showTagDialog.title" :before-close="closeTagDialog" v-model="showTagDialog.visible">
<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"
>
<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">
@@ -88,7 +75,8 @@
<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>
<el-tag v-if="data.children !== null" size="small">{{ data.children.length }}
</el-tag>
</span>
</span>
</template>
@@ -103,12 +91,19 @@
</template>
</el-dialog>
<el-dialog width="600px" :title="showMemDialog.title" v-model="showMemDialog.visible">
<el-dialog width="700px" :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>
<el-button v-auth="'team:member:save'" @click="showAddMemberDialog()" type="primary" icon="plus"
size="small">添加</el-button>
<el-button v-auth="'team:member:del'" @click="deleteMember" :disabled="showMemDialog.chooseId == null"
type="danger" icon="delete" size="small">移除</el-button>
<div style="float: right">
<el-input placeholder="请输入用户名" class="mr2" style="width: 150px"
v-model="showMemDialog.query.username" size="small" @clear="search" clearable></el-input>
<el-button @click="setMemebers" type="success" icon="search" size="small"></el-button>
</div>
<el-table @current-change="chooseMember" border :data="showMemDialog.members.list">
</div>
<el-table @current-change="chooseMember" border :data="showMemDialog.members.list" size="small">
<el-table-column label="选择" width="50px">
<template #default="scope">
<el-radio v-model="showMemDialog.chooseId" :label="scope.row.id">
@@ -116,6 +111,7 @@
</el-radio>
</template>
</el-table-column>
<el-table-column property="name" label="姓名" width="115"></el-table-column>
<el-table-column property="username" label="账号" width="135"></el-table-column>
<el-table-column property="createTime" label="加入时间">
<template #default="scope">
@@ -124,28 +120,18 @@
</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-pagination size="small" @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 style="width: 100%" remote :remote-method="getAccount"
v-model="showMemDialog.memForm.accountIds" filterable multiple placeholder="请输入账号模糊搜索并选择">
<el-option v-for="item in showMemDialog.accounts" :key="item.id"
:label="`${item.username} [${item.name}]`" :value="item.id">
</el-option>
</el-select>
</el-form-item>
</el-form>
@@ -160,21 +146,17 @@
</div>
</template>
<script lang="ts">
import { ref, toRefs, reactive, onMounted, defineComponent } from 'vue';
<script lang="ts" setup>
import { ref, toRefs, reactive, onMounted } 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: '新增团队',
@@ -195,9 +177,10 @@ export default defineComponent({
chooseId: 0,
chooseData: null,
query: {
pageSize: 8,
pageSize: 10,
pageNum: 1,
teamId: null,
username: null,
},
members: {
list: [],
@@ -206,7 +189,7 @@ export default defineComponent({
title: '',
addVisible: false,
memForm: {
accountId: null as any,
accountIds: [] as any,
teamId: 0,
},
accounts: Array(),
@@ -225,6 +208,17 @@ export default defineComponent({
},
});
const {
query,
addTeamDialog,
total,
data,
chooseId,
chooseData,
showMemDialog,
showTagDialog,
} = toRefs(state)
onMounted(() => {
search();
});
@@ -337,7 +331,7 @@ export default defineComponent({
const addMember = async () => {
const memForm = state.showMemDialog.memForm;
memForm.teamId = state.chooseId;
notBlank(memForm.accountId, '请先选择账号');
notBlank(memForm.accountIds, '请先选择账号');
await tagApi.saveTeamMem.request(memForm);
ElMessage.success('保存成功');
@@ -407,34 +401,7 @@ export default defineComponent({
// }
// 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>

View File

@@ -12,8 +12,9 @@
</div>
<div class="personal-user-right">
<el-row>
<el-col :span="24" class="personal-title mb18"
>{{ currentTime }}{{ getUserInfos.username }}生活变的再糟糕也不妨碍我变得更好
<el-col :span="24" class="personal-title mb18">{{ currentTime }}{{
getUserInfos.username
}}生活变的再糟糕也不妨碍我变得更好
</el-col>
<el-col :span="24">
<el-row>
@@ -35,7 +36,9 @@
</el-col>
<el-col :xs="24" :sm="16" class="personal-item mb6">
<div class="personal-item-label">上次登录时间</div>
<div class="personal-item-value">{{ $filters.dateFormat(getUserInfos.lastLoginTime) }}</div>
<div class="personal-item-value">{{
dateFormat(getUserInfos.lastLoginTime)
}}</div>
</el-col>
</el-row>
</el-col>
@@ -54,7 +57,7 @@
</template>
<div class="personal-info-box">
<ul class="personal-info-ul">
<li v-for="(v, k) in msgDialog.msgs.list" :key="k" class="personal-info-li">
<li v-for="(v, k) in msgDialog.msgs.list as any" :key="k" class="personal-info-li">
<a class="personal-info-li-title">{{ `[${getMsgTypeDesc(v.type)}] ${v.msg}` }}</a>
</li>
</ul>
@@ -72,19 +75,13 @@
<el-table-column property="msg" label="消息"></el-table-column>
<el-table-column property="createTime" label="时间" width="150">
<template #default="scope">
{{ $filters.dateFormat(scope.row.createTime) }}
{{ dateFormat(scope.row.createTime) }}
</template>
</el-table-column>
</el-table>
<el-pagination
@current-change="getMsgs"
style="text-align: center"
background
layout="prev, pager, next, total, jumper"
:total="msgDialog.msgs.total"
v-model:current-page="msgDialog.query.pageNum"
:page-size="msgDialog.query.pageSize"
/>
<el-pagination @current-change="getMsgs" style="text-align: center" background
layout="prev, pager, next, total, jumper" :total="msgDialog.msgs.total"
v-model:current-page="msgDialog.query.pageNum" :page-size="msgDialog.query.pageSize" />
</el-dialog>
<!-- 营销推荐 -->
@@ -112,13 +109,8 @@
<el-row :gutter="35">
<el-col :xs="24" :sm="12" :md="8" :lg="6" :xl="4" class="mb20">
<el-form-item label="密码">
<el-input
type="password"
show-password
v-model="accountForm.password"
placeholder="请输入新密码"
clearable
></el-input>
<el-input type="password" show-password v-model="accountForm.password"
placeholder="请输入新密码" clearable></el-input>
</el-form-item>
</el-col>
<!-- -->
@@ -181,17 +173,16 @@
</div>
</template>
<script lang="ts">
<script lang="ts" setup>
import { toRefs, reactive, computed, onMounted } from 'vue';
import { ElMessage } from 'element-plus';
import { formatAxis } from '@/common/utils/formatTime.ts';
import { recommendList } from './mock.ts';
import { useStore } from '@/store/index.ts';
import { personApi } from './api';
export default {
name: 'PersonalPage',
setup() {
import { dateFormat } from '@/common/utils/date';
const store = useStore();
const state = reactive({
accountInfo: {
roles: [],
@@ -208,11 +199,17 @@ export default {
total: null,
},
},
recommendList,
recommendList: [],
accountForm: {
password: '',
},
});
const {
msgDialog,
accountForm,
} = toRefs(state)
// 当前时间提示语
const currentTime = computed(() => {
return formatAxis(new Date());
@@ -261,42 +258,33 @@ export default {
return '通知';
}
};
return {
getUserInfos,
currentTime,
roleInfo,
showMsgs,
getAccountInfo,
getMsgs,
getMsgTypeDesc,
updateAccount,
...toRefs(state),
};
},
};
</script>
<style scoped lang="scss">
@import '../../theme/mixins/mixins.scss';
.personal {
.personal-user {
height: 130px;
display: flex;
align-items: center;
.personal-user-left {
width: 100px;
height: 130px;
border-radius: 3px;
::v-deep(.el-upload) {
height: 100%;
}
.personal-user-left-upload {
img {
width: 100%;
height: 100%;
border-radius: 3px;
}
&:hover {
img {
animation: logoAnimation 0.3s ease-in-out;
@@ -304,51 +292,63 @@ export default {
}
}
}
.personal-user-right {
flex: 1;
padding: 0 15px;
.personal-title {
font-size: 18px;
@include text-ellipsis(1);
}
.personal-item {
display: flex;
align-items: center;
font-size: 13px;
.personal-item-label {
color: gray;
@include text-ellipsis(1);
}
.personal-item-value {
@include text-ellipsis(1);
}
}
}
}
.personal-info {
.personal-info-more {
float: right;
color: gray;
font-size: 13px;
&:hover {
color: var(--color-primary);
cursor: pointer;
}
}
.personal-info-box {
height: 130px;
overflow: hidden;
.personal-info-ul {
list-style: none;
.personal-info-li {
font-size: 13px;
padding-bottom: 10px;
.personal-info-li-title {
display: inline-block;
@include text-ellipsis(1);
color: grey;
text-decoration: none;
}
& a:hover {
color: var(--color-primary);
cursor: pointer;
@@ -357,6 +357,7 @@ export default {
}
}
}
.personal-recommend-row {
.personal-recommend-col {
.personal-recommend {
@@ -366,6 +367,7 @@ export default {
border-radius: 3px;
overflow: hidden;
cursor: pointer;
&:hover {
i {
right: 0px !important;
@@ -373,6 +375,7 @@ export default {
transition: all ease 0.3s;
}
}
i {
position: absolute;
right: -10px;
@@ -381,11 +384,13 @@ export default {
transform: rotate(-30deg);
transition: all ease 0.3s;
}
.personal-recommend-auto {
padding: 15px;
position: absolute;
left: 0;
top: 5%;
.personal-recommend-msg {
font-size: 12px;
margin-top: 10px;
@@ -394,11 +399,13 @@ export default {
}
}
}
.personal-edit {
.personal-edit-title {
position: relative;
padding-left: 10px;
color: #606266;
&::after {
content: '';
width: 2px;
@@ -410,21 +417,26 @@ export default {
background: var(--color-primary);
}
}
.personal-edit-safe-box {
border-bottom: 1px solid #ebeef5;
padding: 15px 0;
.personal-edit-safe-item {
width: 100%;
display: flex;
align-items: center;
justify-content: space-between;
.personal-edit-safe-item-left {
flex: 1;
overflow: hidden;
.personal-edit-safe-item-left-label {
color: #606266;
margin-bottom: 5px;
}
.personal-edit-safe-item-left-value {
color: gray;
@include text-ellipsis(1);
@@ -432,6 +444,7 @@ export default {
}
}
}
&:last-of-type {
padding-bottom: 0;
border-bottom: none;

View File

@@ -1,12 +1,18 @@
<template>
<div class="account-dialog">
<el-dialog :title="title" v-model="dialogVisible" :before-close="cancel" :show-close="false" width="35%" :destroy-on-close="true">
<el-dialog :title="title" v-model="dialogVisible" :before-close="cancel" :show-close="false" width="35%"
:destroy-on-close="true">
<el-form :model="form" ref="accountForm" :rules="rules" label-width="85px">
<el-form-item prop="username" label="用户名:" required>
<el-input :disabled="edit" v-model.trim="form.username" placeholder="请输入账号用户名,密码默认与账号名一致" auto-complete="off"></el-input>
<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 v-if="edit" prop="password" label="密码:" required>
<el-input type="password" v-model.trim="form.password" placeholder="请输入密码" autocomplete="new-password"></el-input>
<el-form-item prop="username" label="用户名:" required>
<el-input :disabled="edit" v-model.trim="form.username" placeholder="请输入账号用户名,密码默认与账号名一致"
auto-complete="off"></el-input>
</el-form-item>
<el-form-item v-if="edit" prop="password" label="密码:">
<el-input type="password" v-model.trim="form.password" placeholder="输入密码可修改用户密码"
autocomplete="new-password"></el-input>
</el-form-item>
</el-form>
@@ -20,14 +26,12 @@
</div>
</template>
<script lang="ts">
import { toRefs, reactive, watch, defineComponent, ref } from 'vue';
<script lang="ts" setup>
import { toRefs, reactive, watch, ref } from 'vue';
import { accountApi } from '../api';
import { ElMessage } from 'element-plus';
export default defineComponent({
name: 'AccountEdit',
props: {
const props = defineProps({
visible: {
type: Boolean,
},
@@ -37,20 +41,21 @@ export default defineComponent({
title: {
type: String,
},
},
setup(props: any, { emit }) {
})
//定义事件
const emit = defineEmits(['update:visible', 'cancel', 'val-change'])
const accountForm: any = ref(null);
const state = reactive({
dialogVisible: false,
edit: false,
form: {
id: null,
username: null,
password: null,
repassword: null,
const rules = {
name: [
{
required: true,
message: '请输入姓名',
trigger: ['change', 'blur'],
},
btnLoading: false,
rules: {
],
username: [
{
required: true,
@@ -58,17 +63,29 @@ export default defineComponent({
trigger: ['change', 'blur'],
},
],
// password: [
// {
// required: true,
// message: '请输入密码',
// trigger: ['change', 'blur'],
// },
// ],
}
const state = reactive({
dialogVisible: false,
edit: false,
form: {
id: null,
name: null,
username: null,
password: null,
repassword: null,
},
btnLoading: false
});
watch(props, (newValue) => {
const {
dialogVisible,
edit,
form,
btnLoading,
} = toRefs(state)
watch(props, (newValue: any) => {
if (newValue.account) {
state.form = { ...newValue.account };
state.edit = true;
@@ -104,15 +121,7 @@ export default defineComponent({
emit('update:visible', false);
emit('cancel');
};
return {
...toRefs(state),
accountForm,
btnOk,
cancel,
};
},
});
</script>
<style lang="scss">
</style>

View File

@@ -2,21 +2,15 @@
<div class="role-list">
<el-card>
<el-button v-auth="'account:add'" type="primary" icon="plus" @click="editAccount(true)">添加</el-button>
<el-button v-auth="'account:add'" :disabled="chooseId == null" @click="editAccount(false)" type="primary" icon="edit">编辑</el-button>
<el-button v-auth="'account:saveRoles'" :disabled="chooseId == null" @click="roleEdit()" type="success" icon="setting"
>角色分配</el-button
>
<el-button v-auth="'account:del'" :disabled="chooseId == null" @click="deleteAccount()" type="danger" icon="delete">删除</el-button>
<el-button v-auth="'account:add'" :disabled="chooseId == null" @click="editAccount(false)" type="primary"
icon="edit">编辑</el-button>
<el-button v-auth="'account:saveRoles'" :disabled="chooseId == null" @click="showRoleEdit()" type="success"
icon="setting">角色分配</el-button>
<el-button v-auth="'account:del'" :disabled="chooseId == null" @click="deleteAccount()" type="danger"
icon="delete">删除</el-button>
<div style="float: right">
<el-input
class="mr2"
placeholder="请输入账号名"
size="small"
style="width: 300px"
v-model="query.username"
@clear="search()"
clearable
></el-input>
<el-input class="mr2" placeholder="请输入账号名" size="small" style="width: 300px" v-model="query.username"
@clear="search()" clearable></el-input>
<el-button @click="search()" type="success" icon="search" size="small"></el-button>
</div>
<el-table :data="datas" ref="table" @current-change="choose" show-overflow-tooltip>
@@ -27,9 +21,10 @@
</el-radio>
</template>
</el-table-column>
<el-table-column prop="name" label="姓名" min-width="115"></el-table-column>
<el-table-column prop="username" label="用户名" min-width="115"></el-table-column>
<el-table-column align="center" prop="status" label="状态" min-width="65">
<el-table-column align="center" prop="status" label="状态" min-width="70">
<template #default="scope">
<el-tag v-if="scope.row.status == 1" type="success">正常</el-tag>
<el-tag v-if="scope.row.status == -1" type="danger">禁用</el-tag>
@@ -37,20 +32,20 @@
</el-table-column>
<el-table-column min-width="160" prop="lastLoginTime" label="最后登录时间" show-overflow-tooltip>
<template #default="scope">
{{ $filters.dateFormat(scope.row.lastLoginTime) }}
{{ dateFormat(scope.row.lastLoginTime) }}
</template>
</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>
<template #default="scope">
{{ $filters.dateFormat(scope.row.createTime) }}
{{ dateFormat(scope.row.createTime) }}
</template>
</el-table-column>
<!-- <el-table-column min-width="115" prop="modifier" label="更新账号"></el-table-column>
<el-table-column min-width="160" prop="updateTime" label="修改时间">
<template #default="scope">
{{ $filters.dateFormat(scope.row.updateTime) }}
{{ dateFormat(scope.row.updateTime) }}
</template>
</el-table-column> -->
@@ -65,37 +60,17 @@
<el-table-column label="操作" min-width="200px">
<template #default="scope">
<el-button
v-auth="'account:changeStatus'"
@click="changeStatus(scope.row)"
v-if="scope.row.status == 1"
type="danger"
icom="tickets"
size="small"
plain
>禁用</el-button
>
<el-button
v-auth="'account:changeStatus'"
v-if="scope.row.status == -1"
type="success"
@click="changeStatus(scope.row)"
size="small"
plain
>启用</el-button
>
<el-button v-auth="'account:changeStatus'" @click="changeStatus(scope.row)"
v-if="scope.row.status == 1" type="danger" icom="tickets" size="small" plain>禁用</el-button>
<el-button v-auth="'account:changeStatus'" v-if="scope.row.status == -1" type="success"
@click="changeStatus(scope.row)" size="small" plain>启用</el-button>
</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-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>
@@ -105,48 +80,41 @@
<el-table-column property="creator" label="分配账号" width="125"></el-table-column>
<el-table-column property="createTime" label="分配时间">
<template #default="scope">
{{ $filters.dateFormat(scope.row.createTime) }}
{{ dateFormat(scope.row.createTime) }}
</template>
</el-table-column>
</el-table>
</el-dialog>
<el-dialog :title="showResourceDialog.title" v-model="showResourceDialog.visible" width="400px">
<el-tree
style="height: 50vh; overflow: auto"
:data="showResourceDialog.resources"
node-key="id"
:props="showResourceDialog.defaultProps"
:expand-on-click-node="true"
>
<el-tree style="height: 50vh; overflow: auto" :data="showResourceDialog.resources" node-key="id"
:props="showResourceDialog.defaultProps" :expand-on-click-node="true">
<template #default="{ node, data }">
<span class="custom-tree-node">
<span v-if="data.type == enums.ResourceTypeEnum.MENU.value">{{ node.label }}</span>
<span v-if="data.type == enums.ResourceTypeEnum.PERMISSION.value" style="color: #67c23a">{{ node.label }}</span>
<span v-if="data.type == enums.ResourceTypeEnum['MENU'].value">{{ node.label }}</span>
<span v-if="data.type == enums.ResourceTypeEnum['PERMISSION'].value" style="color: #67c23a">{{
node.label
}}</span>
</span>
</template>
</el-tree>
</el-dialog>
<role-edit v-model:visible="roleDialog.visible" :account="roleDialog.account" @cancel="cancel()" />
<account-edit v-model:visible="accountDialog.visible" v-model:account="accountDialog.data" @val-change="valChange()" />
<account-edit v-model:visible="accountDialog.visible" v-model:account="accountDialog.data"
@val-change="valChange()" />
</div>
</template>
<script lang='ts'>
import { toRefs, reactive, onMounted, defineComponent } from 'vue';
<script lang='ts' setup>
import { toRefs, reactive, onMounted } from 'vue';
import RoleEdit from './RoleEdit.vue';
import AccountEdit from './AccountEdit.vue';
import enums from '../enums';
import { accountApi } from '../api';
import { ElMessage, ElMessageBox } from 'element-plus';
export default defineComponent({
name: 'AccountList',
components: {
RoleEdit,
AccountEdit,
},
setup() {
import { dateFormat } from '@/common/utils/date';
const state = reactive({
chooseId: null,
/**
@@ -157,6 +125,7 @@ export default defineComponent({
* 查询条件
*/
query: {
username: '',
pageNum: 1,
pageSize: 10,
},
@@ -178,15 +147,26 @@ export default defineComponent({
},
roleDialog: {
visible: false,
account: null,
account: null as any,
roles: [],
},
accountDialog: {
visible: false,
data: null,
data: null as any,
},
});
const {
chooseId,
query,
datas,
total,
showRoleDialog,
showResourceDialog,
roleDialog,
accountDialog,
} = toRefs(state)
onMounted(() => {
search();
});
@@ -240,7 +220,7 @@ export default defineComponent({
search();
};
const roleEdit = () => {
const showRoleEdit = () => {
if (!state.chooseId) {
ElMessage.error('请选择账号');
}
@@ -282,24 +262,7 @@ export default defineComponent({
search();
} catch (err) { }
};
return {
...toRefs(state),
enums,
search,
choose,
showResources,
showRoles,
changeStatus,
handlePageChange,
roleEdit,
editAccount,
cancel,
valChange,
deleteAccount,
};
},
});
</script>
<style lang="scss">
</style>

View File

@@ -1,14 +1,11 @@
<template>
<div class="account-dialog">
<el-dialog
:title="account == null ? '' : '分配“' + account.username + '”的角色'"
v-model="dialogVisible"
:before-close="cancel"
:show-close="false"
>
<el-dialog :title="account == null ? '' : '分配“' + account.username + '”的角色'" v-model="dialogVisible"
:before-close="cancel" :show-close="false">
<div class="toolbar">
<div style="float: left">
<el-input placeholder="请输入角色名" style="width: 150px" v-model="query.name" @clear="clear()" clearable></el-input>
<el-input placeholder="请输入角色名" style="width: 150px" v-model="query.name" @clear="clear()" clearable>
</el-input>
<el-button @click="search" type="success" icon="search"></el-button>
</div>
</div>
@@ -22,15 +19,9 @@
</template>
</el-table-column>
</el-table>
<el-pagination
@current-change="handlePageChange"
style="text-align: center; margin-top: 20px"
background
layout="prev, pager, next, total, jumper"
:total="total"
v-model:current-page="query.pageNum"
:page-size="query.pageSize"
></el-pagination>
<el-pagination @current-change="handlePageChange" style="text-align: center; margin-top: 20px" background
layout="prev, pager, next, total, jumper" :total="total" v-model:current-page="query.pageNum"
:page-size="query.pageSize"></el-pagination>
<template #footer>
<div class="dialog-footer">
@@ -42,29 +33,29 @@
</div>
</template>
<script lang="ts">
import { toRefs, reactive, watch, defineComponent, ref } from 'vue';
<script lang="ts" setup>
import { toRefs, reactive, watch, ref } from 'vue';
import { roleApi, accountApi } from '../api';
import { ElMessage } from 'element-plus';
export default defineComponent({
name: 'RoleEdit',
props: {
const props = defineProps({
visible: {
type: Boolean,
},
account: {
type: [Boolean, Object],
},
},
setup(props: any, { emit }) {
account: Object
})
//定义事件
const emit = defineEmits(['update:visible', 'cancel', 'val-change'])
const roleTable: any = ref(null);
const state = reactive({
dialogVisible: false,
btnLoading: false,
// 所有角色
allRole: [] as any,
// 该账号拥有的角色id
roles: [] as any,
query: {
name: null,
pageNum: 1,
@@ -73,19 +64,28 @@ export default defineComponent({
total: 0,
});
watch(props, (newValue) => {
const {
dialogVisible,
btnLoading,
allRole,
query,
total,
} = toRefs(state)
// 用户拥有的角色信息
let roles: any[] = []
watch(props, (newValue: any) => {
state.dialogVisible = newValue.visible;
if (newValue.account && newValue.account.id != 0) {
if (state.dialogVisible && newValue.account && newValue.account.id != 0) {
accountApi.roleIds
.request({
id: props.account['id'],
id: props.account!.id,
})
.then((res) => {
state.roles = res || [];
roles = res || [];
search();
});
} else {
return;
}
});
@@ -99,7 +99,6 @@ export default defineComponent({
};
const select = (val: any, row: any) => {
let roles = state.roles;
// 如果账号的角色id存在则为取消该角色(删除角色id列表中的该记录id),否则为新增角色
if (roles.includes(row.id)) {
for (let i = 0; i < roles.length; i++) {
@@ -122,7 +121,7 @@ export default defineComponent({
setTimeout(() => {
roleTable.value.clearSelection();
state.allRole.forEach((r: any) => {
if (state.roles.includes(r.id)) {
if (roles.includes(r.id)) {
roleTable.value.toggleRowSelection(r, true);
}
});
@@ -130,9 +129,9 @@ export default defineComponent({
};
const btnOk = async () => {
let roleIds = state.roles.join(',');
let roleIds = roles.join(',');
await accountApi.saveRoles.request({
id: props.account['id'],
id: props.account!.id,
roleIds: roleIds,
});
ElMessage.success('保存成功!');
@@ -164,18 +163,4 @@ export default defineComponent({
state.total = res.total;
checkSelected();
};
return {
...toRefs(state),
roleTable,
search,
handlePageChange,
selectable,
select,
btnOk,
cancel,
clear,
};
},
});
</script>

View File

@@ -1,6 +1,7 @@
<template>
<div>
<el-dialog :title="title" v-model="dvisible" :show-close="false" :before-close="cancel" width="750px" :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>
@@ -14,17 +15,25 @@
</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-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-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-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-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>
@@ -44,13 +53,11 @@
</div>
</template>
<script lang="ts">
<script lang="ts" setup>
import { ref, toRefs, reactive, watch, defineComponent } from 'vue';
import { configApi } from '../api';
export default defineComponent({
name: 'ConfigEdit',
props: {
const props = defineProps({
visible: {
type: Boolean,
},
@@ -60,9 +67,14 @@ export default defineComponent({
title: {
type: String,
},
},
setup(props: any, { emit }) {
})
//定义事件
const emit = defineEmits(['update:visible', 'cancel', 'val-change'])
const configForm: any = ref(null);
const state = reactive({
dvisible: false,
params: [] as any,
@@ -77,7 +89,14 @@ export default defineComponent({
btnLoading: false,
});
watch(props, (newValue) => {
const {
dvisible,
params,
form,
btnLoading,
} = toRefs(state)
watch(props, (newValue: any) => {
state.dvisible = newValue.visible;
if (newValue.data) {
state.form = { ...newValue.data };
@@ -123,17 +142,7 @@ export default defineComponent({
}
});
};
return {
...toRefs(state),
onAddParam,
onDeleteParam,
configForm,
btnOk,
cancel,
};
},
});
</script>
<style lang="scss">
</style>

View File

@@ -2,7 +2,8 @@
<div class="role-list">
<el-card>
<el-button type="primary" icon="plus" @click="editConfig(false)">添加</el-button>
<el-button :disabled="chooseId == null" @click="editConfig(chooseData)" type="primary" icon="edit">编辑</el-button>
<el-button :disabled="chooseId == null" @click="editConfig(chooseData)" type="primary" icon="edit">编辑
</el-button>
<el-table :data="configs" @current-change="choose" ref="table" style="width: 100%">
<el-table-column label="选择" width="55px">
@@ -18,62 +19,42 @@
<el-table-column prop="remark" label="备注" min-width="100px" show-overflow-tooltip></el-table-column>
<el-table-column prop="updateTime" label="更新时间" min-width="100px">
<template #default="scope">
{{ $filters.dateFormat(scope.row.createTime) }}
{{ 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
>
<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
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-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 :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-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-input v-model="paramsDialog.params" :placeholder="paramsDialog.config.remark" autocomplete="off"
clearable></el-input>
</el-form-item>
</el-form>
<template #footer>
@@ -84,24 +65,19 @@
</template>
</el-dialog>
<config-edit :title="configEdit.title" v-model:visible="configEdit.visible" :data="configEdit.config" @val-change="configEditChange" />
<config-edit :title="configEdit.title" v-model:visible="configEdit.visible" :data="configEdit.config"
@val-change="configEditChange" />
</div>
</template>
<script lang="ts">
import { toRefs, reactive, onMounted, defineComponent } from 'vue';
<script lang="ts" setup>
import { toRefs, reactive, onMounted } from 'vue';
import ConfigEdit from './ConfigEdit.vue';
import { configApi } from '../api';
import { ElMessage, ElMessageBox } from 'element-plus';
export default defineComponent({
name: 'ConfigList',
components: {
ConfigEdit,
},
setup() {
import { ElMessage } from 'element-plus';
import { dateFormat } from '@/common/utils/date';
const state = reactive({
dialogFormVisible: false,
currentEditPermissions: false,
query: {
pageNum: 1,
pageSize: 10,
@@ -124,6 +100,16 @@ export default defineComponent({
},
});
const {
query,
total,
configs,
chooseId,
chooseData,
paramsDialog,
configEdit,
} = toRefs(state)
onMounted(() => {
search();
});
@@ -219,20 +205,7 @@ export default defineComponent({
state.configEdit.visible = true;
};
return {
...toRefs(state),
showSetConfigDialog,
closeSetConfigDialog,
setConfig,
search,
handlePageChange,
choose,
configEditChange,
editConfig,
};
},
});
</script>
<style lang="scss">
</style>

View File

@@ -6,7 +6,8 @@
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb10">
<el-form-item prop="type" label="类型" required>
<el-select v-model="form.type" :disabled="typeDisabled" placeholder="请选择">
<el-option v-for="item in enums.ResourceTypeEnum" :key="item.value" :label="item.label" :value="item.value">
<el-option v-for="item in enums.ResourceTypeEnum as any" :key="item.value" :label="item.label"
:value="item.value">
</el-option>
</el-select>
</el-form-item>
@@ -27,55 +28,56 @@
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb10">
<el-form-item v-if="form.type === enums.ResourceTypeEnum.MENU.value" label="图标">
<el-form-item v-if="form.type === menuTypeValue" label="图标">
<icon-selector v-model="form.meta.icon" type="ele" />
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb10">
<el-form-item v-if="form.type === enums.ResourceTypeEnum.MENU.value" prop="code" label="路由名">
<el-form-item v-if="form.type === menuTypeValue" prop="code" label="路由名">
<el-input v-model.trim="form.meta.routeName" placeholder="请输入路由名称"></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb10">
<el-form-item v-if="form.type === enums.ResourceTypeEnum.MENU.value" prop="code" label="组件">
<el-form-item v-if="form.type === menuTypeValue" prop="code" label="组件">
<el-input v-model.trim="form.meta.component" placeholder="请输入组件名"></el-input>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb10">
<el-form-item v-if="form.type === enums.ResourceTypeEnum.MENU.value" prop="code" label="是否缓存">
<el-form-item v-if="form.type === menuTypeValue" prop="code" label="是否缓存">
<el-select v-model="form.meta.isKeepAlive" placeholder="请选择" width="w100">
<el-option v-for="item in trueFalseOption" :key="item.value" :label="item.label" :value="item.value"> </el-option>
<el-option v-for="item in trueFalseOption" :key="item.value" :label="item.label"
:value="item.value"> </el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb10">
<el-form-item v-if="form.type === enums.ResourceTypeEnum.MENU.value" prop="code" label="是否隐藏">
<el-form-item v-if="form.type === menuTypeValue" prop="code" label="是否隐藏">
<el-select v-model="form.meta.isHide" placeholder="请选择" width="w100">
<el-option v-for="item in trueFalseOption" :key="item.value" :label="item.label" :value="item.value"> </el-option>
<el-option v-for="item in trueFalseOption" :key="item.value" :label="item.label"
:value="item.value"> </el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb10">
<el-form-item v-if="form.type === enums.ResourceTypeEnum.MENU.value" prop="code" label="tag不可删除">
<el-form-item v-if="form.type === menuTypeValue" prop="code" label="tag不可删除">
<el-select v-model="form.meta.isAffix" placeholder="请选择" width="w100">
<el-option v-for="item in trueFalseOption" :key="item.value" :label="item.label" :value="item.value"> </el-option>
<el-option v-for="item in trueFalseOption" :key="item.value" :label="item.label"
:value="item.value"> </el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb10">
<el-form-item v-if="form.type === enums.ResourceTypeEnum.MENU.value" prop="code" label="是否iframe">
<el-select @change="changeIsIframe" v-model="form.meta.isIframe" placeholder="请选择" width="w100">
<el-option v-for="item in trueFalseOption" :key="item.value" :label="item.label" :value="item.value"> </el-option>
<el-form-item v-if="form.type === menuTypeValue" prop="code" label="是否iframe">
<el-select @change="changeIsIframe" v-model="form.meta.isIframe" placeholder="请选择"
width="w100">
<el-option v-for="item in trueFalseOption" :key="item.value" :label="item.label"
:value="item.value"> </el-option>
</el-select>
</el-form-item>
</el-col>
<el-col :xs="24" :sm="12" :md="12" :lg="12" :xl="12" class="mb10">
<el-form-item
v-if="form.type === enums.ResourceTypeEnum.MENU.value && form.meta.isIframe"
prop="code"
label="iframe地址"
width="w100"
>
<el-form-item v-if="form.type === menuTypeValue && form.meta.isIframe" prop="code"
label="iframe地址" width="w100">
<el-input v-model.trim="form.meta.link" placeholder="请输入iframe url"></el-input>
</el-form-item>
</el-col>
@@ -92,20 +94,15 @@
</div>
</template>
<script lang="ts">
import { ref, toRefs, reactive, watch, defineComponent } from 'vue';
<script lang="ts" setup>
import { ref, toRefs, reactive, watch } from 'vue';
import { ElMessage } from 'element-plus';
import { resourceApi } from '../api';
import enums from '../enums';
import { notEmpty } from '@/common/assert';
import iconSelector from '@/components/iconSelector/index.vue';
export default defineComponent({
name: 'ResourceEdit',
components: {
iconSelector,
},
props: {
const props = defineProps({
visible: {
type: Boolean,
},
@@ -118,10 +115,15 @@ export default defineComponent({
typeDisabled: {
type: Boolean,
},
},
setup(props: any, { emit }) {
})
//定义事件
const emit = defineEmits(['update:visible', 'cancel', 'val-change'])
const menuForm: any = ref(null);
const menuTypeValue = enums.ResourceTypeEnum['MENU'].value
const defaultMeta = {
routeName: '',
icon: 'Menu',
@@ -131,10 +133,27 @@ export default defineComponent({
isHide: false,
isAffix: false,
isIframe: false,
link: '',
};
const state = reactive({
trueFalseOption: [
const rules = {
name: [
{
required: true,
message: '请输入资源名称',
trigger: ['change', 'blur'],
},
],
weight: [
{
required: true,
message: '请输入序号',
trigger: ['change', 'blur'],
},
],
}
const trueFalseOption = [
{
label: '是',
value: true,
@@ -143,19 +162,10 @@ export default defineComponent({
label: '否',
value: false,
},
],
]
const state = reactive({
dialogVisible: false,
//弹出框对象
dialogForm: {
title: '',
visible: false,
data: {},
},
props: {
value: 'id',
label: 'name',
children: 'children',
},
form: {
id: null,
name: null,
@@ -172,30 +182,19 @@ export default defineComponent({
isHide: false,
isAffix: false,
isIframe: false,
link: '',
},
},
// 资源类型选择是否禁用
// typeDisabled: false,
btnLoading: false,
rules: {
name: [
{
required: true,
message: '请输入资源名称',
trigger: ['change', 'blur'],
},
],
weight: [
{
required: true,
message: '请输入序号',
trigger: ['change', 'blur'],
},
],
},
});
watch(props, (newValue) => {
const {
dialogVisible,
form,
btnLoading,
} = toRefs(state)
watch(props, (newValue: any) => {
state.dialogVisible = newValue.visible;
if (newValue.data) {
state.form = { ...newValue.data };
@@ -285,17 +284,6 @@ export default defineComponent({
emit('update:visible', false);
emit('cancel');
};
return {
...toRefs(state),
enums,
changeIsIframe,
menuForm,
btnOk,
cancel,
};
},
});
</script>
<style lang="scss">
// .m-dialog {

View File

@@ -2,100 +2,57 @@
<div class="menu">
<div class="toolbar">
<div>
<span style="font-size: 14px"><SvgIcon name="info-filled"/>红色字体表示禁用状态</span>
<span style="font-size: 14px">
<SvgIcon name="info-filled" />红色字体表示禁用状态
</span>
</div>
<el-button v-auth="'resource:add'" type="primary" icon="plus" @click="addResource(false)">添加</el-button>
</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"
>
<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" v-if="data.type === enums.ResourceTypeEnum.MENU.value">
<span style="font-size: 13px" v-if="data.type === menuTypeValue">
<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 style="font-size: 13px" v-if="data.type === enums.ResourceTypeEnum.PERMISSION.value">
<span style="font-size: 13px" v-if="data.type === permissionTypeValue">
<span style="color: #3c8dbc"></span>
<span :style="data.status == 1 ? 'color: #67c23a;' : 'color: #f67c6c;'">{{ data.name }}</span>
<span style="color: #3c8dbc"></span>
</span>
<el-link @click.prevent="info(data)" style="margin-left: 25px" icon="view" type="info" :underline="false" />
<el-link @click.prevent="info(data)" style="margin-left: 25px" icon="view" type="info"
:underline="false" />
<el-link
v-auth="'resource:update'"
@click.prevent="editResource(data)"
class="ml5"
type="primary"
icon="edit"
:underline="false"
/>
<el-link v-auth="'resource:update'" @click.prevent="editResource(data)" class="ml5" type="primary"
icon="edit" :underline="false" />
<el-link
v-auth="'resource:add'"
@click.prevent="addResource(data)"
v-if="data.type === enums.ResourceTypeEnum.MENU.value"
icon="circle-plus"
:underline="false"
type="success"
class="ml5"
/>
<el-link v-auth="'resource:add'" @click.prevent="addResource(data)"
v-if="data.type === menuTypeValue" 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 === permissionTypeValue"
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="'resource:changeStatus'" @click.prevent="changeStatus(data, 1)"
v-if="data.status === -1 && data.type === permissionTypeValue"
type="success" icon="circle-check" :underline="false" plain class="ml5" />
<el-link
v-auth="'resource:delete'"
v-if="data.children == null && data.name !== '首页'"
@click.prevent="deleteMenu(data)"
type="danger"
icon="delete"
:underline="false"
plain
class="ml5"
/>
<el-link v-auth="'resource:delete'" v-if="data.children == null && data.name !== '首页'"
@click.prevent="deleteMenu(data)" type="danger" icon="delete" :underline="false" plain
class="ml5" />
</span>
</template>
</el-tree>
<ResourceEdit
:title="dialogForm.title"
v-model:visible="dialogForm.visible"
v-model:data="dialogForm.data"
:typeDisabled="dialogForm.typeDisabled"
:departTree="data"
:type="dialogForm.type"
@val-change="valChange"
></ResourceEdit>
<ResourceEdit :title="dialogForm.title" v-model:visible="dialogForm.visible" v-model:data="dialogForm.data"
:typeDisabled="dialogForm.typeDisabled" :departTree="data" :type="dialogForm.type" @val-change="valChange">
</ResourceEdit>
<el-dialog v-model="infoDialog.visible">
<el-descriptions title="资源信息" :column="2" border>
@@ -123,40 +80,41 @@
<el-descriptions-item v-if="infoDialog.data.type == menuTypeValue" label="是否iframe">
{{ infoDialog.data.meta.isIframe ? '' : '' }}
</el-descriptions-item>
<el-descriptions-item v-if="infoDialog.data.type == menuTypeValue && infoDialog.data.meta.isIframe" label="iframe url">
<el-descriptions-item v-if="infoDialog.data.type == menuTypeValue && infoDialog.data.meta.isIframe"
label="iframe url">
{{ infoDialog.data.meta.link }}
</el-descriptions-item>
<el-descriptions-item label="创建者">{{ infoDialog.data.creator }}</el-descriptions-item>
<el-descriptions-item label="创建时间">{{ $filters.dateFormat(infoDialog.data.createTime) }}</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="更新时间">{{ $filters.dateFormat(infoDialog.data.updateTime) }}</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, reactive, onMounted, defineComponent } from 'vue';
<script lang="ts" setup>
import { toRefs, reactive, onMounted } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import ResourceEdit from './ResourceEdit.vue';
import enums from '../enums';
import { resourceApi } from '../api';
import { dateFormat } from '@/common/utils/date';
const menuTypeValue = enums.ResourceTypeEnum['MENU'].value
const permissionTypeValue = enums.ResourceTypeEnum['PERMISSION'].value
const props = {
label: 'name',
children: 'children',
}
export default defineComponent({
name: 'ResourceList',
components: {
ResourceEdit,
},
setup() {
const state = reactive({
menuTypeValue: enums.ResourceTypeEnum['MENU'].value,
permissionTypeValue: enums.ResourceTypeEnum['PERMISSION'].value,
showBtns: false,
// 当前鼠标右击的节点数据
rightClickData: {},
//弹出框对象
dialogForm: {
type: null,
title: '',
visible: false,
data: { pid: 0, type: 1, weight: 1 },
@@ -169,18 +127,32 @@ export default defineComponent({
visible: false,
// 资源类型选择是否选
data: {
meta: {},
meta: {} as any,
name: '',
type: null,
creator: '',
modifier: '',
createTime: '',
updateTime: '',
weight: null,
code: '',
},
},
data: [],
props: {
label: 'name',
children: 'children',
},
// 展开的节点
defaultExpandedKeys: [] as any[],
});
const {
dialogForm,
infoDialog,
data,
defaultExpandedKeys,
} = toRefs(state)
onMounted(() => {
search();
});
@@ -214,7 +186,7 @@ export default defineComponent({
// 添加顶级菜单情况
if (!data) {
dialog.typeDisabled = true;
dialog.data.type = state.menuTypeValue;
dialog.data.type = menuTypeValue;
dialog.title = '添加顶级菜单';
dialog.visible = true;
return;
@@ -230,16 +202,16 @@ export default defineComponent({
dialog.typeDisabled = true;
let hasPermission = false;
for (let c of data.children) {
if (c.type === state.permissionTypeValue) {
if (c.type === permissionTypeValue) {
hasPermission = true;
break;
}
}
// 如果子节点中存在权限资源,则只能新增权限资源,否则只能新增菜单资源
if (hasPermission) {
dialog.data.type = state.permissionTypeValue;
dialog.data.type = permissionTypeValue;
} else {
dialog.data.type = state.menuTypeValue;
dialog.data.type = menuTypeValue;
}
dialog.data.weight = data.children.length + 1;
}
@@ -314,21 +286,6 @@ export default defineComponent({
}
state.infoDialog.visible = true;
};
return {
...toRefs(state),
enums,
deleteMenu,
addResource,
editResource,
valChange,
changeStatus,
handleNodeExpand,
handleNodeCollapse,
info,
};
},
});
</script>
<style lang="scss">
.menu {

View File

@@ -1,19 +1,15 @@
<template>
<div>
<el-dialog :title="'分配“' + role.name + '”菜单&权限'" v-model="dialogVisible" :before-close="cancel" :show-close="false" width="400px">
<el-tree
style="height: 50vh; overflow: auto"
ref="menuTree"
:data="resources"
show-checkbox
node-key="id"
:default-checked-keys="defaultCheckedKeys"
:props="defaultProps"
>
<el-dialog :title="'分配“' + roleInfo?.name + '”菜单&权限'" v-model="dialogVisible" :before-close="cancel"
:show-close="false" width="400px">
<el-tree style="height: 50vh; overflow: auto" ref="menuTree" :data="resources" show-checkbox node-key="id"
:default-checked-keys="defaultCheckedKeys" :props="defaultProps">
<template #default="{ node, data }">
<span class="custom-tree-node">
<span v-if="data.type == enums.ResourceTypeEnum.MENU.value">{{ node.label }}</span>
<span v-if="data.type == enums.ResourceTypeEnum.PERMISSION.value" style="color: #67c23a">{{ node.label }}</span>
<span v-if="data.type == enums.ResourceTypeEnum['MENU'].value">{{ node.label }}</span>
<span v-if="data.type == enums.ResourceTypeEnum['PERMISSION'].value" style="color: #67c23a">{{
node.label
}}</span>
</span>
</template>
</el-tree>
@@ -27,15 +23,13 @@
</div>
</template>
<script lang="ts">
import { toRefs, reactive, watch, defineComponent, ref } from 'vue';
<script lang="ts" setup>
import { toRefs, reactive, watch, ref } from 'vue';
import { ElMessage } from 'element-plus';
import { roleApi } from '../api';
import enums from '../enums';
export default defineComponent({
name: 'ResourceEdit',
props: {
const props = defineProps({
visible: {
type: Boolean,
},
@@ -53,22 +47,33 @@ export default defineComponent({
resources: {
type: Array,
},
},
setup(props: any, { emit }) {
})
//定义事件
const emit = defineEmits(['update:visible', 'cancel', 'val-change'])
const defaultProps = {
children: 'children',
label: 'name',
}
const menuTree: any = ref(null);
const state = reactive({
dialogVisible: false,
defaultProps: {
children: 'children',
label: 'name',
},
roleInfo: null as any,
});
const {
dialogVisible,
roleInfo,
} = toRefs(state)
watch(
() => props.visible,
(newValue) => {
state.dialogVisible = newValue;
state.roleInfo = props.role
}
);
@@ -76,30 +81,30 @@ export default defineComponent({
* 获取所有菜单树的叶子节点
* @param {Object} trees 菜单树列表
*/
const getAllLeafIds = (trees: any) => {
let leafIds: any = [];
for (let tree of trees) {
setLeafIds(tree, leafIds);
}
return leafIds;
};
// const getAllLeafIds = (trees: any) => {
// let leafIds: any = [];
// for (let tree of trees) {
// setLeafIds(tree, leafIds);
// }
// return leafIds;
// };
const setLeafIds = (tree: any, ids: any) => {
if (tree.children !== null) {
for (let t of tree.children) {
setLeafIds(t, ids);
}
} else {
ids.push(tree.id);
}
};
// const setLeafIds = (tree: any, ids: any) => {
// if (tree.children !== null) {
// for (let t of tree.children) {
// setLeafIds(t, ids);
// }
// } else {
// ids.push(tree.id);
// }
// };
const btnOk = async () => {
let menuIds = menuTree.value.getCheckedKeys();
let halfMenuIds = menuTree.value.getHalfCheckedKeys();
let resources = [].concat(menuIds, halfMenuIds).join(',');
await roleApi.saveResources.request({
id: props.role['id'],
id: props.role!.id,
resourceIds: resources,
});
ElMessage.success('保存成功!');
@@ -111,18 +116,8 @@ export default defineComponent({
emit('update:visible', false);
emit('cancel');
};
return {
...toRefs(state),
enums,
menuTree,
btnOk,
getAllLeafIds,
cancel,
};
},
});
</script>
<style>
</style>

View File

@@ -1,17 +1,14 @@
<template>
<div class="role-dialog">
<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="500px"
:destroy-on-close="true">
<el-form ref="roleForm" :model="form" label-width="90px">
<el-form-item prop="name" label="角色名称:" required>
<el-input v-model="form.name" auto-complete="off"></el-input>
</el-form-item>
<el-form-item prop="code" label="角色code:" required>
<el-input
:disabled="form.id != null"
v-model="form.code"
placeholder="COMMON开头则为所有账号共有角色"
auto-complete="off"
></el-input>
<el-input :disabled="form.id != null" v-model="form.code" placeholder="COMMON开头则为所有账号共有角色"
auto-complete="off"></el-input>
</el-form-item>
<el-form-item label="角色描述:">
<el-input v-model="form.remark" type="textarea" :rows="3" placeholder="请输入角色描述"></el-input>
@@ -27,13 +24,11 @@
</div>
</template>
<script lang="ts">
import { ref, toRefs, reactive, watch, defineComponent } from 'vue';
<script lang="ts" setup>
import { ref, toRefs, reactive, watch } from 'vue';
import { roleApi } from '../api';
export default defineComponent({
name: 'RoleEdit',
props: {
const props = defineProps({
visible: {
type: Boolean,
},
@@ -43,21 +38,31 @@ export default defineComponent({
title: {
type: String,
},
},
setup(props: any, { emit }) {
})
//定义事件
const emit = defineEmits(['update:visible', 'cancel', 'val-change'])
const roleForm: any = ref(null);
const state = reactive({
dvisible: false,
form: {
id: null,
name: '',
code: '',
status: 1,
remark: '',
},
btnLoading: false,
});
watch(props, (newValue) => {
const {
dvisible,
form,
btnLoading,
} = toRefs(state)
watch(props, (newValue: any) => {
state.dvisible = newValue.visible;
if (newValue.data) {
state.form = { ...newValue.data };
@@ -86,15 +91,7 @@ export default defineComponent({
}
});
};
return {
...toRefs(state),
roleForm,
btnOk,
cancel,
};
},
});
</script>
<style lang="scss">
</style>

View File

@@ -2,21 +2,16 @@
<div class="role-list">
<el-card>
<el-button v-auth="'role:add'" type="primary" icon="plus" @click="editRole(false)">添加</el-button>
<el-button v-auth="'role:update'" :disabled="chooseId == null" @click="editRole(chooseData)" type="primary" icon="edit">编辑</el-button>
<el-button v-auth="'role:saveResources'" :disabled="chooseId == null" @click="editResource(chooseData)" type="success" icon="setting"
>分配菜单&权限</el-button
>
<el-button v-auth="'role:del'" :disabled="chooseId == null" @click="deleteRole(chooseData)" type="danger" icon="delete">删除</el-button>
<el-button v-auth="'role:update'" :disabled="chooseId == null" @click="editRole(chooseData)" type="primary"
icon="edit">编辑</el-button>
<el-button v-auth="'role:saveResources'" :disabled="chooseId == null" @click="editResource(chooseData)"
type="success" icon="setting">分配菜单&权限</el-button>
<el-button v-auth="'role:del'" :disabled="chooseId == null" @click="deleteRole(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-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="roles" @current-change="choose" ref="table" style="width: 100%">
@@ -32,12 +27,12 @@
<el-table-column prop="remark" label="描述" min-width="160px" show-overflow-tooltip></el-table-column>
<el-table-column prop="createTime" label="创建时间">
<template #default="scope">
{{ $filters.dateFormat(scope.row.createTime) }}
{{ dateFormat(scope.row.createTime) }}
</template>
</el-table-column>
<el-table-column prop="updateTime" label="修改时间">
<template #default="scope">
{{ $filters.dateFormat(scope.row.updateTime) }}
{{ dateFormat(scope.row.updateTime) }}
</template>
</el-table-column>
<el-table-column label="查看更多" min-width="80px">
@@ -47,51 +42,32 @@
</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-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>
<role-edit :title="roleEdit.title" v-model:visible="roleEdit.visible" :data="roleEdit.role" @val-change="roleEditChange" />
<resource-edit
v-model:visible="resourceDialog.visible"
:role="resourceDialog.role"
:resources="resourceDialog.resources"
:defaultCheckedKeys="resourceDialog.defaultCheckedKeys"
@cancel="cancelEditResources()"
/>
<show-resource
v-model:visible="showResourceDialog.visible"
:title="showResourceDialog.title"
v-model:resources="showResourceDialog.resources"
/>
<role-edit :title="roleEditDialog.title" v-model:visible="roleEditDialog.visible" :data="roleEditDialog.role"
@val-change="roleEditChange" />
<resource-edit v-model:visible="resourceDialog.visible" :role="resourceDialog.role"
:resources="resourceDialog.resources" :defaultCheckedKeys="resourceDialog.defaultCheckedKeys"
@cancel="cancelEditResources()" />
<show-resource v-model:visible="showResourceDialog.visible" :title="showResourceDialog.title"
v-model:resources="showResourceDialog.resources" />
</div>
</template>
<script lang="ts">
import { toRefs, reactive, onMounted, defineComponent } from 'vue';
<script lang="ts" setup>
import { toRefs, reactive, onMounted } from 'vue';
import RoleEdit from './RoleEdit.vue';
import ResourceEdit from './ResourceEdit.vue';
import ShowResource from './ShowResource.vue';
import { roleApi, resourceApi } from '../api';
import { ElMessage, ElMessageBox } from 'element-plus';
export default defineComponent({
name: 'RoleList',
components: {
RoleEdit,
ResourceEdit,
ShowResource,
},
setup() {
import { dateFormat } from '@/common/utils/date';
const state = reactive({
dialogFormVisible: false,
currentEditPermissions: false,
query: {
pageNum: 1,
pageSize: 10,
@@ -107,7 +83,7 @@ export default defineComponent({
resources: [],
defaultCheckedKeys: [],
},
roleEdit: {
roleEditDialog: {
title: '角色编辑',
visible: false,
role: {},
@@ -119,6 +95,17 @@ export default defineComponent({
},
});
const {
query,
total,
roles,
chooseId,
chooseData,
resourceDialog,
roleEditDialog,
showResourceDialog,
} = toRefs(state)
onMounted(() => {
search();
});
@@ -151,12 +138,12 @@ export default defineComponent({
const editRole = (data: any) => {
if (data) {
state.roleEdit.role = data;
state.roleEditDialog.role = data;
} else {
state.roleEdit.role = false;
state.roleEditDialog.role = false;
}
state.roleEdit.visible = true;
state.roleEditDialog.visible = true;
};
const deleteRole = async (data: any) => {
@@ -186,11 +173,6 @@ export default defineComponent({
state.showResourceDialog.visible = true;
};
const closeShowResourceDialog = () => {
state.showResourceDialog.visible = false;
state.showResourceDialog.resources = [];
};
const editResource = async (row: any) => {
let menus = await resourceApi.list.request(null);
// 获取所有菜单列表
@@ -247,22 +229,7 @@ export default defineComponent({
state.resourceDialog.defaultCheckedKeys = [];
}, 10);
};
return {
...toRefs(state),
search,
handlePageChange,
choose,
roleEditChange,
editRole,
deleteRole,
showResources,
closeShowResourceDialog,
editResource,
cancelEditResources,
};
},
});
</script>
<style lang="scss">
</style>

View File

@@ -1,13 +1,17 @@
<template>
<div>
<el-dialog @close="closeDialog" :title="title" :before-close="closeDialog" v-model="dialogVisible" width="400px">
<el-dialog @close="closeDialog" :title="title" :before-close="closeDialog" v-model="dialogVisible"
width="400px">
<el-tree style="height: 50vh; overflow: auto" :data="resources" node-key="id" :props="defaultProps">
<template #default="{ node, data }">
<span class="custom-tree-node">
<span v-if="data.type == enums.ResourceTypeEnum.MENU.value">{{ node.label }}</span>
<span v-if="data.type == enums.ResourceTypeEnum.PERMISSION.value" style="color: #67c23a">{{ node.label }}</span>
<span v-if="data.type == enums.ResourceTypeEnum['MENU'].value">{{ node.label }}</span>
<span v-if="data.type == enums.ResourceTypeEnum['PERMISSION'].value" style="color: #67c23a">{{
node.label
}}</span>
<el-link @click.prevent="info(data)" style="margin-left: 25px" icon="el-icon-view" type="info" :underline="false" />
<el-link @click.prevent="info(data)" style="margin-left: 25px" icon="InfoFilled" type="info"
:underline="false" />
</span>
</template>
</el-tree>
@@ -15,14 +19,12 @@
</div>
</template>
<script lang="ts">
import { getCurrentInstance, toRefs, reactive, watch, defineComponent } from 'vue';
<script lang="ts" setup>
import { getCurrentInstance, toRefs, reactive, watch } from 'vue';
import { ElMessageBox } from 'element-plus';
import enums from '../enums';
export default defineComponent({
name: 'ShowResource',
props: {
const props = defineProps({
visible: {
type: Boolean,
},
@@ -32,16 +34,24 @@ export default defineComponent({
title: {
type: String,
},
},
setup(props: any, { emit }) {
})
//定义事件
const emit = defineEmits(['update:visible', 'update:resources'])
const { proxy } = getCurrentInstance() as any;
const state = reactive({
dialogVisible: false,
defaultProps: {
const defaultProps = {
children: 'children',
label: 'name',
},
}
const state = reactive({
dialogVisible: false,
});
const {
dialogVisible,
} = toRefs(state)
watch(
() => props.visible,
@@ -74,16 +84,8 @@ export default defineComponent({
emit('update:visible', false);
emit('update:resources', []);
};
return {
...toRefs(state),
enums,
info,
closeDialog,
};
},
});
</script>
<style>
</style>

View File

@@ -2,16 +2,10 @@
<div class="role-list">
<el-card>
<div style="float: right">
<el-select
remote
:remote-method="getAccount"
v-model="query.creatorId"
filterable
placeholder="请输入并选择账号"
clearable
class="mr5"
>
<el-option v-for="item in accounts" :key="item.id" :label="item.username" :value="item.id"> </el-option>
<el-select remote :remote-method="getAccount" v-model="query.creatorId" filterable
placeholder="请输入并选择账号" clearable class="mr5">
<el-option v-for="item in accounts" :key="item.id" :label="item.username" :value="item.id">
</el-option>
</el-select>
<el-select v-model="query.type" filterable placeholder="请选择操作结果" clearable class="mr5">
<el-option label="成功" :value="1"> </el-option>
@@ -23,7 +17,7 @@
<el-table-column prop="creator" label="操作人" min-width="100" 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="type" label="结果" min-width="65">
@@ -38,37 +32,39 @@
<el-table-column prop="resp" label="响应信息" min-width="200" show-overflow-tooltip></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-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>
</div>
</template>
<script lang="ts">
import { toRefs, reactive, onMounted, defineComponent } from 'vue';
<script lang="ts" setup>
import { toRefs, reactive, onMounted } from 'vue';
import { logApi, accountApi } from '../api';
export default defineComponent({
name: 'SyslogList',
components: {},
setup() {
import { dateFormat } from '@/common/utils/date';
const state = reactive({
query: {
type: null,
creatorId: null,
pageNum: 1,
pageSize: 10,
name: null,
},
total: 0,
logs: [],
accounts: [],
accounts: [] as any,
});
const {
query,
total,
logs,
accounts,
} = toRefs(state)
onMounted(() => {
search();
});
@@ -90,14 +86,7 @@ export default defineComponent({
});
};
return {
...toRefs(state),
search,
handlePageChange,
getAccount,
};
},
});
</script>
<style lang="scss">
</style>

View File

@@ -17,7 +17,7 @@ const (
WHERE table_schema = (SELECT database())`
// mysql 索引信息
MYSQL_INDEX_INFO = `SELECT INDEX_NAME indexName, COLUMN_NAME columnName, INDEX_TYPE indexType, NON_UNIQUE nonUnique,
MYSQL_INDEX_INFO = `SELECT index_name indexName, column_name columnName, index_type indexType, non_unique nonUnique,
SEQ_IN_INDEX seqInIndex, INDEX_COMMENT indexComment
FROM information_schema.STATISTICS
WHERE table_schema = (SELECT database()) AND table_name = '%s' ORDER BY index_name asc , SEQ_IN_INDEX asc`
@@ -73,20 +73,16 @@ func (mm *MysqlMetadata) GetTableInfos() []map[string]interface{} {
func (mm *MysqlMetadata) GetTableIndex(tableName string) []map[string]interface{} {
res, err := mm.di.innerSelect(fmt.Sprintf(MYSQL_INDEX_INFO, tableName))
biz.ErrIsNilAppendErr(err, "获取表索引信息失败: %s")
// 把查询结果以索引名分组,索引字段以逗号连接
result := make([]map[string]interface{}, 0)
key := ""
i := 0
for k, v := range res {
// 当前的索引名
in := fmt.Sprintf("%v", v["indexName"])
cl := fmt.Sprintf("%v", v["columnName"])
in := v["indexName"].(string)
if key == in {
// 同索引字段以逗号连接
cl1 := fmt.Sprintf("%v", result[i]["columnName"])
result[i]["columnName"] = cl1 + "," + cl
result[i]["columnName"] = result[i]["columnName"].(string) + "," + v["columnName"].(string)
} else {
i = k
key = in

View File

@@ -31,7 +31,7 @@ func (d *dbRepoImpl) GetDbList(condition *entity.DbQuery, pageParam *model.PageP
if condition.TagPathLike != "" {
sql = sql + " AND d.tag_path LIKE '" + condition.TagPathLike + "%'"
}
sql = sql + " ORDER BY d.create_time DESC"
sql = sql + " ORDER BY d.tag_path"
return model.GetPageBySql(sql, pageParam, toEntity)
}

View File

@@ -31,7 +31,7 @@ func (m *machineRepoImpl) GetMachineList(condition *entity.MachineQuery, pagePar
if condition.TagPathLike != "" {
sql = sql + " AND m.tag_path LIKE '" + condition.TagPathLike + "%'"
}
sql = sql + " ORDER BY m.tag_id, m.create_time DESC"
sql = sql + " ORDER BY m.tag_path"
return model.GetPageBySql(sql, pageParam, toEntity)
}

View File

@@ -26,7 +26,7 @@ func (d *mongoRepoImpl) GetList(condition *entity.MongoQuery, pageParam *model.P
if condition.TagPathLike != "" {
sql = sql + " AND d.tag_path LIKE '" + condition.TagPathLike + "%'"
}
sql = sql + " ORDER BY d.create_time DESC"
sql = sql + " ORDER BY d.tag_path"
return model.GetPageBySql(sql, pageParam, toEntity)
}

View File

@@ -29,7 +29,7 @@ func (r *redisRepoImpl) GetRedisList(condition *entity.RedisQuery, pageParam *mo
if condition.TagPathLike != "" {
sql = sql + " AND d.tag_path LIKE '" + condition.TagPathLike + "%'"
}
sql = sql + " ORDER BY d.create_time DESC"
sql = sql + " ORDER BY d.tag_path"
return model.GetPageBySql(sql, pageParam, toEntity)
}

View File

@@ -2,8 +2,9 @@ package form
type AccountCreateForm struct {
Id uint64
Username *string `json:"username" binding:"required,min=4,max=16"`
Password *string `json:"password"`
Name string `json:"name" binding:"required"`
Username string `json:"username" binding:"required,min=4,max=16"`
Password string `json:"password"`
}
type AccountUpdateForm struct {

View File

@@ -7,7 +7,8 @@ import (
type AccountManageVO struct {
model.Model
Username *string `json:"username"`
Name string `json:"name"`
Username string `json:"username"`
Status int `json:"status"`
LastLoginTime *time.Time `json:"lastLoginTime"`
}

View File

@@ -8,6 +8,7 @@ import (
type Account struct {
model.Model
Name string `json:"name"`
Username string `json:"username"`
Password string `json:"-"`
Status int8 `json:"status"`

View File

@@ -56,7 +56,7 @@ func InitAccountRouter(router *gin.RouterGroup) {
ctx.NewReqCtxWithGin(c).Handle(a.Accounts)
})
createAccount := ctx.NewLogInfo("创建账号").WithSave(true)
createAccount := ctx.NewLogInfo("保存账号信息").WithSave(true)
addAccountPermission := ctx.NewPermission("account:add")
account.POST("", func(c *gin.Context) {
ctx.NewReqCtxWithGin(c).

View File

@@ -0,0 +1,6 @@
package form
type TeamMember struct {
TeamId uint64 `json:"teamId"`
AccountIds []uint64 `json:"accountIds"`
}

View File

@@ -5,6 +5,7 @@ import (
sys_applicaiton "mayfly-go/internal/sys/application"
sys_entity "mayfly-go/internal/sys/domain/entity"
"mayfly-go/internal/tag/api/form"
"mayfly-go/internal/tag/api/vo"
"mayfly-go/internal/tag/application"
"mayfly-go/internal/tag/domain/entity"
"mayfly-go/pkg/biz"
@@ -52,26 +53,38 @@ func (p *Team) DelTeam(rc *ctx.ReqCtx) {
// 获取团队的成员信息
func (p *Team) GetTeamMembers(rc *ctx.ReqCtx) {
teamMems := &[]entity.TeamMember{}
rc.ResData = p.TeamApp.GetMemberPage(&entity.TeamMember{TeamId: uint64(ginx.PathParamInt(rc.GinCtx, "id"))},
ginx.GetPageParam(rc.GinCtx), teamMems)
condition := &entity.TeamMember{TeamId: uint64(ginx.PathParamInt(rc.GinCtx, "id"))}
condition.Username = rc.GinCtx.Query("username")
rc.ResData = p.TeamApp.GetMemberPage(condition, ginx.GetPageParam(rc.GinCtx), &[]vo.TeamMember{})
}
// 保存团队信息
func (p *Team) SaveTeamMember(rc *ctx.ReqCtx) {
projectMem := &entity.TeamMember{}
ginx.BindJsonAndValid(rc.GinCtx, projectMem)
teamMems := &form.TeamMember{}
ginx.BindJsonAndValid(rc.GinCtx, teamMems)
rc.ReqParam = fmt.Sprintf("projectId: %d, username: %s", projectMem.TeamId, projectMem.Username)
teamId := teamMems.TeamId
for _, accountId := range teamMems.AccountIds {
if p.TeamApp.IsExistMember(teamId, accountId) {
continue
}
// 校验账号并赋值username
account := &sys_entity.Account{}
account.Id = projectMem.AccountId
account.Id = accountId
biz.ErrIsNil(p.AccountApp.GetAccount(account, "Id", "Username"), "账号不存在")
projectMem.Username = account.Username
projectMem.SetBaseInfo(rc.LoginAccount)
p.TeamApp.SaveMember(projectMem)
teamMember := new(entity.TeamMember)
teamMember.TeamId = teamId
teamMember.AccountId = accountId
teamMember.Username = account.Username
teamMember.SetBaseInfo(rc.LoginAccount)
p.TeamApp.SaveMember(teamMember)
}
rc.ReqParam = teamMems
}
// 删除团队成员

View File

@@ -1,10 +0,0 @@
package vo
// 用户选择项目
type AccountProject struct {
Id uint64 `json:"id"`
Name string `json:"name"`
Remark string `json:"remark"`
}
type AccountProjects []AccountProject

View File

@@ -0,0 +1,14 @@
package vo
import "time"
// 团队成员信息
type TeamMember struct {
Id uint64 `json:"id"`
TeamId uint64 `json:"teamId"`
AccountId uint64 `json:"accountId"`
Username string `json:"username"`
Name string `json:"name"`
Creator string `json:"creator"`
CreateTime *time.Time `json:"createTime"`
}

View File

@@ -17,12 +17,14 @@ type Team interface {
//--------------- 团队成员相关接口 ---------------
GetMemberPage(condition *entity.TeamMember, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
GetMemberPage(condition *entity.TeamMember, pageParam *model.PageParam, toEntity interface{}) *model.PageResult
SaveMember(projectTeamMember *entity.TeamMember)
DeleteMember(teamId, accountId uint64)
IsExistMember(teamId, accounId uint64) bool
// 账号是否有权限访问该项目关联的资源信息
// CanAccess(accountId, projectId uint64) error
@@ -71,8 +73,8 @@ func (p *projectTeamAppImpl) Delete(id uint64) {
// --------------- 团队成员相关接口 ---------------
func (p *projectTeamAppImpl) GetMemberPage(condition *entity.TeamMember, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
return p.projectTeamMemberRepo.GetPageList(condition, pageParam, toEntity, orderBy...)
func (p *projectTeamAppImpl) GetMemberPage(condition *entity.TeamMember, pageParam *model.PageParam, toEntity interface{}) *model.PageResult {
return p.projectTeamMemberRepo.GetPageList(condition, pageParam, toEntity)
}
// 保存团队成员信息
@@ -87,6 +89,10 @@ func (p *projectTeamAppImpl) DeleteMember(teamId, accountId uint64) {
p.projectTeamMemberRepo.DeleteBy(&entity.TeamMember{TeamId: teamId, AccountId: accountId})
}
func (p *projectTeamAppImpl) IsExistMember(teamId, accounId uint64) bool {
return p.projectTeamMemberRepo.IsExist(teamId, accounId)
}
//--------------- 关联项目相关接口 ---------------
func (p *projectTeamAppImpl) ListTagIds(teamId uint64) []uint64 {

View File

@@ -12,7 +12,7 @@ type TeamMember interface {
Save(mp *entity.TeamMember)
GetPageList(condition *entity.TeamMember, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult
GetPageList(condition *entity.TeamMember, pageParam *model.PageParam, toEntity interface{}) *model.PageResult
DeleteBy(condition *entity.TeamMember)

View File

@@ -27,6 +27,6 @@ func (p *tagTreeTeamRepoImpl) DeleteBy(condition *entity.TagTreeTeam) {
func (p *tagTreeTeamRepoImpl) SelectTagPathsByAccountId(accountId uint64) []string {
var res []string
model.GetListBySql2Model("SELECT DISTINCT(t1.tag_path) FROM t_tag_tree_team t1 JOIN t_team_member t2 ON t1.team_id = t2.team_id WHERE t2.account_id = ?", &res, accountId)
model.GetListBySql2Model("SELECT DISTINCT(t1.tag_path) FROM t_tag_tree_team t1 JOIN t_team_member t2 ON t1.team_id = t2.team_id WHERE t2.account_id = ? ORDER BY t1.tag_path", &res, accountId)
return res
}

View File

@@ -1,6 +1,7 @@
package persistence
import (
"fmt"
"mayfly-go/internal/tag/domain/entity"
"mayfly-go/internal/tag/domain/repository"
"mayfly-go/pkg/biz"
@@ -21,8 +22,20 @@ func (p *teamMemberRepoImpl) Save(pm *entity.TeamMember) {
biz.ErrIsNilAppendErr(model.Insert(pm), "保存团队成员失败:%s")
}
func (p *teamMemberRepoImpl) GetPageList(condition *entity.TeamMember, pageParam *model.PageParam, toEntity interface{}, orderBy ...string) *model.PageResult {
return model.GetPage(pageParam, condition, condition, toEntity, orderBy...)
func (p *teamMemberRepoImpl) GetPageList(condition *entity.TeamMember, pageParam *model.PageParam, toEntity interface{}) *model.PageResult {
sql := "SELECT d.*, a.name FROM t_team_member d JOIN t_sys_account a ON d.account_id = a.id WHERE a.status = 1 "
if condition.AccountId != 0 {
sql = fmt.Sprintf("%s AND d.account_id = %d", sql, condition.AccountId)
}
if condition.TeamId != 0 {
sql = fmt.Sprintf("%s AND d.team_id = %d", sql, condition.TeamId)
}
if condition.Username != "" {
sql = sql + " AND d.Username LIKE '%" + condition.Username + "%'"
}
sql = sql + " ORDER BY d.id DESC"
return model.GetPageBySql(sql, pageParam, toEntity)
}
func (p *teamMemberRepoImpl) DeleteBy(condition *entity.TeamMember) {

View File

@@ -283,6 +283,7 @@ COMMIT;
DROP TABLE IF EXISTS `t_sys_account`;
CREATE TABLE `t_sys_account` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`name` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
`username` varchar(30) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
`password` varchar(64) CHARACTER SET utf8mb4 COLLATE utf8mb4_bin NOT NULL,
`status` tinyint(4) DEFAULT NULL,

View File

@@ -1 +0,0 @@
import{_ as s,b as n,k as l,p as c,y as e,q as d,w as m,I as f,B as u,C as _,m as p,A as h}from"./index.1666839152545.js";var x="assets/401.1666839152545.png";const v={name:"401",setup(){const t=n();return{onSetAuth:()=>{f(),t.push("/login")}}}},o=t=>(u("data-v-6ec92039"),t=t(),_(),t),g={class:"error"},y={class:"error-flex"},C={class:"left"},b={class:"left-item"},A=o(()=>e("div",{class:"left-item-animation left-item-num"},"401",-1)),B=o(()=>e("div",{class:"left-item-animation left-item-title"},"\u60A8\u672A\u88AB\u6388\u6743\u6216\u767B\u5F55\u8D85\u65F6\uFF0C\u6CA1\u6709\u64CD\u4F5C\u6743\u9650",-1)),w=o(()=>e("div",{class:"left-item-animation left-item-msg"},null,-1)),S={class:"left-item-animation left-item-btn"},k=o(()=>e("div",{class:"right"},[e("img",{src:x})],-1));function F(t,r,I,a,z,D){const i=l("el-button");return p(),c("div",g,[e("div",y,[e("div",C,[e("div",b,[A,B,w,e("div",S,[d(i,{type:"primary",round:"",onClick:a.onSetAuth},{default:m(()=>[h("\u91CD\u65B0\u767B\u5F55")]),_:1},8,["onClick"])])])]),k])])}var V=s(v,[["render",F],["__scopeId","data-v-6ec92039"]]);export{V as default};

View File

@@ -0,0 +1 @@
import{_ as s,b as n,h as l,j as c,q as e,k as d,w as f,K as m,x as u,y as _,i as p,v as h}from"./index.1667044971054.js";var x="assets/401.1667044971054.png";const v={name:"401",setup(){const t=n();return{onSetAuth:()=>{m(),t.push("/login")}}}},o=t=>(u("data-v-6ec92039"),t=t(),_(),t),g={class:"error"},y={class:"error-flex"},b={class:"left"},C={class:"left-item"},w=o(()=>e("div",{class:"left-item-animation left-item-num"},"401",-1)),A=o(()=>e("div",{class:"left-item-animation left-item-title"},"\u60A8\u672A\u88AB\u6388\u6743\u6216\u767B\u5F55\u8D85\u65F6\uFF0C\u6CA1\u6709\u64CD\u4F5C\u6743\u9650",-1)),B=o(()=>e("div",{class:"left-item-animation left-item-msg"},null,-1)),S={class:"left-item-animation left-item-btn"},k=o(()=>e("div",{class:"right"},[e("img",{src:x})],-1));function F(t,r,I,a,z,D){const i=l("el-button");return p(),c("div",g,[e("div",y,[e("div",b,[e("div",C,[w,A,B,e("div",S,[d(i,{type:"primary",round:"",onClick:a.onSetAuth},{default:f(()=>[h("\u91CD\u65B0\u767B\u5F55")]),_:1},8,["onClick"])])])]),k])])}var V=s(v,[["render",F],["__scopeId","data-v-6ec92039"]]);export{V as default};

View File

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB

View File

@@ -1 +0,0 @@
import{_ as s,b as n,k as l,p as c,y as e,q as m,w as d,B as f,C as u,m as _,A as p}from"./index.1666839152545.js";var x="assets/404.1666839152545.png";const h={name:"404",setup(){const t=n();return{onGoHome:()=>{t.push("/")}}}},o=t=>(f("data-v-69e91ac8"),t=t(),u(),t),v={class:"error"},g={class:"error-flex"},y={class:"left"},F={class:"left-item"},C=o(()=>e("div",{class:"left-item-animation left-item-num"},"404",-1)),b=o(()=>e("div",{class:"left-item-animation left-item-title"},"\u5730\u5740\u8F93\u5165\u6709\u8BEF\uFF0C\u8BF7\u91CD\u65B0\u8F93\u5165\u5730\u5740~",-1)),B=o(()=>e("div",{class:"left-item-animation left-item-msg"},"\u60A8\u53EF\u4EE5\u5148\u68C0\u67E5\u7F51\u5740\uFF0C\u7136\u540E\u91CD\u65B0\u8F93\u5165",-1)),E={class:"left-item-animation left-item-btn"},w=o(()=>e("div",{class:"right"},[e("img",{src:x})],-1));function k(t,a,D,r,I,z){const i=l("el-button");return _(),c("div",v,[e("div",g,[e("div",y,[e("div",F,[C,b,B,e("div",E,[m(i,{type:"primary",round:"",onClick:r.onGoHome},{default:d(()=>[p("\u8FD4\u56DE\u9996\u9875")]),_:1},8,["onClick"])])])]),w])])}var H=s(h,[["render",k],["__scopeId","data-v-69e91ac8"]]);export{H as default};

View File

@@ -0,0 +1 @@
import{_ as s,b as n,h as l,j as c,q as e,k as m,w as d,x as f,y as u,i as _,v as p}from"./index.1667044971054.js";var x="assets/404.1667044971054.png";const h={name:"404",setup(){const t=n();return{onGoHome:()=>{t.push("/")}}}},o=t=>(f("data-v-69e91ac8"),t=t(),u(),t),v={class:"error"},g={class:"error-flex"},y={class:"left"},F={class:"left-item"},b=o(()=>e("div",{class:"left-item-animation left-item-num"},"404",-1)),C=o(()=>e("div",{class:"left-item-animation left-item-title"},"\u5730\u5740\u8F93\u5165\u6709\u8BEF\uFF0C\u8BF7\u91CD\u65B0\u8F93\u5165\u5730\u5740~",-1)),E=o(()=>e("div",{class:"left-item-animation left-item-msg"},"\u60A8\u53EF\u4EE5\u5148\u68C0\u67E5\u7F51\u5740\uFF0C\u7136\u540E\u91CD\u65B0\u8F93\u5165",-1)),w={class:"left-item-animation left-item-btn"},B=o(()=>e("div",{class:"right"},[e("img",{src:x})],-1));function k(t,a,D,r,I,z){const i=l("el-button");return _(),c("div",v,[e("div",g,[e("div",y,[e("div",F,[b,C,E,e("div",w,[m(i,{type:"primary",round:"",onClick:r.onGoHome},{default:d(()=>[p("\u8FD4\u56DE\u9996\u9875")]),_:1},8,["onClick"])])])]),B])])}var H=s(h,[["render",k],["__scopeId","data-v-69e91ac8"]]);export{H as default};

View File

Before

Width:  |  Height:  |  Size: 36 KiB

After

Width:  |  Height:  |  Size: 36 KiB

View File

@@ -1 +1 @@
import{Q as r}from"./index.1666839152545.js";class s{constructor(t,e){this.url=t,this.method=e}setUrl(t){return this.url=t,this}setMethod(t){return this.method=t,this}getUrl(){return r.getApiUrl(this.url)}request(t=null,e=null){return r.send(this,t,e)}requestWithHeaders(t,e){return r.sendWithHeaders(this,t,e)}static create(t,e){return new s(t,e)}}export{s as A};
import{S as r}from"./index.1667044971054.js";class s{constructor(t,e){this.url=t,this.method=e}setUrl(t){return this.url=t,this}setMethod(t){return this.method=t,this}getUrl(){return r.getApiUrl(this.url)}request(t=null,e=null){return r.send(this,t,e)}requestWithHeaders(t,e){return r.sendWithHeaders(this,t,e)}static create(t,e){return new s(t,e)}}export{s as A};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1 @@
.home-container[data-v-8fc94e0e]{overflow-x:hidden}.home-container .home-card-item[data-v-8fc94e0e]{width:100%;height:103px;background:gray;border-radius:4px;transition:all ease .3s;cursor:pointer}.home-container .home-card-item[data-v-8fc94e0e]:hover{box-shadow:0 2px 12px #0000001a;transition:all ease .3s}.home-container .home-card-item-box[data-v-8fc94e0e]{display:flex;align-items:center;position:relative;overflow:hidden}.home-container .home-card-item-box:hover i[data-v-8fc94e0e]{right:0px!important;bottom:0px!important;transition:all ease .3s}.home-container .home-card-item-box i[data-v-8fc94e0e]{position:absolute;right:-10px;bottom:-10px;font-size:70px;transform:rotate(-30deg);transition:all ease .3s}.home-container .home-card-item-box .home-card-item-flex[data-v-8fc94e0e]{padding:0 20px;color:#fff}.home-container .home-card-item-box .home-card-item-flex .home-card-item-title[data-v-8fc94e0e],.home-container .home-card-item-box .home-card-item-flex .home-card-item-tip[data-v-8fc94e0e]{font-size:13px}.home-container .home-card-item-box .home-card-item-flex .home-card-item-title-num[data-v-8fc94e0e]{font-size:18px}.home-container .home-card-item-box .home-card-item-flex .home-card-item-tip-num[data-v-8fc94e0e]{font-size:13px}.home-container .home-card-first[data-v-8fc94e0e]{background:white;border:1px solid #ebeef5;display:flex;align-items:center}.home-container .home-card-first img[data-v-8fc94e0e]{width:60px;height:60px;border-radius:100%;border:2px solid var(--color-primary-light-5)}.home-container .home-card-first .home-card-first-right[data-v-8fc94e0e]{flex:1;display:flex;flex-direction:column}.home-container .home-card-first .home-card-first-right .home-card-first-right-msg[data-v-8fc94e0e]{font-size:13px;color:gray}.home-container .home-monitor[data-v-8fc94e0e]{height:200px}.home-container .home-monitor .flex-warp-item[data-v-8fc94e0e]{width:50%;height:100px;display:flex}.home-container .home-monitor .flex-warp-item .flex-warp-item-box[data-v-8fc94e0e]{margin:auto;height:auto;text-align:center}.home-container .home-warning-card[data-v-8fc94e0e]{height:292px}.home-container .home-warning-card[data-v-8fc94e0e] .el-card{height:100%}.home-container .home-dynamic[data-v-8fc94e0e]{height:200px}.home-container .home-dynamic .home-dynamic-item[data-v-8fc94e0e]{display:flex;width:100%;height:60px;overflow:hidden}.home-container .home-dynamic .home-dynamic-item:first-of-type .home-dynamic-item-line i[data-v-8fc94e0e]{color:orange!important}.home-container .home-dynamic .home-dynamic-item .home-dynamic-item-left[data-v-8fc94e0e]{text-align:right}.home-container .home-dynamic .home-dynamic-item .home-dynamic-item-left .home-dynamic-item-left-time2[data-v-8fc94e0e]{font-size:13px;color:gray}.home-container .home-dynamic .home-dynamic-item .home-dynamic-item-line[data-v-8fc94e0e]{height:60px;border-right:2px dashed #dfdfdf;margin:0 20px;position:relative}.home-container .home-dynamic .home-dynamic-item .home-dynamic-item-line i[data-v-8fc94e0e]{color:var(--color-primary);font-size:12px;position:absolute;top:1px;left:-6px;transform:rotate(46deg);background:white}.home-container .home-dynamic .home-dynamic-item .home-dynamic-item-right[data-v-8fc94e0e]{flex:1}.home-container .home-dynamic .home-dynamic-item .home-dynamic-item-right .home-dynamic-item-right-title i[data-v-8fc94e0e]{margin-right:5px;border:1px solid #dfdfdf;width:20px;height:20px;border-radius:100%;padding:3px 2px 2px;text-align:center;color:var(--color-primary)}.home-container .home-dynamic .home-dynamic-item .home-dynamic-item-right .home-dynamic-item-right-label[data-v-8fc94e0e]{font-size:13px;color:gray}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1 +0,0 @@
var i=Object.defineProperty;var a=Object.getOwnPropertySymbols;var m=Object.prototype.hasOwnProperty,c=Object.prototype.propertyIsEnumerable;var s=(n,e,t)=>e in n?i(n,e,{enumerable:!0,configurable:!0,writable:!0,value:t}):n[e]=t,o=(n,e)=>{for(var t in e||(e={}))m.call(e,t)&&s(n,t,e[t]);if(a)for(var t of a(e))c.call(e,t)&&s(n,t,e[t]);return n};import{S as h}from"./SshTerminal.1666839152545.js";import{_ as p,d,a as l,c as u,e as f,t as _,k as g,p as I,q as v,m as S}from"./index.1666839152545.js";const $=d({name:"SshTerminalPage",components:{SshTerminal:h},props:{machineId:{type:Number}},setup(){const n=l(),e=u({machineId:0,height:700});return f(()=>{e.height=window.innerHeight+5,e.machineId=Number.parseInt(n.query.id)}),o({},_(e))}});function k(n,e,t,N,T,b){const r=g("ssh-terminal");return S(),I("div",null,[v(r,{ref:"terminal",machineId:n.machineId,height:n.height+"px"},null,8,["machineId","height"])])}var y=p($,[["render",k]]);export{y as default};

View File

@@ -0,0 +1 @@
import{_ as s}from"./SshTerminal.1667044971054.js";import{d as o,a as r,c,t as h,e as m,j as d,k as u,l as t,i as l}from"./index.1667044971054.js";const k=o({__name:"SshTerminalPage",setup(_){const a=r(),e=c({machineId:0,height:700}),{machineId:n,height:i}=h(e);return m(()=>{e.height=window.innerHeight+5,e.machineId=Number.parseInt(a.query.id)}),(p,f)=>(l(),d("div",null,[u(s,{ref:"terminal",machineId:t(n),height:t(i)+"px"},null,8,["machineId","height"])]))}});export{k as default};

View File

@@ -1 +0,0 @@
var k=Object.defineProperty,B=Object.defineProperties;var N=Object.getOwnPropertyDescriptors;var f=Object.getOwnPropertySymbols;var z=Object.prototype.hasOwnProperty,A=Object.prototype.propertyIsEnumerable;var h=(e,t,a)=>t in e?k(e,t,{enumerable:!0,configurable:!0,writable:!0,value:a}):e[t]=a,w=(e,t)=>{for(var a in t||(t={}))z.call(t,a)&&h(e,a,t[a]);if(f)for(var a of f(t))A.call(t,a)&&h(e,a,t[a]);return e},C=(e,t)=>B(e,N(t));import{l as S,b as $}from"./api.16668391525453.js";import{_ as P,d as L,c as T,e as U,t as j,k as n,m as p,p as b,q as o,w as u,y as I,O as M,P as O,v as c,A as d,D as R,z as F}from"./index.1666839152545.js";import"./Api.1666839152545.js";const G=L({name:"SyslogList",components:{},setup(){const e=T({query:{pageNum:1,pageSize:10,name:null},total:0,logs:[],accounts:[]});U(()=>{t()});const t=async()=>{let r=await S.list.request(e.query);e.logs=r.list,e.total=r.total},a=r=>{e.query.pageNum=r,t()},m=r=>{$.list.request({username:r}).then(g=>{e.accounts=g.list})};return C(w({},j(e)),{search:t,handlePageChange:a,getAccount:m})}}),H={class:"role-list"},J={style:{float:"right"}};function K(e,t,a,m,r,g){const i=n("el-option"),y=n("el-select"),v=n("el-button"),s=n("el-table-column"),_=n("el-tag"),q=n("el-table"),D=n("el-pagination"),E=n("el-row"),V=n("el-card");return p(),b("div",H,[o(V,null,{default:u(()=>[I("div",J,[o(y,{remote:"","remote-method":e.getAccount,modelValue:e.query.creatorId,"onUpdate:modelValue":t[0]||(t[0]=l=>e.query.creatorId=l),filterable:"",placeholder:"\u8BF7\u8F93\u5165\u5E76\u9009\u62E9\u8D26\u53F7",clearable:"",class:"mr5"},{default:u(()=>[(p(!0),b(M,null,O(e.accounts,l=>(p(),c(i,{key:l.id,label:l.username,value:l.id},null,8,["label","value"]))),128))]),_:1},8,["remote-method","modelValue"]),o(y,{modelValue:e.query.type,"onUpdate:modelValue":t[1]||(t[1]=l=>e.query.type=l),filterable:"",placeholder:"\u8BF7\u9009\u62E9\u64CD\u4F5C\u7ED3\u679C",clearable:"",class:"mr5"},{default:u(()=>[o(i,{label:"\u6210\u529F",value:1}),o(i,{label:"\u5931\u8D25",value:2})]),_:1},8,["modelValue"]),o(v,{onClick:e.search,type:"success",icon:"search"},null,8,["onClick"])]),o(q,{data:e.logs,style:{width:"100%"}},{default:u(()=>[o(s,{prop:"creator",label:"\u64CD\u4F5C\u4EBA","min-width":"100","show-overflow-tooltip":""}),o(s,{prop:"createTime",label:"\u64CD\u4F5C\u65F6\u95F4","min-width":"160"},{default:u(l=>[d(R(e.$filters.dateFormat(l.row.createTime)),1)]),_:1}),o(s,{prop:"type",label:"\u7ED3\u679C","min-width":"65"},{default:u(l=>[l.row.type==1?(p(),c(_,{key:0,type:"success",size:"small"},{default:u(()=>[d("\u6210\u529F")]),_:1})):F("",!0),l.row.type==2?(p(),c(_,{key:1,type:"danger",size:"small"},{default:u(()=>[d("\u5931\u8D25")]),_:1})):F("",!0)]),_:1}),o(s,{prop:"description",label:"\u63CF\u8FF0","min-width":"160","show-overflow-tooltip":""}),o(s,{prop:"reqParam",label:"\u8BF7\u6C42\u4FE1\u606F","min-width":"300","show-overflow-tooltip":""}),o(s,{prop:"resp",label:"\u54CD\u5E94\u4FE1\u606F","min-width":"200","show-overflow-tooltip":""})]),_:1},8,["data"]),o(E,{style:{"margin-top":"20px"},type:"flex",justify:"end"},{default:u(()=>[o(D,{style:{"text-align":"right"},onCurrentChange:e.handlePageChange,total:e.total,layout:"prev, pager, next, total, jumper","current-page":e.query.pageNum,"onUpdate:current-page":t[2]||(t[2]=l=>e.query.pageNum=l),"page-size":e.query.pageSize},null,8,["onCurrentChange","total","current-page","page-size"])]),_:1})]),_:1})])}var Z=P(G,[["render",K]]);export{Z as default};

View File

@@ -0,0 +1 @@
import{l as x,b as B}from"./api.16670449710543.js";import{d as N,c as z,t as A,e as S,h as o,i as c,j as f,k as e,w as a,q as U,l,Q as j,R as I,m,v as _,F as T,U as L,s as w}from"./index.1667044971054.js";import"./Api.1667044971054.js";const P={class:"role-list"},R={style:{float:"right"}},J=N({__name:"SyslogList",setup(M){const r=z({query:{type:null,creatorId:null,pageNum:1,pageSize:10,name:null},total:0,logs:[],accounts:[]}),{query:n,total:F,logs:b,accounts:h}=A(r);S(()=>{i()});const i=async()=>{let s=await x.list.request(r.query);r.logs=s.list,r.total=s.total},C=s=>{r.query.pageNum=s,i()},v=s=>{B.list.request({username:s}).then(u=>{r.accounts=u.list})};return(s,u)=>{const d=o("el-option"),g=o("el-select"),D=o("el-button"),p=o("el-table-column"),y=o("el-tag"),E=o("el-table"),V=o("el-pagination"),k=o("el-row"),q=o("el-card");return c(),f("div",P,[e(q,null,{default:a(()=>[U("div",R,[e(g,{remote:"","remote-method":v,modelValue:l(n).creatorId,"onUpdate:modelValue":u[0]||(u[0]=t=>l(n).creatorId=t),filterable:"",placeholder:"\u8BF7\u8F93\u5165\u5E76\u9009\u62E9\u8D26\u53F7",clearable:"",class:"mr5"},{default:a(()=>[(c(!0),f(j,null,I(l(h),t=>(c(),m(d,{key:t.id,label:t.username,value:t.id},null,8,["label","value"]))),128))]),_:1},8,["modelValue"]),e(g,{modelValue:l(n).type,"onUpdate:modelValue":u[1]||(u[1]=t=>l(n).type=t),filterable:"",placeholder:"\u8BF7\u9009\u62E9\u64CD\u4F5C\u7ED3\u679C",clearable:"",class:"mr5"},{default:a(()=>[e(d,{label:"\u6210\u529F",value:1}),e(d,{label:"\u5931\u8D25",value:2})]),_:1},8,["modelValue"]),e(D,{onClick:i,type:"success",icon:"search"})]),e(E,{data:l(b),style:{width:"100%"}},{default:a(()=>[e(p,{prop:"creator",label:"\u64CD\u4F5C\u4EBA","min-width":"100","show-overflow-tooltip":""}),e(p,{prop:"createTime",label:"\u64CD\u4F5C\u65F6\u95F4","min-width":"160"},{default:a(t=>[_(T(l(L)(t.row.createTime)),1)]),_:1}),e(p,{prop:"type",label:"\u7ED3\u679C","min-width":"65"},{default:a(t=>[t.row.type==1?(c(),m(y,{key:0,type:"success",size:"small"},{default:a(()=>[_("\u6210\u529F")]),_:1})):w("",!0),t.row.type==2?(c(),m(y,{key:1,type:"danger",size:"small"},{default:a(()=>[_("\u5931\u8D25")]),_:1})):w("",!0)]),_:1}),e(p,{prop:"description",label:"\u63CF\u8FF0","min-width":"160","show-overflow-tooltip":""}),e(p,{prop:"reqParam",label:"\u8BF7\u6C42\u4FE1\u606F","min-width":"300","show-overflow-tooltip":""}),e(p,{prop:"resp",label:"\u54CD\u5E94\u4FE1\u606F","min-width":"200","show-overflow-tooltip":""})]),_:1},8,["data"]),e(k,{style:{"margin-top":"20px"},type:"flex",justify:"end"},{default:a(()=>[e(V,{style:{"text-align":"right"},onCurrentChange:C,total:l(F),layout:"prev, pager, next, total, jumper","current-page":l(n).pageNum,"onUpdate:current-page":u[2]||(u[2]=t=>l(n).pageNum=t),"page-size":l(n).pageSize},null,8,["total","current-page","page-size"])]),_:1})]),_:1})])}}});export{J as default};

Some files were not shown because too many files have changed in this diff Show More