This commit is contained in:
‘Liammcl’
2025-01-14 22:33:10 +08:00
parent 7e592df34e
commit e4aa910d4e
11 changed files with 77 additions and 60 deletions

View File

@@ -2,13 +2,10 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8" /> <meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" /> <link rel="icon" type="image/svg+xml" href="/minilogo.png" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /> <meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Uppeta</title> <title>Limq</title>
<!-- Material Icons -->
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Rounded:opsz,wght,FILL,GRAD@20..48,100..700,0..1,-50..200" rel="stylesheet">
<!-- Poppins Font -->
<link href="https://fonts.googleapis.com/css2?family=Poppins:wght@400;500;600;700&display=swap" rel="stylesheet">
</head> </head>
<body> <body>
<div id="root"></div> <div id="root"></div>

View File

Before

Width:  |  Height:  |  Size: 2.9 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

BIN
src/assets/minilogo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@@ -8,11 +8,11 @@ import Editor from "@monaco-editor/react";
export default function ChatAIDrawer({ open, onClose, onExport }) { export default function ChatAIDrawer({ open, onClose, onExport }) {
const STORAGE_KEY = 'chat_history'; const STORAGE_KEY = 'chat_history';
const [storedMessages, setStoredMessages] = useSessionStorage(STORAGE_KEY, '[]'); const [storedMessages, setStoredMessages] = useSessionStorage(STORAGE_KEY, []);
const { messages, input, handleSubmit, handleInputChange, isLoading, setMessages } = useChat({ const { messages, input, handleSubmit, handleInputChange, isLoading, setMessages } = useChat({
api: "https://test-ai-quirkyai.vercel.app/api/chat", api: "https://test-ai-quirkyai.vercel.app/api/chat",
initialMessages: JSON.parse(storedMessages), initialMessages: storedMessages.length>0? JSON.parse(storedMessages):[],
}); });
const messagesEndRef = useRef(null); const messagesEndRef = useRef(null);
@@ -88,7 +88,6 @@ export default function ChatAIDrawer({ open, onClose, onExport }) {
width={800} width={800}
open={open} open={open}
onClose={onClose} onClose={onClose}
className="rounded-l-xl"
> >
<div className="flex flex-col h-[calc(100vh-108px)]"> <div className="flex flex-col h-[calc(100vh-108px)]">
<div className="flex-1 overflow-y-auto px-4 space-y-6"> <div className="flex-1 overflow-y-auto px-4 space-y-6">

View File

@@ -1,7 +1,7 @@
import React from "react"; import React from "react";
import logo from "@/assets/logo.png"; import logo from "@/assets/logo.png";
import logoCollapsed from "@/assets/logo-collapsed.png"; import logoCollapsed from "@/assets/minilogo.png";
export const Logo = ({ collapsed, isDarkMode }) => ( export const Logo = ({ collapsed, isDarkMode }) => (
<div className="logo"> <div className="logo">

View File

@@ -3,6 +3,8 @@ import { CodeHighlight } from "@mantine/code-highlight";
import { DownloadOutlined, EditOutlined, CheckOutlined, CloseOutlined } from '@ant-design/icons'; import { DownloadOutlined, EditOutlined, CheckOutlined, CloseOutlined } from '@ant-design/icons';
import { useRef, useEffect, useState } from 'react'; import { useRef, useEffect, useState } from 'react';
import { useDifyChat } from '@/hooks/aichat'; import { useDifyChat } from '@/hooks/aichat';
import Editor from "@monaco-editor/react";
import {useTheme}from '@/contexts/ThemeContext'
export default function DifyChatDrawer({ open, onClose, onExport }) { export default function DifyChatDrawer({ open, onClose, onExport }) {
const { const {
@@ -14,7 +16,7 @@ export default function DifyChatDrawer({ open, onClose, onExport }) {
storedMessages, storedMessages,
setStoredMessages setStoredMessages
} = useDifyChat(); } = useDifyChat();
const {isDarkMode}=useTheme()
const [input, setInput] = useState(''); const [input, setInput] = useState('');
const messagesEndRef = useRef(null); const messagesEndRef = useRef(null);
@@ -25,6 +27,20 @@ export default function DifyChatDrawer({ open, onClose, onExport }) {
messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' }); messagesEndRef.current?.scrollIntoView({ behavior: 'smooth' });
}, [messages]); }, [messages]);
useEffect(() => {
if (storedMessages && storedMessages.length > 0) {
console.log(storedMessages,'storedMessages');
setMessages(storedMessages);
}
}, []);
useEffect(() => {
if (messages.length > 0) {
setStoredMessages(messages);
}
}, [messages]);
const handleSendMessage = async (e) => { const handleSendMessage = async (e) => {
e.preventDefault(); e.preventDefault();
await sendMessage(input); await sendMessage(input);
@@ -67,15 +83,20 @@ export default function DifyChatDrawer({ open, onClose, onExport }) {
} }
}; };
const handleClearHistory = () => {
clearHistory();
setStoredMessages([]);
};
return ( return (
<Drawer <Drawer
title={ title={
<div className="flex justify-between items-center"> <div className="flex justify-between items-center">
<span className="text-lg font-medium text-gray-800">AI 助手</span> <span className="text-lg font-medium dark:text-gray-200">AI 助手</span>
<Button <Button
size="small" size="small"
className="hover:bg-gray-100" className="hover:bg-gray-100 dark:hover:bg-gray-700"
onClick={clearHistory} onClick={handleClearHistory}
> >
清空历史 清空历史
</Button> </Button>
@@ -85,7 +106,6 @@ export default function DifyChatDrawer({ open, onClose, onExport }) {
width={800} width={800}
open={open} open={open}
onClose={onClose} onClose={onClose}
className="rounded-l-xl"
> >
<div className="flex flex-col h-[calc(100vh-108px)]"> <div className="flex flex-col h-[calc(100vh-108px)]">
<div className="flex-1 overflow-y-auto px-4 space-y-6"> <div className="flex-1 overflow-y-auto px-4 space-y-6">
@@ -94,13 +114,15 @@ export default function DifyChatDrawer({ open, onClose, onExport }) {
key={message.id} key={message.id}
className={`rounded-lg p-4 transition-all ${ className={`rounded-lg p-4 transition-all ${
message.role === 'assistant' message.role === 'assistant'
? 'bg-blue-50 hover:bg-blue-100' ? 'bg-blue-50 hover:bg-blue-100 dark:bg-blue-900/30 dark:hover:bg-blue-900/50'
: 'bg-gray-50 hover:bg-gray-100' : 'bg-gray-50 hover:bg-gray-100 dark:bg-gray-800 dark:hover:bg-gray-700'
}`} }`}
> >
<div className="flex justify-between items-center mb-3"> <div className="flex justify-between items-center mb-3">
<span className={`font-medium ${ <span className={`font-medium ${
message.role === 'assistant' ? 'text-blue-600' : 'text-gray-600' message.role === 'assistant'
? 'text-blue-600 dark:text-blue-400'
: 'text-gray-600 dark:text-gray-300'
}`}> }`}>
{message.role === 'assistant' ? 'AI 助手' : '用户'} {message.role === 'assistant' ? 'AI 助手' : '用户'}
</span> </span>
@@ -112,7 +134,7 @@ export default function DifyChatDrawer({ open, onClose, onExport }) {
icon={<EditOutlined />} icon={<EditOutlined />}
disabled={isLoading} disabled={isLoading}
onClick={() => handleEdit(message)} onClick={() => handleEdit(message)}
className="text-gray-500 hover:text-blue-600" className="text-gray-500 hover:text-blue-600 dark:text-gray-400 dark:hover:text-blue-400"
> >
编辑 编辑
</Button> </Button>
@@ -121,7 +143,7 @@ export default function DifyChatDrawer({ open, onClose, onExport }) {
size="small" size="small"
icon={<DownloadOutlined />} icon={<DownloadOutlined />}
onClick={() => handleExport(message.content)} onClick={() => handleExport(message.content)}
className="text-gray-500 hover:text-blue-600" className="text-gray-500 hover:text-blue-600 dark:text-gray-400 dark:hover:text-blue-400"
> >
导出 导出
</Button> </Button>
@@ -132,12 +154,12 @@ export default function DifyChatDrawer({ open, onClose, onExport }) {
{message.role === "assistant" ? ( {message.role === "assistant" ? (
<div className="relative"> <div className="relative">
{editingMessageId === message.id ? ( {editingMessageId === message.id ? (
<div className="rounded-lg border border-blue-200"> <div className="rounded-lg border border-blue-200 dark:border-blue-700">
<Editor <Editor
height="300px" height="300px"
defaultLanguage="json" defaultLanguage="json"
value={editingContent} value={editingContent}
theme="vs-light" theme={isDarkMode ? "vs-dark" : "vs-light"}
options={{ options={{
minimap: { enabled: false }, minimap: { enabled: false },
scrollBeyondLastLine: false, scrollBeyondLastLine: false,
@@ -152,12 +174,12 @@ export default function DifyChatDrawer({ open, onClose, onExport }) {
editor.focus(); editor.focus();
}} }}
/> />
<div className="flex justify-end gap-2 p-2 bg-gray-50 border-t"> <div className="flex justify-end gap-2 p-2 bg-gray-50 dark:bg-gray-800 border-t dark:border-gray-700">
<Button <Button
size="small" size="small"
icon={<CloseOutlined />} icon={<CloseOutlined />}
onClick={handleCancelEdit} onClick={handleCancelEdit}
className="hover:bg-gray-200" className="hover:bg-gray-200 dark:hover:bg-gray-700"
> >
取消 取消
</Button> </Button>
@@ -166,7 +188,7 @@ export default function DifyChatDrawer({ open, onClose, onExport }) {
size="small" size="small"
icon={<CheckOutlined />} icon={<CheckOutlined />}
onClick={handleSaveEdit} onClick={handleSaveEdit}
className="bg-blue-600 hover:bg-blue-700" className="bg-blue-600 hover:bg-blue-700 dark:bg-blue-700 dark:hover:bg-blue-800"
> >
保存 保存
</Button> </Button>
@@ -179,12 +201,12 @@ export default function DifyChatDrawer({ open, onClose, onExport }) {
copyLabel="复制代码" copyLabel="复制代码"
copiedLabel="已复制!" copiedLabel="已复制!"
withLineNumbers withLineNumbers
className="rounded-lg" className="rounded-lg dark:border-gray-700"
/> />
)} )}
</div> </div>
) : ( ) : (
<div className="text-gray-700 whitespace-pre-wrap break-words"> <div className="text-gray-700 dark:text-gray-300 whitespace-pre-wrap break-words">
{message.content} {message.content}
</div> </div>
)} )}
@@ -193,20 +215,20 @@ export default function DifyChatDrawer({ open, onClose, onExport }) {
<div ref={messagesEndRef} /> <div ref={messagesEndRef} />
</div> </div>
<div className="border-t bg-white p-4"> <div className="border-t dark:border-gray-700 bg-white dark:bg-gray-800 p-4">
<form onSubmit={handleSendMessage} className="flex gap-2"> <form onSubmit={handleSendMessage} className="flex gap-2">
<Input <Input
value={input} value={input}
placeholder="请输入您的问题..." placeholder="请输入您的问题..."
onChange={(e) => setInput(e.target.value)} onChange={(e) => setInput(e.target.value)}
disabled={isLoading} disabled={isLoading}
className="flex-1 rounded-lg border-gray-300 hover:border-blue-400 focus:border-blue-600 focus:shadow-blue-100" className="flex-1 rounded-lg border-gray-300 dark:border-gray-600 hover:border-blue-400 dark:hover:border-blue-500 focus:border-blue-600 dark:focus:border-blue-500 focus:shadow-blue-100 dark:focus:shadow-blue-900 dark:bg-gray-700 dark:text-gray-200"
/> />
<Button <Button
type="primary" type="primary"
htmlType="submit" htmlType="submit"
loading={isLoading} loading={isLoading}
className="rounded-lg bg-blue-600 hover:bg-blue-700" className="rounded-lg bg-blue-600 hover:bg-blue-700 dark:bg-blue-700 dark:hover:bg-blue-800"
> >
发送 发送
</Button> </Button>

View File

@@ -9,7 +9,7 @@ const STORAGE_KEY = 'dify_chat_history';
const CONVERSATION_ID_KEY = 'dify_conversation_id'; const CONVERSATION_ID_KEY = 'dify_conversation_id';
export function useDifyChat() { export function useDifyChat() {
const [storedMessages, setStoredMessages] = useSessionStorage(STORAGE_KEY, '[]'); const [storedMessages, setStoredMessages] = useSessionStorage(STORAGE_KEY, []);
const [conversationId, setConversationId] = useSessionStorage(CONVERSATION_ID_KEY, ''); const [conversationId, setConversationId] = useSessionStorage(CONVERSATION_ID_KEY, '');
const [messages, setMessages] = useState([]); const [messages, setMessages] = useState([]);
const [isLoading, setIsLoading] = useState(false); const [isLoading, setIsLoading] = useState(false);
@@ -18,7 +18,7 @@ export function useDifyChat() {
const clearHistory = () => { const clearHistory = () => {
setMessages([]); setMessages([]);
setStoredMessages('[]'); setStoredMessages([]);
setConversationId(''); setConversationId('');
message.success('历史记录已清空'); message.success('历史记录已清空');
}; };

View File

@@ -201,7 +201,7 @@ export default function MenuManagement() {
}, []); }, []);
return ( return (
<div className="bg-gradient-to-b from-gray-50 to-white min-h-screen p-2"> <div className="bg-gradient-to-b min-h-screen p-2">
<Card className="shadow-lg rounded-lg"> <Card className="shadow-lg rounded-lg">
<div className="flex justify-between items-center mb-4"> <div className="flex justify-between items-center mb-4">
<Title level={4} className="mb-0">菜单权限管理</Title> <Title level={4} className="mb-0">菜单权限管理</Title>

View File

@@ -89,10 +89,7 @@ const AppRoutes = () => {
index index
element={<Navigate to="/company/serviceTemplate" replace />} element={<Navigate to="/company/serviceTemplate" replace />}
/> />
{/* 渲染所有路由 */}
{renderRoutes(allRoutes)} {renderRoutes(allRoutes)}
<Route path="*" element={<NotFound />} /> <Route path="*" element={<NotFound />} />
</Route> </Route>
</Routes> </Routes>

View File

@@ -112,27 +112,27 @@ body {
.dark { .dark {
@apply bg-gray-900; @apply bg-gray-900;
.ant-card { // .ant-card {
background: #1f1f1f; // background: #1f1f1f;
.ant-card-head { // .ant-card-head {
color: rgba(255, 255, 255, 0.85); // color: rgba(255, 255, 255, 0.85);
border-bottom-color: #303030; // border-bottom-color: #303030;
} // }
} // }
.ant-table { // .ant-table {
background: #1f1f1f; // background: #1f1f1f;
.ant-table-thead>tr>th { // .ant-table-thead>tr>th {
background: #141414; // background: #141414;
color: rgba(255, 255, 255, 0.85); // color: rgba(255, 255, 255, 0.85);
} // }
.ant-table-tbody>tr>td { // .ant-table-tbody>tr>td {
border-bottom: 1px solid #303030; // border-bottom: 1px solid #303030;
} // }
} // }
} }
// Statistics card styles // Statistics card styles

View File

@@ -13,15 +13,21 @@ const getAntIcon = (iconName) => {
const generateMenuItems = (routes, menuKeys = [], parentPath = "") => { const generateMenuItems = (routes, menuKeys = [], parentPath = "") => {
return routes return routes
.filter((route) => { .filter((route) => {
if (!menuKeys.length) return !route.hidden; if (route.hidden) return false;
if (!menuKeys.length) return true;
const isRouteAllowed = menuKeys.includes(route.key); const isRouteAllowed = menuKeys.includes(route.key);
// 如果有子路由,只要子路由中有被授权的,父路由就应该显示 // 如果有子路由,只要子路由中有被授权的,父路由就应该显示
if (route.children) { if (route.children) {
const hasAllowedChildren = route.children.some(child => const hasAllowedChildren = route.children.some(child =>
!child.hidden && (
menuKeys.includes(child.key) || menuKeys.includes(child.key) ||
(child.children && child.children.some(grandChild => menuKeys.includes(grandChild.key))) (child.children && child.children.some(grandChild =>
!grandChild.hidden && menuKeys.includes(grandChild.key)
))
)
); );
return hasAllowedChildren || isRouteAllowed; return hasAllowedChildren || isRouteAllowed;
} }
@@ -56,16 +62,12 @@ export const filterRoutesByMenuKeys = (routes, menuKeys) => {
return []; return [];
} }
return routes.reduce((acc, route) => { return routes.reduce((acc, route) => {
// 检查当前路由的 key 是否在 menuKeys 中
const isRouteAllowed = menuKeys.includes(route.key); const isRouteAllowed = menuKeys.includes(route.key);
// 递归处理子路由
let filteredChildren = []; let filteredChildren = [];
if (route.children) { if (route.children) {
filteredChildren = filterRoutesByMenuKeys(route.children, menuKeys); filteredChildren = filterRoutesByMenuKeys(route.children, menuKeys);
} }
// 如果当前路由被允许或者有被允许的子路由,则保留该路由
if (isRouteAllowed || filteredChildren.length > 0) { if (isRouteAllowed || filteredChildren.length > 0) {
acc.push({ acc.push({
...route, ...route,