From b10093f9d8a949c6a6f8f8ed6d5b9c1bb044b569 Mon Sep 17 00:00:00 2001 From: NathanAlex7 <18898599977@163.com> Date: Mon, 21 Sep 2026 13:33:00 +0800 Subject: [PATCH] =?UTF-8?q?=E7=AE=80=E5=8C=96=E4=B8=9A=E5=8A=A1=E5=8A=A9?= =?UTF-8?q?=E6=89=8B=E4=B8=BA=E5=8D=95=E4=B8=80=E5=85=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/test_operations_center.py | 6 ++++-- scripts/test_cloud_assistant_access.cjs | 12 ++++++------ src/components/xingbang/BusinessAssistant.tsx | 17 ++++++----------- src/components/xingbang/business-assistant.css | 4 +--- 4 files changed, 17 insertions(+), 22 deletions(-) diff --git a/backend/test_operations_center.py b/backend/test_operations_center.py index bdec91c..1b532f6 100644 --- a/backend/test_operations_center.py +++ b/backend/test_operations_center.py @@ -172,8 +172,10 @@ class OperationsCenterTests(unittest.TestCase): self.assertNotIn("

{item.detail}

", workspace) self.assertNotIn('"方案比较"', workspace) self.assertNotIn('tab === "scenario"', workspace) - self.assertIn("mini-openclaw", assistant) - self.assertIn("", assistant) + self.assertNotIn("mini-openclaw", assistant) + self.assertNotIn("", assistant) + self.assertIn('className="ba-workspace"', assistant) + self.assertIn("当前身份只会调用本项目授权的 Skills、规则和业务工具", assistant) self.assertIn("IdentitySelector", assistant) self.assertIn("IdentitySelector", dashboard) self.assertIn("统计范围", dashboard) diff --git a/scripts/test_cloud_assistant_access.cjs b/scripts/test_cloud_assistant_access.cjs index e1d00d4..f2065a8 100644 --- a/scripts/test_cloud_assistant_access.cjs +++ b/scripts/test_cloud_assistant_access.cjs @@ -11,13 +11,13 @@ const coreProxy = read("src/app/api/core/[...path]/route.ts"); assert.match( assistant, - /setNativeAccess\(body\.identity\?\.role==="admin"&&!!sessionStorage\.getItem\("xingbang-access-token"\)\)/, - "只有带正式令牌的管理员才能进入原版通用工具工作台", + /
/, + "业务助手应保留单一、直接的项目操作入口", ); -assert.match( +assert.doesNotMatch( assistant, - /nativeAccess\?<\/AppProvider>:conversation/, - "客户经理和评审员应回落到受角色约束的项目会话,而不是请求管理员接口", + /mini-openclaw|ChatPanel|AppProvider|nativeAccess|ba-mode-tabs/, + "业务人员页面不应继续展示或加载重复的 mini-openclaw 入口", ); assert.match( assistant, @@ -35,4 +35,4 @@ assert.match( "修复不得放开原版终端、文件和网页工具的管理员边界", ); -console.log("Cloud mini-openclaw role access checks passed"); +console.log("Cloud business-assistant single-entry checks passed"); diff --git a/src/components/xingbang/BusinessAssistant.tsx b/src/components/xingbang/BusinessAssistant.tsx index 395a0ca..35af834 100644 --- a/src/components/xingbang/BusinessAssistant.tsx +++ b/src/components/xingbang/BusinessAssistant.tsx @@ -1,11 +1,9 @@ "use client"; import {ChangeEvent,FormEvent,useEffect,useMemo,useState} from "react"; -import {ArrowUpRight,Bot,FilePlus2,MessagesSquare,Paperclip,Send,Sparkles,UserRound} from "lucide-react"; +import {ArrowUpRight,Bot,FilePlus2,Paperclip,Send,Sparkles,UserRound} from "lucide-react"; import AgentNav from "./AgentNav"; import IdentitySelector,{ROLE_LABEL} from "./IdentitySelector"; -import ChatPanel from "@/components/chat/ChatPanel"; -import {AppProvider} from "@/lib/store"; import {newRequestId} from "@/lib/request-id"; import "./business-assistant.css"; @@ -20,15 +18,13 @@ function headers(json=true){ export default function BusinessAssistant(){ const [projects,setProjects]=useState([]),[identity,setIdentity]=useState(null),[projectId,setProjectId]=useState(""); const [input,setInput]=useState(""),[busy,setBusy]=useState(false),[error,setError]=useState(""); - const [mode,setMode]=useState<"business"|"openclaw">("business"); - const [nativeAccess,setNativeAccess]=useState(false); const [messages,setMessages]=useState([{id:"hello",side:"assistant",text:"你好,我是兴邦对公业务助手。选择项目后,你可以直接告诉我查状态、发起AI预审、生成报告或补充材料。"}]); const role=identity?.role||"manager",project=projects.find(p=>p.id===projectId); const quick=useMemo(()=>role==="reviewer"?["查看项目状态","发起独立评审","生成评审报告"]:role==="admin"?["查看项目状态"]:["查看项目状态","发起AI预审","生成预审报告","提交正式评审"],[role]); async function load(){ setError(""); - try{const response=await fetch(`${ROOT}/projects`,{headers:headers()});const body=await response.json();if(!response.ok)throw Error(body.detail||"身份或项目读取失败");setProjects(body.projects);setIdentity(body.identity);setNativeAccess(body.identity?.role==="admin"&&!!sessionStorage.getItem("xingbang-access-token"));const requested=new URLSearchParams(location.search).get("project");setProjectId(current=>current||(requested&&body.projects.some((p:any)=>p.id===requested)?requested:body.projects[0]?.id||""));} + try{const response=await fetch(`${ROOT}/projects`,{headers:headers()});const body=await response.json();if(!response.ok)throw Error(body.detail||"身份或项目读取失败");setProjects(body.projects);setIdentity(body.identity);const requested=new URLSearchParams(location.search).get("project");setProjectId(current=>current||(requested&&body.projects.some((p:any)=>p.id===requested)?requested:body.projects[0]?.id||""));} catch(e:any){setError(e.message)} } useEffect(()=>{load()},[]); @@ -48,17 +44,16 @@ export default function BusinessAssistant(){ finally{setBusy(false)} } function submit(event:FormEvent){event.preventDefault();send(input)} - const conversation=
{messages.map(message=>
{message.side==="assistant"&&}

{message.text}

{message.side==="user"&&}
{message.objectId&&操作凭证 · {message.objectId}}{message.url&&查看项目中的结果}
)}{busy&&

正在通过 mini-openclaw 执行并记录…

}
+ const conversation=
{messages.map(message=>
{message.side==="assistant"&&}

{message.text}

{message.side==="user"&&}
{message.objectId&&操作凭证 · {message.objectId}}{message.url&&查看项目中的结果}
)}{busy&&

正在执行并生成可追溯记录…

}
{error&&
{error}
}
setInput(e.target.value)} placeholder={`例如:帮我做一次 ${projectId||"当前项目"} 风险研判`}/>
当前身份只会调用本项目授权的 Skills、规则和业务工具,并保留操作凭证。
; return
-

MINI-OPENCLAW BUSINESS ENTRY

一句话,推进对公业务

对话和页面按钮共用同一套 Skills、规则、权限与留痕。
- +

XINGBANG BUSINESS ASSISTANT

一句话,推进对公业务

对话和页面按钮共用同一套 Skills、规则、权限与留痕。
正在处理
{project&&<>
当前阶段{project.stage}
下一步{project.next_action}
进入项目}
- {mode==="business"?
+
{conversation} -
:
{nativeAccess?"mini-openclaw 原生管理工作台":`${ROLE_LABEL[role]} mini-openclaw 工作台`}{nativeAccess?"管理员已认证,可使用原版通用工具":`已绑定 ${projectId};按当前身份调用受限 Skills、规则和项目工具`}
查看项目事实
{nativeAccess?:conversation}
} +
; } diff --git a/src/components/xingbang/business-assistant.css b/src/components/xingbang/business-assistant.css index e5334b4..5c7da36 100644 --- a/src/components/xingbang/business-assistant.css +++ b/src/components/xingbang/business-assistant.css @@ -1,4 +1,4 @@ -.ba-shell{min-height:100vh;background:#f1f5f2;color:#173127;font-family:Arial,"Microsoft YaHei",sans-serif}.ba-shell *{box-sizing:border-box}.ba-main{max-width:1420px;margin:auto;padding:44px 38px 70px}.ba-heading{display:flex;justify-content:space-between;align-items:flex-end;gap:24px}.ba-heading>div>p{font-size:10px;letter-spacing:.16em;color:#378265;margin-bottom:10px}.ba-heading h1{font:500 36px/1.2 Georgia,"Microsoft YaHei",serif}.ba-heading>div>span{display:block;color:#74857d;font-size:12px;margin-top:12px}.ba-mode-tabs{display:flex;gap:5px;margin-top:28px}.ba-mode-tabs button{display:flex;align-items:center;gap:7px;border:0;background:transparent;color:#6f8178;padding:10px 13px;border-radius:9px;font-size:11px}.ba-mode-tabs button.active{background:#dcece3;color:#17633f;font-weight:700}.ba-context{display:grid;grid-template-columns:minmax(280px,1.4fr) .7fr 1fr auto;align-items:center;gap:18px;background:#0e2a1e;color:white;margin:10px 0 16px;padding:18px 22px;border-radius:15px;box-shadow:0 15px 42px #0d2d201c}.ba-context>div{min-width:0;border-right:1px solid #ffffff17}.ba-context span{display:block;font-size:9px;color:#7ba18e;margin-bottom:7px}.ba-context select{width:90%;border:0;background:transparent;color:#eaf7f0;font-size:13px;font-weight:700}.ba-context select option{color:#1a2a22}.ba-context b{display:block;font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ba-context>a{display:flex;align-items:center;gap:6px;color:#73dfa9;font-size:11px}.ba-workspace{display:grid;grid-template-columns:290px minmax(0,1fr);min-height:610px;border:1px solid #dfe8e2;border-radius:18px;overflow:hidden;background:#fff;box-shadow:0 30px 80px #274d3a0e}.ba-workspace>aside{padding:30px 24px;background:linear-gradient(160deg,#f7fbf8,#edf5f0);border-right:1px solid #e1eae4}.ba-workspace>aside>svg{color:#2c9c6a}.ba-workspace aside h2{font-size:17px;margin:18px 0 10px}.ba-workspace aside p{color:#718179;font-size:11px;line-height:1.8;margin-bottom:24px}.ba-workspace aside button{width:100%;display:flex;justify-content:space-between;align-items:center;border:1px solid #dbe7df;background:#fff;color:#345a48;padding:12px 13px;border-radius:9px;margin-bottom:9px;font-size:11px;cursor:pointer}.ba-workspace aside button:hover{border-color:#62ad89;color:#17633f}.ba-workspace aside small{display:flex;align-items:flex-start;gap:7px;color:#8a9991;font-size:9px;line-height:1.7;margin-top:28px}.ba-chat{display:flex;flex-direction:column;min-width:0}.ba-messages{flex:1;padding:28px 34px;overflow:auto;max-height:560px}.ba-messages article{max-width:75%;margin-bottom:18px}.ba-messages article>div{display:flex;align-items:flex-start;gap:9px}.ba-messages article p{padding:13px 15px;border-radius:12px;background:#edf4f0;color:#385347;font-size:12px;line-height:1.8}.ba-messages article.user{margin-left:auto}.ba-messages article.user p{background:#17633f;color:#fff}.ba-messages article svg{color:#369367;margin-top:13px;flex-shrink:0}.ba-messages article small{display:block;color:#9aa6a0;font-size:9px;margin:7px 0 0 25px}.ba-messages article a{display:inline-flex;align-items:center;gap:4px;color:#24724f;font-size:10px;margin:6px 0 0 25px}.ba-messages .pending{opacity:.65}.ba-error{margin:0 28px 10px;color:#9c463b;background:#fff0ed;padding:10px 13px;border-radius:8px;font-size:11px}.ba-chat form{display:flex;align-items:center;gap:9px;margin:0 28px;padding:8px 9px;border:1px solid #d8e3dc;border-radius:13px;background:#fff;box-shadow:0 8px 25px #244a370d}.ba-chat form>input{flex:1;border:0;outline:0;padding:10px;font-size:12px}.ba-chat form>button,.ba-attach{width:38px;height:38px;display:grid;place-items:center;border:0;border-radius:9px;cursor:pointer}.ba-chat form>button{background:#17633f;color:#fff}.ba-chat form>button:disabled{opacity:.4}.ba-attach{color:#668078;background:#eff4f1}.ba-attach input{display:none}.ba-chat footer{display:flex;align-items:center;gap:6px;color:#93a199;font-size:9px;padding:10px 38px 20px}.ba-native{height:610px;display:grid;grid-template-rows:auto 1fr;overflow:hidden;border:1px solid #dfe8e2;border-radius:18px;background:#fff;box-shadow:0 30px 80px #274d3a0e}.ba-native>header{display:flex;justify-content:space-between;align-items:center;padding:14px 20px;border-bottom:1px solid #e1e9e4;background:#f6faf7}.ba-native>header>div,.ba-native>header>a{display:flex;align-items:center;gap:9px}.ba-native>header b{font-size:12px}.ba-native>header span{display:block;color:#819087;font-size:9px;margin-top:4px}.ba-native>header>a{color:#21704d;font-size:10px}@media(max-width:820px){.ba-main{padding:26px 14px 50px}.ba-heading{align-items:flex-start;flex-direction:column}.ba-heading h1{font-size:29px}.ba-context{grid-template-columns:1fr 1fr}.ba-workspace{grid-template-columns:1fr}.ba-workspace>aside{border-right:0;border-bottom:1px solid #e1eae4}.ba-messages{padding:22px 18px}.ba-messages article{max-width:90%}.ba-chat form{margin:0 14px}.ba-chat footer{padding-left:22px}} +.ba-shell{min-height:100vh;background:#f1f5f2;color:#173127;font-family:Arial,"Microsoft YaHei",sans-serif}.ba-shell *{box-sizing:border-box}.ba-main{max-width:1420px;margin:auto;padding:44px 38px 70px}.ba-heading{display:flex;justify-content:space-between;align-items:flex-end;gap:24px}.ba-heading>div>p{font-size:10px;letter-spacing:.16em;color:#378265;margin-bottom:10px}.ba-heading h1{font:500 36px/1.2 Georgia,"Microsoft YaHei",serif}.ba-heading>div>span{display:block;color:#74857d;font-size:12px;margin-top:12px}.ba-context{display:grid;grid-template-columns:minmax(280px,1.4fr) .7fr 1fr auto;align-items:center;gap:18px;background:#0e2a1e;color:white;margin:28px 0 16px;padding:18px 22px;border-radius:15px;box-shadow:0 15px 42px #0d2d201c}.ba-context>div{min-width:0;border-right:1px solid #ffffff17}.ba-context span{display:block;font-size:9px;color:#7ba18e;margin-bottom:7px}.ba-context select{width:90%;border:0;background:transparent;color:#eaf7f0;font-size:13px;font-weight:700}.ba-context select option{color:#1a2a22}.ba-context b{display:block;font-size:12px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.ba-context>a{display:flex;align-items:center;gap:6px;color:#73dfa9;font-size:11px}.ba-workspace{display:grid;grid-template-columns:290px minmax(0,1fr);min-height:610px;border:1px solid #dfe8e2;border-radius:18px;overflow:hidden;background:#fff;box-shadow:0 30px 80px #274d3a0e}.ba-workspace>aside{padding:30px 24px;background:linear-gradient(160deg,#f7fbf8,#edf5f0);border-right:1px solid #e1eae4}.ba-workspace>aside>svg{color:#2c9c6a}.ba-workspace aside h2{font-size:17px;margin:18px 0 10px}.ba-workspace aside p{color:#718179;font-size:11px;line-height:1.8;margin-bottom:24px}.ba-workspace aside button{width:100%;display:flex;justify-content:space-between;align-items:center;border:1px solid #dbe7df;background:#fff;color:#345a48;padding:12px 13px;border-radius:9px;margin-bottom:9px;font-size:11px;cursor:pointer}.ba-workspace aside button:hover{border-color:#62ad89;color:#17633f}.ba-workspace aside small{display:flex;align-items:flex-start;gap:7px;color:#8a9991;font-size:9px;line-height:1.7;margin-top:28px}.ba-chat{display:flex;flex-direction:column;min-width:0}.ba-messages{flex:1;padding:28px 34px;overflow:auto;max-height:560px}.ba-messages article{max-width:75%;margin-bottom:18px}.ba-messages article>div{display:flex;align-items:flex-start;gap:9px}.ba-messages article p{padding:13px 15px;border-radius:12px;background:#edf4f0;color:#385347;font-size:12px;line-height:1.8}.ba-messages article.user{margin-left:auto}.ba-messages article.user p{background:#17633f;color:#fff}.ba-messages article svg{color:#369367;margin-top:13px;flex-shrink:0}.ba-messages article small{display:block;color:#9aa6a0;font-size:9px;margin:7px 0 0 25px}.ba-messages article a{display:inline-flex;align-items:center;gap:4px;color:#24724f;font-size:10px;margin:6px 0 0 25px}.ba-messages .pending{opacity:.65}.ba-error{margin:0 28px 10px;color:#9c463b;background:#fff0ed;padding:10px 13px;border-radius:8px;font-size:11px}.ba-chat form{display:flex;align-items:center;gap:9px;margin:0 28px;padding:8px 9px;border:1px solid #d8e3dc;border-radius:13px;background:#fff;box-shadow:0 8px 25px #244a370d}.ba-chat form>input{flex:1;border:0;outline:0;padding:10px;font-size:12px}.ba-chat form>button,.ba-attach{width:38px;height:38px;display:grid;place-items:center;border:0;border-radius:9px;cursor:pointer}.ba-chat form>button{background:#17633f;color:#fff}.ba-chat form>button:disabled{opacity:.4}.ba-attach{color:#668078;background:#eff4f1}.ba-attach input{display:none}.ba-chat footer{display:flex;align-items:center;gap:6px;color:#93a199;font-size:9px;padding:10px 38px 20px}@media(max-width:820px){.ba-main{padding:26px 14px 50px}.ba-heading{align-items:flex-start;flex-direction:column}.ba-heading h1{font-size:29px}.ba-context{grid-template-columns:1fr 1fr}.ba-workspace{grid-template-columns:1fr}.ba-workspace>aside{border-right:0;border-bottom:1px solid #e1eae4}.ba-messages{padding:22px 18px}.ba-messages article{max-width:90%}.ba-chat form{margin:0 14px}.ba-chat footer{padding-left:22px}} /* Larger business copy and clearer assistant actions. */ .ba-shell{font-family:"Microsoft YaHei","PingFang SC",sans-serif;font-size:15px} @@ -10,8 +10,6 @@ .ba-workspace aside button:hover{background:#edf7f1;border-color:#3e8f68;box-shadow:0 4px 14px #244a3712} .ba-workspace aside small{font-size:12px}.ba-messages article p{font-size:15px}.ba-messages article small{font-size:12px}.ba-messages article a{font-size:13px;font-weight:700} .ba-error{font-size:14px}.ba-chat form{border-color:#bdd2c6;box-shadow:0 8px 28px #244a3715}.ba-chat form>input{font-size:15px}.ba-chat form>button,.ba-attach{width:44px;height:44px}.ba-chat footer{font-size:12px} -.ba-native>header b{font-size:14px}.ba-native>header span{font-size:12px}.ba-native>header>a{font-size:13px;font-weight:700} -.ba-native>.ba-chat{min-height:0}.ba-native>.ba-chat .ba-messages{max-height:none} /* Give operator messages a full row so the bubble cannot drift into the middle. */ .ba-messages article.user{width:100%;max-width:100%;margin-left:0}