You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

202 lines
8.6 KiB

2 weeks ago
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>03 列表族实测</title>
<style>
body { font-family: system-ui, sans-serif; padding: 20px; background: #f5f5f5; max-width: 1100px; }
h1 { font-size: 1.2rem; }
h2 { font-size: 1rem; margin-top: 28px; border-left: 4px solid #1976d2; padding-left: 8px; }
button { padding: 6px 14px; font-size: 13px; cursor: pointer;
background: #1976d2; color: #fff; border: none; border-radius: 4px; margin-right: 6px; }
button.run-all { background: #388e3c; font-size: 14px; padding: 8px 20px; margin-bottom: 16px; }
pre { background: #fff; border: 1px solid #ddd; border-radius: 4px;
padding: 12px; font-size: 11px; white-space: pre-wrap; max-height: 300px; overflow-y: auto; }
ul.assertions { list-style: none; padding: 0; margin: 6px 0; }
ul.assertions li { font-size: 13px; padding: 2px 0; }
.case { background: #fff; border: 1px solid #ddd; border-radius: 6px;
padding: 14px; margin-top: 14px; }
.case h3 { margin: 0 0 8px; font-size: 13px; color: #333; }
a.back { font-size: 13px; color: #1976d2; }
</style>
</head>
<body>
<a class="back" href="index.html">← 返回首页</a>
<h1>🗒 03 列表族实测</h1>
<p>对应 spec-endpoints.md「列表」子域 6 条接口</p>
<button class="run-all" onclick="runAll()">▶ 全部运行</button>
<!-- ① POST /api/opportunity/page (viewType=MINE) -->
<div class="case" id="case-mine">
<h3>① POST /api/opportunity/page — viewType=MINE(我负责的)</h3>
<button onclick="runMine()">运行</button>
<ul class="assertions" id="assert-mine"></ul>
<pre id="result-mine">(待运行)</pre>
</div>
<!-- ② POST /api/opportunity/page (viewType=RECENT) -->
<div class="case" id="case-recent">
<h3>② POST /api/opportunity/page — viewType=RECENT(最近访问)</h3>
<button onclick="runRecent()">运行</button>
<ul class="assertions" id="assert-recent"></ul>
<pre id="result-recent">(待运行)</pre>
</div>
<!-- ③ POST /api/opportunity/page (viewType=PUBLIC_POOL) -->
<div class="case" id="case-pool">
<h3>③ POST /api/opportunity/page — viewType=PUBLIC_POOL(公海)</h3>
<button onclick="runPool()">运行</button>
<ul class="assertions" id="assert-pool"></ul>
<pre id="result-pool">(待运行)</pre>
</div>
<!-- ④ POST /api/opportunity/page (viewType=MANAGE) -->
<div class="case" id="case-manage">
<h3>④ POST /api/opportunity/page — viewType=MANAGE(管理列表)</h3>
<button onclick="runManage()">运行</button>
<ul class="assertions" id="assert-manage"></ul>
<pre id="result-manage">(待运行)</pre>
</div>
<!-- ⑤ POST /api/opportunity/page?viewType=RECENT — 最近访问(已合并) -->
<div class="case" id="case-rv">
<h3>⑤ POST /api/opportunity/page?viewType=RECENT — 最近访问(已合并入 /page)</h3>
<button onclick="runRecentVisits()">运行</button>
<ul class="assertions" id="assert-rv"></ul>
<pre id="result-rv">(待运行)</pre>
</div>
<!-- ⑥ POST /api/opportunity/board/stage-summary — 看板汇总 -->
<div class="case" id="case-board-summary">
<h3>⑥ POST /api/opportunity/board/stage-summary — 看板各阶段汇总(spec 预期)</h3>
<button onclick="runBoardSummary()">运行</button>
<ul class="assertions" id="assert-board-summary"></ul>
<pre id="result-board-summary">(待运行)</pre>
</div>
<!-- ⑦ POST /api/opportunity/board/cards — 看板单阶段卡片 -->
<div class="case" id="case-board-cards">
<h3>⑦ POST /api/opportunity/board/cards — 看板单阶段卡片(spec 预期)</h3>
<button onclick="runBoardCards()">运行</button>
<ul class="assertions" id="assert-board-cards"></ul>
<pre id="result-board-cards">(待运行)</pre>
</div>
<!-- ⑧ POST /api/opportunity/view-touch — 访问上报(实际路径) -->
<div class="case" id="case-touch">
<h3>⑧ POST /api/opportunity/view-touch — 访问上报</h3>
<button onclick="runTouch()">运行</button>
<ul class="assertions" id="assert-touch"></ul>
<pre id="result-touch">(待运行)</pre>
</div>
<script src="common.js"></script>
<script>
// 断言辅助(指定 ul id)
function a(ulId, label, pass, hint) {
const ul = document.getElementById(ulId);
const li = document.createElement('li');
li.style.color = pass ? '#1a7a1a' : '#b00020';
li.textContent = (pass ? '✅ ' : '❌ ') + label + (hint && !pass ? ` — ${hint}` : '');
ul.appendChild(li);
}
function clearCase(suffix) {
document.getElementById('assert-' + suffix).innerHTML = '';
document.getElementById('result-' + suffix).textContent = '运行中…';
}
// ① MINE
async function runMine() {
clearCase('mine');
const res = await apiFetch('POST', '/api/opportunity/page', { viewType: 'MINE', page: 1, size: 10 });
renderResult('result-mine', res);
a('assert-mine', 'HTTP 200', res.status === 200, `实际 ${res.status}`);
a('assert-mine', 'success=true', res.data?.success === true, JSON.stringify(res.data?.message));
const d = res.data?.data;
a('assert-mine', '返回 PageResult(含 records 字段)', Array.isArray(d?.records), `data=${JSON.stringify(d)?.slice(0,80)}`);
if (Array.isArray(d?.records) && d.records.length > 0) {
const row = d.records[0];
a('assert-mine', '行有 id 字段', !!row.id, JSON.stringify(row).slice(0,80));
a('assert-mine', '行有 name/opportunityName 字段', !!(row.name || row.opportunityName || row.projectName), JSON.stringify(row).slice(0,80));
}
}
// ② RECENT
async function runRecent() {
clearCase('recent');
const res = await apiFetch('POST', '/api/opportunity/page', { viewType: 'RECENT', page: 1, size: 10 });
renderResult('result-recent', res);
a('assert-recent', 'HTTP 200', res.status === 200, `实际 ${res.status}`);
a('assert-recent', 'success=true', res.data?.success === true, JSON.stringify(res.data?.message));
a('assert-recent', '返回 PageResult', Array.isArray(res.data?.data?.records), JSON.stringify(res.data?.data)?.slice(0,80));
}
// ③ PUBLIC_POOL
async function runPool() {
clearCase('pool');
const res = await apiFetch('POST', '/api/opportunity/page', { viewType: 'PUBLIC_POOL', page: 1, size: 10 });
renderResult('result-pool', res);
a('assert-pool', 'HTTP 200', res.status === 200, `实际 ${res.status}`);
a('assert-pool', 'success=true', res.data?.success === true, JSON.stringify(res.data?.message));
a('assert-pool', '返回 PageResult', Array.isArray(res.data?.data?.records), JSON.stringify(res.data?.data)?.slice(0,80));
}
// ④ MANAGE
async function runManage() {
clearCase('manage');
const res = await apiFetch('POST', '/api/opportunity/page', { viewType: 'MANAGE', page: 1, size: 10 });
renderResult('result-manage', res);
a('assert-manage', 'HTTP 200', res.status === 200, `实际 ${res.status}`);
a('assert-manage', 'success=true', res.data?.success === true, JSON.stringify(res.data?.message));
a('assert-manage', '返回 PageResult', Array.isArray(res.data?.data?.records), JSON.stringify(res.data?.data)?.slice(0,80));
}
// ⑤ /page?viewType=RECENT(已合并)
async function runRecentVisits() {
clearCase('rv');
const res = await apiFetch('POST', '/api/opportunity/page', { viewType: 'RECENT', pageNum: 1, pageSize: 10 });
renderResult('result-rv', res);
a('assert-rv', 'HTTP 200', res.status === 200, `实际 ${res.status}`);
a('assert-rv', 'success=true', res.data?.success === true, JSON.stringify(res.data?.message));
}
// ⑥ board/stage-summary
async function runBoardSummary() {
clearCase('board-summary');
const res = await apiFetch('POST', '/api/opportunity/board/stage-summary', {});
renderResult('result-board-summary', res);
a('assert-board-summary', 'HTTP 200(spec 预期看板汇总接口)', res.status === 200, `实际 ${res.status}`);
}
// ⑦ board/cards
async function runBoardCards() {
clearCase('board-cards');
const res = await apiFetch('POST', '/api/opportunity/board/cards', { offset: 0, limit: 5 });
renderResult('result-board-cards', res);
a('assert-board-cards', 'HTTP 200(spec 预期看板卡片接口)', res.status === 200, `实际 ${res.status}`);
}
// ⑧ /view-touch(实际路径)
async function runTouch() {
clearCase('touch');
const res = await apiFetch('POST', '/api/opportunity/view-touch', null, { oppId: '999999999001' });
renderResult('result-touch', res);
a('assert-touch', 'HTTP 200', res.status === 200, `实际 ${res.status}`);
a('assert-touch', 'success=true', res.data?.success === true, JSON.stringify(res.data?.message));
}
async function runAll() {
await runMine();
await runRecent();
await runPool();
await runManage();
await runRecentVisits();
await runBoardSummary();
await runBoardCards();
await runTouch();
}
</script>
</body>
</html>