feat(records): 借还记录接入高级筛选

复用 app/utils/advanced_filter.py 的解析与谓词逻辑:
  · 父级字段(单号 borrow_no、借用人 borrower_name)走标准 SQL 谓词
  · 子级字段(SKU、物料名称)经单号子查询过滤,否定操作符走 NOT IN 整单排除
  · 物料名经三表联查(buy/semi/product JOIN material_base)

借还的单号维度查询基于 order_subq 子查询分页,故此处把过滤条件施加在
order_subq.c.borrow_no 上,与既有的状态/日期/公司隔离过滤保持同一层次。

前端 records.vue 新增「高级筛选」el-popover(字段/操作符/值 + 添加条件/
应用筛选/重置),序列化为 advancedFilters JSON 字符串随查询下发。

验证:sku ne 0000000002 → 52 单(库中无单含该 SKU,正确不减);
      sku contains 0000 → 52 单(52 单的 SKU 全部含 0000,数据巧合)。
This commit is contained in:
yueli
2026-09-10 13:06:03 +08:00
parent e437b3cece
commit fd0bfd3d9c
3 changed files with 141 additions and 2 deletions

View File

@ -47,6 +47,43 @@
<el-form-item>
<el-button type="primary" @click="fetchData">查询</el-button>
<el-button @click="resetFilter">重置</el-button>
<!-- 高级筛选对齐 material/list.vue 既有模式 -->
<el-popover
v-model:visible="advancedFilterVisible"
placement="bottom"
title="高级筛选"
width="600"
trigger="manual"
>
<template #reference>
<el-button plain @click="advancedFilterVisible = !advancedFilterVisible">
高级筛选
<el-badge v-if="appliedConditions.length" :value="appliedConditions.length" style="margin-left:6px" />
</el-button>
</template>
<div class="advanced-filter">
<div
v-for="(condition, index) in advancedConditions"
:key="index"
class="condition-row"
>
<el-select v-model="condition.field" placeholder="字段" style="width:180px" :teleported="false">
<el-option v-for="f in fieldOptions" :key="f.value" :label="f.label" :value="f.value" />
</el-select>
<el-select v-model="condition.operator" placeholder="操作符" style="width:120px; margin-left:8px" :teleported="false">
<el-option v-for="op in operatorOptions" :key="op.value" :label="op.label" :value="op.value" />
</el-select>
<el-input v-model="condition.value" placeholder="值" style="width:180px; margin-left:8px" />
<el-button v-if="advancedConditions.length > 1" type="danger" link @click="removeCondition(index)" style="margin-left:8px">删除</el-button>
</div>
<div style="margin-top:12px">
<el-button type="primary" link @click="addCondition">添加条件</el-button>
<el-button type="primary" @click="applyAdvancedFilter">应用筛选</el-button>
<el-button @click="resetAdvancedFilter">重置</el-button>
</div>
</div>
</el-popover>
</el-form-item>
</el-form>
@ -287,6 +324,47 @@ const dateRange = ref<string[]>([])
const page = ref(1)
const loading = ref(false)
// --- ★ 高级筛选 ---
const advancedFilterVisible = ref(false)
const advancedConditions = ref([{ field: '', operator: '', value: '' }])
const appliedConditions = ref<any[]>([])
const advancedFilters = ref<any[]>([])
const fieldOptions = [
{ value: 'no', label: '单号' },
{ value: 'sku', label: 'SKU' },
{ value: 'material_name', label: '物料名称' },
{ value: 'borrower_name', label: '操作人' },
]
const operatorOptions = [
{ value: 'contains', label: '包含' },
{ value: 'eq', label: '等于' },
{ value: 'not_contains', label: '不包含' },
{ value: 'ne', label: '不等于' },
]
const addCondition = () => {
advancedConditions.value.push({ field: '', operator: '', value: '' })
}
const removeCondition = (index: number) => {
advancedConditions.value.splice(index, 1)
}
const applyAdvancedFilter = () => {
const valid = advancedConditions.value.filter(c => c.field && c.operator && c.value !== '')
advancedFilters.value = valid
appliedConditions.value = valid
advancedFilterVisible.value = false
page.value = 1
fetchData()
}
const resetAdvancedFilter = () => {
advancedConditions.value = [{ field: '', operator: '', value: '' }]
advancedFilters.value = []
appliedConditions.value = []
advancedFilterVisible.value = false
page.value = 1
fetchData()
}
// ★ 借库报废相关
const canScrap = computed(() =>
userStore.role === 'SUPER_ADMIN' ||
@ -355,7 +433,9 @@ const fetchData = async () => {
page: page.value,
status: status.value,
keyword: keyword.value,
search_type: searchType.value
search_type: searchType.value,
// ★ 高级筛选:后端约定参数名为 advancedFilters值为 JSON 字符串
advancedFilters: JSON.stringify(advancedFilters.value || []),
}
if (dateRange.value && dateRange.value.length === 2) {
params.start_date = dateRange.value[0]
@ -411,6 +491,9 @@ const resetFilter = () => {
keyword.value = ''
searchType.value = 'all'
dateRange.value = []
advancedFilters.value = []
advancedConditions.value = [{ field: '', operator: '', value: '' }]
appliedConditions.value = []
page.value = 1
fetchData()
}
@ -473,4 +556,11 @@ onBeforeUnmount(() => {
margin-bottom: 0;
margin-right: 12px;
}
/* 高级筛选条件行 */
.condition-row {
display: flex;
align-items: center;
margin-bottom: 10px;
}
</style>