diff --git a/backend/apps/terminology/api/terminology.py b/backend/apps/terminology/api/terminology.py index 34db52c5..e437d457 100644 --- a/backend/apps/terminology/api/terminology.py +++ b/backend/apps/terminology/api/terminology.py @@ -23,9 +23,10 @@ @router.get("/page/{current_page}/{page_size}") async def pager(session: SessionDep, current_user: CurrentUser, current_page: int, page_size: int, - word: Optional[str] = Query(None, description="搜索术语(可选)")): + word: Optional[str] = Query(None, description="搜索术语(可选)"), + dslist: Optional[list[int]] = Query(None, description="数据集ID集合(可选)")): current_page, page_size, total_count, total_pages, _list = page_terminology(session, current_page, page_size, word, - current_user.oid) + current_user.oid, dslist) return { "current_page": current_page, diff --git a/backend/apps/terminology/curd/terminology.py b/backend/apps/terminology/curd/terminology.py index 282041fc..fe14a318 100644 --- a/backend/apps/terminology/curd/terminology.py +++ b/backend/apps/terminology/curd/terminology.py @@ -55,12 +55,32 @@ def get_terminology_base_query(oid: int, name: Optional[str] = None): def build_terminology_query(session: SessionDep, oid: int, name: Optional[str] = None, - paginate: bool = True, current_page: int = 1, page_size: int = 10): + paginate: bool = True, current_page: int = 1, page_size: int = 10, + dslist: Optional[list[int]] = None): """ 构建术语查询的通用方法 """ parent_ids_subquery, child = get_terminology_base_query(oid, name) + # 添加数据源筛选条件 + if dslist is not None and len(dslist) > 0: + datasource_conditions = [] + # datasource_ids 与 dslist 中的任一元素有交集 + for ds_id in dslist: + # 使用 JSONB 包含操作符,但需要确保类型正确 + datasource_conditions.append( + Terminology.datasource_ids.contains([ds_id]) + ) + + # datasource_ids 为空数组 + empty_array_condition = Terminology.datasource_ids == [] + + ds_filter_condition = or_( + *datasource_conditions, + empty_array_condition + ) + parent_ids_subquery = parent_ids_subquery.where(ds_filter_condition) + # 计算总数 count_stmt = select(func.count()).select_from(parent_ids_subquery.subquery()) total_count = session.execute(count_stmt).scalar() @@ -176,12 +196,12 @@ def execute_terminology_query(session: SessionDep, stmt) -> List[TerminologyInfo def page_terminology(session: SessionDep, current_page: int = 1, page_size: int = 10, - name: Optional[str] = None, oid: Optional[int] = 1): + name: Optional[str] = None, oid: Optional[int] = 1, dslist: Optional[list[int]] = None): """ 分页查询术语(原方法保持不变) """ stmt, total_count, total_pages, current_page, page_size = build_terminology_query( - session, oid, name, True, current_page, page_size + session, oid, name, True, current_page, page_size, dslist ) _list = execute_terminology_query(session, stmt) diff --git a/frontend/src/api/professional.ts b/frontend/src/api/professional.ts index 64408d3a..451ff885 100644 --- a/frontend/src/api/professional.ts +++ b/frontend/src/api/professional.ts @@ -2,10 +2,7 @@ import { request } from '@/utils/request' export const professionalApi = { getList: (pageNum: any, pageSize: any, params: any) => - request.get(`/system/terminology/page/${pageNum}/${pageSize}`, { - params, - }), - + request.get(`/system/terminology/page/${pageNum}/${pageSize}${params}`), updateEmbedded: (data: any) => request.put('/system/terminology', data), deleteEmbedded: (params: any) => request.delete('/system/terminology', { data: params }), getOne: (id: any) => request.get(`/system/terminology/${id}`), diff --git a/frontend/src/views/system/professional/index.vue b/frontend/src/views/system/professional/index.vue index 55be0837..d898006b 100644 --- a/frontend/src/views/system/professional/index.vue +++ b/frontend/src/views/system/professional/index.vue @@ -15,6 +15,9 @@ import { cloneDeep } from 'lodash-es' import { genFileId, type UploadInstance, type UploadProps, type UploadRawFile } from 'element-plus' import { useCache } from '@/utils/useCache.ts' import { settingsApi } from '@/api/setting.ts' +import { convertFilterText, FilterText } from '@/components/filter-text' +import { DrawerMain } from '@/components/drawer-main' +import iconFilter from '@/assets/svg/icon-filter_outlined.svg' interface Form { id?: string | null @@ -33,11 +36,15 @@ const allDsList = ref([]) const keywords = ref('') const oldKeywords = ref('') const searchLoading = ref(false) +const drawerMainRef = ref() const selectable = () => { return true } onMounted(() => { + datasourceApi.list().then((res) => { + filterOption.value[0].option = [...res] + }) search() }) const dialogFormVisible = ref(false) @@ -51,6 +58,11 @@ const pageInfo = reactive({ total: 0, }) +const state = reactive({ + conditions: [], + filterTexts: [], +}) + const dialogTitle = ref('') const updateLoading = ref(false) const defaultForm = { @@ -288,15 +300,29 @@ const handleToggleRowSelection = (check: boolean = true) => { isIndeterminate.value = !(i === 0 || i === arr.length) } +const configParams = () => { + let str = '' + if (keywords.value) { + str += `word=${keywords.value}` + } + + state.conditions.forEach((ele: any) => { + ele.value.forEach((itx: any) => { + str += str ? `&${ele.field}=${itx}` : `${ele.field}=${itx}` + }) + }) + + if (str.length) { + str = `?${str}` + } + return str +} + const search = () => { searchLoading.value = true oldKeywords.value = keywords.value professionalApi - .getList( - pageInfo.currentPage, - pageInfo.pageSize, - keywords.value ? { word: keywords.value } : {} - ) + .getList(pageInfo.currentPage, pageInfo.pageSize, configParams()) .then((res) => { toggleRowLoading.value = true fieldList.value = res.data @@ -426,6 +452,49 @@ const deleteHandlerItem = (idx: number) => { pageForm.value.other_words = pageForm.value.other_words!.filter((_, index) => index !== idx) } +const filterOption = ref([ + { + type: 'select', + option: [], + field: 'dslist', + title: t('ds.title'), + operate: 'in', + property: { placeholder: t('common.empty') + t('ds.title') }, + }, +]) + +const fillFilterText = () => { + const textArray = state.conditions?.length + ? convertFilterText(state.conditions, filterOption.value) + : [] + state.filterTexts = [...textArray] + Object.assign(state.filterTexts, textArray) +} +const searchCondition = (conditions: any) => { + state.conditions = conditions + fillFilterText() + search() + drawerMainClose() +} + +const clearFilter = (params?: number) => { + let index = params ? params : 0 + if (isNaN(index)) { + state.filterTexts = [] + } else { + state.filterTexts.splice(index, 1) + } + drawerMainRef.value.clearFilter(index) +} + +const drawerMainOpen = async () => { + drawerMainRef.value.init() +} + +const drawerMainClose = () => { + drawerMainRef.value.close() +} + const changeStatus = (id: any, val: any) => { professionalApi .enable(id, val + '') @@ -485,6 +554,12 @@ const changeStatus = (id: any, val: any) => { {{ $t('user.batch_import') }} + + + {{ $t('user.filter') }} +