feat: add multiple arrival batch rows

This commit is contained in:
Codex 2026-10-12 11:54:11 +08:00
parent 7aeb35595e
commit 8c229bc162
2 changed files with 74 additions and 8 deletions

View File

@ -20075,7 +20075,7 @@ function showOrderDetail(encodedStr, title) {
html += '</tr></thead><tbody id="unfinishedOrderBody"><tr><td colspan="6" style="border:1.5px solid #b0b0b0;padding:20px;text-align:center;color:#bbb">加载中...</td></tr></tbody></table>';
} else {
html += '<div style="display:flex;align-items:center;margin:0 0 8px 0">';
html += '<div style="margin-left:auto"><button id="addBatchBtn" class="btn btn-sm btn-outline" onclick="addArrivalBatchRow(this)" data-order-id="' + row.id + '" data-order-type="' + orderType + '" style="font-size:12px">➕ 添加一行</button></div>';
html += '<div style="margin-left:auto;display:flex;align-items:center;gap:6px"><button id="addMultiBatchBtn" class="btn btn-sm btn-outline" onclick="promptAddArrivalBatchRows(this)" data-order-id="' + row.id + '" data-order-type="' + orderType + '" style="font-size:12px">➕ 添加多行</button><button id="addBatchBtn" class="btn btn-sm btn-outline" onclick="addArrivalBatchRow(this)" data-order-id="' + row.id + '" data-order-type="' + orderType + '" style="font-size:12px">➕ 添加一行</button></div>';
html += '</div>';
var showDefectiveQty = orderType === 'packaging';
html += '<table id="arrivalBatchTable" style="width:100%;border-collapse:collapse;font-size:13px;table-layout:fixed" data-order-qty="' + (parseInt(row['数量']) || 0) + '"><thead><tr>';
@ -20256,19 +20256,17 @@ function showOrderDetail(encodedStr, title) {
})();
}
function addArrivalBatchRow(btn) {
btn.disabled = true;
btn.textContent = '⋯';
function appendArrivalBatchEditingRow(btn) {
var overlay = btn.closest('.modal-overlay');
if (!overlay) {
var tbl = btn.closest('table');
if (tbl) overlay = tbl.closest('.modal-overlay');
}
if (!overlay) return null;
var orderId = btn.getAttribute('data-order-id');
var orderType = btn.getAttribute('data-order-type') || 'domestic';
var tbody = overlay.querySelector('#arrivalBatchBody');
if (!tbody) { btn.disabled = false; btn.textContent = '➕ 添加一行'; return; }
if (tbody.querySelector('.batch-editing')) { btn.disabled = false; btn.textContent = '➕ 添加一行'; return; }
if (!tbody) return null;
// 计算下一个批次编号
var lastNum = 0;
@ -20299,10 +20297,37 @@ function addArrivalBatchRow(btn) {
// 追加到表格末尾(合计行上方)
tbody.appendChild(tr);
recalcArrivalUnfinished();
return tr;
}
function addArrivalBatchRow(btn) {
var tr = appendArrivalBatchEditingRow(btn);
if (!tr) return;
var inp = tr.querySelector('input[type="date"]');
if (inp) inp.focus();
}
function promptAddArrivalBatchRows(btn) {
showCenteredPrompt('添加多行', '请输入需要添加的到货批次行数(1–100行):', '3', {
type: 'number', min: 1, max: 100, step: 1, inputMode: 'numeric'
}).then(function(value) {
if (value === null) return;
var count = Number(value);
if (!Number.isInteger(count) || count < 1 || count > 100) {
showCenteredAlert('输入有误', '请输入1到100之间的整数');
return;
}
var firstRow = null;
for (var i = 0; i < count; i++) {
var row = appendArrivalBatchEditingRow(btn);
if (!row) break;
if (!firstRow) firstRow = row;
}
var firstInput = firstRow && firstRow.querySelector('input[type="date"]');
if (firstInput) firstInput.focus();
});
}
/**
* 取消新增行(删除未保存的行)
*/
@ -27330,15 +27355,22 @@ window.showCenteredConfirm = function(title, message) {
};
// 通用居中输入弹窗(返回Promise,resolve(输入值)=确认, resolve(null)=取消)
window.showCenteredPrompt = function(title, message, defaultValue) {
window.showCenteredPrompt = function(title, message, defaultValue, options) {
return new Promise(function(resolve) {
options = options || {};
var inputType = options.type === 'number' ? 'number' : 'text';
var inputAttrs = '';
if (options.min !== undefined) inputAttrs += ' min="' + escHtml(String(options.min)) + '"';
if (options.max !== undefined) inputAttrs += ' max="' + escHtml(String(options.max)) + '"';
if (options.step !== undefined) inputAttrs += ' step="' + escHtml(String(options.step)) + '"';
if (options.inputMode) inputAttrs += ' inputmode="' + escHtml(String(options.inputMode)) + '"';
var modal = document.createElement('div');
modal.style.cssText = 'position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.45);z-index:100000;display:flex;align-items:center;justify-content:center;overflow-y:auto';
modal.innerHTML = '<div style="background:#fff;border-radius:12px;width:440px;max-width:90vw;box-shadow:0 8px 24px rgba(0,0,0,0.15);margin:20px;overflow:hidden">' +
'<div style="padding:24px 24px 12px">' +
'<h3 style="margin:0 0 12px;font-size:17px">' + escHtml(title) + '</h3>' +
(message ? '<p style="margin:0 0 12px;font-size:14px;color:#555;line-height:1.6">' + message + '</p>' : '') +
'<input type="text" id="promptInput" value="' + escHtml(defaultValue || '') + '" style="width:100%;box-sizing:border-box;padding:8px 12px;font-size:14px;border:1px solid #d9d9d9;border-radius:6px;outline:none" autocomplete="off">' +
'<input type="' + inputType + '" id="promptInput" value="' + escHtml(defaultValue || '') + '"' + inputAttrs + ' style="width:100%;box-sizing:border-box;padding:8px 12px;font-size:14px;border:1px solid #d9d9d9;border-radius:6px;outline:none" autocomplete="off">' +
'</div>' +
'<div style="padding:12px 24px 20px;display:flex;justify-content:flex-end;gap:10px">' +
'<button class="btn" id="promptCancelBtn">取消</button>' +

View File

@ -0,0 +1,34 @@
"""订单详情到货批次批量加行回归测试。"""
import unittest
from pathlib import Path
APP_JS = (Path(__file__).resolve().parents[1] / 'static/js/app.js').read_text(encoding='utf-8')
class OrderArrivalMultiRowsTest(unittest.TestCase):
def test_detail_toolbar_places_multi_row_button_before_single_row_button(self):
multi_pos = APP_JS.index('id="addMultiBatchBtn"')
single_pos = APP_JS.index('id="addBatchBtn"', multi_pos)
self.assertLess(multi_pos, single_pos)
self.assertIn('onclick="promptAddArrivalBatchRows(this)"', APP_JS[multi_pos:single_pos])
def test_multi_row_prompt_requires_bounded_integer(self):
self.assertIn("showCenteredPrompt('添加多行'", APP_JS)
self.assertIn("type: 'number', min: 1, max: 100, step: 1", APP_JS)
self.assertIn('!Number.isInteger(count) || count < 1 || count > 100', APP_JS)
def test_shared_row_builder_allows_repeated_appends(self):
self.assertIn('function appendArrivalBatchEditingRow(btn)', APP_JS)
self.assertIn('for (var i = 0; i < count; i++)', APP_JS)
self.assertIn('var row = appendArrivalBatchEditingRow(btn)', APP_JS)
self.assertNotIn("tbody.querySelector('.batch-editing')", APP_JS)
def test_centered_prompt_supports_numeric_inputs_without_breaking_text_default(self):
self.assertIn('window.showCenteredPrompt = function(title, message, defaultValue, options)', APP_JS)
self.assertIn("var inputType = options.type === 'number' ? 'number' : 'text'", APP_JS)
if __name__ == '__main__':
unittest.main()