feat: 统一分页表格组件、修复系统配置无法配置单个属性

This commit is contained in:
meilin.huang
2023-06-28 21:35:03 +08:00
parent 0d155d592b
commit e2c929aae1
21 changed files with 1151 additions and 822 deletions

View File

@@ -11,7 +11,7 @@ const config = {
baseWsUrl: `${(window as any).globalConfig.BaseWsUrl || `${location.protocol == 'https:' ? 'wss:' : 'ws:'}//${getBaseApiUrl()}`}/api`,
// 系统版本
version: 'v1.4.3'
version: 'v1.4.4'
}
export default config

View File

@@ -0,0 +1,339 @@
<template>
<div class="page-table">
<!--
实现通过我们配置好的 查询条件
首先去创建form表单根据我们配置的查询条件去做一个循环判断展示出不用类型所对应不同的输入框
比如text对应普通的输入框select对应下拉选择dateTime对应日期时间选择器
在使用时父组件会传来一个queryForm空的对象
循环出来的输入框会绑定表格配置中的prop字段绑定在queryForm对象中
-->
<el-card>
<div class="query" ref="queryRef">
<div>
<div v-if="props.query.length > 0" class="query-head">
<div style="display: flex; align-items: center;">
<el-form :model="props.queryForm" label-width="70px" style="display: flex;">
<el-form-item :label="item.label" style="margin-right: 20px; margin-bottom: 0px;"
v-for="item in props.query?.slice(0, defaultQueryCount)" :key="item.prop">
<!-- 这里只获取指定个数的筛选条件 -->
<el-input v-model="queryForm[item.prop]" :placeholder="'输入' + item.label + '关键字'"
clearable v-if="item.type == 'text'"></el-input>
<el-select-v2 v-model="queryForm[item.prop]" :options="item.options" clearable
:placeholder="'选择' + item.label + '关键字'" v-else-if="item.type == 'select'" />
<el-date-picker v-model="queryForm[item.prop]" clearable type="datetimerange"
format="YYYY-MM-DD hh:mm:ss" value-format="x" range-separator=""
start-placeholder="开始时间" end-placeholder="结束时间" v-else-if="item.type == 'date'" />
<slot :name="item.slot"></slot>
</el-form-item>
</el-form>
<template v-if="props.query?.length > defaultQueryCount">
<el-button @click="isOpenMoreQuery = !isOpenMoreQuery" v-if="!isOpenMoreQuery"
icon="ArrowDownBold" circle></el-button>
<el-button @click="isOpenMoreQuery = !isOpenMoreQuery" v-else icon="ArrowUpBold"
circle></el-button>
</template>
<el-button @click="queryData()" type="primary" plain>查询</el-button>
<el-button @click="reset()">重置</el-button>
</div>
</div>
<!-- 这里做的是一个类似于折叠面板的功能 -->
<div class="query-content" :class="isOpenMoreQuery ? 'is-open' : ''">
<el-form :model="props.queryForm" label-width="70px" style="display: flex; flex-wrap: wrap;">
<el-form-item :label="item.label" style="margin-right: 20px; margin-bottom: 0px;"
v-for="item in props.query?.slice(defaultQueryCount)" :key="item.prop">
<!-- 这里获取除前两个以外所有的筛选条件 -->
<el-input v-model="queryForm[item.prop]" :placeholder="'输入' + item.label + '关键字'" clearable
v-if="item.type == 'text'"></el-input>
<el-select-v2 v-model="queryForm[item.prop]" :options="item.options" clearable
:placeholder="'选择' + item.label + '关键字'" v-else-if="item.type == 'select'" />
<el-date-picker v-model="queryForm[item.prop]" clearable type="datetimerange"
format="YYYY-MM-DD hh:mm:ss" value-format="x" range-separator=""
start-placeholder="开始时间" end-placeholder="结束时间" v-else-if="item.type == 'date'" />
<slot :name="item.slot"></slot>
</el-form-item>
</el-form>
</div>
</div>
<div class="slot">
<!-- 查询栏右侧slot插槽用来添加表格其他操作比如新增数据删除数据等其他操作 -->
<slot name="queryRight"></slot>
<!--
动态表头显示根据表格每条配置项中的show字段来决定改列是否显示或者隐藏
columns 就是我们表格配置的数组对象
-->
<el-popover placement="bottom" title="表格配置" :width="200" trigger="click">
<div v-for="(item, index) in props.columns" :key="index">
<el-checkbox v-model="item.show" :label="item.label" :true-label="true" :false-label="false" />
</div>
<template #reference>
<!-- 一个Element Plus中的图标 -->
<el-button icon="Operation"></el-button>
</template>
</el-popover>
</div>
</div>
<el-table v-bind="$attrs" max-height="700" @current-change="choose" :data="props.data" border
highlight-current-row show-overflow-tooltip>
<el-table-column v-if="props.showChooseColumn" label="选择" align="center" width="53px">
<template #default="scope">
<el-radio v-model="state.chooseId" :label="scope.row.id">
<i></i>
</el-radio>
</template>
</el-table-column>
<template v-for="(item, index) in columns">
<el-table-column :key="index" v-if="item.show" :prop="item.prop" :label="item.label" :fixed="item.fixed"
:align="item.align" :show-overflow-tooltip="item.showOverflowTooltip || true"
:min-width="item.minWidth" :sortable="item.sortable || false" :type="item.type" :width="item.width">
<!-- 插槽预留功能 -->
<template #default="scope" v-if="item.slot">
<slot :name="item.slot" :data="scope.row"></slot>
</template>
<template #default="scope" v-else>
<span>{{ item.formatFunc ? item.formatFunc(scope.row[item.prop]) : scope.row[item.prop]
}}</span>
</template>
</el-table-column>
</template>
</el-table>
<el-row style="margin-top: 20px" type="flex" justify="end">
<el-pagination @current-change="handlePageChange" @size-change="handleSizeChange" style="text-align: right"
layout="prev, pager, next, total, sizes, jumper" :total="props.total"
v-model:current-page="state.pageNum" v-model:page-size="state.pageSize"
:page-sizes="[10, 15, 20, 25]" />
</el-row>
</el-card>
</div>
</template>
<script lang='ts' setup>
import { toRefs, watch, reactive, onMounted } from 'vue';
import { TableColumn, TableQuery } from './index';
const emit = defineEmits(['update:queryForm', 'update:pageNum', 'update:pageSize', 'update:chooseData', 'pageChange'])
const props = defineProps({
// 是否显示选择列
showChooseColumn: {
type: Boolean,
default: false,
},
// 选择列绑定的主键key字段名
chooseDataIdKey: {
type: String,
default: "id"
},
// 当前选择的数据
chooseData: {
type: Object
},
// 列信息
columns: {
type: Array<TableColumn>,
default: function () {
return [];
}
},
// 表格数据
data: {
type: Array,
},
total: {
type: [Number],
default: 0,
},
pageNum: {
type: Number,
default: 1,
},
pageSize: {
type: [Number],
default: 10,
},
// 绑定的查询表单
queryForm: {
type: Object,
default: function () {
return {};
}
},
// 查询条件配置
query: {
type: Array<TableQuery>,
default: function () {
return [];
}
}
})
const state = reactive({
pageSize: 10,
pageNum: 1,
chooseData: null as any,
chooseId: 0 as any,
isOpenMoreQuery: false,
defaultQueryCount: 2, // 默认显示的查询参数个数
queryForm: {} as any,
})
const {
isOpenMoreQuery,
defaultQueryCount,
queryForm,
} = toRefs(state)
watch(() => props.queryForm, (newValue: any) => {
state.queryForm = newValue
})
watch(() => props.chooseData, (newValue: any) => {
state.chooseData = newValue
if (newValue) {
state.chooseId = state.chooseData[props.chooseDataIdKey]
} else {
state.chooseId = 0;
}
})
watch(() => props.pageNum, (newValue: any) => {
state.pageNum = newValue
})
watch(() => props.pageSize, (newValue: any) => {
state.pageSize = newValue
})
watch(() => props.data, (newValue: any) => {
if (newValue.length > 0) {
props.columns.forEach(item => {
if (item.autoWidth && item.show) {
item.minWidth = TableColumn.flexColumnWidth(item.prop, item.label, props.data) as any
}
})
}
})
onMounted(() => {
state.pageNum = props.pageNum;
state.pageSize = props.pageSize;
state.queryForm = props.queryForm;
})
// 处理选中了列表中的某一条数据
const choose = (item: any) => {
if (!item || !props.showChooseColumn) {
return;
}
state.chooseData = item;
state.chooseId = item[props.chooseDataIdKey]
emit('update:chooseData', state.chooseData)
};
const handlePageChange = () => {
emit('update:pageNum', state.pageNum)
emit("pageChange")
}
const handleSizeChange = () => {
emit('update:pageSize', state.pageSize)
emit("pageChange")
}
const queryData = () => {
// 触发重新调用查询接口即可
emit("pageChange")
}
const reset = () => {
// 触发重新调用查询接口即可
state.queryForm = {};
emit('update:queryForm', state.queryForm)
emit("pageChange")
}
</script>
<style scoped lang="scss">
.page-table {
.query {
margin-bottom: 10px;
overflow: hidden;
.query-head {
display: flex;
align-items: center;
justify-content: space-between;
}
.query-content {
width: 100%;
max-height: 0px;
transition: all 0.8s;
}
.is-open {
padding: 10px 0;
max-height: 200px;
}
display: flex;
align-items: flex-start;
justify-content: space-between;
margin-bottom: 10px;
.query-content {
display: flex;
align-items: flex-start;
.query-form {
.el-form-item {
margin: 0px;
margin-right: 20px;
}
}
}
.slot {
display: flex;
justify-content: flex-end;
padding-right: 20px;
}
}
.page {
margin-top: 10px;
}
}
::v-deep(.el-form-item__label) {
font-weight: bold;
}
.el-input {
width: 200px;
}
.el-select-v2 {
width: 200px;
}
::v-deep(.el-date-editor) {
width: 380px !important;
}
</style>

View File

@@ -0,0 +1,233 @@
import { dateFormat } from '@/common/utils/date';
export class TableColumn {
/**
* 属性字段
*/
prop: string;
/**
* 显示表头
*/
label: string;
/**
* 是否自动计算宽度
*/
autoWidth: boolean = true;
/**
* 最小宽度
*/
minWidth: number | string;
/**
* 插槽名
*/
slot: string;
showOverflowTooltip: boolean = true;
sortable: boolean = false;
type: string;
width: number | string;
fixed: any;
align: string = "center"
formatFunc: Function
show: boolean = true
constructor(prop: string, label: string) {
this.prop = prop;
this.label = label;
}
static new(prop: string, label: string): TableColumn {
return new TableColumn(prop, label)
}
setMinWidth(minWidth: number | string): TableColumn {
this.minWidth = minWidth
this.autoWidth = false;
return this;
}
setSlot(slot: string): TableColumn {
this.slot = slot
return this;
}
setFormatFunc(func: Function): TableColumn {
this.formatFunc = func;
return this;
}
/**
* 为时间字段,则使用默认时间格式函数
* @returns this
*/
isTime(): TableColumn {
this.setFormatFunc(dateFormat)
return this;
}
fixedRight(): TableColumn {
this.fixed = "right";
return this;
}
fixedLeft(): TableColumn {
this.fixed = "left";
return this;
}
/**
*
* @param str 字符串
* @param tableData 表数据
* @param label 表头label也参与宽度计算
* @returns 列宽度
*/
static flexColumnWidth = (str: any, label: string, tableData: any) => {
// str为该列的字段名(传字符串);tableData为该表格的数据源(传变量);
str = str + '';
let columnContent = '';
if (!tableData || !tableData.length || tableData.length === 0 || tableData === undefined) {
return;
}
if (!str || !str.length || str.length === 0 || str === undefined) {
return;
}
// 获取该列中最长的数据(内容)
let index = 0;
for (let i = 0; i < tableData.length; i++) {
if (!tableData[i][str]) {
continue;
}
const now_temp = tableData[i][str] + '';
const max_temp = tableData[index][str] + '';
if (now_temp.length > max_temp.length) {
index = i;
}
}
columnContent = tableData[index][str] + '';
const contentWidth: number = TableColumn.getContentWidth(columnContent);
// 获取label的宽度取较大的宽度
const columnWidth: number = TableColumn.getContentWidth(label) + 30;
const flexWidth: number = contentWidth > columnWidth ? contentWidth : columnWidth;
return flexWidth + 'px';
};
/**
* 获取内容所需要占用的宽度
*/
static getContentWidth = (content: any): number => {
if (!content) {
return 50;
}
// 以下分配的单位长度可根据实际需求进行调整
let flexWidth = 0;
for (const char of content) {
if (flexWidth > 500) {
break;
}
if ((char >= '0' && char <= '9') || (char >= 'a' && char <= 'z')) {
// 小写字母、数字字符
flexWidth += 9.3;
continue;
}
if (char >= 'A' && char <= 'Z') {
flexWidth += 9;
continue;
}
if (char >= '\u4e00' && char <= '\u9fa5') {
// 如果是中文字符为字符分配16个单位宽度
flexWidth += 20;
} else {
// 其他种类字符
flexWidth += 8;
}
}
if (flexWidth > 400) {
// 设置最大宽度
flexWidth = 400;
}
return flexWidth;
};
}
export class TableQuery {
/**
* 属性字段
*/
prop: string;
/**
* 显示表头
*/
label: string;
/**
* 查询类型text、select、date
*/
type: string;
/**
* select可选值
*/
options: any;
/**
* 插槽名
*/
slot: string;
constructor(prop: string, label: string) {
this.prop = prop;
this.label = label;
}
static new(prop: string, label: string): TableQuery {
return new TableQuery(prop, label)
}
static text(prop: string, label: string): TableQuery {
const tq = new TableQuery(prop, label)
tq.type = 'text';
return tq;
}
static select(prop: string, label: string): TableQuery {
const tq = new TableQuery(prop, label)
tq.type = 'select';
return tq;
}
static date(prop: string, label: string): TableQuery {
const tq = new TableQuery(prop, label)
tq.type = 'date';
return tq;
}
static slot(prop: string, label: string, slotName: string): TableQuery {
const tq = new TableQuery(prop, label)
tq.slot = slotName;
return tq;
}
setOptions(options: any): TableQuery {
this.options = options;
return this
}
}

View File

@@ -1,85 +1,66 @@
<template>
<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>
<div style="float: right">
<el-select @focus="getTags" v-model="query.tagPath" placeholder="请选择标签" filterable clearable>
<page-table :query="state.queryConfig" v-model:query-form="query" :show-choose-column="true"
v-model:choose-data="state.chooseData" :data="datas" :columns="state.columns" :total="total"
v-model:page-size="query.pageSize" v-model:page-num="query.pageNum" @pageChange="search()">
<template #tagPathSelect>
<el-select @focus="getTags" v-model="query.tagPath" placeholder="请选择标签" @clear="search" filterable clearable
style="width: 200px">
<el-option v-for="item in tags" :key="item" :label="item" :value="item"> </el-option>
</el-select>
<el-button type="success" icon="search" @click="search()" class="ml5"></el-button>
</div>
<el-table :data="datas" ref="table" @current-change="choose" show-overflow-tooltip stripe>
<el-table-column label="选择" width="60px">
<template #default="scope">
<el-radio v-model="chooseId" :label="scope.row.id">
<i></i>
</el-radio>
</template>
</el-table-column>
<el-table-column prop="tagPath" label="标签路径" min-width="150" show-overflow-tooltip>
<template #default="scope">
<tag-info :tag-path="scope.row.tagPath" />
<span class="ml5">
{{ scope.row.tagPath }}
</span>
</template>
</el-table-column>
<el-table-column prop="name" label="名称" min-width="160" show-overflow-tooltip></el-table-column>
<el-table-column min-width="170" label="host:port" show-overflow-tooltip>
<template #default="scope">
{{ `${scope.row.host}:${scope.row.port}` }}
</template>
</el-table-column>
<el-table-column prop="type" label="类型" min-width="90"></el-table-column>
<el-table-column prop="database" label="数据库" min-width="80">
<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>
</template>
<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>
</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">{{ db }}</el-link>
<el-link type="primary" plain size="small" :underline="false"
@click="showTableInfo(scope.row, db)" style="position: absolute; right: 4px">操作
</el-link>
</div>
</el-popover>
</template>
</el-table-column>
<el-table-column prop="username" label="用户名" min-width="100"></el-table-column>
<el-table-column prop="remark" label="备注" min-width="150" show-overflow-tooltip></el-table-column>
</template>
<el-table-column label="操作" min-width="160" fixed="right">
<template #default="scope">
<el-link plain size="small" :underline="false" @click="showInfo(scope.row)">
详情</el-link>
<el-divider direction="vertical" border-style="dashed" />
<el-link class="ml5" type="primary" plain size="small" :underline="false"
@click="onShowSqlExec(scope.row)">
SQL执行记录</el-link>
<template #queryRight>
<el-button v-auth="permissions.saveDb" type="primary" icon="plus" @click="editDb(true)">添加</el-button>
<el-button v-auth="permissions.saveDb" :disabled="!chooseData" @click="editDb(false)" type="primary"
icon="edit">编辑</el-button>
<el-button v-auth="permissions.delDb" :disabled="!chooseData" @click="deleteDb(chooseData.id)" type="danger"
icon="delete">删除</el-button>
</template>
<template #tagPath="{ data }">
<tag-info :tag-path="data.tagPath" />
<span class="ml5">
{{ data.tagPath }}
</span>
</template>
<template #hostPort="{ data }">
{{ `${data.host}:${data.port}` }}
</template>
<template #database="{ data }">
<el-popover placement="right" trigger="click" :width="300">
<template #reference>
<el-link type="primary" :underline="false" plain @click="selectDb(data.dbs)">查看
</el-link>
</template>
</el-table-column>
</el-table>
<el-row style="margin-top: 20px" type="flex" justify="end">
<el-pagination style="text-align: right" @current-change="handlePageChange" :total="total"
layout="prev, pager, next, total, jumper" v-model:current-page="query.pageNum"
:page-size="query.pageSize"></el-pagination>
</el-row>
</el-card>
<el-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>
</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">{{ db }}</el-link>
<el-link type="primary" plain size="small" :underline="false" @click="showTableInfo(data, db)"
style="position: absolute; right: 4px">操作
</el-link>
</div>
</el-popover>
</template>
<template #action="{ data }">
<el-link plain size="small" :underline="false" @click="showInfo(data)">
详情</el-link>
<el-divider direction="vertical" border-style="dashed" />
<el-link class="ml5" type="primary" plain size="small" :underline="false" @click="onShowSqlExec(data)">
SQL执行记录</el-link>
</template>
</page-table>
<el-dialog width="80%" :title="`${db} 表信息`" :before-close="closeTableInfo" v-model="tableInfoDialog.visible">
<el-row class="mb10">
@@ -147,8 +128,7 @@
<template #default="scope">
<el-link @click.prevent="showColumns(scope.row)" type="primary">字段</el-link>
<el-link class="ml5" @click.prevent="showTableIndex(scope.row)" type="success">索引</el-link>
<el-link class="ml5"
v-if="tableCreateDialog.enableEditTypes.indexOf(tableCreateDialog.type) > -1"
<el-link class="ml5" v-if="tableCreateDialog.enableEditTypes.indexOf(tableCreateDialog.type) > -1"
@click.prevent="openEditTable(scope.row)" type="warning">编辑表</el-link>
<el-link class="ml5" @click.prevent="showCreateDdl(scope.row)" type="info">DDL</el-link>
</template>
@@ -218,8 +198,8 @@
</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">
@@ -299,6 +279,8 @@ import { Search as SearchIcon } from '@element-plus/icons-vue'
import { tagApi } from '../tag/api';
import { dateFormat } from '@/common/utils/date';
import TagInfo from '../component/TagInfo.vue';
import PageTable from '@/components/pagetable/PageTable.vue'
import { TableColumn, TableQuery } from '@/components/pagetable';
const DbEdit = defineAsyncComponent(() => import('./DbEdit.vue'));
const CreateTable = defineAsyncComponent(() => import('./CreateTable.vue'));
@@ -313,11 +295,10 @@ const state = reactive({
dbId: 0,
db: '',
tags: [],
chooseId: null as any,
/**
* 选中的数据
*/
chooseData: null,
chooseData: null as any,
/**
* 查询条件
*/
@@ -326,6 +307,19 @@ const state = reactive({
pageNum: 1,
pageSize: 10,
},
queryConfig: [
TableQuery.slot("tagPath", "标签", "tagPathSelect"),
],
columns: [
TableColumn.new("tagPath", "标签路径").setSlot("tagPath"),
TableColumn.new("name", "名称"),
TableColumn.new("host", "host:port").setSlot("hostPort"),
TableColumn.new("type", "类型"),
TableColumn.new("database", "数据库").setSlot("database").setMinWidth(60),
TableColumn.new("username", "用户名"),
TableColumn.new("remark", "备注"),
TableColumn.new("action", "操作").setSlot("action").setMinWidth(155).fixedRight(),
],
datas: [],
total: 0,
infoDialog: {
@@ -408,7 +402,7 @@ const {
dbId,
db,
tags,
chooseId,
chooseData,
query,
datas,
total,
@@ -452,14 +446,6 @@ const filterTableInfos = computed(() => {
});
});
const choose = (item: any) => {
if (!item) {
return;
}
state.chooseId = item.id;
state.chooseData = item;
};
const search = async () => {
let res: any = await dbApi.dbs.request(state.query);
// 切割数据库
@@ -471,11 +457,6 @@ const search = async () => {
state.total = res.total;
};
const handlePageChange = (curPage: number) => {
state.query.pageNum = curPage;
search();
};
const showInfo = (info: any) => {
state.infoDialog.data = info;
state.infoDialog.visible = true;
@@ -498,7 +479,6 @@ const editDb = async (isAdd = false) => {
const valChange = () => {
state.chooseData = null;
state.chooseId = null;
search();
};
@@ -512,7 +492,6 @@ const deleteDb = async (id: number) => {
await dbApi.deleteDb.request({ id });
ElMessage.success('删除成功');
state.chooseData = null;
state.chooseId = null;
search();
} catch (err) { }
};
@@ -651,7 +630,7 @@ const closeTableInfo = () => {
const showColumns = async (row: any) => {
state.chooseTableName = row.tableName;
state.columnDialog.columns = await dbApi.columnMetadata.request({
id: state.chooseId,
id: state.chooseData.id,
db: state.db,
tableName: row.tableName,
});
@@ -662,7 +641,7 @@ const showColumns = async (row: any) => {
const showTableIndex = async (row: any) => {
state.chooseTableName = row.tableName;
state.indexDialog.indexs = await dbApi.tableIndex.request({
id: state.chooseId,
id: state.chooseData.id,
db: state.db,
tableName: row.tableName,
});
@@ -673,7 +652,7 @@ const showTableIndex = async (row: any) => {
const showCreateDdl = async (row: any) => {
state.chooseTableName = row.tableName;
const res = await dbApi.tableDdl.request({
id: state.chooseId,
id: state.chooseData.id,
db: state.db,
tableName: row.tableName,
});
@@ -694,10 +673,10 @@ const dropTable = async (row: any) => {
});
SqlExecBox({
sql: `DROP TABLE ${tableName}`,
dbId: state.chooseId,
dbId: state.chooseData.id,
db: state.db,
runSuccessCallback: async () => {
state.tableInfoDialog.infos = await dbApi.tableInfos.request({ id: state.chooseId, db: state.db });
state.tableInfoDialog.infos = await dbApi.tableInfos.request({ id: state.chooseData.id, db: state.db });
},
});
} catch (err) { }
@@ -733,12 +712,12 @@ const openEditTable = async (row: any) => {
if (row.tableName) {
state.tableCreateDialog.title = '修改表'
let indexs = await dbApi.tableIndex.request({
id: state.chooseId,
id: state.chooseData.id,
db: state.db,
tableName: row.tableName,
});
let columns = await dbApi.columnMetadata.request({
id: state.chooseId,
id: state.chooseData.id,
db: state.db,
tableName: row.tableName,
});
@@ -746,6 +725,4 @@ const openEditTable = async (row: any) => {
}
}
</script>
<style lang="scss">
</style>
<style lang="scss"></style>

View File

@@ -1,127 +1,96 @@
<template>
<div>
<el-card>
<div>
<page-table :query="state.queryConfig" v-model:query-form="params" :show-choose-column="true"
v-model:choose-data="state.chooseData" :data="data.list" :columns="state.columns" :total="data.total"
v-model:page-size="params.pageSize" v-model:page-num="params.pageNum" @pageChange="search()">
<template #tagPathSelect>
<el-select @focus="getTags" v-model="params.tagPath" placeholder="请选择标签" @clear="search" filterable
clearable style="width: 200px">
<el-option v-for="item in tags" :key="item" :label="item" :value="item"> </el-option>
</el-select>
</template>
<template #queryRight>
<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-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-button class="ml5" @click="search" type="success" icon="search"></el-button>
</div>
</div>
<el-button v-auth="'machine:update'" type="primary" icon="edit" :disabled="!chooseData"
@click="openFormDialog(chooseData)" plain>编辑</el-button>
<el-button v-auth="'machine:del'" :disabled="!chooseData" @click="deleteMachine(chooseData.id)"
type="danger" icon="delete">删除</el-button>
</template>
<el-table :data="data.list" stripe style="width: 100%" @current-change="choose">
<el-table-column label="选择" width="55px">
<template #default="scope">
<el-radio v-model="currentId" :label="scope.row.id">
<i></i>
</el-radio>
<template #tagPath="{ data }">
<tag-info :tag-path="data.tagPath" />
<span class="ml5">
{{ data.tagPath }}
</span>
</template>
<template #ipPort="{ data }">
<el-link :disabled="data.status == -1" @click="showMachineStats(data)" type="primary" :underline="false">
{{ `${data.ip}:${data.port}` }}
</el-link>
</template>
<template #status="{ data }">
<el-switch v-auth:disabled="'machine:update'" :width="52" v-model="data.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(data)"></el-switch>
</template>
<template #action="{ data }">
<span v-auth="'machine:terminal'">
<el-link :disabled="data.status == -1" type="primary" @click="showTerminal(data)" 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="data.status == -1" @click="showFileManage(data)" plain size="small"
:underline="false">文件</el-link>
<el-divider direction="vertical" border-style="dashed" />
</span>
<el-link :disabled="data.status == -1" type="warning" @click="serviceManager(data)" plain size="small"
:underline="false">脚本</el-link>
<el-divider direction="vertical" border-style="dashed" />
<el-dropdown>
<span class="el-dropdown-link-machine-list">
更多
<el-icon class="el-icon--right">
<arrow-down />
</el-icon>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item>
<el-link @click="showInfo(data)" plain :underline="false" size="small">详情
</el-link>
</el-dropdown-item>
<el-dropdown-item>
<el-link @click="showProcess(data)" :disabled="data.status == -1" plain :underline="false"
size="small">进程</el-link>
</el-dropdown-item>
<el-dropdown-item v-if="data.enableRecorder == 1">
<el-link v-auth="'machine:update'" @click="showRec(data)" plain :underline="false"
size="small">终端回放</el-link>
</el-dropdown-item>
<el-dropdown-item>
<el-link v-auth="'machine:close-cli'" :disabled="!data.hasCli || data.status == -1"
type="danger" @click="closeCli(data)" plain size="small" :underline="false">关闭连接
</el-link>
</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-table-column>
<el-table-column prop="tagPath" label="标签路径" min-width="150" show-overflow-tooltip>
<template #default="scope">
<tag-info :tag-path="scope.row.tagPath" />
<span class="ml5">
{{ scope.row.tagPath }}
</span>
</template>
</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">
{{ `${scope.row.ip}:${scope.row.port}` }}
</el-link>
</template>
</el-table-column>
<el-table-column prop="username" label="用户名" min-width="100">
</el-table-column>
<el-table-column prop="status" label="状态" min-width="80">
<template #default="scope">
<el-switch v-auth:disabled="'machine:update'" :width="52" 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>
</template>
</el-table-column>
<el-table-column prop="remark" label="备注" min-width="250" show-overflow-tooltip></el-table-column>
<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-divider direction="vertical" border-style="dashed" />
</span>
<span v-auth="'machine:file'">
<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-divider direction="vertical" border-style="dashed" />
<el-dropdown>
<span class="el-dropdown-link-machine-list">
更多
<el-icon class="el-icon--right">
<arrow-down />
</el-icon>
</span>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item>
<el-link @click="showInfo(scope.row)" 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>
<el-link v-auth="'machine:close-cli'"
: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>
</template>
</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-row>
</el-card>
</el-dropdown>
</template>
</page-table>
<el-dialog v-model="infoDialog.visible">
<el-descriptions title="详情" :column="3" border>
@@ -184,6 +153,8 @@ import { machineApi } from './api';
import { tagApi } from '../tag/api';
import { dateFormat } from '@/common/utils/date';
import TagInfo from '../component/TagInfo.vue';
import PageTable from '@/components/pagetable/PageTable.vue'
import { TableColumn, TableQuery } from '@/components/pagetable';
// 组件
const MachineEdit = defineAsyncComponent(() => import('./MachineEdit.vue'));
@@ -203,6 +174,20 @@ const state = reactive({
name: null,
tagPath: null,
},
queryConfig: [
TableQuery.slot("tagPath", "标签", "tagPathSelect"),
TableQuery.text("ip", "IP"),
TableQuery.text("name", "名称"),
],
columns: [
TableColumn.new("tagPath", "标签路径").setSlot("tagPath"),
TableColumn.new("name", "名称"),
TableColumn.new("ipPort", "ip:port").setSlot("ipPort"),
TableColumn.new("username", "用户名"),
TableColumn.new("status", "状态").setSlot("status"),
TableColumn.new("remark", "备注"),
TableColumn.new("action", "操作").setSlot("action").setMinWidth(235).fixedRight(),
],
// 列表数据
data: {
list: [],
@@ -212,9 +197,8 @@ const state = reactive({
visible: false,
data: null as any,
},
// 当前选中数据id
currentId: 0,
currentData: null,
// 当前选中数据
chooseData: null as any,
serviceDialog: {
visible: false,
machineId: 0,
@@ -252,8 +236,7 @@ const {
params,
data,
infoDialog,
currentId,
currentData,
chooseData,
serviceDialog,
processDialog,
fileDialog,
@@ -266,14 +249,6 @@ onMounted(async () => {
search();
});
const choose = (item: any) => {
if (!item) {
return;
}
state.currentId = item.id;
state.currentData = item;
};
const showTerminal = (row: any) => {
const { href } = router.resolve({
path: `/machine/terminal`,
@@ -303,7 +278,7 @@ const getTags = async () => {
const openFormDialog = async (machine: any) => {
let dialogTitle;
if (machine) {
state.machineEditDialog.data = state.currentData as any;
state.machineEditDialog.data = state.chooseData as any;
dialogTitle = '编辑机器';
} else {
state.machineEditDialog.data = null;
@@ -323,8 +298,7 @@ const deleteMachine = async (id: number) => {
});
await machineApi.del.request({ id });
ElMessage.success('操作成功');
state.currentId = 0;
state.currentData = null;
state.chooseData = null;
search();
} catch (err) { }
};
@@ -339,6 +313,9 @@ const serviceManager = (row: any) => {
* 调整机器状态
*/
const changeStatus = async (row: any) => {
if (!row.id) {
return;
}
await machineApi.changeStatus.request({ id: row.id, status: row.status });
};
@@ -352,15 +329,14 @@ const showMachineStats = async (machine: any) => {
};
const submitSuccess = () => {
state.currentId = 0;
state.currentData = null;
state.chooseData = null;
search();
};
const showFileManage = (currentData: any) => {
const showFileManage = (chooseData: any) => {
state.fileDialog.visible = true;
state.fileDialog.machineId = currentData.id;
state.fileDialog.title = `${currentData.name} => ${currentData.ip}`;
state.fileDialog.machineId = chooseData.id;
state.fileDialog.title = `${chooseData.name} => ${chooseData.ip}`;
};
const search = async () => {
@@ -368,11 +344,6 @@ const search = async () => {
state.data = res;
};
const handlePageChange = (curPage: number) => {
state.params.pageNum = curPage;
search();
};
const showInfo = (info: any) => {
state.infoDialog.data = info;
state.infoDialog.visible = true;

View File

@@ -1,56 +1,23 @@
<template>
<div class="role-list">
<el-card>
<div>
<div>
<page-table :query="state.queryConfig" v-model:query-form="query" :show-choose-column="true"
v-model:choose-data="state.chooseData" :data="authcerts" :columns="state.columns" :total="total"
v-model:page-size="query.pageSize" v-model:page-num="query.pageNum" @pageChange="search()">
<template #queryRight>
<el-button type="primary" icon="plus" @click="edit(false)">添加</el-button>
<el-button :disabled="chooseId == null" @click="edit(chooseData)" type="primary" icon="edit">编辑
<el-button :disabled="!chooseData" @click="edit(chooseData)" type="primary" icon="edit">编辑
</el-button>
<el-button :disabled="chooseId == null" @click="deleteAc(chooseData)" type="danger" icon="delete">删除
<el-button :disabled="!chooseData" @click="deleteAc(chooseData)" type="danger" icon="delete">删除
</el-button>
<div style="float: right">
<el-input class="ml5" placeholder="请输入凭证名称" style="width: 200px" v-model="query.name" @clear="search"
plain clearable></el-input>
<el-button class="ml5" @click="search" type="success" icon="search"></el-button>
</div>
</div>
</template>
<el-table :data="authcerts" @current-change="choose" ref="table" style="width: 100%">
<el-table-column label="选择" width="55px">
<template #default="scope">
<el-radio v-model="chooseId" :label="scope.row.id">
<i></i>
</el-radio>
</template>
</el-table-column>
<el-table-column prop="name" label="名称" min-width="60px" show-overflow-tooltip></el-table-column>
<el-table-column prop="authMethod" label="认证方式" min-width="50px">
<template #default="scope">
<el-tag v-if="scope.row.authMethod == 1" type="success" size="small">密码</el-tag>
<el-tag v-if="scope.row.authMethod == 2" size="small">密钥</el-tag>
</template>
</el-table-column>
<el-table-column prop="remark" label="备注" min-width="100px" show-overflow-tooltip>
</el-table-column>
<el-table-column prop="creator" label="创建人" min-width="60px"></el-table-column>
<el-table-column prop="createTime" label="创建时间" min-width="100px">
<template #default="scope">
{{ dateFormat(scope.row.createTime) }}
</template>
</el-table-column>
<el-table-column prop="modifier" label="修改者" min-width="60px" show-overflow-tooltip></el-table-column>
<el-table-column prop="updateTime" label="更新时间" min-width="100px">
<template #default="scope">
{{ dateFormat(scope.row.updateTime) }}
</template>
</el-table-column>
</el-table>
<el-row style="margin-top: 20px" type="flex" justify="end">
<el-pagination style="text-align: right" @current-change="handlePageChange" :total="total"
layout="prev, pager, next, total, jumper" v-model:current-page="query.pageNum"
:page-size="query.pageSize"></el-pagination>
</el-row>
</el-card>
<template #authMethod="{ data }">
<el-tag v-if="data.authMethod == 1" type="success" size="small">密码</el-tag>
<el-tag v-if="data.authMethod == 2" size="small">密钥</el-tag>
</template>
</page-table>
<auth-cert-edit :title="editor.title" v-model:visible="editor.visible" :data="editor.authcert"
@val-change="editChange" />
@@ -62,19 +29,30 @@ import { toRefs, reactive, onMounted } from 'vue';
import AuthCertEdit from './AuthCertEdit.vue';
import { authCertApi } from '../api';
import { ElMessage, ElMessageBox } from 'element-plus';
import { dateFormat } from '@/common/utils/date';
import PageTable from '@/components/pagetable/PageTable.vue'
import { TableColumn, TableQuery } from '@/components/pagetable';
const state = reactive({
query: {
pageNum: 1,
pageSize: 10,
name: null,
type: null,
},
queryConfig: [
TableQuery.text("name", "凭证名称"),
],
columns: [
TableColumn.new("name", "名称"),
TableColumn.new("authMethod", "认证方式").setSlot("authMethod"),
TableColumn.new("remark", "备注"),
TableColumn.new("creator", "创建人"),
TableColumn.new("createTime", "创建时间").isTime(),
TableColumn.new("creator", "修改者"),
TableColumn.new("createTime", "修改时间").isTime(),
],
total: 0,
authcerts: [],
chooseId: null,
chooseData: null,
chooseData: null as any,
paramsDialog: {
visible: false,
config: null as any,
@@ -92,7 +70,6 @@ const {
query,
total,
authcerts,
chooseId,
chooseData,
editor,
} = toRefs(state)
@@ -107,22 +84,8 @@ const search = async () => {
state.total = res.total;
};
const handlePageChange = (curPage: number) => {
state.query.pageNum = curPage;
search();
};
const choose = (item: any) => {
if (!item) {
return;
}
state.chooseId = item.id;
state.chooseData = item;
};
const editChange = () => {
ElMessage.success('保存成功');
state.chooseId = null;
state.chooseData = null;
search();
};
@@ -147,7 +110,6 @@ const deleteAc = async (data: any) => {
await authCertApi.delete.request({ id: data.id });
ElMessage.success('删除成功');
state.chooseData = null;
state.chooseId = null;
search();
} catch (err) { }

View File

@@ -1,59 +1,35 @@
<template>
<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>
<div style="float: right">
<el-select @focus="getTags" v-model="query.tagPath" placeholder="请选择标签" filterable clearable>
<page-table :query="state.queryConfig" v-model:query-form="query" :show-choose-column="true"
v-model:choose-data="state.chooseData" :data="list" :columns="state.columns" :total="total"
v-model:page-size="query.pageSize" v-model:page-num="query.pageNum" @pageChange="search()">
<template #tagPathSelect>
<el-select @focus="getTags" v-model="query.tagPath" placeholder="请选择标签" @clear="search" filterable clearable
style="width: 200px">
<el-option v-for="item in tags" :key="item" :label="item" :value="item"> </el-option>
</el-select>
<el-button class="ml5" @click="search" type="success" icon="search"></el-button>
</div>
<el-table :data="list" style="width: 100%" @current-change="choose" stripe>
<el-table-column label="选择" width="60px">
<template #default="scope">
<el-radio v-model="currentId" :label="scope.row.id">
<i></i>
</el-radio>
</template>
</el-table-column>
<el-table-column prop="tagPath" label="标签路径" min-width="150" show-overflow-tooltip>
<template #default="scope">
<tag-info :tag-path="scope.row.tagPath" />
<span class="ml5">
{{ scope.row.tagPath }}
</span>
</template>
</el-table-column>
<el-table-column prop="name" label="名称" width></el-table-column>
<el-table-column prop="uri" label="连接uri" min-width="150" show-overflow-tooltip>
<template #default="scope">
{{ scope.row.uri.split('@')[1] }}
</template>
</el-table-column>
<el-table-column prop="createTime" label="创建时间" min-width="150">
<template #default="scope">
{{ dateFormat(scope.row.createTime) }}
</template>
</el-table-column>
<el-table-column prop="creator" label="创建人"></el-table-column>
</template>
<el-table-column label="操作" width>
<template #default="scope">
<el-link type="primary" @click="showDatabases(scope.row.id)" 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-row>
</el-card>
<template #queryRight>
<el-button type="primary" icon="plus" @click="editMongo(true)" plain>添加</el-button>
<el-button type="primary" icon="edit" :disabled="!chooseData" @click="editMongo(false)" plain>编辑
</el-button>
<el-button type="danger" icon="delete" :disabled="!chooseData" @click="deleteMongo" plain>删除
</el-button>
</template>
<template #tagPath="{ data }">
<tag-info :tag-path="data.tagPath" />
<span class="ml5">
{{ data.tagPath }}
</span>
</template>
<template #action="{ data }">
<el-link type="primary" @click="showDatabases(data.id)" plain size="small" :underline="false">数据库</el-link>
</template>
</page-table>
<el-dialog width="800px" :title="databaseDialog.title" v-model="databaseDialog.visible">
<el-table :data="databaseDialog.data" size="small">
@@ -76,8 +52,7 @@
</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 }}
@@ -199,8 +174,9 @@ import { ElMessage, ElMessageBox } from 'element-plus';
import { tagApi } from '../tag/api';
import MongoEdit from './MongoEdit.vue';
import { formatByteSize } from '@/common/utils/format';
import { dateFormat } from '@/common/utils/date';
import TagInfo from '../component/TagInfo.vue';
import PageTable from '@/components/pagetable/PageTable.vue'
import { TableColumn, TableQuery } from '@/components/pagetable';
const state = reactive({
tags: [],
@@ -210,13 +186,23 @@ const state = reactive({
},
list: [],
total: 0,
currentId: null,
currentData: null as any,
chooseData: null as any,
query: {
pageNum: 1,
pageSize: 10,
tagPath: null,
},
queryConfig: [
TableQuery.slot("tagPath", "标签", "tagPathSelect"),
],
columns: [
TableColumn.new("tagPath", "标签路径").setSlot("tagPath"),
TableColumn.new("name", "名称"),
TableColumn.new("uri", "连接uri"),
TableColumn.new("createTime", "创建时间").isTime(),
TableColumn.new("creator", "创建人"),
TableColumn.new("action", "操作").setSlot("action").setMinWidth(100).fixedRight(),
],
mongoEditDialog: {
visible: false,
data: null as any,
@@ -255,7 +241,7 @@ const {
tags,
list,
total,
currentId,
chooseData,
query,
mongoEditDialog,
databaseDialog,
@@ -267,19 +253,6 @@ onMounted(async () => {
search();
});
const handlePageChange = (curPage: number) => {
state.query.pageNum = curPage;
search();
};
const choose = (item: any) => {
if (!item) {
return;
}
state.currentId = item.id;
state.currentData = item;
};
const showDatabases = async (id: number) => {
// state.query.tagPath = row.tagPath
state.dbOps.dbId = id
@@ -291,7 +264,7 @@ const showDatabases = async (id: number) => {
const showDatabaseStats = async (dbName: string) => {
state.databaseDialog.statsDialog.data = await mongoApi.runCommand.request({
id: state.currentId,
id: state.chooseData.id,
database: dbName,
command: {
dbStats: 1,
@@ -310,7 +283,7 @@ const showCollections = async (database: string) => {
};
const setCollections = async (database: string) => {
const res = await mongoApi.collections.request({ id: state.currentId, database });
const res = await mongoApi.collections.request({ id: state.chooseData.id, database });
const collections = [] as any;
for (let r of res) {
collections.push({ name: r });
@@ -323,7 +296,7 @@ const setCollections = async (database: string) => {
*/
const showCollectionStats = async (collection: string) => {
state.collectionsDialog.statsDialog.data = await mongoApi.runCommand.request({
id: state.currentId,
id: state.chooseData.id,
database: state.collectionsDialog.database,
command: {
collStats: collection,
@@ -338,7 +311,7 @@ const showCollectionStats = async (collection: string) => {
*/
const onDeleteCollection = async (collection: string) => {
await mongoApi.runCommand.request({
id: state.currentId,
id: state.chooseData.id,
database: state.collectionsDialog.database,
command: {
drop: collection,
@@ -355,7 +328,7 @@ const showCreateCollectionDialog = () => {
const onCreateCollection = async () => {
const form = state.createCollectionDialog.form;
await mongoApi.runCommand.request({
id: state.currentId,
id: state.chooseData.id,
database: state.collectionsDialog.database,
command: {
create: form.name,
@@ -374,10 +347,9 @@ const deleteMongo = async () => {
cancelButtonText: '取消',
type: 'warning',
});
await mongoApi.deleteMongo.request({ id: state.currentId });
await mongoApi.deleteMongo.request({ id: state.chooseData.id });
ElMessage.success('删除成功');
state.currentData = null;
state.currentId = null;
state.chooseData = null;
search();
} catch (err) { }
};
@@ -397,20 +369,17 @@ const editMongo = async (isAdd = false) => {
state.mongoEditDialog.data = null;
state.mongoEditDialog.title = '新增mongo';
} else {
state.mongoEditDialog.data = state.currentData;
state.mongoEditDialog.data = state.chooseData;
state.mongoEditDialog.title = '修改mongo';
}
state.mongoEditDialog.visible = true;
};
const valChange = () => {
state.currentId = null;
state.currentData = null;
state.chooseData = null;
search();
};
</script>
<style>
</style>
<style></style>

View File

@@ -1,7 +1,7 @@
<template>
<div>
<el-button @click="showEditDialog(null)" icon="plus" size="small" plain type="primary" class="mb10">添加新行</el-button>
<el-table size="small" border :data="hashValues" min-height=300 stripe>
<el-table size="small" border :data="hashValues" height="450" min-height=300 stripe>
<el-table-column type="index" :label="'ID (Total: ' + total + ')'" sortable width="100">
</el-table-column>
<el-table-column resizable sortable prop="field" label="field" show-overflow-tooltip min-width="100">

View File

@@ -1,7 +1,7 @@
<template>
<div>
<el-button @click="showEditDialog(null)" icon="plus" size="small" plain type="primary" class="mb10">添加新行</el-button>
<el-table size="small" border :data="values" min-height=300 stripe>
<el-table size="small" border :data="values" height="450" min-height=300 stripe>
<el-table-column type="index" :label="'ID (Total: ' + total + ')'" sortable width="100">
</el-table-column>
<el-table-column resizable sortable prop="value" label="value" show-overflow-tooltip min-width="200">

View File

@@ -1,7 +1,7 @@
<template>
<div>
<el-button @click="showEditDialog(null)" icon="plus" size="small" plain type="primary" class="mb10">添加新行</el-button>
<el-table size="small" border :data="setDatas" min-height=300 stripe>
<el-table size="small" border :data="setDatas" height="450" min-height=300 stripe>
<el-table-column type="index" :label="'ID (Total: ' + total + ')'" sortable width="100">
</el-table-column>
<el-table-column resizable sortable prop="value" label="value" show-overflow-tooltip min-width="200">

View File

@@ -1,7 +1,7 @@
<template>
<div>
<el-button @click="showEditDialog(null)" icon="plus" size="small" plain type="primary" class="mb10">添加新行</el-button>
<el-table size="small" border :data="values" min-height=300 stripe>
<el-table size="small" border :data="values" height="450" min-height=300 stripe>
<el-table-column type="index" :label="'ID (Total: ' + total + ')'" sortable width="100">
</el-table-column>
<el-table-column resizable sortable prop="score" label="score" show-overflow-tooltip min-width="100">

View File

@@ -1,56 +1,41 @@
<template>
<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>
<div style="float: right">
<el-select @focus="getTags" v-model="query.tagPath" placeholder="请选择标签" filterable clearable>
<page-table :query="state.queryConfig" v-model:query-form="query" :show-choose-column="true"
v-model:choose-data="state.chooseData" :data="redisTable" :columns="state.columns" :total="total"
v-model:page-size="query.pageSize" v-model:page-num="query.pageNum" @pageChange="search()">
<template #tagPathSelect>
<el-select @focus="getTags" v-model="query.tagPath" placeholder="请选择标签" @clear="search" filterable clearable
style="width: 200px">
<el-option v-for="item in tags" :key="item" :label="item" :value="item"> </el-option>
</el-select>
<el-button class="ml5" @click="search" type="success" icon="search"></el-button>
</div>
<el-table :data="redisTable" @current-change="choose" stripe>
<el-table-column label="选择" width="60px">
<template #default="scope">
<el-radio v-model="currentId" :label="scope.row.id">
<i></i>
</el-radio>
</template>
</el-table-column>
<el-table-column prop="tagPath" label="标签路径" min-width="150" show-overflow-tooltip>
<template #default="scope">
<tag-info :tag-path="scope.row.tagPath" />
<span class="ml5">
{{ scope.row.tagPath }}
</span>
</template>
</el-table-column>
<el-table-column prop="name" label="名称" min-width="100"></el-table-column>
<el-table-column prop="host" label="host:port" min-width="150" show-overflow-tooltip> </el-table-column>
<el-table-column prop="mode" label="mode" min-width="100"></el-table-column>
<el-table-column prop="remark" label="备注" min-width="120" show-overflow-tooltip></el-table-column>
</template>
<el-table-column label="更多" min-width="155" fixed="right">
<template #default="scope">
<el-link @click="showDetail(scope.row)" :underline="false">详情</el-link>
<el-divider direction="vertical" border-style="dashed" />
<template #queryRight>
<el-button type="primary" icon="plus" @click="editRedis(true)" plain>添加</el-button>
<el-button type="primary" icon="edit" :disabled="!chooseData" @click="editRedis(false)" plain>编辑
</el-button>
<el-button type="danger" icon="delete" :disabled="!chooseData" @click="deleteRedis" plain>删除
</el-button>
</template>
<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="primary" :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-row>
</el-card>
<template #tagPath="{ data }">
<tag-info :tag-path="data.tagPath" />
<span class="ml5">
{{ data.tagPath }}
</span>
</template>
<template #more="{ data }">
<el-link @click="showDetail(data)" :underline="false">详情</el-link>
<el-divider direction="vertical" border-style="dashed" />
<el-link v-if="data.mode === 'standalone' || data.mode === 'sentinel'" type="primary"
@click="showInfoDialog(data)" :underline="false">单机信息</el-link>
<el-link @click="onShowClusterInfo(data)" v-if="data.mode === 'cluster'" type="primary"
:underline="false">集群信息</el-link>
</template>
</page-table>
<info v-model:visible="infoDialog.visible" :title="infoDialog.title" :info="infoDialog.info"></info>
@@ -81,8 +66,8 @@
</el-tooltip>
</template>
<template #default="scope">
<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 @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>
@@ -90,8 +75,8 @@
<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-tooltip class="box-item" effect="dark" content="如果节点是slave并且已知master节点则为master节点ID否则为符号'-'"
placement="top">
<el-icon>
<question-filled />
</el-icon>
@@ -112,8 +97,7 @@
<template #header>
configEpoch
<el-tooltip class="box-item" effect="dark"
content="节点的epoch值如果该节点是从节点则为其主节点的epoch值。每当节点发生失败切换时都会创建一个新的独特的递增的epoch。"
placement="top">
content="节点的epoch值如果该节点是从节点则为其主节点的epoch值。每当节点发生失败切换时都会创建一个新的独特的递增的epoch。" placement="top">
<el-icon>
<question-filled />
</el-icon>
@@ -164,19 +148,30 @@ import { tagApi } from '../tag/api';
import RedisEdit from './RedisEdit.vue';
import { dateFormat } from '@/common/utils/date';
import TagInfo from '../component/TagInfo.vue';
import PageTable from '@/components/pagetable/PageTable.vue'
import { TableColumn, TableQuery } from '@/components/pagetable';
const state = reactive({
tags: [],
redisTable: [],
total: 0,
currentId: null,
currentData: null,
chooseData: null as any,
query: {
tagPath: null,
pageNum: 1,
pageSize: 10,
clusterId: null,
},
queryConfig: [
TableQuery.slot("tagPath", "标签", "tagPathSelect"),
],
columns: [
TableColumn.new("tagPath", "标签路径").setSlot("tagPath"),
TableColumn.new("name", "名称"),
TableColumn.new("host", "host:port"),
TableColumn.new("mode", "mode"),
TableColumn.new("remark", "备注"),
TableColumn.new("more", "更多").setSlot("more").setMinWidth(155).fixedRight(),
],
detailDialog: {
visible: false,
data: null as any,
@@ -209,7 +204,7 @@ const {
tags,
redisTable,
total,
currentId,
chooseData,
query,
detailDialog,
clusterInfoDialog,
@@ -221,24 +216,12 @@ onMounted(async () => {
search();
});
const handlePageChange = (curPage: number) => {
state.query.pageNum = curPage;
search();
};
const showDetail = (detail: any) => {
state.detailDialog.data = detail;
state.detailDialog.visible = true;
}
const choose = (item: any) => {
if (!item) {
return;
}
state.currentId = item.id;
state.currentData = item;
};
const deleteRedis = async () => {
try {
await ElMessageBox.confirm(`确定删除该redis?`, '提示', {
@@ -246,10 +229,9 @@ const deleteRedis = async () => {
cancelButtonText: '取消',
type: 'warning',
});
await redisApi.delRedis.request({ id: state.currentId });
await redisApi.delRedis.request({ id: state.chooseData.id });
ElMessage.success('删除成功');
state.currentData = null;
state.currentId = null;
state.chooseData = null;
search();
} catch (err) { }
};
@@ -288,19 +270,16 @@ const editRedis = async (isAdd = false) => {
state.redisEditDialog.data = null;
state.redisEditDialog.title = '新增redis';
} else {
state.redisEditDialog.data = state.currentData;
state.redisEditDialog.data = state.chooseData;
state.redisEditDialog.title = '修改redis';
}
state.redisEditDialog.visible = true;
};
const valChange = () => {
state.currentId = null;
state.currentData = null;
state.chooseData = null;
search();
};
</script>
<style>
</style>
<style></style>

View File

@@ -1,47 +1,32 @@
<template>
<div class="role-list">
<el-card>
<el-button v-auth="'team:save'" type="primary" icon="plus" @click="showSaveTeamDialog(false)">添加</el-button>
<el-button v-auth="'team:del'" :disabled="!chooseId" @click="deleteTeam(chooseData)" type="danger"
icon="delete">删除</el-button>
<div>
<page-table :query="state.queryConfig" v-model:query-form="query" :show-choose-column="true"
v-model:choose-data="state.chooseData" :data="data" :columns="state.columns" :total="total"
v-model:page-size="query.pageSize" v-model:page-num="query.pageNum" @pageChange="search()">
<div style="float: right">
<el-input placeholder="请输入团队名称" class="mr2" style="width: 200px" v-model="query.name" @clear="search"
clearable></el-input>
<el-button @click="search" type="success" icon="search"></el-button>
</div>
<el-table :data="data" @current-change="choose" ref="table" style="width: 100%">
<el-table-column label="选择" width="55px">
<template #default="scope">
<el-radio v-model="chooseId" :label="scope.row.id">
<i></i>
</el-radio>
</template>
</el-table-column>
<el-table-column prop="name" label="团队名称"></el-table-column>
<el-table-column prop="remark" label="备注" min-width="160px" show-overflow-tooltip></el-table-column>
<el-table-column prop="createTime" label="创建时间">
<template #default="scope">
{{ dateFormat(scope.row.createTime) }}
</template>
</el-table-column>
<el-table-column prop="creator" label="创建者"> </el-table-column>
<el-table-column label="操作" min-width="80px">
<template #default="scope">
<el-link @click.prevent="showMembers(scope.row)" :underline="false" type="primary">成员</el-link>
<el-divider direction="vertical" border-style="dashed" />
<el-link @click.prevent="showTags(scope.row)" :underline="false" type="success">标签</el-link>
<el-divider v-auth="'team:save'" direction="vertical" border-style="dashed" />
<el-link v-auth="'team:save'" @click.prevent="showSaveTeamDialog(scope.row)" :underline="false" type="warning">编辑</el-link>
</template>
</el-table-column>
</el-table>
<el-row style="margin-top: 20px" type="flex" justify="end">
<el-pagination style="text-align: right" @current-change="handlePageChange" :total="total"
layout="prev, pager, next, total, jumper" v-model:current-page="query.pageNum"
:page-size="query.pageSize"></el-pagination>
</el-row>
</el-card>
<template #queryRight>
<el-button v-auth="'team:save'" type="primary" icon="plus" @click="showSaveTeamDialog(false)">添加</el-button>
<el-button v-auth="'team:del'" :disabled="!chooseData" @click="deleteTeam(chooseData)" type="danger"
icon="delete">删除</el-button>
</template>
<template #tagPath="{ data }">
<tag-info :tag-path="data.tagPath" />
<span class="ml5">
{{ data.tagPath }}
</span>
</template>
<template #action="{ data }">
<el-link @click.prevent="showMembers(data)" :underline="false" type="primary">成员</el-link>
<el-divider direction="vertical" border-style="dashed" />
<el-link @click.prevent="showTags(data)" :underline="false" type="success">标签</el-link>
<el-divider v-auth="'team:save'" direction="vertical" border-style="dashed" />
<el-link v-auth="'team:save'" @click.prevent="showSaveTeamDialog(data)" :underline="false"
type="warning">编辑</el-link>
</template>
</page-table>
<el-dialog width="400px" title="团队编辑" :before-close="cancelSaveTeam" v-model="addTeamDialog.visible">
<el-form ref="teamForm" :model="addTeamDialog.form" label-width="70px">
@@ -66,8 +51,8 @@
<el-form-item prop="tag" 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">
: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">
@@ -98,8 +83,8 @@
<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-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>
</div>
@@ -153,6 +138,8 @@ import { accountApi } from '../../system/api';
import { ElMessage, ElMessageBox } from 'element-plus';
import { dateFormat } from '@/common/utils/date';
import { notBlank } from '@/common/assert';
import PageTable from '@/components/pagetable/PageTable.vue'
import { TableColumn, TableQuery } from '@/components/pagetable';
const teamForm: any = ref(null);
const tagTreeRef: any = ref(null);
@@ -168,10 +155,19 @@ const state = reactive({
pageSize: 10,
name: null,
},
queryConfig: [
TableQuery.text("name", "团队名称"),
],
columns: [
TableColumn.new("name", "团队名称"),
TableColumn.new("remark", "备注"),
TableColumn.new("createTime", "创建时间").isTime(),
TableColumn.new("creator", "创建人"),
TableColumn.new("action", "操作").setSlot("action").setMinWidth(100).fixedRight(),
],
total: 0,
data: [],
chooseId: 0,
chooseData: null,
chooseData: null as any,
showMemDialog: {
visible: false,
chooseId: 0,
@@ -213,7 +209,6 @@ const {
addTeamDialog,
total,
data,
chooseId,
chooseData,
showMemDialog,
showTagDialog,
@@ -229,19 +224,6 @@ const search = async () => {
state.total = res.total;
};
const handlePageChange = (curPage: number) => {
state.query.pageNum = curPage;
search();
};
const choose = (item: any) => {
if (!item) {
return;
}
state.chooseId = item.id;
state.chooseData = item;
};
const showSaveTeamDialog = (data: any) => {
if (data) {
state.addTeamDialog.form.id = data.id;
@@ -278,6 +260,7 @@ const deleteTeam = (data: any) => {
}).then(async () => {
await tagApi.delTeam.request({ id: data.id });
ElMessage.success('删除成功!');
state.chooseData = null;
search();
});
};
@@ -332,7 +315,7 @@ const showAddMemberDialog = () => {
const addMember = async () => {
const memForm = state.showMemDialog.memForm;
memForm.teamId = state.chooseId;
memForm.teamId = state.chooseData.id;
notBlank(memForm.accountIds, '请先选择账号');
await tagApi.saveTeamMem.request(memForm);
@@ -404,6 +387,4 @@ const tagTreeNodeCheck = () => {
// console.log(state.showTagDialog.tagTreeTeams);
// }
</script>
<style lang="scss">
</style>
<style lang="scss"></style>

View File

@@ -1,76 +1,41 @@
<template>
<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="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="请输入账号名" style="width: 200px" v-model="query.username" @clear="search()"
clearable></el-input>
<el-button @click="search()" type="success" icon="search"></el-button>
</div>
<el-table :data="datas" ref="table" @current-change="choose" show-overflow-tooltip>
<el-table-column label="选择" width="55px">
<template #default="scope">
<el-radio v-model="chooseId" :label="scope.row.id">
<i></i>
</el-radio>
</template>
</el-table-column>
<el-table-column prop="name" label="姓名" min-width="115"></el-table-column>
<el-table-column prop="username" label="用户名" min-width="115"></el-table-column>
<div>
<page-table :query="state.queryConfig" v-model:query-form="query" :show-choose-column="true"
v-model:choose-data="state.chooseData" :data="datas" :columns="state.columns" :total="total"
v-model:page-size="query.pageSize" v-model:page-num="query.pageNum" @pageChange="search()">
<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>
</template>
</el-table-column>
<el-table-column min-width="160" prop="lastLoginTime" label="最后登录时间" show-overflow-tooltip>
<template #default="scope">
{{ dateFormat(scope.row.lastLoginTime) }}
</template>
</el-table-column>
<template #queryRight>
<el-button v-auth="'account:add'" type="primary" icon="plus" @click="editAccount(true)">添加</el-button>
<el-button v-auth="'account:add'" :disabled="state.chooseData == null" @click="editAccount(false)"
type="primary" icon="edit">编辑</el-button>
<el-button v-auth="'account:saveRoles'" :disabled="state.chooseData == null" @click="showRoleEdit()"
type="success" icon="setting">角色分配</el-button>
<el-button v-auth="'account:del'" :disabled="state.chooseData == null" @click="deleteAccount()"
type="danger" icon="delete">删除</el-button>
</template>
<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">
{{ dateFormat(scope.row.createTime) }}
</template>
</el-table-column>
<template #status="{ data }">
<el-tag v-if="data.status == 1" type="success">正常</el-tag>
<el-tag v-if="data.status == -1" type="danger">禁用</el-tag>
</template>
<!-- <el-table-column min-width="120" prop="remark" label="备注" show-overflow-tooltip></el-table-column> -->
<el-table-column label="查看更多" min-width="150">
<template #default="scope">
<el-link @click.prevent="showRoles(scope.row)" type="success">角色</el-link>
<template #showmore="{ data }">
<el-link @click.prevent="showRoles(data)" type="success">角色</el-link>
<el-link class="ml5" @click.prevent="showResources(scope.row)" type="info">菜单&权限</el-link>
</template>
</el-table-column>
<el-link class="ml5" @click.prevent="showResources(data)" type="info">菜单&权限</el-link>
</template>
<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" size="small" plain>禁用</el-button>
<template #action="{ data }">
<el-button v-auth="'account:changeStatus'" @click="changeStatus(data)" v-if="data.status == 1" type="danger"
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'" v-if="data.status == -1" type="success"
@click="changeStatus(data)" size="small" plain>启用</el-button>
<el-button v-auth="'account:add'" :disabled="!scope.row.otpSecret || scope.row.otpSecret == '-'"
@click="resetOtpSecret(scope.row)" type="warning" size="small" plain>重置OTP</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-row>
</el-card>
<el-button v-auth="'account:add'" :disabled="!data.otpSecret || data.otpSecret == '-'"
@click="resetOtpSecret(data)" type="warning" size="small" plain>重置OTP</el-button>
</template>
</page-table>
<el-dialog width="500px" :title="showRoleDialog.title" v-model="showRoleDialog.visible">
<el-table border :data="showRoleDialog.accountRoles">
@@ -112,13 +77,14 @@ import enums from '../enums';
import { accountApi } from '../api';
import { ElMessage, ElMessageBox } from 'element-plus';
import { dateFormat } from '@/common/utils/date';
import PageTable from '@/components/pagetable/PageTable.vue'
import { TableColumn, TableQuery } from '@/components/pagetable';
const state = reactive({
chooseId: null,
/**
* 选中的数据
*/
chooseData: null,
chooseData: null as any,
/**
* 查询条件
*/
@@ -127,6 +93,21 @@ const state = reactive({
pageNum: 1,
pageSize: 10,
},
queryConfig: [
TableQuery.text("username", "用户名"),
],
columns: [
TableColumn.new("name", "姓名"),
TableColumn.new("username", "用户名"),
TableColumn.new("status", "状态").setSlot("status"),
TableColumn.new("lastLoginTime", "最后登录时间").isTime(),
TableColumn.new("showmore", "查看更多").setSlot("showmore").setMinWidth(150),
TableColumn.new("creator", "创建账号"),
TableColumn.new("createTime", "创建时间").isTime(),
TableColumn.new("modifier", "更新账号"),
TableColumn.new("updateTime", "更新时间").isTime(),
TableColumn.new("action", "操作").setSlot("action").fixedRight().setMinWidth(200),
],
datas: [],
total: 0,
showRoleDialog: {
@@ -155,7 +136,6 @@ const state = reactive({
});
const {
chooseId,
query,
datas,
total,
@@ -169,14 +149,6 @@ onMounted(() => {
search();
});
const choose = (item: any) => {
if (!item) {
return;
}
state.chooseId = item.id;
state.chooseData = item;
};
const search = async () => {
let res: any = await accountApi.list.request(state.query);
state.datas = res.list;
@@ -228,7 +200,7 @@ const handlePageChange = (curPage: number) => {
};
const showRoleEdit = () => {
if (!state.chooseId) {
if (!state.chooseData) {
ElMessage.error('请选择账号');
}
state.roleDialog.visible = true;
@@ -262,10 +234,9 @@ const deleteAccount = async () => {
cancelButtonText: '取消',
type: 'warning',
});
await accountApi.del.request({ id: state.chooseId });
await accountApi.del.request({ id: state.chooseData.id });
ElMessage.success('删除成功');
state.chooseData = null;
state.chooseId = null;
search();
} catch (err) { }
};

View File

@@ -6,7 +6,7 @@
<div style="float: left">
<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>
<el-button class="ml5" @click="search" type="success" icon="search"></el-button>
</div>
</div>
<el-table :data="allRole" border ref="roleTable" @select="select" style="width: 100%">

View File

@@ -1,42 +1,26 @@
<template>
<div class="role-list">
<el-card>
<el-button v-auth="'config:save'" type="primary" icon="plus" @click="editConfig(false)">添加</el-button>
<el-button v-auth="'config:save'" :disabled="chooseId == null" @click="editConfig(chooseData)" type="primary"
icon="edit">编辑
</el-button>
<div>
<page-table :show-choose-column="true" v-model:choose-data="state.chooseData" :data="configs"
:columns="state.columns" :total="total" v-model:page-size="query.pageSize" v-model:page-num="query.pageNum"
@pageChange="search()">
<el-table :data="configs" @current-change="choose" ref="table" style="width: 100%">
<el-table-column label="选择" width="55px">
<template #default="scope">
<el-radio v-model="chooseId" :label="scope.row.id">
<i></i>
</el-radio>
</template>
</el-table-column>
<el-table-column prop="name" label="配置项" min-width="100px" show-overflow-tooltip></el-table-column>
<el-table-column prop="key" label="配置key" min-width="100px"></el-table-column>
<el-table-column prop="value" label="配置值" show-overflow-tooltip></el-table-column>
<el-table-column prop="remark" label="备注" min-width="100px" show-overflow-tooltip></el-table-column>
<el-table-column prop="updateTime" label="更新时间" min-width="100px">
<template #default="scope">
{{ dateFormat(scope.row.updateTime) }}
</template>
</el-table-column>
<el-table-column prop="modifier" label="修改者" min-width="60px" show-overflow-tooltip></el-table-column>
<el-table-column label="操作" min-width="50" fixed="right">
<template #default="scope">
<el-link :disabled="scope.row.status == -1" type="warning" @click="showSetConfigDialog(scope.row)"
plain size="small" :underline="false">配置</el-link>
</template>
</el-table-column>
</el-table>
<el-row style="margin-top: 20px" type="flex" justify="end">
<el-pagination 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>
<template #queryRight>
<el-button v-auth="'config:save'" type="primary" icon="plus" @click="editConfig(false)">添加</el-button>
<el-button v-auth="'config:save'" :disabled="chooseData == null" @click="editConfig(chooseData)"
type="primary" icon="edit">编辑
</el-button>
</template>
<template #status="{ data }">
<el-tag v-if="data.status == 1" type="success">正常</el-tag>
<el-tag v-if="data.status == -1" type="danger">禁用</el-tag>
</template>
<template #action="{ data }">
<el-link :disabled="data.status == -1" type="warning" @click="showSetConfigDialog(data)" plain size="small"
:underline="false">配置</el-link>
</template>
</page-table>
<el-dialog :before-close="closeSetConfigDialog" title="配置项设置" v-model="paramsDialog.visible" width="500px">
<el-form v-if="paramsDialog.paramsFormItem.length > 0" ref="paramsFormRef" :model="paramsDialog.params"
@@ -76,7 +60,8 @@ import { ref, toRefs, reactive, onMounted } from 'vue';
import ConfigEdit from './ConfigEdit.vue';
import { configApi } from '../api';
import { ElMessage } from 'element-plus';
import { dateFormat } from '@/common/utils/date';
import PageTable from '@/components/pagetable/PageTable.vue'
import { TableColumn } from '@/components/pagetable';
const paramsFormRef: any = ref(null)
const state = reactive({
@@ -85,10 +70,18 @@ const state = reactive({
pageSize: 10,
name: null,
},
columns: [
TableColumn.new("name", "配置项"),
TableColumn.new("key", "配置key"),
TableColumn.new("value", "配置值"),
TableColumn.new("remark", "备注"),
TableColumn.new("modifier", "更新账号"),
TableColumn.new("updateTime", "更新时间").isTime(),
TableColumn.new("action", "操作").setSlot("action").fixedRight().setMinWidth(60),
],
total: 0,
configs: [],
chooseId: null,
chooseData: null,
chooseData: null as any,
paramsDialog: {
visible: false,
config: null as any,
@@ -106,7 +99,6 @@ const {
query,
total,
configs,
chooseId,
chooseData,
paramsDialog,
configEdit,
@@ -122,11 +114,6 @@ const search = async () => {
state.total = res.total;
};
const handlePageChange = (curPage: number) => {
state.query.pageNum = curPage;
search();
};
const showSetConfigDialog = (row: any) => {
state.paramsDialog.config = row;
// 存在配置项则弹窗提示输入对应的配置项
@@ -155,30 +142,37 @@ const closeSetConfigDialog = () => {
};
const setConfig = async () => {
paramsFormRef.value.validate(async (valid: boolean) => {
if (!valid) {
return false;
}
let paramsValue = state.paramsDialog.params;
if (state.paramsDialog.paramsFormItem.length > 0) {
// 如果配置项删除则需要将value中对应的字段移除
for (let paramKey in paramsValue) {
if (!hasParam(paramKey, state.paramsDialog.paramsFormItem)) {
delete paramsValue[paramKey];
}
let paramsValue = state.paramsDialog.params;
if (state.paramsDialog.paramsFormItem.length > 0) {
await paramsFormRef.value.validate(async (valid: boolean) => {
if (!valid) {
paramsValue = null as any;
return false;
}
if (state.paramsDialog.paramsFormItem.length > 0) {
// 如果配置项删除则需要将value中对应的字段移除
for (let paramKey in paramsValue) {
if (!hasParam(paramKey, state.paramsDialog.paramsFormItem)) {
delete paramsValue[paramKey];
}
}
paramsValue = JSON.stringify(paramsValue);
}
paramsValue = JSON.stringify(paramsValue);
}
await configApi.save.request({
id: state.paramsDialog.config.id,
key: state.paramsDialog.config.key,
name: state.paramsDialog.config.name,
value: paramsValue,
});
ElMessage.success('保存成功');
closeSetConfigDialog();
search();
}
// 说明校验失败
if (paramsValue == null) {
return;
}
await configApi.save.request({
id: state.paramsDialog.config.id,
key: state.paramsDialog.config.key,
name: state.paramsDialog.config.name,
value: paramsValue,
});
ElMessage.success('保存成功');
closeSetConfigDialog();
search();
};
@@ -191,17 +185,8 @@ const hasParam = (paramKey: string, paramItems: any) => {
return false;
};
const choose = (item: any) => {
if (!item) {
return;
}
state.chooseId = item.id;
state.chooseData = item;
};
const configEditChange = () => {
ElMessage.success('保存成功');
state.chooseId = null;
state.chooseData = null;
search();
};

View File

@@ -1,52 +1,28 @@
<template>
<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>
<div>
<page-table :query="state.queryConfig" v-model:query-form="query" :show-choose-column="true"
v-model:choose-data="state.chooseData" :data="roles" :columns="state.columns" :total="total"
v-model:page-size="query.pageSize" v-model:page-num="query.pageNum" @pageChange="search()">
<div style="float: right">
<el-input placeholder="请输入角色名称" class="mr2" style="width: 200px" v-model="query.name" @clear="search"
clearable></el-input>
<el-button @click="search" type="success" icon="search"></el-button>
</div>
<el-table :data="roles" @current-change="choose" ref="table" style="width: 100%">
<el-table-column label="选择" width="55px">
<template #default="scope">
<el-radio v-model="chooseId" :label="scope.row.id">
<i></i>
</el-radio>
</template>
</el-table-column>
<el-table-column prop="name" label="角色名称"></el-table-column>
<el-table-column prop="code" label="角色code"></el-table-column>
<el-table-column prop="remark" label="描述" min-width="160px" show-overflow-tooltip></el-table-column>
<el-table-column prop="createTime" label="创建时间">
<template #default="scope">
{{ dateFormat(scope.row.createTime) }}
</template>
</el-table-column>
<el-table-column prop="updateTime" label="修改时间">
<template #default="scope">
{{ dateFormat(scope.row.updateTime) }}
</template>
</el-table-column>
<el-table-column label="查看更多" min-width="80px">
<template #default="scope">
<el-link @click.prevent="showResources(scope.row)" type="info">菜单&权限</el-link>
</template>
</el-table-column>
</el-table>
<el-row style="margin-top: 20px" type="flex" justify="end">
<el-pagination style="text-align: right" @current-change="handlePageChange" :total="total"
layout="prev, pager, next, total, jumper" v-model:current-page="query.pageNum"
:page-size="query.pageSize"></el-pagination>
</el-row>
</el-card>
<template #queryRight>
<el-button v-auth="'role:add'" type="primary" icon="plus" @click="editRole(false)">添加</el-button>
<el-button v-auth="'role:update'" :disabled="chooseData == null" @click="editRole(chooseData)"
type="primary" icon="edit">编辑</el-button>
<el-button v-auth="'role:saveResources'" :disabled="chooseData == null" @click="editResource(chooseData)"
type="success" icon="setting">分配菜单&权限</el-button>
<el-button v-auth="'role:del'" :disabled="chooseData == null" @click="deleteRole(chooseData)" type="danger"
icon="delete">删除</el-button>
</template>
<template #status="{ data }">
<el-tag v-if="data.status == 1" type="success" size="small">正常</el-tag>
<el-tag v-if="data.status == -1" type="danger" size="small">禁用</el-tag>
</template>
<template #showmore="{ data }">
<el-link @click.prevent="showResources(data)" type="info">菜单&权限</el-link>
</template>
</page-table>
<role-edit :title="roleEditDialog.title" v-model:visible="roleEditDialog.visible" :data="roleEditDialog.role"
@val-change="roleEditChange" />
@@ -65,7 +41,8 @@ import ResourceEdit from './ResourceEdit.vue';
import ShowResource from './ShowResource.vue';
import { roleApi, resourceApi } from '../api';
import { ElMessage, ElMessageBox } from 'element-plus';
import { dateFormat } from '@/common/utils/date';
import PageTable from '@/components/pagetable/PageTable.vue'
import { TableColumn, TableQuery } from '@/components/pagetable';
const state = reactive({
query: {
@@ -73,10 +50,23 @@ const state = reactive({
pageSize: 10,
name: null,
},
queryConfig: [
TableQuery.text("name", "角色名"),
],
columns: [
TableColumn.new("name", "角色名称"),
TableColumn.new("code", "角色code"),
TableColumn.new("remark", "备注"),
TableColumn.new("status", "状态").setSlot("status"),
TableColumn.new("creator", "创建账号"),
TableColumn.new("createTime", "创建时间").isTime(),
TableColumn.new("modifier", "更新账号"),
TableColumn.new("updateTime", "更新时间").isTime(),
TableColumn.new("showmore", "查看更多").setSlot("showmore").setMinWidth(150).fixedRight(),
],
total: 0,
roles: [],
chooseId: null,
chooseData: null,
chooseData: null as any,
resourceDialog: {
visible: false,
role: {},
@@ -99,7 +89,6 @@ const {
query,
total,
roles,
chooseId,
chooseData,
resourceDialog,
roleEditDialog,
@@ -116,22 +105,8 @@ const search = async () => {
state.total = res.total;
};
const handlePageChange = (curPage: number) => {
state.query.pageNum = curPage;
search();
};
const choose = (item: any) => {
if (!item) {
return;
}
state.chooseId = item.id;
state.chooseData = item;
};
const roleEditChange = () => {
ElMessage.success('修改成功!');
state.chooseId = null;
state.chooseData = null;
search();
};
@@ -230,6 +205,4 @@ const cancelEditResources = () => {
}, 10);
};
</script>
<style lang="scss">
</style>
<style lang="scss"></style>

View File

@@ -1,49 +1,29 @@
<template>
<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">
<div>
<page-table :query="state.queryConfig" v-model:query-form="query" :data="logs" :columns="state.columns"
:total="total" v-model:page-size="query.pageSize" v-model:page-num="query.pageNum" @pageChange="search()">
<template #selectAccount>
<el-select remote :remote-method="getAccount" v-model="query.creatorId" filterable placeholder="请输入并选择账号"
clearable class="mr5" style="width: 200px">
<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>
<el-option label="失败" :value="2"> </el-option>
</el-select>
<el-button @click="search" type="success" icon="search"></el-button>
</div>
<el-table :data="logs" style="width: 100%">
<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">
{{ dateFormat(scope.row.createTime) }}
</template>
</el-table-column>
<el-table-column prop="type" label="结果" min-width="65">
<template #default="scope">
<el-tag v-if="scope.row.type == 1" type="success" size="small">成功</el-tag>
<el-tag v-if="scope.row.type == 2" type="danger" size="small">失败</el-tag>
</template>
</el-table-column>
<el-table-column prop="description" label="描述" min-width="160" show-overflow-tooltip></el-table-column>
</template>
<el-table-column prop="reqParam" label="操作信息" min-width="300" show-overflow-tooltip></el-table-column>
<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-row>
</el-card>
<template #type="{ data }">
<el-tag v-if="data.type == 1" type="success" size="small">成功</el-tag>
<el-tag v-if="data.type == 2" type="danger" size="small">失败</el-tag>
</template>
</page-table>
</div>
</template>
<script lang="ts" setup>
import { toRefs, reactive, onMounted } from 'vue';
import { logApi, accountApi } from '../api';
import { dateFormat } from '@/common/utils/date';
import PageTable from '@/components/pagetable/PageTable.vue'
import { TableColumn, TableQuery } from '@/components/pagetable';
const state = reactive({
query: {
@@ -51,8 +31,22 @@ const state = reactive({
creatorId: null,
pageNum: 1,
pageSize: 10,
name: null,
},
queryConfig: [
TableQuery.slot("creatorId", "操作人", "selectAccount"),
TableQuery.select("type", "操作结果").setOptions([
{ label: "成功", value: 1 },
{ label: "失败", value: 2 },
]),
],
columns: [
TableColumn.new("creator", "操作人"),
TableColumn.new("createTime", "操作时间").isTime(),
TableColumn.new("type", "结果").setSlot("type"),
TableColumn.new("description", "描述"),
TableColumn.new("reqParam", "操作信息"),
TableColumn.new("resp", "响应信息"),
],
total: 0,
logs: [],
accounts: [] as any,
@@ -75,11 +69,6 @@ const search = async () => {
state.total = res.total;
};
const handlePageChange = (curPage: number) => {
state.query.pageNum = curPage;
search();
};
const getAccount = (username: any) => {
accountApi.list.request({ username }).then((res) => {
state.accounts = res.list;
@@ -87,6 +76,4 @@ const getAccount = (username: any) => {
};
</script>
<style lang="scss">
</style>
<style lang="scss"></style>

View File

@@ -2,6 +2,7 @@ package api
import (
"context"
"fmt"
"mayfly-go/internal/redis/api/form"
"mayfly-go/pkg/biz"
"mayfly-go/pkg/ginx"
@@ -34,6 +35,7 @@ func (r *Redis) Hdel(rc *req.Ctx) {
ri, key := r.checkKeyAndGetRedisIns(rc)
field := rc.GinCtx.Query("field")
rc.ReqParam = fmt.Sprintf("key=%s, field=%s", key, field)
delRes, err := ri.GetCmdable().HDel(context.TODO(), key, field).Result()
biz.ErrIsNilAppendErr(err, "hdel err: %s")
rc.ResData = delRes

View File

@@ -20,7 +20,7 @@ type Role struct {
func (r *Role) Roles(rc *req.Ctx) {
g := rc.GinCtx
condition := &entity.Role{}
condition := &entity.Role{Name: g.Query("name")}
rc.ResData = r.RoleApp.GetPageList(condition, ginx.GetPageParam(g), new([]entity.Role))
}

View File

@@ -4,7 +4,7 @@ import "fmt"
const (
AppName = "mayfly-go"
Version = "v1.4.3"
Version = "v1.4.4"
)
func GetAppInfo() string {