fix: 第31轮搜索审计修复 — 否定组日期NULL安全/多列NOT假阳性/year_counts一致性/缓存键空格等8项
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, onUnmounted, watch } from 'vue'
|
||||
import { useRouter, useRoute } from 'vue-router'
|
||||
import { useRouter, useRoute, onBeforeRouteUpdate } from 'vue-router'
|
||||
import { useAuthStore } from '../../stores/auth'
|
||||
import { NInput, NButton, NEmpty, NSelect, NCheckboxGroup, NCheckbox, NPagination, NIcon, NRadioGroup, NRadio, NSlider, NModal, NPopover } from 'naive-ui'
|
||||
import { SearchOutline, FilterOutline, EyeOffOutline, SettingsOutline } from '@vicons/ionicons5'
|
||||
@@ -63,6 +63,7 @@ const expandedGroups = ref<Record<string, boolean>>({})
|
||||
const showFilters = ref(localStorage.getItem('search:showFilters') !== 'false')
|
||||
const yearCounts = ref<{ year: number; count: number }[]>([])
|
||||
const showCustomYear = ref(false) // 自定义年份范围展开
|
||||
const _skipRouteSync = ref(0) // R31: guard against re-entry from syncSearchToUrl
|
||||
const pageSize = ref(Number(localStorage.getItem('search:pageSize')) || 20)
|
||||
// 从 URL 恢复的页码(syncSearchToUrl 写入)
|
||||
const restoredPage = ref(1)
|
||||
@@ -93,6 +94,7 @@ function onYearSliderChange(val: any) {
|
||||
yearFromStr.value = String(val[0])
|
||||
yearToStr.value = String(val[1])
|
||||
datePreset.value = null
|
||||
showCustomYear.value = false // R31: 滑动时收起自定义年份
|
||||
urlDateFrom.value = ''; urlDateTo.value = '' // P6: clear stale URL dates
|
||||
if (_sliderTimer) clearTimeout(_sliderTimer)
|
||||
_sliderTimer = setTimeout(() => goToPage(1), 250)
|
||||
@@ -503,6 +505,13 @@ onMounted(async () => {
|
||||
await goToPage(restoredPage.value)
|
||||
})
|
||||
|
||||
// R31: 浏览器后退/前进时恢复搜索状态
|
||||
onBeforeRouteUpdate(() => {
|
||||
if (_skipRouteSync.value > 0) return // syncSearchToUrl 触发的路由变化,跳过
|
||||
restoreFromQuery()
|
||||
goToPage(restoredPage.value)
|
||||
})
|
||||
|
||||
/** 同步当前搜索参数到 URL query */
|
||||
function syncSearchToUrl() {
|
||||
const q: Record<string, string> = {}
|
||||
@@ -549,7 +558,8 @@ function syncSearchToUrl() {
|
||||
const currentPage = KEYSET_SORTS.has(sort.value) ? keysetPage.value : page.value
|
||||
if (currentPage > 1) q.p = String(currentPage)
|
||||
// R24: use push instead of replace to preserve browser back-button history
|
||||
router.push({ query: q }).catch(() => {})
|
||||
_skipRouteSync.value++
|
||||
router.push({ query: q }).catch(() => {}).finally(() => _skipRouteSync.value--)
|
||||
}
|
||||
|
||||
function resetAllFilters() {
|
||||
@@ -578,6 +588,7 @@ function resetAllFilters() {
|
||||
booleanOp.value = 'and'
|
||||
exactPhrase.value = false
|
||||
showCustomYear.value = false
|
||||
pageSize.value = Number(localStorage.getItem('search:pageSize')) || 20 // R31: 重置每页条数
|
||||
goToPage(1)
|
||||
}
|
||||
|
||||
|
||||
@@ -85,9 +85,9 @@ const translated = computed(() => {
|
||||
items.push({ field: `[${tag}] ${label}`, value: `"${val}"` })
|
||||
}
|
||||
}
|
||||
// Date range: YYYY:YYYY[DP] or YYYY/MM/DD:YYYY/MM/DD[DP]
|
||||
// 对 displayText(已展开 #N)扫描,确保历史引用中的 DP 也能被翻译
|
||||
const yrRe = /(\d{4}(?:[-\/]\d{2}[-\/]\d{2})?)\s*:\s*(\d{4}(?:[-\/]\d{2}[-\/]\d{2})?)\s*\[DP\]/g
|
||||
// Date range: supports YYYY:YYYY[DP], YYYY-MM-DD:YYYY-MM-DD[DP], YYYY/MM/DD:YYYY/MM/DD[DP],
|
||||
// YYYY-M-D:YYYY-M-D[DP] (single digit), YYYY-MM:YYYY-MM[DP] (month-only)
|
||||
const yrRe = /(\d{4}(?:[-\/]\d{1,2}(?:[-\/]\d{1,2})?)?)\s*:\s*(\d{4}(?:[-\/]\d{1,2}(?:[-\/]\d{1,2})?)?)\s*\[DP\]/g
|
||||
while ((m = yrRe.exec(displayText)) !== null) {
|
||||
const key = `dp_range_${m.index}`
|
||||
if (!seen.has(key)) {
|
||||
@@ -155,6 +155,8 @@ function addToQuery() {
|
||||
let val = builderValue.value.trim()
|
||||
// 去除用户输入的多余引号
|
||||
val = val.replace(/^['""“]+|['""”]+$/g, '')
|
||||
// R31: 去除内部双引号防止构建非法 PubMed 语法(保留单引号如 don't)
|
||||
val = val.replace(/["""“”]/g, '')
|
||||
if (!val) {
|
||||
message.warning('请输入搜索词')
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user