Files
sys-analysis-design/ba-output/e-commerce/03-specification/WF_US002-003_v1.0.html
Leonard-ThindPad-P50 2c7bcde741 improve BA skill
2026-09-09 06:34:57 +07:00

240 lines
13 KiB
HTML
Raw 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.

<!--
WF_US002-003_v1.0.html — wireframe low-fi tự chứa, sinh bởi skill ba-3-specification (activity wf).
Nguồn hành vi: SRS_US002-003_v1.0.md · Nguồn bố cục: e-commerce/docs/sections/07-giao-dien.md §7.1.1 SCR-04 (chế độ 🎨)
Nguồn quy ước: UICONV_e-commerce_v1.0.md v1.1
Giữ nguyên <style>/<script> của template gốc (ba-3-specification/templates/wireframe.html) để mọi WF
trong dự án trông và thao tác giống nhau. Chỉ thay nội dung trong <main>.
Thành phần chỉ có ở prototype (SAD §7.1.1) mà SRS chưa có (checkbox chọn dòng, badge "Sản phẩm đã hết
hàng", badge "Giá đã thay đổi") được vẽ trong khối .proto-only, KHÔNG có data-c — xem WF §5 (#1,#2,#3).
-->
<!doctype html>
<html lang="vi">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>WF — US-002, US-003 — Giỏ hàng (SCR-04)</title>
<style>
:root{--ink:#222;--mute:#777;--line:#bbb;--fill:#eee;--fill2:#f7f7f7;--accent:#3b6ea5;--danger:#b23a3a;--bg:#fafafa}
*{box-sizing:border-box}
body{margin:0;font:14px/1.45 system-ui,Segoe UI,Roboto,sans-serif;color:var(--ink);background:var(--bg)}
header.toolbar{position:sticky;top:0;z-index:9;background:#fff;border-bottom:1px solid var(--line);padding:8px 16px;display:flex;gap:16px;align-items:center;flex-wrap:wrap}
header.toolbar h1{font-size:15px;margin:0 16px 0 0}
header.toolbar label{font-size:12px;color:var(--mute)}
header.toolbar select{font:inherit}
.legend{font-size:12px;color:var(--mute);margin-left:auto}
main{padding:24px 16px;display:grid;gap:40px;justify-items:center}
.screen{width:100%;max-width:1200px;background:#fff;border:1px solid var(--line);border-radius:6px;overflow:hidden}
.screen>h2{margin:0;padding:8px 12px;font-size:13px;font-weight:600;background:var(--fill2);border-bottom:1px dashed var(--line);color:var(--mute)}
.screen>h2 small{font-weight:400;margin-left:8px}
.body{padding:12px;display:grid;gap:12px}
.zone{border:1px dashed var(--line);border-radius:4px;padding:10px;position:relative;background:#fff;min-height:44px}
.zone::before{content:attr(data-zone) " · " attr(data-name);position:absolute;top:-8px;left:8px;background:#fff;padding:0 4px;font-size:10px;color:var(--mute);letter-spacing:.02em}
.row{display:flex;gap:8px;align-items:center;flex-wrap:wrap}
.row.right{justify-content:flex-end}
.row.between{justify-content:space-between}
[data-c]{position:relative}
[data-c]::after{content:attr(data-c);position:absolute;top:-9px;right:-4px;font-size:9px;background:var(--ink);color:#fff;padding:0 4px;border-radius:3px;line-height:14px;opacity:.85}
.label{font-weight:600;font-size:16px}
.btn{display:inline-block;padding:6px 14px;border:1px solid var(--ink);border-radius:4px;background:#fff;cursor:default;white-space:nowrap}
.btn-primary{background:var(--accent);border-color:var(--accent);color:#fff}
.btn-secondary{background:#fff}
.btn-danger{border-color:var(--danger);color:var(--danger)}
.btn[aria-disabled="true"]{opacity:.45;border-style:dashed}
.link{color:var(--accent);text-decoration:underline}
.input,.select{display:inline-block;min-width:160px;padding:6px 8px;border:1px solid var(--line);border-radius:4px;background:#fff;color:var(--mute)}
.select::after{content:" ▾";float:right}
.field{display:grid;gap:4px}
.field>span{font-size:12px}
.field>span .req{color:var(--danger)}
.hint{font-size:12px;color:var(--mute)}
.table{width:100%;border-collapse:collapse}
.table th,.table td{border:1px solid var(--line);padding:6px 8px;text-align:left;font-size:13px}
.table th{background:var(--fill2)}
.card{border:1px solid var(--line);border-radius:4px;padding:10px;background:#fff}
.badge{display:inline-block;padding:0 8px;border:1px solid var(--line);border-radius:10px;font-size:12px;background:var(--fill2)}
.img{background:repeating-linear-gradient(45deg,var(--fill),var(--fill) 6px,var(--fill2) 6px,var(--fill2) 12px);border:1px solid var(--line);min-height:80px;display:grid;place-items:center;color:var(--mute);font-size:12px}
.skeleton{height:14px;background:linear-gradient(90deg,var(--fill),var(--fill2),var(--fill));border-radius:3px;margin:8px 0}
.state{padding:24px;text-align:center;color:var(--mute);border:1px dashed var(--line);border-radius:4px;display:grid;gap:10px;justify-items:center}
.state.error{border-color:var(--danger);color:var(--danger)}
.state .illus{width:80px;height:60px}
.spinner{display:inline-block;width:14px;height:14px;border:2px solid #fff;border-top-color:transparent;border-radius:50%;vertical-align:-2px;margin-right:6px}
.modal{max-width:520px;margin:0 auto;border:1px solid var(--ink);border-radius:6px;background:#fff;box-shadow:0 0 0 2000px rgba(0,0,0,.25)}
.modal .body{padding:16px}
[data-state]{display:none}
[data-state="default"]{display:block}
body[data-view="mobile"] .screen{max-width:390px}
body[data-view="mobile"] .m-hide{display:none!important}
body[data-view="mobile"] .m-stack{flex-direction:column;align-items:stretch}
body[data-view="desktop"] .d-hide{display:none!important}
body[data-view="mobile"] .table{display:none}
body[data-view="mobile"] .cards{display:grid!important;gap:8px}
.cards{display:none}
footer.note{padding:16px;font-size:12px;color:var(--mute);text-align:center}
.proto-only{border:1px dashed var(--danger);border-radius:4px;padding:6px;background:#fdf2f2}
.proto-only .tag{display:inline-block;font-size:10px;color:var(--danger);border:1px solid var(--danger);border-radius:3px;padding:0 4px;margin-bottom:4px}
</style>
</head>
<body data-view="desktop">
<header class="toolbar">
<h1>WF — US-002, US-003 · Giỏ hàng (SCR-04)</h1>
<label>Khung nhìn
<select id="view"><option value="desktop">Desktop ≥1024</option><option value="mobile">Mobile &lt;768</option></select>
</label>
<label>Trạng thái
<select id="state">
<option value="default">Mặc định (có dữ liệu)</option>
<option value="loading">Đang tải</option>
<option value="empty-none">Rỗng — Giỏ hàng trống</option>
<option value="error">Lỗi tải dữ liệu</option>
</select>
</label>
<span class="legend">Nhãn đen = C-id/F-id trong SRS §2.A.3.1 · khung đứt xám = vùng Zn (WF §3.1.1) · khung đứt đỏ = chỉ có ở prototype, chưa có trong SRS (xem WF §5) · hành vi xem SRS, không suy từ hình</span>
</header>
<main>
<section class="screen" id="SCR-04">
<h2>SCR-04 · Giỏ hàng <small>US-002 (xem) + US-003 (sửa/xoá) · vào từ SCR-01 (icon giỏ hàng) · vai trò: ROLE-01 Guest, ROLE-02 Customer</small></h2>
<div class="body">
<div class="zone" data-zone="Z1" data-name="Header màn hình">
<span class="label" data-c="C01" data-key="cart.title">Giỏ hàng của bạn (5 sản phẩm)</span>
</div>
<div class="row m-stack" style="align-items:flex-start">
<!-- Z2 — danh sách nhóm seller -->
<div class="zone" data-zone="Z2" data-name="Danh sách nhóm seller" style="flex:2;min-width:0">
<div data-state="default">
<!-- Nhóm seller "Shop A" -->
<div class="card" style="margin-bottom:10px">
<div class="row between" data-c="C02"><b>Shop A</b></div>
<div class="row between" style="margin-top:8px">
<div class="row">
<div class="img" data-c="C03" style="width:64px;height:64px">ảnh</div>
<div>
<div data-c="C04">Áo thun basic</div>
<div class="hint" data-c="C05">Size M, Đỏ</div>
<div class="hint" data-c="C06" data-key="cart.item.price">100.000 ₫</div>
</div>
</div>
<div class="row">
<div class="proto-only d-hide" style="display:inline-block"><span class="tag">chỉ ở prototype</span><br>☐ chọn dòng</div>
<span class="row" data-c="F01">
<span class="btn btn-secondary">−</span><span class="input" style="min-width:40px;text-align:center;padding:6px">2</span><span class="btn btn-secondary">+</span>
</span>
<span class="btn btn-danger" data-c="C07" data-key="btn.delete">Xoá</span>
</div>
</div>
<div class="row between" style="margin-top:8px">
<div class="row">
<div class="img" style="width:64px;height:64px">ảnh</div>
<div>
<div>Quần jean slimfit</div>
<div class="hint">Size 30</div>
<div class="hint">50.000 ₫</div>
</div>
</div>
<div class="row">
<span class="row"><span class="btn btn-secondary">−</span><span class="input" style="min-width:40px;text-align:center;padding:6px">1</span><span class="btn btn-secondary">+</span></span>
<span class="btn btn-danger" data-key="btn.delete">Xoá</span>
</div>
</div>
<div class="row right" style="margin-top:8px;border-top:1px dashed var(--line);padding-top:6px">
<span data-c="C08">Tạm tính: 250.000 ₫</span>
</div>
</div>
<!-- Nhóm seller "Shop B" -->
<div class="card">
<div class="row between"><b>Shop B</b></div>
<div class="row between" style="margin-top:8px">
<div class="row">
<div class="img" style="width:64px;height:64px">ảnh</div>
<div>
<div>Balo du lịch</div>
<div class="hint">Đen</div>
<div class="hint">200.000 ₫</div>
<div class="proto-only" style="margin-top:4px"><span class="tag">chỉ ở prototype — WF §5 #2</span> badge "Sản phẩm đã hết hàng"</div>
</div>
</div>
<div class="row">
<span class="row"><span class="btn btn-secondary" aria-disabled="true">−</span><span class="input" style="min-width:40px;text-align:center;padding:6px">1</span><span class="btn btn-secondary" aria-disabled="true">+</span></span>
<span class="btn btn-danger" data-key="btn.delete">Xoá</span>
</div>
</div>
<div class="row right" style="margin-top:8px;border-top:1px dashed var(--line);padding-top:6px">
<span>Tạm tính: 200.000 ₫</span>
</div>
</div>
</div>
<div data-state="loading"><div class="skeleton"></div><div class="skeleton" style="width:85%"></div><div class="skeleton" style="width:92%"></div></div>
<div data-state="empty-none">
<div class="state">
<div class="img illus">minh hoạ</div>
<span data-key="cart.empty.title">Giỏ hàng trống</span>
<span class="btn btn-primary" data-c="C10" data-key="cart.empty.cta">Tiếp tục mua sắm</span>
</div>
</div>
<div data-state="error">
<div class="state error" data-c="C12">
<span data-key="cart.error.load">Không tải được dữ liệu, thử lại.</span>
<span class="btn btn-secondary" data-c="C13" data-key="btn.retry">Thử lại</span>
</div>
</div>
</div>
<!-- Z3 — tổng kết -->
<div class="zone m-hide" data-zone="Z3" data-name="Tổng kết (sidebar desktop / footer mobile)" style="flex:1;min-width:220px" data-state-visible-in="default">
<div class="proto-only" style="margin-bottom:8px"><span class="tag">chỉ ở prototype — WF §5 #1</span> "Tổng số lượng đã chọn" (phụ thuộc checkbox chọn dòng, chưa có trong SRS)</div>
<div class="label" data-c="C09">Tổng cộng: 585.000 ₫</div>
<div class="btn btn-primary" data-c="C11" data-key="btn.checkout" style="width:100%;text-align:center;margin-top:10px">Tiến hành Checkout</div>
</div>
</div>
<!-- Footer tổng kết cố định đáy — chỉ hiện ở mobile -->
<div class="zone d-hide" data-zone="Z3" data-name="Tổng kết (footer mobile)" style="position:sticky;bottom:0;background:#fff">
<div class="row between">
<span class="label">Tổng cộng: 585.000 ₫</span>
<span class="btn btn-primary" style="text-align:center">Tiến hành Checkout</span>
</div>
</div>
</div>
</section>
</main>
<footer class="note">Wireframe low-fi · bố cục và trạng thái · không phải thiết kế visual · nguồn hành vi: SRS_US002-003_v1.0.md · nguồn bố cục: e-commerce/docs/sections/07-giao-dien.md §7.1.1 (chế độ 🎨) · nguồn quy ước: UICONV_e-commerce_v1.0.md</footer>
<script>
(function(){
var view=document.getElementById('view'),state=document.getElementById('state');
function apply(){
document.body.setAttribute('data-view',view.value);
var s=state.value;
document.querySelectorAll('[data-state]').forEach(function(el){
var on=el.getAttribute('data-state')===s;
el.style.display=on?(el.tagName==='SPAN'?'inline':'block'):'none';
});
}
view.addEventListener('change',apply);state.addEventListener('change',apply);apply();
})();
</script>
</body>
</html>