Compare commits
8 Commits
5f3ceef3fd
...
5513e4cd81
| Author | SHA1 | Date | |
|---|---|---|---|
| 5513e4cd81 | |||
| 83f040728f | |||
| d3d35e03cd | |||
| 9f0134b2e4 | |||
| b3fdc65d33 | |||
| 9c70d78d9f | |||
| cfb36ebf0b | |||
| c6fd0aca90 |
@ -1,6 +1,7 @@
|
||||
# app/api/v1/common/print.py
|
||||
from flask import Blueprint, request, jsonify
|
||||
from app.services.print.label_service import LabelPrintService
|
||||
from app.services.print.print_config import PrintConfigManager
|
||||
from app.models.inbound.buy import StockBuy
|
||||
# 引入其他模型 StockSemi, StockProduct
|
||||
import traceback
|
||||
@ -24,4 +25,25 @@ def execute_print():
|
||||
LabelPrintService.send_to_printer(data)
|
||||
return jsonify({"code": 200, "msg": "指令已发送至打印机"})
|
||||
except Exception as e:
|
||||
return jsonify({"code": 500, "msg": str(e)}), 500
|
||||
return jsonify({"code": 500, "msg": str(e)}), 500
|
||||
|
||||
|
||||
@print_bp.route('/config', methods=['GET'])
|
||||
def get_printer_config():
|
||||
try:
|
||||
label = PrintConfigManager.get_config('label_printer')
|
||||
network = PrintConfigManager.get_config('network_printer')
|
||||
config = {'label_printer': label, 'network_printer': network}
|
||||
return jsonify({"code": 200, "msg": "success", "data": config})
|
||||
except Exception as e:
|
||||
return jsonify({"code": 500, "msg": str(e)}), 500
|
||||
|
||||
|
||||
@print_bp.route('/config', methods=['POST'])
|
||||
def update_printer_config():
|
||||
try:
|
||||
data = request.get_json()
|
||||
PrintConfigManager.save_config(data)
|
||||
return jsonify({"code": 200, "msg": "配置保存成功"})
|
||||
except Exception as e:
|
||||
return jsonify({"code": 500, "msg": str(e)}), 500
|
||||
|
||||
@ -114,3 +114,15 @@ def get_user_suggestions():
|
||||
keyword = request.args.get('keyword', '')
|
||||
data = ProductInboundService.search_system_users(keyword)
|
||||
return jsonify({"code": 200, "msg": "success", "data": data})
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# 7. 获取筛选选项
|
||||
# ------------------------------------------------------------------
|
||||
@inbound_product_bp.route('/options', methods=['GET'])
|
||||
def get_options():
|
||||
try:
|
||||
data = ProductInboundService.get_filter_options()
|
||||
return jsonify({"code": 200, "msg": "success", "data": data})
|
||||
except Exception as e:
|
||||
return jsonify({"code": 500, "msg": str(e)}), 500
|
||||
|
||||
@ -128,3 +128,15 @@ def get_user_suggestions():
|
||||
keyword = request.args.get('keyword', '')
|
||||
data = SemiInboundService.search_system_users(keyword)
|
||||
return jsonify({"code": 200, "msg": "success", "data": data})
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# 7. 获取筛选选项
|
||||
# ------------------------------------------------------------------
|
||||
@inbound_semi_bp.route('/options', methods=['GET'])
|
||||
def get_options():
|
||||
try:
|
||||
data = SemiInboundService.get_filter_options()
|
||||
return jsonify({"code": 200, "msg": "success", "data": data})
|
||||
except Exception as e:
|
||||
return jsonify({"code": 500, "msg": str(e)}), 500
|
||||
|
||||
@ -4,6 +4,7 @@ from . import inbound_bp
|
||||
from app.schemas.stock_schema import stock_service_schema
|
||||
from app.services.inbound.service_service import ServiceService
|
||||
from app.utils.decorators import role_required
|
||||
import traceback
|
||||
|
||||
|
||||
@inbound_bp.route('/service/search-base', methods=['GET'])
|
||||
@ -49,6 +50,7 @@ def get_service_list():
|
||||
})
|
||||
except Exception as e:
|
||||
current_app.logger.error(f'获取服务列表失败: {str(e)}')
|
||||
traceback.print_exc()
|
||||
return jsonify({'code': 500, 'msg': '内部服务器错误'}), 500
|
||||
|
||||
|
||||
@ -162,3 +164,16 @@ def get_user_suggestions():
|
||||
keyword = request.args.get('keyword', '')
|
||||
data = ServiceService.search_system_users(keyword)
|
||||
return jsonify({'code': 200, 'msg': 'success', 'data': data})
|
||||
|
||||
|
||||
# ------------------------------------------------------------------
|
||||
# 获取筛选选项
|
||||
# ------------------------------------------------------------------
|
||||
@inbound_bp.route('/service/options', methods=['GET'])
|
||||
@jwt_required()
|
||||
def get_options():
|
||||
try:
|
||||
data = ServiceService.get_filter_options()
|
||||
return jsonify({'code': 200, 'msg': 'success', 'data': data})
|
||||
except Exception as e:
|
||||
return jsonify({'code': 500, 'msg': str(e)}), 500
|
||||
|
||||
@ -280,7 +280,7 @@ class ProductInboundService:
|
||||
# 6. 获取列表
|
||||
# ============================================================
|
||||
@staticmethod
|
||||
def get_list(page, limit, keyword=None, statuses=None):
|
||||
def get_list(page, limit, keyword=None, statuses=None, category=None, material_type=None):
|
||||
from app.models.inbound.product import StockProduct
|
||||
try:
|
||||
query = db.session.query(StockProduct).outerjoin(MaterialBase, StockProduct.base_id == MaterialBase.id)
|
||||
@ -295,6 +295,13 @@ class ProductInboundService:
|
||||
StockProduct.sku.ilike(f'%{keyword}%')
|
||||
))
|
||||
|
||||
# 类别筛选
|
||||
if category and category.strip():
|
||||
query = query.filter(MaterialBase.category == category.strip())
|
||||
# 类型筛选
|
||||
if material_type and material_type.strip():
|
||||
query = query.filter(MaterialBase.material_type == material_type.strip())
|
||||
|
||||
if not statuses:
|
||||
statuses = ['在库', '借库']
|
||||
|
||||
@ -377,3 +384,25 @@ class ProductInboundService:
|
||||
return users
|
||||
except Exception:
|
||||
return []
|
||||
|
||||
# ============================================================
|
||||
# 8. 获取筛选选项(类别、类型)
|
||||
# ============================================================
|
||||
@staticmethod
|
||||
def get_filter_options():
|
||||
try:
|
||||
from app.models.base import MaterialBase
|
||||
categories = db.session.query(MaterialBase.category) \
|
||||
.filter(MaterialBase.category != None, MaterialBase.category != '') \
|
||||
.distinct().all()
|
||||
types = db.session.query(MaterialBase.material_type) \
|
||||
.filter(MaterialBase.material_type != None, MaterialBase.material_type != '') \
|
||||
.distinct().all()
|
||||
return {
|
||||
"categories": [r[0] for r in categories],
|
||||
"types": [r[0] for r in types]
|
||||
}
|
||||
except Exception:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return {"categories": [], "types": []}
|
||||
|
||||
@ -378,7 +378,7 @@ class SemiInboundService:
|
||||
# 6. 获取列表
|
||||
# ============================================================
|
||||
@staticmethod
|
||||
def get_list(page, limit, keyword=None, statuses=None):
|
||||
def get_list(page, limit, keyword=None, statuses=None, category=None, material_type=None):
|
||||
from app.models.inbound.semi import StockSemi
|
||||
try:
|
||||
query = db.session.query(StockSemi).outerjoin(MaterialBase, StockSemi.base_id == MaterialBase.id)
|
||||
@ -397,6 +397,13 @@ class SemiInboundService:
|
||||
)
|
||||
)
|
||||
|
||||
# 类别筛选
|
||||
if category and category.strip():
|
||||
query = query.filter(MaterialBase.category == category.strip())
|
||||
# 类型筛选
|
||||
if material_type and material_type.strip():
|
||||
query = query.filter(MaterialBase.material_type == material_type.strip())
|
||||
|
||||
if not statuses:
|
||||
statuses = ['在库', '借库']
|
||||
|
||||
@ -479,3 +486,25 @@ class SemiInboundService:
|
||||
return users
|
||||
except Exception:
|
||||
return []
|
||||
|
||||
# ============================================================
|
||||
# 8. 获取筛选选项(类别、类型)
|
||||
# ============================================================
|
||||
@staticmethod
|
||||
def get_filter_options():
|
||||
try:
|
||||
from app.models.base import MaterialBase
|
||||
categories = db.session.query(MaterialBase.category) \
|
||||
.filter(MaterialBase.category != None, MaterialBase.category != '') \
|
||||
.distinct().all()
|
||||
types = db.session.query(MaterialBase.material_type) \
|
||||
.filter(MaterialBase.material_type != None, MaterialBase.material_type != '') \
|
||||
.distinct().all()
|
||||
return {
|
||||
"categories": [r[0] for r in categories],
|
||||
"types": [r[0] for r in types]
|
||||
}
|
||||
except Exception:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return {"categories": [], "types": []}
|
||||
|
||||
@ -4,6 +4,7 @@ from app.models.inbound.service import StockService
|
||||
from app.models.base import MaterialBase
|
||||
from datetime import datetime, timedelta
|
||||
import re
|
||||
import traceback
|
||||
|
||||
|
||||
class ServiceService:
|
||||
@ -130,24 +131,29 @@ class ServiceService:
|
||||
query = StockService.query.filter_by(is_deleted=False)
|
||||
# 关键词搜索:可搜索 SKU 或 关联物料名称
|
||||
if keyword:
|
||||
# 子查询查找物料名称匹配的 base_id
|
||||
subquery = MaterialBase.query.filter(
|
||||
MaterialBase.name.ilike(f'%{keyword}%')
|
||||
).subquery()
|
||||
# 直接子查询
|
||||
query = query.filter(
|
||||
db.or_(
|
||||
StockService.sku.ilike(f'%{keyword}%'),
|
||||
StockService.base_id.in_([row.id for row in db.session.query(subquery.c.id)])
|
||||
StockService.base_id.in_(
|
||||
db.session.query(MaterialBase.id).filter(MaterialBase.name.ilike(f'%{keyword}%'))
|
||||
)
|
||||
)
|
||||
)
|
||||
if start_date:
|
||||
start = datetime.strptime(start_date, '%Y-%m-%d')
|
||||
query = query.filter(StockService.created_at >= start)
|
||||
try:
|
||||
start = datetime.strptime(start_date, '%Y-%m-%d')
|
||||
query = query.filter(StockService.created_at >= start)
|
||||
except ValueError:
|
||||
pass # ignore invalid date format
|
||||
if end_date:
|
||||
end = datetime.strptime(end_date, '%Y-%m-%d')
|
||||
# 包含当天
|
||||
end = end + timedelta(days=1) - timedelta(seconds=1)
|
||||
query = query.filter(StockService.created_at <= end)
|
||||
try:
|
||||
end = datetime.strptime(end_date, '%Y-%m-%d')
|
||||
# 包含当天
|
||||
end = end + timedelta(days=1) - timedelta(seconds=1)
|
||||
query = query.filter(StockService.created_at <= end)
|
||||
except ValueError:
|
||||
pass
|
||||
if provider_name:
|
||||
query = query.filter(StockService.provider_name.ilike(f'%{provider_name}%'))
|
||||
# 总数
|
||||
@ -204,3 +210,25 @@ class ServiceService:
|
||||
return users
|
||||
except Exception:
|
||||
return []
|
||||
|
||||
# ============================================================
|
||||
# 获取筛选选项(类别、类型)
|
||||
# ============================================================
|
||||
@classmethod
|
||||
def get_filter_options(cls):
|
||||
try:
|
||||
from app.models.base import MaterialBase
|
||||
categories = db.session.query(MaterialBase.category) \
|
||||
.filter(MaterialBase.category != None, MaterialBase.category != '') \
|
||||
.distinct().all()
|
||||
types = db.session.query(MaterialBase.material_type) \
|
||||
.filter(MaterialBase.material_type != None, MaterialBase.material_type != '') \
|
||||
.distinct().all()
|
||||
return {
|
||||
"categories": [r[0] for r in categories],
|
||||
"types": [r[0] for r in types]
|
||||
}
|
||||
except Exception:
|
||||
import traceback
|
||||
traceback.print_exc()
|
||||
return {"categories": [], "types": []}
|
||||
|
||||
@ -3,6 +3,7 @@ import base64
|
||||
import os
|
||||
from io import BytesIO
|
||||
from PIL import Image, ImageDraw, ImageFont
|
||||
from .print_config import PrintConfigManager
|
||||
|
||||
# 引入二维码生成库
|
||||
try:
|
||||
@ -12,8 +13,7 @@ except ImportError:
|
||||
|
||||
|
||||
class LabelPrintService:
|
||||
PRINTER_IP = "192.168.9.221"
|
||||
PRINTER_PORT = 9100
|
||||
# Printer IP and port now managed by PrintConfigManager
|
||||
|
||||
# ================= 1. 尺寸与分辨率配置 (300 DPI) =================
|
||||
DOTS_PER_MM = 12 # 300 DPI
|
||||
@ -271,8 +271,9 @@ class LabelPrintService:
|
||||
|
||||
@staticmethod
|
||||
def send_to_printer(data):
|
||||
ip = LabelPrintService.PRINTER_IP
|
||||
port = LabelPrintService.PRINTER_PORT
|
||||
config = PrintConfigManager.get_config('label_printer')
|
||||
ip = config['ip']
|
||||
port = config['port']
|
||||
|
||||
try:
|
||||
# 1. 获取 RGB 图像
|
||||
|
||||
@ -1,11 +1,13 @@
|
||||
import socket
|
||||
import datetime
|
||||
from .print_config import PrintConfigManager
|
||||
|
||||
|
||||
class NetworkPrintService:
|
||||
def __init__(self, ip='192.168.9.250', port=9100):
|
||||
self.ip = ip
|
||||
self.port = port
|
||||
def __init__(self, ip=None, port=None):
|
||||
config = PrintConfigManager.get_config('network_printer')
|
||||
self.ip = ip if ip is not None else config['ip']
|
||||
self.port = port if port is not None else config['port']
|
||||
|
||||
def _send_to_printer(self, content):
|
||||
"""
|
||||
@ -37,4 +39,4 @@ class NetworkPrintService:
|
||||
|
||||
def print_stocktake_report(self, data):
|
||||
# 同样处理
|
||||
return self._send_to_printer(f"盘点报告: 应盘{data.get('total')}, 实盘{data.get('scanned')}")
|
||||
return self._send_to_printer(f"盘点报告: 应盘{data.get('total')}, 实盘{data.get('scanned')}")
|
||||
|
||||
61
inventory-backend/app/services/print/print_config.py
Normal file
61
inventory-backend/app/services/print/print_config.py
Normal file
@ -0,0 +1,61 @@
|
||||
import json
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
class PrintConfigManager:
|
||||
CONFIG_FILENAME = 'printer_config.json'
|
||||
DEFAULT_CONFIG = {
|
||||
'label_printer': {'ip': '192.168.9.221', 'port': 9100},
|
||||
'network_printer': {'ip': '192.168.9.250', 'port': 9100}
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def _get_config_path(cls):
|
||||
# Determine the path relative to this file's directory
|
||||
current_dir = Path(__file__).parent
|
||||
return current_dir / cls.CONFIG_FILENAME
|
||||
|
||||
@classmethod
|
||||
def get_config(cls, printer_type='label_printer'):
|
||||
"""
|
||||
Retrieve configuration for a given printer type.
|
||||
Returns a dict with 'ip' and 'port'.
|
||||
"""
|
||||
config_path = cls._get_config_path()
|
||||
if not config_path.exists():
|
||||
# Write default config if not exists
|
||||
cls.save_config(cls.DEFAULT_CONFIG)
|
||||
config = cls.DEFAULT_CONFIG
|
||||
else:
|
||||
try:
|
||||
with open(config_path, 'r', encoding='utf-8') as f:
|
||||
config = json.load(f)
|
||||
except Exception as e:
|
||||
print(f"Error reading printer config: {e}")
|
||||
config = cls.DEFAULT_CONFIG
|
||||
# Return specific printer config, falling back to default for that type
|
||||
printer_config = config.get(printer_type, cls.DEFAULT_CONFIG.get(printer_type))
|
||||
# Ensure it's a dict with ip and port
|
||||
if not printer_config or 'ip' not in printer_config:
|
||||
printer_config = cls.DEFAULT_CONFIG.get(printer_type, {'ip': '127.0.0.1', 'port': 9100})
|
||||
return printer_config
|
||||
|
||||
@classmethod
|
||||
def save_config(cls, new_config):
|
||||
"""
|
||||
Save entire config dictionary to file.
|
||||
new_config should be a dict with keys 'label_printer' and/or 'network_printer'.
|
||||
"""
|
||||
config_path = cls._get_config_path()
|
||||
try:
|
||||
# If file exists, merge existing with new
|
||||
existing = {}
|
||||
if config_path.exists():
|
||||
with open(config_path, 'r', encoding='utf-8') as f:
|
||||
existing = json.load(f)
|
||||
existing.update(new_config)
|
||||
with open(config_path, 'w', encoding='utf-8') as f:
|
||||
json.dump(existing, f, indent=2)
|
||||
except Exception as e:
|
||||
print(f"Error saving printer config: {e}")
|
||||
raise
|
||||
@ -14,4 +14,19 @@ export function executePrint(data: any) {
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
export function getPrinterConfig() {
|
||||
return request({
|
||||
url: '/common/print/config',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
export function updatePrinterConfig(data: any) {
|
||||
return request({
|
||||
url: '/common/print/config',
|
||||
method: 'post',
|
||||
data
|
||||
})
|
||||
}
|
||||
|
||||
@ -49,3 +49,11 @@ export function getUserSuggestions(params: any) {
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// 筛选选项
|
||||
export function getFilterOptions() {
|
||||
return request({
|
||||
url: '/inbound/product/options',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
@ -52,3 +52,11 @@ export function getUserSuggestions(params: any) {
|
||||
params
|
||||
})
|
||||
}
|
||||
|
||||
// 筛选选项
|
||||
export function getFilterOptions() {
|
||||
return request({
|
||||
url: '/inbound/semi/options',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
@ -122,6 +122,14 @@ export function getUserSuggestions(params: any) {
|
||||
})
|
||||
}
|
||||
|
||||
// 筛选选项
|
||||
export function getFilterOptions() {
|
||||
return request({
|
||||
url: '/v1/inbound/service/options',
|
||||
method: 'get'
|
||||
})
|
||||
}
|
||||
|
||||
// 删除服务权益
|
||||
export function deleteService(id: number) {
|
||||
return request({
|
||||
|
||||
@ -4,7 +4,12 @@
|
||||
<template #header>
|
||||
<div class="card-header">
|
||||
<span class="title">👋 欢迎回来,{{ userStore.username }}</span>
|
||||
<el-tag type="success">系统运行正常</el-tag>
|
||||
<div style="display: flex; align-items: center; gap: 10px;">
|
||||
<el-tag type="success">系统运行正常</el-tag>
|
||||
<el-button type="info" plain size="small" @click="openPrinterDialog" :icon="Setting" class="printer-btn">
|
||||
打印设置
|
||||
</el-button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -30,20 +35,103 @@
|
||||
</div>
|
||||
</div>
|
||||
</el-card>
|
||||
|
||||
<el-dialog v-model="printerDialogVisible" title="打印机 IP 配置" width="500px">
|
||||
<el-form :model="printerForm" label-width="120px">
|
||||
<el-form-item label="标签打印机 IP">
|
||||
<el-input v-model="printerForm.label_ip" placeholder="例如 192.168.9.221" />
|
||||
</el-form-item>
|
||||
<el-form-item label="标签打印机端口">
|
||||
<el-input v-model.number="printerForm.label_port" placeholder="例如 9100" />
|
||||
</el-form-item>
|
||||
<el-form-item label="网络打印机 IP">
|
||||
<el-input v-model="printerForm.network_ip" placeholder="例如 192.168.9.250" />
|
||||
</el-form-item>
|
||||
<el-form-item label="网络打印机端口">
|
||||
<el-input v-model.number="printerForm.network_port" placeholder="例如 9100" />
|
||||
</el-form-item>
|
||||
</el-form>
|
||||
<template #footer>
|
||||
<span class="dialog-footer">
|
||||
<el-button @click="printerDialogVisible = false">取消</el-button>
|
||||
<el-button type="primary" @click="savePrinterConfig" :loading="loading">保存</el-button>
|
||||
</span>
|
||||
</template>
|
||||
</el-dialog>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, reactive } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
// 1. 引入 User Store
|
||||
import { useUserStore } from '@/stores/user'
|
||||
// 引入需要的图标
|
||||
import { Box, TrendCharts, ShoppingCart, Operation } from '@element-plus/icons-vue'
|
||||
import { Box, TrendCharts, ShoppingCart, Operation, Setting } from '@element-plus/icons-vue'
|
||||
import { getPrinterConfig, updatePrinterConfig } from '@/api/common/print'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
const router = useRouter()
|
||||
// 2. 实例化 store
|
||||
const userStore = useUserStore()
|
||||
|
||||
// 打印机配置相关
|
||||
const printerDialogVisible = ref(false)
|
||||
const printerForm = reactive({
|
||||
label_ip: '',
|
||||
label_port: '',
|
||||
network_ip: '',
|
||||
network_port: ''
|
||||
})
|
||||
const loading = ref(false)
|
||||
|
||||
const openPrinterDialog = async () => {
|
||||
try {
|
||||
loading.value = true
|
||||
const res = await getPrinterConfig()
|
||||
if (res.code === 200) {
|
||||
const config = res.data
|
||||
printerForm.label_ip = config.label_printer?.ip || ''
|
||||
printerForm.label_port = config.label_printer?.port || ''
|
||||
printerForm.network_ip = config.network_printer?.ip || ''
|
||||
printerForm.network_port = config.network_printer?.port || ''
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
printerDialogVisible.value = true
|
||||
}
|
||||
|
||||
const savePrinterConfig = async () => {
|
||||
try {
|
||||
loading.value = true
|
||||
const config = {
|
||||
label_printer: {
|
||||
ip: printerForm.label_ip,
|
||||
port: Number(printerForm.label_port)
|
||||
},
|
||||
network_printer: {
|
||||
ip: printerForm.network_ip,
|
||||
port: Number(printerForm.network_port)
|
||||
}
|
||||
}
|
||||
const res = await updatePrinterConfig(config)
|
||||
if (res.code === 200) {
|
||||
ElMessage.success('保存成功')
|
||||
printerDialogVisible.value = false
|
||||
} else {
|
||||
ElMessage.error(res.msg || '保存失败')
|
||||
}
|
||||
} catch (e) {
|
||||
ElMessage.error('请求异常')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
// 统一跳转函数
|
||||
const handleNav = (path: string) => {
|
||||
router.push(path)
|
||||
@ -109,4 +197,4 @@ const handleNav = (path: string) => {
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@ -1,21 +1,36 @@
|
||||
<template>
|
||||
<div class="semi-module">
|
||||
<div class="header-tools">
|
||||
<div class="left-tools">
|
||||
<div class="left-tools" style="display: flex; align-items: center; gap: 10px; flex-wrap: wrap;">
|
||||
<el-input
|
||||
v-model="queryParams.keyword"
|
||||
placeholder="🔍 搜索物料 / 批号 / SN / 工单号 / BOM..."
|
||||
class="search-input"
|
||||
placeholder="请输入名称或规格"
|
||||
clearable
|
||||
@clear="fetchData"
|
||||
@keyup.enter="fetchData"
|
||||
style="width: 300px; margin-right: 10px;"
|
||||
style="width: 240px;"
|
||||
/>
|
||||
<el-select
|
||||
v-model="queryParams.category"
|
||||
placeholder="类别"
|
||||
clearable
|
||||
filterable
|
||||
@change="fetchData"
|
||||
style="width: 160px;"
|
||||
>
|
||||
<template #append>
|
||||
<el-button :icon="Search" @click="fetchData"/>
|
||||
</template>
|
||||
</el-input>
|
||||
|
||||
<el-option v-for="item in categoryOptions" :key="item" :label="item" :value="item" />
|
||||
</el-select>
|
||||
<el-select
|
||||
v-model="queryParams.material_type"
|
||||
placeholder="类型"
|
||||
clearable
|
||||
filterable
|
||||
@change="fetchData"
|
||||
style="width: 160px;"
|
||||
>
|
||||
<el-option v-for="item in typeOptions" :key="item" :label="item" :value="item" />
|
||||
</el-select>
|
||||
<el-button type="primary" plain @click="fetchData">搜索</el-button>
|
||||
<el-button @click="resetQuery">重置</el-button>
|
||||
<el-select
|
||||
v-model="queryParams.statuses"
|
||||
multiple
|
||||
@ -166,7 +181,7 @@
|
||||
v-model:current-page="queryParams.page"
|
||||
v-model:page-size="queryParams.pageSize"
|
||||
:total="total"
|
||||
:page-sizes="[15, 30, 50, 100]"
|
||||
:page-sizes="[100, 200, 500, 1000]"
|
||||
layout="total, sizes, prev, pager, next, jumper"
|
||||
background
|
||||
@size-change="fetchData"
|
||||
@ -436,7 +451,8 @@ import {
|
||||
createSemiInbound,
|
||||
updateSemiInbound,
|
||||
deleteSemiInbound,
|
||||
searchMaterialBase
|
||||
searchMaterialBase,
|
||||
getFilterOptions
|
||||
} from '@/api/inbound/semi'
|
||||
import { uploadFile, deleteFile } from '@/api/inbound/buy'
|
||||
import WebRtcCamera from '@/components/Camera/WebRtcCamera.vue'
|
||||
@ -453,7 +469,9 @@ const dialogStatus = ref<'create' | 'update'>('create')
|
||||
const tableData = ref([])
|
||||
const total = ref(0)
|
||||
const formRef = ref()
|
||||
const queryParams = reactive({ page: 1, pageSize: 15, keyword: '', statuses: ['在库', '借库'] })
|
||||
const queryParams = reactive({ page: 1, pageSize: 100, keyword: '', category: '', material_type: '', statuses: ['在库', '借库'] })
|
||||
const categoryOptions = ref<string[]>([])
|
||||
const typeOptions = ref<string[]>([])
|
||||
const materialOptions = ref<any[]>([])
|
||||
|
||||
// 打印相关变量
|
||||
@ -625,6 +643,26 @@ const fetchData = async () => {
|
||||
} finally { loading.value = false }
|
||||
}
|
||||
|
||||
const fetchOptions = async () => {
|
||||
try {
|
||||
const res: any = await getFilterOptions()
|
||||
if (res.code === 200) {
|
||||
categoryOptions.value = res.data.categories
|
||||
typeOptions.value = res.data.types
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Fetch options failed', e)
|
||||
}
|
||||
}
|
||||
|
||||
const resetQuery = () => {
|
||||
queryParams.keyword = ''
|
||||
queryParams.category = ''
|
||||
queryParams.material_type = ''
|
||||
queryParams.page = 1
|
||||
fetchData()
|
||||
}
|
||||
|
||||
const handleCreate = () => {
|
||||
dialogStatus.value = 'create'
|
||||
resetForm()
|
||||
@ -780,7 +818,10 @@ const getStatusType = (status: string) => { const map: any = { '在库': 'succes
|
||||
const getQualityType = (status: string) => { const map: any = { '合格': 'success', '不合格': 'danger', '待检': 'info', '返修中': 'warning' }; return map[status] || 'info' }
|
||||
const formatMoney = (val: any) => { const num = Number(val); return isNaN(num) ? '-' : `¥ ${num.toFixed(2)}` }
|
||||
|
||||
onMounted(() => fetchData())
|
||||
onMounted(() => {
|
||||
fetchData()
|
||||
fetchOptions()
|
||||
})
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
Reference in New Issue
Block a user