diff --git a/web/public/help-img/db.webp b/web/public/help-img/db.webp
index 7f5e795..642cf8b 100644
Binary files a/web/public/help-img/db.webp and b/web/public/help-img/db.webp differ
diff --git a/web/src/database/ObjectExplorer.vue b/web/src/database/ObjectExplorer.vue
index d52f8f8..d571788 100644
--- a/web/src/database/ObjectExplorer.vue
+++ b/web/src/database/ObjectExplorer.vue
@@ -87,12 +87,16 @@ function columnsTip(table: ExplorerTable) {
{{ collapsed.has(db.name) ? '▸' : '▾' }}
+
{{ db.name }}
{{ db.tables.length }} 张表
-
+
+
{{ table.name }}
+ 新建表
-
+
@@ -171,7 +179,7 @@ function columnsTip(table: ExplorerTable) {
}
.db + .db {
- margin-top: 4px;
+ margin-top: 8px;
}
.row {
@@ -206,18 +214,47 @@ function columnsTip(table: ExplorerTable) {
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 {
font-size: 12px;
color: #9ca3af;
}
-.table-row,
-.new-table {
- padding-left: 26px;
-}
-
.new-table {
color: #2563eb;
+ padding-left: 30px;
}
/* ⋯ 平时浅一点,鼠标移到这一行再变深 */
diff --git a/web/src/database/completion.test.ts b/web/src/database/completion.test.ts
index afbf8b8..4761c6b 100644
--- a/web/src/database/completion.test.ts
+++ b/web/src/database/completion.test.ts
@@ -44,9 +44,12 @@ describe('查询窗口的自动补全', () => {
expect(sorted('SELECT * FROM 用户表, |')[0]).toBe('CampusEnvironment')
})
- test('语句里用到的表的列排在其他列前面', () => {
- const columns = sorted('SELECT | FROM 用户表').filter((label) => ['姓名', 'light'].includes(label))
- expect(columns).toEqual(['姓名', 'light'])
+ test('写了表名后只列用到的表的列,还没写时列所有表的列', () => {
+ expect(labels('SELECT | FROM 用户表')).toContain('姓名')
+ 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('空格触发:只在要写表名的地方弹出', () => {
diff --git a/web/src/database/completion.ts b/web/src/database/completion.ts
index 2b46c6f..e3aa211 100644
--- a/web/src/database/completion.ts
+++ b/web/src/database/completion.ts
@@ -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 }))
}
-/** 所有表的列(同名的列只列一次);语句里用到的表的列排在前面 */
+/** 查询窗口里用到的表(FROM / JOIN 等后面写了的)的列;还没写表名时列所有表的列。同名的列只列一次 */
function columnSuggestions(tables: ExplorerTable[], refs: TableRef[], bracketOpen: boolean): SqlSuggestion[] {
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()
- 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) {
const key = column.name.toUpperCase()
const existing = columns.get(key)
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 }) => ({
diff --git a/web/src/designer/FormDesigner.vue b/web/src/designer/FormDesigner.vue
index 913a6cb..c921020 100644
--- a/web/src/designer/FormDesigner.vue
+++ b/web/src/designer/FormDesigner.vue
@@ -677,7 +677,9 @@ const noop = () => {}
}
.actions {
+ container-type: inline-size;
display: flex;
+ flex-wrap: wrap;
align-items: center;
gap: 6px;
font-size: 12px;
@@ -713,6 +715,8 @@ const noop = () => {}
}
.tips {
+ /* 提示文字不占位置:画布窄时不显示(只留作空白把缩放推到右边),按钮放不下再折到第二行,不撑出横向滚动条 */
+ flex: 1 1 0;
min-width: 0;
margin-left: 8px;
overflow: hidden;
@@ -721,6 +725,12 @@ const noop = () => {}
color: #7a838e;
}
+@container (max-width: 560px) {
+ .tips {
+ visibility: hidden;
+ }
+}
+
.window {
align-self: flex-start;
border: 1px solid #9aa3ad;
diff --git a/web/src/pages/DatabasePage.vue b/web/src/pages/DatabasePage.vue
index f26e4d5..45dcfd7 100644
--- a/web/src/pages/DatabasePage.vue
+++ b/web/src/pages/DatabasePage.vue
@@ -296,13 +296,10 @@ onMounted(refresh)
flex-direction: column;
}
-.tabs :deep(.n-tabs-pane-wrapper) {
+/* 标签页内容直接是 .tabs 的子元素(display-directive="show"):占标签栏下面剩下的高度,不能用 height: 100%,否则多出标签栏的高度,整页出滚动条 */
+.tabs :deep(.n-tab-pane) {
flex: 1;
min-height: 0;
-}
-
-.tabs :deep(.n-tab-pane) {
- height: 100%;
padding: 0;
background: #fff;
}