REPO-005: HoloLake Era V0.1 · 工单-计划-授权-回执闭环
This commit is contained in:
commit
cf0179e6ca
|
|
@ -0,0 +1,10 @@
|
|||
# AGENTS.md · guanghu-research · REPO-005
|
||||
光湖研发主仓库。HoloLake Era V0.1 产品开发。
|
||||
## V0.1 模块
|
||||
- src/pages/HomePage.tsx — 任务首页
|
||||
- src/pages/PlanPage.tsx — 计划详情
|
||||
- src/pages/AuthorizePage.tsx — 授权管理
|
||||
- src/pages/ReceiptPage.tsx — 回执记录
|
||||
- src/pages/SettingsPage.tsx — 系统设置
|
||||
## 数据闭环
|
||||
工单 → 计划 → 授权 → 执行 → 回执
|
||||
|
|
@ -0,0 +1 @@
|
|||
<!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8"/><meta name="viewport" content="width=device-width,initial-scale=1.0"/><title>HoloLake Era · V0.1</title><link rel="stylesheet" href="/src/styles.css"/></head><body><div id="root"></div><script type="module" src="/src/main.tsx"></script></body></html>
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"name": "guanghu-research",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"description": "HoloLake Era · 光湖研发主仓库 · V0.1",
|
||||
"type": "module",
|
||||
"scripts": { "dev": "vite", "build": "tsc && vite build", "preview": "vite preview" },
|
||||
"dependencies": { "react": "^19.0.0", "react-dom": "^19.0.0" },
|
||||
"devDependencies": { "@types/react": "^19.0.0", "@types/react-dom": "^19.0.0", "typescript": "^5.7.0", "vite": "^6.0.0", "@vitejs/plugin-react": "^4.0.0" }
|
||||
}
|
||||
|
|
@ -0,0 +1,39 @@
|
|||
import React, { useState } from "react";
|
||||
import { Page } from "./lib/types";
|
||||
import { HomePage } from "./pages/HomePage";
|
||||
import { PlanPage } from "./pages/PlanPage";
|
||||
import { AuthorizePage } from "./pages/AuthorizePage";
|
||||
import { ReceiptPage } from "./pages/ReceiptPage";
|
||||
import { SettingsPage } from "./pages/SettingsPage";
|
||||
|
||||
const NAV: { id: Page; label: string; icon: string }[] = [
|
||||
{ id: "home", label: "任务首页", icon: "📋" },
|
||||
{ id: "plan", label: "计划详情", icon: "📐" },
|
||||
{ id: "authorize", label: "授权管理", icon: "🔑" },
|
||||
{ id: "receipt", label: "回执记录", icon: "📝" },
|
||||
{ id: "settings", label: "系统设置", icon: "⚙️" },
|
||||
];
|
||||
|
||||
export function App() {
|
||||
const [page, setPage] = useState<Page>("home");
|
||||
return (
|
||||
<div className="app">
|
||||
<nav className="sidebar">
|
||||
<div className="sidebar-logo">🌊 HoloLake Era</div>
|
||||
{NAV.map((item) => (
|
||||
<button key={item.id} className={`nav-item ${page === item.id ? "active" : ""}`} onClick={() => setPage(item.id)}>
|
||||
<span>{item.icon}</span><span>{item.label}</span>
|
||||
</button>
|
||||
))}
|
||||
<div style={{ marginTop: "auto", fontSize: "0.7rem", color: "#999" }}>v0.1 · REPO-005</div>
|
||||
</nav>
|
||||
<main className="main">
|
||||
{page === "home" && <HomePage onNavigate={setPage} />}
|
||||
{page === "plan" && <PlanPage />}
|
||||
{page === "authorize" && <AuthorizePage />}
|
||||
{page === "receipt" && <ReceiptPage />}
|
||||
{page === "settings" && <SettingsPage />}
|
||||
</main>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
import { WorkOrder, Receipt } from "./types";
|
||||
export const sampleWorkOrders: WorkOrder[] = [
|
||||
{ id: "WO-20260722-001", summary: "部署 CV2 EmotionAdapter", target: "在 55867 安装并验证 CV2 情感注入模块", scope: ["cv2_emotion_adapter/"], risk: "low", status: "completed", acceptanceCriteria: ["模块文件可读","注册表状态更新","回执生成"], createdBy: "页页", createdAt: "2026-07-22T17:14+08:00", updatedAt: "2026-07-22T17:15+08:00" },
|
||||
{ id: "WO-20260722-002", summary: "Notion GLS 标准迁移到 yeye", target: "10 份 GLS/AGE 文档导入 yeye vault", scope: ["knowledge/"], risk: "low", status: "completed", acceptanceCriteria: ["10份文档可读","知识库索引完整","迁移回执生成"], createdBy: "页页", createdAt: "2026-07-22T17:28+08:00", updatedAt: "2026-07-22T17:30+08:00" },
|
||||
{ id: "WO-20260722-003", summary: "搭建四域原型", target: "创建可追溯的四域样本", scope: ["hololake-world-minimum/"], risk: "medium", status: "planned", acceptanceCriteria: ["四域独立入口","主域→零感域路径可追溯","回执可验证"], createdBy: "页页", createdAt: "2026-07-22T17:58+08:00", updatedAt: "2026-07-22T18:05+08:00" },
|
||||
];
|
||||
export const sampleReceipts: Receipt[] = [
|
||||
{ id: "RC-20260722-001", workOrderId: "WO-20260722-001", actions: ["创建模块描述文件","更新注册表状态","生成HLDP回执"], executor: "小线 · PER-YE-LN-001", evidence: ["receipts/2026-07-22-cv2-install.yml"], result: "success", limitations: ["需服务器验证"], completedAt: "2026-07-22T17:15+08:00" },
|
||||
{ id: "RC-20260722-002", workOrderId: "WO-20260722-002", actions: ["读取Notion GLS文档","创建knowledge/10份文档","生成迁移回执"], executor: "小线 · PER-YE-LN-001", evidence: ["receipts/2026-07-22-knowledge-migration.yml"], result: "success", limitations: ["Awen摆渡车线待补"], completedAt: "2026-07-22T17:30+08:00" },
|
||||
];
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
export interface WorkOrder { id: string; summary: string; target: string; scope: string[]; risk: "low"|"medium"|"high"|"critical"; status: "draft"|"planned"|"authorized"|"executing"|"completed"|"closed"; acceptanceCriteria: string[]; createdBy: string; createdAt: string; updatedAt: string; }
|
||||
export interface Receipt { id: string; workOrderId: string; actions: string[]; executor: string; evidence: string[]; result: "success"|"partial"|"failed"; limitations: string[]; completedAt: string; }
|
||||
export type Page = "home"|"plan"|"authorize"|"receipt"|"settings";
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
import React from "react"; import { createRoot } from "react-dom/client"; import { App } from "./App";
|
||||
createRoot(document.getElementById("root")!).render(<App />);
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
import React, { useState } from "react";
|
||||
export function AuthorizePage() {
|
||||
const [approved, setApproved] = useState<string[]>([]);
|
||||
const items = [{ id: "WO-20260722-003", summary: "搭建四域原型", scope: ["hololake-world-minimum/","home.md"], risk: "中", expires: "2026-07-23T23:59+08:00" }];
|
||||
return <div><h1 className="page-title">🔑 授权管理</h1>
|
||||
<div className="stats"><div className="stat-card"><div className="stat-value">{items.length-approved.length}</div><div className="stat-label">待审批</div></div><div className="stat-card"><div className="stat-value">{approved.length}</div><div className="stat-label">已批准</div></div><div className="stat-card"><div className="stat-value">0</div><div className="stat-label">已拒绝</div></div><div className="stat-card"><div className="stat-value">0</div><div className="stat-label">已过期</div></div></div>
|
||||
<h2 style={{fontSize:"1.1rem",marginBottom:"1rem"}}>待审批</h2>
|
||||
{items.map(a=><div className="card" key={a.id}><div className="card-header"><div><div className="card-title">{a.summary}</div><div className="card-subtitle">{a.id}</div></div><span className={`badge ${approved.includes(a.id)?"badge-success":"badge-warning"}`}>{approved.includes(a.id)?"已批准 ✅":"待审批"}</span></div><table><tbody><tr><th style={{width:"100px"}}>授权范围</th><td>{a.scope.join(" · ")}</td></tr><tr><th>风险</th><td><span className="badge badge-warning">{a.risk}</span></td></tr><tr><th>过期</th><td>{a.expires}</td></tr><tr><th>次数</th><td>单次</td></tr><tr><th>回滚</th><td>git revert</td></tr></tbody></table>{!approved.includes(a.id)?<div style={{display:"flex",gap:"0.5rem",marginTop:"1rem"}}><button className="btn btn-success" onClick={()=>setApproved([...approved,a.id])}>✅ 批准 — 单次执行</button><button className="btn btn-danger">❌ 拒绝</button></div>:<div style={{marginTop:"1rem",fontSize:"0.9rem",color:"var(--su)"}}>✅ 已批准 · 有效期至 {a.expires}</div>}</div>)}
|
||||
</div>;
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
import React from "react"; import { Page } from "../lib/types"; import { sampleWorkOrders } from "../lib/sample-data";
|
||||
export function HomePage({ onNavigate }: { onNavigate: (p: Page) => void }) {
|
||||
const t = sampleWorkOrders.length, c = sampleWorkOrders.filter(w => w.status === "completed").length, a = sampleWorkOrders.filter(w => w.status === "planned").length;
|
||||
return <div>
|
||||
<h1 className="page-title">📋 任务首页</h1>
|
||||
<div className="stats"><div className="stat-card"><div className="stat-value">{t}</div><div className="stat-label">全部工单</div></div><div className="stat-card"><div className="stat-value">{a}</div><div className="stat-label">进行中</div></div><div className="stat-card"><div className="stat-value">{c}</div><div className="stat-label">已完成</div></div><div className="stat-card"><div className="stat-value">—</div><div className="stat-label">待处理</div></div></div>
|
||||
<h2 style={{fontSize:"1.1rem",marginBottom:"1rem"}}>最近工单</h2>
|
||||
{sampleWorkOrders.map(wo => <div className="card" key={wo.id}><div className="card-header"><div><div className="card-title">{wo.summary}</div><div className="card-subtitle">{wo.id} · {wo.createdBy}</div></div><span className={`badge ${wo.status==="completed"?"badge-success":wo.status==="planned"?"badge-info":"badge-draft"}`}>{wo.status==="completed"?"已完成":wo.status==="planned"?"计划中":wo.status}</span></div><div style={{fontSize:"0.85rem",color:"#666",marginBottom:"0.75rem"}}>🎯 {wo.target}</div><div style={{display:"flex",gap:"0.5rem"}}><button className="btn" onClick={()=>onNavigate("plan")}>查看计划 →</button>{wo.status!=="completed"&&<button className="btn btn-primary" onClick={()=>onNavigate("authorize")}>授权执行 →</button>}<button className="btn" onClick={()=>onNavigate("receipt")}>查看回执 →</button></div></div>)}
|
||||
</div>;
|
||||
}
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
import React from "react";
|
||||
export function PlanPage() { return <div><h1 className="page-title">📐 计划详情</h1><div className="card"><div className="card-header"><div className="card-title">WO-20260722-003 · 搭建四域原型</div><span className="badge badge-info">计划中</span></div><table><tbody><tr><th style={{width:"120px"}}>目标</th><td>按企业四域重建创建可追溯的四域样本</td></tr><tr><th>范围</th><td>hololake-world-minimum/</td></tr><tr><th>风险评估</th><td><span className="badge badge-warning">中</span> · 需确认入口设计</td></tr><tr><th>依赖项</th><td>REPO-004 Tolaria 源码可构建</td></tr></tbody></table><h3 style={{marginTop:"1rem",fontSize:"0.95rem"}}>验收条件</h3><ul style={{paddingLeft:"1.5rem",fontSize:"0.9rem"}}><li>四域都具备独立入口</li><li>主域→分域→零域→零感域 路径可追溯</li><li>回执可验证</li></ul><h3 style={{marginTop:"1rem",fontSize:"0.95rem"}}>待授权项</h3><div className="card" style={{background:"#fef3c7",border:"1px solid #fbbf24"}}><strong>⚠️ 需要授权</strong><ul style={{paddingLeft:"1.5rem",fontSize:"0.85rem",marginTop:"0.5rem"}}><li>创建 hololake-world-minimum/ 目录结构</li><li>写入四域入口页面</li><li>更新 home.md 导航链接</li></ul><button className="btn btn-primary" style={{marginTop:"0.75rem"}}>前往授权 →</button></div></div></div>; }
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
import React from "react"; import { sampleReceipts } from "../lib/sample-data";
|
||||
export function ReceiptPage() { return <div><h1 className="page-title">📝 回执记录</h1>{sampleReceipts.map(rc=><div className="card" key={rc.id}><div className="card-header"><div><div className="card-title">{rc.id}</div><div className="card-subtitle">关联 {rc.workOrderId} · {rc.executor}</div></div><span className={`badge ${rc.result==="success"?"badge-success":"badge-warning"}`}>{rc.result==="success"?"✅ 成功":"⚠️ 部分"}</span></div><table><tbody><tr><th style={{width:"100px"}}>执行动作</th><td><ul style={{paddingLeft:"1.2rem",margin:0}}>{rc.actions.map((a,i)=><li key={i}>{a}</li>)}</ul></td></tr><tr><th>证据</th><td><ul style={{paddingLeft:"1.2rem",margin:0}}>{rc.evidence.map((e,i)=><li key={i} style={{fontSize:"0.85rem"}}>{e}</li>)}</ul></td></tr><tr><th>限制</th><td>{rc.limitations.length>0?rc.limitations.map((l,i)=><span key={i} className="badge badge-warning" style={{marginRight:"0.25rem"}}>{l}</span>):"无"}</td></tr><tr><th>完成</th><td>{rc.completedAt}</td></tr></tbody></table></div>)}</div>; }
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
import React, { useState } from "react";
|
||||
export function SettingsPage() {
|
||||
const [ep, setEp] = useState("http://localhost:11434");
|
||||
return <div><h1 className="page-title">⚙️ 系统设置</h1>
|
||||
<div className="card"><h3 style={{marginBottom:"1rem"}}>🔗 仓库连接</h3>
|
||||
<div className="form-group"><label className="form-label">事实源仓库</label><input className="form-input" value="https://guanghulab.com/fifth-domain/bingshuo/yeye" readOnly/><div className="form-help">所有工单和回执存于此仓库</div></div>
|
||||
<div className="form-group"><label className="form-label">连接状态</label><div style={{display:"flex",alignItems:"center",gap:"0.5rem",marginTop:"0.5rem"}}><span style={{width:8,height:8,borderRadius:"50%",background:"var(--su)",display:"inline-block"}}/><span style={{fontSize:"0.9rem"}}>已连接 · main · 最后同步 2026-07-22</span></div></div>
|
||||
</div>
|
||||
<div className="card"><h3 style={{marginBottom:"1rem"}}>🤖 本地模型</h3>
|
||||
<div className="form-group"><label className="form-label">服务端点</label><input className="form-input" value={ep} onChange={e=>setEp(e.target.value)}/><div className="form-help">默认连接本地 Ollama</div></div>
|
||||
</div>
|
||||
<div className="card"><h3 style={{marginBottom:"1rem"}}>📊 系统信息</h3><table><tbody><tr><th style={{width:"150px"}}>版本</th><td>HoloLake Era v0.1</td></tr><tr><th>仓库</th><td>REPO-005 · guanghu-research</td></tr><tr><th>Bundle</th><td>com.guanghu.desktop</td></tr><tr><th>平台</th><td>macOS Apple Silicon</td></tr><tr><th>频道</th><td>CH-YEYE-001</td></tr><tr><th>管家</th><td>小线 · PER-YE-LN-001 · 在线</td></tr></tbody></table></div>
|
||||
<div className="card"><h3 style={{marginBottom:"1rem"}}>🔒 隐私</h3><ul style={{paddingLeft:"1.2rem",fontSize:"0.9rem",color:"var(--t2)"}}><li>数据存储在本地 Git 仓库</li><li>不上传对话、文件或凭证</li><li>不保存 token 或密码</li></ul></div>
|
||||
</div>;
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
:root { --bg: #fff; --bg2: #f5f5f5; --text: #1a1a1a; --t2: #666; --bd: #e0e0e0; --ac: #2563eb; --ah: #1d4ed8; --su: #16a34a; --wa: #d97706; --da: #dc2626; --r: 8px; }
|
||||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||||
body { font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",system-ui,sans-serif; background: var(--bg); color: var(--text); line-height: 1.6; }
|
||||
.app { display: flex; min-height: 100vh; }
|
||||
.sidebar { width: 200px; background: var(--bg2); border-right: 1px solid var(--bd); padding: 1.5rem 0.75rem; display: flex; flex-direction: column; gap: 0.25rem; }
|
||||
.sidebar-logo { font-size: 1rem; font-weight: 700; margin-bottom: 1rem; color: var(--ac); padding: 0 0.5rem; }
|
||||
.nav-item { display: flex; align-items: center; gap: 0.5rem; padding: 0.4rem 0.6rem; border-radius: var(--r); cursor: pointer; font-size: 0.85rem; color: var(--t2); transition: all 0.15s; border: none; background: none; width: 100%; text-align: left; }
|
||||
.nav-item:hover { background: #e8e8e8; }
|
||||
.nav-item.active { background: var(--ac); color: white; }
|
||||
.main { flex: 1; padding: 2rem; max-width: 860px; }
|
||||
.page-title { font-size: 1.4rem; font-weight: 700; margin-bottom: 1.5rem; }
|
||||
.card { background: var(--bg); border: 1px solid var(--bd); border-radius: var(--r); padding: 1.1rem; margin-bottom: 0.75rem; }
|
||||
.card-header { display: flex; justify-content: space-between; align-items: flex-start; margin-bottom: 0.5rem; }
|
||||
.card-title { font-weight: 600; font-size: 0.95rem; }
|
||||
.card-subtitle { font-size: 0.8rem; color: var(--t2); }
|
||||
.badge { display: inline-block; padding: 0.12rem 0.45rem; border-radius: 12px; font-size: 0.72rem; font-weight: 600; }
|
||||
.badge-success { background: #dcfce7; color: var(--su); }
|
||||
.badge-warning { background: #fef3c7; color: var(--wa); }
|
||||
.badge-info { background: #dbeafe; color: var(--ac); }
|
||||
.badge-draft { background: #f3f4f6; color: #9ca3af; }
|
||||
table { width: 100%; border-collapse: collapse; margin: 0.75rem 0; }
|
||||
th, td { padding: 0.5rem 0.75rem; text-align: left; border-bottom: 1px solid var(--bd); font-size: 0.85rem; }
|
||||
th { font-weight: 600; color: var(--t2); }
|
||||
.btn { padding: 0.4rem 0.8rem; border-radius: var(--r); border: 1px solid var(--bd); background: var(--bg); cursor: pointer; font-size: 0.8rem; transition: all 0.15s; }
|
||||
.btn-primary { background: var(--ac); color: white; border-color: var(--ac); }
|
||||
.btn-primary:hover { background: var(--ah); }
|
||||
.btn-success { background: var(--su); color: white; border-color: var(--su); }
|
||||
.btn-danger { background: var(--da); color: white; border-color: var(--da); }
|
||||
.stats { display: grid; grid-template-columns: repeat(4,1fr); gap: 0.75rem; margin-bottom: 1.5rem; }
|
||||
.stat-card { background: var(--bg2); border: 1px solid var(--bd); border-radius: var(--r); padding: 0.75rem; text-align: center; }
|
||||
.stat-value { font-size: 1.3rem; font-weight: 700; }
|
||||
.stat-label { font-size: 0.75rem; color: var(--t2); margin-top: 0.2rem; }
|
||||
.form-group { margin-bottom: 0.75rem; }
|
||||
.form-label { display: block; font-weight: 600; margin-bottom: 0.2rem; font-size: 0.85rem; }
|
||||
.form-input { width: 100%; padding: 0.4rem; border: 1px solid var(--bd); border-radius: var(--r); font-size: 0.85rem; }
|
||||
.form-help { font-size: 0.75rem; color: var(--t2); margin-top: 0.2rem; }
|
||||
|
|
@ -0,0 +1 @@
|
|||
{ "compilerOptions": { "target": "ES2020", "module": "ESNext", "moduleResolution": "bundler", "jsx": "react-jsx", "strict": true, "esModuleInterop": true, "skipLibCheck": true }, "include": ["src"] }
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
import { defineConfig } from "vite"; import react from "@vitejs/plugin-react";
|
||||
export default defineConfig({ plugins: [react()], server: { port: 5173, open: true } });
|
||||
Loading…
Reference in New Issue