feat: 日常优化

This commit is contained in:
meilin.huang
2022-02-14 14:58:25 +08:00
parent 2698157ce7
commit 5def4bc26d
14 changed files with 234 additions and 139 deletions

View File

@@ -5,17 +5,71 @@
*/
export function formatByteSize(size: any) {
const value = Number(size);
if (size && !isNaN(value)) {
const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB', 'BB'];
let index = 0;
let k = value;
if (value >= 1024) {
while (k > 1024) {
k = k / 1024;
index++;
}
if (size && !isNaN(value)) {
const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB', 'BB'];
let index = 0;
let k = value;
if (value >= 1024) {
while (k > 1024) {
k = k / 1024;
index++;
}
return `${k.toFixed(2)}${units[index]}`;
}
return '-';
return `${k.toFixed(2)}${units[index]}`;
}
return '-';
}
/**
* 格式化json字符串
* @param txt json字符串
* @param compress 是否压缩
* @returns 格式化后的字符串
*/
export function formatJsonString(txt: string, compress: boolean) {
var indentChar = ' ';
if (/^\s*$/.test(txt)) {
console.log('数据为空,无法格式化! ');
return txt;
}
try {
var data = JSON.parse(txt);
} catch (e: any) {
console.log('数据源语法错误,格式化失败! 错误信息: ' + e.description, 'err');
return txt;
}
var draw: any = [],
line = compress ? '' : '\n',
// eslint-disable-next-line no-unused-vars
nodeCount: number = 0,
// eslint-disable-next-line no-unused-vars
maxDepth: number = 0;
var notify = function (name: any, value: any, isLast: any, indent: any, formObj: any) {
nodeCount++; /*节点计数*/
for (var i = 0, tab = ''; i < indent; i++) tab += indentChar; /* 缩进HTML */
tab = compress ? '' : tab; /*压缩模式忽略缩进*/
maxDepth = ++indent; /*缩进递增并记录*/
if (value && value.constructor == Array) {
/*处理数组*/
draw.push(tab + (formObj ? '"' + name + '": ' : '') + '[' + line); /*缩进'[' 然后换行*/
for (var i = 0; i < value.length; i++) notify(i, value[i], i == value.length - 1, indent, false);
draw.push(tab + ']' + (isLast ? line : ',' + line)); /*缩进']'换行,若非尾元素则添加逗号*/
} else if (value && typeof value == 'object') {
/*处理对象*/
draw.push(tab + (formObj ? '"' + name + '": ' : '') + '{' + line); /*缩进'{' 然后换行*/
var len = 0,
i = 0;
for (var key in value) len++;
for (var key in value) notify(key, value[key], ++i == len, indent, true);
draw.push(tab + '}' + (isLast ? line : ',' + line)); /*缩进'}'换行,若非尾元素则添加逗号*/
} else {
if (typeof value == 'string') value = '"' + value + '"';
draw.push(tab + (formObj ? '"' + name + '": ' : '') + value + (isLast ? '' : ',') + line);
}
};
var isLast = true,
indent = 0;
notify('', data, isLast, indent, false);
return draw.join('');
}

View File

@@ -46,7 +46,7 @@ import { ElOption, ElSelect } from 'element-plus';
const CodeMirror = (window as any).CodeMirror || _CodeMirror;
export default defineComponent({
name: 'codemirror',
name: 'CodeMirror',
components: {
ElOption,
ElSelect,
@@ -173,7 +173,7 @@ export default defineComponent({
watch(
() => props.modelValue,
(newValue, oldValue) => {
(newValue) => {
handerCodeChange(newValue);
}
);

View File

@@ -22,9 +22,7 @@
</template>
<script lang="ts">
import { ref, toRefs, reactive, watch, defineComponent, onMounted } from 'vue';
import { ElMessage } from 'element-plus';
import { notEmpty } from '@/common/assert';
import { toRefs, reactive, watch, defineComponent, onMounted } from 'vue';
import { projectApi } from '../project/api';
export default defineComponent({

View File

@@ -26,45 +26,26 @@
<el-tab-pane label="字段" name="1">
<el-table :data="tableData.fields.res">
<el-table-column :prop="item.prop" :label="item.label" v-for="item in tableData.fields.colNames" :key="item.prop">
<template v-if="item.prop === 'name'" #default="scope">
<el-input size="small" v-model="scope.row.name"></el-input>
</template>
<!--eslint-disable-next-line-->
<template v-if="item.prop === 'type'" #default="scope">
<el-select filterable size="small" v-model="scope.row.type">
<template #default="scope">
<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 typeList" :key="typeValue" :value="typeValue">{{ typeValue }}</el-option>
</el-select>
</template>
<!--eslint-disable-next-line-->
<template v-if="item.prop === 'value'" #default="scope">
<el-input size="small" v-model="scope.row.value"> </el-input>
</template>
<!--eslint-disable-next-line-->
<template v-if="item.prop === 'length'" #default="scope">
<el-input size="small" v-model="scope.row.length"> </el-input>
</template>
<!--eslint-disable-next-line-->
<template v-if="item.prop === 'notNull'" #default="scope">
<el-checkbox size="small" v-model="scope.row.notNull"> </el-checkbox>
</template>
<!--eslint-disable-next-line-->
<template v-if="item.prop === 'pri'" #default="scope">
<el-checkbox size="small" v-model="scope.row.pri"> </el-checkbox>
</template>
<!--eslint-disable-next-line-->
<template v-if="item.prop === 'auto_increment'" #default="scope">
<el-checkbox size="small" v-model="scope.row.auto_increment"> </el-checkbox>
</template>
<!-- <template v-if="item.prop === 'un_signed'" #default="scope">
<el-checkbox :disabled="scope.row.type === 'int'" size="small" v-model="scope.row.un_signed"> </el-checkbox>
</template> -->
<!--eslint-disable-next-line-->
<template v-if="item.prop === 'remark'" #default="scope">
<el-input size="small" v-model="scope.row.remark"> </el-input>
</template>
<!--eslint-disable-next-line-->
<template v-if="item.prop === 'action'" #default="scope">
<el-button type="text" size="small" @click.prevent="deleteRow(scope.$index)">删除</el-button>
<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-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 === '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-button v-if="item.prop === 'action'" type="text" size="small" @click.prevent="deleteRow(scope.$index)">删除</el-button>
</template>
</el-table-column>
</el-table>
@@ -85,8 +66,8 @@
<script lang="ts">
import { watch, toRefs, reactive, defineComponent, ref, getCurrentInstance } from 'vue';
import { TYPE_LIST, CHARACTER_SET_NAME_LIST } from './service.ts';
import { dbApi } from '../../db/api';
import { ElMessage } from 'element-plus';
import SqlExecBox from './component/SqlExecBox.ts';
export default defineComponent({
name: 'createTable',
props: {
@@ -178,6 +159,7 @@ export default defineComponent({
});
const cancel = () => {
emit('update:visible', false);
reset();
};
const addRow = () => {
state.tableData.fields.res.push({
@@ -196,39 +178,36 @@ export default defineComponent({
};
const submit = async () => {
let data = state.tableData;
let str = '';
let primary_key = '';
let fields: string[] = [];
data.fields.res.forEach((item) => {
str += `\`${item.name}\` ${item.type}${+item.length > 0 ? `(${item.length})` : ''} ${item.notNull ? 'NOT NULL' : ''} ${
item.auto_increment ? 'AUTO_INCREMENT' : ''
} ${item.value ? 'DEFAULT ' + item.value : item.notNull ? '' : 'DEFAULT NULL'} ${item.remark ? `COMMENT '${item.remark}'` : ''},\n`;
fields.push(
`${item.name} ${item.type}${+item.length > 0 ? `(${item.length})` : ''} ${item.notNull ? 'NOT NULL' : ''} ${
item.auto_increment ? 'AUTO_INCREMENT' : ''
} ${item.value ? 'DEFAULT ' + item.value : item.notNull ? '' : 'DEFAULT NULL'} ${
item.remark ? `COMMENT '${item.remark}'` : ''
} \n`
);
if (item.pri) {
primary_key += `\`${item.name}\`,`;
primary_key += `${item.name},`;
}
});
let sql = `
CREATE TABLE \`${data.tableName}\` (
${str}
PRIMARY KEY (${primary_key.slice(0, -1)})
CREATE TABLE ${data.tableName} (
${fields.join(',')}
${primary_key ? `, PRIMARY KEY (${primary_key.slice(0, -1)})` : ''}
) ENGINE=InnoDB DEFAULT CHARSET=${data.characterSet} COLLATE=utf8mb4_bin COMMENT='${data.tableComment}';`;
try {
state.btnloading = true;
const res = await dbApi.sqlExec.request({
id: props.dbId,
sql: sql,
});
state.btnloading = false;
ElMessage.success('创建成功');
proxy.$parent.tableInfo({ id: props.dbId });
reset();
cancel();
} catch (err) {
console.error(err);
state.btnloading = false;
ElMessage.error('创建失败');
}
SqlExecBox({
sql: sql,
dbId: props.dbId as any,
runSuccessCallback: () => {
ElMessage.success('创建成功');
proxy.$parent.tableInfo({ id: props.dbId });
cancel();
},
});
};
const reset = () => {
formRef.value.resetFields();

View File

@@ -114,6 +114,11 @@
<el-link class="ml5" @click.prevent="showCreateDdl(scope.row)" type="info">SQL</el-link>
</template>
</el-table-column>
<el-table-column label="操作" min-width="80">
<template #default="scope">
<el-link @click.prevent="dropTable(scope.row)" type="danger">删除</el-link>
</template>
</el-table-column>
</el-table>
</el-dialog>
@@ -155,9 +160,10 @@ import { toRefs, reactive, onMounted, defineComponent } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { formatByteSize } from '@/common/utils/format';
import DbEdit from './DbEdit.vue';
import CreateTable from '../component/Table/CreateTable.vue';
import CreateTable from './CreateTable.vue';
import { dbApi } from './api';
import { projectApi } from '../project/api.ts';
import SqlExecBox from './component/SqlExecBox.ts';
export default defineComponent({
name: 'DbList',
components: {
@@ -306,10 +312,30 @@ export default defineComponent({
tableName: row.tableName,
});
state.ddlDialog.ddl = res[0]['Create Table'];
console.log(state.ddlDialog);
state.ddlDialog.visible = true;
};
/**
* 删除表
*/
const dropTable = async (row: any) => {
try {
const tableName = row.tableName;
await ElMessageBox.confirm(`确定删除'${tableName}'表?`, '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
});
SqlExecBox({
sql: `DROP TABLE ${tableName}`,
dbId: state.chooseId,
runSuccessCallback: async () => {
state.tableInfoDialog.infos = await dbApi.tableInfos.request({ id: state.chooseId });
},
});
} catch (err) {}
};
return {
...toRefs(state),
// enums,
@@ -324,6 +350,7 @@ export default defineComponent({
showColumns,
showTableIndex,
showCreateDdl,
dropTable,
formatByteSize,
};
},

View File

@@ -196,7 +196,7 @@
</template>
<script lang="ts">
import { h, toRefs, reactive, computed, defineComponent, ref } from 'vue';
import { toRefs, reactive, computed, defineComponent, ref } from 'vue';
import { dbApi } from './api';
import _ from 'lodash';
@@ -218,7 +218,7 @@ import { ElMessage, ElMessageBox } from 'element-plus';
import ProjectEnvSelect from '../component/ProjectEnvSelect.vue';
import config from '@/common/config';
import { getSession } from '@/common/utils/storage';
import SqlExecBox from './SqlExecBox';
import SqlExecBox from './component/SqlExecBox';
export default defineComponent({
name: 'SqlExec',
@@ -828,8 +828,8 @@ export default defineComponent({
// 添加新数据行
const addRow = async () => {
const tableNmae = state.nowTableName;
const columns = await getColumns(tableNmae);
const tableName = state.nowTableName;
const columns = await getColumns(tableName);
// key: 字段名value: 字段名提示
let obj: any = {};
@@ -840,7 +840,7 @@ export default defineComponent({
let values = Object.values(obj).join(',');
let sql = `INSERT INTO ${state.nowTableName} (${columnNames}) VALUES (${values});`;
promptExeSql(sql, null, () => {
onRefresh(tableNmae);
onRefresh(tableName);
});
};

View File

@@ -8,7 +8,7 @@ export type SqlExecProps = {
cancelCallback?: Function
}
const boxId = 'Sql-Exec-ID'
const boxId = 'sql-exec-id'
const renderBox = (): VNode => {
const props: SqlExecProps = {
@@ -32,7 +32,7 @@ let boxInstance: any
const SqlExecBox = (props: SqlExecProps): void => {
if (boxInstance) {
const boxVue = boxInstance.component
// 调用open方法显示弹框
// 调用open方法显示弹框注意不能使用boxVue.ctx来调用组件函数build打包后ctx会获取不到
boxVue.proxy.open(props);
} else {
boxInstance = renderBox()

View File

@@ -12,7 +12,7 @@
<script lang="ts">
import { toRefs, reactive, defineComponent } from 'vue';
import { dbApi } from './api';
import { dbApi } from '../api';
import { ElDialog, ElButton } from 'element-plus';
// import base style
import 'codemirror/lib/codemirror.css';
@@ -71,6 +71,7 @@ export default defineComponent({
*/
const runSql = async () => {
try {
state.btnLoading = true;
await dbApi.sqlExec.request({
id: state.dbId,
sql: state.sql.trim(),
@@ -79,9 +80,10 @@ export default defineComponent({
} catch (e) {
runSuccess = false;
}
if (runSuccessCallback) {
if (runSuccess && runSuccessCallback) {
runSuccessCallback();
}
state.btnLoading = false;
cancel();
};

View File

@@ -6,7 +6,7 @@
<script lang="ts">
import SshTerminal from './SshTerminal.vue';
import { reactive, toRefs, onBeforeMount, defineComponent, onMounted } from 'vue';
import { reactive, toRefs, defineComponent, onMounted } from 'vue';
import { useRoute } from 'vue-router';
export default defineComponent({

View File

@@ -11,7 +11,7 @@
icon="edit"
>编辑</el-button
>
<el-button @click="showMembers(chooseData)" :disabled="chooseId == null" type="success" icon="setting">成员管理</el-button>
<el-button @click="showMembers(chooseData)" :disabled="chooseId == null" type="success" icon="user">成员管理</el-button>
<el-button @click="showEnv(chooseData)" :disabled="chooseId == null" type="info" icon="setting">环境管理</el-button>

View File

@@ -2,13 +2,13 @@
<el-dialog :title="title" v-model="dialogVisible" :before-close="cancel" :show-close="false" width="750px" :destroy-on-close="true">
<el-form label-width="85px">
<el-form-item prop="key" label="key:">
<el-input :disabled="operationType == 2" v-model="keyInfo.key"></el-input>
<el-input :disabled="operationType == 2" v-model="key.key"></el-input>
</el-form-item>
<el-form-item prop="timed" label="过期时间:">
<el-input v-model.number="keyInfo.timed" type="number"></el-input>
<el-input v-model.number="key.timed" type="number"></el-input>
</el-form-item>
<el-form-item prop="dataType" label="数据类型:">
<el-select :disabled="operationType == 2" style="width: 100%" v-model="keyInfo.type" placeholder="请选择数据类型">
<el-select :disabled="operationType == 2" style="width: 100%" v-model="key.type" placeholder="请选择数据类型">
<el-option key="string" label="string" value="string"> </el-option>
<el-option key="hash" label="hash" value="hash"> </el-option>
<el-option key="set" label="set" value="set"> </el-option>
@@ -16,7 +16,13 @@
</el-form-item>
<el-form-item v-if="keyInfo.type == 'string'" prop="value" label="内容:">
<el-input class="json-text" v-model="stringValue" type="textarea" :autosize="{ minRows: 10, maxRows: 20 }"></el-input>
<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-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>
</el-select>
</div>
</el-form-item>
<span v-if="keyInfo.type == 'hash'">
@@ -40,7 +46,7 @@
</el-table-column>
<el-table-column label="操作" width="90">
<template #default="scope">
<el-button type="danger" @click="hashValue.splice(scope.$index, 1)" icon="delete" size="small" plain>删除</el-button>
<el-button type="danger" @click="hash.value.splice(scope.$index, 1)" icon="delete" size="small" plain>删除</el-button>
</template>
</el-table-column>
</el-table>
@@ -49,8 +55,8 @@
<span v-if="keyInfo.type == 'set'">
<el-button @click="onAddSetValue" icon="plus" size="small" plain class="mt10">添加</el-button>
<el-table :data="setValue" stripe style="width: 100%">
<el-table-column prop="value" label="value" width>
<template #default="scope" min-width="200">
<el-table-column prop="value" label="value" min-width="200">
<template #default="scope">
<el-input
v-model="scope.row.value"
clearable
@@ -62,7 +68,7 @@
</el-table-column>
<el-table-column label="操作" width="90">
<template #default="scope">
<el-button type="danger" @click="setValue.splice(scope.$index, 1)" icon="delete" 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>
@@ -81,6 +87,7 @@ import { defineComponent, reactive, watch, toRefs } from 'vue';
import { redisApi } from './api';
import { ElMessage } from 'element-plus';
import { isTrue, notEmpty } from '@/common/assert';
import { formatJsonString } from '@/common/utils/format';
export default defineComponent({
name: 'DateEdit',
@@ -119,32 +126,40 @@ export default defineComponent({
dialogVisible: false,
operationType: 1,
redisId: '',
keyInfo: {
key: {
key: '',
type: 'string',
timed: -1,
},
stringValue: '',
hashValue: [
{
key: '',
value: '',
},
],
setValue: [{ value: '' }],
string: {
type: 'text',
value: '',
},
hash: {
value: [
{
key: '',
value: '',
},
],
},
set: {
value: [{ value: '' }],
},
});
const cancel = () => {
emit('update:visible', false);
emit('cancel');
setTimeout(() => {
state.keyInfo = {
state.key = {
key: '',
type: 'string',
timed: -1,
};
state.stringValue = '';
state.hashValue = [
state.string.value = '';
state.string.type = 'text';
state.hash.value = [
{
key: '',
value: '',
@@ -178,7 +193,7 @@ export default defineComponent({
() => props.keyInfo,
(val) => {
if (val) {
state.keyInfo = { ...val };
state.key = { ...val };
}
},
{
@@ -190,7 +205,7 @@ export default defineComponent({
() => props.stringValue,
(val) => {
if (val) {
state.stringValue = val;
state.string.value = val;
}
},
{
@@ -202,7 +217,7 @@ export default defineComponent({
() => props.setValue,
(val) => {
if (val) {
state.setValue = val;
state.set.value = val;
}
},
{
@@ -214,7 +229,7 @@ export default defineComponent({
() => props.hashValue,
(val) => {
if (val) {
state.hashValue = val;
state.hash.value = val;
}
},
{
@@ -223,26 +238,26 @@ export default defineComponent({
);
const saveValue = async () => {
notEmpty(state.keyInfo.key, 'key不能为空');
notEmpty(state.key.key, 'key不能为空');
if (state.keyInfo.type == 'string') {
notEmpty(state.stringValue, 'value不能为空');
const sv = { value: state.stringValue, id: state.redisId };
Object.assign(sv, state.keyInfo);
if (state.key.type == 'string') {
notEmpty(state.string.value, 'value不能为空');
const sv = { value: formatJsonString(state.string.value, true), id: state.redisId };
Object.assign(sv, state.key);
await redisApi.saveStringValue.request(sv);
}
if (state.keyInfo.type == 'hash') {
isTrue(state.hashValue.length > 0, 'hash内容不能为空');
const sv = { value: state.hashValue, id: state.redisId };
Object.assign(sv, state.keyInfo);
if (state.key.type == 'hash') {
isTrue(state.hash.value.length > 0, 'hash内容不能为空');
const sv = { value: state.hash.value, id: state.redisId };
Object.assign(sv, state.key);
await redisApi.saveHashValue.request(sv);
}
if (state.keyInfo.type == 'set') {
isTrue(state.setValue.length > 0, 'set内容不能为空');
const sv = { value: state.setValue.map((x) => x.value), id: state.redisId };
Object.assign(sv, state.keyInfo);
if (state.key.type == 'set') {
isTrue(state.set.value.length > 0, 'set内容不能为空');
const sv = { value: state.set.value.map((x) => x.value), id: state.redisId };
Object.assign(sv, state.key);
await redisApi.saveSetValue.request(sv);
}
@@ -252,11 +267,22 @@ export default defineComponent({
};
const onAddHashValue = () => {
state.hashValue.push({ key: '', value: '' });
state.hash.value.push({ key: '', value: '' });
};
const onAddSetValue = () => {
state.setValue.push({ value: '' });
state.set.value.push({ value: '' });
};
// 更改文本类型
const onChangeTextType = (val: string) => {
if (val == 'json') {
state.string.value = formatJsonString(state.string.value, false);
return;
}
if (val == 'text') {
state.string.value = formatJsonString(state.string.value, true);
}
};
return {
@@ -265,7 +291,23 @@ export default defineComponent({
cancel,
onAddHashValue,
onAddSetValue,
onChangeTextType,
};
},
});
</script>
</script>
<style lang="scss">
#string-value-text {
flex-grow: 1;
display: flex;
position: relative;
.text-type-select {
position: absolute;
z-index: 2;
right: 10px;
top: 10px;
max-width: 70px;
}
}
</style>

View File

@@ -24,7 +24,7 @@
<el-form-item label="key" label-width="40px">
<el-input
placeholder="支持*模糊key"
style="width: 180px"
style="width: 240px"
v-model="scanParam.match"
@clear="clear()"
clearable
@@ -83,7 +83,6 @@
</template>
<script lang="ts">
import ValueDialog from './ValueDialog.vue';
import { redisApi } from './api';
import { toRefs, reactive, defineComponent } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
@@ -94,7 +93,6 @@ import { isTrue, notNull } from '@/common/assert';
export default defineComponent({
name: 'DataOperation',
components: {
ValueDialog,
DataEdit,
ProjectEnvSelect,
},
@@ -106,11 +104,6 @@ export default defineComponent({
query: {
envId: 0,
},
// redis: {
// id: 0,
// info: '',
// conf: '',
// },
scanParam: {
id: null,
cluster: 0,