Skip to content

Commit

Permalink
feat: 溯源支持mobile
Browse files Browse the repository at this point in the history
  • Loading branch information
dolphin0618 committed Jul 9, 2024
1 parent d0ed84b commit feb5e5e
Show file tree
Hide file tree
Showing 4 changed files with 132 additions and 74 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export default function SourceEntry({ extra, end, source, className = '', onSour
{(() => {
switch (source) {
case SourceType.FILE:
return <Badge className="cursor-pointer hidden md:block" onClick={onSource}>{t('chat.source')}</Badge>;
return <Badge className="cursor-pointer" onClick={onSource}>{t('chat.source')}</Badge>;
case SourceType.NO_PERMISSION:
return <p className="flex text-xs text-gray-400 gap-1 items-center"><InfoCircledIcon className="text-red-300" />{t('chat.noAccess')}</p>;
case SourceType.LINK:
Expand Down
15 changes: 11 additions & 4 deletions src/frontend/src/contexts/locationContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ type locationContextType = {
extraComponent: any;
setExtraComponent: (newState: any) => void;
appConfig: any;
reloadConfig: () => void
};

//initial value for location context
Expand All @@ -48,7 +49,8 @@ const initialValue = {
setExtraNavigation: () => { },
extraComponent: <></>,
setExtraComponent: () => { },
appConfig: { libAccepts: [] }
appConfig: { libAccepts: [] },
reloadConfig: () => { }
};

export const locationContext = createContext<locationContextType>(initialValue);
Expand All @@ -65,8 +67,7 @@ export function LocationProvider({ children }: { children: ReactNode }) {
libAccepts: []
})

// 获取系统配置
useEffect(() => {
const loadConfig = () => {
getAppConfig().then(res => {
setAppConfig({
isDev: res.env === 'dev',
Expand All @@ -80,6 +81,11 @@ export function LocationProvider({ children }: { children: ReactNode }) {
chatPrompt: !!res.application_usage_tips
})
})
}

// 获取系统配置
useEffect(() => {
loadConfig()
}, [])

return (
Expand All @@ -95,7 +101,8 @@ export function LocationProvider({ children }: { children: ReactNode }) {
setExtraNavigation,
extraComponent,
setExtraComponent,
appConfig
appConfig,
reloadConfig: loadConfig
}}
>
{children}
Expand Down
166 changes: 103 additions & 63 deletions src/frontend/src/pages/ChatAppPage/components/ResouceModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,36 @@ const Anwser = ({ id, msg, onInit, onAdd }) => {

//
let timer = null
const ResultPanne = ({ chatId, words, data, onClose, onAdd, children }: { chatId: string, words: string[], data: any, onClose: any, onAdd: any, children: any }) => {
const ResultPanne = ({ chatId, words, data, onClose, onAdd, children, closeDialog }: { chatId: string, words: string[], data: any, onClose: any, onAdd: any, children: any, closeDialog: () => void }) => {
const { t } = useTranslation()
const [editCustomKey, setEditCustomKey] = useState(false)
const inputRef = useRef(null)

// 移动端
const [collapse, setCollapse] = useState(true)
const [isMobile, setIsMobile] = useState(true)
const [width, setWidth] = useState(window.innerWidth);
const [height, setHeight] = useState(window.innerHeight);
const checkIsMobile = () => {
if (width < 640) {
setIsMobile(true)
} else {
setIsMobile(false)
}
}
useEffect(() => {
const handleResize = () => {
setWidth(window.innerWidth);
setHeight(window.innerHeight);
};
window.addEventListener("resize", handleResize);
checkIsMobile()
return () => {
window.removeEventListener("resize", handleResize);
}
}, [width])
// 移动端 e

const handleAddKeyword = (str: string) => {
setEditCustomKey(false)
if (!str) return
Expand Down Expand Up @@ -88,71 +113,86 @@ const ResultPanne = ({ chatId, words, data, onClose, onAdd, children }: { chatId
setTimeout(() => document.getElementById('taginput')?.focus(), 0);
}

return <div className="flex gap-4 mt-4" style={{ height: 'calc(100vh - 10rem)' }}>
{/* left */}
<div className="w-[300px] bg-gray-100 dark:bg-[#3C4048] rounded-md py-4 px-2 h-full overflow-y-auto no-scrollbar">
{/* label */}
{/* 中英 */}
<div className="mb-4 text-sm font-bold flex place-items-center space-x-1">
<span>{t('chat.filterLabel')}</span>
<TooltipProvider delayDuration={100}>
<Tooltip>
<TooltipTrigger>
<QuestionMarkCircledIcon />
</TooltipTrigger>
<TooltipContent>
<p className="w-[170px] break-words">{t('chat.tooltipText')}</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
return <div className="flex gap-4 mt-4" style={!isMobile ? { height: 'calc(100vh - 10rem)' } : { height: 'calc(100vh - 4rem)' }}>
{
isMobile && <div className="absolute top-0 left-4 z-10 bg-gray-100 dark:bg-gray-950 py-1 px-2 pb-2 rounded-md">
{!collapse && <span onClick={() => { setCollapse(true) }} className="">收起</span>}
{collapse && <span onClick={() => { setCollapse(false) }} className="">展开</span>}
</div>
<div className="flex flex-wrap gap-2">
{words.map((str, i) => <div key={str} className="badge badge-info h-[auto] gap-2 text-gray-600 bg-[rgba(53,126,249,.15)] dark:text-slate-50">{str}<span className="cursor-pointer font-thin" onClick={() => onClose(i)}>x</span></div>)}
{
editCustomKey ? <div className="badge badge-info gap-2 cursor-pointer bg-[rgba(53,126,249,.15)]"><input ref={inputRef} id="taginput" className="w-20 h-4 py-0 border-none outline-none bg-gray-50"
onKeyDown={(event) => {
if (event.key === "Enter" && !event.shiftKey) {
handleAddKeyword(inputRef.current.value);
}
}}
onBlur={() => {
handleAddKeyword(inputRef.current.value);
}}></input></div> :
<div className="badge badge-info gap-2 cursor-pointer bg-[rgba(53,126,249,.86)] text-gray-50" onClick={handleOpenInput}><span>{t('chat.addCustomLabel')}</span></div>
}
}
{
isMobile && <div className="absolute top-0 right-4 z-10 bg-gray-100 dark:bg-gray-950 py-1 px-2 pb-2 rounded-md">
<span onClick={closeDialog} >关闭</span>
</div>
{/* files */}
<div className="mt-4">
<p className="mb-4 text-sm font-bold">{t('chat.sourceDocumentsLabel')}</p>
{files.map(_file =>
_file.right ? <div key={_file.id} onClick={() => setFile(_file)} className={`group rounded-xl bg-[#fff] dark:bg-[#303134] hover-bg-gray-200 flex items-center px-4 mb-2 relative min-h-16 cursor-pointer ${file?.id === _file.id && 'bg-gray-200'}`}>
<p className="text-sm break-all">{_file.fileName}</p>
<div className="absolute right-1 top-1 gap-2 hidden group-hover:flex">
{
_file.fileUrl && <div className="tooltip" data-tip={t('chat.downloadPDFTooltip')}>
<a href="javascript:;" onClick={(event) => { downloadFile(checkSassUrl(_file.fileUrl), _file.fileName.replace(/\.[\w\d]+$/, '.pdf')); event.stopPropagation() }} >
<Import color="rgba(53,126,249,1)" size={22} strokeWidth={1.5}></Import>
</a>
</div>
}
{
_file.originUrl && <div className="tooltip tooltip-left" data-tip={t('chat.downloadOriginalTooltip')}>
<a href="javascript:;" onClick={(event) => { downloadFile(checkSassUrl(_file.originUrl), _file.fileName); event.stopPropagation() }} >
<Download color="rgba(53,126,249,1)" size={20} strokeWidth={1.5}></Download>
</a>
</div>
}
</div>
<span className="absolute right-1 bottom-1 text-blue-400 text-sm">{_file.score}</span>
</div> :
<div key={_file.id} className={`msk group rounded-xl bg-[#fff] hover-bg-gray-200 flex items-center px-4 mb-2 relative min-h-16 cursor-pointer ${file?.id === _file.id && 'bg-gray-200'}`}>
<p className="text-sm blur-sm">是真的马赛克.msk</p>
}
{/* left */}
{
(!isMobile || !collapse) && <div className="sm:w-[300px] bg-gray-100 dark:bg-[#3C4048] rounded-md py-4 px-2 h-full overflow-y-auto no-scrollbar w-[200px] max-h-[100%] sm:max-h-full absolute sm:static z-20 sm:z-auto">
{/* label */}
{/* 中英 */}
<div className="mb-4 text-sm font-bold place-items-center space-x-1 hidden sm:block">
<div className="flex">
<span>{t('chat.filterLabel')}</span>
<TooltipProvider delayDuration={100}>
<Tooltip>
<TooltipTrigger>
<QuestionMarkCircledIcon />
</TooltipTrigger>
<TooltipContent>
<p className="w-[170px] break-words">{t('chat.tooltipText')}</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
</div>
<div className="flex flex-wrap gap-2 hidden sm:block">
{words.map((str, i) => <div key={str} className="badge badge-info h-[auto] gap-2 text-gray-600 bg-[rgba(53,126,249,.15)] dark:text-slate-50">{str}<span className="cursor-pointer font-thin" onClick={() => onClose(i)}>x</span></div>)}
{
editCustomKey ? <div className="badge badge-info gap-2 cursor-pointer bg-[rgba(53,126,249,.15)]"><input ref={inputRef} id="taginput" className="w-20 h-4 py-0 border-none outline-none bg-gray-50"
onKeyDown={(event) => {
if (event.key === "Enter" && !event.shiftKey) {
handleAddKeyword(inputRef.current.value);
}
}}
onBlur={() => {
handleAddKeyword(inputRef.current.value);
}}></input></div> :
<div className="badge badge-info gap-2 cursor-pointer bg-[rgba(53,126,249,.86)] text-gray-50" onClick={handleOpenInput}><span>{t('chat.addCustomLabel')}</span></div>
}
</div>
{/* files */}
<div className="mt-4">
<p className="mb-4 text-sm font-bold">{t('chat.sourceDocumentsLabel')}</p>
{files.map(_file =>
_file.right ? <div key={_file.id} onClick={() => setFile(_file)} className={`group rounded-xl bg-[#fff] dark:bg-[#303134] hover-bg-gray-200 flex items-center px-4 mb-2 relative min-h-16 cursor-pointer ${file?.id === _file.id && 'bg-gray-200'}`}>
<p className="text-sm break-all">{_file.fileName}</p>
<div className="absolute right-1 top-1 gap-2 hidden group-hover:flex">
{
_file.fileUrl && <div className="tooltip" data-tip={t('chat.downloadPDFTooltip')}>
<a href="javascript:;" onClick={(event) => { downloadFile(checkSassUrl(_file.fileUrl), _file.fileName.replace(/\.[\w\d]+$/, '.pdf')); event.stopPropagation() }} >
<Import color="rgba(53,126,249,1)" size={22} strokeWidth={1.5}></Import>
</a>
</div>
}
{
_file.originUrl && <div className="tooltip tooltip-left" data-tip={t('chat.downloadOriginalTooltip')}>
<a href="javascript:;" onClick={(event) => { downloadFile(checkSassUrl(_file.originUrl), _file.fileName); event.stopPropagation() }} >
<Download color="rgba(53,126,249,1)" size={20} strokeWidth={1.5}></Download>
</a>
</div>
}
</div>
<span className="absolute right-1 bottom-1 text-blue-400 text-sm">{_file.score}</span>
</div>
)}
{!files.length && <p className="text-sm text-center mt-10 text-gray-500">{t('chat.noMatchedFilesMessage')}</p>}
</div> :
<div key={_file.id} className={`msk group rounded-xl bg-[#fff] hover-bg-gray-200 flex items-center px-4 mb-2 relative min-h-16 cursor-pointer ${file?.id === _file.id && 'bg-gray-200'}`}>
<p className="text-sm blur-sm">是真的马赛克.msk</p>
<span className="absolute right-1 bottom-1 text-blue-400 text-sm">{_file.score}</span>
</div>
)}
{!files.length && <p className="text-sm text-center mt-10 text-gray-500">{t('chat.noMatchedFilesMessage')}</p>}
</div>
</div>
</div>
}
{/* file pane */}
{file && children(file)}
</div>
Expand Down Expand Up @@ -190,7 +230,7 @@ const ResouceModal = forwardRef((props, ref) => {
</DialogHeader> */}
{open && <div>
<Anwser id={data.messageId} msg={data.message} onInit={setKeywords} onAdd={handleAddWord}></Anwser>
<ResultPanne words={keywords} chatId={data.chatId} data={data} onClose={handleDelKeyword} onAdd={handleAddWord}>
<ResultPanne words={keywords} chatId={data.chatId} data={data} onClose={handleDelKeyword} onAdd={handleAddWord} closeDialog={()=>setOpen(false)}>
{
(file) => file.fileUrl ?
<MemoizedFileView data={file}></MemoizedFileView> :
Expand Down
23 changes: 17 additions & 6 deletions src/frontend/src/pages/SystemPage/components/Config.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,15 @@ import { useContext, useEffect, useRef, useState } from "react";
import AceEditor from "react-ace";
import { useTranslation } from "react-i18next";
import { Button } from "../../../components/bs-ui/button";
import { alertContext } from "../../../contexts/alertContext";
import { getSysConfigApi, setSysConfigApi } from "../../../controllers/API/user";
import { captureAndAlertRequestErrorHoc } from "../../../controllers/request";
import { locationContext } from "@/contexts/locationContext";
import { useToast } from "@/components/bs-ui/toast/use-toast";

export default function Config() {
const { setErrorData, setSuccessData } = useContext(alertContext);
const { toast, message } = useToast()
const { reloadConfig } = useContext(locationContext)

const [config, setConfig] = useState('')

const { t } = useTranslation()
Expand All @@ -21,15 +24,23 @@ export default function Config() {

const handleSave = () => {
if (validataRef.current.length) {
return setErrorData({
return toast({
variant: 'error',
title: `yaml${t('formatError')}`,
list: validataRef.current.map(el => el.text),
});
description: validataRef.current.map(el => el.text)
})
}

captureAndAlertRequestErrorHoc(setSysConfigApi({ data: codeRef.current }).then(res => {
setSuccessData({ title: t('success') })
message({
variant: 'success',
title: t('prompt'),
description: t('success')
})
setConfig(codeRef.current)

// 更新配置信息
reloadConfig()
}))
}

Expand Down

0 comments on commit feb5e5e

Please sign in to comment.