feat: add full-column sorting and advanced filtering to semi module

Co-authored-by: aider (openai/DeepSeek-V3.2-Thinking) <aider@aider.chat>
This commit is contained in:
dxc
2026-03-02 16:18:51 +08:00
parent 893be24071
commit 37f4b1a94f
3 changed files with 264 additions and 37 deletions

View File

@ -54,6 +54,36 @@
<el-button type="primary" plain class="search-btn" @click="fetchData">搜索</el-button>
<el-button class="reset-btn" @click="resetQuery">重置</el-button>
<el-popover
placement="bottom"
width="600"
trigger="click"
v-model:visible="advancedFilterVisible"
>
<template #reference>
<el-button type="primary" plain>高级筛选</el-button>
</template>
<div style="padding: 10px;">
<div v-for="(cond, idx) in advancedConditions" :key="idx" style="display: flex; gap: 10px; margin-bottom: 10px; align-items: center;">
<el-select v-model="cond.field" placeholder="字段" style="width: 150px;">
<el-option v-for="opt in fieldOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
</el-select>
<el-select v-model="cond.operator" placeholder="操作符" style="width: 120px;">
<el-option v-for="opt in operatorOptions" :key="opt.value" :label="opt.label" :value="opt.value" />
</el-select>
<el-input v-model="cond.value" placeholder="值" style="flex: 1;" />
<el-button type="danger" size="small" @click="removeCondition(idx)" :disabled="advancedConditions.length === 1">删除</el-button>
</div>
<div style="display: flex; justify-content: space-between; margin-top: 10px;">
<el-button type="primary" size="small" @click="addCondition">添加条件</el-button>
<div>
<el-button size="small" @click="resetAdvancedFilter">重置</el-button>
<el-button type="primary" size="small" @click="applyAdvancedFilter">应用</el-button>
</div>
</div>
</div>
</el-popover>
<el-select
v-model="queryParams.statuses"
multiple
@ -103,6 +133,7 @@
class="modern-table"
highlight-current-row
header-cell-class-name="table-header-gray"
@sort-change="handleSortChange"
>
<template v-for="col in allColumns" :key="col.prop">
<el-table-column
@ -110,6 +141,7 @@
:prop="col.prop"
:label="col.label"
:min-width="col.minWidth || '140'"
:sortable="col.sortable ? 'custom' : false"
show-overflow-tooltip
>
<template #default="scope" v-if="col.prop === 'material_name'">
@ -586,6 +618,30 @@ const queryParams = reactive({ page: 1, pageSize: 100, keyword: '', category: ''
const categoryOptions = ref<string[]>([])
const typeOptions = ref<string[]>([])
const companyOptions = ref<string[]>([]) // [新增]
const advancedFilterVisible = ref(false)
const advancedConditions = ref([{ field: '', operator: '', value: '' }])
const fieldOptions = ref([
{ label: '所属公司', value: 'company_name' },
{ label: '名称', value: 'material_name' },
{ label: '规格型号', value: 'spec_model' },
{ label: '类别', value: 'category' },
{ label: '类型', value: 'material_type' },
{ label: '状态', value: 'status' },
{ label: '质量状态', value: 'quality_status' },
{ label: '库位', value: 'warehouse_location' },
{ label: 'BOM编号', value: 'bom_code' },
{ label: '工单号', value: 'work_order_code' },
])
const operatorOptions = ref([
{ label: '等于', value: '=' },
{ label: '不等于', value: '!=' },
{ label: '包含', value: 'like' },
{ label: '不包含', value: 'not_like' },
{ label: '大于', value: '>' },
{ label: '小于', value: '<' },
{ label: '大于等于', value: '>=' },
{ label: '小于等于', value: '<=' },
])
const materialOptions = ref<any[]>([])
const searchPage = ref(1)
const searchKeyword = ref('')
@ -618,39 +674,39 @@ const modeLocked = ref(false)
// 列定义
const baseColumns = [
{prop: 'company_name', label: '所属公司'}, // [新增]
{prop: 'material_name', label: '名称'},
{prop: 'category', label: '类别'},
{prop: 'material_type', label: '类型'},
{prop: 'spec_model', label: '规格型号'},
{prop: 'unit', label: '单位'},
{prop: 'company_name', label: '所属公司', sortable: true}, // [新增]
{prop: 'material_name', label: '名称', sortable: true},
{prop: 'category', label: '类别', sortable: true},
{prop: 'material_type', label: '类型', sortable: true},
{prop: 'spec_model', label: '规格型号', sortable: true},
{prop: 'unit', label: '单位', sortable: true},
]
const stockColumns = [
{prop: 'id', label: 'ID', minWidth: '60'},
{prop: 'base_id', label: 'BaseID', minWidth: '80'},
{prop: 'sku', label: 'SKU', minWidth: '120'},
{prop: 'inbound_date', label: '入库日期', minWidth: '120'},
{prop: 'barcode', label: '条码', minWidth: '120'},
{prop: 'sn_bn', label: '序列号/批号', minWidth: '160'},
{prop: 'status', label: '状态', minWidth: '100'},
{prop: 'quality_status', label: '质量状态', minWidth: '100'},
{prop: 'qty_inbound', label: '入库量', minWidth: '100'},
{prop: 'qty_stock', label: '库存数', minWidth: '100'},
{prop: 'qty_available', label: '可用数', minWidth: '100'},
{prop: 'warehouse_loc', label: '库位', minWidth: '120'},
{prop: 'bom_code', label: 'BOM编号', minWidth: '120'},
{prop: 'bom_version', label: 'BOM版本', minWidth: '90'},
{prop: 'work_order_code', label: '工单号', minWidth: '120'},
{prop: 'raw_material_cost', label: '原料成本', minWidth: '100'},
{prop: 'unit_total_cost', label: '单件成本', minWidth: '100'},
{prop: 'total_price', label: '总成本', minWidth: '100'},
{prop: 'production_manager', label: '生产负责人', minWidth: '100'},
{prop: 'production_start_time', label: '生产开始', minWidth: '160'},
{prop: 'production_end_time', label: '生产结束', minWidth: '160'},
{prop: 'arrival_photo', label: '到货图', minWidth: '100'},
{prop: 'quality_report_link', label: '质量报告', minWidth: '100'},
{prop: 'detail_link', label: '详情链接', minWidth: '100'},
{prop: 'id', label: 'ID', minWidth: '60', sortable: true},
{prop: 'base_id', label: 'BaseID', minWidth: '80', sortable: true},
{prop: 'sku', label: 'SKU', minWidth: '120', sortable: true},
{prop: 'inbound_date', label: '入库日期', minWidth: '120', sortable: true},
{prop: 'barcode', label: '条码', minWidth: '120', sortable: true},
{prop: 'sn_bn', label: '序列号/批号', minWidth: '160', sortable: false},
{prop: 'status', label: '状态', minWidth: '100', sortable: true},
{prop: 'quality_status', label: '质量状态', minWidth: '100', sortable: true},
{prop: 'qty_inbound', label: '入库量', minWidth: '100', sortable: true},
{prop: 'qty_stock', label: '库存数', minWidth: '100', sortable: true},
{prop: 'qty_available', label: '可用数', minWidth: '100', sortable: true},
{prop: 'warehouse_loc', label: '库位', minWidth: '120', sortable: true},
{prop: 'bom_code', label: 'BOM编号', minWidth: '120', sortable: true},
{prop: 'bom_version', label: 'BOM版本', minWidth: '90', sortable: true},
{prop: 'work_order_code', label: '工单号', minWidth: '120', sortable: true},
{prop: 'raw_material_cost', label: '原料成本', minWidth: '100', sortable: true},
{prop: 'unit_total_cost', label: '单件成本', minWidth: '100', sortable: true},
{prop: 'total_price', label: '总成本', minWidth: '100', sortable: true},
{prop: 'production_manager', label: '生产负责人', minWidth: '100', sortable: true},
{prop: 'production_start_time', label: '生产开始', minWidth: '160', sortable: true},
{prop: 'production_end_time', label: '生产结束', minWidth: '160', sortable: true},
{prop: 'arrival_photo', label: '到货图', minWidth: '100', sortable: false},
{prop: 'quality_report_link', label: '质量报告', minWidth: '100', sortable: false},
{prop: 'detail_link', label: '详情链接', minWidth: '100', sortable: false},
]
const allColumns = [...baseColumns, ...stockColumns]
@ -943,10 +999,31 @@ const handleEntryModeChange = (val: string) => {
else { form.batch_number = ''; if(formRef.value) formRef.value.clearValidate('batch_number') }
}
const handleSortChange = ({ column, prop, order }: any) => {
if (order === 'ascending') {
queryParams.orderByColumn = prop
queryParams.isAsc = 'true'
} else if (order === 'descending') {
queryParams.orderByColumn = prop
queryParams.isAsc = 'false'
} else {
queryParams.orderByColumn = ''
queryParams.isAsc = ''
}
queryParams.page = 1
fetchData()
}
const fetchData = async () => {
loading.value = true
try {
const params = { ...queryParams, statuses: queryParams.statuses.join(',') }
const params = {
...queryParams,
statuses: queryParams.statuses.join(','),
orderByColumn: queryParams.orderByColumn,
isAsc: queryParams.isAsc,
advancedFilters: queryParams.advancedFilters.length > 0 ? JSON.stringify(queryParams.advancedFilters) : ''
}
const res: any = await getSemiList(params)
tableData.value = res.data.items || []
total.value = res.data.total || 0
@ -975,6 +1052,27 @@ const resetQuery = () => {
fetchData()
}
const addCondition = () => {
advancedConditions.value.push({ field: '', operator: '', value: '' })
}
const removeCondition = (index: number) => {
advancedConditions.value.splice(index, 1)
}
const applyAdvancedFilter = () => {
const validConditions = advancedConditions.value.filter(c => c.field && c.operator && c.value !== '')
queryParams.advancedFilters = validConditions
advancedFilterVisible.value = false
queryParams.page = 1
fetchData()
}
const resetAdvancedFilter = () => {
advancedConditions.value = [{ field: '', operator: '', value: '' }]
queryParams.advancedFilters = []
advancedFilterVisible.value = false
queryParams.page = 1
fetchData()
}
const handleCreate = () => {
dialogStatus.value = 'create'
resetForm()