This commit is contained in:
zhangkai
2024-07-02 16:14:12 +08:00
parent 8b8f4111e2
commit a7677c32fb
37 changed files with 592 additions and 240 deletions

View File

@@ -8,12 +8,12 @@
<link rel="icon" href="/favicon.ico" /> <link rel="icon" href="/favicon.ico" />
<script src="/node_modules/ace-builds/src-min-noconflict/ace.js" type="text/javascript"></script> <script src="/node_modules/ace-builds/src-min-noconflict/ace.js" type="text/javascript"></script>
<title>NPCs</title> <title>NPCs</title>
<script type="module" crossorigin src="/assets/index-92a8c43f.js"></script> <script type="module" crossorigin src="/assets/index-7689cf6c.js"></script>
<link rel="modulepreload" crossorigin href="/assets/acebuilds-fbc0ccc6.js"> <link rel="modulepreload" crossorigin href="/assets/acebuilds-fbc0ccc6.js">
<link rel="modulepreload" crossorigin href="/assets/reactflow-c250d835.js"> <link rel="modulepreload" crossorigin href="/assets/reactflow-c250d835.js">
<link rel="modulepreload" crossorigin href="/assets/reactdrop-be699031.js"> <link rel="modulepreload" crossorigin href="/assets/reactdrop-be699031.js">
<link rel="modulepreload" crossorigin href="/assets/pdfjs-36654f0a.js"> <link rel="modulepreload" crossorigin href="/assets/pdfjs-36654f0a.js">
<link rel="stylesheet" href="/assets/index-29c2092e.css"> <link rel="stylesheet" href="/assets/index-9b55ebe4.css">
</head> </head>
<body id='body' style="width: 100%; height:100%"> <body id='body' style="width: 100%; height:100%">

View File

@@ -55,40 +55,40 @@
"language": "语言" "language": "语言"
}, },
"skills": { "skills": {
"manageTemplate": "管理能模板", "manageTemplate": "管理能模板",
"createNew": "新建能", "createNew": "新建能",
"customSkills": "自定义能", "customSkills": "自定义能",
"chooseOnline": "选择上线版本", "chooseOnline": "选择上线版本",
"executionSteps": "能通过可视化的流程编排,明确任务执行步骤", "executionSteps": "能通过可视化的流程编排,明确任务执行步骤",
"sceneTemplates": "我们提供场景模板供您使用和参考", "sceneTemplates": "我们提供场景模板供您使用和参考",
"manageProjects": "在此页面管理您的技能,对技能上下线、编辑等等", "manageProjects": "在此页面管理您的能力,对能力上下线、编辑等等",
"skillSearch": "搜索您需要的能", "skillSearch": "搜索您需要的能",
"confirmDeleteSkill": "确认删除该能?", "confirmDeleteSkill": "确认删除该能",
"backToSkillList": "返回能列表", "backToSkillList": "返回能列表",
"skillTemplateManagement": "能模板管理,模板对所有用户可见,支持拖拽排序、删除操作", "skillTemplateManagement": "能模板管理,模板对所有用户可见,支持拖拽排序、删除操作",
"templateName": "模板名称", "templateName": "模板名称",
"templateDescription": "模板描述", "templateDescription": "模板描述",
"confirmText": "是否确认删除该能模板?", "confirmText": "是否确认删除该能模板?",
"skillSettings": "能设置", "skillSettings": "能设置",
"basicInfo": "基础信息", "basicInfo": "基础信息",
"skillName": "能名称", "skillName": "能名称",
"description": "描述", "description": "描述",
"parameterInfo": "参数信息", "parameterInfo": "参数信息",
"advancedConfiguration": "高级配置", "advancedConfiguration": "高级配置",
"nextStep": "下一步,高级配置", "nextStep": "下一步,高级配置",
"skillNameRequired": "请填写能名称", "skillNameRequired": "请填写能名称",
"skillNameTooLong": "能名称过长不要超过30字", "skillNameTooLong": "能名称过长不要超过30字",
"skillNameExists": "该名称已存在", "skillNameExists": "该名称已存在",
"skillDescRequired": "请填写能描述", "skillDescRequired": "请填写能描述",
"skillDescTooLong": "能描述过长不要超过200字", "skillDescTooLong": "能描述过长不要超过200字",
"errorTitle": "关键信息有误", "errorTitle": "关键信息有误",
"onlineFailure": "上线失败", "onlineFailure": "上线失败",
"onlineSuccessful": "上线成功", "onlineSuccessful": "上线成功",
"custom": "自定义", "custom": "自定义",
"skillTemplate": "能模板", "skillTemplate": "能模板",
"skillTemplateChoose": "您可以从这里挑选一个模板开始,或者自定义高级模板", "skillTemplateChoose": "您可以从这里挑选一个模板开始,或者自定义高级模板",
"createTemplate": "创建模板", "createTemplate": "创建模板",
"createSuccessTitle": "能创建成功", "createSuccessTitle": "能创建成功",
"createFailureTitle": "创建失败", "createFailureTitle": "创建失败",
"createdBy": "创建用户", "createdBy": "创建用户",
"offline": "下线", "offline": "下线",
@@ -442,7 +442,7 @@
"build": { "build": {
"create": "创建", "create": "创建",
"assistant": "NPC", "assistant": "NPC",
"skill": "能", "skill": "能",
"tools": "工具", "tools": "工具",
"save": "保存", "save": "保存",
"online": "上线", "online": "上线",
@@ -455,7 +455,7 @@
"portraitOptimization": "NPC画像优化", "portraitOptimization": "NPC画像优化",
"automaticOptimization": "自动优化", "automaticOptimization": "自动优化",
"createDescription": "通过描述角色和任务来创建你的NPC", "createDescription": "通过描述角色和任务来创建你的NPC",
"nextDescription": "NPC可以调用多个能和工具", "nextDescription": "NPC可以调用多个能和工具",
"searchAssistant": "搜索您需要的NPC", "searchAssistant": "搜索您需要的NPC",
"manageAssistant": "在此页面管理您的NPC对NPC上下线、编辑等等", "manageAssistant": "在此页面管理您的NPC对NPC上下线、编辑等等",
"establishAssistant": "创建NPC", "establishAssistant": "创建NPC",
@@ -473,7 +473,7 @@
"guideReplaced": "引导词已替换", "guideReplaced": "引导词已替换",
"openingReplaced": "开场白已替换", "openingReplaced": "开场白已替换",
"toolsReplaced": "工具已替换", "toolsReplaced": "工具已替换",
"skillsReplaced": "能已替换", "skillsReplaced": "能已替换",
"allReplaced": "已全部替换", "allReplaced": "已全部替换",
"basicConfiguration": "基础配置", "basicConfiguration": "基础配置",
"modelConfiguration": "AI模型配置", "modelConfiguration": "AI模型配置",
@@ -503,13 +503,13 @@
"addTool": "添加工具", "addTool": "添加工具",
"search": "搜索", "search": "搜索",
"empty": "空空如也", "empty": "空空如也",
"onlineSA": "上线能&NPC", "onlineSA": "上线能&NPC",
"params": "参数", "params": "参数",
"added": "已添加", "added": "已添加",
"add": "添加", "add": "添加",
"configurationUpdated": "配置已更新", "configurationUpdated": "配置已更新",
"addSkill": "添加能", "addSkill": "添加能",
"createSkill": "创建能", "createSkill": "创建能",
"nameRequired": "名称不可为空", "nameRequired": "名称不可为空",
"nameMaxLength": "名称最多50个字符", "nameMaxLength": "名称最多50个字符",
"descMaxLength": "最多1000个字符", "descMaxLength": "最多1000个字符",
@@ -2411,7 +2411,7 @@
}, },
"NPCsChatLLM": { "NPCsChatLLM": {
"display_name": "NPCsChatLLM", "display_name": "NPCsChatLLM",
"description": "`OpenAI`聊天大型语言模型API。", "description": "无缝切换支持的大语言模型",
"description_url": "", "description_url": "",
"template": { "template": {
"api_base": { "api_base": {

View File

@@ -2411,7 +2411,7 @@
}, },
"NPCsChatLLM": { "NPCsChatLLM": {
"display_name": "NPCsChatLLM", "display_name": "NPCsChatLLM",
"description": "`OpenAI`聊天大型语言模型API。", "description": "无缝切换支持的大语言模型",
"description_url": "", "description_url": "",
"template": { "template": {
"api_base": { "api_base": {

View File

@@ -211,6 +211,9 @@ path.react-flow__edge-interaction:hover {
.bs-chat-bg { .bs-chat-bg {
width: 100%; width: 100%;
background: url(/points.png) 0 100% repeat-x; /* background: url(/points.png) 0 100% repeat-x; */
background-size: 10px 432px; background-size: 10px 432px;
/* background: rgba(0,0,0,0.9); */
/* background-color: #000; */
} }

BIN
src/assets/.DS_Store vendored

Binary file not shown.

Binary file not shown.

BIN
src/assets/chat/copy.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 915 B

BIN
src/assets/chat/xiazai.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 936 B

View File

@@ -91,7 +91,7 @@ export default function ChatInput({ clear, form, questions, inputForm, wsUrl, on
// 锁定 input // 锁定 input
setInputLock({ locked: true, reason: '' }) setInputLock({ locked: true, reason: '' })
await createWebSocket(chatId) await createWebSocket(chatId)
console.log(wsMsg,inputKey); // console.log(wsMsg,inputKey);
sendWsMsg(wsMsg) sendWsMsg(wsMsg)
// 滚动聊天到底 // 滚动聊天到底
@@ -189,7 +189,7 @@ export default function ChatInput({ clear, form, questions, inputForm, wsUrl, on
// 接受 ws 消息 // 接受 ws 消息
const handleWsMessage = (data) => { const handleWsMessage = (data) => {
console.log(data) // console.log(data)
if (Array.isArray(data) && data.length) return if (Array.isArray(data) && data.length) return
if (data.type === "begin") { if (data.type === "begin") {
setIsStop(false) setIsStop(false)
@@ -311,9 +311,10 @@ export default function ChatInput({ clear, form, questions, inputForm, wsUrl, on
} }
}} }}
></textarea> ></textarea>
<div className=" absolute w-full flex justify-center bottom-32 pointer-events-none"> {!isStop && <div className=" absolute w-full flex justify-center bottom-32 pointer-events-none">
<Button className="rounded-full pointer-events-auto" variant="outline" disabled={isStop} onClick={() => { setIsStop(true); stop(); }}><StopCircle className="mr-2" />Stop</Button> <Button className="rounded-full pointer-events-auto" variant="outline" disabled={isStop} onClick={() => { setIsStop(true); stop(); }}><StopCircle className="mr-2" />Stop</Button>
</div> </div>}
{/* <p className="w-[100%] text-center text-[#666666]">内容由AI生成仅供参考</p> */} {/* <p className="w-[100%] text-center text-[#666666]">内容由AI生成仅供参考</p> */}
</div> </div>
<p className="text-center text-sm pt-2 pb-4 text-gray-400">{appConfig.dialogTips}</p> <p className="text-center text-sm pt-2 pb-4 text-gray-400">{appConfig.dialogTips}</p>

View File

@@ -38,7 +38,7 @@ const colorList = [
"#95A5A6" "#95A5A6"
] ]
export default function MessageBs({ data, onUnlike = () => { }, flow_type, onSource }: { data: ChatMessageType, flow_type: string, onUnlike?: any, onSource?: any }) { export default function MessageBs({ data, onUnlike = () => { }, flow_type, onSource }: { data: ChatMessageType, flow_type: any, onUnlike?: any, onSource?: any }) {
const avatarColor = colorList[ const avatarColor = colorList[
(data.sender?.split('').reduce((num, s) => num + s.charCodeAt(), 0) || 0) % colorList.length (data.sender?.split('').reduce((num, s) => num + s.charCodeAt(), 0) || 0) % colorList.length
] ]
@@ -88,22 +88,32 @@ export default function MessageBs({ data, onUnlike = () => { }, flow_type, onSou
const chatId = useMessageStore(state => state.chatId) const chatId = useMessageStore(state => state.chatId)
return <div className="flex w-full py-1"> return <div className="flex w-full py-1">
<div className="w-fit max-w-[90%]"> <div className="w-[100%]">
{data.sender && <p className="text-gray-600 text-xs mb-2">{data.sender}</p>} {data.sender && <p className="text-gray-600 text-xs mb-2">{data.sender}</p>}
<div className="flex items-start avatarZk"> <div className="flex items-start avatarZk">
{/* {(data.flow_id == "06b1d374-ba97-46e6-8782-c56dec8dcc17" || data.flow_id == "ed8e21f6-9757-43d0-b076-8c6e81bb0580") && <img src={robot2} className="w-[50px]" alt=""/>} {/* {(data.flow_id == "06b1d374-ba97-46e6-8782-c56dec8dcc17" || data.flow_id == "ed8e21f6-9757-43d0-b076-8c6e81bb0580") && <img src={robot2} className="w-[50px]" alt=""/>}
{data.flow_id == "ca214b41-2b73-4585-b172-bf1e546cf6ec" && <img src={robot3} className="w-[50px]" alt=""/>} {data.flow_id == "ca214b41-2b73-4585-b172-bf1e546cf6ec" && <img src={robot3} className="w-[50px]" alt=""/>}
{(data.flow_id != "06b1d374-ba97-46e6-8782-c56dec8dcc17" && data.flow_id != "ed8e21f6-9757-43d0-b076-8c6e81bb0580" && data.flow_id != "ca214b41-2b73-4585-b172-bf1e546cf6ec") && <img src={robot} className="w-[50px]" alt=""/>} */} {(data.flow_id != "06b1d374-ba97-46e6-8782-c56dec8dcc17" && data.flow_id != "ed8e21f6-9757-43d0-b076-8c6e81bb0580" && data.flow_id != "ca214b41-2b73-4585-b172-bf1e546cf6ec") && <img src={robot} className="w-[50px]" alt=""/>} */}
{data.flow_id && <TitleIconBg className="w-[40px] h-[40px]" img={data.avatar_img} id={data.avatar_color ? data.avatar_color : data.flow_id} ><img src={data.avatar_img ? data.avatar_img : (flow_type == "assistant" ? npcIcon : nengliIcon)} alt="" /></TitleIconBg>} {data.flow_id && <TitleIconBg className="w-[40px] h-[40px] mr-[10px]" img={flow_type.avatar_img} id={flow_type.avatar_color ? flow_type.avatar_color : data.flow_id} ><img src={flow_type.avatar_img ? flow_type.avatar_img : (flow_type.type == "assistant" ? npcIcon : nengliIcon)} alt="" /></TitleIconBg>}
<div ref={messageRef} className={`min-h-8 min-w-[110px] max-w-[90%] ml-[10px] ${data.id && data.end && 'pb-8'}`}> <div ref={messageRef} className="text-sm max-w-[calc(100%-100px)]">
{/* <div className="chat-start-zk relative">
{data.message.toString() ? mkdown : <span className="loading loading-ring loading-md"></span>} */}
{/* @user */}
{/* {data.receiver && <p className="text-blue-500 text-sm">@ {data.receiver.user_name}</p>} */}
{/* 光标 */}
{/* {data.message.toString() && !data.end && <div className="animate-cursor absolute w-2 h-5 ml-1 bg-gray-600" style={{ left: cursor.x, top: cursor.y }}></div>} */}
{/* </div> */}
{data.message.toString() ?
<div className="chat-start-zk relative"> <div className="chat-start-zk relative">
{data.message.toString() ? mkdown : <span className="loading loading-ring loading-md"></span>} {mkdown}
{/* @user */} {/* @user */}
{data.receiver && <p className="text-blue-500 text-sm">@ {data.receiver.user_name}</p>} {data.receiver && <p className="text-blue-500 text-sm">@ {data.receiver.user_name}</p>}
{/* 光标 */} {/* 光标 */}
{/* {data.message.toString() && !data.end && <div className="animate-cursor absolute w-2 h-5 ml-1 bg-gray-600" style={{ left: cursor.x, top: cursor.y }}></div>} */} {/* {data.message.toString() && !data.end && <div className="animate-cursor absolute w-2 h-5 ml-1 bg-gray-600" style={{ left: cursor.x, top: cursor.y }}></div>} */}
</div> </div>
: <div><LoadIcon className="text-gray-400" /></div>
}
{/* 赞 踩 */} {/* 赞 踩 */}
{!!data.id && data.end && <Thumbs {!!data.id && data.end && <Thumbs

View File

@@ -75,7 +75,7 @@ export default function MessagePanne({ useName, guideWord, loadMore, flow_type }
} else if (msg.thought) { } else if (msg.thought) {
type = 'system' type = 'system'
} }
console.log(type)
switch (type) { switch (type) {
case 'user': case 'user':
return <MessageUser key={msg.id} useName={useName} data={msg} />; return <MessageUser key={msg.id} useName={useName} data={msg} />;
@@ -88,7 +88,8 @@ export default function MessagePanne({ useName, guideWord, loadMore, flow_type }
onSource={(data) => { sourceRef.current?.openModal(data) }} onSource={(data) => { sourceRef.current?.openModal(data) }}
/>; />;
case 'system': case 'system':
return <MessageSystem key={msg.id} data={msg} />; // return <MessageSystem key={msg.id} data={msg} />;
return <RunLog key={msg.id} data={msg} />;
case 'separator': case 'separator':
return <Separator key={msg.id} text={msg.message || t('chat.roundOver')} />; return <Separator key={msg.id} text={msg.message || t('chat.roundOver')} />;
case 'file': case 'file':

View File

@@ -34,6 +34,8 @@ export default function RunLog({ data }) {
// if (!knowledge) throw new Error('调试日志无法匹配到使用的知识库详情id:' + data.message.tool_key) // if (!knowledge) throw new Error('调试日志无法匹配到使用的知识库详情id:' + data.message.tool_key)
title = knowledge ? `${data.end ? '已搜索' : '正在搜索'} ${knowledge.name}` : '知识库已被删除,无法获取知识库名' title = knowledge ? `${data.end ? '已搜索' : '正在搜索'} ${knowledge.name}` : '知识库已被删除,无法获取知识库名'
} else if (data.category === 'processing') {
title = data.thought.slice(0, 50)+"..."
} }
return [title, lost] return [title, lost]
}, [assistantState, data]) }, [assistantState, data])
@@ -46,7 +48,7 @@ export default function RunLog({ data }) {
data.end ? <ToastIcon type={lost ? 'error' : 'success'} /> : data.end ? <ToastIcon type={lost ? 'error' : 'success'} /> :
<LoadIcon className="text-primary duration-300" /> <LoadIcon className="text-primary duration-300" />
} }
<span>{title}</span> <span className="text-[#666]">{title}</span>
</div> </div>
<CaretDownIcon className={open && 'rotate-180'} /> <CaretDownIcon className={open && 'rotate-180'} />
</div> </div>

View File

@@ -154,7 +154,6 @@ export const useMessageStore = create<State & Actions>((set, get) => ({
console.log('change updateCurrentMessage'); console.log('change updateCurrentMessage');
const messages = get().messages const messages = get().messages
const isRunLog = runLogsTypes.includes(wsdata.category); const isRunLog = runLogsTypes.includes(wsdata.category);
console.log(wsdata,isRunLog,messages)
// run log类型存在嵌套情况使用 extra 匹配 currentMessage; 否则取最近 // run log类型存在嵌套情况使用 extra 匹配 currentMessage; 否则取最近
const currentMessageIndex = isRunLog ? const currentMessageIndex = isRunLog ?
messages.findLastIndex((msg) => msg.extra === wsdata.extra) messages.findLastIndex((msg) => msg.extra === wsdata.extra)
@@ -173,13 +172,12 @@ export const useMessageStore = create<State & Actions>((set, get) => ({
} }
messages[currentMessageIndex] = newCurrentMessage messages[currentMessageIndex] = newCurrentMessage
console.log(newCurrentMessage) console.log(messages,currentMessageIndex,newCurrentMessage)
// 会话特殊处理,兼容后端的缺陷 // 会话特殊处理,兼容后端的缺陷
if (!isRunLog) { if (!isRunLog) {
// start - end 之间没有内容删除load // start - end 之间没有内容删除load
if (newCurrentMessage.end && !(newCurrentMessage.files.length || newCurrentMessage.thought || newCurrentMessage.message)) { if (newCurrentMessage.end && !(newCurrentMessage.files.length || newCurrentMessage.thought || newCurrentMessage.message)) {
// messages.pop() messages.pop()
console.log("删了")
} }
// 无 messageid 删除 // 无 messageid 删除
// if (newCurrentMessage.end && !newCurrentMessage.id) { // if (newCurrentMessage.end && !newCurrentMessage.id) {

View File

@@ -10,7 +10,7 @@ import {
SheetTitle, SheetTitle,
SheetTrigger, SheetTrigger,
} from "../../bs-ui/sheet"; } from "../../bs-ui/sheet";
import CardComponent from "../cardComponent"; import CardComponent, { TitleIconBg } from "../cardComponent";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { SpotlightCard } from "@lobehub/ui"; import { SpotlightCard } from "@lobehub/ui";
import { Flexbox } from 'react-layout-kit'; import { Flexbox } from 'react-layout-kit';
@@ -20,6 +20,8 @@ import robot3 from "../../../assets/robot3.png";
import zidingyi1 from "../../../assets/npc/zidingyi1.png"; import zidingyi1 from "../../../assets/npc/zidingyi1.png";
import zidingyi2 from "../../../assets/npc/zidingyi2.png"; import zidingyi2 from "../../../assets/npc/zidingyi2.png";
import borderR from "../../../assets/npc/border-r.png"; import borderR from "../../../assets/npc/border-r.png";
import npcIcon from "../../../assets/npc/npcIcon.png";
import nengliIcon from "../../../assets/npc/nengliIcon.png";
export default function SkillSheet({ select, children, onSelect }) { export default function SkillSheet({ select, children, onSelect }) {
const [keyword, setKeyword] = useState(""); const [keyword, setKeyword] = useState("");
@@ -65,19 +67,13 @@ export default function SkillSheet({ select, children, onSelect }) {
<span> <span>
<span> <span>
<div> <div>
{/* <img src={robot} className="w-[160px]" alt=""/> */} <TitleIconBg className="w-[160px] h-[160px] min-w-[160px]" img={item.avatar_img} id={item.avatar_color ? item.avatar_color : item.id} ><img src={item.avatar_img ? item.avatar_img : (item.flow_type == "assistant" ? npcIcon : nengliIcon)} alt="" /></TitleIconBg>
{(item.id == "06b1d374-ba97-46e6-8782-c56dec8dcc17" || item.id == "ed8e21f6-9757-43d0-b076-8c6e81bb0580") && <img src={robot2} className="w-[160px]" alt=""/>}
{item.id == "ca214b41-2b73-4585-b172-bf1e546cf6ec" && <img src={robot3} className="w-[160px]" alt=""/>}
{(item.id != "06b1d374-ba97-46e6-8782-c56dec8dcc17" && item.id != "ed8e21f6-9757-43d0-b076-8c6e81bb0580" && item.id != "ca214b41-2b73-4585-b172-bf1e546cf6ec") && <img src={robot} className="w-[160px]" alt=""/>}
</div> </div>
</span> </span>
</span> </span>
</div> </div>
<div> <div>
{(item.id == "06b1d374-ba97-46e6-8782-c56dec8dcc17" || item.id == "ed8e21f6-9757-43d0-b076-8c6e81bb0580") && <img src={robot2} className="w-[42px]" alt=""/>} <TitleIconBg className="w-[40px] h-[40px] min-w-[40px]" img={item.avatar_img} id={item.avatar_color ? item.avatar_color : item.id} ><img src={item.avatar_img ? item.avatar_img : (item.flow_type == "assistant" ? npcIcon : nengliIcon)} alt="" /></TitleIconBg>
{item.id == "ca214b41-2b73-4585-b172-bf1e546cf6ec" && <img src={robot3} className="w-[42px]" alt=""/>}
{(item.id != "06b1d374-ba97-46e6-8782-c56dec8dcc17" && item.id != "ed8e21f6-9757-43d0-b076-8c6e81bb0580" && item.id != "ca214b41-2b73-4585-b172-bf1e546cf6ec") && <img src={robot} className="w-[42px]" alt=""/>}
{/* <img src={robot} className="w-[42px]" alt=""/> */}
<div> <div>
<p>{item.name}</p> <p>{item.name}</p>
<div> <div>

View File

@@ -107,10 +107,10 @@ export default function InputFileComponent({
onClick={handleButtonClick} onClick={handleButtonClick}
className={ className={
editNode editNode
? "input-edit-node input-dialog text-muted-foreground" ? "input-edit-node input-dialog text-[#999]"
: disabled : disabled
? "input-disable input-dialog input-primary" ? "input-disable input-dialog input-primary"
: "input-dialog input-primary text-muted-foreground" : "input-dialog input-primary text-[#999]"
} }
> >
{myValue !== "" ? myValue : placeholder} {myValue !== "" ? myValue : placeholder}

View File

@@ -56,6 +56,12 @@ export async function setSysConfigApi(data) {
export async function getRoleSkillsApi(params): Promise<{ data: any[], total: number }> { export async function getRoleSkillsApi(params): Promise<{ data: any[], total: number }> {
return await axios.get(`/api/v1/role_access/flow`, { params }); return await axios.get(`/api/v1/role_access/flow`, { params });
} }
/**
* 根据角色获取技能列表
*/
export async function getRoleAssistApi(params): Promise<{ data: any[], total: number }> {
return await axios.get(`/api/v1/role_access/list_type`, { params });
}
/** /**
* 根据角色获取知识库列表 * 根据角色获取知识库列表
*/ */

View File

@@ -3,6 +3,8 @@ import { useState } from "react";
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter"; import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";
import { oneDark } from "react-syntax-highlighter/dist/cjs/styles/prism"; import { oneDark } from "react-syntax-highlighter/dist/cjs/styles/prism";
import { copyText, programmingLanguages } from "../../../../utils"; import { copyText, programmingLanguages } from "../../../../utils";
import copy from "../../../../assets/chat/copy.png"
import xiazai from "../../../../assets/chat/xiazai.png"
interface Props { interface Props {
language: string; language: string;
@@ -42,17 +44,19 @@ export function CodeBlock({ language, value }) {
URL.revokeObjectURL(url); URL.revokeObjectURL(url);
}; };
return ( return (
<div className="codeblock font-sans text-[16px]"> <div className="codeblock font-sans text-[16px] daimaStyle">
<div className="code-block-modal"> <div className="code-block-modal">
<span className="code-block-modal-span">{language}</span> <span className="code-block-modal-span text-[#999999] text-[14px]">{language}</span>
<div className="flex items-center"> <div className="flex items-center">
<button className="code-block-modal-button" onClick={copyToClipboard}> <button className="code-block-modal-button" onClick={copyToClipboard}>
{isCopied ? <IconCheck size={18} /> : <IconClipboard size={18} />} {/* {isCopied ? <IconCheck size={18} /> : <IconClipboard size={18} />} */}
{isCopied ? "Copied!" : "Copy code"} <img src={copy} alt="" className="w-[14px]" />
{/* {isCopied ? "Copied!" : "Copy code"} */}
</button> </button>
<button className="code-block-modal-button" onClick={downloadAsFile}> <button className="code-block-modal-button ml-[10px]" onClick={downloadAsFile}>
<IconDownload size={18} /> {/* <IconDownload size={18} /> */}
<img src={xiazai} alt="" className="w-[14px]" />
</button> </button>
</div> </div>
</div> </div>

View File

@@ -387,7 +387,7 @@ export default function ChatPanne({ customWsHost = '', data }) {
wsUrl={wsUrl} wsUrl={wsUrl}
onBeforSend={getWsParamData} onBeforSend={getWsParamData}
loadMore={() => loadMoreHistoryMsg(flow.id,"flow")} loadMore={() => loadMoreHistoryMsg(flow.id,"flow")}
type={type} type={{"avatar_img":flow.avatar_img,"avatar_color":flow.avatar_color,"type":type}}
inputForm={flowSate.isForm ? <ChatReportForm flow={flow} onStart={sendReport} /> : null} inputForm={flowSate.isForm ? <ChatReportForm flow={flow} onStart={sendReport} /> : null}
/> />
{/* <div ref={messagesRef} className={`chat-panne h-full overflow-y-scroll no-scrollbar px-[35px] ${isRoom || isReport ? 'pb-0' : 'pb-0'}`}> {/* <div ref={messagesRef} className={`chat-panne h-full overflow-y-scroll no-scrollbar px-[35px] ${isRoom || isReport ? 'pb-0' : 'pb-0'}`}>
@@ -426,7 +426,7 @@ export default function ChatPanne({ customWsHost = '', data }) {
wsUrl={wsUrl} wsUrl={wsUrl}
onBeforSend={getWsParamData} onBeforSend={getWsParamData}
loadMore={() => loadMoreHistoryMsg(assistant.id,"assistant")} loadMore={() => loadMoreHistoryMsg(assistant.id,"assistant")}
type={type} type={{"avatar_img":assistant.avatar_img,"avatar_color":assistant.avatar_color,"type":type}}
inputForm={null} inputForm={null}
/> />
</div> </div>

View File

@@ -60,7 +60,9 @@ export default function SkillChatPage() {
"chat_id": _chatId, "chat_id": _chatId,
"create_time": "-", "create_time": "-",
"update_time": "-", "update_time": "-",
"flow_type": card.flow_type "flow_type": card.flow_type,
"avatar_img": card.avatar_img,
"avatar_color": card.avatar_color,
}) })
setSelelctChat({ id: card.id, chatId: _chatId, type: card.flow_type }) setSelelctChat({ id: card.id, chatId: _chatId, type: card.flow_type })
@@ -92,7 +94,7 @@ export default function SkillChatPage() {
return <div className="flex"> return <div className="flex">
<div className="h-screen w-[288px] relative"> <div className="h-screen w-[288px] relative">
<div className="xinDuiHua absolute"> <div className="xinDuiHua absolute bg-[#000000] xinDuiHua-box">
<SkillChatSheet onSelect={handlerSelectFlow}> <SkillChatSheet onSelect={handlerSelectFlow}>
<div id="newchat" className="xinDuiHua-btn cursor-pointer"> <div id="newchat" className="xinDuiHua-btn cursor-pointer">
{/* <PlusBoxIcon className="dark:hidden"></PlusBoxIcon> */} {/* <PlusBoxIcon className="dark:hidden"></PlusBoxIcon> */}

View File

@@ -371,6 +371,7 @@ export default function ChatPanne({ customWsHost = '', data }) {
wsUrl={wsUrl} wsUrl={wsUrl}
onBeforSend={getWsParamData} onBeforSend={getWsParamData}
loadMore={() => loadMoreHistoryMsg(flow.id, type)} loadMore={() => loadMoreHistoryMsg(flow.id, type)}
type={{"avatar_img":flow.avatar_img,"avatar_color":flow.avatar_color,"type":type}}
inputForm={flowSate.isForm ? <ChatReportForm flow={flow} onStart={sendReport} /> : null} inputForm={flowSate.isForm ? <ChatReportForm flow={flow} onStart={sendReport} /> : null}
/> />
{/* <div ref={messagesRef} className={`chat-panne h-full overflow-y-scroll no-scrollbar px-[35px] ${isRoom || isReport ? 'pb-0' : 'pb-0'}`}> {/* <div ref={messagesRef} className={`chat-panne h-full overflow-y-scroll no-scrollbar px-[35px] ${isRoom || isReport ? 'pb-0' : 'pb-0'}`}>

View File

@@ -46,7 +46,7 @@ export default function FilesPage() {
}) })
) )
// loadData(); // loadData();
// setTimeout(() => reload(), 5000); setTimeout(() => reload(), 5000);
const [hasPermission, setHasPermission] = useState(true) const [hasPermission, setHasPermission] = useState(true)
const { appConfig } = useContext(locationContext) const { appConfig } = useContext(locationContext)

View File

@@ -10,11 +10,14 @@ import { classNames, nodeColors, nodeIconsLucide, getNodeNames, nodeIMgsLucide,
import gengduo from "../../../../assets/npc/gengduo.png"; import gengduo from "../../../../assets/npc/gengduo.png";
import gang from "../../../../assets/npc/gang.png"; import gang from "../../../../assets/npc/gang.png";
import wenhao from "../../../../assets/npc/wenhao.png"; import wenhao from "../../../../assets/npc/wenhao.png";
import del from "../../../../assets/npc/del.png";
import { JSX } from "react/jsx-runtime"; import { JSX } from "react/jsx-runtime";
import DescriptionModel from "../../../../modals/descriptionModel"; import DescriptionModel from "../../../../modals/descriptionModel";
import { typesContext } from "@/contexts/typesContext";
export default function PersonalComponents({ onDragStart, launch }) { export default function PersonalComponents({ onDragStart, launch }) {
const { addSavedComponent, checkComponentsName, delComponent, savedComponents } = useContext(userContext) const { addSavedComponent, checkComponentsName, delComponent, savedComponents } = useContext(userContext)
const { types } = useContext(typesContext);
const addComponent = (data) => { const addComponent = (data) => {
if (checkComponentsName(data.node.display_name)) { if (checkComponentsName(data.node.display_name)) {
@@ -126,29 +129,28 @@ export default function PersonalComponents({ onDragStart, launch }) {
<CircleX className="side-bar-components-icon ml-2 cursor-pointer" onClick={(e) => handleDel(e, comp)} /> <CircleX className="side-bar-components-icon ml-2 cursor-pointer" onClick={(e) => handleDel(e, comp)} />
</div> */} </div> */}
<div className="npc_zujianList_item"> <div className="npc_zujianList_item">
<div className="npcInfoItemBg"> <div className="npcInfoItemBg z-[0]">
<span> <span>
<span> <span>
<div style={{background:nodeColors["Save"] ?? nodeColors.unknown}}> <div style={{background:nodeColors[types[comp.data.type]] ?? nodeColors.unknown}}>
</div> </div>
</span> </span>
</span> </span>
</div> </div>
<div className="z-[10]">
<div> <div>
<div> <img src={nodeIMgsLucide1[types[comp.data.type]]} className="w-[14px]" alt="" />
<img src={nodeIMgsLucide1["Save"]} className="w-[14px]" alt="" />
<p>{comp.name ?? ""}</p> <p>{comp.name ?? ""}</p>
{/* <img src={wenhao} className="w-[14px]" alt="" /> */} {/* <img src={wenhao} className="w-[14px]" alt="" /> */}
{comp.description_url && <img src={wenhao} onClick={() => { {comp.description_url && <img src={wenhao} onClick={() => {
console.log(111)
openPopUp(<DescriptionModel data={comp.description_url} />) openPopUp(<DescriptionModel data={comp.description_url} />)
}} className="w-[14px] z-[1]" alt="" />} }} className="w-[14px] z-[1]" alt="" />}
</div> </div>
<div> <div onClick={(e) => handleDel(e, comp)}>
{/* <img src={gang} className="w-[14px]" alt="" /> */} <img src={del} className="w-[14px] cursor-pointer z-[10]" alt="" />
</div> </div>
</div> </div>
<p>{comp.description}</p> <p>{comp.data.node.description}</p>
</div> </div>
</div> </div>
</div> </div>

View File

@@ -54,7 +54,7 @@ export default function SkillTemps({ flows, isTemp = false,
{/* {(item.id == "06b1d374-ba97-46e6-8782-c56dec8dcc17" || item.id == "ed8e21f6-9757-43d0-b076-8c6e81bb0580") && <img src={robot2} className="w-[160px]" alt=""/>} {/* {(item.id == "06b1d374-ba97-46e6-8782-c56dec8dcc17" || item.id == "ed8e21f6-9757-43d0-b076-8c6e81bb0580") && <img src={robot2} className="w-[160px]" alt=""/>}
{item.id == "ca214b41-2b73-4585-b172-bf1e546cf6ec" && <img src={robot3} className="w-[160px]" alt=""/>} {item.id == "ca214b41-2b73-4585-b172-bf1e546cf6ec" && <img src={robot3} className="w-[160px]" alt=""/>}
{(item.id != "06b1d374-ba97-46e6-8782-c56dec8dcc17" && item.id != "ed8e21f6-9757-43d0-b076-8c6e81bb0580" && item.id != "ca214b41-2b73-4585-b172-bf1e546cf6ec") && <img src={robot} className="w-[160px]" alt=""/>} */} {(item.id != "06b1d374-ba97-46e6-8782-c56dec8dcc17" && item.id != "ed8e21f6-9757-43d0-b076-8c6e81bb0580" && item.id != "ca214b41-2b73-4585-b172-bf1e546cf6ec") && <img src={robot} className="w-[160px]" alt=""/>} */}
<TitleIconBg className="w-[160px] h-[160px]" img={item.avatar_img} id={item.avatar_color ? item.avatar_color : item.id} ><img src={item.avatar_img ? item.avatar_img : nengliIcon} alt="" /></TitleIconBg> <TitleIconBg className="w-[160px] h-[160px]" img={item.avatar_img} id={item.avatar_color ? item.avatar_color : item.flow_id} ><img src={item.avatar_img ? item.avatar_img : nengliIcon} alt="" /></TitleIconBg>
</div> </div>
</span> </span>
</span> </span>
@@ -64,7 +64,7 @@ export default function SkillTemps({ flows, isTemp = false,
{item.id == "ca214b41-2b73-4585-b172-bf1e546cf6ec" && <img src={robot3} className="w-[42px]" alt=""/>} {item.id == "ca214b41-2b73-4585-b172-bf1e546cf6ec" && <img src={robot3} className="w-[42px]" alt=""/>}
{(item.id != "06b1d374-ba97-46e6-8782-c56dec8dcc17" && item.id != "ed8e21f6-9757-43d0-b076-8c6e81bb0580" && item.id != "ca214b41-2b73-4585-b172-bf1e546cf6ec") && <img src={robot} className="w-[42px]" alt=""/>} */} {(item.id != "06b1d374-ba97-46e6-8782-c56dec8dcc17" && item.id != "ed8e21f6-9757-43d0-b076-8c6e81bb0580" && item.id != "ca214b41-2b73-4585-b172-bf1e546cf6ec") && <img src={robot} className="w-[42px]" alt=""/>} */}
{/* <img src={robot} className="w-[42px]" alt=""/> */} {/* <img src={robot} className="w-[42px]" alt=""/> */}
<TitleIconBg className="w-[40px] h-[40px] min-w-[40px]" img={item.avatar_img} id={item.avatar_color ? item.avatar_color : item.id} ><img src={item.avatar_img ? item.avatar_img : nengliIcon} alt="" /></TitleIconBg> <TitleIconBg className="w-[40px] h-[40px] min-w-[40px]" img={item.avatar_img} id={item.avatar_color ? item.avatar_color : item.flow_id} ><img src={item.avatar_img ? item.avatar_img : nengliIcon} alt="" /></TitleIconBg>
<div> <div>
<p>{item.name}</p> <p>{item.name}</p>
<div> <div>

View File

@@ -5,7 +5,7 @@ import { Dialog, DialogTrigger } from "@/components/bs-ui/dialog";
import { useAssistantStore } from "@/store/assistantStore"; import { useAssistantStore } from "@/store/assistantStore";
import { ChevronLeftIcon, Pencil2Icon } from "@radix-ui/react-icons"; import { ChevronLeftIcon, Pencil2Icon } from "@radix-ui/react-icons";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { useNavigate } from "react-router-dom"; import { useLocation, useNavigate } from "react-router-dom";
import EditAssistantDialog from "./EditAssistantDialog"; import EditAssistantDialog from "./EditAssistantDialog";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import npcIcon from "../../../../assets/npc/npcIcon.png"; import npcIcon from "../../../../assets/npc/npcIcon.png";
@@ -20,6 +20,7 @@ export default function Header({ onSave, onLine }) {
const [editShow, setEditShow] = useState(false); const [editShow, setEditShow] = useState(false);
const needSaveRef = useRef(false) const needSaveRef = useRef(false)
const {state} = useLocation();
useEffect(() => { useEffect(() => {
if (needSaveRef.current) { if (needSaveRef.current) {
needSaveRef.current = false needSaveRef.current = false
@@ -33,7 +34,7 @@ export default function Header({ onSave, onLine }) {
} }
return <div className="flex justify-between items-center border-b px-4"> return <div className="flex justify-between items-center border-b px-4">
<div className="flex items-center gap-2 py-4"> <div className="flex items-center gap-2 py-4">
<Button variant="outline" size="icon" onClick={() => navigate(-1)}><ChevronLeftIcon className="h-4 w-4" /></Button> <Button variant="outline" size="icon" onClick={() => navigate('/build/assist',{state:state})}><ChevronLeftIcon className="h-4 w-4" /></Button>
{/* <TitleIconBg id={assistantState.id} className="ml-4"><AssistantIcon /></TitleIconBg> */} {/* <TitleIconBg id={assistantState.id} className="ml-4"><AssistantIcon /></TitleIconBg> */}
<TitleIconBg className="w-[40px] h-[40px]" img={assistantState.avatar_img} id={assistantState.avatar_color ? assistantState.avatar_color : assistantState.id} ><img src={assistantState.avatar_img ? assistantState.avatar_img : npcIcon} alt="" /></TitleIconBg> <TitleIconBg className="w-[40px] h-[40px]" img={assistantState.avatar_img} id={assistantState.avatar_color ? assistantState.avatar_color : assistantState.id} ><img src={assistantState.avatar_img ? assistantState.avatar_img : npcIcon} alt="" /></TitleIconBg>
<span className="bisheng-title text-[#fff]">{assistantState.name}</span> <span className="bisheng-title text-[#fff]">{assistantState.name}</span>

View File

@@ -52,7 +52,7 @@ export default function TestChat({ assisId, guideQuestion }) {
guideWord='' guideWord=''
wsUrl={wsUrl} wsUrl={wsUrl}
onBeforSend={getWsParamData} onBeforSend={getWsParamData}
type="assistant" type={{"avatar_img":assistantState.avatar_img,"avatar_color":assistantState.avatar_color,"type":"assistant"}}
></ChatComponent> ></ChatComponent>
</div> </div>
}; };

View File

@@ -5,7 +5,7 @@ import { captureAndAlertRequestErrorHoc } from "@/controllers/request";
import { useAssistantStore } from "@/store/assistantStore"; import { useAssistantStore } from "@/store/assistantStore";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import { useParams } from "react-router"; import { useParams } from "react-router";
import { useNavigate } from "react-router-dom"; import { useLocation, useNavigate } from "react-router-dom";
import Header from "./components/editAssistant/Header"; import Header from "./components/editAssistant/Header";
import Prompt from "./components/editAssistant/Prompt"; import Prompt from "./components/editAssistant/Prompt";
import Setting from "./components/editAssistant/Setting"; import Setting from "./components/editAssistant/Setting";
@@ -58,6 +58,7 @@ export default function editAssistant() {
}) })
}) })
} }
const {state} = useLocation();
// 上线助手 // 上线助手
const handleOnline = async () => { const handleOnline = async () => {
@@ -72,7 +73,8 @@ export default function editAssistant() {
}) })
}) })
setTimeout(() => { setTimeout(() => {
navigate('/build') // navigate('/build')
navigate('/build/assist',{state:state})
}, 1200); }, 1200);
} }

View File

@@ -1,7 +1,7 @@
import { ArrowLeft, ChevronUp } from "lucide-react"; import { ArrowLeft, ChevronUp } from "lucide-react";
import { useContext, useEffect, useMemo, useRef, useState } from "react"; import { useContext, useEffect, useMemo, useRef, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useNavigate, useParams } from "react-router-dom"; import { useLocation, useNavigate, useParams } from "react-router-dom";
import L2ParameterComponent from "../../CustomNodes/GenericNode/components/parameterComponent/l2Index"; import L2ParameterComponent from "../../CustomNodes/GenericNode/components/parameterComponent/l2Index";
import ShadTooltip from "../../components/ShadTooltipComponent"; import ShadTooltip from "../../components/ShadTooltipComponent";
import { Button } from "../../components/ui/button"; import { Button } from "../../components/ui/button";
@@ -41,8 +41,9 @@ export default function l2Edit() {
const [avatar_img, setAvatar_img] = useState("") const [avatar_img, setAvatar_img] = useState("")
const [avatar_color, setAvatar_color] = useState("") const [avatar_color, setAvatar_color] = useState("")
const randomNum = Math.floor(Math.random()*(4-0+1)+0).toString(); const randomNum = Math.floor(Math.random()*(4-0+1)+0).toString();
const {state} = useLocation();
useEffect(() => { useEffect(() => {
console.log(id)
if(!id){ if(!id){
setAvatar_color(randomNum); setAvatar_color(randomNum);
} }
@@ -51,11 +52,18 @@ export default function l2Edit() {
// 已有flow 数据时,不再请求 // 已有flow 数据时,不再请求
if (flow?.id === id) { if (flow?.id === id) {
setIsL2(true) setIsL2(true)
console.log(1)
nameRef.current.value = flow.name nameRef.current.value = flow.name
descRef.current.value = flow.description descRef.current.value = flow.description
guideRef.current.value = flow.guide_word guideRef.current.value = flow.guide_word
if(state && state.avatar_img){
setAvatar_img(state.avatar_img);
}else{
setAvatar_img(flow.avatar_img); setAvatar_img(flow.avatar_img);
if(flow.avatar_color){ }
if(state && state.avatar_color){
setAvatar_color(state.avatar_color);
}else if(flow.avatar_color){
setAvatar_color(flow.avatar_color); setAvatar_color(flow.avatar_color);
}else{ }else{
setAvatar_color(id); setAvatar_color(id);
@@ -70,8 +78,14 @@ export default function l2Edit() {
nameRef.current.value = _flow.name nameRef.current.value = _flow.name
descRef.current.value = _flow.description descRef.current.value = _flow.description
guideRef.current.value = _flow.guide_word guideRef.current.value = _flow.guide_word
if(state && state.avatar_img){
setAvatar_img(state.avatar_img);
}else{
setAvatar_img(_flow.avatar_img); setAvatar_img(_flow.avatar_img);
if(_flow.avatar_color){ }
if(state && state.avatar_color){
setAvatar_color(state.avatar_color);
}else if(_flow.avatar_color){
setAvatar_color(_flow.avatar_color); setAvatar_color(_flow.avatar_color);
}else{ }else{
setAvatar_color(id); setAvatar_color(id);
@@ -152,7 +166,8 @@ export default function l2Edit() {
await saveFlow({...flow, name, description, guide_word: guideWords, avatar_img, avatar_color}) await saveFlow({...flow, name, description, guide_word: guideWords, avatar_img, avatar_color})
setLoading(false) setLoading(false)
setSuccessData({ title: t('success') }); setSuccessData({ title: t('success') });
setTimeout(() => /^\/skill\/[\w\d-]+/.test(location.pathname) && navigate(-1), 2000); setTimeout(() => navigate('/build/skills',{state:state}), 2000);
// setTimeout(() => /^\/skill\/[\w\d-]+/.test(location.pathname) && navigate(-1), 2000);
} }
// 表单收缩 // 表单收缩
@@ -234,7 +249,7 @@ export default function l2Edit() {
<div className="p-6 pb-48 h-screen overflow-y-auto"> <div className="p-6 pb-48 h-screen overflow-y-auto">
<div className="flex justify-between w-full"> <div className="flex justify-between w-full">
<ShadTooltip content={t('back')} side="right"> <ShadTooltip content={t('back')} side="right">
<button className="extra-side-bar-buttons w-[36px]" onClick={() => window.history.length < 3 ? navigate('/build/skills') : navigate(-1)}> <button className="extra-side-bar-buttons w-[36px]" onClick={() => navigate('/build/skills',{state:state})}>
<ArrowLeft strokeWidth={1.5} className="side-bar-button-size" /> <ArrowLeft strokeWidth={1.5} className="side-bar-button-size" />
</button> </button>
</ShadTooltip> </ShadTooltip>

View File

@@ -7,7 +7,7 @@ import { AssistantItemDB, changeAssistantStatusApi, deleteAssistantApi, getAssis
import { FlowType } from "../../types/flow"; import { FlowType } from "../../types/flow";
import { useTable } from "../../util/hook"; import { useTable } from "../../util/hook";
import CreateAssistant from "./components/CreateAssistant"; import CreateAssistant from "./components/CreateAssistant";
import { useNavigate } from "react-router-dom"; import { useLocation, useNavigate } from "react-router-dom";
import { captureAndAlertRequestErrorHoc } from "@/controllers/request"; import { captureAndAlertRequestErrorHoc } from "@/controllers/request";
import { useToast } from "@/components/bs-ui/toast/use-toast"; import { useToast } from "@/components/bs-ui/toast/use-toast";
import { bsConfirm } from "@/components/bs-ui/alertDialog/useConfirm"; import { bsConfirm } from "@/components/bs-ui/alertDialog/useConfirm";
@@ -23,7 +23,7 @@ import sousuo from "../../assets/npc/sousuo.png";
import del from "../../assets/npc/del.png"; import del from "../../assets/npc/del.png";
import zidingyi1 from "../../assets/npc/zidingyi1.png"; import zidingyi1 from "../../assets/npc/zidingyi1.png";
import zidingyijia from "../../assets/npc/zidingyijia.png"; import zidingyijia from "../../assets/npc/zidingyijia.png";
import { useContext, useRef, useState } from "react"; import { useContext, useEffect, useRef, useState } from "react";
import { SpotlightCard } from "@lobehub/ui"; import { SpotlightCard } from "@lobehub/ui";
import { Flexbox } from 'react-layout-kit'; import { Flexbox } from 'react-layout-kit';
import { updataOnlineState } from "@/controllers/API/flow"; import { updataOnlineState } from "@/controllers/API/flow";
@@ -45,12 +45,23 @@ export default function Assistants() {
getAssistantsApi(param.page, param.pageSize, param.keyword) getAssistantsApi(param.page, param.pageSize, param.keyword)
) )
if(dataSource[0] && dataSource[0].type != 0){ if(dataSource == "" || dataSource[0].type != 0){
dataSource.unshift({"type":0}) dataSource.unshift({"type":0})
} }
const inputRef = useRef(null); const inputRef = useRef(null);
const {state} = useLocation();
useEffect(() => {
if(state){
if(state.page){
setPage(state.page)
}
if(state.value){
inputRef.current.value = state.value
search(state.value)
}
}
}, [])
const handleDelete = () => { const handleDelete = () => {
// bsConfirm({ // bsConfirm({
// desc: t('确认删除该NPC'), // desc: t('确认删除该NPC'),
@@ -147,16 +158,17 @@ export default function Assistants() {
<p>{item.desc}</p> <p>{item.desc}</p>
<div> <div>
<div> <div>
{!openSwitch && <div className="w-[80px] h-[27px] bianji mr-[14px]" onClick={() => item.status !== 1 && navigate('/assistant/' + item.id)}> {!openSwitch && <div className="w-[80px] h-[27px] bianji mr-[14px]" onClick={() => item.status !== 1 && navigate('/assistant/' + item.id,{state: { value:inputRef.current.value, page:page}})}>
<img src={bianji} className="w-[14px] mr-[10px]" alt=""/> <img src={bianji} className="w-[14px] mr-[10px]" alt=""/>
</div>} </div>}
<div className="w-[27px] h-[27px] create" onClick={() => delConfirm(item)}> {item.write && <div className="w-[27px] h-[27px] create" onClick={() => delConfirm(item)}>
<img src={del} className="w-[14px]" alt=""/> <img src={del} className="w-[14px]" alt=""/>
</div>}
</div> </div>
</div> {/* <Switch checked={openSwitch} onCheckedChange={handleChange} /> */}
<Switch checked={openSwitch} onCheckedChange={handleChange} /> <Switch checked={openSwitch} onClick={() => !item.write && item.status !== 1 && message({ title: t('prompt'), description: t('skills.contactAdmin'), variant: 'warning' })} onCheckedChange={item.write && handleChange} />
</div> </div>
</div>} </div>}
</Flexbox> </Flexbox>
@@ -169,8 +181,8 @@ export default function Assistants() {
<div className="flex gap-2 absolute top-[-47px] right-[27px] btn-r"> <div className="flex gap-2 absolute top-[-47px] right-[27px] btn-r">
<div> <div>
<img ref={inputRef} src={sousuo} alt="" /> <img src={sousuo} alt="" />
<input type="text" placeholder="搜索" onChange={(e) => search(e.target.value)} /> <input ref={inputRef} type="text" placeholder="搜索" onChange={(e) => search(e.target.value)} />
</div> </div>
</div> </div>
{/* list */} {/* list */}

View File

@@ -3,7 +3,7 @@ import { bsConfirm } from "@/components/bs-ui/alertDialog/useConfirm";
import { useToast } from "@/components/bs-ui/toast/use-toast"; import { useToast } from "@/components/bs-ui/toast/use-toast";
import { useContext, useEffect, useRef, useState } from "react"; import { useContext, useEffect, useRef, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useNavigate } from "react-router-dom"; import { useLocation, useNavigate } from "react-router-dom";
import CardComponent, { TitleIconBg } from "../../components/bs-comp/cardComponent"; import CardComponent, { TitleIconBg } from "../../components/bs-comp/cardComponent";
import { MoveOneIcon } from "../../components/bs-icons/moveOne"; import { MoveOneIcon } from "../../components/bs-icons/moveOne";
import { Button } from "../../components/bs-ui/button"; import { Button } from "../../components/bs-ui/button";
@@ -48,7 +48,7 @@ export default function Skills() {
readFlowsFromDatabase(param.page, param.pageSize, param.keyword) readFlowsFromDatabase(param.page, param.pageSize, param.keyword)
) )
if(dataSource[0] && dataSource[0].type != 0){ if(dataSource == "" || dataSource[0].type != 0){
dataSource.unshift({"type":0}) dataSource.unshift({"type":0})
} }
@@ -60,7 +60,18 @@ export default function Skills() {
const { open: tempOpen, flowRef, toggleTempModal } = useCreateTemp() const { open: tempOpen, flowRef, toggleTempModal } = useCreateTemp()
const [isShareLink, setIsShareLink] = useState(false) const [isShareLink, setIsShareLink] = useState(false)
const [isShareLinkData, setIsShareLinkData] = useState("Any"); const [isShareLinkData, setIsShareLinkData] = useState("Any");
const {state} = useLocation();
useEffect(() => {
if(state){
if(state.page){
setPage(state.page)
}
if(state.value){
search(state.value)
inputRef.current.value = state.value
}
}
}, [])
// 上下线 // 上下线
const handleCheckedChange = (checked, data) => { const handleCheckedChange = (checked, data) => {
// data.versionId todo // data.versionId todo
@@ -90,11 +101,12 @@ export default function Skills() {
const handleSetting = (data) => { const handleSetting = (data) => {
// console.log('data :>> ', data); // console.log('data :>> ', data);
const vid = data.version_list.find(item => item.is_current === 1)?.id const vid = data.version_list.find(item => item.is_current === 1)?.id
navigate(`/build/skill/${data.id}/${vid}`) navigate(`/build/skill/${data.id}/${vid}`,{state: { value:inputRef.current.value, page:page}})
} }
// 选模板(创建技能) // 选模板(创建技能)
const handldSelectTemp = async (tempId) => { const handldSelectTemp = async (tempId) => {
console.log(tempId)
const [flow] = await readTempsDatabase(tempId.id) const [flow] = await readTempsDatabase(tempId.id)
flow.name = `${flow.name}-${generateUUID(5)}` flow.name = `${flow.name}-${generateUUID(5)}`
@@ -102,13 +114,14 @@ export default function Skills() {
res.user_name = user.user_name res.user_name = user.user_name
res.write = true res.write = true
setOpen(false) setOpen(false)
navigate(`/build/skill/${res.id}/${res.version_id}`) navigate(`/build/skill/${res.id}/${res.version_id}`,{state: { avatar_color:tempId.avatar_color ? tempId.avatar_color : tempId.id, avatar_img:tempId.avatar_img}})
})) }))
} }
const nvaigate = useNavigate() const nvaigate = useNavigate()
const handleEdit = (id,pageNo) => { const handleEdit = (id,pageNo) => {
// onBeforeEdit?.() // onBeforeEdit?.()
console.log()
window.SearchSkillsPage = { no: pageNo, key: inputRef.current.value }; window.SearchSkillsPage = { no: pageNo, key: inputRef.current.value };
nvaigate("/skill/" + id) nvaigate("/skill/" + id)
} }
@@ -161,7 +174,7 @@ export default function Skills() {
<p>{item.description}</p> <p>{item.description}</p>
<div> <div>
<div> <div>
{!openSwitch && <div className="w-[80px] h-[27px] bianji mr-[14px]" onClick={() => handleEdit(item.id,page.pageNo)}> {!openSwitch && <div className="w-[80px] h-[27px] bianji mr-[14px]" onClick={() => handleSetting(item)}>
<img src={bianji} className="w-[14px] mr-[10px]" alt=""/> <img src={bianji} className="w-[14px] mr-[10px]" alt=""/>
</div>} </div>}
@@ -174,11 +187,13 @@ export default function Skills() {
<img src={share} className="w-[14px]" alt=""/> <img src={share} className="w-[14px]" alt=""/>
</div>} </div>}
<div className="w-[27px] h-[27px] create" onClick={() => delConfirm(item)}> {item.write && <div className="w-[27px] h-[27px] create" onClick={() => delConfirm(item)}>
<img src={del} className="w-[14px]" alt=""/> <img src={del} className="w-[14px]" alt=""/>
</div>}
</div> </div>
</div> {/* <Switch checked={openSwitch} onCheckedChange={handleChange} /> */}
<Switch checked={openSwitch} onCheckedChange={handleChange} /> <Switch checked={openSwitch} onClick={() => !item.write && item.status !== 2 && message({ title: t('prompt'), description: "请联系管理员上线能力", variant: 'warning' })} onCheckedChange={item.write && handleChange} />
</div> </div>
<div className="absolute right-[7px] top-[7px]"> <div className="absolute right-[7px] top-[7px]">
<CardSelectVersion <CardSelectVersion
@@ -207,7 +222,7 @@ export default function Skills() {
<img src={sousuo} alt="" /> <img src={sousuo} alt="" />
<input ref={inputRef} type="text" placeholder="搜索" onChange={(e) => search(e.target.value)} /> <input ref={inputRef} type="text" placeholder="搜索" onChange={(e) => search(e.target.value)} />
</div> </div>
{user.role === 'admin' && <div onClick={() => navigate('/build/temps')}><img src={moban} className="w-[14px] mr-[5px] btn-r-d" alt=""/><span></span></div>} {user.role === 'admin' && <div onClick={() => navigate('/build/temps',{state: { value:inputRef.current.value, page:page}})}><img src={moban} className="w-[14px] mr-[5px] btn-r-d" alt=""/><span></span></div>}
{/* {user.role === 'admin' && <div onClick={() => setIsTempPage(true)}><img src={moban} className="w-[14px] mr-[5px] btn-r-d" alt=""/><span>模版管理</span></div>} */} {/* {user.role === 'admin' && <div onClick={() => setIsTempPage(true)}><img src={moban} className="w-[14px] mr-[5px] btn-r-d" alt=""/><span>模版管理</span></div>} */}
</div> </div>
{/* list */} {/* list */}

View File

@@ -17,10 +17,13 @@ import { SpotlightCard } from "@lobehub/ui";
import { Flexbox } from 'react-layout-kit'; import { Flexbox } from 'react-layout-kit';
import robot from "../../assets/robot.png"; import robot from "../../assets/robot.png";
import del from "../../assets/npc/del.png"; import del from "../../assets/npc/del.png";
import { useNavigate } from "react-router-dom"; import { useLocation, useNavigate } from "react-router-dom";
import nengliIcon from "../../assets/npc/nengliIcon.png";
import { TitleIconBg } from "@/components/bs-comp/cardComponent";
export default function Templates() { export default function Templates() {
const navigate = useNavigate() const navigate = useNavigate()
const {state} = useLocation();
const onChange = () => { } const onChange = () => { }
const { t } = useTranslation() const { t } = useTranslation()
@@ -89,13 +92,15 @@ export default function Templates() {
<span> <span>
<span> <span>
<div> <div>
<img src={robot} className="w-[160px]" alt=""/> {/* <img src={robot} className="w-[160px]" alt=""/> */}
<TitleIconBg className="w-[160px] h-[160px]" img={item.avatar_img} id={item.avatar_color ? item.avatar_color : item.flow_id} ><img src={item.avatar_img ? item.avatar_img : nengliIcon} alt="" /></TitleIconBg>
</div> </div>
</span> </span>
</span> </span>
</div> </div>
<div> <div>
<img src={robot} className="w-[42px]" alt=""/> {/* <img src={robot} className="w-[42px]" alt=""/> */}
<TitleIconBg className="w-[40px] h-[40px] min-w-[40px]" img={item.avatar_img} id={item.avatar_color ? item.avatar_color : item.flow_id} ><img src={item.avatar_img ? item.avatar_img : nengliIcon} alt="" /></TitleIconBg>
<div> <div>
<p>{item.name}</p> <p>{item.name}</p>
<div> <div>
@@ -111,7 +116,7 @@ export default function Templates() {
</Flexbox> </Flexbox>
); );
return <div className="p-6 h-screen overflow-y-auto temps"> return <div className="p-6 h-screen overflow-y-auto temps">
<div className="fanhui" onClick={() => navigate('/build/skills')}> <div className="fanhui" onClick={() => navigate('/build/skills',{state:state})}>
</div> </div>
{/* <p className="text-gray-500">{t('skills.skillTemplateManagement')}</p> */} {/* <p className="text-gray-500">{t('skills.skillTemplateManagement')}</p> */}

View File

@@ -0,0 +1,225 @@
import { Search } from "lucide-react";
import { useContext, useEffect, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { Button } from "../../../components/ui/button";
import { Input } from "../../../components/ui/input";
import { Switch } from "../../../components/ui/switch";
import {
Table,
TableBody,
TableCell,
TableHead,
TableHeader,
TableRow
} from "../../../components/ui/table";
import { alertContext } from "../../../contexts/alertContext";
import { createRole, getRoleAssistApi, getRoleLibsApi, getRolePermissionsApi, getRoleSkillsApi, updateRoleNameApi, updateRolePermissionsApi } from "../../../controllers/API/user";
import { useDebounce } from "../../../util/hook";
import { captureAndAlertRequestErrorHoc } from "../../../controllers/request";
const pageSize = 10
const SearchPanne = ({ title, total, onChange, children }) => {
const [page, setPage] = useState(1)
const pageCount = Math.ceil(total / pageSize)
const searchKeyRef = useRef('')
const { t } = useTranslation()
const handleSearch = useDebounce((e) => {
searchKeyRef.current = e.target.value
setPage(1)
onChange(1, searchKeyRef.current)
}, 500, false)
const loadPage = (page) => {
setPage(page)
onChange(page, searchKeyRef.current)
}
return <>
<div className="mt-20 flex justify-between items-center relative">
<p className="font-bold">{title}</p>
<Input className="w-[300px] rounded-full" onChange={handleSearch}></Input>
<Search className="absolute right-2" color="#999" />
</div>
<div className="mt-4">
{children}
</div>
<div className="join grid grid-cols-2 w-[200px] mx-auto my-4">
<button disabled={page === 1} className="join-item btn btn-outline btn-xs" onClick={() => loadPage(page - 1)}>{t('previousPage')}</button>
<button disabled={page >= pageCount} className="join-item btn btn-outline btn-xs" onClick={() => loadPage(page + 1)}>{t('nextPage')}</button>
</div>
</>
}
// -1 id表示新增
export default function EditRole({ id, name, onChange, onBeforeChange }) {
const { setErrorData, setSuccessData } = useContext(alertContext);
const { t } = useTranslation()
const [form, setForm] = useState({
name,
useSkills: [],
useLibs: [],
manageLibs: []
})
useEffect(() => {
if (id !== -1) {
// 获取详情
getRolePermissionsApi(id).then(res => {
const useSkills = [], useLibs = [], manageLibs = []
res.data.forEach(item => {
switch (item.type) {
case 1: useLibs.push(Number(item.third_id)); break;
case 2: useSkills.push(item.third_id); break;
case 3: manageLibs.push(Number(item.third_id)); break;
}
})
setForm({ name, useSkills, useLibs, manageLibs })
})
}
}, [id])
const switchDataChange = (id, key, checked) => {
const index = form[key].findIndex(el => el === id)
checked && index === -1 && form[key].push(id)
!checked && index !== -1 && form[key].splice(index, 1)
setForm({ ...form, [key]: form[key] })
}
// 知识库管理权限switch
const switchLibManage = (id, checked) => {
switchDataChange(id, 'manageLibs', checked)
if (checked) switchDataChange(id, 'useLibs', checked)
}
// 知识库使用权限switch
const switchUseLib = (id, checked) => {
if (!checked && form.manageLibs.includes(id)) return
switchDataChange(id, 'useLibs', checked)
}
const { data: skillData, change: handleSkillChange } = usePageData<any>(id, 'skill')
const { data: libData, change: handleLibChange } = usePageData<any>(id, 'lib')
const handleSave = async () => {
if (!form.name.length || form.name.length > 50) {
return setErrorData({
title: t('prompt'),
list: [t('system.roleNameRequired'), t('system.roleNamePrompt')],
});
}
if (onBeforeChange(form.name)) {
return setErrorData({
title: t('prompt'),
list: [t('system.roleNameExists')]
})
}
// 新增先创建角色
let roleId = id
if (id === -1) {
const res = await captureAndAlertRequestErrorHoc(createRole(form.name))
roleId = res.id
} else {
// 更新基本信息
captureAndAlertRequestErrorHoc(updateRoleNameApi(roleId, form.name))
}
// 更新角色权限
const res = await Promise.all([
updateRolePermissionsApi({ role_id: roleId, access_id: form.useSkills, type: 2 }),
updateRolePermissionsApi({ role_id: roleId, access_id: form.useLibs, type: 1 }),
updateRolePermissionsApi({ role_id: roleId, access_id: form.manageLibs, type: 3 })
])
console.log('form :>> ', form, res);
setSuccessData({ title: t('success') })
onChange(true)
}
return <div className="max-w-[600px] mx-auto pt-4">
<div className="font-bold mt-4">
<p className="mb-4 text-[#fff]">{t('system.roleName')}</p>
<Input placeholder={t('system.roleName')} value={form.name} onChange={(e) => setForm({ ...form, name: e.target.value })} maxLength={60}></Input>
</div>
<div className="">
<SearchPanne title={t('system.skillAuthorization')} total={skillData.total} onChange={handleSkillChange}>
<Table>
<TableHeader>
<TableRow>
<TableHead className=" text-[#fff]">{t('system.skillName')}</TableHead>
<TableHead className="w-[100px] text-[#fff]">{t('system.creator')}</TableHead>
<TableHead className="text-right text-[#fff]">{t('system.usePermission')}</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{skillData.data.map((el) => (
<TableRow key={el.id}>
<TableCell className="font-medium text-[#fff]">{el.name}</TableCell>
<TableCell className=" text-[#fff]">{el.user_name}</TableCell>
<TableCell className="text-right">
<Switch checked={form.useSkills.includes(el.id)} onCheckedChange={(bln) => switchDataChange(el.id, 'useSkills', bln)} />
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</SearchPanne>
</div>
<div className="">
<SearchPanne title={t('system.knowledgeAuthorization')} total={libData.total} onChange={handleLibChange}>
<Table>
<TableHeader>
<TableRow>
<TableHead className=" text-[#fff]">{t('system.skillName')}</TableHead>
<TableHead className="w-[100px] text-[#fff]">{t('system.creator')}</TableHead>
<TableHead className="text-right text-[#fff]">{t('system.usePermission')}</TableHead>
<TableHead className="text-right text-[#fff]">{t('system.managePermission')}</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{libData.data.map((el) => (
<TableRow key={el.id}>
<TableCell className="font-medium text-[#fff]">{el.name}</TableCell>
<TableCell className=" text-[#fff]">{el.user_name}</TableCell>
<TableCell className="text-right">
<Switch checked={form.useLibs.includes(el.id)} onCheckedChange={(bln) => switchUseLib(el.id, bln)} />
</TableCell>
<TableCell className="text-right">
<Switch checked={form.manageLibs.includes(el.id)} onCheckedChange={(bln) => switchLibManage(el.id, bln)} />
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</SearchPanne>
</div>
<div className="flex justify-center gap-4 mt-16">
<Button variant="outline" className="h-8 rounded-full px-16" onClick={() => onChange()}>{t('cancel')}</Button>
<Button className="h-8 rounded-full px-16 bg-[#FFD54C] hover:bg-[#FFD54C]" onClick={handleSave}>{t('save')}</Button>
</div>
</div>
}
const usePageData = <T,>(id: number, key: 'skill' | 'lib') => {
const [data, setData] = useState<{ data: T[], total: number }>({ data: [], total: 0 })
useEffect(() => {
loadData()
}, [])
const loadData = async (page = 1, keyword = '') => {
const param = {
name: keyword,
role_id: id === -1 ? 0 : id,
page_num: page,
page_size: pageSize
}
const data = key === 'skill' ? await getRoleSkillsApi(param) : await getRoleLibsApi(param)
setData(data)
}
return {
data,
change: loadData
}
}

View File

@@ -1,8 +1,8 @@
import { Search } from "lucide-react"; import { useContext, useEffect, useState } from "react";
import { useContext, useEffect, useRef, useState } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { Button } from "../../../components/ui/button"; import { Button } from "../../../components/bs-ui/button";
import { Input } from "../../../components/ui/input"; import { Input, SearchInput } from "../../../components/bs-ui/input";
import AutoPagination from "../../../components/bs-ui/pagination/autoPagination";
import { Switch } from "../../../components/ui/switch"; import { Switch } from "../../../components/ui/switch";
import { import {
Table, Table,
@@ -11,45 +11,40 @@ import {
TableHead, TableHead,
TableHeader, TableHeader,
TableRow TableRow
} from "../../../components/ui/table"; } from "../../../components/bs-ui/table";
import { alertContext } from "../../../contexts/alertContext"; import { alertContext } from "../../../contexts/alertContext";
import { createRole, getRoleLibsApi, getRolePermissionsApi, getRoleSkillsApi, updateRoleNameApi, updateRolePermissionsApi } from "../../../controllers/API/user"; import { createRole, getRoleAssistApi, getRoleLibsApi, getRolePermissionsApi, getRoleSkillsApi, updateRoleNameApi, updateRolePermissionsApi } from "../../../controllers/API/user";
import { useDebounce } from "../../../util/hook";
import { captureAndAlertRequestErrorHoc } from "../../../controllers/request"; import { captureAndAlertRequestErrorHoc } from "../../../controllers/request";
import { useTable } from "../../../util/hook";
const pageSize = 10 const SearchPanne = ({ role_id, title, type, children }) => {
const SearchPanne = ({ title, total, onChange, children }) => { const { page, pageSize, data, total, loading, setPage, search } = useTable({ pageSize: 10 }, (params) => {
const [page, setPage] = useState(1) const { page, pageSize, keyword } = params
const pageCount = Math.ceil(total / pageSize) const param = {
const searchKeyRef = useRef('') name: keyword,
const { t } = useTranslation() role_id,
page_num: page,
const handleSearch = useDebounce((e) => { page_size: pageSize
searchKeyRef.current = e.target.value
setPage(1)
onChange(1, searchKeyRef.current)
}, 500, false)
const loadPage = (page) => {
setPage(page)
onChange(page, searchKeyRef.current)
} }
return type === 'skill' ? getRoleSkillsApi(param)
: (type === 'assistant' ? getRoleAssistApi({ ...param, type: 'assistant' })
: getRoleLibsApi(param))
})
return <> return <>
<div className="mt-20 flex justify-between items-center relative"> <div className="mt-20 flex justify-between items-center relative">
<p className="font-bold">{title}</p> <p className="font-bold text-[#fff]">{title}</p>
<Input className="w-[300px] rounded-full" onChange={handleSearch}></Input> <SearchInput onChange={(e) => search(e.target.value)}></SearchInput>
<Search className="absolute right-2" color="#999" />
</div> </div>
<div className="mt-4"> <div className="mt-4">
{children} {loading ?
<div className="w-full h-[468px] flex justify-center items-center z-10 bg-[rgba(255,255,255,0.6)] dark:bg-blur-shared">
<span className="loading loading-infinity loading-lg"></span>
</div> </div>
<div className="join grid grid-cols-2 w-[200px] mx-auto my-4"> : children(data)}
<button disabled={page === 1} className="join-item btn btn-outline btn-xs" onClick={() => loadPage(page - 1)}>{t('previousPage')}</button>
<button disabled={page >= pageCount} className="join-item btn btn-outline btn-xs" onClick={() => loadPage(page + 1)}>{t('nextPage')}</button>
</div> </div>
<AutoPagination className="m-0 mt-4 w-auto justify-end" page={page} pageSize={pageSize} total={total} onChange={setPage}></AutoPagination>
</> </>
} }
@@ -62,21 +57,23 @@ export default function EditRole({ id, name, onChange, onBeforeChange }) {
name, name,
useSkills: [], useSkills: [],
useLibs: [], useLibs: [],
useAssistant: [],
manageLibs: [] manageLibs: []
}) })
useEffect(() => { useEffect(() => {
if (id !== -1) { if (id !== -1) {
// 获取详情 // 获取详情,初始化选中数据
getRolePermissionsApi(id).then(res => { getRolePermissionsApi(id).then(res => {
const useSkills = [], useLibs = [], manageLibs = [] const useSkills = [], useLibs = [], manageLibs = [], useAssistant = []
res.data.forEach(item => { res.data.forEach(item => {
switch (item.type) { switch (item.type) {
case 1: useLibs.push(Number(item.third_id)); break; case 1: useLibs.push(Number(item.third_id)); break;
case 2: useSkills.push(item.third_id); break; case 2: useSkills.push(item.third_id); break;
case 3: manageLibs.push(Number(item.third_id)); break; case 3: manageLibs.push(Number(item.third_id)); break;
case 5: useAssistant.push(item.third_id); break;
} }
}) })
setForm({ name, useSkills, useLibs, manageLibs }) setForm({ name, useSkills, useLibs, useAssistant, manageLibs })
}) })
} }
}, [id]) }, [id])
@@ -98,10 +95,14 @@ export default function EditRole({ id, name, onChange, onBeforeChange }) {
if (!checked && form.manageLibs.includes(id)) return if (!checked && form.manageLibs.includes(id)) return
switchDataChange(id, 'useLibs', checked) switchDataChange(id, 'useLibs', checked)
} }
/**
const { data: skillData, change: handleSkillChange } = usePageData<any>(id, 'skill') * 保存权限信息
const { data: libData, change: handleLibChange } = usePageData<any>(id, 'lib') * 1.验证重名
* 2.新增时先保存基本信息 创建 ID
* 3.修改时先更新基本信息
* 4.批量 保存各个种类权限信息(助手、技能、知识库等)
* @returns
*/
const handleSave = async () => { const handleSave = async () => {
if (!form.name.length || form.name.length > 50) { if (!form.name.length || form.name.length > 50) {
return setErrorData({ return setErrorData({
@@ -115,7 +116,7 @@ export default function EditRole({ id, name, onChange, onBeforeChange }) {
list: [t('system.roleNameExists')] list: [t('system.roleNameExists')]
}) })
} }
// 新增先创建角色 // 没有id时需要走创建流程否则修改
let roleId = id let roleId = id
if (id === -1) { if (id === -1) {
const res = await captureAndAlertRequestErrorHoc(createRole(form.name)) const res = await captureAndAlertRequestErrorHoc(createRole(form.name))
@@ -128,7 +129,8 @@ export default function EditRole({ id, name, onChange, onBeforeChange }) {
const res = await Promise.all([ const res = await Promise.all([
updateRolePermissionsApi({ role_id: roleId, access_id: form.useSkills, type: 2 }), updateRolePermissionsApi({ role_id: roleId, access_id: form.useSkills, type: 2 }),
updateRolePermissionsApi({ role_id: roleId, access_id: form.useLibs, type: 1 }), updateRolePermissionsApi({ role_id: roleId, access_id: form.useLibs, type: 1 }),
updateRolePermissionsApi({ role_id: roleId, access_id: form.manageLibs, type: 3 }) updateRolePermissionsApi({ role_id: roleId, access_id: form.manageLibs, type: 3 }),
updateRolePermissionsApi({ role_id: roleId, access_id: form.useAssistant, type: 5 })
]) ])
console.log('form :>> ', form, res); console.log('form :>> ', form, res);
@@ -136,26 +138,57 @@ export default function EditRole({ id, name, onChange, onBeforeChange }) {
onChange(true) onChange(true)
} }
return <div className="max-w-[600px] mx-auto pt-4"> const roleId = id === -1 ? 0 : id
return <div className="max-w-[600px] mx-auto pt-4 h-[calc(100vh-136px)] overflow-y-auto pb-10 px-[3px] scrollbar-hide">
<div className="font-bold mt-4"> <div className="font-bold mt-4">
<p className="mb-4 text-[#fff]">{t('system.roleName')}</p> <p className="mb-4 text-[#fff]">{t('system.roleName')}</p>
<Input placeholder={t('system.roleName')} value={form.name} onChange={(e) => setForm({ ...form, name: e.target.value })} maxLength={60}></Input> <Input className="npcInput3" placeholder={t('system.roleName')} value={form.name} onChange={(e) => setForm({ ...form, name: e.target.value })} maxLength={60}></Input>
</div> </div>
{/* 助手 */}
<div className=""> <div className="">
<SearchPanne title={t('system.skillAuthorization')} total={skillData.total} onChange={handleSkillChange}> <SearchPanne title={"NPC授权"} role_id={roleId} type={'assistant'}>
{(data) => (
<Table> <Table>
<TableHeader> <TableHeader>
<TableRow> <TableRow>
<TableHead className=" text-[#fff]">{t('system.skillName')}</TableHead> <TableHead className="text-[#fff]">NPC名称</TableHead>
<TableHead className="w-[100px] text-[#fff]">{t('system.creator')}</TableHead> <TableHead className="w-[100px] text-[#fff]">{t('system.creator')}</TableHead>
<TableHead className="text-right text-[#fff]">{t('system.usePermission')}</TableHead> <TableHead className="text-right text-[#fff]">{t('system.usePermission')}</TableHead>
</TableRow> </TableRow>
</TableHeader> </TableHeader>
<TableBody> <TableBody>
{skillData.data.map((el) => ( {data.map((el) => (
<TableRow key={el.id}> <TableRow key={el.id}>
<TableCell className="font-medium text-[#fff]">{el.name}</TableCell> <TableCell className="font-medium text-[#fff]">{el.name}</TableCell>
<TableCell className=" text-[#fff]">{el.user_name}</TableCell> <TableCell className="text-[#fff]">{el.user_name}</TableCell>
<TableCell className="text-right">
<Switch checked={form.useAssistant.includes(el.id)} onCheckedChange={(bln) => switchDataChange(el.id, 'useAssistant', bln)} />
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
)}
</SearchPanne>
</div>
{/* 技能 */}
<div className="">
<SearchPanne title={t('system.skillAuthorization')} role_id={roleId} type={'skill'}>
{(data) => (
<Table>
<TableHeader>
<TableRow>
<TableHead className="text-[#fff]">{t('system.skillName')}</TableHead>
<TableHead className="w-[100px] text-[#fff]">{t('system.creator')}</TableHead>
<TableHead className="text-right text-[#fff]">{t('system.usePermission')}</TableHead>
</TableRow>
</TableHeader>
<TableBody>
{data.map((el) => (
<TableRow key={el.id}>
<TableCell className="font-medium text-[#fff]">{el.name}</TableCell>
<TableCell className="text-[#fff]">{el.user_name}</TableCell>
<TableCell className="text-right"> <TableCell className="text-right">
<Switch checked={form.useSkills.includes(el.id)} onCheckedChange={(bln) => switchDataChange(el.id, 'useSkills', bln)} /> <Switch checked={form.useSkills.includes(el.id)} onCheckedChange={(bln) => switchDataChange(el.id, 'useSkills', bln)} />
</TableCell> </TableCell>
@@ -163,24 +196,27 @@ export default function EditRole({ id, name, onChange, onBeforeChange }) {
))} ))}
</TableBody> </TableBody>
</Table> </Table>
)}
</SearchPanne> </SearchPanne>
</div> </div>
{/* 知识库 */}
<div className=""> <div className="">
<SearchPanne title={t('system.knowledgeAuthorization')} total={libData.total} onChange={handleLibChange}> <SearchPanne title={t('system.knowledgeAuthorization')} role_id={roleId} type={'lib'}>
{(data) => (
<Table> <Table>
<TableHeader> <TableHeader>
<TableRow> <TableRow>
<TableHead className=" text-[#fff]">{t('system.skillName')}</TableHead> <TableHead className="text-[#fff]">{t('lib.libraryName')}</TableHead>
<TableHead className="w-[100px] text-[#fff]">{t('system.creator')}</TableHead> <TableHead className="w-[100px] text-[#fff]">{t('system.creator')}</TableHead>
<TableHead className="text-right text-[#fff]">{t('system.usePermission')}</TableHead> <TableHead className="text-right text-[#fff]">{t('system.usePermission')}</TableHead>
<TableHead className="text-right text-[#fff]">{t('system.managePermission')}</TableHead> <TableHead className="text-right text-[#fff]">{t('system.managePermission')}</TableHead>
</TableRow> </TableRow>
</TableHeader> </TableHeader>
<TableBody> <TableBody>
{libData.data.map((el) => ( {data.map((el) => (
<TableRow key={el.id}> <TableRow key={el.id}>
<TableCell className="font-medium text-[#fff]">{el.name}</TableCell> <TableCell className="font-medium text-[#fff]">{el.name}</TableCell>
<TableCell className=" text-[#fff]">{el.user_name}</TableCell> <TableCell className="text-[#fff]">{el.user_name}</TableCell>
<TableCell className="text-right"> <TableCell className="text-right">
<Switch checked={form.useLibs.includes(el.id)} onCheckedChange={(bln) => switchUseLib(el.id, bln)} /> <Switch checked={form.useLibs.includes(el.id)} onCheckedChange={(bln) => switchUseLib(el.id, bln)} />
</TableCell> </TableCell>
@@ -191,6 +227,7 @@ export default function EditRole({ id, name, onChange, onBeforeChange }) {
))} ))}
</TableBody> </TableBody>
</Table> </Table>
)}
</SearchPanne> </SearchPanne>
</div> </div>
<div className="flex justify-center gap-4 mt-16"> <div className="flex justify-center gap-4 mt-16">
@@ -199,27 +236,3 @@ export default function EditRole({ id, name, onChange, onBeforeChange }) {
</div> </div>
</div> </div>
} }
const usePageData = <T,>(id: number, key: 'skill' | 'lib') => {
const [data, setData] = useState<{ data: T[], total: number }>({ data: [], total: 0 })
useEffect(() => {
loadData()
}, [])
const loadData = async (page = 1, keyword = '') => {
const param = {
name: keyword,
role_id: id === -1 ? 0 : id,
page_num: page,
page_size: pageSize
}
const data = key === 'skill' ? await getRoleSkillsApi(param) : await getRoleLibsApi(param)
setData(data)
}
return {
data,
change: loadData
}
}

View File

@@ -90,7 +90,7 @@ export default function Roles() {
<TableCell className="font-medium dialogueLog-body">{el.role_name}</TableCell> <TableCell className="font-medium dialogueLog-body">{el.role_name}</TableCell>
<TableCell className="dialogueLog-body">{el.create_time.replace('T', ' ')}</TableCell> <TableCell className="dialogueLog-body">{el.create_time.replace('T', ' ')}</TableCell>
<TableCell className="text-right dialogueLog-body"> <TableCell className="text-right dialogueLog-body">
<Button variant="link" disabled={[1, 2].includes(el.id)} onClick={() => setRole(el)}>{t('edit')}</Button> <Button variant="link" onClick={() => setRole(el)}>{t('edit')}</Button>
<Button variant="link" disabled={[1, 2].includes(el.id)} onClick={() => handleDelete(el)} className="text-red-500">{t('delete')}</Button> <Button variant="link" disabled={[1, 2].includes(el.id)} onClick={() => handleDelete(el)} className="text-red-500">{t('delete')}</Button>
</TableCell> </TableCell>
</TableRow> </TableRow>

View File

@@ -207,7 +207,7 @@
@apply focus:placeholder-transparent focus:ring-ring focus:border-ring @apply focus:placeholder-transparent focus:ring-ring focus:border-ring
} }
.input-primary { .input-primary {
@apply bg-background block border-border form-input px-3 placeholder:text-muted-foreground rounded-md shadow-sm sm:text-sm truncate w-full; @apply bg-background block border-border form-input px-3 placeholder:text-[#999] rounded-md shadow-sm sm:text-sm truncate w-full;
} }
.input-edit-node{ .input-edit-node{

View File

@@ -204,6 +204,9 @@
width: 100%!important; width: 100%!important;
height: 100%; height: 100%;
} }
.chata{
height: 96vh!important;
}
} }
.chatShareM{ .chatShareM{
.duihua-chat{ .duihua-chat{
@@ -3180,7 +3183,7 @@
justify-content: center; justify-content: center;
background: #0D0D0D!important; background: #0D0D0D!important;
cursor: pointer; cursor: pointer;
z-index: 1; z-index: 10;
>span{ >span{
font-family: PingFang SC; font-family: PingFang SC;
font-weight: 400; font-weight: 400;
@@ -3216,12 +3219,16 @@
background: #1A1A1A; background: #1A1A1A;
border-radius: 7px; border-radius: 7px;
margin: 7px; margin: 7px;
position: relative;
cursor: pointer; cursor: pointer;
.npcInfoItemBg{ .npcInfoItemBg{
position: relative; position: absolute;
overflow: hidden; overflow: hidden;
width: 100%;
height: 64px; height: 64px;
margin-bottom: -56px; top: 0;
left: 0;
// margin-bottom: -56px;
background: rgba(0, 0, 0, 0.06); background: rgba(0, 0, 0, 0.06);
-webkit-mask-image: linear-gradient(to bottom, #fff, transparent); -webkit-mask-image: linear-gradient(to bottom, #fff, transparent);
mask-image: linear-gradient(to bottom, #fff, transparent); mask-image: linear-gradient(to bottom, #fff, transparent);
@@ -3240,6 +3247,7 @@
-webkit-box-align: center; -webkit-box-align: center;
-ms-flex-align: center; -ms-flex-align: center;
align-items: center; align-items: center;
z-index: -1;
>span{ >span{
width: 180px; width: 180px;
height: 180px; height: 180px;
@@ -3312,6 +3320,7 @@
} }
>div:nth-of-type(2){ >div:nth-of-type(2){
margin-right: 14px; margin-right: 14px;
z-index: 1000;
} }
} }
>p{ >p{
@@ -4177,3 +4186,32 @@
.input-dialog{ .input-dialog{
color: #999999; color: #999999;
} }
.daimaStyle{
background: rgba(255, 255, 255, 0.05);
border-radius: 14px!important;
border: none;
overflow: hidden;
margin: 6px 0;
&:focus{
// border: 1px solid #997e1f!important;
border: none!important;
outline: none!important;
}
.code-block-modal{
background: rgba(255, 213, 76, 0.04);
}
.overflow-auto{
background: rgba(255, 255, 255, 0.05)!important;
background-color: rgba(255, 255, 255, 0.05)!important;
.language-sql{
background-color: rgba(255, 255, 255, 0.00)!important;
// background: rgba(255, 255, 255, 0.05)!important;
// background: none;
span{
background: none;
// background: rgba(255, 255, 255, 0.05)!important;
}
}
}
}

View File

@@ -71,7 +71,7 @@ export function useTable<T extends object>(param, apiFun) {
const requestIdRef = useRef(0); // 控制请求响应顺序 const requestIdRef = useRef(0); // 控制请求响应顺序
const loadData = () => { const loadData = () => {
setLoading(true); // setLoading(true);
const requestId = ++requestIdRef.current const requestId = ++requestIdRef.current
apiFun({ ...page, ...paramRef.current }).then(res => { apiFun({ ...page, ...paramRef.current }).then(res => {
if (requestId !== requestIdRef.current) return if (requestId !== requestIdRef.current) return
@@ -79,9 +79,9 @@ export function useTable<T extends object>(param, apiFun) {
// res.data.unshift({type:0}) // res.data.unshift({type:0})
setData(res.data); setData(res.data);
setTotal(res.total); setTotal(res.total);
setLoading(false); // setLoading(false);
}).catch(() => { }).catch(() => {
setLoading(false); // setLoading(false);
}) })
} }
const debounceLoad = useDebounce(loadData, 600, false) const debounceLoad = useDebounce(loadData, 600, false)