diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 000000000..1c2a1e73d --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,19 @@ +# Repository Guidelines + +## Icons + +- Prefer Lucide icons when adding or replacing icons. Do not use Element Plus icons when a suitable Lucide icon exists. +- Lucide icons are auto-imported; do not add manual imports for them. +- In Vue templates, use kebab-case components with the `i-lucide-` prefix, for example ``. +- In TSX templates, use the auto-imported `ILucide` PascalCase component name, for example ``. +- When an icon is referenced as a component variable or passed as a variable value in TS/TSX or script code, use the auto-imported `IconLucide` PascalCase name, for example `const icon = IconLucideEye` or `icon={IconLucideEye}`. +- Except inside an `el-button`/`ElButton` icon slot, wrap rendered Lucide icons with `el-icon`/`ElIcon`. +- Set icon `size`, `color`, spacing, and other presentation classes on the `el-icon`/`ElIcon` wrapper rather than on the Lucide component. For example, use `` in Vue templates and `` in TSX. +- Inside an `el-button`/`ElButton` icon slot, the Lucide component may be used directly because the button provides the icon wrapper and sizing context. + +## TypeScript and Validation + +- Parts of the existing codebase intentionally lack complete TypeScript typing. Do not expand the task to retrofit types in unrelated existing code. +- Add reasonable, focused types for newly introduced TypeScript/TSX code and for existing declarations that must change to support the new code. +- Do not run repository-wide type checks or builds by default for localized changes. These checks may fail because of unrelated legacy issues and are not required unless the user explicitly requests them. +- Prefer targeted inspection or lightweight checks when validation is useful, without treating unrelated existing type errors as part of the task. diff --git a/packages/assets/styles/utilities.scss b/packages/assets/styles/utilities.scss index 9df39b2f7..091ac4b52 100644 --- a/packages/assets/styles/utilities.scss +++ b/packages/assets/styles/utilities.scss @@ -272,6 +272,10 @@ vertical-align: text-top !important; } +.align-icon-text { + vertical-align: -0.125em !important; +} + .float-start { float: left !important; } @@ -13241,4 +13245,4 @@ .text-amber-500 { color: oklch(76.9% 0.188 70.08); -} \ No newline at end of file +} diff --git a/packages/dag/src/components/LeftSidebar.vue b/packages/dag/src/components/LeftSidebar.vue index b9ac44d14..6bdef1330 100644 --- a/packages/dag/src/components/LeftSidebar.vue +++ b/packages/dag/src/components/LeftSidebar.vue @@ -286,7 +286,7 @@ export default { 'source.id': this.activeConnection.id, taskId: this.$store.state.dataflow.taskId, meta_type: { - in: ['collection', 'table'], + in: ['collection', 'table', 'view'], }, is_deleted: false, sourceType: 'SOURCE', diff --git a/packages/dag/src/components/NodesPanel.vue b/packages/dag/src/components/NodesPanel.vue index 953e73174..19f5169e2 100644 --- a/packages/dag/src/components/NodesPanel.vue +++ b/packages/dag/src/components/NodesPanel.vue @@ -158,7 +158,7 @@ const handleFetchTables = async () => { size: tableState.pageSize, where: { meta_type: { - in: ['collection', 'table'], + in: ['collection', 'table', 'view'], }, is_deleted: false, sourceType: 'SOURCE', diff --git a/packages/dag/src/components/elements/NodesPopover.vue b/packages/dag/src/components/elements/NodesPopover.vue index c1474efde..45f10926f 100644 --- a/packages/dag/src/components/elements/NodesPopover.vue +++ b/packages/dag/src/components/elements/NodesPopover.vue @@ -693,7 +693,10 @@ defineExpose({
- + + + +
@@ -732,7 +735,10 @@ defineExpose({ class="flex h-8 align-center gap-2 px-3 connection-item rounded-lg user-select-none" @click="onClickTable(item)" > - + + + + ([]) const total = ref(0) - const tableMap = ref({}) + const tableNames = ref([]) + const tableMap = ref>({}) const loadData = () => { loading.value = true @@ -42,16 +55,15 @@ export const TableListCard = observer( : getPageTables(params) fn.then((data) => { - const map = {} - const items = data?.items || [] + const map: Record = {} + const items: TableMetadata[] = data?.items || [] items.forEach((t) => { - if (t.uniqueIndexCounts || t.primaryKeyCounts) { - map[t.tableName] = t - } + map[t.tableName] = t }) tableMap.value = map + tableNames.value = items.map((t) => t.tableName) list.value = getPrimaryKeyTablesByType( - items.map((t) => t.tableName) || [], + tableNames.value, props.filterType, tableMap.value, ).map((tableName) => ({ tableName })) @@ -65,7 +77,7 @@ export const TableListCard = observer( () => props.filterType, () => { list.value = getPrimaryKeyTablesByType( - list.value.map((t) => t.tableName) || [], + tableNames.value, props.filterType, tableMap.value, ).map((tableName) => ({ tableName })) @@ -93,8 +105,13 @@ export const TableListCard = observer( placement="right" open-delay={400} > - - {name} + + {tableMap.value[name]?.meta_type === 'view' && ( + + + + )} + {name} {tableMap.value[name]?.tableComment && ( {`(${tableMap.value[name].tableComment})`} )} diff --git a/packages/dag/src/components/form/table-list-card/style.scss b/packages/dag/src/components/form/table-list-card/style.scss index c0886e67c..9181a0153 100644 --- a/packages/dag/src/components/form/table-list-card/style.scss +++ b/packages/dag/src/components/form/table-list-card/style.scss @@ -29,6 +29,15 @@ white-space: nowrap; overflow: hidden; text-overflow: ellipsis; + + .table-list-view-icon { + color: color-mix( + in srgb, + var(--el-text-color-secondary) 72%, + var(--color-primary) + ); + } + &:hover { background-color: var(--fill-hover); } diff --git a/packages/dag/src/components/form/table-select/index.tsx b/packages/dag/src/components/form/table-select/index.tsx index c75da16b8..3e69440f6 100644 --- a/packages/dag/src/components/form/table-select/index.tsx +++ b/packages/dag/src/components/form/table-select/index.tsx @@ -279,6 +279,20 @@ export const TableSelect = connect( {i18n.t('public_data_no_data')}

), + option: ({ item }) => { + return ( + + + {item.meta_type === 'view' ? ( + + ) : ( + + )} + + {item.value} + + ) + }, } if (props.allowCreate) { diff --git a/packages/dag/src/components/form/table-selector/TableSelector.vue b/packages/dag/src/components/form/table-selector/TableSelector.vue index f5fba275d..d09a21a6c 100644 --- a/packages/dag/src/components/form/table-selector/TableSelector.vue +++ b/packages/dag/src/components/form/table-selector/TableSelector.vue @@ -18,6 +18,13 @@ import 'vue-virtual-scroller/dist/vue-virtual-scroller.css' const dataflowStore = useDataflowStore() const { t } = useI18n() +interface TableMetadata { + tableComment?: string + primaryKeyCounts?: number + uniqueIndexCounts?: number + meta_type?: string +} + // Props const props = defineProps({ connectionId: { @@ -51,7 +58,7 @@ const isOpenClipMode = ref(false) const isFocus = ref(false) const clipboardValue = ref('') const errorTables = ref({}) -const tableMap = ref({}) +const tableMap = ref>({}) const table = ref({ tables: [] as string[], checked: [] as string[], @@ -162,16 +169,20 @@ const getTables = () => { fn.then((res = {}) => { const data = res.items || [] const tables = data.map((it) => it.tableName) - const map = {} + const map: Record = {} data.forEach((el = {}) => { const { tableName, tableComment, primaryKeyCounts = 0, uniqueIndexCounts = 0, + meta_type, } = el - if (tableComment || primaryKeyCounts || uniqueIndexCounts) { - map[tableName] = { tableComment, primaryKeyCounts, uniqueIndexCounts } + map[tableName] = { + tableComment, + primaryKeyCounts, + uniqueIndexCounts, + meta_type, } }) tableMap.value = map @@ -416,6 +427,13 @@ getTables() :enterable="false" > + + + + + +
+ + + ([]) const connectionsTotalPage = computed(() => Math.ceil(connectionsTotal.value / pageSize), @@ -135,7 +144,7 @@ export function useFetchConnections() { size: tableState.pageSize, where: { meta_type: { - in: ['collection', 'table'], + in: ['collection', 'table', 'view'], }, is_deleted: false, sourceType: 'SOURCE', @@ -149,6 +158,7 @@ export function useFetchConnections() { id: true, source: true, original_name: true, + meta_type: true, }, // order: ['original_name ASC'], } @@ -189,6 +199,7 @@ export function useFetchConnections() { id: tb.id, name: tb.original_name, comment: tb.comment, + meta_type: tb.meta_type, sourceName: tb.source?.name, sourceId: tb.source?.id, })) diff --git a/packages/dag/src/composables/useFormScope.ts b/packages/dag/src/composables/useFormScope.ts index 73d4afe50..45c1ab716 100644 --- a/packages/dag/src/composables/useFormScope.ts +++ b/packages/dag/src/composables/useFormScope.ts @@ -480,7 +480,7 @@ export function useFormScope({ canvasRef }) { filter.where && Object.assign(filter.where, { meta_type: { - in: ['collection', 'table'], + in: ['collection', 'table', 'view'], }, is_deleted: false, sourceType: 'SOURCE', @@ -488,6 +488,7 @@ export function useFormScope({ canvasRef }) { Object.assign(filter, { fields: { original_name: true, + meta_type: true, }, order: ['original_name ASC'], }) @@ -505,6 +506,7 @@ export function useFormScope({ canvasRef }) { return { label: item.original_name + (item.comment ? `(${item.comment})` : ''), value: item.original_name, + meta_type: item.meta_type, } }) const table = filter.where.original_name?.like diff --git a/packages/dag/src/locale/lang/en.js b/packages/dag/src/locale/lang/en.js index 81b5eac6e..8037b402d 100644 --- a/packages/dag/src/locale/lang/en.js +++ b/packages/dag/src/locale/lang/en.js @@ -1101,6 +1101,7 @@ export default { 'You have not enabled data validation', packages_dag_only_include_pk: 'Only Include Primary Key', packages_dag_only_include_uk: 'Only Include Unique Index', + packages_dag_only_include_views: 'Views Only', packages_dag_auto_repair: 'Auto Repair', packages_dag_auto_repair_tips: 'Enable auto repair', packages_dag_auto_repair_disabled_tips: diff --git a/packages/dag/src/locale/lang/zh-CN.js b/packages/dag/src/locale/lang/zh-CN.js index bd58a07b7..7baa9efa8 100644 --- a/packages/dag/src/locale/lang/zh-CN.js +++ b/packages/dag/src/locale/lang/zh-CN.js @@ -893,6 +893,7 @@ export default { '主从合并后不支持将 {val} 作为写入目标', packages_dag_only_include_pk: '仅包含主键', packages_dag_only_include_uk: '仅包含唯一索引', + packages_dag_only_include_views: '仅包含视图', packages_dag_full_validation: '全量校验', packages_dag_incremental_validation: '增量校验', packages_dag_incremental_validation_tips: '仅校验新增和修改的数据', diff --git a/packages/dag/src/mixins/formScope.js b/packages/dag/src/mixins/formScope.js index d2485ec69..869563909 100644 --- a/packages/dag/src/mixins/formScope.js +++ b/packages/dag/src/mixins/formScope.js @@ -363,7 +363,7 @@ export default { filter.where && Object.assign(filter.where, { meta_type: { - in: ['collection', 'table'], + in: ['collection', 'table', 'view'], }, is_deleted: false, sourceType: 'SOURCE', diff --git a/packages/dag/src/nodes/Database.js b/packages/dag/src/nodes/Database.js index d08c682e4..1b4b914d9 100644 --- a/packages/dag/src/nodes/Database.js +++ b/packages/dag/src/nodes/Database.js @@ -240,6 +240,10 @@ export class Database extends NodeType { ), value: 'NoKeys', }, + { + label: i18n.t('packages_dag_only_include_views'), + value: 'View', + }, ], }, syncSourcePartitionTableEnable: { diff --git a/packages/dag/src/util.ts b/packages/dag/src/util.ts index e09e86b22..a8513bc3c 100644 --- a/packages/dag/src/util.ts +++ b/packages/dag/src/util.ts @@ -143,6 +143,9 @@ export function getPrimaryKeyTablesByType( case 'NoKeys': list = result.filter((t) => !t.primaryKeyCounts && !t.uniqueIndexCounts) break + case 'View': + list = result.filter((t) => t.meta_type === 'view') + break default: list = result } diff --git a/packages/ldp/src/MDM.vue b/packages/ldp/src/MDM.vue index 7c9fe1cb1..248ff5bec 100644 --- a/packages/ldp/src/MDM.vue +++ b/packages/ldp/src/MDM.vue @@ -994,7 +994,7 @@ async function loadFields() { size: 1, where: { 'source.id': taskDialogConfig.value.from.id, - meta_type: { in: ['collection', 'table'] }, + meta_type: { in: ['collection', 'table', 'view'] }, is_deleted: false, sourceType: 'SOURCE', original_name: taskDialogConfig.value.tableName,