470 lines
24 KiB
Plaintext
470 lines
24 KiB
Plaintext
@extends($layout)
|
||
@section('content')
|
||
|
||
@php
|
||
$bom = $detail['bom'] ?? [];
|
||
$columns = $detail['columns'] ?? [];
|
||
$items = $detail['items'] ?? [];
|
||
$sources = $detail['sources'] ?? [];
|
||
$hiddenColumns = $detail['hiddenColumns'] ?? [];
|
||
$filledKeys = $detail['filledKeys'] ?? [];
|
||
$imageColumns = $detail['imageColumns'] ?? [];
|
||
$fileColumns = $detail['fileColumns'] ?? [];
|
||
$bomId = (int)($bom['id'] ?? 0);
|
||
@endphp
|
||
|
||
<link rel="stylesheet" href="{{ BASE_URL }}/dist/handsontable/handsontable.full.min.css">
|
||
|
||
<div class="pageheader">
|
||
<div class="pageicon"><span class="fa fa-fw fa-list-check"></span></div>
|
||
<div class="pagetitle">
|
||
<h1>{{ $bom['productName'] ?? $bom['bomNo'] ?? __('menu.bom') }}</h1>
|
||
<p>{{ __('text.bom_subtitle', 'BOM 明细') }} — {{ $bom['bomNo'] ?? '' }}</p>
|
||
</div>
|
||
<div class="maincontent" style="margin-top:16px;">
|
||
<a href="{{ BASE_URL }}/bom/show" class="btn btn-default"><i class="fa fa-arrow-left"></i> {{ __('links.back', '返回') }}</a>
|
||
<button type="button" class="btn btn-primary" id="btnAddRow"><i class="fa fa-plus"></i> {{ __('label.add_row', '新增行') }}</button>
|
||
<button type="button" class="btn btn-default" id="btnAddColumn"><i class="fa fa-columns"></i> {{ __('label.add_column', '增加列') }}</button>
|
||
<button type="button" class="btn btn-default" id="btnColumnSettings"><i class="fa fa-eye"></i> 列设置</button>
|
||
|
||
{{-- Teable 导入下拉 --}}
|
||
<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-cloud-download"></i> Teable 导入 <span class="caret"></span>
|
||
</button>
|
||
<ul class="dropdown-menu dropdown-menu-right">
|
||
<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 class="divider"></li>
|
||
<li><a href="javascript:void(0)" id="btnSourceManager"><i class="fa fa-database"></i> 数据源管理</a></li>
|
||
</ul>
|
||
</div>
|
||
|
||
{{-- Excel 下拉 --}}
|
||
<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-file-excel"></i> Excel <span class="caret"></span>
|
||
</button>
|
||
<ul class="dropdown-menu dropdown-menu-right">
|
||
<li><a href="javascript:void(0)" id="btnExcelTemplate"><i class="fa fa-download"></i> 模板导出</a></li>
|
||
<li><a href="javascript:void(0)" id="btnExcelImport"><i class="fa fa-upload"></i> 导入数据</a></li>
|
||
<li class="divider"></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>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="maincontent">
|
||
<div class="maincontentinner">
|
||
|
||
{{-- Teable 源配置区已移至「数据源管理」弹窗 --}}
|
||
<div id="bomAlert" class="alert" style="display:none;"></div>
|
||
|
||
{{-- Handsontable 容器(JS 动态铺满剩余视口) --}}
|
||
<div id="bomHot" style="width:100%;height:600px;overflow:hidden;"></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 curl -X GET "https://table.universal-onebot.com/api/table/tblXXX/record?fieldKeyType=name" \ -H "Authorization: Bearer teable_...""></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="columnSettingsModal" 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:520px;margin:6% auto;background:#fff;border-radius:4px;padding:18px;">
|
||
<h4 style="margin-top:0;"><i class="fa fa-eye"></i> 列设置</h4>
|
||
<p class="text-muted">勾选需要显示的列;未勾选的列将被隐藏。</p>
|
||
<div id="columnChecklist" style="max-height:360px;overflow-y:auto;border:1px solid #ddd;padding:10px;border-radius:4px;"></div>
|
||
<div style="margin-top:10px;">
|
||
<button type="button" class="btn btn-default btn-xs" id="btnHideEmpty"><i class="fa fa-eye-slash"></i> 一键隐藏空列</button>
|
||
<button type="button" class="btn btn-default btn-xs" id="btnShowAll"><i class="fa fa-eye"></i> 全部显示</button>
|
||
</div>
|
||
<div style="text-align:right;margin-top:12px;">
|
||
<button type="button" class="btn btn-primary" id="btnColumnSettingsSave">确定</button>
|
||
<button type="button" class="btn btn-default" id="btnColumnSettingsClose">取消</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;">×</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;">
|
||
|
||
<script src="{{ BASE_URL }}/dist/handsontable/handsontable.full.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 HIDDEN = {!! json_encode(array_values($hiddenColumns)) !!};
|
||
var FILLED = {!! json_encode(array_values($filledKeys)) !!};
|
||
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 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');
|
||
});
|
||
}
|
||
|
||
// ---- 数据映射:后端 items -> Handsontable data ----
|
||
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;
|
||
});
|
||
|
||
// ---- renderer ----
|
||
function parseAttach(value) {
|
||
if (Array.isArray(value)) { return value; }
|
||
if (typeof value === 'string' && value.charAt(0) === '[') {
|
||
try { var a = JSON.parse(value); return Array.isArray(a) ? a : []; } catch (e) { return []; }
|
||
}
|
||
return [];
|
||
}
|
||
|
||
function imageRenderer(instance, td, row, col, prop, value) {
|
||
td.innerHTML = '';
|
||
td.style.verticalAlign = 'middle';
|
||
parseAttach(value).forEach(function (it) {
|
||
if (!it || !it.url) { return; }
|
||
var a = document.createElement('a');
|
||
a.href = it.url; a.target = '_blank'; a.title = it.name || '';
|
||
var img = document.createElement('img');
|
||
img.src = it.url; img.alt = it.name || '';
|
||
img.style.height = '42px'; img.style.width = 'auto'; img.style.margin = '2px'; img.style.borderRadius = '2px';
|
||
a.appendChild(img); td.appendChild(a);
|
||
});
|
||
return td;
|
||
}
|
||
|
||
function fileRenderer(instance, td, row, col, prop, value) {
|
||
td.innerHTML = '';
|
||
parseAttach(value).forEach(function (it) {
|
||
if (!it || !it.url) { return; }
|
||
var a = document.createElement('a');
|
||
a.href = it.url; a.target = '_blank'; a.textContent = it.name || '下载';
|
||
a.style.display = 'block'; a.style.margin = '2px 0';
|
||
td.appendChild(a);
|
||
});
|
||
return td;
|
||
}
|
||
|
||
function delRenderer(instance, td, row) {
|
||
td.innerHTML = '';
|
||
td.style.textAlign = 'center';
|
||
var btn = document.createElement('button');
|
||
btn.type = 'button'; btn.className = 'btn btn-xs btn-danger';
|
||
btn.textContent = '删';
|
||
btn.onclick = function () { deleteRow(row); };
|
||
td.appendChild(btn);
|
||
return td;
|
||
}
|
||
|
||
// ---- 列定义 ----
|
||
var colDefs = [];
|
||
FIXED.forEach(function (key) {
|
||
colDefs.push({ data: key, title: FIXED_LABELS[key] || key, width: 120 });
|
||
});
|
||
COLUMNS.forEach(function (col) {
|
||
colDefs.push({ data: col.key, title: col.label || col.key, width: 140 });
|
||
});
|
||
|
||
IMAGE_COLS.forEach(function (key) {
|
||
var d = colDefs.filter(function (c) { return c.data === key; })[0];
|
||
if (d) { d.renderer = imageRenderer; d.readOnly = true; d.width = 120; }
|
||
});
|
||
FILE_COLS.forEach(function (key) {
|
||
var d = colDefs.filter(function (c) { return c.data === key; })[0];
|
||
if (d) { d.renderer = fileRenderer; d.readOnly = true; d.width = 160; }
|
||
});
|
||
|
||
// 操作列
|
||
colDefs.push({ data: '_delete', title: '操作', width: 60, readOnly: true, renderer: delRenderer });
|
||
|
||
// 初始隐藏列索引
|
||
var hiddenIndexes = HIDDEN.map(function (k) { return ALL_KEYS.indexOf(k); }).filter(function (i) { return i >= 0; });
|
||
|
||
// ---- 初始化 Handsontable ----
|
||
var hotContainer = document.getElementById('bomHot');
|
||
function calcHeight() {
|
||
var top = jQuery(hotContainer).offset().top;
|
||
var winH = jQuery(window).height();
|
||
var h = winH - top - 24;
|
||
return h < 300 ? 300 : h;
|
||
}
|
||
var hot = new Handsontable(hotContainer, {
|
||
data: data,
|
||
columns: colDefs,
|
||
colHeaders: true,
|
||
rowHeaders: false,
|
||
width: '100%',
|
||
height: calcHeight(),
|
||
licenseKey: 'non-commercial-and-evaluation',
|
||
stretchH: 'all',
|
||
manualColumnResize: true,
|
||
manualRowResize: true,
|
||
filters: true,
|
||
dropdownMenu: true,
|
||
search: true,
|
||
contextMenu: ['row_above', 'row_below', 'remove_row', '---------', 'copy', 'cut', 'paste', '---------', 'undo', 'redo'],
|
||
copyPaste: true,
|
||
undo: true,
|
||
hiddenColumns: { columns: hiddenIndexes, indicators: true },
|
||
afterChange: function (changes, source) {
|
||
if (!changes || source === 'loadData' || source === 'updateData' || source === 'alter') { return; }
|
||
var rows = {};
|
||
changes.forEach(function (ch) {
|
||
if (ch[2] === ch[3]) { return; }
|
||
rows[ch[0]] = true;
|
||
});
|
||
Object.keys(rows).forEach(function (r) { saveRow(parseInt(r, 10)); });
|
||
}
|
||
});
|
||
|
||
// 表格铺满屏幕:窗口尺寸变化时重算高度
|
||
jQuery(window).on('resize', function () { hot.updateSettings({ height: calcHeight() }); });
|
||
|
||
// ---- 保存行 ----
|
||
function saveRow(row) {
|
||
var d = hot.getSourceDataAtRow(row);
|
||
if (!d) { return; }
|
||
var payload = {};
|
||
ALL_KEYS.forEach(function (k) { payload[k] = d[k] || ''; });
|
||
if (d._id) { payload.itemId = d._id; }
|
||
api('POST', BASE + '/bom/api/' + BOM_ID + '/item', payload, function (res) {
|
||
if (res.itemId && !d._id) { d._id = res.itemId; }
|
||
});
|
||
}
|
||
|
||
// ---- 删除行 ----
|
||
function deleteRow(row) {
|
||
if (!confirm("{{ __('text.confirm_delete') }}")) { return; }
|
||
var d = hot.getSourceDataAtRow(row);
|
||
if (!d._id) { hot.alter('remove_row', row); return; }
|
||
api('DELETE', BASE + '/bom/api/item/' + d._id, null, function () { hot.alter('remove_row', row); });
|
||
}
|
||
|
||
// ---- 新增行 ----
|
||
jQuery('#btnAddRow').on('click', function () {
|
||
var empty = { _id: null };
|
||
ALL_KEYS.forEach(function (k) { empty[k] = ''; });
|
||
data.push(empty);
|
||
hot.updateData(data);
|
||
hot.selectCell(data.length - 1, 0);
|
||
});
|
||
|
||
// ---- 列显隐 ----
|
||
function openColumnSettings() {
|
||
var html = '';
|
||
FIXED.forEach(function (key) {
|
||
html += '<label style="display:block;font-weight:normal;margin-bottom:4px;"><input type="checkbox" class="col-check" data-key="' + key + '"' + (HIDDEN.indexOf(key) === -1 ? ' checked' : '') + '> ' + (FIXED_LABELS[key] || key) + '</label>';
|
||
});
|
||
COLUMNS.forEach(function (col) {
|
||
html += '<label style="display:block;font-weight:normal;margin-bottom:4px;"><input type="checkbox" class="col-check" data-key="' + col.key + '"' + (HIDDEN.indexOf(col.key) === -1 ? ' checked' : '') + '> ' + (col.label || col.key) + '</label>';
|
||
});
|
||
jQuery('#columnChecklist').html(html || '<em class="text-muted">暂无列</em>');
|
||
jQuery('#columnSettingsModal').show();
|
||
}
|
||
|
||
jQuery('#btnColumnSettings').on('click', openColumnSettings);
|
||
jQuery('#btnColumnSettingsClose').on('click', function () { jQuery('#columnSettingsModal').hide(); });
|
||
|
||
jQuery('#btnHideEmpty').on('click', function () {
|
||
jQuery('#columnChecklist .col-check').each(function () {
|
||
var key = jQuery(this).data('key');
|
||
if (FILLED.indexOf(key) === -1) { jQuery(this).prop('checked', false); }
|
||
});
|
||
});
|
||
jQuery('#btnShowAll').on('click', function () {
|
||
jQuery('#columnChecklist .col-check').prop('checked', true);
|
||
});
|
||
|
||
jQuery('#btnColumnSettingsSave').on('click', function () {
|
||
var hiddenKeys = [];
|
||
jQuery('#columnChecklist .col-check').each(function () {
|
||
if (!jQuery(this).prop('checked')) { hiddenKeys.push(jQuery(this).data('key')); }
|
||
});
|
||
var idx = hiddenKeys.map(function (k) { return ALL_KEYS.indexOf(k); }).filter(function (i) { return i >= 0; });
|
||
hot.updateSettings({ hiddenColumns: { columns: idx, indicators: true } });
|
||
api('POST', BASE + '/bom/api/' + BOM_ID + '/column-visibility', { keys: hiddenKeys }, function () {
|
||
jQuery('#columnSettingsModal').hide();
|
||
HIDDEN = hiddenKeys;
|
||
});
|
||
});
|
||
|
||
// ---- 增加列 ----
|
||
jQuery('#btnAddColumn').on('click', function () {
|
||
var key = prompt("{{ __('label.column_key', '列标识(英文或中文,用于存储)') }}", "");
|
||
if (!key) { return; }
|
||
var label = prompt("{{ __('label.column_label', '列显示名') }}", key);
|
||
if (label === null) { return; }
|
||
api('POST', BASE + '/bom/api/' + BOM_ID + '/column', { key: key, label: label }, function () { location.reload(); });
|
||
});
|
||
|
||
// ---- 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
|