feat: 重构全局搜索框为复合条件选择,支持按名称、俗名、规格进行精准查杂

This commit is contained in:
dxc
2026-03-11 13:37:52 +08:00
parent e224a07a47
commit b1cc280a71
12 changed files with 156 additions and 65 deletions

View File

@ -124,7 +124,8 @@ def get_list():
'isAsc': request.args.get('isAsc', None), 'isAsc': request.args.get('isAsc', None),
'advancedFilters': advanced_filters_list, 'advancedFilters': advanced_filters_list,
'enableWarningSort': request.args.get('enableWarningSort', 'false').lower() == 'true', 'enableWarningSort': request.args.get('enableWarningSort', 'false').lower() == 'true',
'has_stock': request.args.get('has_stock', '') 'has_stock': request.args.get('has_stock', ''),
'searchField': request.args.get('searchField', 'all')
} }
user_permissions = get_current_user_permissions() user_permissions = get_current_user_permissions()

View File

@ -116,6 +116,7 @@ def get_list():
limit = request.args.get('pageSize', 15, type=int) limit = request.args.get('pageSize', 15, type=int)
keyword = request.args.get('keyword', '') keyword = request.args.get('keyword', '')
sku = request.args.get('sku', '') sku = request.args.get('sku', '')
search_field = request.args.get('searchField', 'all')
# 新增筛选参数 # 新增筛选参数
category = request.args.get('category', '') category = request.args.get('category', '')
@ -138,7 +139,7 @@ def get_list():
statuses_str = request.args.get('statuses', '') statuses_str = request.args.get('statuses', '')
statuses = statuses_str.split(',') if statuses_str else [] statuses = statuses_str.split(',') if statuses_str else []
result = BuyInboundService.get_list(page, limit, keyword, sku, statuses, category, material_type, company, result = BuyInboundService.get_list(page, limit, keyword, sku, search_field, statuses, category, material_type, company,
order_by, is_asc, advanced_filters) order_by, is_asc, advanced_filters)
# 字段级脱敏 # 字段级脱敏
user_permissions = get_current_user_permissions() user_permissions = get_current_user_permissions()

View File

@ -74,6 +74,7 @@ def get_list():
limit = request.args.get('pageSize', 15, type=int) limit = request.args.get('pageSize', 15, type=int)
keyword = request.args.get('keyword', '') keyword = request.args.get('keyword', '')
sku = request.args.get('sku', '') sku = request.args.get('sku', '')
search_field = request.args.get('searchField', 'all')
statuses_str = request.args.get('statuses', '') statuses_str = request.args.get('statuses', '')
statuses = statuses_str.split(',') if statuses_str else [] statuses = statuses_str.split(',') if statuses_str else []
category = request.args.get('category', '') category = request.args.get('category', '')
@ -105,7 +106,7 @@ def get_list():
# 调用服务,传入所有参数 # 调用服务,传入所有参数
result = ProductInboundService.get_list( result = ProductInboundService.get_list(
page, limit, keyword, sku, statuses, page, limit, keyword, sku, search_field, statuses,
category=extra_filters.get('category'), category=extra_filters.get('category'),
material_type=extra_filters.get('material_type'), material_type=extra_filters.get('material_type'),
company=extra_filters.get('company'), company=extra_filters.get('company'),

View File

@ -74,6 +74,7 @@ def get_list():
limit = request.args.get('pageSize', 15, type=int) limit = request.args.get('pageSize', 15, type=int)
keyword = request.args.get('keyword', '') keyword = request.args.get('keyword', '')
sku = request.args.get('sku', '') sku = request.args.get('sku', '')
search_field = request.args.get('searchField', 'all')
statuses_str = request.args.get('statuses', '') statuses_str = request.args.get('statuses', '')
statuses = statuses_str.split(',') if statuses_str else [] statuses = statuses_str.split(',') if statuses_str else []
company = request.args.get('company', '') company = request.args.get('company', '')
@ -105,7 +106,7 @@ def get_list():
# 调用服务,传入所有参数 # 调用服务,传入所有参数
result = SemiInboundService.get_list( result = SemiInboundService.get_list(
page, limit, keyword, sku, statuses, page, limit, keyword, sku, search_field, statuses,
**extra_filters **extra_filters
) )
user_permissions = get_current_user_permissions() user_permissions = get_current_user_permissions()

View File

@ -169,14 +169,23 @@ class MaterialBaseService:
.outerjoin(MaterialWarningSetting, MaterialBase.id == MaterialWarningSetting.base_id) .outerjoin(MaterialWarningSetting, MaterialBase.id == MaterialWarningSetting.base_id)
if filters: if filters:
# 1. 关键词模糊搜索 # 1. 关键词精准搜索(支持指定字段)
if filters.get('keyword'): search_field = filters.get('searchField', 'all')
kw = f"%{filters['keyword']}%" keyword = filters.get('keyword')
query = query.filter(or_( if keyword:
MaterialBase.name.ilike(kw), kw = f"%{keyword}%"
MaterialBase.common_name.ilike(kw), if search_field == 'name':
MaterialBase.spec_model.ilike(kw) query = query.filter(MaterialBase.name.ilike(kw))
)) elif search_field == 'common_name':
query = query.filter(MaterialBase.common_name.ilike(kw))
elif search_field == 'spec':
query = query.filter(MaterialBase.spec_model.ilike(kw))
else: # 'all' 默认全局模糊匹配
query = query.filter(or_(
MaterialBase.name.ilike(kw),
MaterialBase.common_name.ilike(kw),
MaterialBase.spec_model.ilike(kw)
))
# 2. 精确筛选 # 2. 精确筛选
company = filters.get('company') company = filters.get('company')

View File

@ -240,26 +240,41 @@ class BuyInboundService:
# 5. 获取列表 (支持排序和高级筛选) # 5. 获取列表 (支持排序和高级筛选)
# ============================================================ # ============================================================
@staticmethod @staticmethod
def get_list(page, limit, keyword=None, sku=None, statuses=None, category=None, material_type=None, company=None, def get_list(page, limit, keyword=None, sku=None, search_field='all', statuses=None, category=None, material_type=None, company=None,
order_by='', is_asc='', advanced_filters=None): order_by='', is_asc='', advanced_filters=None):
try: try:
from sqlalchemy import and_, or_ from sqlalchemy import and_, or_
query = db.session.query(StockBuy).outerjoin(MaterialBase, StockBuy.base_id == MaterialBase.id) query = db.session.query(StockBuy).outerjoin(MaterialBase, StockBuy.base_id == MaterialBase.id)
# 1. 通用关键词搜索(名称、规格、公司 # 1. 通用关键词搜索(支持指定字段精准搜索
if keyword: if keyword:
k_str = f'%{keyword.strip()}%' k_str = f'%{keyword.strip()}%'
conditions = [ if search_field == 'name':
StockBuy.barcode.ilike(k_str), query = query.filter(MaterialBase.name.ilike(k_str))
StockBuy.batch_number.ilike(k_str), elif search_field == 'spec':
StockBuy.serial_number.ilike(k_str), query = query.filter(MaterialBase.spec_model.ilike(k_str))
StockBuy.supplier_name.ilike(k_str), elif search_field == 'common_name':
StockBuy.buyer_name.ilike(k_str), query = query.filter(MaterialBase.common_name.ilike(k_str))
MaterialBase.name.ilike(k_str), elif search_field == 'barcode':
MaterialBase.spec_model.ilike(k_str), query = query.filter(StockBuy.barcode.ilike(k_str))
MaterialBase.company_name.ilike(k_str), # 关键词也支持搜公司 elif search_field == 'batch_number':
] query = query.filter(StockBuy.batch_number.ilike(k_str))
query = query.filter(or_(*conditions)) elif search_field == 'supplier_name':
query = query.filter(StockBuy.supplier_name.ilike(k_str))
elif search_field == 'buyer_name':
query = query.filter(StockBuy.buyer_name.ilike(k_str))
else: # 'all' 默认全局模糊匹配
conditions = [
StockBuy.barcode.ilike(k_str),
StockBuy.batch_number.ilike(k_str),
StockBuy.serial_number.ilike(k_str),
StockBuy.supplier_name.ilike(k_str),
StockBuy.buyer_name.ilike(k_str),
MaterialBase.name.ilike(k_str),
MaterialBase.spec_model.ilike(k_str),
MaterialBase.company_name.ilike(k_str),
]
query = query.filter(or_(*conditions))
# 1.1 SKU 独立搜索 # 1.1 SKU 独立搜索
if sku and sku.strip(): if sku and sku.strip():

View File

@ -270,22 +270,35 @@ class ProductInboundService:
return [] return []
@staticmethod @staticmethod
def get_list(page, limit, keyword=None, sku=None, statuses=None, category=None, material_type=None, company=None, def get_list(page, limit, keyword=None, sku=None, search_field='all', statuses=None, category=None, material_type=None, company=None,
order_by_column=None, is_asc=None, advanced_filters=None): order_by_column=None, is_asc=None, advanced_filters=None):
from app.models.inbound.product import StockProduct from app.models.inbound.product import StockProduct
try: try:
query = db.session.query(StockProduct).outerjoin(MaterialBase, StockProduct.base_id == MaterialBase.id) query = db.session.query(StockProduct).outerjoin(MaterialBase, StockProduct.base_id == MaterialBase.id)
# 1. 通用关键词搜索(名称、规格、公司 # 1. 通用关键词搜索(支持指定字段精准搜索
if keyword: if keyword:
kw = f'%{keyword}%' kw = f'%{keyword}%'
query = query.filter(or_( if search_field == 'name':
MaterialBase.name.ilike(kw), query = query.filter(MaterialBase.name.ilike(kw))
MaterialBase.spec_model.ilike(kw), elif search_field == 'spec':
MaterialBase.company_name.ilike(kw), query = query.filter(MaterialBase.spec_model.ilike(kw))
StockProduct.serial_number.ilike(kw), elif search_field == 'common_name':
StockProduct.work_order_code.ilike(kw), query = query.filter(MaterialBase.common_name.ilike(kw))
StockProduct.order_id.ilike(kw) elif search_field == 'serial_number':
)) query = query.filter(StockProduct.serial_number.ilike(kw))
elif search_field == 'work_order_code':
query = query.filter(StockProduct.work_order_code.ilike(kw))
elif search_field == 'order_id':
query = query.filter(StockProduct.order_id.ilike(kw))
else: # 'all' 默认全局模糊匹配
query = query.filter(or_(
MaterialBase.name.ilike(kw),
MaterialBase.spec_model.ilike(kw),
MaterialBase.company_name.ilike(kw),
StockProduct.serial_number.ilike(kw),
StockProduct.work_order_code.ilike(kw),
StockProduct.order_id.ilike(kw)
))
# 1.1 SKU 独立搜索 # 1.1 SKU 独立搜索
if sku and sku.strip(): if sku and sku.strip():
sku_str = f'%{sku.strip()}%' sku_str = f'%{sku.strip()}%'

View File

@ -359,25 +359,38 @@ class SemiInboundService:
return [] return []
@staticmethod @staticmethod
def get_list(page, limit, keyword=None, sku=None, statuses=None, category=None, material_type=None, company=None, def get_list(page, limit, keyword=None, sku=None, search_field='all', statuses=None, category=None, material_type=None, company=None,
order_by_column=None, is_asc=None, advanced_filters=None): order_by_column=None, is_asc=None, advanced_filters=None):
from app.models.inbound.semi import StockSemi from app.models.inbound.semi import StockSemi
try: try:
query = db.session.query(StockSemi).outerjoin(MaterialBase, StockSemi.base_id == MaterialBase.id) query = db.session.query(StockSemi).outerjoin(MaterialBase, StockSemi.base_id == MaterialBase.id)
# 1. 通用关键词搜索(名称、规格、公司 # 1. 通用关键词搜索(支持指定字段精准搜索
if keyword: if keyword:
kw = f'%{keyword}%' kw = f'%{keyword}%'
query = query.filter( if search_field == 'name':
or_( query = query.filter(MaterialBase.name.ilike(kw))
MaterialBase.name.ilike(kw), elif search_field == 'spec':
MaterialBase.spec_model.ilike(kw), query = query.filter(MaterialBase.spec_model.ilike(kw))
MaterialBase.company_name.ilike(kw), elif search_field == 'common_name':
StockSemi.batch_number.ilike(kw), query = query.filter(MaterialBase.common_name.ilike(kw))
StockSemi.serial_number.ilike(kw), elif search_field == 'batch_number':
StockSemi.work_order_code.ilike(kw), query = query.filter(StockSemi.batch_number.ilike(kw))
StockSemi.bom_code.ilike(kw) elif search_field == 'work_order_code':
query = query.filter(StockSemi.work_order_code.ilike(kw))
elif search_field == 'bom_code':
query = query.filter(StockSemi.bom_code.ilike(kw))
else: # 'all' 默认全局模糊匹配
query = query.filter(
or_(
MaterialBase.name.ilike(kw),
MaterialBase.spec_model.ilike(kw),
MaterialBase.company_name.ilike(kw),
StockSemi.batch_number.ilike(kw),
StockSemi.serial_number.ilike(kw),
StockSemi.work_order_code.ilike(kw),
StockSemi.bom_code.ilike(kw)
)
) )
)
# 1.1 SKU 独立搜索 # 1.1 SKU 独立搜索
if sku and sku.strip(): if sku and sku.strip():
sku_str = f'%{sku.strip()}%' sku_str = f'%{sku.strip()}%'

View File

@ -5,11 +5,20 @@
<div class="filter-container"> <div class="filter-container">
<el-input <el-input
v-model="queryParams.keyword" v-model="queryParams.keyword"
placeholder="请输入名称、俗名或规格" placeholder="请输入搜索关键字"
style="width: 240px; margin-right: 10px;" style="width: 320px; margin-right: 10px;"
clearable clearable
@input="handleInputSearch" @keyup.enter="handleQuery"
/> >
<template #prepend>
<el-select v-model="queryParams.searchField" style="width: 90px">
<el-option label="全部" value="all" />
<el-option label="名称" value="name" />
<el-option label="俗名" value="common_name" />
<el-option label="规格" value="spec" />
</el-select>
</template>
</el-input>
<el-select <el-select
v-model="queryParams.company" v-model="queryParams.company"
@ -562,6 +571,7 @@ interface QueryParams {
pageNum: number; pageNum: number;
pageSize: number; pageSize: number;
keyword: string; keyword: string;
searchField: string;
category: string; category: string;
type: string; type: string;
company: string; company: string;
@ -746,6 +756,7 @@ const queryParams = reactive<QueryParams>({
pageNum: 1, pageNum: 1,
pageSize: 100, pageSize: 100,
keyword: '', keyword: '',
searchField: 'all',
category: '', category: '',
type: '', type: '',
company: '', company: '',
@ -966,6 +977,7 @@ const handleQuery = () => {
const resetQuery = () => { const resetQuery = () => {
queryParams.keyword = ''; queryParams.keyword = '';
queryParams.searchField = 'all';
queryParams.category = ''; queryParams.category = '';
queryParams.type = ''; queryParams.type = '';
queryParams.company = ''; queryParams.company = '';

View File

@ -17,15 +17,22 @@
<el-input <el-input
v-model="queryParams.keyword" v-model="queryParams.keyword"
placeholder="请输入名称、规格搜索..." placeholder="请输入搜索关键字"
class="filter-item-input" class="filter-item-input"
clearable clearable
@input="handleInputSearch"
@clear="fetchData" @clear="fetchData"
@keyup.enter="fetchData" @keyup.enter="fetchData"
style="width: 200px;" style="width: 280px;"
> >
<template #prefix><el-icon><Search /></el-icon></template> <template #prepend>
<el-select v-model="queryParams.searchField" style="width: 90px">
<el-option label="全部" value="all" />
<el-option label="名称" value="name" />
<el-option label="规格" value="spec" />
<el-option label="条码" value="barcode" />
<el-option label="批号" value="batch_number" />
</el-select>
</template>
</el-input> </el-input>
<el-input <el-input
@ -771,6 +778,7 @@ const queryParams = reactive({
page: 1, page: 1,
pageSize: 50, pageSize: 50,
keyword: '', keyword: '',
searchField: 'all',
sku: '', sku: '',
category: '', category: '',
material_type: '', material_type: '',
@ -1277,6 +1285,7 @@ const loadWarehouseTree = async () => {
const resetQuery = () => { const resetQuery = () => {
queryParams.keyword = '' queryParams.keyword = ''
queryParams.searchField = 'all'
queryParams.sku = '' queryParams.sku = ''
queryParams.category = '' queryParams.category = ''
queryParams.material_type = '' queryParams.material_type = ''

View File

@ -16,15 +16,22 @@
<el-input <el-input
v-model="queryParams.keyword" v-model="queryParams.keyword"
placeholder="请输入名称、规格搜索..." placeholder="请输入搜索关键字"
class="filter-item-input" class="filter-item-input"
clearable clearable
@input="handleInputSearch"
@clear="fetchData" @clear="fetchData"
@keyup.enter="fetchData" @keyup.enter="fetchData"
style="width: 200px;" style="width: 280px;"
> >
<template #prefix><el-icon><Search /></el-icon></template> <template #prepend>
<el-select v-model="queryParams.searchField" style="width: 90px">
<el-option label="全部" value="all" />
<el-option label="名称" value="name" />
<el-option label="规格" value="spec" />
<el-option label="序列号" value="serial_number" />
<el-option label="工单" value="work_order_code" />
</el-select>
</template>
</el-input> </el-input>
<el-input <el-input
@ -581,7 +588,7 @@ const dialogStatus = ref<'create' | 'update'>('create')
const tableData = ref([]) const tableData = ref([])
const total = ref(0) const total = ref(0)
const formRef = ref() const formRef = ref()
const queryParams = reactive({ page: 1, pageSize: 50, keyword: '', sku: '', category: '', material_type: '', statuses: ['在库', '借库'], company: '', orderByColumn: '', isAsc: '', advancedFilters: [] }) const queryParams = reactive({ page: 1, pageSize: 50, keyword: '', searchField: 'all', sku: '', category: '', material_type: '', statuses: ['在库', '借库'], company: '', orderByColumn: '', isAsc: '', advancedFilters: [] })
const categoryOptions = ref<string[]>([]) const categoryOptions = ref<string[]>([])
const typeOptions = ref<string[]>([]) const typeOptions = ref<string[]>([])
const companyOptions = ref<string[]>([]) // [新增] const companyOptions = ref<string[]>([]) // [新增]
@ -1018,6 +1025,7 @@ const loadWarehouseTree = async () => {
const resetQuery = () => { const resetQuery = () => {
queryParams.keyword = '' queryParams.keyword = ''
queryParams.searchField = 'all'
queryParams.sku = '' queryParams.sku = ''
queryParams.category = '' queryParams.category = ''
queryParams.material_type = '' queryParams.material_type = ''

View File

@ -17,15 +17,22 @@
<el-input <el-input
v-model="queryParams.keyword" v-model="queryParams.keyword"
placeholder="请输入名称、规格搜索..." placeholder="请输入搜索关键字"
class="filter-item-input" class="filter-item-input"
clearable clearable
@input="handleInputSearch"
@clear="fetchData" @clear="fetchData"
@keyup.enter="fetchData" @keyup.enter="fetchData"
style="width: 200px;" style="width: 280px;"
> >
<template #prefix><el-icon><Search /></el-icon></template> <template #prepend>
<el-select v-model="queryParams.searchField" style="width: 90px">
<el-option label="全部" value="all" />
<el-option label="名称" value="name" />
<el-option label="规格" value="spec" />
<el-option label="批号" value="batch_number" />
<el-option label=" BOM" value="bom_code" />
</el-select>
</template>
</el-input> </el-input>
<el-input <el-input
@ -647,7 +654,7 @@ const dialogStatus = ref<'create' | 'update'>('create')
const tableData = ref([]) const tableData = ref([])
const total = ref(0) const total = ref(0)
const formRef = ref() const formRef = ref()
const queryParams = reactive({ page: 1, pageSize: 50, keyword: '', sku: '', category: '', material_type: '', statuses: ['在库', '借库'], company: '', orderByColumn: '', isAsc: '', advancedFilters: [] }) const queryParams = reactive({ page: 1, pageSize: 50, keyword: '', searchField: 'all', sku: '', category: '', material_type: '', statuses: ['在库', '借库'], company: '', orderByColumn: '', isAsc: '', advancedFilters: [] })
const categoryOptions = ref<string[]>([]) const categoryOptions = ref<string[]>([])
const typeOptions = ref<string[]>([]) const typeOptions = ref<string[]>([])
const companyOptions = ref<string[]>([]) // [新增] const companyOptions = ref<string[]>([]) // [新增]
@ -1152,6 +1159,7 @@ const loadWarehouseTree = async () => {
const resetQuery = () => { const resetQuery = () => {
queryParams.keyword = '' queryParams.keyword = ''
queryParams.searchField = 'all'
queryParams.sku = '' queryParams.sku = ''
queryParams.category = '' queryParams.category = ''
queryParams.material_type = '' queryParams.material_type = ''