diff --git a/inventory-backend/app/api/v1/inbound/service.py b/inventory-backend/app/api/v1/inbound/service.py index 017b15a..7ce3034 100644 --- a/inventory-backend/app/api/v1/inbound/service.py +++ b/inventory-backend/app/api/v1/inbound/service.py @@ -6,6 +6,22 @@ from app.services.inbound.service_service import ServiceService from app.utils.decorators import role_required +@inbound_bp.route('/service/search-base', methods=['GET']) +@jwt_required() +def search_base(): + """搜索基础物料""" + keyword = request.args.get('keyword', '') + try: + data = ServiceService.search_base_material(keyword) + return jsonify({ + 'code': 200, + 'msg': 'success', + 'data': data + }) + except Exception as e: + current_app.logger.error(f'搜索基础物料失败: {str(e)}') + return jsonify({'code': 500, 'msg': '内部服务器错误'}), 500 + @inbound_bp.route('/service', methods=['GET']) @jwt_required() def get_service_list(): diff --git a/inventory-backend/app/services/inbound/service_service.py b/inventory-backend/app/services/inbound/service_service.py index 4dea2e4..b97250d 100644 --- a/inventory-backend/app/services/inbound/service_service.py +++ b/inventory-backend/app/services/inbound/service_service.py @@ -33,6 +33,35 @@ class ServiceService: suffix = str(suffix_num).zfill(cls.SKU_SUFFIX_LEN) return f'{prefix}{suffix}' + @classmethod + def search_base_material(cls, keyword): + """搜索基础物料,供前端远程选择""" + try: + query = MaterialBase.query.filter(MaterialBase.is_enabled == True) + if keyword: + query = query.filter( + db.or_( + MaterialBase.name.ilike(f'%{keyword}%'), + MaterialBase.spec_model.ilike(f'%{keyword}%'), + ) + ) + query = query.order_by(MaterialBase.id.desc()).limit(20) + results = [] + for item in query.all(): + results.append({ + 'id': item.id, + 'name': item.name, + 'spec': item.spec_model, + 'category': item.category, + 'unit': item.unit, + 'type': item.material_type, + }) + return results + except Exception as e: + import traceback + traceback.print_exc() + return [] + @classmethod def create_service(cls, data): """创建服务权益记录""" diff --git a/inventory-web/src/api/inbound/service.ts b/inventory-web/src/api/inbound/service.ts index e5dc209..10efeca 100644 --- a/inventory-web/src/api/inbound/service.ts +++ b/inventory-web/src/api/inbound/service.ts @@ -47,6 +47,15 @@ export interface ServiceUpdateRequest { description?: string } +export interface MaterialBaseItem { + id: number + name: string + spec: string + category: string + unit: string + type: string +} + // 获取服务权益列表 export function getServiceList(params: ServiceQueryParams) { return request({ @@ -82,6 +91,19 @@ export function updateService(id: number, data: ServiceUpdateRequest) { }) } +// 搜索基础物料 +export function searchMaterialBase(keyword: string) { + return request<{ + code: number + msg: string + data: MaterialBaseItem[] + }>({ + url: '/v1/inbound/service/search-base', + method: 'get', + params: { keyword } + }) +} + // 删除服务权益 export function deleteService(id: number) { return request({ diff --git a/inventory-web/src/views/stock/inbound/service.vue b/inventory-web/src/views/stock/inbound/service.vue index c72f584..e2d1859 100644 --- a/inventory-web/src/views/stock/inbound/service.vue +++ b/inventory-web/src/views/stock/inbound/service.vue @@ -87,15 +87,52 @@ @close="resetDialog" > - - -
需要先创建基础物料
-
+ + + + + +
+ {{ item.name }} + {{ item.spec }} +
+
+
+
+
+ + + 未输入时展示最新物料;输入关键词进行精确搜索。 + + +
+ +
+ + + + + + + +
import { ref, reactive, onMounted } from 'vue' +import { InfoFilled } from '@element-plus/icons-vue' import type { FormInstance, FormRules } from 'element-plus' import { ElMessage, ElMessageBox } from 'element-plus' import { @@ -140,9 +178,11 @@ import { createService, updateService, deleteService, + searchMaterialBase, type ServiceItem, type ServiceQueryParams, - type ServiceCreateRequest + type ServiceCreateRequest, + type MaterialBaseItem } from '@/api/inbound/service' // 表格数据 @@ -152,6 +192,9 @@ const page = ref(1) const perPage = ref(20) const total = ref(0) +const materialOptions = ref([]) +const searchLoading = ref(false) + const searchForm = reactive({ keyword: '', provider_name: '', @@ -209,6 +252,39 @@ const handlePageChange = (val: number) => { loadData() } +const handleMaterialDropdownVisible = (visible: boolean) => { + if (visible && materialOptions.value.length === 0) { + handleSearchMaterial('') + } +} + +const handleSearchMaterial = async (query: string) => { + searchLoading.value = true + try { + const res = await searchMaterialBase(query) + if (res.code === 200) { + materialOptions.value = res.data + } else { + materialOptions.value = [] + } + } catch (error) { + materialOptions.value = [] + } finally { + searchLoading.value = false + } +} + +const onMaterialSelected = (val: number) => { + const item = materialOptions.value.find(i => i.id === val) + if (item) { + form.material_name = item.name + form.spec_model = item.spec + form.category = item.category + form.unit = item.unit + form.material_type = item.type + } +} + // 弹窗相关 const dialogVisible = ref(false) const dialogTitle = ref('') @@ -216,6 +292,11 @@ const formRef = ref() const form = reactive({ id: 0, base_id: 0, + material_name: '', + spec_model: '', + category: '', + unit: '', + material_type: '', sale_price: 0, provider_name: '', description: '' @@ -250,6 +331,11 @@ const handleEdit = (row: ServiceItem) => { Object.assign(form, { id: row.id, base_id: row.base_id, + material_name: row.material_name || '', + spec_model: row.spec_model || '', + category: row.category || '', + unit: row.unit || '', + material_type: row.material_type || '', sale_price: row.sale_price, provider_name: row.provider_name, description: row.description