fix: 第三轮PubMed搜索审计 — 14项修复(P0×5, P1×4, P3×5)
P0 (搜索结果错误): - 门控补全 sb/stat/uid_terms + dep_from/dep_to - [SB] 映射修复: medline→citation_status, pubmed→no-op - 括号组内单否定词丢失 not_() 修复 - HomeView URL watch 补全 sort/field/retracted/negative - HomeView→SearchView 日期精度保留(urlDateFrom/urlDateTo) P1 (功能缺陷): - [ALL] 字段标签注册(_FIELD_TAG_MAP + _ALL_FIELD_TAGS) - 独立日期字段 "2024-01-01"[DP] 降级修复(_dispatch_term) - 浮点日期范围交换(2026-01-01:2024-01-01 → 自动排序) - page_size URL 恢复 + syncSearchToUrl 输出 P3 (健壮性): - MeSH 展开 try/catch(expand_atm + _expand_mesh_tag_ids) - sort/field/boolean 参数验证(field_validator) - GET /search 查询长度限制(100词) - query_expansion.py 中文正则同步 [一-鿿㐀-䶿豈-] - import_mesh_full entry_terms 统一小写(匹配 JSONB @> 精确比较) 测试: 127/127 搜索测试通过,前端构建无报错
This commit is contained in:
@@ -31,6 +31,7 @@ const savedPmids = ref<Set<number>>(new Set())
|
||||
|
||||
// ── 筛选参数 ──
|
||||
const yearFromStr = ref(''); const yearToStr = ref('')
|
||||
const urlDateFrom = ref(''); const urlDateTo = ref('') // 来自 URL 的完整日期(保留精度)
|
||||
const datePreset = ref<string | null>(null)
|
||||
const selectedTiers = ref<string[]>([])
|
||||
const selectedTags = ref<string[]>([])
|
||||
@@ -299,6 +300,10 @@ const { page, total, goToPage } = usePagination({
|
||||
if (datePreset.value === 'custom') {
|
||||
if (yearFromStr.value && !isNaN(yf) && yf >= 1900 && yf <= 2100) body.year_from = yf
|
||||
if (yearToStr.value && !isNaN(yt) && yt >= 1900 && yt <= 2100) body.year_to = yt
|
||||
} else if (!datePreset.value && (urlDateFrom.value || urlDateTo.value)) {
|
||||
// 来自 URL 的完整日期(保留月日精度)
|
||||
if (urlDateFrom.value) body.date_from = urlDateFrom.value
|
||||
if (urlDateTo.value) body.date_to = urlDateTo.value
|
||||
} else {
|
||||
if (!datePreset.value && yearFromStr.value && !isNaN(yf) && yf >= 1900 && yf <= 2100) body.year_from = yf
|
||||
if (!datePreset.value && yearToStr.value && !isNaN(yt) && yt >= 1900 && yt <= 2100) body.year_to = yt
|
||||
@@ -378,10 +383,12 @@ function restoreFromQuery() {
|
||||
datePreset.value = null
|
||||
if (route.query.date_from) {
|
||||
const df = String(route.query.date_from)
|
||||
urlDateFrom.value = df // 保留完整日期避免精度丢失
|
||||
if (df.length >= 4) yearFromStr.value = df.slice(0, 4)
|
||||
}
|
||||
if (route.query.date_to) {
|
||||
const dt = String(route.query.date_to)
|
||||
urlDateTo.value = dt
|
||||
if (dt.length >= 4) yearToStr.value = dt.slice(0, 4)
|
||||
}
|
||||
}
|
||||
@@ -402,6 +409,10 @@ function restoreFromQuery() {
|
||||
if (route.query.medline_only) medlineOnly.value = route.query.medline_only === 'true'
|
||||
if (route.query.exclude_preprints) excludePreprints.value = route.query.exclude_preprints === 'true'
|
||||
if (route.query.p) restoredPage.value = parseInt(String(route.query.p)) || 1
|
||||
if (route.query.page_size) {
|
||||
const ps = parseInt(String(route.query.page_size))
|
||||
if (ps >= 10 && ps <= 100) pageSize.value = ps
|
||||
}
|
||||
// keyset 游标不能从 URL 恢复 → page>1 回退到首页
|
||||
if (sort.value === 'date' && restoredPage.value > 1) restoredPage.value = 1
|
||||
}
|
||||
@@ -411,8 +422,12 @@ watch(datePreset, (val) => {
|
||||
if (val && val !== 'custom') {
|
||||
yearFromStr.value = ''
|
||||
yearToStr.value = ''
|
||||
urlDateFrom.value = ''; urlDateTo.value = '' // 清除 URL 日期
|
||||
if (searched.value) goToPage(1)
|
||||
}
|
||||
if (val === 'custom') {
|
||||
urlDateFrom.value = ''; urlDateTo.value = '' // 自定义年份覆盖 URL 日期
|
||||
}
|
||||
})
|
||||
|
||||
// P2-10: 弹窗关闭时(mask-closable 或确定按钮)自动搜索
|
||||
@@ -491,6 +506,7 @@ function syncSearchToUrl() {
|
||||
if (hasAssociatedData.value) q.associated_data = 'true'
|
||||
if (medlineOnly.value) q.medline_only = 'true'
|
||||
if (excludePreprints.value) q.exclude_preprints = 'true'
|
||||
if (pageSize.value !== 20) q.page_size = String(pageSize.value)
|
||||
// 页码持久化到 URL(不同排序下使用各自的分页)
|
||||
const currentPage = sort.value === 'date' ? keysetPage.value : page.value
|
||||
if (currentPage > 1) q.p = String(currentPage)
|
||||
|
||||
@@ -370,7 +370,7 @@ function restoreFromUrl() {
|
||||
// ── 同步状态到 URL ──
|
||||
const _mounted = ref(false)
|
||||
const searchKey = computed(() =>
|
||||
JSON.stringify([searchParams.value.query, searchParams.value.tag_ids, searchParams.value.date_from, searchParams.value.date_to, searchParams.value.sort])
|
||||
JSON.stringify([searchParams.value.query, searchParams.value.tag_ids, searchParams.value.date_from, searchParams.value.date_to, searchParams.value.sort, searchParams.value.field, searchParams.value.retracted, searchParams.value.negative_result])
|
||||
)
|
||||
|
||||
watch(searchKey, () => {
|
||||
@@ -380,6 +380,10 @@ watch(searchKey, () => {
|
||||
if (searchParams.value.date_from) query.date_from = searchParams.value.date_from
|
||||
if (searchParams.value.date_to) query.date_to = searchParams.value.date_to
|
||||
if (searchParams.value.query) query.q = searchParams.value.query
|
||||
if (searchParams.value.sort && searchParams.value.sort !== 'date') query.sort = searchParams.value.sort
|
||||
if (searchParams.value.field && searchParams.value.field !== 'all') query.field = searchParams.value.field
|
||||
if (searchParams.value.retracted) query.retracted = searchParams.value.retracted
|
||||
if (searchParams.value.negative_result) query.negative = searchParams.value.negative_result
|
||||
router.replace({ query })
|
||||
})
|
||||
|
||||
|
||||
Reference in New Issue
Block a user