diff --git a/inventory-backend/app/services/inbound/base_service.py b/inventory-backend/app/services/inbound/base_service.py index eecc8e2..635ebaa 100644 --- a/inventory-backend/app/services/inbound/base_service.py +++ b/inventory-backend/app/services/inbound/base_service.py @@ -36,12 +36,32 @@ class MaterialBaseService: # 支持搜索公司名 MaterialBase.company_name.ilike(f'%{keyword}%') ) - ).limit(20) + ) + + # [修改1] 增加返回数量限制 + # 原为 limit(20),现改为 1000,确保前端能获取所有(或足够多)的数据 + query = query.limit(1000) + + # 获取查询结果对象列表 + db_items = query.all() + + # [修改2] 规格型号排序逻辑 + # 要求:只考虑 '/' 前面的内容进行排序 + # 使用 Python 的 sort 方法,提取 spec_model 中 '/' 前的部分 + def get_sort_key(item): + if not item.spec_model: + return "" + # 如果包含 '/',取前半部分;否则取整个字符串 + parts = item.spec_model.split('/') + return parts[0] if len(parts) > 0 else item.spec_model + + # 执行排序 + db_items.sort(key=get_sort_key) results = [] - for item in query.all(): + for item in db_items: results.append({ - 'id': item.id, + 'id': item.id, # 必须保留ID供前端逻辑使用,视觉上的隐藏请在前端处理 'companyName': item.company_name, 'name': item.name, 'commonName': item.common_name, @@ -116,8 +136,10 @@ class MaterialBaseService: is_active = bool(int(filters['isEnabled'])) query = query.filter_by(is_enabled=is_active) - # 按 ID 倒序排列 - pagination = query.order_by(MaterialBase.id.desc()).paginate(page=page, per_page=limit, error_out=False) + # [修改3] 默认排序方式改为按 spec_model 排序 + # 如果需要更复杂的“/前内容”排序,通常直接按字符串排序也能满足前缀分组的需求 + pagination = query.order_by(MaterialBase.spec_model.asc()).paginate(page=page, per_page=limit, + error_out=False) items_list = [] for item in pagination.items: diff --git a/inventory-web/src/views/material/list.vue b/inventory-web/src/views/material/list.vue index cf9240c..a3132ad 100644 --- a/inventory-web/src/views/material/list.vue +++ b/inventory-web/src/views/material/list.vue @@ -30,8 +30,9 @@ filterable allow-create default-first-option - style="width: 140px; margin-right: 10px;" + style="width: 240px; margin-right: 10px;" @change="handleQuery" + popper-class="long-dropdown" > @@ -45,6 +46,7 @@ default-first-option style="width: 140px; margin-right: 10px;" @change="handleQuery" + popper-class="long-dropdown" > @@ -479,7 +481,8 @@ const cameraRef = ref | null>(null); const currentCameraField = ref<'generalImage' | 'generalManual'>('generalImage'); const columns = reactive({ - id: { visible: true }, + // [修改] 默认隐藏 ID + id: { visible: false }, companyName: { visible: true }, name: { visible: true }, commonName: { visible: true }, @@ -498,9 +501,8 @@ const categoryOptions = ref([]); const typeOptions = ref([]); const categoryTreeOptions = ref([]); -// [修改] 将类别拆分为前后两部分进行绑定 -const tempCategoryPrefix = ref([]); // 前缀部分 (Cascader) -const tempCategorySuffix = ref(''); // 后缀部分 (Input) +const tempCategoryPrefix = ref([]); +const tempCategorySuffix = ref(''); const queryParams = reactive({ pageNum: 1, @@ -537,26 +539,20 @@ const initForm = { const form = ref({...initForm}); -// [新增] 自定义验证规则:确保拼合后的类别符合4层结构 const validateCategoryLevel = (rule: any, value: any, callback: any) => { - // 实时计算拼合结果 const prefixStr = tempCategoryPrefix.value.join('/'); const suffixStr = tempCategorySuffix.value.trim(); - // 如果两边都为空,报错 if (!prefixStr && !suffixStr) { callback(new Error('请填写或选择类别')); return; } - // 拼合路径 let fullPath = ''; if (prefixStr && suffixStr) fullPath = prefixStr + '/' + suffixStr; else if (prefixStr) fullPath = prefixStr; else fullPath = suffixStr; - // 检查层级数量 (以 / 分割后的数组长度) - // 4层结构意味着有3个斜杠,例如 A/B/C/D => length 4 const levels = fullPath.split('/').filter(p => p.trim() !== '').length; if (levels !== 4) { @@ -569,7 +565,6 @@ const validateCategoryLevel = (rule: any, value: any, callback: any) => { const rules = reactive({ name: [{ required: true, message: '请输入基础信息名称', trigger: 'blur' }], companyName: [{ required: true, message: '请输入公司名称', trigger: 'change' }], - // [修改] 使用自定义验证器 category: [{ required: true, validator: validateCategoryLevel, trigger: 'change' }], type: [{ required: true, message: '请选择或输入类型', trigger: 'change' }], spec: [{ required: true, message: '请输入规格型号', trigger: 'blur' }], @@ -678,14 +673,12 @@ const handleSizeChange = (command: 'large' | 'default' | 'small') => { tableSize.value = command; }; -// 新增:分页大小改变处理 const handlePageSizeChange = (val: number) => { queryParams.pageSize = val; - queryParams.pageNum = 1; // 切换大小时重置到第一页 + queryParams.pageNum = 1; getList(); }; -// 新增:当前页码改变处理 const handlePageCurrentChange = (val: number) => { queryParams.pageNum = val; getList(); @@ -706,11 +699,9 @@ const handleEdit = (row: MaterialBaseVO) => { const data = JSON.parse(JSON.stringify(row)); Object.assign(form.value, data); - // [修改] 解析已有 category,拆分为 Prefix 和 Suffix if (data.category) { const parts = data.category.split('/'); if (parts.length > 0) { - // 取最后一部分作为 Suffix,其余作为 Prefix tempCategorySuffix.value = parts.pop() || ''; tempCategoryPrefix.value = parts; } else { @@ -722,7 +713,6 @@ const handleEdit = (row: MaterialBaseVO) => { tempCategorySuffix.value = ''; } - // 初始化文件列表 const images = row.generalImage || []; const manuals = row.generalManual || []; @@ -776,7 +766,6 @@ const submitForm = async () => { const finalManualList = form.value.generalManual.filter(item => !isExternalLink(item)); if (manualExternalUrl.value) finalManualList.push(manualExternalUrl.value); - // [修改] 提交前组合字符串:Prefix + / + Suffix const prefixStr = tempCategoryPrefix.value.join('/'); const suffixStr = tempCategorySuffix.value.trim(); let fullCategory = ''; @@ -817,7 +806,6 @@ const resetForm = () => { fileListImage.value = []; fileListManual.value = []; - // [修改] 重置前后缀 tempCategoryPrefix.value = []; tempCategorySuffix.value = ''; @@ -982,14 +970,12 @@ onMounted(() => { flex-direction: column; } -/* 新增:分页样式 */ .pagination-container { margin-top: 15px; display: flex; justify-content: flex-start; } -/* 上传相关样式 */ .upload-container { display: flex; flex-wrap: wrap; gap: 8px; } :deep(.el-upload--picture-card) { width: 100px; height: 100px; line-height: 100px; } :deep(.el-upload-list--picture-card .el-upload-list__item) { width: 100px; height: 100px; } @@ -998,7 +984,14 @@ onMounted(() => { .camera-card .text { font-size: 12px; margin-top: 5px; } .camera-card .el-icon { font-size: 24px; } -/* 表格缩略图样式 */ .file-preview-cell { display: flex; align-items: center; justify-content: center; position: relative; } .more-badge { position: absolute; top: -5px; right: -5px; background: #909399; color: #fff; border-radius: 10px; padding: 0 4px; font-size: 10px; transform: scale(0.9); } + + + + \ No newline at end of file