Compare commits
5 Commits
875de73a3a
...
c58a0a6d14
| Author | SHA1 | Date | |
|---|---|---|---|
| c58a0a6d14 | |||
| f612c47143 | |||
| 5ea2be58ae | |||
| 6d80c90b66 | |||
| 30ab1c186c |
@ -1,4 +1,4 @@
|
|||||||
from flask import Blueprint, request, jsonify # .material -> .base refactor checked
|
from flask import Blueprint, request, jsonify, send_file # .material -> .base refactor checked
|
||||||
from app.services.inbound.inbound_summary_service import InboundSummaryService
|
from app.services.inbound.inbound_summary_service import InboundSummaryService
|
||||||
|
|
||||||
# 定义蓝图
|
# 定义蓝图
|
||||||
@ -33,3 +33,39 @@ def get_list():
|
|||||||
# 生产环境建议记录详细日志
|
# 生产环境建议记录详细日志
|
||||||
print(f"Inbound Summary Error: {str(e)}")
|
print(f"Inbound Summary Error: {str(e)}")
|
||||||
return jsonify({'code': 500, 'msg': str(e)}), 500
|
return jsonify({'code': 500, 'msg': str(e)}), 500
|
||||||
|
|
||||||
|
|
||||||
|
@bp.route('/export', methods=['GET'])
|
||||||
|
def export_data():
|
||||||
|
"""
|
||||||
|
导出入库记录 Excel
|
||||||
|
支持筛选条件:keyword, start_date, end_date, source_type
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# 获取参数
|
||||||
|
keyword = request.args.get('keyword', '')
|
||||||
|
start_date = request.args.get('start_date')
|
||||||
|
end_date = request.args.get('end_date')
|
||||||
|
source_type = request.args.get('source_type')
|
||||||
|
|
||||||
|
# 调用导出服务
|
||||||
|
file_stream = InboundSummaryService.export_excel(
|
||||||
|
keyword=keyword,
|
||||||
|
start_date=start_date,
|
||||||
|
end_date=end_date,
|
||||||
|
source_type=source_type
|
||||||
|
)
|
||||||
|
|
||||||
|
from datetime import datetime
|
||||||
|
now = datetime.now().strftime('%Y%m%d_%H%M%S')
|
||||||
|
filename = f"入库记录_{now}.xlsx"
|
||||||
|
|
||||||
|
return send_file(
|
||||||
|
file_stream,
|
||||||
|
mimetype='application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||||
|
as_attachment=True,
|
||||||
|
download_name=filename
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Inbound Summary Export Error: {str(e)}")
|
||||||
|
return jsonify({'code': 500, 'msg': str(e)}), 500
|
||||||
|
|||||||
@ -315,6 +315,8 @@ class MaterialBaseService:
|
|||||||
filter_conditions.append(column != value)
|
filter_conditions.append(column != value)
|
||||||
elif operator == 'contains':
|
elif operator == 'contains':
|
||||||
filter_conditions.append(column.ilike(f'%{value}%'))
|
filter_conditions.append(column.ilike(f'%{value}%'))
|
||||||
|
elif operator == 'not_contains':
|
||||||
|
filter_conditions.append(~column.ilike(f'%{value}%'))
|
||||||
elif operator == 'ge':
|
elif operator == 'ge':
|
||||||
try:
|
try:
|
||||||
num_val = float(value)
|
num_val = float(value)
|
||||||
@ -636,7 +638,7 @@ class MaterialBaseService:
|
|||||||
# 2.1 采购库存 (StockBuy)
|
# 2.1 采购库存 (StockBuy)
|
||||||
query_buy = db.session.query(StockBuy, MaterialBase).join(
|
query_buy = db.session.query(StockBuy, MaterialBase).join(
|
||||||
MaterialBase, StockBuy.base_id == MaterialBase.id
|
MaterialBase, StockBuy.base_id == MaterialBase.id
|
||||||
)
|
).filter(StockBuy.stock_quantity > 0)
|
||||||
for cond in filter_conditions:
|
for cond in filter_conditions:
|
||||||
query_buy = query_buy.filter(cond)
|
query_buy = query_buy.filter(cond)
|
||||||
list_buy = query_buy.all()
|
list_buy = query_buy.all()
|
||||||
@ -644,7 +646,7 @@ class MaterialBaseService:
|
|||||||
# 2.2 半成品库存 (StockSemi)
|
# 2.2 半成品库存 (StockSemi)
|
||||||
query_semi = db.session.query(StockSemi, MaterialBase).join(
|
query_semi = db.session.query(StockSemi, MaterialBase).join(
|
||||||
MaterialBase, StockSemi.base_id == MaterialBase.id
|
MaterialBase, StockSemi.base_id == MaterialBase.id
|
||||||
)
|
).filter(StockSemi.stock_quantity > 0)
|
||||||
for cond in filter_conditions:
|
for cond in filter_conditions:
|
||||||
query_semi = query_semi.filter(cond)
|
query_semi = query_semi.filter(cond)
|
||||||
list_semi = query_semi.all()
|
list_semi = query_semi.all()
|
||||||
@ -652,7 +654,7 @@ class MaterialBaseService:
|
|||||||
# 2.3 成品库存 (StockProduct)
|
# 2.3 成品库存 (StockProduct)
|
||||||
query_product = db.session.query(StockProduct, MaterialBase).join(
|
query_product = db.session.query(StockProduct, MaterialBase).join(
|
||||||
MaterialBase, StockProduct.base_id == MaterialBase.id
|
MaterialBase, StockProduct.base_id == MaterialBase.id
|
||||||
)
|
).filter(StockProduct.stock_quantity > 0)
|
||||||
for cond in filter_conditions:
|
for cond in filter_conditions:
|
||||||
query_product = query_product.filter(cond)
|
query_product = query_product.filter(cond)
|
||||||
list_product = query_product.all()
|
list_product = query_product.all()
|
||||||
|
|||||||
@ -403,6 +403,8 @@ class BuyInboundService:
|
|||||||
filter_conditions.append(column != value)
|
filter_conditions.append(column != value)
|
||||||
elif operator == 'contains':
|
elif operator == 'contains':
|
||||||
filter_conditions.append(column.ilike(f'%{value}%'))
|
filter_conditions.append(column.ilike(f'%{value}%'))
|
||||||
|
elif operator == 'not_contains':
|
||||||
|
filter_conditions.append(~column.ilike(f'%{value}%'))
|
||||||
elif operator == 'ge':
|
elif operator == 'ge':
|
||||||
try:
|
try:
|
||||||
num_val = float(value)
|
num_val = float(value)
|
||||||
|
|||||||
@ -6,6 +6,10 @@ from app.models.inbound.semi import StockSemi
|
|||||||
from app.models.inbound.product import StockProduct
|
from app.models.inbound.product import StockProduct
|
||||||
from app.models.base import MaterialBase
|
from app.models.base import MaterialBase
|
||||||
import traceback
|
import traceback
|
||||||
|
from io import BytesIO
|
||||||
|
from openpyxl import Workbook
|
||||||
|
from openpyxl.styles import Font, PatternFill, Alignment
|
||||||
|
from openpyxl.utils import get_column_letter
|
||||||
|
|
||||||
|
|
||||||
class InboundSummaryService:
|
class InboundSummaryService:
|
||||||
@ -110,6 +114,12 @@ class InboundSummaryService:
|
|||||||
)
|
)
|
||||||
query = query.filter(rule)
|
query = query.filter(rule)
|
||||||
|
|
||||||
|
# 日期补全:解决零点截断问题
|
||||||
|
if end_date and len(str(end_date).strip()) == 10:
|
||||||
|
end_date = f"{str(end_date).strip()} 23:59:59"
|
||||||
|
if start_date and len(str(start_date).strip()) == 10:
|
||||||
|
start_date = f"{str(start_date).strip()} 00:00:00"
|
||||||
|
|
||||||
if start_date and end_date:
|
if start_date and end_date:
|
||||||
query = query.filter(cte.c.inbound_date.between(start_date, end_date))
|
query = query.filter(cte.c.inbound_date.between(start_date, end_date))
|
||||||
|
|
||||||
@ -206,3 +216,179 @@ class InboundSummaryService:
|
|||||||
print("【InboundSummaryService Error】:", str(e))
|
print("【InboundSummaryService Error】:", str(e))
|
||||||
traceback.print_exc()
|
traceback.print_exc()
|
||||||
raise e
|
raise e
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def export_excel(keyword=None, start_date=None, end_date=None, source_type=None):
|
||||||
|
"""
|
||||||
|
导出入库记录 Excel
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# 复用 get_list 的查询逻辑,但不分页(获取全部数据)
|
||||||
|
# 构建三个子查询
|
||||||
|
q_buy = db.session.query(
|
||||||
|
StockBuy.id.label('id'),
|
||||||
|
StockBuy.base_id.label('base_id'),
|
||||||
|
StockBuy.sku.label('sku'),
|
||||||
|
StockBuy.in_date.label('inbound_date'),
|
||||||
|
StockBuy.in_quantity.label('in_qty'),
|
||||||
|
StockBuy.stock_quantity.label('current_qty'),
|
||||||
|
cast(StockBuy.supplier_name, String).label('source_info'),
|
||||||
|
StockBuy.status.label('orig_status'),
|
||||||
|
cast(getattr(StockBuy, 'batch_number', literal('')), String).label('batch_number'),
|
||||||
|
cast(getattr(StockBuy, 'serial_number', getattr(StockBuy, 'sn', literal(''))), String).label('serial_number'),
|
||||||
|
cast(literal('buy'), String).label('source_type')
|
||||||
|
)
|
||||||
|
|
||||||
|
q_semi = db.session.query(
|
||||||
|
StockSemi.id.label('id'),
|
||||||
|
StockSemi.base_id.label('base_id'),
|
||||||
|
StockSemi.sku.label('sku'),
|
||||||
|
StockSemi.production_date.label('inbound_date'),
|
||||||
|
StockSemi.in_quantity.label('in_qty'),
|
||||||
|
StockSemi.stock_quantity.label('current_qty'),
|
||||||
|
cast(StockSemi.production_manager, String).label('source_info'),
|
||||||
|
StockSemi.status.label('orig_status'),
|
||||||
|
cast(getattr(StockSemi, 'batch_number', literal('')), String).label('batch_number'),
|
||||||
|
cast(getattr(StockSemi, 'serial_number', getattr(StockSemi, 'sn', literal(''))), String).label('serial_number'),
|
||||||
|
cast(literal('semi'), String).label('source_type')
|
||||||
|
)
|
||||||
|
|
||||||
|
q_product = db.session.query(
|
||||||
|
StockProduct.id.label('id'),
|
||||||
|
StockProduct.base_id.label('base_id'),
|
||||||
|
StockProduct.sku.label('sku'),
|
||||||
|
StockProduct.production_date.label('inbound_date'),
|
||||||
|
StockProduct.in_quantity.label('in_qty'),
|
||||||
|
StockProduct.stock_quantity.label('current_qty'),
|
||||||
|
cast(StockProduct.production_manager, String).label('source_info'),
|
||||||
|
StockProduct.status.label('orig_status'),
|
||||||
|
cast(getattr(StockProduct, 'batch_number', literal('')), String).label('batch_number'),
|
||||||
|
cast(getattr(StockProduct, 'serial_number', getattr(StockProduct, 'sn', literal(''))), String).label('serial_number'),
|
||||||
|
cast(literal('product'), String).label('source_type')
|
||||||
|
)
|
||||||
|
|
||||||
|
combined_query = union_all(q_buy, q_semi, q_product)
|
||||||
|
cte = combined_query.subquery()
|
||||||
|
|
||||||
|
query = db.session.query(
|
||||||
|
cte,
|
||||||
|
MaterialBase.name.label('material_name'),
|
||||||
|
MaterialBase.spec_model.label('spec_model'),
|
||||||
|
MaterialBase.category.label('category'),
|
||||||
|
MaterialBase.material_type.label('material_type')
|
||||||
|
).outerjoin(
|
||||||
|
MaterialBase, cte.c.base_id == MaterialBase.id
|
||||||
|
)
|
||||||
|
|
||||||
|
# 过滤条件
|
||||||
|
if keyword:
|
||||||
|
rule = or_(
|
||||||
|
cte.c.sku.ilike(f'%{keyword}%'),
|
||||||
|
cte.c.source_info.ilike(f'%{keyword}%'),
|
||||||
|
cte.c.batch_number.ilike(f'%{keyword}%'),
|
||||||
|
cte.c.serial_number.ilike(f'%{keyword}%'),
|
||||||
|
MaterialBase.name.ilike(f'%{keyword}%'),
|
||||||
|
MaterialBase.spec_model.ilike(f'%{keyword}%')
|
||||||
|
)
|
||||||
|
query = query.filter(rule)
|
||||||
|
|
||||||
|
# 日期补全:解决零点截断问题
|
||||||
|
if end_date and len(str(end_date).strip()) == 10:
|
||||||
|
end_date = f"{str(end_date).strip()} 23:59:59"
|
||||||
|
if start_date and len(str(start_date).strip()) == 10:
|
||||||
|
start_date = f"{str(start_date).strip()} 00:00:00"
|
||||||
|
|
||||||
|
if start_date and end_date:
|
||||||
|
query = query.filter(cte.c.inbound_date.between(start_date, end_date))
|
||||||
|
|
||||||
|
if source_type:
|
||||||
|
query = query.filter(cte.c.source_type == source_type)
|
||||||
|
|
||||||
|
# 排序
|
||||||
|
query = query.order_by(desc(cte.c.inbound_date), asc(cte.c.sku))
|
||||||
|
|
||||||
|
# 获取全部数据
|
||||||
|
rows = query.all()
|
||||||
|
|
||||||
|
# 创建 Excel
|
||||||
|
wb = Workbook()
|
||||||
|
ws = wb.active
|
||||||
|
ws.title = "入库记录"
|
||||||
|
|
||||||
|
# 表头样式
|
||||||
|
header_font = Font(bold=True, size=11, color="FFFFFF")
|
||||||
|
header_fill = PatternFill(start_color="4472C4", end_color="4472C4", fill_type="solid")
|
||||||
|
header_alignment = Alignment(horizontal="center", vertical="center")
|
||||||
|
|
||||||
|
# 写表头
|
||||||
|
headers = ['SKU', '物品名称', '规格型号', '分类', '入库来源', '入库/生产日期', '入库数量', '批次/序列号', '供应商/负责人', '当前状态']
|
||||||
|
for col, header in enumerate(headers, 1):
|
||||||
|
cell = ws.cell(row=1, column=col, value=header)
|
||||||
|
cell.font = header_font
|
||||||
|
cell.fill = header_fill
|
||||||
|
cell.alignment = header_alignment
|
||||||
|
|
||||||
|
# 写数据
|
||||||
|
type_map = {'buy': '采购入库', 'semi': '半成品生产', 'product': '成品完工'}
|
||||||
|
|
||||||
|
for row_idx, row in enumerate(rows, 2):
|
||||||
|
date_str = ""
|
||||||
|
if row.inbound_date:
|
||||||
|
try:
|
||||||
|
date_str = row.inbound_date.strftime('%Y-%m-%d')
|
||||||
|
except Exception:
|
||||||
|
date_str = str(row.inbound_date)
|
||||||
|
|
||||||
|
in_qty = float(row.in_qty) if row.in_qty is not None else 0.0
|
||||||
|
current_qty = float(row.current_qty) if row.current_qty is not None else 0.0
|
||||||
|
|
||||||
|
final_status = row.orig_status
|
||||||
|
if current_qty <= 0:
|
||||||
|
final_status = "已出库"
|
||||||
|
elif current_qty < in_qty:
|
||||||
|
final_status = "部分出库"
|
||||||
|
|
||||||
|
b_num = row.batch_number or ""
|
||||||
|
s_num = row.serial_number or ""
|
||||||
|
if b_num and s_num and b_num != s_num:
|
||||||
|
display_batch_sn = f"{b_num}/{s_num}"
|
||||||
|
elif b_num:
|
||||||
|
display_batch_sn = b_num
|
||||||
|
elif s_num:
|
||||||
|
display_batch_sn = s_num
|
||||||
|
else:
|
||||||
|
display_batch_sn = "-"
|
||||||
|
|
||||||
|
ws.cell(row=row_idx, column=1, value=row.sku or "")
|
||||||
|
ws.cell(row=row_idx, column=2, value=row.material_name or "未知物品")
|
||||||
|
ws.cell(row=row_idx, column=3, value=row.spec_model or "")
|
||||||
|
ws.cell(row=row_idx, column=4, value=row.category or "")
|
||||||
|
ws.cell(row=row_idx, column=5, value=type_map.get(row.source_type, "未知类型"))
|
||||||
|
ws.cell(row=row_idx, column=6, value=date_str)
|
||||||
|
ws.cell(row=row_idx, column=7, value=in_qty)
|
||||||
|
ws.cell(row=row_idx, column=8, value=display_batch_sn)
|
||||||
|
ws.cell(row=row_idx, column=9, value=row.source_info or "")
|
||||||
|
ws.cell(row=row_idx, column=10, value=final_status)
|
||||||
|
|
||||||
|
# 自动调整列宽
|
||||||
|
for col in range(1, len(headers) + 1):
|
||||||
|
max_length = 0
|
||||||
|
column_letter = get_column_letter(col)
|
||||||
|
for row in range(2, len(rows) + 2):
|
||||||
|
cell_value = ws.cell(row=row, column=col).value
|
||||||
|
if cell_value:
|
||||||
|
max_length = max(max_length, len(str(cell_value)))
|
||||||
|
adjusted_width = min(max_length + 2, 50)
|
||||||
|
ws.column_dimensions[column_letter].width = adjusted_width
|
||||||
|
|
||||||
|
# 输出到字节流
|
||||||
|
file_stream = BytesIO()
|
||||||
|
wb.save(file_stream)
|
||||||
|
file_stream.seek(0)
|
||||||
|
|
||||||
|
return file_stream
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
print("【InboundSummaryService Export Error】:", str(e))
|
||||||
|
traceback.print_exc()
|
||||||
|
raise e
|
||||||
@ -198,6 +198,12 @@ class OutboundService:
|
|||||||
支持跨表搜索:单号、领用人、SKU、物料名称、规格型号
|
支持跨表搜索:单号、领用人、SKU、物料名称、规格型号
|
||||||
search_type: all, no, name, sku, material_name, spec_model
|
search_type: all, no, name, sku, material_name, spec_model
|
||||||
"""
|
"""
|
||||||
|
# 日期补全:解决零点截断问题
|
||||||
|
if end_date and len(str(end_date).strip()) == 10:
|
||||||
|
end_date = f"{str(end_date).strip()} 23:59:59"
|
||||||
|
if start_date and len(str(start_date).strip()) == 10:
|
||||||
|
start_date = f"{str(start_date).strip()} 00:00:00"
|
||||||
|
|
||||||
# 1. 构建基础查询
|
# 1. 构建基础查询
|
||||||
# 如果有关键词,需要联表搜索物料名称和规格型号
|
# 如果有关键词,需要联表搜索物料名称和规格型号
|
||||||
if keyword:
|
if keyword:
|
||||||
@ -403,11 +409,12 @@ class OutboundService:
|
|||||||
'items': []
|
'items': []
|
||||||
}
|
}
|
||||||
|
|
||||||
# --- 查询物品详细信息 (名称, 规格, 类型, 类别) ---
|
# --- 查询物品详细信息 (名称, 规格, 类型, 类别, 批号/SN) ---
|
||||||
item_name = "未知物品"
|
item_name = "未知物品"
|
||||||
item_spec = ""
|
item_spec = ""
|
||||||
item_cat = ""
|
item_cat = ""
|
||||||
item_type = ""
|
item_type = ""
|
||||||
|
batch_sn = "-"
|
||||||
|
|
||||||
ModelClass = model_map.get(d.source_table)
|
ModelClass = model_map.get(d.source_table)
|
||||||
if ModelClass and d.stock_id:
|
if ModelClass and d.stock_id:
|
||||||
@ -415,7 +422,10 @@ class OutboundService:
|
|||||||
# 生产环境建议优化为预加载或批量查询
|
# 生产环境建议优化为预加载或批量查询
|
||||||
try:
|
try:
|
||||||
stock_item = ModelClass.query.get(d.stock_id)
|
stock_item = ModelClass.query.get(d.stock_id)
|
||||||
if stock_item and stock_item.base:
|
if stock_item:
|
||||||
|
# 获取批号/序列号用于追溯
|
||||||
|
batch_sn = getattr(stock_item, 'batch_number', None) or getattr(stock_item, 'serial_number', None) or '-'
|
||||||
|
if stock_item.base:
|
||||||
item_name = stock_item.base.name
|
item_name = stock_item.base.name
|
||||||
item_spec = stock_item.base.spec_model
|
item_spec = stock_item.base.spec_model
|
||||||
item_cat = stock_item.base.category
|
item_cat = stock_item.base.category
|
||||||
@ -445,7 +455,8 @@ class OutboundService:
|
|||||||
'material_type': item_type,
|
'material_type': item_type,
|
||||||
'quantity': qty,
|
'quantity': qty,
|
||||||
'unit_price': price,
|
'unit_price': price,
|
||||||
'subtotal': subtotal
|
'subtotal': subtotal,
|
||||||
|
'batch_sn': batch_sn
|
||||||
})
|
})
|
||||||
|
|
||||||
# 4. 排序输出
|
# 4. 排序输出
|
||||||
|
|||||||
@ -176,7 +176,7 @@ const handleLogout = () => {
|
|||||||
<footer v-if="!isLoginPage" class="app-footer">
|
<footer v-if="!isLoginPage" class="app-footer">
|
||||||
<span class="version-tag">
|
<span class="version-tag">
|
||||||
<el-icon style="vertical-align: middle; margin-right: 4px"><InfoFilled /></el-icon>
|
<el-icon style="vertical-align: middle; margin-right: 4px"><InfoFilled /></el-icon>
|
||||||
当前版本:V3.9(4.3盘库错误修改)
|
当前版本:V3.10(4.7部署)
|
||||||
</span>
|
</span>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
|||||||
@ -16,3 +16,12 @@ export function getInboundSummaryList(params: InboundSummaryQuery) {
|
|||||||
params
|
params
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function exportInboundSummary(params: any) {
|
||||||
|
return request({
|
||||||
|
url: '/v1/inbound/summary/export',
|
||||||
|
method: 'get',
|
||||||
|
params,
|
||||||
|
responseType: 'blob'
|
||||||
|
})
|
||||||
|
}
|
||||||
@ -657,6 +657,7 @@ const operatorOptions = ref([
|
|||||||
{ value: 'eq', label: '等于' },
|
{ value: 'eq', label: '等于' },
|
||||||
{ value: 'ne', label: '不等于' },
|
{ value: 'ne', label: '不等于' },
|
||||||
{ value: 'contains', label: '包含' },
|
{ value: 'contains', label: '包含' },
|
||||||
|
{ value: 'not_contains', label: '不包含' },
|
||||||
{ value: 'ge', label: '大于等于' },
|
{ value: 'ge', label: '大于等于' },
|
||||||
{ value: 'le', label: '小于等于' }
|
{ value: 'le', label: '小于等于' }
|
||||||
]);
|
]);
|
||||||
|
|||||||
@ -53,6 +53,7 @@
|
|||||||
<el-table-column v-if="hasColumnPermission('material_type')" prop="material_type" label="类型" width="120" show-overflow-tooltip />
|
<el-table-column v-if="hasColumnPermission('material_type')" prop="material_type" label="类型" width="120" show-overflow-tooltip />
|
||||||
<el-table-column v-if="hasColumnPermission('category')" prop="category" label="类别" width="120" show-overflow-tooltip />
|
<el-table-column v-if="hasColumnPermission('category')" prop="category" label="类别" width="120" show-overflow-tooltip />
|
||||||
<el-table-column v-if="hasColumnPermission('spec_model')" prop="spec_model" label="规格型号" min-width="150" show-overflow-tooltip />
|
<el-table-column v-if="hasColumnPermission('spec_model')" prop="spec_model" label="规格型号" min-width="150" show-overflow-tooltip />
|
||||||
|
<el-table-column prop="batch_sn" label="批号/SN" min-width="120" />
|
||||||
|
|
||||||
<el-table-column v-if="hasColumnPermission('quantity')" prop="quantity" label="数量" width="100" />
|
<el-table-column v-if="hasColumnPermission('quantity')" prop="quantity" label="数量" width="100" />
|
||||||
<el-table-column v-if="hasColumnPermission('unit_price')" prop="unit_price" label="单价" width="120">
|
<el-table-column v-if="hasColumnPermission('unit_price')" prop="unit_price" label="单价" width="120">
|
||||||
|
|||||||
@ -880,6 +880,7 @@ const operatorOptions = ref([
|
|||||||
{ value: 'eq', label: '等于' },
|
{ value: 'eq', label: '等于' },
|
||||||
{ value: 'ne', label: '不等于' },
|
{ value: 'ne', label: '不等于' },
|
||||||
{ value: 'contains', label: '包含' },
|
{ value: 'contains', label: '包含' },
|
||||||
|
{ value: 'not_contains', label: '不包含' },
|
||||||
{ value: 'ge', label: '大于等于' },
|
{ value: 'ge', label: '大于等于' },
|
||||||
{ value: 'le', label: '小于等于' }
|
{ value: 'le', label: '小于等于' }
|
||||||
])
|
])
|
||||||
|
|||||||
@ -28,6 +28,10 @@
|
|||||||
@change="handleFilter"
|
@change="handleFilter"
|
||||||
/>
|
/>
|
||||||
<el-button type="primary" class="filter-item" style="margin-left: 10px;" @click="handleFilter">查询</el-button>
|
<el-button type="primary" class="filter-item" style="margin-left: 10px;" @click="handleFilter">查询</el-button>
|
||||||
|
|
||||||
|
<el-button type="success" plain class="filter-item" style="margin-left: 10px;" @click="handleExport" :loading="exportLoading">
|
||||||
|
导出Excel
|
||||||
|
</el-button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<el-table
|
<el-table
|
||||||
@ -100,14 +104,16 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, reactive, onMounted } from 'vue'
|
import { ref, reactive, onMounted } from 'vue'
|
||||||
import { getInboundSummaryList } from '@/api/inbound/inbound_summary'
|
import { getInboundSummaryList, exportInboundSummary } from '@/api/inbound/inbound_summary'
|
||||||
import { useUserStore } from '@/stores/user'
|
import { useUserStore } from '@/stores/user'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
|
|
||||||
const userStore = useUserStore()
|
const userStore = useUserStore()
|
||||||
|
|
||||||
const list = ref([])
|
const list = ref([])
|
||||||
const total = ref(0)
|
const total = ref(0)
|
||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
|
const exportLoading = ref(false)
|
||||||
|
|
||||||
const listQuery = reactive({
|
const listQuery = reactive({
|
||||||
page: 1,
|
page: 1,
|
||||||
@ -147,6 +153,48 @@ const handleFilter = () => {
|
|||||||
fetchData()
|
fetchData()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 导出 Excel
|
||||||
|
const handleExport = () => {
|
||||||
|
exportLoading.value = true
|
||||||
|
const params = {
|
||||||
|
keyword: listQuery.keyword,
|
||||||
|
source_type: listQuery.source_type,
|
||||||
|
start_date: listQuery.dateRange ? listQuery.dateRange[0] : null,
|
||||||
|
end_date: listQuery.dateRange ? listQuery.dateRange[1] : null
|
||||||
|
}
|
||||||
|
|
||||||
|
exportInboundSummary(params)
|
||||||
|
.then((response: any) => {
|
||||||
|
const blob = new Blob([response], { type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' })
|
||||||
|
const url = window.URL.createObjectURL(blob)
|
||||||
|
const link = document.createElement('a')
|
||||||
|
link.href = url
|
||||||
|
|
||||||
|
const now = new Date()
|
||||||
|
const year = now.getFullYear()
|
||||||
|
const month = String(now.getMonth() + 1).padStart(2, '0')
|
||||||
|
const day = String(now.getDate()).padStart(2, '0')
|
||||||
|
const hour = String(now.getHours()).padStart(2, '0')
|
||||||
|
const minute = String(now.getMinutes()).padStart(2, '0')
|
||||||
|
const second = String(now.getSeconds()).padStart(2, '0')
|
||||||
|
const filename = `入库记录_${year}${month}${day}_${hour}${minute}${second}.xlsx`
|
||||||
|
|
||||||
|
link.setAttribute('download', filename)
|
||||||
|
document.body.appendChild(link)
|
||||||
|
link.click()
|
||||||
|
document.body.removeChild(link)
|
||||||
|
window.URL.revokeObjectURL(url)
|
||||||
|
ElMessage.success('导出成功')
|
||||||
|
})
|
||||||
|
.catch((err: any) => {
|
||||||
|
console.error('导出失败', err)
|
||||||
|
ElMessage.error('导出失败')
|
||||||
|
})
|
||||||
|
.finally(() => {
|
||||||
|
exportLoading.value = false
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
// 来源类型的 Tag 颜色
|
// 来源类型的 Tag 颜色
|
||||||
const getSourceTag = (type: string) => {
|
const getSourceTag = (type: string) => {
|
||||||
if (type === 'buy') return 'success' // 绿色
|
if (type === 'buy') return 'success' // 绿色
|
||||||
|
|||||||
Reference in New Issue
Block a user