Commit 90452495 authored by Data Governance Dev's avatar Data Governance Dev

feat(web3): Schema/选择数据表 改 el-select filterable 模糊搜索

之前用原生 <select> 无法过滤,schema 列表动辄几十个、数据表几百张,用户找目标很不方便。
改成 Element Plus <el-select filterable>,内置模糊搜索 + clearable 一键清空。

- Schema:filterable + 动态 placeholder(无数据时显示「请先测试连接」)
- 选择数据表:filterable + label 带表注释「表名(注释)」格式 + 三态 placeholder

实测:tests/test_filterable_select.py 跑 Playwright,输入 "house" 后下拉只剩匹配项 c_house_data,其他 5 个 schema 被过滤,匹配文字高亮。
parent 85dfa83f
...@@ -134,20 +134,39 @@ ...@@ -134,20 +134,39 @@
<div class="form-row"> <div class="form-row">
<span class="form-label"><span class="required">*</span>Schema</span> <span class="form-label"><span class="required">*</span>Schema</span>
<div class="form-control"> <div class="form-control">
<select class="select-input" v-model="form.schema" :disabled="schemaOptions.length === 0"> <el-select
<option v-if="schemaOptions.length === 0" value="" disabled>请先测试连接</option> v-model="form.schema"
<option v-for="s in schemaOptions" :key="s" :value="s">{{ s }}</option> filterable
</select> clearable
:loading="loadingSchema"
:disabled="loadingSchema || schemaOptions.length === 0"
:placeholder="schemaOptions.length === 0 ? '请先测试连接' : '请选择 schema(可输入关键字过滤)'"
style="width: 100%;"
>
<el-option v-for="s in schemaOptions" :key="s" :label="s" :value="s" />
</el-select>
<div v-if="loadingSchema" class="form-hint">正在加载 schema 列表...</div> <div v-if="loadingSchema" class="form-hint">正在加载 schema 列表...</div>
</div> </div>
</div> </div>
<div class="form-row"> <div class="form-row">
<span class="form-label"><span class="required">*</span>选择数据表</span> <span class="form-label"><span class="required">*</span>选择数据表</span>
<div class="form-control"> <div class="form-control">
<select class="select-input" v-model="form.table" :disabled="tableOptions.length === 0"> <el-select
<option v-if="tableOptions.length === 0" value="" disabled>{{ form.schema ? '请选择数据表' : '请先选择 schema' }}</option> v-model="form.table"
<option v-for="t in tableOptions" :key="t.table_name" :value="t.table_name">{{ t.table_name }}{{ t.table_comment ? `(${t.table_comment})` : '' }}</option> filterable
</select> clearable
:loading="loadingTable"
:disabled="loadingTable || tableOptions.length === 0"
:placeholder="!form.schema ? '请先选择 schema' : (tableOptions.length === 0 ? '请选择数据表' : '请选择数据表(可输入表名/注释关键字过滤)')"
style="width: 100%;"
>
<el-option
v-for="t in tableOptions"
:key="t.table_name"
:label="t.table_name + (t.table_comment ? `(${t.table_comment})` : '')"
:value="t.table_name"
/>
</el-select>
<div v-if="loadingTable" class="form-hint">正在加载表列表...</div> <div v-if="loadingTable" class="form-hint">正在加载表列表...</div>
<div v-else class="form-hint">选择数据表后自动加载该表全部字段</div> <div v-else class="form-hint">选择数据表后自动加载该表全部字段</div>
</div> </div>
......
"""Inject fake schema/table data via Vue setupState to test filterable behavior."""
import sys, time
from playwright.sync_api import sync_playwright
CHROME = r"C:\Users\duanqi\scoop\apps\googlechrome\current\chrome.exe"
URL = "http://localhost:5175/#/config"
with sync_playwright() as p:
browser = p.chromium.launch(executable_path=CHROME, args=["--no-sandbox"])
ctx = browser.new_context(viewport={"width": 1440, "height": 900})
page = ctx.new_page()
page.goto(URL)
page.wait_for_load_state("networkidle")
# Open modal & Tab2
page.locator("button:has-text('新增任务')").first.click()
time.sleep(0.4)
page.locator(".modal-tab:has-text('数据连接')").click()
time.sleep(0.4)
# Try to access Vue's setup state by walking up from the modal-mask
# In Vue 3, component instances are exposed on __vueParentComponent
inject_result = page.evaluate("""() => {
// Walk DOM to find a Vue component that has BOTH schemaOptions AND tableOptions (TaskModal)
let taskModalState = null;
function walk(el) {
if (!el || taskModalState) return;
const inst = el.__vueParentComponent;
if (inst && inst.setupState && 'schemaOptions' in inst.setupState && 'tableOptions' in inst.setupState) {
taskModalState = inst.setupState;
return;
}
// walk into children that might have parent components (VNode roots)
for (const child of el.children) walk(child);
walk(el.parentElement);
}
walk(document.querySelector('.modal-mask.show .modal'));
if (!taskModalState) return 'no TaskModal with both schemaOptions+tableOptions';
// Reset loading flags to false (in case walk found a loading sub-component)
if ('loadingSchema' in taskModalState) taskModalState.loadingSchema = false;
if ('loadingTable' in taskModalState) taskModalState.loadingTable = false;
taskModalState.schemaOptions = [
'smart_build', 'c_bri_xzqh', 'c_house_data', 'public', 'information_schema', 'mysql',
];
taskModalState.tableOptions = [
{ table_name: 't_user_info', table_comment: '用户信息表' },
{ table_name: 't_user_address', table_comment: '用户地址表' },
{ table_name: 't_order_master', table_comment: '订单主表' },
{ table_name: 't_order_detail', table_comment: '订单明细' },
{ table_name: 't_product_info', table_comment: null },
];
taskModalState.form.schema = 'c_house_data';
return {
found: true,
schemaCount: taskModalState.schemaOptions.length,
tableCount: taskModalState.tableOptions.length,
};
}""")
print("=== inject result ===")
print(inject_result)
time.sleep(0.5)
# Now both selects should be enabled with filterable input
info = page.evaluate("""() => {
const sels = Array.from(document.querySelectorAll('.modal-mask.show .modal .el-select'));
return sels.map(s => {
const inp = s.querySelector('input');
return {
disabled: inp ? inp.disabled : null,
placeholder: inp ? inp.getAttribute('placeholder') : null,
currentValue: s.querySelector('.el-select__placeholder')?.textContent || null,
renderedValue: s.querySelector('.el-select__selected-item')?.textContent || null,
};
});
}""")
print("=== el-select state after inject ===")
for i, s in enumerate(info):
print(f" #{i}: {s}")
# Debug: inspect why table disabled
dbg = page.evaluate("""() => {
const sels = Array.from(document.querySelectorAll('.modal-mask.show .modal .el-select'));
let taskModalState = null;
for (const sel of sels) {
let el = sel;
while (el) {
const inst = el.__vueParentComponent;
if (inst && inst.setupState && 'schemaOptions' in inst.setupState) {
taskModalState = inst.setupState;
break;
}
el = el.parentElement;
}
if (taskModalState) break;
}
return {
schemaLen: taskModalState?.schemaOptions?.length,
schemaIsArr: Array.isArray(taskModalState?.schemaOptions),
tableLen: taskModalState?.tableOptions?.length,
tableIsArr: Array.isArray(taskModalState?.tableOptions),
loadingTable: taskModalState?.loadingTable,
};
}""")
print(f"\n=== debug ===\n{dbg}")
# Screenshot of full data table section with injected data
data_section = page.locator(".modal-mask.show .modal .form-section").filter(has_text="数据表")
data_section.first.screenshot(path=r"C:\Users\duanqi\Desktop\tools\数据治理\web3\tests\filterable_with_data.png")
# Now click into schema select and type to test filter
schema_input = page.locator(".modal-mask.show .modal .form-section").filter(has_text="数据表").locator(".el-select").first.locator("input")
schema_input.click()
time.sleep(0.4)
schema_input.fill("house")
time.sleep(0.8)
# Take screenshot of full modal showing dropdown
page.screenshot(path=r"C:\Users\duanqi\Desktop\tools\数据治理\web3\tests\filterable_schema_search.png", full_page=False)
# Get dropdown items visible
items = page.evaluate("""() => {
const dropdowns = Array.from(document.querySelectorAll('.el-select-dropdown'));
const visible = dropdowns.find(d => getComputedStyle(d).display !== 'none' && d.querySelectorAll('.el-option').length > 0);
if (!visible) return 'no dropdown visible';
return Array.from(visible.querySelectorAll('.el-option')).map(o => o.textContent.trim());
}""")
print(f"\n=== schema dropdown after typing 'house' ===")
print(items)
# Take a screenshot of the dropdown
page.screenshot(path=r"C:\Users\duanqi\Desktop\tools\数据治理\web3\tests\filterable_schema_search.png", full_page=False)
browser.close()
print("done")
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment