Files
Leantime/app/Domain/Bom/Templates/detail.blade.php

500 lines
25 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

@extends($layout)
@section('content')
@php
$bom = $detail['bom'] ?? [];
$columns = $detail['columns'] ?? [];
$items = $detail['items'] ?? [];
$sources = $detail['sources'] ?? [];
$hiddenColumns = $detail['hiddenColumns'] ?? [];
$imageColumns = $detail['imageColumns'] ?? [];
$fileColumns = $detail['fileColumns'] ?? [];
$bomId = (int)($bom['id'] ?? 0);
@endphp
<div class="pageheader" style="display:flex; align-items:center; justify-content:space-between; flex-wrap:wrap; gap:8px;">
<div style="display:flex; align-items:center; gap:10px;">
<div class="pageicon"><span class="fa fa-fw fa-list-check"></span></div>
<div class="pagetitle" style="margin:0;">
<h1 style="margin:0;">{{ $bom['productName'] ?? $bom['bomNo'] ?? __('menu.bom') }}</h1>
<p style="margin:0;">{{ __('text.bom_subtitle', 'BOM 明细') }} {{ $bom['bomNo'] ?? '' }}</p>
</div>
</div>
<div style="white-space:nowrap;">
<button type="button" class="btn btn-primary" id="btnEdit"><i class="fa fa-pencil"></i> 编辑</button>
<button type="button" class="btn btn-success" id="btnSave"><i class="fa fa-save"></i> 保存</button>
{{-- 插入图片/附件 --}}
<div class="btn-group" style="position:relative;display:inline-block;">
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="fa fa-paperclip"></i> 插入 <span class="caret"></span>
</button>
<ul class="dropdown-menu dropdown-menu-right">
<li><a href="javascript:void(0)" id="btnInsertImage"><i class="fa fa-image"></i> 插入图片</a></li>
<li><a href="javascript:void(0)" id="btnInsertAttach"><i class="fa fa-file"></i> 插入附件</a></li>
</ul>
</div>
{{-- 一级菜单:导入导出 --}}
<div class="btn-group" style="position:relative;display:inline-block;">
<button type="button" class="btn btn-default dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
<i class="fa fa-exchange"></i> 导入导出 <span class="caret"></span>
</button>
<ul class="dropdown-menu dropdown-menu-right">
<li class="dropdown-header"><i class="fa fa-cloud-download"></i> Teable 导入</li>
<li><a href="javascript:void(0)" id="btnTeableImport"><i class="fa fa-refresh"></i> 从数据源导入</a></li>
<li><a href="javascript:void(0)" id="btnTeablePaste"><i class="fa fa-paste"></i> 粘贴 API 导入</a></li>
<li><a href="javascript:void(0)" id="btnSourceManager"><i class="fa fa-database"></i> 数据源管理</a></li>
<li class="divider"></li>
<li class="dropdown-header"><i class="fa fa-upload"></i> Excel 导入</li>
<li><a href="javascript:void(0)" id="btnExcelImport"><i class="fa fa-upload"></i> 导入数据</a></li>
<li><a href="javascript:void(0)" id="btnExcelTemplate"><i class="fa fa-download"></i> 模板导出</a></li>
<li class="divider"></li>
<li class="dropdown-header"><i class="fa fa-file-export"></i> 数据导出</li>
<li><a href="javascript:void(0)" id="btnExcelExport"><i class="fa fa-file-excel-o"></i> 导出数据 (xlsx)</a></li>
<li><a href="javascript:void(0)" id="btnExcelExportCsv"><i class="fa fa-file"></i> 导出数据 (csv)</a></li>
</ul>
</div>
<button type="button" class="btn btn-default" id="btnFullscreen"><i class="fa fa-expand"></i> 全屏</button>
<a href="{{ BASE_URL }}/bom/show" class="btn btn-default"><i class="fa fa-arrow-left"></i> {{ __('links.back', '返回') }}</a>
</div>
</div>
<div class="maincontent">
<div class="maincontentinner">
<div id="bomAlert" class="alert" style="display:none;"></div>
<div id="bomError" class="alert alert-danger" style="display:none;"></div>
{{-- Univer 表格容器(动态铺满屏幕,自适应缩放) --}}
<div id="bomHot" style="width:100%;height:calc(100vh - 200px);min-height:400px;border:1px solid #e5e5e5;"></div>
</div>
</div>
{{-- Teable API 粘贴导入弹窗 --}}
<div id="teablePasteModal" style="display:none;position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,.45);z-index:9999;overflow:auto;">
<div style="max-width:760px;margin:6% auto;background:#fff;border-radius:4px;padding:18px;">
<h4 style="margin-top:0;"><i class="fa fa-paste"></i> 粘贴 Teable API 文档</h4>
<p class="text-muted">直接粘贴整段 API 文档或 curl 命令,自动解析连接信息;解析不全时可手动补齐下方字段。</p>
<textarea id="teablePasteText" style="width:100%;height:180px;font-family:monospace;font-size:12px;" placeholder="# Table: UAGP350B&#10;&#10;curl -X GET &quot;https://table.universal-onebot.com/api/table/tblXXX/record?fieldKeyType=name&quot; \&#10; -H &quot;Authorization: Bearer teable_...&quot;"></textarea>
<div style="margin-top:10px;">
<div class="row">
<div class="col-md-12"><label style="font-size:12px;">表名/别名</label><input type="text" id="pasteName" class="form-control"></div>
</div>
<div class="row" style="margin-top:6px;">
<div class="col-md-12"><label style="font-size:12px;">Base URL</label><input type="text" id="pasteBaseUrl" class="form-control" placeholder="https://table.universal-onebot.com"></div>
</div>
<div class="row" style="margin-top:6px;">
<div class="col-md-5"><label style="font-size:12px;">Table ID</label><input type="text" id="pasteTableId" class="form-control" placeholder="tbl..."></div>
<div class="col-md-7"><label style="font-size:12px;">Token</label><input type="text" id="pasteToken" class="form-control" placeholder="teable_acc..."></div>
</div>
</div>
<div id="teableParseResult" style="display:none;margin:10px 0;padding:10px;background:#f5f5f5;border:1px solid #ddd;border-radius:4px;font-size:13px;"></div>
<div style="text-align:right;margin-top:12px;">
<button type="button" class="btn btn-default" id="btnTeableParse"><i class="fa fa-search"></i> 解析</button>
<button type="button" class="btn btn-primary" id="btnTeablePasteGo"><i class="fa fa-cloud-download"></i> 导入</button>
<button type="button" class="btn btn-default" id="btnTeablePasteClose">取消</button>
</div>
</div>
</div>
{{-- 数据源管理弹窗 --}}
<div id="sourceManagerModal" style="display:none;position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,.45);z-index:9999;overflow:auto;">
<div style="max-width:640px;margin:6% auto;background:#fff;border-radius:4px;padding:18px;">
<h4 style="margin-top:0;"><i class="fa fa-database"></i> Teable 数据源管理</h4>
<div id="sourceList" style="margin-bottom:10px;">
@forelse ($sources as $s)
<span class="label label-info" style="margin-right:6px;">
{{ $s['name'] ?: $s['tableId'] }}
<a href="javascript:void(0)" class="btnDelSource" data-id="{{ $s['id'] }}" style="color:#fff;">&times;</a>
</span>
@empty
<em class="text-muted">{{ __('text.no_sources', '未配置数据源') }}</em>
@endforelse
</div>
<div class="row">
<div class="col-md-3"><input type="text" id="srcName" class="form-control" placeholder="{{ __('label.source_name', '表名/别名') }}"></div>
<div class="col-md-3"><input type="text" id="srcBaseUrl" class="form-control" placeholder="https://table.universal-onebot.com"></div>
<div class="col-md-2"><input type="text" id="srcTableId" class="form-control" placeholder="tbl..."></div>
<div class="col-md-3"><input type="text" id="srcToken" class="form-control" placeholder="teable_acc..."></div>
<div class="col-md-1"><button type="button" class="btn btn-default" id="btnAddSource">{{ __('links.add', '添加') }}</button></div>
</div>
<div style="text-align:right;margin-top:12px;">
<button type="button" class="btn btn-default" id="btnSourceManagerClose">关闭</button>
</div>
</div>
</div>
{{-- 隐藏文件输入 --}}
<input type="file" id="excelFile" accept=".xlsx,.csv" style="display:none;">
<input type="file" id="attachFile" accept="image/*,.pdf,.doc,.docx,.xls,.xlsx,.zip,.rar,.7z,.txt,.csv" style="display:none;">
<script src="{{ BASE_URL }}/dist/js/compiled-univer-component.3.9.8.min.js"></script>
@once
@push('scripts')
<script>
jQuery(document).ready(function () {
var BOM_ID = {{ $bomId }};
var BASE = '{{ BASE_URL }}';
var FIXED = {!! json_encode(array_keys($fixedColumns)) !!};
var FIXED_LABELS = {!! json_encode($fixedColumns) !!};
var COLUMNS = {!! json_encode(array_map(function($c){ return ['key'=>$c['key'],'label'=>$c['label']]; }, $columns)) !!};
var IMAGE_COLS = {!! json_encode(array_values($imageColumns)) !!};
var FILE_COLS = {!! json_encode(array_values($fileColumns)) !!};
var RAW_ITEMS = {!! json_encode($items, JSON_UNESCAPED_UNICODE | JSON_HEX_TAG | JSON_HEX_APOS | JSON_HEX_QUOT | JSON_HEX_AMP) !!};
var ALL_KEYS = FIXED.concat(COLUMNS.map(function (c) { return c.key; }));
var csrf = function () { return jQuery('meta[name="csrf-token"]').attr('content') || ''; };
function showAlert(msg, type) {
var el = jQuery('#bomAlert');
el.attr('class', 'alert alert-' + (type || 'info')).text(msg).show();
}
function showError(msg) { jQuery('#bomError').text(msg).show(); }
function api(method, url, data, cb) {
var opts = { url: url, method: method, headers: { 'X-CSRF-TOKEN': csrf() }, dataType: 'json' };
if (data !== undefined) { opts.contentType = 'application/json'; opts.data = JSON.stringify(data); }
return jQuery.ajax(opts).done(function (res) { cb && cb(res); }).fail(function (xhr) {
var m = xhr.responseJSON && xhr.responseJSON.message ? xhr.responseJSON.message : ('HTTP ' + xhr.status);
showAlert(m, 'danger');
});
}
// 数据附件列已在后端转成显示文本image.png 等),直接用
var data = RAW_ITEMS.map(function (it) {
var o = { _id: it.id || null };
ALL_KEYS.forEach(function (k) { o[k] = (it[k] === undefined || it[k] === null) ? '' : it[k]; });
return o;
});
var dirtyRows = {}; // 0-based 数据行索引 -> true
// 初始化 Univer
var host = document.getElementById('bomHot');
var inst;
try {
inst = window.createOneBotSheet({
container: host,
data: data,
columns: ALL_KEYS,
fixedLabels: FIXED_LABELS,
imageColumns: IMAGE_COLS,
fileColumns: FILE_COLS,
onCellChange: function (row, col, value) {
// 全量对比方案:事件回调不做任何 data 修改(否则 data 基线被污染,对比永远相等)
// 只做行标记,供潜在 UI 提示用
if (row >= 0 && row < data.length) {
dirtyRows[row] = true;
}
},
onCellClick: function (row, col) {
handleAttachmentClick(row, col);
}
});
} catch (e) {
showError('Univer 初始化失败: ' + e.message);
return;
}
var sheet = inst.sheet;
// 读取某 sheet 行0-based 数据行索引)为 payload
function buildPayload(dataRowIndex) {
var payload = {};
ALL_KEYS.forEach(function (k, c) {
var v = sheet.getRange(dataRowIndex + 1, c).getDisplayValue();
payload[k] = (v === undefined || v === null) ? '' : String(v);
});
return payload;
}
function saveRow(dataRowIndex) {
var payload = buildPayload(dataRowIndex);
// 已有 data 条目(有 _id则更新否则新建
if (data[dataRowIndex] && data[dataRowIndex]._id) {
payload.itemId = data[dataRowIndex]._id;
}
api('POST', BASE + '/bom/api/' + BOM_ID + '/item', payload, function (res) {
if (res.itemId && (!data[dataRowIndex] || !data[dataRowIndex]._id)) {
if (!data[dataRowIndex]) { data[dataRowIndex] = {}; }
data[dataRowIndex]._id = res.itemId;
}
});
}
// 判断某 sheet 行是否有任何非空值
function rowHasValue(dataRowIndex) {
for (var c = 0; c < ALL_KEYS.length; c++) {
var v = sheet.getRange(dataRowIndex + 1, c).getDisplayValue();
if (v !== undefined && v !== null && String(v).trim() !== '') { return true; }
}
return false;
}
// 编辑 / 保存
jQuery('#btnEdit').on('click', function () {
showAlert('表格已解锁,可编辑数据;完成后点「保存」提交', 'info');
});
jQuery('#btnSave').on('click', function () {
// 先结束编辑态,让编辑器把值提交进 sheet
var endPromise = (inst.endEditing && typeof inst.endEditing === 'function')
? inst.endEditing().catch(function () { return true; })
: Promise.resolve(true);
endPromise.then(function () {
setTimeout(function () {
// 遍历 sheet 实际数据行(非 data 数组),空表/新行都能保存
// 注意FWorksheet facade 没有 getRowCount(),必须用 inst.getRowCount()(内部走 getMaxRows
var sheetRowCount = inst.getRowCount ? inst.getRowCount() : 0;
var saved = 0;
for (var r = 0; r < sheetRowCount - 1; r++) {
if (rowHasValue(r)) { saveRow(r); saved++; }
}
if (saved > 0) {
showAlert('已保存 ' + saved + ' 行', 'success');
} else {
showAlert('没有需要保存的改动', 'info');
}
}, 500);
});
});
// ---- 全屏切换 ----
jQuery('#btnFullscreen').on('click', function () {
var el = document.getElementById('bomHot');
if (document.fullscreenElement) {
document.exitFullscreen();
} else if (el.requestFullscreen) {
el.requestFullscreen();
}
});
document.addEventListener('fullscreenchange', function () {
var btn = jQuery('#btnFullscreen');
if (document.fullscreenElement) {
btn.html('<i class="fa fa-compress"></i> 退出全屏');
} else {
btn.html('<i class="fa fa-expand"></i> 全屏');
}
});
// ---- 表格自适应布满屏幕 ----
function resizeBomHot() {
var host = document.getElementById('bomHot');
if (!host) { return; }
// 非全屏时:高度 = 视口高 - 顶部工具栏约 200px全屏时铺满
if (document.fullscreenElement === host) {
host.style.height = '100vh';
host.style.width = '100vw';
} else {
host.style.height = 'calc(100vh - 200px)';
host.style.width = '100%';
}
// 容器尺寸变化后,让表格缩放适配宽度
if (inst && typeof inst.fitToContainer === 'function') {
setTimeout(function () { inst.fitToContainer(); }, 50);
}
}
jQuery(window).on('resize', resizeBomHot);
resizeBomHot();
// ---- 插入图片/附件(上传到 /bom/api/{id}/attachment图片插浮动缩略图附件写单元格链接 ----
var attachKind = 'image'; // 'image' | 'file'
function doInsertAttach() {
var cell = inst.getActiveCell ? inst.getActiveCell() : null;
var row = cell ? cell.getRow() : 0;
var col = cell ? cell.getColumn() : 0;
// 表头行(第 0 行)不允许插附件,回退到第一数据行
if (row < 1) { row = 1; }
jQuery('#attachFile').off('change').on('change', function () {
var file = this.files[0];
if (!file) { return; }
var fd = new FormData();
fd.append('file', file);
jQuery.ajax({
url: BASE + '/bom/api/' + BOM_ID + '/attachment',
method: 'POST',
headers: { 'X-CSRF-TOKEN': csrf() },
data: fd, processData: false, contentType: false, dataType: 'json'
}).done(function (res) {
var d = (res && res.data) || {};
if (!d.url) { showAlert('上传失败:未返回地址', 'danger'); return; }
var isImage = (attachKind === 'image' && d.image);
// 图片:插入浮动缩略图
if (isImage) {
try { inst.insertImageAt(row, col, d.url, 'URL', 0); } catch (e) {}
}
// 持久化:把 {url,name,image} 追加到当前单元格的附件 JSON 数组,并保存(刷新后可重渲染,避免黑圈/丢失)
var existing = [];
try {
var cur = sheet.getRange(row, col).getDisplayValue();
if (cur) { existing = JSON.parse(cur); }
} catch (e) { existing = []; }
if (!Array.isArray(existing)) { existing = []; }
existing.push({ url: d.url, name: d.name, image: isImage });
try { sheet.getRange(row, col).setValue(JSON.stringify(existing)); } catch (e) {}
saveRow(row - 1);
showAlert((isImage ? '图片' : '附件') + '已插入并保存:' + d.name, 'success');
}).fail(function (xhr) {
var m = xhr.responseJSON && xhr.responseJSON.message ? xhr.responseJSON.message : ('HTTP ' + xhr.status);
showAlert(m, 'danger');
});
jQuery(this).val('');
});
jQuery('#attachFile').click();
}
jQuery('#btnInsertImage').on('click', function () { attachKind = 'image'; doInsertAttach(); });
jQuery('#btnInsertAttach').on('click', function () { attachKind = 'file'; doInsertAttach(); });
// ---- 附件点击PDF 弹窗预览,图片新窗口放大,其它下载 ----
function isExt(url, exts) {
var m = /\.([a-zA-Z0-9]+)(?:[?#].*)?$/.exec((url || '').split('?')[0]);
if (!m) { return false; }
return exts.indexOf(m[1].toLowerCase()) >= 0;
}
function handleAttachmentClick(row, col) {
// row 是 0-based sheet 行号;第 0 行表头不处理
if (row < 1 || col < 0 || col >= ALL_KEYS.length) { return; }
var key = ALL_KEYS[col];
var isFileCol = FILE_COLS.indexOf(key) >= 0;
var isImageCol = IMAGE_COLS.indexOf(key) >= 0;
if (!isFileCol && !isImageCol) { return; }
var v = null;
try { v = sheet.getRange(row, col).getDisplayValue(); } catch (e) {}
if (!v || typeof v !== 'string') { return; }
var arr = null;
try { arr = JSON.parse(v); } catch (e) { arr = null; }
if (!Array.isArray(arr) || arr.length === 0) {
// 单元格直接存了 URL 文本(插入附件写入的「名称 + URL」
var mUrl = /https?:\/\/[^\s]+/.exec(v);
if (mUrl) { arr = [{ url: mUrl[0], name: v.replace(mUrl[0], '').trim() || '附件' }]; }
else { return; }
}
var item = arr[0];
var url = item && item.url;
if (!url) { return; }
var name = item.name || '附件';
if (isExt(url, ['pdf'])) {
// PDF 弹窗预览
if (window.__onebotOpenPdf) { window.__onebotOpenPdf(url, name); }
else { window.open(url, '_blank'); }
} else if (isImageCol || /^image\//i.test(item.mime || '') || isExt(url, ['png', 'jpg', 'jpeg', 'gif', 'webp', 'bmp'])) {
// 图片:新窗口放大查看
window.open(url, '_blank');
} else {
// 其它附件:触发下载
var a = document.createElement('a');
a.href = url;
a.download = name || '';
a.target = '_blank';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
}
}
// ---- Teable 源管理 ----
jQuery('#btnSourceManager').on('click', function () { jQuery('#sourceManagerModal').show(); });
jQuery('#btnSourceManagerClose').on('click', function () { jQuery('#sourceManagerModal').hide(); });
jQuery('#btnAddSource').on('click', function () {
api('POST', BASE + '/bom/api/' + BOM_ID + '/source', {
name: jQuery('#srcName').val(),
baseUrl: jQuery('#srcBaseUrl').val(),
tableId: jQuery('#srcTableId').val(),
token: jQuery('#srcToken').val()
}, function () { location.reload(); });
});
jQuery('#sourceList').on('click', '.btnDelSource', function () {
if (!confirm("{{ __('text.confirm_delete') }}")) { return; }
api('DELETE', BASE + '/bom/api/source/' + jQuery(this).data('id'), null, function () { location.reload(); });
});
// ---- Teable 从数据源导入 ----
jQuery('#btnTeableImport').on('click', function () {
if (!confirm("{{ __('text.teable_import_confirm', '从 Teable 拉取最新数据并导入为明细?') }}")) { return; }
api('POST', BASE + '/bom/api/' + BOM_ID + '/import/teable', {}, function (res) {
showAlert("{{ __('text.imported', '已导入') }} " + (res.imported || 0) + " {{ __('label.rows', '行') }}", 'success');
setTimeout(function () { location.reload(); }, 800);
});
});
// ---- Teable API 粘贴导入 ----
jQuery('#btnTeablePaste').on('click', function () {
jQuery('#teablePasteText').val('');
jQuery('#pasteName').val(''); jQuery('#pasteBaseUrl').val(''); jQuery('#pasteTableId').val(''); jQuery('#pasteToken').val('');
jQuery('#teableParseResult').hide().empty();
jQuery('#teablePasteModal').show();
jQuery('#teablePasteText').trigger('focus');
});
jQuery('#btnTeablePasteClose').on('click', function () { jQuery('#teablePasteModal').hide(); });
jQuery('#btnTeableParse').on('click', function () {
var text = jQuery('#teablePasteText').val();
jQuery('#btnTeableParse').prop('disabled', true);
api('POST', BASE + '/bom/api/teable/parse', { text: text }, function (res) {
var d = res.data || {};
if (d.baseUrl) { jQuery('#pasteBaseUrl').val(d.baseUrl); }
if (d.tableId) { jQuery('#pasteTableId').val(d.tableId); }
if (d.token) { jQuery('#pasteToken').val(d.token); }
if (d.name) { jQuery('#pasteName').val(d.name); }
jQuery('#teableParseResult').html('<strong>解析完成</strong>,已回填到上方输入框,可手动修改后导入。').show();
}).always(function () { jQuery('#btnTeableParse').prop('disabled', false); });
});
jQuery('#btnTeablePasteGo').on('click', function () {
var baseUrl = jQuery('#pasteBaseUrl').val().trim();
var tableId = jQuery('#pasteTableId').val().trim();
var token = jQuery('#pasteToken').val().trim();
var name = jQuery('#pasteName').val().trim();
if (!baseUrl || !tableId || !token) { showAlert('请先点「解析」,或手动填写 Base URL / Table ID / Token', 'warning'); return; }
var text = jQuery('#teablePasteText').val() ||
('# Table: ' + (name || tableId) + '\n' +
'curl "' + baseUrl + '/api/table/' + tableId + '/record?fieldKeyType=name" \\\n' +
' -H "Authorization: Bearer ' + token + '"');
jQuery('#btnTeablePasteGo').prop('disabled', true);
api('POST', BASE + '/bom/api/' + BOM_ID + '/import/teable-paste', { text: text }, function (res) {
jQuery('#teablePasteModal').hide();
showAlert('已导入 ' + (res.imported || 0) + ' 行', 'success');
setTimeout(function () { location.reload(); }, 800);
}).always(function () { jQuery('#btnTeablePasteGo').prop('disabled', false); });
});
// ---- Excel ----
jQuery('#btnExcelTemplate').on('click', function () { window.location.href = BASE + '/bom/api/' + BOM_ID + '/export-template'; });
jQuery('#btnExcelImport').on('click', function () { jQuery('#excelFile').click(); });
jQuery('#excelFile').on('change', function () {
var file = this.files[0];
if (!file) { return; }
var fd = new FormData();
fd.append('file', file);
jQuery.ajax({
url: BASE + '/bom/api/' + BOM_ID + '/import/excel',
method: 'POST',
headers: { 'X-CSRF-TOKEN': csrf() },
data: fd, processData: false, contentType: false, dataType: 'json'
}).done(function (res) {
showAlert("{{ __('text.imported', '已导入') }} " + (res.imported || 0) + " {{ __('label.rows', '行') }}", 'success');
setTimeout(function () { location.reload(); }, 800);
}).fail(function (xhr) {
var m = xhr.responseJSON && xhr.responseJSON.message ? xhr.responseJSON.message : ('HTTP ' + xhr.status);
showAlert(m, 'danger');
});
jQuery(this).val('');
});
jQuery('#btnExcelExport').on('click', function () { window.location.href = BASE + '/bom/api/' + BOM_ID + '/export?format=xlsx'; });
jQuery('#btnExcelExportCsv').on('click', function () { window.location.href = BASE + '/bom/api/' + BOM_ID + '/export?format=csv'; });
});
</script>
@endpush
@endonce
@endsection