Compare commits
3 Commits
18da3979a9
...
8cae6ee7f6
| Author | SHA1 | Date | |
|---|---|---|---|
| 8cae6ee7f6 | |||
| 94ff7cecdc | |||
| 17a61b489c |
@ -110,4 +110,26 @@ def get_history(id):
|
|||||||
})
|
})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return jsonify({"code": 500, "msg": str(e)}), 500
|
return jsonify({"code": 500, "msg": str(e)}), 500
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# 6. 供应商建议
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
@inbound_buy_bp.route('/suggestions/suppliers', methods=['GET'])
|
||||||
|
def get_supplier_suggestions():
|
||||||
|
base_id = request.args.get('base_id', type=int)
|
||||||
|
if not base_id:
|
||||||
|
return jsonify({"code": 400, "msg": "base_id required"}), 400
|
||||||
|
data = BuyInboundService.get_history_suppliers(base_id)
|
||||||
|
return jsonify({"code": 200, "msg": "success", "data": data})
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# 7. 系统用户建议
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
@inbound_buy_bp.route('/suggestions/users', methods=['GET'])
|
||||||
|
def get_user_suggestions():
|
||||||
|
keyword = request.args.get('keyword', '')
|
||||||
|
data = BuyInboundService.search_system_users(keyword)
|
||||||
|
return jsonify({"code": 200, "msg": "success", "data": data})
|
||||||
|
|||||||
@ -103,4 +103,14 @@ def get_history(id):
|
|||||||
return jsonify({"code": 200, "msg": "success", "data": data})
|
return jsonify({"code": 200, "msg": "success", "data": data})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return jsonify({"code": 500, "msg": str(e)}), 500
|
return jsonify({"code": 500, "msg": str(e)}), 500
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# 6. 系统用户建议
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
@inbound_product_bp.route('/suggestions/users', methods=['GET'])
|
||||||
|
def get_user_suggestions():
|
||||||
|
keyword = request.args.get('keyword', '')
|
||||||
|
data = ProductInboundService.search_system_users(keyword)
|
||||||
|
return jsonify({"code": 200, "msg": "success", "data": data})
|
||||||
|
|||||||
@ -117,4 +117,14 @@ def get_history(id):
|
|||||||
})
|
})
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return jsonify({"code": 500, "msg": str(e)}), 500
|
return jsonify({"code": 500, "msg": str(e)}), 500
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# 6. 系统用户建议
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
@inbound_semi_bp.route('/suggestions/users', methods=['GET'])
|
||||||
|
def get_user_suggestions():
|
||||||
|
keyword = request.args.get('keyword', '')
|
||||||
|
data = SemiInboundService.search_system_users(keyword)
|
||||||
|
return jsonify({"code": 200, "msg": "success", "data": data})
|
||||||
|
|||||||
@ -138,3 +138,27 @@ def delete_service(service_id):
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
current_app.logger.error(f'删除服务权益失败: {str(e)}')
|
current_app.logger.error(f'删除服务权益失败: {str(e)}')
|
||||||
return jsonify({'code': 500, 'msg': '内部服务器错误'}), 500
|
return jsonify({'code': 500, 'msg': '内部服务器错误'}), 500
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# 供应商建议
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
@inbound_bp.route('/service/suggestions/providers', methods=['GET'])
|
||||||
|
@jwt_required()
|
||||||
|
def get_provider_suggestions():
|
||||||
|
base_id = request.args.get('base_id', type=int)
|
||||||
|
if not base_id:
|
||||||
|
return jsonify({'code': 400, 'msg': 'base_id required'}), 400
|
||||||
|
data = ServiceService.get_history_providers(base_id)
|
||||||
|
return jsonify({'code': 200, 'msg': 'success', 'data': data})
|
||||||
|
|
||||||
|
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
# 系统用户建议
|
||||||
|
# ------------------------------------------------------------------
|
||||||
|
@inbound_bp.route('/service/suggestions/users', methods=['GET'])
|
||||||
|
@jwt_required()
|
||||||
|
def get_user_suggestions():
|
||||||
|
keyword = request.args.get('keyword', '')
|
||||||
|
data = ServiceService.search_system_users(keyword)
|
||||||
|
return jsonify({'code': 200, 'msg': 'success', 'data': data})
|
||||||
|
|||||||
@ -60,7 +60,9 @@ class BuyInboundService:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def search_base_material(keyword):
|
def search_base_material(keyword):
|
||||||
try:
|
try:
|
||||||
|
# [核心修改] 只查询已启用的物料,防止选择已禁用的历史物料
|
||||||
query = MaterialBase.query.filter(MaterialBase.is_enabled == True)
|
query = MaterialBase.query.filter(MaterialBase.is_enabled == True)
|
||||||
|
|
||||||
if keyword:
|
if keyword:
|
||||||
query = query.filter(
|
query = query.filter(
|
||||||
or_(
|
or_(
|
||||||
@ -100,6 +102,10 @@ class BuyInboundService:
|
|||||||
if not material:
|
if not material:
|
||||||
raise ValueError("所选物料不存在")
|
raise ValueError("所选物料不存在")
|
||||||
|
|
||||||
|
# [核心修改] 后端二次校验:如果物料已停用,禁止入库
|
||||||
|
if not material.is_enabled:
|
||||||
|
raise ValueError(f"物料【{material.name}】已停用,无法办理新入库。")
|
||||||
|
|
||||||
# --- [修复点] 执行唯一性校验 ---
|
# --- [修复点] 执行唯一性校验 ---
|
||||||
BuyInboundService._check_unique(
|
BuyInboundService._check_unique(
|
||||||
base_id=base_id,
|
base_id=base_id,
|
||||||
@ -345,4 +351,46 @@ class BuyInboundService:
|
|||||||
return {"total": pagination.total, "items": items}
|
return {"total": pagination.total, "items": items}
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return {"total": 0, "items": []}
|
return {"total": 0, "items": []}
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 6. 供应商历史查询
|
||||||
|
# ============================================================
|
||||||
|
@staticmethod
|
||||||
|
def get_history_suppliers(base_id):
|
||||||
|
"""返回该物料关联的供应商列表(去重)"""
|
||||||
|
try:
|
||||||
|
query = db.session.query(StockBuy.supplier_name).filter(
|
||||||
|
StockBuy.base_id == base_id,
|
||||||
|
StockBuy.supplier_name.isnot(None)
|
||||||
|
).distinct().order_by(StockBuy.supplier_name)
|
||||||
|
suppliers = [row[0] for row in query.all()]
|
||||||
|
return suppliers
|
||||||
|
except Exception:
|
||||||
|
return []
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 7. 系统用户搜索
|
||||||
|
# ============================================================
|
||||||
|
@staticmethod
|
||||||
|
def search_system_users(keyword):
|
||||||
|
"""搜索系统用户(活跃状态)"""
|
||||||
|
from app.models.system import SysUser
|
||||||
|
try:
|
||||||
|
query = SysUser.query.filter(SysUser.status == 'active')
|
||||||
|
if keyword:
|
||||||
|
kw = f'%{keyword}%'
|
||||||
|
query = query.filter(db.or_(
|
||||||
|
SysUser.username.ilike(kw),
|
||||||
|
SysUser.email.ilike(kw)
|
||||||
|
))
|
||||||
|
query = query.order_by(SysUser.username)
|
||||||
|
users = []
|
||||||
|
for u in query.limit(20).all():
|
||||||
|
users.append({
|
||||||
|
'value': u.username,
|
||||||
|
'email': u.email
|
||||||
|
})
|
||||||
|
return users
|
||||||
|
except Exception:
|
||||||
|
return []
|
||||||
|
|||||||
@ -40,7 +40,7 @@ class ProductInboundService:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def search_base_material(keyword):
|
def search_base_material(keyword):
|
||||||
try:
|
try:
|
||||||
# 1. 基础查询:必须是已启用的物料
|
# [核心修改] 只查询已启用的物料
|
||||||
query = MaterialBase.query.filter(MaterialBase.is_enabled == True)
|
query = MaterialBase.query.filter(MaterialBase.is_enabled == True)
|
||||||
|
|
||||||
# 2. 动态条件:如果传入了关键词,则增加模糊匹配条件
|
# 2. 动态条件:如果传入了关键词,则增加模糊匹配条件
|
||||||
@ -85,6 +85,10 @@ class ProductInboundService:
|
|||||||
material = MaterialBase.query.get(base_id)
|
material = MaterialBase.query.get(base_id)
|
||||||
if not material: raise ValueError("物料不存在")
|
if not material: raise ValueError("物料不存在")
|
||||||
|
|
||||||
|
# [核心修改] 后端二次校验:如果物料已停用,禁止入库
|
||||||
|
if not material.is_enabled:
|
||||||
|
raise ValueError(f"物料【{material.name}】已停用,无法办理新入库。")
|
||||||
|
|
||||||
# --- [核心修改] 执行唯一性校验 ---
|
# --- [核心修改] 执行唯一性校验 ---
|
||||||
ProductInboundService._check_unique(
|
ProductInboundService._check_unique(
|
||||||
serial_number=data.get('serial_number')
|
serial_number=data.get('serial_number')
|
||||||
@ -346,4 +350,30 @@ class ProductInboundService:
|
|||||||
return {"total": pagination.total, "items": items}
|
return {"total": pagination.total, "items": items}
|
||||||
except:
|
except:
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return {"total": 0, "items": []}
|
return {"total": 0, "items": []}
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 7. 系统用户搜索
|
||||||
|
# ============================================================
|
||||||
|
@staticmethod
|
||||||
|
def search_system_users(keyword):
|
||||||
|
"""搜索系统用户(活跃状态)"""
|
||||||
|
from app.models.system import SysUser
|
||||||
|
try:
|
||||||
|
query = SysUser.query.filter(SysUser.status == 'active')
|
||||||
|
if keyword:
|
||||||
|
kw = f'%{keyword}%'
|
||||||
|
query = query.filter(db.or_(
|
||||||
|
SysUser.username.ilike(kw),
|
||||||
|
SysUser.email.ilike(kw)
|
||||||
|
))
|
||||||
|
query = query.order_by(SysUser.username)
|
||||||
|
users = []
|
||||||
|
for u in query.limit(20).all():
|
||||||
|
users.append({
|
||||||
|
'value': u.username,
|
||||||
|
'email': u.email
|
||||||
|
})
|
||||||
|
return users
|
||||||
|
except Exception:
|
||||||
|
return []
|
||||||
|
|||||||
@ -54,7 +54,7 @@ class SemiInboundService:
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def search_base_material(keyword):
|
def search_base_material(keyword):
|
||||||
try:
|
try:
|
||||||
# 基础查询:必须是已启用的物料
|
# [核心修改] 只查询已启用的物料
|
||||||
query = MaterialBase.query.filter(MaterialBase.is_enabled == True)
|
query = MaterialBase.query.filter(MaterialBase.is_enabled == True)
|
||||||
|
|
||||||
# 如果有关键词,进行模糊匹配
|
# 如果有关键词,进行模糊匹配
|
||||||
@ -101,6 +101,10 @@ class SemiInboundService:
|
|||||||
if not material:
|
if not material:
|
||||||
raise ValueError(f"ID为 {base_id} 的基础物料不存在")
|
raise ValueError(f"ID为 {base_id} 的基础物料不存在")
|
||||||
|
|
||||||
|
# [核心修改] 后端二次校验:如果物料已停用,禁止入库
|
||||||
|
if not material.is_enabled:
|
||||||
|
raise ValueError(f"物料【{material.name}】已停用,无法办理新入库。")
|
||||||
|
|
||||||
# --- [核心修改] 执行唯一性校验 ---
|
# --- [核心修改] 执行唯一性校验 ---
|
||||||
SemiInboundService._check_unique(
|
SemiInboundService._check_unique(
|
||||||
base_id=base_id,
|
base_id=base_id,
|
||||||
@ -448,4 +452,30 @@ class SemiInboundService:
|
|||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(f"List Error: {e}")
|
print(f"List Error: {e}")
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
return {"total": 0, "items": []}
|
return {"total": 0, "items": []}
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 7. 系统用户搜索
|
||||||
|
# ============================================================
|
||||||
|
@staticmethod
|
||||||
|
def search_system_users(keyword):
|
||||||
|
"""搜索系统用户(活跃状态)"""
|
||||||
|
from app.models.system import SysUser
|
||||||
|
try:
|
||||||
|
query = SysUser.query.filter(SysUser.status == 'active')
|
||||||
|
if keyword:
|
||||||
|
kw = f'%{keyword}%'
|
||||||
|
query = query.filter(db.or_(
|
||||||
|
SysUser.username.ilike(kw),
|
||||||
|
SysUser.email.ilike(kw)
|
||||||
|
))
|
||||||
|
query = query.order_by(SysUser.username)
|
||||||
|
users = []
|
||||||
|
for u in query.limit(20).all():
|
||||||
|
users.append({
|
||||||
|
'value': u.username,
|
||||||
|
'email': u.email
|
||||||
|
})
|
||||||
|
return users
|
||||||
|
except Exception:
|
||||||
|
return []
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
# app/services/inbound/service_service.py
|
||||||
from app import db
|
from app import db
|
||||||
from app.models.inbound.service import StockService
|
from app.models.inbound.service import StockService
|
||||||
from app.models.base import MaterialBase
|
from app.models.base import MaterialBase
|
||||||
@ -37,7 +38,9 @@ class ServiceService:
|
|||||||
def search_base_material(cls, keyword):
|
def search_base_material(cls, keyword):
|
||||||
"""搜索基础物料,供前端远程选择"""
|
"""搜索基础物料,供前端远程选择"""
|
||||||
try:
|
try:
|
||||||
|
# [核心修改] 只查询已启用的物料
|
||||||
query = MaterialBase.query.filter(MaterialBase.is_enabled == True)
|
query = MaterialBase.query.filter(MaterialBase.is_enabled == True)
|
||||||
|
|
||||||
if keyword:
|
if keyword:
|
||||||
query = query.filter(
|
query = query.filter(
|
||||||
db.or_(
|
db.or_(
|
||||||
@ -66,9 +69,15 @@ class ServiceService:
|
|||||||
def create_service(cls, data):
|
def create_service(cls, data):
|
||||||
"""创建服务权益记录"""
|
"""创建服务权益记录"""
|
||||||
# 检查基础物料是否存在
|
# 检查基础物料是否存在
|
||||||
base = MaterialBase.query.get(data.get('base_id'))
|
base_id = data.get('base_id')
|
||||||
|
base = MaterialBase.query.get(base_id)
|
||||||
if not base:
|
if not base:
|
||||||
raise ValueError('基础物料不存在')
|
raise ValueError('基础物料不存在')
|
||||||
|
|
||||||
|
# [核心修改] 后端二次校验:如果物料已停用,禁止创建服务权益
|
||||||
|
if not base.is_enabled:
|
||||||
|
raise ValueError(f"物料【{base.name}】已停用,无法创建新的服务权益。")
|
||||||
|
|
||||||
# 生成SKU
|
# 生成SKU
|
||||||
sku = cls._generate_sku()
|
sku = cls._generate_sku()
|
||||||
service = StockService(
|
service = StockService(
|
||||||
@ -144,12 +153,54 @@ class ServiceService:
|
|||||||
# 总数
|
# 总数
|
||||||
total = query.count()
|
total = query.count()
|
||||||
# 分页
|
# 分页
|
||||||
items = query.order_by(StockService.created_at.desc())\
|
items = query.order_by(StockService.created_at.desc()) \
|
||||||
.offset((page - 1) * per_page)\
|
.offset((page - 1) * per_page) \
|
||||||
.limit(per_page).all()
|
.limit(per_page).all()
|
||||||
return {
|
return {
|
||||||
'items': [item.to_dict() for item in items],
|
'items': [item.to_dict() for item in items],
|
||||||
'total': total,
|
'total': total,
|
||||||
'page': page,
|
'page': page,
|
||||||
'per_page': per_page
|
'per_page': per_page
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 供应商历史查询
|
||||||
|
# ============================================================
|
||||||
|
@classmethod
|
||||||
|
def get_history_providers(cls, base_id):
|
||||||
|
"""返回该物料关联的服务商列表(去重)"""
|
||||||
|
try:
|
||||||
|
query = db.session.query(StockService.provider_name).filter(
|
||||||
|
StockService.base_id == base_id,
|
||||||
|
StockService.provider_name.isnot(None)
|
||||||
|
).distinct().order_by(StockService.provider_name)
|
||||||
|
providers = [row[0] for row in query.all()]
|
||||||
|
return providers
|
||||||
|
except Exception:
|
||||||
|
return []
|
||||||
|
|
||||||
|
# ============================================================
|
||||||
|
# 系统用户搜索
|
||||||
|
# ============================================================
|
||||||
|
@classmethod
|
||||||
|
def search_system_users(cls, keyword):
|
||||||
|
"""搜索系统用户(活跃状态)"""
|
||||||
|
from app.models.system import SysUser
|
||||||
|
try:
|
||||||
|
query = SysUser.query.filter(SysUser.status == 'active')
|
||||||
|
if keyword:
|
||||||
|
kw = f'%{keyword}%'
|
||||||
|
query = query.filter(db.or_(
|
||||||
|
SysUser.username.ilike(kw),
|
||||||
|
SysUser.email.ilike(kw)
|
||||||
|
))
|
||||||
|
query = query.order_by(SysUser.username)
|
||||||
|
users = []
|
||||||
|
for u in query.limit(20).all():
|
||||||
|
users.append({
|
||||||
|
'value': u.username,
|
||||||
|
'email': u.email
|
||||||
|
})
|
||||||
|
return users
|
||||||
|
except Exception:
|
||||||
|
return []
|
||||||
|
|||||||
@ -60,4 +60,22 @@ export function deleteFile(filename: string) {
|
|||||||
url: `/common/files/${filename}`, // 对应后端 /api/v1/common/files/<filename>
|
url: `/common/files/${filename}`, // 对应后端 /api/v1/common/files/<filename>
|
||||||
method: 'delete'
|
method: 'delete'
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 8. 供应商建议
|
||||||
|
export function getSupplierSuggestions(params: any) {
|
||||||
|
return request({
|
||||||
|
url: '/inbound/buy/suggestions/suppliers',
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 9. 用户建议
|
||||||
|
export function getUserSuggestions(params: any) {
|
||||||
|
return request({
|
||||||
|
url: '/inbound/buy/suggestions/users',
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@ -39,4 +39,13 @@ export function searchMaterialBase(keyword: string) {
|
|||||||
method: 'get',
|
method: 'get',
|
||||||
params: { keyword }
|
params: { keyword }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 用户建议
|
||||||
|
export function getUserSuggestions(params: any) {
|
||||||
|
return request({
|
||||||
|
url: '/inbound/product/suggestions/users',
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@ -42,4 +42,13 @@ export function searchMaterialBase(keyword: string) {
|
|||||||
method: 'get',
|
method: 'get',
|
||||||
params: { keyword }
|
params: { keyword }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 用户建议
|
||||||
|
export function getUserSuggestions(params: any) {
|
||||||
|
return request({
|
||||||
|
url: '/inbound/semi/suggestions/users',
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|||||||
@ -104,6 +104,24 @@ export function searchMaterialBase(keyword: string) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 供应商建议
|
||||||
|
export function getProviderSuggestions(params: any) {
|
||||||
|
return request({
|
||||||
|
url: '/v1/inbound/service/suggestions/providers',
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用户建议
|
||||||
|
export function getUserSuggestions(params: any) {
|
||||||
|
return request({
|
||||||
|
url: '/v1/inbound/service/suggestions/users',
|
||||||
|
method: 'get',
|
||||||
|
params
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// 删除服务权益
|
// 删除服务权益
|
||||||
export function deleteService(id: number) {
|
export function deleteService(id: number) {
|
||||||
return request({
|
return request({
|
||||||
|
|||||||
@ -511,58 +511,64 @@ const form = reactive({
|
|||||||
arrival_photo: [] as string[], inspection_report: [] as string[]
|
arrival_photo: [] as string[], inspection_report: [] as string[]
|
||||||
})
|
})
|
||||||
|
|
||||||
// 历史记录辅助函数
|
|
||||||
const HISTORY_KEYS = { SUPPLIER: 'history_suppliers', PURCHASER: 'history_purchasers', EMAIL: 'history_emails', MATERIAL: 'history_materials' }
|
// 供应商建议 API
|
||||||
const saveToHistory = (key: string, value: string) => {
|
const fetchSupplierSuggestions = async (query: string, cb: any) => {
|
||||||
if (!value) return
|
if (!form.base_id) {
|
||||||
|
cb([])
|
||||||
|
return
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
const existing = localStorage.getItem(key)
|
const res: any = await getSupplierSuggestions({ base_id: form.base_id })
|
||||||
let list = existing ? JSON.parse(existing) : []
|
if (res.code === 200) {
|
||||||
list = list.filter((i: string) => i !== value)
|
const suppliers = res.data.map((name: string) => ({ value: name }))
|
||||||
list.unshift(value)
|
const filtered = query ? suppliers.filter((item: any) => item.value.toLowerCase().includes(query.toLowerCase())) : suppliers
|
||||||
if (list.length > 20) list = list.slice(0, 20)
|
cb(filtered)
|
||||||
localStorage.setItem(key, JSON.stringify(list))
|
} else {
|
||||||
} catch (e) { console.error('save history failed', e) }
|
cb([])
|
||||||
}
|
}
|
||||||
const getHistoryList = (key: string): any[] => {
|
} catch (e) {
|
||||||
try { return (JSON.parse(localStorage.getItem(key) || '[]')).map((v: string) => ({value: v})) } catch (e) { return [] }
|
cb([])
|
||||||
}
|
}
|
||||||
const saveMaterialHistory = (item: any) => {
|
|
||||||
if (!item || !item.id) return
|
|
||||||
const key = HISTORY_KEYS.MATERIAL
|
|
||||||
try {
|
|
||||||
let list = JSON.parse(localStorage.getItem(key) || '[]')
|
|
||||||
list = list.filter((i: any) => i.id !== item.id)
|
|
||||||
list.unshift({...item, isHistory: true})
|
|
||||||
if (list.length > 10) list = list.slice(0, 10)
|
|
||||||
localStorage.setItem(key, JSON.stringify(list))
|
|
||||||
} catch (e) {}
|
|
||||||
}
|
|
||||||
const getMaterialHistory = () => {
|
|
||||||
try { return JSON.parse(localStorage.getItem(HISTORY_KEYS.MATERIAL) || '[]') } catch (e) { return [] }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const createFilter = (queryString: string) => { return (item: any) => (item.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0) }
|
// 用户建议 API
|
||||||
const getTableDataUnique = (field: string) => { return Array.from(new Set(tableData.value.map((i: any) => i[field]).filter(Boolean))).map(i => ({value: i})) }
|
const fetchUserSuggestions = async (query: string, cb: any) => {
|
||||||
const mixedSearch = (queryString: string, tableField: string, storageKey: string, cb: any) => {
|
try {
|
||||||
const tableList = getTableDataUnique(tableField)
|
const res: any = await getUserSuggestions({ keyword: query })
|
||||||
const historyList = getHistoryList(storageKey)
|
if (res.code === 200) {
|
||||||
const map = new Map()
|
const users = res.data.map((user: any) => ({ value: user.value, email: user.email }))
|
||||||
historyList.forEach(i => map.set(i.value, i))
|
cb(users)
|
||||||
tableList.forEach(i => map.set(i.value, i))
|
} else {
|
||||||
const allList = Array.from(map.values())
|
cb([])
|
||||||
const results = queryString ? allList.filter(createFilter(queryString)) : allList
|
}
|
||||||
cb(results)
|
} catch (e) {
|
||||||
|
cb([])
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const querySearchSupplier = (qs: string, cb: any) => mixedSearch(qs, 'supplier_name', HISTORY_KEYS.SUPPLIER, cb)
|
const querySearchSupplier = (qs: string, cb: any) => fetchSupplierSuggestions(qs, cb)
|
||||||
const handleSupplierSelect = (item: any) => saveToHistory(HISTORY_KEYS.SUPPLIER, item.value)
|
const handleSupplierSelect = (item: any) => {
|
||||||
const querySearchPurchaser = (qs: string, cb: any) => mixedSearch(qs, 'purchaser', HISTORY_KEYS.PURCHASER, cb)
|
form.supplier_name = item.value
|
||||||
const handlePurchaserSelect = (item: any) => saveToHistory(HISTORY_KEYS.PURCHASER, item.value)
|
}
|
||||||
const querySearchEmail = (qs: string, cb: any) => mixedSearch(qs, 'purchaser_email', HISTORY_KEYS.EMAIL, cb)
|
const querySearchPurchaser = (qs: string, cb: any) => fetchUserSuggestions(qs, cb)
|
||||||
const handleEmailSelect = (item: any) => saveToHistory(HISTORY_KEYS.EMAIL, item.value)
|
const handlePurchaserSelect = (item: any) => {
|
||||||
|
form.purchaser = item.value
|
||||||
|
form.purchaser_email = item.email || ''
|
||||||
|
}
|
||||||
|
const querySearchEmail = (qs: string, cb: any) => fetchUserSuggestions(qs, (users: any[]) => {
|
||||||
|
const emailUsers = users.filter(u => u.email).map(u => ({ value: u.email }))
|
||||||
|
const filtered = qs ? emailUsers.filter((item: any) => item.value.toLowerCase().includes(qs.toLowerCase())) : emailUsers
|
||||||
|
cb(filtered)
|
||||||
|
})
|
||||||
|
const handleEmailSelect = (item: any) => {
|
||||||
|
form.purchaser_email = item.value
|
||||||
|
}
|
||||||
const currencyOptions = [{value: 'CNY', desc: '人民币'}, {value: 'USD', desc: '美元'}, {value: 'EUR', desc: '欧元'}]
|
const currencyOptions = [{value: 'CNY', desc: '人民币'}, {value: 'USD', desc: '美元'}, {value: 'EUR', desc: '欧元'}]
|
||||||
const querySearchCurrency = (queryString: string, cb: any) => { cb(queryString ? currencyOptions.filter(createFilter(queryString)) : currencyOptions) }
|
const querySearchCurrency = (queryString: string, cb: any) => {
|
||||||
|
const filtered = queryString ? currencyOptions.filter(item => item.value.toLowerCase().includes(queryString.toLowerCase()) || item.desc.toLowerCase().includes(queryString.toLowerCase())) : currencyOptions
|
||||||
|
cb(filtered)
|
||||||
|
}
|
||||||
|
|
||||||
const handleMaterialDropdownVisible = (visible: boolean) => { if (visible && materialOptions.value.length === 0) handleSearchMaterial('') }
|
const handleMaterialDropdownVisible = (visible: boolean) => { if (visible && materialOptions.value.length === 0) handleSearchMaterial('') }
|
||||||
const handleSearchMaterial = async (query: string) => {
|
const handleSearchMaterial = async (query: string) => {
|
||||||
@ -570,17 +576,12 @@ const handleSearchMaterial = async (query: string) => {
|
|||||||
try {
|
try {
|
||||||
const res: any = await searchMaterialBase(query)
|
const res: any = await searchMaterialBase(query)
|
||||||
const apiResults = (res.data || []).map((i: any) => ({...i, isHistory: false}))
|
const apiResults = (res.data || []).map((i: any) => ({...i, isHistory: false}))
|
||||||
if (!query) {
|
materialOptions.value = apiResults
|
||||||
const history = getMaterialHistory()
|
|
||||||
const historyIds = new Set(history.map((h: any) => h.id))
|
|
||||||
materialOptions.value = [...history, ...apiResults.filter((apiItem: any) => !historyIds.has(apiItem.id))]
|
|
||||||
} else { materialOptions.value = apiResults }
|
|
||||||
} finally { searchLoading.value = false }
|
} finally { searchLoading.value = false }
|
||||||
}
|
}
|
||||||
const onMaterialSelected = (val: number) => {
|
const onMaterialSelected = (val: number) => {
|
||||||
const item = materialOptions.value.find(i => i.id === val)
|
const item = materialOptions.value.find(i => i.id === val)
|
||||||
if (item) {
|
if (item) {
|
||||||
saveMaterialHistory(item)
|
|
||||||
form.material_name = item.name
|
form.material_name = item.name
|
||||||
form.spec_model = item.spec
|
form.spec_model = item.spec
|
||||||
form.category = item.category
|
form.category = item.category
|
||||||
@ -739,10 +740,6 @@ const submitForm = async () => {
|
|||||||
}
|
}
|
||||||
} else { await updateBuyInbound(form.id!, payload); ElMessage.success('更新成功') }
|
} else { await updateBuyInbound(form.id!, payload); ElMessage.success('更新成功') }
|
||||||
|
|
||||||
// 成功后保存历史记录
|
|
||||||
saveToHistory(HISTORY_KEYS.SUPPLIER, form.supplier_name)
|
|
||||||
saveToHistory(HISTORY_KEYS.PURCHASER, form.purchaser)
|
|
||||||
saveToHistory(HISTORY_KEYS.EMAIL, form.purchaser_email)
|
|
||||||
|
|
||||||
await fetchData()
|
await fetchData()
|
||||||
visible.value = false
|
visible.value = false
|
||||||
@ -917,4 +914,4 @@ onMounted(() => fetchData())
|
|||||||
.camera-card:hover { border-color: #409EFF; color: #409EFF; }
|
.camera-card:hover { border-color: #409EFF; color: #409EFF; }
|
||||||
.camera-card .text { font-size: 12px; margin-top: 5px; }
|
.camera-card .text { font-size: 12px; margin-top: 5px; }
|
||||||
.camera-card .el-icon { font-size: 24px; }
|
.camera-card .el-icon { font-size: 24px; }
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -471,18 +471,12 @@ const handleSearchMaterial = async (query: string) => {
|
|||||||
try {
|
try {
|
||||||
const res: any = await searchMaterialBase(query)
|
const res: any = await searchMaterialBase(query)
|
||||||
const apiResults = (res.data || []).map((i: any) => ({ ...i, isHistory: false }))
|
const apiResults = (res.data || []).map((i: any) => ({ ...i, isHistory: false }))
|
||||||
if (!query) {
|
materialOptions.value = apiResults
|
||||||
const history = getMaterialHistory()
|
|
||||||
const historyIds = new Set(history.map((h: any) => h.id))
|
|
||||||
const filteredApi = apiResults.filter((apiItem: any) => !historyIds.has(apiItem.id))
|
|
||||||
materialOptions.value = [...history, ...filteredApi]
|
|
||||||
} else { materialOptions.value = apiResults }
|
|
||||||
} finally { searchLoading.value = false }
|
} finally { searchLoading.value = false }
|
||||||
}
|
}
|
||||||
const onMaterialSelected = (val: number) => {
|
const onMaterialSelected = (val: number) => {
|
||||||
const item = materialOptions.value.find(i => i.id === val)
|
const item = materialOptions.value.find(i => i.id === val)
|
||||||
if (item) {
|
if (item) {
|
||||||
saveMaterialHistory(item)
|
|
||||||
// Auto-populate readonly fields
|
// Auto-populate readonly fields
|
||||||
form.material_name = item.name
|
form.material_name = item.name
|
||||||
form.spec_model = item.spec
|
form.spec_model = item.spec
|
||||||
@ -649,7 +643,6 @@ const submitForm = async () => {
|
|||||||
const newItem = res.data
|
const newItem = res.data
|
||||||
if (newItem) { ElMessage.info('发送打印...'); try { await executePrint(newItem); ElMessage.success('指令已发送') } catch (e: any) { ElMessage.warning('打印失败') } }
|
if (newItem) { ElMessage.info('发送打印...'); try { await executePrint(newItem); ElMessage.success('指令已发送') } catch (e: any) { ElMessage.warning('打印失败') } }
|
||||||
} else { await updateProductInbound(form.id!, payload); ElMessage.success('更新成功') }
|
} else { await updateProductInbound(form.id!, payload); ElMessage.success('更新成功') }
|
||||||
saveToHistory(HISTORY_KEYS.PRODUCTION_MANAGER, form.production_manager)
|
|
||||||
visible.value = false; fetchData()
|
visible.value = false; fetchData()
|
||||||
} catch(e:any) {
|
} catch(e:any) {
|
||||||
// 捕获后端报错
|
// 捕获后端报错
|
||||||
@ -715,4 +708,4 @@ onMounted(() => fetchData())
|
|||||||
.camera-card:hover { border-color: #409EFF; color: #409EFF; }
|
.camera-card:hover { border-color: #409EFF; color: #409EFF; }
|
||||||
.camera-card .text { font-size: 12px; margin-top: 5px; }
|
.camera-card .text { font-size: 12px; margin-top: 5px; }
|
||||||
.camera-card .el-icon { font-size: 24px; }
|
.camera-card .el-icon { font-size: 24px; }
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -530,49 +530,16 @@ const form = reactive({
|
|||||||
// ------------------------------------
|
// ------------------------------------
|
||||||
// 历史记录管理器
|
// 历史记录管理器
|
||||||
// ------------------------------------
|
// ------------------------------------
|
||||||
const HISTORY_KEYS = { PRODUCTION_MANAGER: 'history_production_managers', MATERIAL: 'history_semi_materials' }
|
|
||||||
const saveToHistory = (key: string, value: string) => {
|
|
||||||
if (!value) return
|
|
||||||
try {
|
|
||||||
const existing = localStorage.getItem(key)
|
|
||||||
let list = existing ? JSON.parse(existing) : []
|
|
||||||
list = list.filter((i: string) => i !== value)
|
|
||||||
list.unshift(value)
|
|
||||||
if (list.length > 20) list = list.slice(0, 20)
|
|
||||||
localStorage.setItem(key, JSON.stringify(list))
|
|
||||||
} catch (e) { console.error('save history failed', e) }
|
|
||||||
}
|
|
||||||
const getHistoryList = (key: string): any[] => { try { return (JSON.parse(localStorage.getItem(key) || '[]')).map((v: string) => ({value: v})) } catch (e) { return [] } }
|
|
||||||
const saveMaterialHistory = (item: any) => {
|
|
||||||
if (!item || !item.id) return
|
|
||||||
const key = HISTORY_KEYS.MATERIAL
|
|
||||||
try {
|
|
||||||
let list = JSON.parse(localStorage.getItem(key) || '[]')
|
|
||||||
list = list.filter((i: any) => i.id !== item.id)
|
|
||||||
list.unshift({...item, isHistory: true})
|
|
||||||
if (list.length > 10) list = list.slice(0, 10)
|
|
||||||
localStorage.setItem(key, JSON.stringify(list))
|
|
||||||
} catch (e) {}
|
|
||||||
}
|
|
||||||
const getMaterialHistory = () => { try { return JSON.parse(localStorage.getItem(HISTORY_KEYS.MATERIAL) || '[]') } catch (e) { return [] } }
|
|
||||||
|
|
||||||
// ------------------------------------
|
// ------------------------------------
|
||||||
// Autocomplete & Search Logic
|
// Autocomplete & Search Logic (后端 API 驱动)
|
||||||
// ------------------------------------
|
// ------------------------------------
|
||||||
const createFilter = (queryString: string) => { return (item: any) => (item.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0) }
|
const querySearchManager = async (query: string, cb: any) => {
|
||||||
const getTableDataUnique = (field: string) => { return Array.from(new Set(tableData.value.map((i: any) => i[field]).filter(Boolean))).map(i => ({value: i})) }
|
// 后续会从后端获取用户建议,暂时先返回空列表
|
||||||
const mixedSearch = (queryString: string, tableField: string, storageKey: string, cb: any) => {
|
cb([])
|
||||||
const tableList = getTableDataUnique(tableField)
|
}
|
||||||
const historyList = getHistoryList(storageKey)
|
const handleManagerSelect = (item: any) => {
|
||||||
const map = new Map()
|
// 无需保存历史
|
||||||
historyList.forEach(i => map.set(i.value, i))
|
|
||||||
tableList.forEach(i => map.set(i.value, i))
|
|
||||||
const allList = Array.from(map.values())
|
|
||||||
const results = queryString ? allList.filter(createFilter(queryString)) : allList
|
|
||||||
cb(results)
|
|
||||||
}
|
}
|
||||||
const querySearchManager = (qs: string, cb: any) => mixedSearch(qs, 'production_manager', HISTORY_KEYS.PRODUCTION_MANAGER, cb)
|
|
||||||
const handleManagerSelect = (item: any) => saveToHistory(HISTORY_KEYS.PRODUCTION_MANAGER, item.value)
|
|
||||||
|
|
||||||
// ------------------------------------
|
// ------------------------------------
|
||||||
// Material Search (Matches Buy.vue)
|
// Material Search (Matches Buy.vue)
|
||||||
@ -583,17 +550,12 @@ const handleSearchMaterial = async (query: string) => {
|
|||||||
try {
|
try {
|
||||||
const res: any = await searchMaterialBase(query)
|
const res: any = await searchMaterialBase(query)
|
||||||
const apiResults = (res.data || []).map((i: any) => ({...i, isHistory: false}))
|
const apiResults = (res.data || []).map((i: any) => ({...i, isHistory: false}))
|
||||||
if (!query) {
|
materialOptions.value = apiResults
|
||||||
const history = getMaterialHistory()
|
|
||||||
const historyIds = new Set(history.map((h: any) => h.id))
|
|
||||||
materialOptions.value = [...history, ...apiResults.filter((apiItem: any) => !historyIds.has(apiItem.id))]
|
|
||||||
} else { materialOptions.value = apiResults }
|
|
||||||
} finally { searchLoading.value = false }
|
} finally { searchLoading.value = false }
|
||||||
}
|
}
|
||||||
const onMaterialSelected = (val: number) => {
|
const onMaterialSelected = (val: number) => {
|
||||||
const item = materialOptions.value.find(i => i.id === val)
|
const item = materialOptions.value.find(i => i.id === val)
|
||||||
if (item) {
|
if (item) {
|
||||||
saveMaterialHistory(item)
|
|
||||||
// Populate form fields
|
// Populate form fields
|
||||||
form.material_name = item.name
|
form.material_name = item.name
|
||||||
form.spec_model = item.spec
|
form.spec_model = item.spec
|
||||||
@ -800,7 +762,6 @@ const submitForm = async () => {
|
|||||||
catch (printErr: any) { ElMessage.warning('入库成功,但自动打印失败:' + (printErr.msg || '未知错误')) }
|
catch (printErr: any) { ElMessage.warning('入库成功,但自动打印失败:' + (printErr.msg || '未知错误')) }
|
||||||
}
|
}
|
||||||
} else { await updateSemiInbound(form.id!, payload); ElMessage.success('更新成功') }
|
} else { await updateSemiInbound(form.id!, payload); ElMessage.success('更新成功') }
|
||||||
saveToHistory(HISTORY_KEYS.PRODUCTION_MANAGER, form.production_manager)
|
|
||||||
await fetchData(); visible.value = false
|
await fetchData(); visible.value = false
|
||||||
} catch (e: any) {
|
} catch (e: any) {
|
||||||
// 捕获后端报错
|
// 捕获后端报错
|
||||||
@ -876,4 +837,4 @@ onMounted(() => fetchData())
|
|||||||
.camera-card:hover { border-color: #409EFF; color: #409EFF; }
|
.camera-card:hover { border-color: #409EFF; color: #409EFF; }
|
||||||
.camera-card .text { font-size: 12px; margin-top: 5px; }
|
.camera-card .text { font-size: 12px; margin-top: 5px; }
|
||||||
.camera-card .el-icon { font-size: 24px; }
|
.camera-card .el-icon { font-size: 24px; }
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
Reference in New Issue
Block a user