feat: enhance AutoProbeResultsContainer with dynamic cell styling and active field management

This commit is contained in:
Marvin Zhang
2025-05-16 13:13:13 +08:00
parent f3d6a56315
commit 9144ab5fe0
8 changed files with 106 additions and 10 deletions

View File

@@ -3,6 +3,7 @@ import { computed, ref } from 'vue';
import { ClTag } from '@/components';
import { translate, getIconByItemType } from '@/utils';
import { TAB_NAME_RESULTS, TAB_NAME_PREVIEW } from '@/constants';
import { CellStyle } from 'element-plus';
const t = translate;
@@ -10,6 +11,7 @@ const t = translate;
const props = defineProps<{
data?: PageData | PageData[];
fields?: AutoProbeNavItem[];
activeFieldName?: string;
}>();
// Refs
@@ -39,6 +41,7 @@ const tableColumns = computed<TableColumns<PageData>>(() => {
return {
key: field.name,
label: field.name,
minWidth: '200',
value: (row: PageData) => {
switch (field.type) {
case 'list':
@@ -65,6 +68,16 @@ const tableData = computed<TableData<PageData | PageData[]>>(() => {
return [data];
});
const tableCellStyle: CellStyle<PageData> = ({ column }) => {
const { activeFieldName } = props;
if (column.columnKey === activeFieldName) {
return {
backgroundColor: 'var(--el-color-primary-light-9)',
};
}
return {};
};
// Methods
const onTabSelect = (id: string) => {
if (activeTabName.value === id) {
@@ -125,8 +138,11 @@ defineOptions({ name: 'ClAutoProbeResultsContainer' });
</cl-nav-tabs>
<div class="results" v-if="activeTabName === TAB_NAME_RESULTS">
<cl-table
:key="JSON.stringify(tableColumns)"
:columns="tableColumns"
:data="tableData"
:header-cell-style="tableCellStyle"
:cell-style="tableCellStyle"
embedded
hide-footer
/>

View File

@@ -80,4 +80,10 @@ export declare global {
parent?: AutoProbeNavItem;
fieldCount?: number;
}
interface AutoProbeResults {
data?: PageData | PageData[];
fields?: AutoProbeNavItem[];
activeField?: AutoProbeNavItem;
}
}

View File

@@ -3,4 +3,5 @@ interface TreeNode<T = any> {
value?: any;
children?: T[];
path?: string;
level?: number;
}

View File

@@ -125,7 +125,8 @@ defineOptions({ name: 'ClDetailLayout' });
overflow: hidden;
.nav-actions {
height: fit-content;
height: 53px;
flex: 0 0 53px;
}
.nav-select {
@@ -143,7 +144,7 @@ defineOptions({ name: 'ClDetailLayout' });
.content-container {
flex: 1;
height: calc(100% - 41px - 50px);
height: calc(100% - 41px - 53px);
}
}
}

View File

@@ -316,7 +316,6 @@ export const getDefaultStoreActions = <T = any>(
state,
commit,
}: StoreActionContext<BaseStoreState<T>>) => {
console.debug('getList');
const { page, size } = state.tablePagination;
try {
commit('setTableLoading', true);

View File

@@ -1,8 +1,9 @@
<script setup lang="ts">
import { computed, ref, watch } from 'vue';
import { useStore } from 'vuex';
import { getIconByExtractType, getIconByItemType, translate } from '@/utils';
import { cloneDeep } from 'lodash';
import { getIconByExtractType, getIconByItemType, translate } from '@/utils';
import { useAutoProbeDetail } from '@/views';
// i18n
const t = translate;
@@ -11,6 +12,8 @@ const t = translate;
const store = useStore();
const { autoprobe: state } = store.state as RootStoreState;
const { activeId } = useAutoProbeDetail();
// form data
const form = computed<AutoProbe>(() => state.form);
const pageFields = computed(() => form.value?.page_pattern?.fields);
@@ -19,6 +22,54 @@ const pagePagination = computed(() => form.value?.page_pattern?.pagination);
const pageData = computed<PageData>(() => form.value?.page_data || {});
const pageNavItemId = 'page';
// results data and fields based on active item
const resultsDataFields = computed<AutoProbeResults>(() => {
const rootDataFields: AutoProbeResults = {
data: pageData.value,
fields: computedTreeItems.value[0].children?.filter(
item => item.type !== 'pagination'
),
};
if (!activeNavItem.value || !pageData.value) {
return rootDataFields;
}
const item = activeNavItem.value;
if (item.level === 0) {
return rootDataFields;
} else if (item.level === 1) {
if (item.type === 'list') {
return {
data: pageData.value[item.name!],
fields: item.children,
} as AutoProbeResults;
}
return {
...rootDataFields,
activeField: item,
};
} else {
let currentItem = item;
while (currentItem.parent) {
const parent = currentItem.parent;
if (parent.level === 1 && parent.type === 'list') {
return {
data: pageData.value[parent.name!],
fields: parent.children,
activeField: currentItem,
} as AutoProbeResults;
}
currentItem = currentItem.parent;
}
return rootDataFields;
}
});
const resultsData = computed(() => resultsDataFields.value.data);
const resultsFields = computed(() => resultsDataFields.value.fields);
const resultsActiveField = computed(() => resultsDataFields.value.activeField);
const normalizeItem = (item: AutoProbeNavItem) => {
const label = item.label ?? `${item.name} (${item.children?.length || 0})`;
let icon: Icon;
@@ -38,7 +89,8 @@ const normalizeItem = (item: AutoProbeNavItem) => {
// Helper function to recursively process list items
const processListItem = (
list: ListRule,
parent?: AutoProbeNavItem
parent?: AutoProbeNavItem,
level: number = 1
): AutoProbeNavItem => {
const listItem: AutoProbeNavItem = {
id: list.name,
@@ -47,6 +99,7 @@ const processListItem = (
rule: list,
children: [],
parent,
level,
};
// Add fields directly if they exist
@@ -60,6 +113,7 @@ const processListItem = (
type: 'field',
rule: field,
parent: listItem,
level: level + 1,
})
);
});
@@ -68,7 +122,7 @@ const processListItem = (
// Recursively process nested lists if they exist
if (list.item_pattern?.lists && list.item_pattern.lists.length > 0) {
list.item_pattern.lists.forEach((nestedList: ListRule) => {
listItem.children!.push(processListItem(nestedList, listItem));
listItem.children!.push(processListItem(nestedList, listItem, level + 1));
});
}
@@ -96,6 +150,7 @@ const computedTreeItems = computed<AutoProbeNavItem[]>(() => {
name: form.value.page_pattern.name,
type: 'page_pattern',
children: [],
level: 0,
};
// Add fields directly if they exist
@@ -109,6 +164,7 @@ const computedTreeItems = computed<AutoProbeNavItem[]>(() => {
type: 'field',
rule: field,
parent: rootItem,
level: 1,
})
);
});
@@ -117,7 +173,7 @@ const computedTreeItems = computed<AutoProbeNavItem[]>(() => {
// Add lists directly if they exist
if (pageLists.value) {
pageLists.value.forEach(list => {
rootItem.children!.push(processListItem(list, rootItem));
rootItem.children!.push(processListItem(list, rootItem, 1));
});
}
@@ -131,6 +187,7 @@ const computedTreeItems = computed<AutoProbeNavItem[]>(() => {
type: 'pagination',
rule: pagePagination.value,
parent: rootItem,
level: 1,
})
);
}
@@ -146,6 +203,11 @@ watch(
{ immediate: true }
);
watch(activeId, () => {
// Reset active item when the page changes
activeNavItem.value = undefined;
});
// ref
const sidebarRef = ref();
const detailContainerRef = ref<HTMLElement | null>(null);
@@ -193,11 +255,11 @@ defineOptions({ name: 'ClAutoProbeDetailTabPatterns' });
</div>
</div>
<!--TODO: implement the data for activeNavItem-->
<cl-auto-probe-results-container
v-if="detailNavItem"
:data="pageData"
:fields="activeNavItem?.children"
:data="resultsData"
:fields="resultsFields"
:active-field-name="resultsActiveField?.name"
@size-change="onSizeChange"
/>
</div>

View File

@@ -0,0 +1,9 @@
import { useDetail } from '@/layouts/content';
const useAutoProbeDetail = () => {
return {
...useDetail<AutoProbe>('autoprobe'),
};
};
export default useAutoProbeDetail;

View File

@@ -92,6 +92,7 @@ import TaskDetailTabLogs from './task/detail/tabs/TaskDetailTabLogs.vue';
import TaskDetailTabOverview from './task/detail/tabs/TaskDetailTabOverview.vue';
import TaskList from './task/list/TaskList.vue';
import TokenList from './token/list/TokenList.vue';
import useAutoProbeDetail from './autoprobe/detail/useAutoProbeDetail';
import useAutoProbeList from './autoprobe/list/useAutoProbeList';
import useDatabaseDetail from './database/detail/useDatabaseDetail';
import useDatabaseList from './database/list/useDatabaseList';
@@ -218,6 +219,7 @@ export {
TaskDetailTabOverview as ClTaskDetailTabOverview,
TaskList as ClTaskList,
TokenList as ClTokenList,
useAutoProbeDetail as useAutoProbeDetail,
useAutoProbeList as useAutoProbeList,
useDatabaseDetail as useDatabaseDetail,
useDatabaseList as useDatabaseList,