数据库页面:对象资源管理器加库和表的图标,表缩进挂在库下面(左边竖线),一眼分得清层级;标签页内容改成占标签栏下面剩下的高度,不再多出 39px 撑出整页滚动条;查询补全写了 FROM / JOIN 后只列用到的表的列。设计器画布窄时操作栏折行、不显示提示文字,不再撑出横向滚动条。使用说明的数据库截图同步
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
f9a2739353
commit
e177753ad6
6 files changed
+66
-20
No files matched your search
Binary file not shown.
|
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 27 KiB |
@@ -87,12 +87,16 @@ function columnsTip(table: ExplorerTable) {
|
|||||||
<div v-for="db in databases" :key="db.name" class="db">
|
<div v-for="db in databases" :key="db.name" class="db">
|
||||||
<div class="row db-row" @click="toggle(db.name)" @contextmenu="databaseMenu($event, db.name)">
|
<div class="row db-row" @click="toggle(db.name)" @contextmenu="databaseMenu($event, db.name)">
|
||||||
<span class="arrow">{{ collapsed.has(db.name) ? '▸' : '▾' }}</span>
|
<span class="arrow">{{ collapsed.has(db.name) ? '▸' : '▾' }}</span>
|
||||||
|
<svg class="icon db-icon" viewBox="0 0 16 16" aria-hidden="true">
|
||||||
|
<ellipse cx="8" cy="3.5" rx="5.5" ry="2" />
|
||||||
|
<path d="M2.5 3.5v9c0 1.1 2.5 2 5.5 2s5.5-.9 5.5-2v-9M2.5 8c0 1.1 2.5 2 5.5 2s5.5-.9 5.5-2" />
|
||||||
|
</svg>
|
||||||
<span class="name">{{ db.name }}</span>
|
<span class="name">{{ db.name }}</span>
|
||||||
<span class="count">{{ db.tables.length }} 张表</span>
|
<span class="count">{{ db.tables.length }} 张表</span>
|
||||||
<button class="more" title="更多操作" @click="databaseMenu($event, db.name)">⋯</button>
|
<button class="more" title="更多操作" @click="databaseMenu($event, db.name)">⋯</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<template v-if="!collapsed.has(db.name)">
|
<div v-if="!collapsed.has(db.name)" class="tables">
|
||||||
<div
|
<div
|
||||||
v-for="table in db.tables"
|
v-for="table in db.tables"
|
||||||
:key="table.name"
|
:key="table.name"
|
||||||
@@ -101,11 +105,15 @@ function columnsTip(table: ExplorerTable) {
|
|||||||
@click="emit('action', { kind: 'editRows', database: db.name, table })"
|
@click="emit('action', { kind: 'editRows', database: db.name, table })"
|
||||||
@contextmenu="tableMenu($event, db.name, table)"
|
@contextmenu="tableMenu($event, db.name, table)"
|
||||||
>
|
>
|
||||||
|
<svg class="icon table-icon" viewBox="0 0 16 16" aria-hidden="true">
|
||||||
|
<rect x="2" y="3" width="12" height="10" rx="1.5" />
|
||||||
|
<path d="M2 6.5h12M6.5 6.5V13" />
|
||||||
|
</svg>
|
||||||
<span class="name">{{ table.name }}</span>
|
<span class="name">{{ table.name }}</span>
|
||||||
<button class="more" title="更多操作" @click="tableMenu($event, db.name, table)">⋯</button>
|
<button class="more" title="更多操作" @click="tableMenu($event, db.name, table)">⋯</button>
|
||||||
</div>
|
</div>
|
||||||
<div class="row new-table" @click="emit('action', { kind: 'newTable', database: db.name })">+ 新建表</div>
|
<div class="row new-table" @click="emit('action', { kind: 'newTable', database: db.name })">+ 新建表</div>
|
||||||
</template>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -171,7 +179,7 @@ function columnsTip(table: ExplorerTable) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.db + .db {
|
.db + .db {
|
||||||
margin-top: 4px;
|
margin-top: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.row {
|
.row {
|
||||||
@@ -206,18 +214,47 @@ function columnsTip(table: ExplorerTable) {
|
|||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.icon {
|
||||||
|
flex: none;
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
fill: none;
|
||||||
|
stroke-width: 1.3;
|
||||||
|
stroke-linecap: round;
|
||||||
|
stroke-linejoin: round;
|
||||||
|
}
|
||||||
|
|
||||||
|
.db-icon {
|
||||||
|
stroke: #2563eb;
|
||||||
|
}
|
||||||
|
|
||||||
|
.db-icon ellipse {
|
||||||
|
fill: #dbeafe;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-icon {
|
||||||
|
stroke: #6b7280;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-icon rect {
|
||||||
|
fill: #f9fafb;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 表挂在库下面:往里缩进,左边一条竖线对齐库图标 */
|
||||||
|
.tables {
|
||||||
|
margin-left: 33px;
|
||||||
|
padding-left: 6px;
|
||||||
|
border-left: 1px solid #e5e7eb;
|
||||||
|
}
|
||||||
|
|
||||||
.count {
|
.count {
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
color: #9ca3af;
|
color: #9ca3af;
|
||||||
}
|
}
|
||||||
|
|
||||||
.table-row,
|
|
||||||
.new-table {
|
|
||||||
padding-left: 26px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.new-table {
|
.new-table {
|
||||||
color: #2563eb;
|
color: #2563eb;
|
||||||
|
padding-left: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ⋯ 平时浅一点,鼠标移到这一行再变深 */
|
/* ⋯ 平时浅一点,鼠标移到这一行再变深 */
|
||||||
|
|||||||
@@ -44,9 +44,12 @@ describe('查询窗口的自动补全', () => {
|
|||||||
expect(sorted('SELECT * FROM 用户表, |')[0]).toBe('CampusEnvironment')
|
expect(sorted('SELECT * FROM 用户表, |')[0]).toBe('CampusEnvironment')
|
||||||
})
|
})
|
||||||
|
|
||||||
test('语句里用到的表的列排在其他列前面', () => {
|
test('写了表名后只列用到的表的列,还没写时列所有表的列', () => {
|
||||||
const columns = sorted('SELECT | FROM 用户表').filter((label) => ['姓名', 'light'].includes(label))
|
expect(labels('SELECT | FROM 用户表')).toContain('姓名')
|
||||||
expect(columns).toEqual(['姓名', 'light'])
|
expect(labels('SELECT | FROM 用户表')).not.toContain('light')
|
||||||
|
expect(labels('SELECT | FROM 用户表 u JOIN CampusEnvironment e ON u.ID = e.ID')).toContain('light')
|
||||||
|
expect(labels('SELECT |')).toContain('light')
|
||||||
|
expect(labels('SELECT |')).toContain('姓名')
|
||||||
})
|
})
|
||||||
|
|
||||||
test('空格触发:只在要写表名的地方弹出', () => {
|
test('空格触发:只在要写表名的地方弹出', () => {
|
||||||
|
|||||||
@@ -324,17 +324,16 @@ function tableSuggestions(tables: ExplorerTable[], bracketOpen: boolean, rank: s
|
|||||||
return tables.map((table) => ({ label: table.name, kind: 'table', insert: nameText(table.name, bracketOpen), detail: '表', sort: rank + table.name }))
|
return tables.map((table) => ({ label: table.name, kind: 'table', insert: nameText(table.name, bracketOpen), detail: '表', sort: rank + table.name }))
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 所有表的列(同名的列只列一次);语句里用到的表的列排在前面 */
|
/** 查询窗口里用到的表(FROM / JOIN 等后面写了的)的列;还没写表名时列所有表的列。同名的列只列一次 */
|
||||||
function columnSuggestions(tables: ExplorerTable[], refs: TableRef[], bracketOpen: boolean): SqlSuggestion[] {
|
function columnSuggestions(tables: ExplorerTable[], refs: TableRef[], bracketOpen: boolean): SqlSuggestion[] {
|
||||||
const used = new Set(refs.map((r) => r.table))
|
const used = new Set(refs.map((r) => r.table))
|
||||||
const ordered = [...tables.filter((t) => used.has(t)), ...tables.filter((t) => !used.has(t))]
|
|
||||||
const columns = new Map<string, { column: ExplorerTable['columns'][number]; tables: string[]; sort: string }>()
|
const columns = new Map<string, { column: ExplorerTable['columns'][number]; tables: string[]; sort: string }>()
|
||||||
for (const table of ordered) {
|
for (const table of used.size > 0 ? tables.filter((t) => used.has(t)) : tables) {
|
||||||
for (const column of table.columns) {
|
for (const column of table.columns) {
|
||||||
const key = column.name.toUpperCase()
|
const key = column.name.toUpperCase()
|
||||||
const existing = columns.get(key)
|
const existing = columns.get(key)
|
||||||
if (existing) existing.tables.push(table.name)
|
if (existing) existing.tables.push(table.name)
|
||||||
else columns.set(key, { column, tables: [table.name], sort: `${used.has(table) ? 1 : 2}${pad(columns.size)}` })
|
else columns.set(key, { column, tables: [table.name], sort: `1${pad(columns.size)}` })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return [...columns.values()].map(({ column, tables, sort }) => ({
|
return [...columns.values()].map(({ column, tables, sort }) => ({
|
||||||
|
|||||||
@@ -677,7 +677,9 @@ const noop = () => {}
|
|||||||
}
|
}
|
||||||
|
|
||||||
.actions {
|
.actions {
|
||||||
|
container-type: inline-size;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
gap: 6px;
|
gap: 6px;
|
||||||
font-size: 12px;
|
font-size: 12px;
|
||||||
@@ -713,6 +715,8 @@ const noop = () => {}
|
|||||||
}
|
}
|
||||||
|
|
||||||
.tips {
|
.tips {
|
||||||
|
/* 提示文字不占位置:画布窄时不显示(只留作空白把缩放推到右边),按钮放不下再折到第二行,不撑出横向滚动条 */
|
||||||
|
flex: 1 1 0;
|
||||||
min-width: 0;
|
min-width: 0;
|
||||||
margin-left: 8px;
|
margin-left: 8px;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
@@ -721,6 +725,12 @@ const noop = () => {}
|
|||||||
color: #7a838e;
|
color: #7a838e;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@container (max-width: 560px) {
|
||||||
|
.tips {
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.window {
|
.window {
|
||||||
align-self: flex-start;
|
align-self: flex-start;
|
||||||
border: 1px solid #9aa3ad;
|
border: 1px solid #9aa3ad;
|
||||||
|
|||||||
@@ -296,13 +296,10 @@ onMounted(refresh)
|
|||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
}
|
}
|
||||||
|
|
||||||
.tabs :deep(.n-tabs-pane-wrapper) {
|
/* 标签页内容直接是 .tabs 的子元素(display-directive="show"):占标签栏下面剩下的高度,不能用 height: 100%,否则多出标签栏的高度,整页出滚动条 */
|
||||||
|
.tabs :deep(.n-tab-pane) {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
}
|
|
||||||
|
|
||||||
.tabs :deep(.n-tab-pane) {
|
|
||||||
height: 100%;
|
|
||||||
padding: 0;
|
padding: 0;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user