添加价格列表搜索功能

- 后端 prices.go 新增搜索查询参数,支持按模型名称模糊搜索
- 更新缓存键和总数缓存键,包含搜索查询参数
- 前端 Prices.vue 添加搜索输入框和搜索处理逻辑
- 实现搜索防抖,优化用户交互体验
- 重置搜索时自动返回第一页
This commit is contained in:
wood chen 2025-03-12 16:46:59 +08:00
parent 680d684016
commit 2b2cc35a1c
2 changed files with 54 additions and 5 deletions

View File

@ -19,6 +19,7 @@ func GetPrices(c *gin.Context) {
pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "20")) pageSize, _ := strconv.Atoi(c.DefaultQuery("pageSize", "20"))
channelType := c.Query("channel_type") // 厂商筛选参数 channelType := c.Query("channel_type") // 厂商筛选参数
modelType := c.Query("model_type") // 模型类型筛选参数 modelType := c.Query("model_type") // 模型类型筛选参数
searchQuery := c.Query("search") // 搜索查询参数
if page < 1 { if page < 1 {
page = 1 page = 1
@ -30,8 +31,8 @@ func GetPrices(c *gin.Context) {
offset := (page - 1) * pageSize offset := (page - 1) * pageSize
// 构建缓存键 // 构建缓存键
cacheKey := fmt.Sprintf("prices_page_%d_size_%d_channel_%s_type_%s", cacheKey := fmt.Sprintf("prices_page_%d_size_%d_channel_%s_type_%s_search_%s",
page, pageSize, channelType, modelType) page, pageSize, channelType, modelType, searchQuery)
// 尝试从缓存获取 // 尝试从缓存获取
if cachedData, found := database.GlobalCache.Get(cacheKey); found { if cachedData, found := database.GlobalCache.Get(cacheKey); found {
@ -51,10 +52,14 @@ func GetPrices(c *gin.Context) {
if modelType != "" { if modelType != "" {
query = query.Where("model_type = ?", modelType) query = query.Where("model_type = ?", modelType)
} }
// 添加搜索条件
if searchQuery != "" {
query = query.Where("model LIKE ?", "%"+searchQuery+"%")
}
// 获取总数 - 使用缓存优化 // 获取总数 - 使用缓存优化
var total int64 var total int64
totalCacheKey := fmt.Sprintf("prices_count_channel_%s_type_%s", channelType, modelType) totalCacheKey := fmt.Sprintf("prices_count_channel_%s_type_%s_search_%s", channelType, modelType, searchQuery)
if cachedTotal, found := database.GlobalCache.Get(totalCacheKey); found { if cachedTotal, found := database.GlobalCache.Get(totalCacheKey); found {
if t, ok := cachedTotal.(int64); ok { if t, ok := cachedTotal.(int64); ok {

View File

@ -22,6 +22,21 @@
</div> </div>
</template> </template>
<!-- 添加搜索框 -->
<div class="search-section">
<el-input
v-model="searchQuery"
placeholder="搜索模型名称"
clearable
prefix-icon="Search"
@input="handleSearch"
>
<template #prefix>
<el-icon><Search /></el-icon>
</template>
</el-input>
</div>
<div class="filter-section"> <div class="filter-section">
<div class="filter-label">厂商筛选:</div> <div class="filter-label">厂商筛选:</div>
<div class="provider-filters"> <div class="provider-filters">
@ -502,7 +517,7 @@ import { ref, computed, onMounted, watch } from 'vue'
import axios from 'axios' import axios from 'axios'
import { ElMessage, ElMessageBox } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus'
import { useRouter } from 'vue-router' import { useRouter } from 'vue-router'
import { Edit, Delete, Check, Close, Document } from '@element-plus/icons-vue' import { Edit, Delete, Check, Close, Document, Search } from '@element-plus/icons-vue'
const props = defineProps({ const props = defineProps({
user: Object user: Object
@ -524,6 +539,7 @@ const form = ref({
const router = useRouter() const router = useRouter()
const selectedProvider = ref('') const selectedProvider = ref('')
const selectedModelType = ref('') const selectedModelType = ref('')
const searchQuery = ref('')
const isAdmin = computed(() => props.user?.role === 'admin') const isAdmin = computed(() => props.user?.role === 'admin')
@ -588,6 +604,10 @@ const loadPrices = async () => {
if (selectedModelType.value) { if (selectedModelType.value) {
params.model_type = selectedModelType.value params.model_type = selectedModelType.value
} }
//
if (searchQuery.value) {
params.search = searchQuery.value
}
try { try {
const [pricesRes, providersRes] = await Promise.all([ const [pricesRes, providersRes] = await Promise.all([
@ -600,7 +620,7 @@ const loadPrices = async () => {
providers.value = providersRes.data providers.value = providersRes.data
// //
const cacheKey = `${currentPage.value}-${pageSize.value}-${selectedProvider.value}-${selectedModelType.value}` const cacheKey = `${currentPage.value}-${pageSize.value}-${selectedProvider.value}-${selectedModelType.value}-${searchQuery.value}`
cachedPrices.value.set(cacheKey, { cachedPrices.value.set(cacheKey, {
prices: pricesRes.data.data, prices: pricesRes.data.data,
total: pricesRes.data.total total: pricesRes.data.total
@ -1058,6 +1078,19 @@ watch(selectedModelType, () => {
loadPrices() loadPrices()
}) })
//
watch(searchQuery, () => {
// 使
if (searchDebounceTimer) clearTimeout(searchDebounceTimer)
searchDebounceTimer = setTimeout(() => {
currentPage.value = 1 //
loadPrices()
}, 300)
})
//
let searchDebounceTimer = null
// //
const duplicateRow = (index) => { const duplicateRow = (index) => {
const newRow = { ...batchForms.value[index] } const newRow = { ...batchForms.value[index] }
@ -1107,6 +1140,12 @@ const approveAllPending = async () => {
} }
} }
//
const handleSearch = () => {
currentPage.value = 1 //
loadPrices()
}
onMounted(() => { onMounted(() => {
loadModelTypes() loadModelTypes()
loadPrices() loadPrices()
@ -1137,6 +1176,11 @@ onMounted(() => {
color: #606266; color: #606266;
} }
.search-section {
margin: 16px 0;
max-width: 400px;
}
.provider-filters { .provider-filters {
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;