Skip to content

Commit c31c66d

Browse files
authored
feat(tables): add menu button for tables metadata and columns (#457)
* feat(tables): add menu button for metadata * refactor(tables): menu button for columns
1 parent 47b859d commit c31c66d

File tree

10 files changed

+306
-109
lines changed

10 files changed

+306
-109
lines changed

src/api/editor.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@ const makeSqlData = (sql: string) => {
2020
})
2121
}
2222

23-
const addDatabaseParams = () => {
23+
const addDatabaseParams = (database?: string) => {
2424
const appStore = useAppStore()
2525
return {
2626
params: {
27-
db: appStore.database,
27+
db: database || appStore.database,
2828
},
2929
} as AxiosRequestConfig
3030
}
@@ -100,8 +100,8 @@ const getTableByName = (tableName: string, database?: string) => {
100100
)
101101
}
102102

103-
const runSQL = (code: string): Promise<HttpResponse> => {
104-
return axios.post(sqlUrl, makeSqlData(code), addDatabaseParams())
103+
const runSQL = (code: string, database?: string): Promise<HttpResponse> => {
104+
return axios.post(sqlUrl, makeSqlData(code), addDatabaseParams(database))
105105
}
106106

107107
const checkScriptsTable = () => {

src/assets/style/global.less

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,10 @@ body {
431431
text-transform: uppercase;
432432
}
433433

434+
.rotate-90 {
435+
transform: rotate(90deg);
436+
}
437+
434438
.rotate-270 {
435439
transform: rotate(270deg);
436440
}

src/assets/style/select.less

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -57,18 +57,13 @@
5757

5858
.arco-dropdown {
5959
padding: 4px 0;
60-
border: 0;
6160
border-radius: 4px;
6261
box-shadow: 0 2px 10px 0 var(--box-shadow-color);
6362
.arco-dropdown-option {
6463
font-size: 13px;
6564
}
6665
}
6766

68-
.arco-dropdown-option:not(.arco-dropdown-option-disabled):hover {
69-
background-color: transparent;
70-
}
71-
7267
.arco-dropdown-option-content {
7368
width: 100%;
7469
height: 32px;

src/components/short-cut/index.vue

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
<template lang="pug">
2-
a-tooltip(:content="codeInfo.code")
2+
a-tooltip(position="right" :content="codeInfo.code")
33
a-button(
44
type="text"
55
size="small"
@@ -8,8 +8,8 @@ a-tooltip(:content="codeInfo.code")
88
@click="clickShortCut(codeInfo.code, codeInfo.cursorPosition)"
99
@mouseenter="hoverQuickSelect(node)"
1010
) {{ label }}
11-
template(v-if="label === ''" #icon)
12-
svg.icon-16.icon-color
11+
template(v-if="icon" #icon)
12+
svg.icon-16.icon-color.query-icon
1313
use(href="#query")
1414
</template>
1515

@@ -23,6 +23,7 @@ a-tooltip(:content="codeInfo.code")
2323
node: TreeChild
2424
parent: TableTreeParent
2525
database: string
26+
icon?: boolean
2627
}>()
2728
2829
const { inputFromNewLineToQueryCode } = useQueryCode()
@@ -57,37 +58,39 @@ a-tooltip(:content="codeInfo.code")
5758
switch (type) {
5859
case 'select*100':
5960
return {
60-
code: formatter(`SELECT * FROM "${parent.title}"${orderBy}DESC LIMIT 100;`),
61+
code: formatter(`SELECT * FROM ${props.database}."${parent.title}"${orderBy}DESC LIMIT 100;`),
6162
cursorPosition: 0,
6263
}
6364
case 'count':
6465
return {
65-
code: formatter(`SELECT count(*) FROM "${parent.title}" GROUP BY ${node.title};`),
66+
code: formatter(`SELECT count(*) FROM ${props.database}."${parent.title}" GROUP BY ${node.title};`),
6667
cursorPosition: 0,
6768
}
6869
case 'where=':
6970
return {
70-
code: formatter(`SELECT * FROM "${parent.title}" WHERE ${node.title} = ${orderBy}DESC;`),
71+
code: formatter(`SELECT * FROM ${props.database}."${parent.title}" WHERE ${node.title} = ${orderBy}DESC;`),
7172
cursorPosition: `${orderBy}DESC;`.length,
7273
}
7374
case 'select100':
7475
return {
75-
code: formatter(`SELECT ${node.title} FROM "${parent.title}"${orderBy}DESC LIMIT 100;`),
76+
code: formatter(`SELECT ${node.title} FROM ${props.database}."${parent.title}"${orderBy}DESC LIMIT 100;`),
7677
cursorPosition: 0,
7778
}
7879
case 'max':
7980
return {
80-
code: formatter(`SELECT max(${node.title}) FROM "${parent.title}";`),
81+
code: formatter(`SELECT max(${node.title}) FROM ${props.database}."${parent.title}";`),
8182
cursorPosition: 0,
8283
}
8384
case 'min':
8485
return {
85-
code: formatter(`SELECT min(${node.title}) FROM "${parent.title}";`),
86+
code: formatter(`SELECT min(${node.title}) FROM ${props.database}."${parent.title}";`),
8687
cursorPosition: 0,
8788
}
8889
case 'where<':
8990
return {
90-
code: formatter(`SELECT * FROM "${parent.title}" WHERE ${parent.timeIndexName} < ${orderBy}DESC LIMIT 100;`),
91+
code: formatter(
92+
`SELECT * FROM ${props.database}."${parent.title}" WHERE ${parent.timeIndexName} < ${orderBy}DESC LIMIT 100;`
93+
),
9194
cursorPosition: `${orderBy}DESC LIMIT 100;`.length,
9295
}
9396
default:
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
<template lang="pug">
2+
a-dropdown.tables(trigger="click" position="right")
3+
a-button.menu-button(type="text" @click="(event) => clickMenu(event, nodeData)")
4+
template(#icon)
5+
svg.icon-14.rotate-90
6+
use(href="#extra")
7+
template(#content)
8+
a-doption(
9+
v-if="!isColumn"
10+
click.stop
11+
:class="nodeData.childrenType === 'columns' && expandedKeys?.includes(nodeData.key) ? 'selected' : ''"
12+
@click="(event) => expandChildren(event, nodeData, 'columns')"
13+
)
14+
template(#icon)
15+
svg.icon
16+
use(href="#columns")
17+
| {{ $t('dashboard.columns') }}
18+
a-tooltip(position="right" :content="$t('dashboard.hints.details')")
19+
a-doption(
20+
v-if="!isColumn"
21+
:class="nodeData.childrenType === 'details' && expandedKeys?.includes(nodeData.key) ? 'selected' : ''"
22+
@click="(event) => expandChildren(event, nodeData, 'details')"
23+
)
24+
template(#icon)
25+
svg.icon
26+
use(href="#details")
27+
| {{ $t('dashboard.details') }}
28+
a-doption(v-if="!isColumn" click.stop)
29+
a-space(v-for="item of SHORTCUT_MAP['TABLE']")
30+
ShortCut(
31+
icon
32+
:type="item.value"
33+
:node="nodeData"
34+
:parent="nodeData.iconType ? expandedTablesTree[nodeData.parentKey] : nodeData"
35+
:label="'Query table'"
36+
:database="database"
37+
)
38+
a-doption(v-else)
39+
a-dropdown.quick-select(trigger="hover" position="right")
40+
a-space(:size="6")
41+
svg.icon.icon-color
42+
use(href="#query")
43+
| {{ $t('dashboard.quickSelect') }}
44+
template(#content)
45+
a-doption(v-for="item of SHORTCUT_MAP[nodeData.iconType]")
46+
ShortCut(
47+
:type="item.value"
48+
:node="nodeData"
49+
:parent="nodeData.iconType ? expandedTablesTree[nodeData.parentKey] : nodeData"
50+
:label="item.label"
51+
:database="database"
52+
)
53+
a-tooltip(content="Copy to Clipboard" position="right")
54+
a-doption(@click="copy(nodeData.title)")
55+
template(#icon)
56+
svg.icon.icon-color(v-if="copied === false")
57+
use(href="#copy-new")
58+
svg.icon(v-else)
59+
icon-check.success-color
60+
| Copy name
61+
</template>
62+
63+
<script lang="ts" setup name="TableMenu">
64+
import type { TableTreeParent } from '@/store/modules/database/types'
65+
import type { OptionsType } from '@/types/global'
66+
import { useClipboard } from '@vueuse/core'
67+
68+
const props = defineProps<{
69+
nodeData: TableTreeParent
70+
expandedKeys?: number[]
71+
expandedTablesTree?: TableTreeParent[]
72+
database: string
73+
isColumn?: boolean
74+
}>()
75+
const emits = defineEmits(['expandChildren'])
76+
const source = ref('')
77+
const { copy, copied } = useClipboard({ source })
78+
79+
const expandChildren = (event: MouseEvent, nodeData: TableTreeParent, childrenType: string) => {
80+
event.stopPropagation()
81+
emits('expandChildren', event, nodeData, childrenType)
82+
}
83+
84+
const SHORTCUT_MAP: { [key: string]: OptionsType[] } = {
85+
TABLE: [{ value: 'select*100', label: 'Query table' }],
86+
FIELD: [
87+
{ value: 'select100', label: 'Query column' },
88+
{
89+
value: 'max',
90+
label: 'Query max',
91+
},
92+
{
93+
value: 'min',
94+
label: 'Query min',
95+
},
96+
],
97+
TAG: [
98+
{ value: 'count', label: 'Count by' },
99+
{ value: 'where=', label: 'Filter by' },
100+
],
101+
TIMESTAMP: [
102+
{ value: 'select*100', label: 'Query table' },
103+
{
104+
value: 'where<',
105+
label: 'Filter by',
106+
},
107+
],
108+
}
109+
110+
const clickMenu = (event: MouseEvent, nodeData: TableTreeParent) => {
111+
if (props.expandedKeys.includes(nodeData.key)) {
112+
event.stopPropagation()
113+
}
114+
}
115+
</script>
116+
117+
<style lang="less" scoped>
118+
:deep(.arco-btn-text[type='button']) {
119+
color: var(--main-font-color);
120+
border-radius: 0;
121+
width: 100%;
122+
justify-content: flex-start;
123+
}
124+
125+
.menu-button {
126+
display: none;
127+
}
128+
129+
.arco-btn.arco-dropdown-open {
130+
display: flex;
131+
background: var(--list-hover-color);
132+
}
133+
134+
.icon-14 {
135+
width: 14px;
136+
height: 14px;
137+
}
138+
139+
:deep(.query-icon) {
140+
width: 14px;
141+
height: 14px;
142+
}
143+
</style>
144+
145+
<style lang="less">
146+
.tables {
147+
.arco-dropdown {
148+
.arco-dropdown-option {
149+
&:not(.arco-dropdown-option-disabled, .arco-dropdown-option-has-suffix):hover {
150+
background-color: var(--list-hover-color);
151+
}
152+
.arco-dropdown-option-content {
153+
.arco-btn-text[type='button'] {
154+
padding: 0 16px 0 0;
155+
svg {
156+
color: var(--small-font-color);
157+
}
158+
&:hover {
159+
background: transparent;
160+
}
161+
}
162+
}
163+
&.arco-dropdown-option-has-suffix {
164+
padding: 0;
165+
&:hover {
166+
background: transparent;
167+
}
168+
}
169+
}
170+
.arco-dropdown-option.selected {
171+
color: var(--brand-color);
172+
svg {
173+
color: var(--brand-font-color);
174+
}
175+
}
176+
}
177+
}
178+
</style>

src/hooks/sider-tabs.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -85,8 +85,7 @@ export default function useSiderTabs() {
8585
})
8686
})
8787

88-
// Deprecated.
89-
const loadMoreDetails = async (nodeData: TableTreeParent, isSilent?: boolean) => {
88+
const loadMoreDetails = async (nodeData: TableTreeParent, isSilent?: boolean, database?: string) => {
9089
isRefreshingDetails.value[nodeData.key] = true
9190
const createTable = new Promise<object>((resolve, reject) => {
9291
if (nodeData.tableType !== 'BASE TABLE') {
@@ -97,7 +96,7 @@ export default function useSiderTabs() {
9796
return
9897
}
9998
editorAPI
100-
.runSQL(`show create table "${nodeData.title}"`)
99+
.runSQL(`show create table "${nodeData.title}"`, database)
101100
.then((res: any) => {
102101
const sql = `${res.output[0].records.rows[0][1]}`
103102
const regex = /ttl = '(\w)+'/g
@@ -123,7 +122,8 @@ export default function useSiderTabs() {
123122
.runSQL(
124123
nodeData.timeIndexName !== '%TIMESTAMP%'
125124
? `select count(*), min (${nodeData.timeIndexName}), max (${nodeData.timeIndexName}) from "${nodeData.title}"`
126-
: `select count(*) from "${nodeData.title}"`
125+
: `select count(*) from "${nodeData.title}"`,
126+
database
127127
)
128128
.then((res: any) => {
129129
const resArray = res.output[0].records.rows[0]
@@ -143,7 +143,7 @@ export default function useSiderTabs() {
143143
})
144144
}
145145
if (!nodeData.timeIndexName) {
146-
loadMoreColumns(nodeData).then(() => getRowAndTime())
146+
loadMoreColumns(nodeData, false, database).then(() => getRowAndTime())
147147
} else {
148148
getRowAndTime()
149149
}
@@ -171,15 +171,15 @@ export default function useSiderTabs() {
171171
info: createTableResult.value,
172172
class: 'details',
173173
})
174-
addChildren(nodeData.key, children, nodeData.timeIndexName, 'details', isSilent)
174+
addChildren(nodeData.key, children, nodeData.timeIndexName, 'details', isSilent, database)
175175
isRefreshingDetails.value[nodeData.key] = false
176176
return children
177177
}
178178

179179
const loadMore = async (nodeData: TableTreeParent) => {
180-
// if (nodeData.childrenType === 'details') {
181-
// return loadMoreDetails(nodeData)
182-
// }
180+
if (nodeData.childrenType === 'details') {
181+
return loadMoreDetails(nodeData, false, databaseActiveKeys.value[0])
182+
}
183183
return loadMoreColumns(nodeData, false, databaseActiveKeys.value[0])
184184
}
185185

src/locale/en-US/dashboard.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ export default {
6464
'dashboard.clearCode': 'Clear code',
6565
'dashboard.refresh': 'Refresh',
6666
'dashboard.hints.columns': 'View column name and type, create quick query from any column.',
67-
'dashboard.hints.details': 'View metadata of the table.',
67+
'dashboard.hints.details': 'Metadata of the table',
6868
'dashboard.hints.quickSelect': 'Shortcut for generating SQL query for the table.',
6969
'dashboard.hints.copy': 'Copy table name to clipboard.',
7070
}

src/store/modules/app/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ const useAppStore = defineStore('app', {
4949
},
5050

5151
async login(form: Partial<AppState>) {
52+
// TODO: add a save and test button
5253
try {
5354
this.updateSettings(form)
5455
// check if settings are valid

src/store/modules/code-run/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ const useCodeRunStore = defineStore('codeRun', () => {
3737
}
3838

3939
const API_MAP: AnyObject = {
40-
sql: editorAPI.runSQL,
40+
sql: (code: string, params: PromForm) => editorAPI.runSQL(code),
4141
python: editorAPI.runScript,
4242
promql: editorAPI.runPromQL,
4343
}

0 commit comments

Comments
 (0)