u
This commit is contained in:
1 parent
742ca27f2e
commit
95fd2aeced
1 file changed
+193
-91
+193
-91
@@ -1,117 +1,219 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>金额输入 Demo</title>
|
||||
<meta charset="UTF-8" />
|
||||
<title>ERP账单 - 方案4 Demo(diff提交 + 新增末尾)</title>
|
||||
|
||||
<style>
|
||||
body {
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI";
|
||||
background: #f5f6f7;
|
||||
}
|
||||
<style>
|
||||
body { font-family: Arial; margin: 20px; }
|
||||
table { border-collapse: collapse; width: 100%; margin-top: 10px; }
|
||||
th, td { border: 1px solid #ddd; padding: 6px; text-align: left; }
|
||||
th { background: #f5f5f5; }
|
||||
input { width: 100%; box-sizing: border-box; }
|
||||
button { margin: 5px; }
|
||||
.toolbar { margin-bottom: 10px; }
|
||||
.badge { padding: 2px 6px; background: orange; color: white; border-radius: 4px; }
|
||||
</style>
|
||||
|
||||
.container {
|
||||
max-width: 420px;
|
||||
margin: 0 auto;
|
||||
background: #fff;
|
||||
min-height: 100vh;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.amount-wrapper {
|
||||
text-align: center;
|
||||
padding: 30px 0;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.amount-label {
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.amount {
|
||||
font-size: 42px;
|
||||
font-weight: 600;
|
||||
color: #111;
|
||||
letter-spacing: -1px;
|
||||
}
|
||||
|
||||
/* 点击反馈 */
|
||||
.amount-wrapper:active {
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
/* 隐藏输入 */
|
||||
.hidden-input {
|
||||
position: absolute;
|
||||
opacity: 0;
|
||||
pointer-events: none;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
<div class="container">
|
||||
|
||||
<!-- 金额 -->
|
||||
<div class="amount-wrapper" onclick="focusInput()">
|
||||
<div class="amount-label">金额</div>
|
||||
<div class="amount" id="amount">¥ 0.00</div>
|
||||
</div>
|
||||
|
||||
<!-- 隐藏输入 -->
|
||||
<input
|
||||
id="input"
|
||||
class="hidden-input"
|
||||
type="text"
|
||||
inputmode="decimal"
|
||||
oninput="handleInput(this.value)"
|
||||
/>
|
||||
<h2>账单明细(方案4:分页 + diff提交)</h2>
|
||||
|
||||
<div class="toolbar">
|
||||
<button onclick="prevPage()">上一页</button>
|
||||
<button onclick="nextPage()">下一页</button>
|
||||
<button onclick="addRow()">新增费用</button>
|
||||
<button onclick="submit()">提交(只提交diff)</button>
|
||||
<span id="pageInfo"></span>
|
||||
</div>
|
||||
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>ID</th>
|
||||
<th>费用名称</th>
|
||||
<th>金额</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="tbody"></tbody>
|
||||
</table>
|
||||
|
||||
<h3>Diff 输出:</h3>
|
||||
<pre id="output"></pre>
|
||||
|
||||
<script>
|
||||
let value = "0";
|
||||
// ===============================
|
||||
// 模拟后端数据(200条)
|
||||
// ===============================
|
||||
let serverData = Array.from({length: 200}, (_, i) => ({
|
||||
id: i + 1,
|
||||
name: "费用-" + (i + 1),
|
||||
amount: Math.floor(Math.random() * 1000)
|
||||
}));
|
||||
|
||||
function focusInput() {
|
||||
document.getElementById("input").focus();
|
||||
// ===============================
|
||||
// 分页参数
|
||||
// ===============================
|
||||
let page = 1;
|
||||
let pageSize = 10;
|
||||
|
||||
// ===============================
|
||||
// diff结构(核心)
|
||||
// ===============================
|
||||
const state = {
|
||||
originalMap: new Map(),
|
||||
updated: new Map(),
|
||||
created: [],
|
||||
deleted: new Set()
|
||||
};
|
||||
|
||||
// ===============================
|
||||
// 模拟分页接口
|
||||
// ===============================
|
||||
function fetchPage(page, size) {
|
||||
const start = (page - 1) * size;
|
||||
const end = start + size;
|
||||
return serverData.slice(start, end);
|
||||
}
|
||||
|
||||
// ===============================
|
||||
// 渲染
|
||||
// ===============================
|
||||
function render() {
|
||||
const data = fetchPage(page, pageSize);
|
||||
|
||||
const tbody = document.getElementById("tbody");
|
||||
tbody.innerHTML = "";
|
||||
|
||||
state.originalMap.clear();
|
||||
|
||||
data.forEach(row => {
|
||||
state.originalMap.set(row.id, JSON.parse(JSON.stringify(row)));
|
||||
|
||||
if (state.deleted.has(row.id)) return;
|
||||
|
||||
const tr = document.createElement("tr");
|
||||
|
||||
tr.innerHTML = `
|
||||
<td>${row.id}</td>
|
||||
<td><input value="${row.name}" onchange="edit(${row.id}, 'name', this.value)"></td>
|
||||
<td><input value="${row.amount}" onchange="edit(${row.id}, 'amount', this.value)"></td>
|
||||
<td>
|
||||
<button onclick="del(${row.id})">删除</button>
|
||||
${state.updated.has(row.id) ? '<span class="badge">已修改</span>' : ''}
|
||||
</td>
|
||||
`;
|
||||
|
||||
tbody.appendChild(tr);
|
||||
});
|
||||
|
||||
document.getElementById("pageInfo").innerText =
|
||||
`第 ${page} 页 / 共 ${Math.ceil(serverData.length / pageSize)} 页`;
|
||||
}
|
||||
|
||||
// ===============================
|
||||
// 编辑(diff核心)
|
||||
// ===============================
|
||||
function edit(id, field, value) {
|
||||
const original = state.originalMap.get(id);
|
||||
if (!original) return;
|
||||
|
||||
let current = state.updated.get(id);
|
||||
if (!current) {
|
||||
current = {...original};
|
||||
}
|
||||
|
||||
function handleInput(text) {
|
||||
// 只保留数字和小数点
|
||||
let v = text.replace(/[^\d.]/g, "");
|
||||
current[field] = field === "amount" ? Number(value) : value;
|
||||
state.updated.set(id, current);
|
||||
}
|
||||
|
||||
// 只允许一个 .
|
||||
const parts = v.split(".");
|
||||
if (parts.length > 2) {
|
||||
v = parts[0] + "." + parts[1];
|
||||
}
|
||||
// ===============================
|
||||
// 删除
|
||||
// ===============================
|
||||
function del(id) {
|
||||
state.deleted.add(id);
|
||||
state.updated.delete(id);
|
||||
render();
|
||||
}
|
||||
|
||||
// 限制两位小数
|
||||
if (v.includes(".")) {
|
||||
let [int, dec] = v.split(".");
|
||||
v = int + "." + dec.slice(0, 2);
|
||||
}
|
||||
// ===============================
|
||||
// 新增(关键修复:追加到末尾 + 跳最后页)
|
||||
// ===============================
|
||||
function addRow() {
|
||||
const newId = Date.now();
|
||||
|
||||
// 去前导0
|
||||
if (v.startsWith("0") && v[1] !== ".") {
|
||||
v = v.replace(/^0+/, "");
|
||||
}
|
||||
const row = {
|
||||
id: newId,
|
||||
name: "新费用",
|
||||
amount: 0
|
||||
};
|
||||
|
||||
if (v === "") v = "0";
|
||||
// ✅ 追加到末尾(ERP标准:append-only)
|
||||
serverData.push(row);
|
||||
|
||||
value = v;
|
||||
updateUI();
|
||||
// 记录create diff
|
||||
state.created.push(row);
|
||||
|
||||
// ✅ 自动跳转到最后一页
|
||||
page = Math.ceil(serverData.length / pageSize);
|
||||
|
||||
render();
|
||||
}
|
||||
|
||||
// ===============================
|
||||
// 分页
|
||||
// ===============================
|
||||
function nextPage() {
|
||||
if (page * pageSize < serverData.length) {
|
||||
page++;
|
||||
render();
|
||||
}
|
||||
}
|
||||
|
||||
function updateUI() {
|
||||
document.getElementById("amount").innerText = "¥ " + value;
|
||||
document.getElementById("input").value = value;
|
||||
function prevPage() {
|
||||
if (page > 1) {
|
||||
page--;
|
||||
render();
|
||||
}
|
||||
}
|
||||
|
||||
// ===============================
|
||||
// 提交 diff(核心)
|
||||
// ===============================
|
||||
function submit() {
|
||||
|
||||
const patch = {
|
||||
create: state.created,
|
||||
update: [],
|
||||
delete: Array.from(state.deleted)
|
||||
};
|
||||
|
||||
// 计算update diff
|
||||
state.updated.forEach((newVal, id) => {
|
||||
const oldVal = state.originalMap.get(id);
|
||||
if (!oldVal) return;
|
||||
|
||||
if (
|
||||
oldVal.name !== newVal.name ||
|
||||
oldVal.amount !== newVal.amount
|
||||
) {
|
||||
patch.update.push(newVal);
|
||||
}
|
||||
});
|
||||
|
||||
document.getElementById("output").innerText =
|
||||
JSON.stringify(patch, null, 2);
|
||||
|
||||
console.log("提交patch:", patch);
|
||||
|
||||
alert("已生成diff,请查看下方JSON或控制台");
|
||||
}
|
||||
|
||||
// 初始渲染
|
||||
render();
|
||||
</script>
|
||||
|
||||
</body>
|
||||
|
||||
Reference in new issue
Block a user