feat:Terminology supports data source filtering. (#498)

This commit is contained in:
王嘉豪
2025-11-26 19:50:36 +08:00
committed by GitHub
parent 592e5131c5
commit 46f0ca3f36
4 changed files with 117 additions and 14 deletions
+3 -2
View File
@@ -23,9 +23,10 @@ router = APIRouter(tags=["Terminology"], prefix="/system/terminology")
@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,
+23 -3
View File
@@ -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)
+1 -4
View File
@@ -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}`),
@@ -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<any[]>([])
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<boolean>(false)
@@ -51,6 +58,11 @@ const pageInfo = reactive({
total: 0,
})
const state = reactive<any>({
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<any[]>([
{
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') }}
</el-button>
</el-upload>
<el-button secondary @click="drawerMainOpen">
<template #icon>
<iconFilter></iconFilter>
</template>
{{ $t('user.filter') }}
</el-button>
<el-button type="primary" @click="editHandler(null)">
<template #icon>
<icon_add_outlined></icon_add_outlined>
@@ -498,6 +573,11 @@ const changeStatus = (id: any, val: any) => {
class="table-content"
:class="multipleSelectionAll?.length && 'show-pagination_height'"
>
<filter-text
:total="pageInfo.total"
:filter-texts="state.filterTexts"
@clear-filter="clearFilter"
/>
<div class="preview-or-schema">
<el-table
ref="multipleTableRef"
@@ -781,6 +861,11 @@ const changeStatus = (id: any, val: any) => {
</el-form-item>
</el-form>
</el-drawer>
<drawer-main
ref="drawerMainRef"
:filter-options="filterOption"
@trigger-filter="searchCondition"
/>
</template>
<style lang="less" scoped>