fix: 数据导出精度修复、redis数据操作优化

This commit is contained in:
meilin.huang
2023-08-29 21:31:08 +08:00
parent 1e5b1868ab
commit 537b179e78
2 changed files with 105 additions and 38 deletions

View File

@@ -6,6 +6,11 @@ export function exportCsv(filename: string, columns: string[], datas: []) {
let dataValueArr: any = []; let dataValueArr: any = [];
for (let column of columns) { for (let column of columns) {
let val: any = data[column]; let val: any = data[column];
if (val == null || val == undefined) {
dataValueArr.push('');
continue;
}
if (typeof val == 'string' && val) { if (typeof val == 'string' && val) {
// csv格式如果有逗号整体用双引号括起来如果里面还有双引号就替换成两个双引号这样导出来的格式就不会有问题了 // csv格式如果有逗号整体用双引号括起来如果里面还有双引号就替换成两个双引号这样导出来的格式就不会有问题了
if (val.indexOf(',') != -1) { if (val.indexOf(',') != -1) {
@@ -16,9 +21,9 @@ export function exportCsv(filename: string, columns: string[], datas: []) {
// 再将逗号转义 // 再将逗号转义
val = `"${val}"`; val = `"${val}"`;
} }
dataValueArr.push(val); dataValueArr.push(val + '\t');
} else { } else {
dataValueArr.push(val); dataValueArr.push(val + '\t');
} }
} }
cvsData.push(dataValueArr); cvsData.push(dataValueArr);

View File

@@ -29,8 +29,8 @@
</el-row> </el-row>
</el-col> </el-col>
<el-col v-loading="state.loadingKeyTree" :span="7" class="el-scrollbar flex-auto" style="overflow: auto"> <el-col v-loading="state.loadingKeyTree" :span="7">
<div> <div class="key-list-vtree">
<el-row> <el-row>
<el-col :span="2"> <el-col :span="2">
<el-input v-model="state.keySeparator" placeholder="分割符" size="small" class="ml5" /> <el-input v-model="state.keySeparator" placeholder="分割符" size="small" class="ml5" />
@@ -52,7 +52,7 @@
</el-row> </el-row>
<el-row class="mb5 mt5"> <el-row class="mb5 mt5">
<el-col :span="20"> <el-col :span="19">
<el-button class="ml5" :disabled="!scanParam.id || !scanParam.db" @click="scan(true)" type="success" icon="more" size="small" plain <el-button class="ml5" :disabled="!scanParam.id || !scanParam.db" @click="scan(true)" type="success" icon="more" size="small" plain
>加载更多</el-button >加载更多</el-button
> >
@@ -79,7 +79,7 @@
>flush</el-button >flush</el-button
> >
</el-col> </el-col>
<el-col :span="4"> <el-col :span="5">
<span style="display: inline-block" class="mt5">keys:{{ state.dbsize }}</span> <span style="display: inline-block" class="mt5">keys:{{ state.dbsize }}</span>
</el-col> </el-col>
</el-row> </el-row>
@@ -97,43 +97,40 @@
@node-click="handleKeyTreeNodeClick" @node-click="handleKeyTreeNodeClick"
@node-expand="keyTreeNodeExpand" @node-expand="keyTreeNodeExpand"
@node-collapse="keyTreeNodeCollapse" @node-collapse="keyTreeNodeCollapse"
@node-contextmenu="rightClickNode"
> >
<template #default="{ node, data }"> <template #default="{ node, data }">
<span class="custom-tree-node key-list-custom-node"> <span class="el-dropdown-link key-list-custom-node" :title="node.label">
<el-dropdown size="small" trigger="contextmenu"> <span v-if="data.type == 1">
<span class="el-dropdown-link"> <SvgIcon :size="15" :name="node.expanded ? 'folder-opened' : 'folder'" />
<span v-if="data.type == 1 && !node.expanded">
<SvgIcon :size="15" name="folder" />
</span> </span>
<span v-if="data.type == 1 && node.expanded"> <span :class="'ml5 ' + (data.type == 1 ? 'folder-label' : 'key-label')">
<SvgIcon :size="15" name="folder-opened" />
</span>
<span v-if="data.type == 1" class="ml5" style="font-weight: bold">
{{ node.label }}
</span>
<span v-if="data.type == 2" class="ml5" style="color: #67c23a">
{{ node.label }} {{ node.label }}
</span> </span>
<span v-if="!node.isLeaf" class="ml5" style="font-weight: bold"> ({{ data.keyCount }}) </span> <span v-if="!node.isLeaf" class="ml5" style="font-weight: bold"> ({{ data.keyCount }}) </span>
</span> </span>
<template #dropdown v-if="data.type == 2">
<el-dropdown-menu>
<el-dropdown-item @click="showKeyDetail(data.key, true)">
<el-link type="primary" icon="plus" :underline="false" style="margin-left: 2px">新tab打开</el-link>
</el-dropdown-item>
<span v-auth="'redis:data:del'">
<el-dropdown-item @click="delKey(data.key)">
<el-link type="danger" icon="delete" :underline="false" style="margin-left: 2px">删除</el-link>
</el-dropdown-item>
</span>
</el-dropdown-menu>
</template>
</el-dropdown>
</span>
</template> </template>
</el-tree> </el-tree>
<!-- right context menu -->
<div ref="rightMenuRef" class="key-list-right-menu">
<!-- folder right menu -->
<div v-if="!state.rightClickNode?.isLeaf"></div>
<!-- key right menu -->
<div v-else>
<el-row>
<el-link @click="showKeyDetail(state.rightClickNode.key, true)" type="primary" icon="plus" :underline="false"
>新tab打开</el-link
>
</el-row>
<el-row class="mt5">
<el-link @click="delKey(state.rightClickNode.key)" v-auth="'redis:data:del'" type="danger" icon="delete" :underline="false"
>删除</el-link
>
</el-row>
</div>
</div>
</div> </div>
</el-col> </el-col>
@@ -204,6 +201,7 @@ const treeProps = {
const defaultCount = 250; const defaultCount = 250;
const keyTreeRef: any = ref(null); const keyTreeRef: any = ref(null);
const rightMenuRef: any = ref(null);
const state = reactive({ const state = reactive({
tags: [], tags: [],
@@ -217,6 +215,7 @@ const state = reactive({
keyTreeExpanded: new Set(), keyTreeExpanded: new Set(),
activeName: '', activeName: '',
dataTabs: {} as any, dataTabs: {} as any,
rightClickNode: {} as any,
scanParam: { scanParam: {
id: null as any, id: null as any,
mode: '', mode: '',
@@ -407,6 +406,7 @@ const expandAllKeyNode = (nodes: any) => {
}; };
const handleKeyTreeNodeClick = async (data: any) => { const handleKeyTreeNodeClick = async (data: any) => {
hideAllMenus();
// 目录则不做处理 // 目录则不做处理
if (data.type == 1) { if (data.type == 1) {
return; return;
@@ -478,6 +478,43 @@ const keyTreeNodeCollapse = (data: any, node: any, component: any) => {
state.keyTreeExpanded.delete(data.key); state.keyTreeExpanded.delete(data.key);
}; };
const rightClickNode = (event: any, data: any, node: any) => {
hideAllMenus();
keyTreeRef.value.setCurrentKey(node.key);
state.rightClickNode = node;
// nextTick for dom render
nextTick(() => {
let top = event.clientY;
const menu = rightMenuRef.value;
menu.style.display = 'block';
// position in bottom
if (document.body.clientHeight - top < menu.clientHeight) {
top -= menu.clientHeight;
}
menu.style.left = `${event.clientX}px`;
menu.style.top = `${top}px`;
document.addEventListener('click', hideAllMenus, { once: true });
});
};
const hideAllMenus = () => {
let menus: any = document.querySelectorAll('.key-list-right-menu');
if (menus.length === 0) {
return;
}
state.rightClickNode = null;
for (const menu of menus) {
menu.style.display = 'none';
}
};
const searchKey = async () => { const searchKey = async () => {
state.scanParam.cursor = {}; state.scanParam.cursor = {};
await scan(false); await scan(false);
@@ -596,6 +633,14 @@ const delKey = (key: string) => {
height: calc(100vh - 250px); height: calc(100vh - 250px);
} }
.key-list-vtree .folder-label {
font-weight: bold;
}
.key-list-vtree .key-label {
color: #67c23a;
}
.key-list-vtree .key-list-custom-node { .key-list-vtree .key-list-custom-node {
width: 100%; width: 100%;
overflow: hidden; overflow: hidden;
@@ -604,4 +649,21 @@ const delKey = (key: string) => {
height: 22px; height: 22px;
line-height: 22px; line-height: 22px;
} }
/* right menu style start */
.key-list-right-menu {
display: none;
position: fixed;
top: 0;
left: 0;
padding: 5px;
z-index: 99999;
overflow: hidden;
border-radius: 3px;
border: 2px solid lightgrey;
background: #fafafa;
}
.dark-mode .key-list-right-menu {
background: #263238;
}
</style> </style>