router.js 2.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162
  1. /* router.js —— hash 路由。
  2. * 约定:#/path?k=v&k2=v2&focus=objectId
  3. * #/overview 工作台(我的 / 池子 / 现场看板)
  4. * #/pool?priority=S&sort=score 机会卡片池(?opp=<id> 打开卡片抽屉)
  5. * #/artifacts 成果
  6. * #/insights?ins=id 归纳(可选打开某条)
  7. * #/projects #/project/<id> 项目
  8. * #/members #/member/<name> 成员
  9. * #/sources 原材料层
  10. * #/record 记录层
  11. * #/stages #/stage/<S1..S5> 阶段进度
  12. * hash 每次变化都完整重建 URL,保证"复制链接 → 新标签打开 → 状态一致"。 */
  13. (function () {
  14. "use strict";
  15. const knownParams = ["q", "priority", "stage", "category", "evidence", "layer", "member", "ins", "art", "src", "ev", "back", "ch", "sort", "focus", "type", "status", "opp"];
  16. function parseHash(hash) {
  17. const h = (hash || window.location.hash || "").replace(/^#\/?/, "");
  18. const [pathPart, qs] = h.split("?");
  19. const segs = pathPart.split("/").filter(Boolean).map(decodeURIComponent);
  20. const params = {};
  21. new URLSearchParams(qs || "").forEach((v, k) => { params[k] = v; });
  22. return { route: segs[0] || "overview", arg: segs[1] || null, params };
  23. }
  24. /* 由当前 route + 参数生成 hash,参数按固定顺序排,空值不写入 */
  25. function buildHash(route, arg, params) {
  26. const p = new URLSearchParams();
  27. for (const k of knownParams) {
  28. const v = params[k];
  29. if (v != null && v !== "") p.set(k, v);
  30. }
  31. const s = p.toString();
  32. let path = route;
  33. if ((route === "project" || route === "member" || route === "stage" || route === "report") && arg) {
  34. path += "/" + encodeURIComponent(arg);
  35. }
  36. return "#/" + path + (s ? "?" + s : "");
  37. }
  38. /* 用 replaceState 更新 hash,不触发页面滚动与额外 hashchange。
  39. * params 是"完整的参数表":调用方先展开当前参数再覆盖,这样切筛选不会把抽屉参数带跑。 */
  40. function write(params, opts) {
  41. const cur = parseHash();
  42. const hash = buildHash(cur.route, cur.arg, params);
  43. if (hash === window.location.hash) return;
  44. try {
  45. history.replaceState(null, "", hash);
  46. } catch (e) {
  47. window.location.hash = hash; /* file:// 等环境对 replaceState 有限制时的退路 */
  48. }
  49. if (!opts || !opts.silent) document.dispatchEvent(new CustomEvent("route:changed"));
  50. }
  51. function go(hash) {
  52. if (hash === window.location.hash) return;
  53. window.location.hash = hash;
  54. }
  55. window.KF_ROUTER = { parseHash, buildHash, write, go };
  56. })();