|
@@ -1,220 +1,443 @@
|
|
|
-import React, { useState, useRef, useEffect } from 'react';
|
|
|
|
|
|
|
+import * as React from 'react';
|
|
|
|
|
+import { observer } from 'mobx-react';
|
|
|
|
|
+import { generatePath, useLocation, useParams } from 'react-router-dom';
|
|
|
|
|
+import { Form, Input, Button, message, Upload, UploadProps } from 'antd';
|
|
|
|
|
+import { apis } from '@/apis';
|
|
|
|
|
+import router from '@/router';
|
|
|
|
|
+import { ArrowLeftOutlined } from '@ant-design/icons';
|
|
|
|
|
+import config, { getHeaders } from '@/apis/config';
|
|
|
|
|
+import ImgPre from './imgPre';
|
|
|
|
|
+const { TextArea } = Input;
|
|
|
|
|
+import { useState, useRef, useEffect } from 'react';
|
|
|
import ReactQuill from 'react-quill';
|
|
import ReactQuill from 'react-quill';
|
|
|
import 'react-quill/dist/quill.snow.css';
|
|
import 'react-quill/dist/quill.snow.css';
|
|
|
-import { Form } from 'antd';
|
|
|
|
|
import { EyeOutlined, CloseOutlined } from '@ant-design/icons';
|
|
import { EyeOutlined, CloseOutlined } from '@ant-design/icons';
|
|
|
|
|
+import './style.less';
|
|
|
|
|
|
|
|
-// 提取纯文本工具函数
|
|
|
|
|
const extractPlainText = (html: string): string => {
|
|
const extractPlainText = (html: string): string => {
|
|
|
- if (!html) return '';
|
|
|
|
|
- const tempDiv = document.createElement('div');
|
|
|
|
|
- tempDiv.innerHTML = html;
|
|
|
|
|
- return tempDiv.textContent || '';
|
|
|
|
|
|
|
+ if (!html) return '';
|
|
|
|
|
+ const tempDiv = document.createElement('div');
|
|
|
|
|
+ tempDiv.innerHTML = html;
|
|
|
|
|
+ return tempDiv.textContent || '';
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
interface LinkData {
|
|
interface LinkData {
|
|
|
- id: string;
|
|
|
|
|
- originalText: string;
|
|
|
|
|
- url: string;
|
|
|
|
|
- isDeleted: boolean;
|
|
|
|
|
|
|
+ id: string;
|
|
|
|
|
+ originalText: string;
|
|
|
|
|
+ url: string;
|
|
|
|
|
+ isDeleted: boolean;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
-const PersistentImagePreviewEditor = () => {
|
|
|
|
|
- const [form] = Form.useForm();
|
|
|
|
|
- const [editorHtml, setEditorHtml] = useState<string>(`
|
|
|
|
|
- <p>系统工作人员联系方式</p>
|
|
|
|
|
- <p>系统工作人员联系方式表:
|
|
|
|
|
- <a href="http://xia0miduo.gicp.net:9000/papbtest//pdf/a2965738189226381312/a2966033634305507328/【示意图序号_a2966033634305507328_3】.jpg" target="_blank">【示意图序号_a2966033634305507328_2】</a>,
|
|
|
|
|
- 第二个标签 <a href="https://jkeckms.ryuiso.com/chat-bg.jpg" target="_blank">【示意图序号_a2966033634305507328_2】</a>,
|
|
|
|
|
- 第三个标签 <a href="https://jkeckms.ryuiso.com/chat-bg.jpg" target="_blank">【示意图序号_a2966033634305507328_2】</a>
|
|
|
|
|
- </p>
|
|
|
|
|
- `);
|
|
|
|
|
- const [plainText, setPlainText] = useState<string>(extractPlainText(editorHtml));
|
|
|
|
|
- const [linkDataList, setLinkDataList] = useState<LinkData[]>([]);
|
|
|
|
|
- const [activeImageUrl, setActiveImageUrl] = useState<string | null>(null);
|
|
|
|
|
-
|
|
|
|
|
- // 跟踪鼠标是否在链接或预览区上
|
|
|
|
|
- const [isHoveringLink, setIsHoveringLink] = useState(false);
|
|
|
|
|
- const [isHoveringPreview, setIsHoveringPreview] = useState(false);
|
|
|
|
|
-
|
|
|
|
|
- const editorContainerRef = useRef<HTMLDivElement>(null);
|
|
|
|
|
- const previewRef = useRef<HTMLDivElement>(null);
|
|
|
|
|
-
|
|
|
|
|
- // 初始化链接数据
|
|
|
|
|
- useEffect(() => {
|
|
|
|
|
- const parser = new DOMParser();
|
|
|
|
|
- const doc = parser.parseFromString(editorHtml, 'text/html');
|
|
|
|
|
- const links = doc.getElementsByTagName('a');
|
|
|
|
|
- const linksData: LinkData[] = [];
|
|
|
|
|
-
|
|
|
|
|
- Array.from(links).forEach((link, index) => {
|
|
|
|
|
- linksData.push({
|
|
|
|
|
- id: `link-${index}-${Date.now()}`,
|
|
|
|
|
- originalText: link.textContent || '',
|
|
|
|
|
- url: link.href,
|
|
|
|
|
- isDeleted: false
|
|
|
|
|
- });
|
|
|
|
|
- });
|
|
|
|
|
-
|
|
|
|
|
- setLinkDataList(linksData);
|
|
|
|
|
- }, []);
|
|
|
|
|
-
|
|
|
|
|
- // 处理内容变化
|
|
|
|
|
- const handleEditorChange = (html: string) => {
|
|
|
|
|
- setEditorHtml(html);
|
|
|
|
|
- const text = extractPlainText(html);
|
|
|
|
|
- setPlainText(text);
|
|
|
|
|
- form.setFieldsValue({ slice_text: text });
|
|
|
|
|
-
|
|
|
|
|
- if (linkDataList.length > 0) {
|
|
|
|
|
- const updatedLinks = [...linkDataList];
|
|
|
|
|
- updatedLinks.forEach(link => {
|
|
|
|
|
- const feature = link.originalText.slice(0, 5);
|
|
|
|
|
- link.isDeleted = !text.includes(feature);
|
|
|
|
|
- });
|
|
|
|
|
- setLinkDataList(updatedLinks);
|
|
|
|
|
- }
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- // 处理鼠标悬停链接事件
|
|
|
|
|
- useEffect(() => {
|
|
|
|
|
- if (!editorContainerRef.current) return;
|
|
|
|
|
-
|
|
|
|
|
- const handleMouseOverLink = (e: MouseEvent) => {
|
|
|
|
|
- const target = e.target as HTMLAnchorElement;
|
|
|
|
|
- if (target.tagName === 'A') {
|
|
|
|
|
- const url = target.href;
|
|
|
|
|
- const isKnownLink = linkDataList.some(link =>
|
|
|
|
|
- !link.isDeleted && url.includes(link.url)
|
|
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+const FormItem = Form.Item;
|
|
|
|
|
+
|
|
|
|
|
+interface TakaiSliceDetailRequest {
|
|
|
|
|
+ knowledgeId: string,
|
|
|
|
|
+ sliceId: string,
|
|
|
|
|
+ sliceText: string,
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const SliceDetail: React.FC = () => {
|
|
|
|
|
+
|
|
|
|
|
+ const [form] = Form.useForm();
|
|
|
|
|
+
|
|
|
|
|
+ const params = useParams();
|
|
|
|
|
+ const location = useLocation();
|
|
|
|
|
+ const { text, page } = location.state;
|
|
|
|
|
+ const [listLoading, setListLoading] = React.useState(false);
|
|
|
|
|
+
|
|
|
|
|
+ const [editorHtml, setEditorHtml] = useState<string>(``);
|
|
|
|
|
+ // RichTextImageRight
|
|
|
|
|
+ const RichTextImageRight = () => {
|
|
|
|
|
+ const [plainText, setPlainText] = useState<string>(extractPlainText(editorHtml));
|
|
|
|
|
+ const [linkDataList, setLinkDataList] = useState<LinkData[]>([]);
|
|
|
|
|
+ const [activeImageUrl, setActiveImageUrl] = useState<string | null>(null);
|
|
|
|
|
+
|
|
|
|
|
+ // 跟踪鼠标是否在链接或预览区上
|
|
|
|
|
+ const [isHoveringLink, setIsHoveringLink] = useState(false);
|
|
|
|
|
+ const [isHoveringPreview, setIsHoveringPreview] = useState(false);
|
|
|
|
|
+
|
|
|
|
|
+ const editorContainerRef = useRef<HTMLDivElement>(null);
|
|
|
|
|
+ const previewRef = useRef<HTMLDivElement>(null);
|
|
|
|
|
+
|
|
|
|
|
+ // 初始化链接数据
|
|
|
|
|
+ useEffect(() => {
|
|
|
|
|
+ const start = async () => {
|
|
|
|
|
+ if (!params.sliceId || !params.knowledgeId) {
|
|
|
|
|
+ throw new Error('参数错误');
|
|
|
|
|
+ }
|
|
|
|
|
+ const res = await apis.fetchTakaiSliceDetail(params.sliceId, params.knowledgeId);
|
|
|
|
|
+ const info = res.data.data;
|
|
|
|
|
+ const parser = new DOMParser();
|
|
|
|
|
+ const doc = parser.parseFromString(info.slice_text, 'text/html');
|
|
|
|
|
+ const links = doc.getElementsByTagName('a');
|
|
|
|
|
+ const linksData: LinkData[] = [];
|
|
|
|
|
+ Array.from(links).forEach((link, index) => {
|
|
|
|
|
+ linksData.push({
|
|
|
|
|
+ id: `link-${index}-${Date.now()}`,
|
|
|
|
|
+ originalText: link.textContent || '',
|
|
|
|
|
+ url: link.href,
|
|
|
|
|
+ isDeleted: false
|
|
|
|
|
+ });
|
|
|
|
|
+ });
|
|
|
|
|
+
|
|
|
|
|
+ setLinkDataList(linksData);
|
|
|
|
|
+ }
|
|
|
|
|
+ start()
|
|
|
|
|
+ }, []);
|
|
|
|
|
+
|
|
|
|
|
+ // 处理内容变化
|
|
|
|
|
+ const handleEditorChange = (html: string) => {
|
|
|
|
|
+ setEditorHtml(html);
|
|
|
|
|
+ const text = extractPlainText(html);
|
|
|
|
|
+ setPlainText(text);
|
|
|
|
|
+ form.setFieldsValue({ slice_text: text });
|
|
|
|
|
+
|
|
|
|
|
+ if (linkDataList.length > 0) {
|
|
|
|
|
+ const updatedLinks = [...linkDataList];
|
|
|
|
|
+ updatedLinks.forEach(link => {
|
|
|
|
|
+ const feature = link.originalText.slice(0, 5);
|
|
|
|
|
+ link.isDeleted = !text.includes(feature);
|
|
|
|
|
+ });
|
|
|
|
|
+ setLinkDataList(updatedLinks);
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ // 处理鼠标悬停链接事件
|
|
|
|
|
+ useEffect(() => {
|
|
|
|
|
+ if (!editorContainerRef.current) return;
|
|
|
|
|
+
|
|
|
|
|
+ const handleMouseOverLink = (e: MouseEvent) => {
|
|
|
|
|
+ const target = e.target as HTMLAnchorElement;
|
|
|
|
|
+ if (target.tagName === 'A') {
|
|
|
|
|
+ const url = target.href;
|
|
|
|
|
+ const isKnownLink = linkDataList.some(link =>
|
|
|
|
|
+ !link.isDeleted && url.includes(link.url)
|
|
|
|
|
+ );
|
|
|
|
|
+
|
|
|
|
|
+ if (isKnownLink) {
|
|
|
|
|
+ console.log('图片地址:', url);
|
|
|
|
|
+ setActiveImageUrl(url);
|
|
|
|
|
+ setIsHoveringLink(true);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const handleMouseOutLink = () => {
|
|
|
|
|
+ setIsHoveringLink(false);
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ const container = editorContainerRef.current;
|
|
|
|
|
+ container.addEventListener('mouseover', handleMouseOverLink);
|
|
|
|
|
+ container.addEventListener('mouseout', handleMouseOutLink);
|
|
|
|
|
+
|
|
|
|
|
+ return () => {
|
|
|
|
|
+ container.removeEventListener('mouseover', handleMouseOverLink);
|
|
|
|
|
+ container.removeEventListener('mouseout', handleMouseOutLink);
|
|
|
|
|
+ };
|
|
|
|
|
+ }, [linkDataList]);
|
|
|
|
|
+
|
|
|
|
|
+ // 控制预览区显示/隐藏的核心逻辑
|
|
|
|
|
+ useEffect(() => {
|
|
|
|
|
+ // 当鼠标既不在链接上也不在预览区上时,才隐藏预览
|
|
|
|
|
+ if (!isHoveringLink && !isHoveringPreview) {
|
|
|
|
|
+ setActiveImageUrl(null);
|
|
|
|
|
+ }
|
|
|
|
|
+ }, [isHoveringLink, isHoveringPreview]);
|
|
|
|
|
+
|
|
|
|
|
+ // 配置编辑器(无工具栏)
|
|
|
|
|
+ const modules = {
|
|
|
|
|
+ toolbar: false
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ return (
|
|
|
|
|
+ <div>
|
|
|
|
|
+ {/* 容器:编辑器 + 右侧图片预览区 */}
|
|
|
|
|
+ <div style={{
|
|
|
|
|
+ display: 'flex',
|
|
|
|
|
+ gap: '16px',
|
|
|
|
|
+ alignItems: 'flex-start'
|
|
|
|
|
+ }}>
|
|
|
|
|
+ {/* 500x500的编辑器 */}
|
|
|
|
|
+ <div
|
|
|
|
|
+ ref={editorContainerRef}
|
|
|
|
|
+ style={{
|
|
|
|
|
+ width: '500px',
|
|
|
|
|
+ height: '500px',
|
|
|
|
|
+ border: '1px solid #d9d9d9',
|
|
|
|
|
+ borderRadius: '4px',
|
|
|
|
|
+ overflow: 'hidden'
|
|
|
|
|
+ }}
|
|
|
|
|
+ >
|
|
|
|
|
+ <ReactQuill
|
|
|
|
|
+ value={editorHtml}
|
|
|
|
|
+ onChange={handleEditorChange}
|
|
|
|
|
+ modules={modules}
|
|
|
|
|
+ style={{ height: '100%' }}
|
|
|
|
|
+ placeholder="请输入内容..."
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ {/* 右侧图片预览区(固定位置) */}
|
|
|
|
|
+ {activeImageUrl && (
|
|
|
|
|
+ <div
|
|
|
|
|
+ ref={previewRef}
|
|
|
|
|
+ style={{
|
|
|
|
|
+ width: '300px',
|
|
|
|
|
+ height: '500px',
|
|
|
|
|
+ backgroundColor: 'white',
|
|
|
|
|
+ borderRadius: '8px',
|
|
|
|
|
+ boxShadow: '0 4px 16px rgba(0, 0, 0, 0.15)',
|
|
|
|
|
+ padding: '12px',
|
|
|
|
|
+ border: '1px solid #eee',
|
|
|
|
|
+ display: 'flex',
|
|
|
|
|
+ flexDirection: 'column'
|
|
|
|
|
+ }}
|
|
|
|
|
+ onMouseOver={() => setIsHoveringPreview(true)}
|
|
|
|
|
+ onMouseOut={() => setIsHoveringPreview(false)}
|
|
|
|
|
+ >
|
|
|
|
|
+ <div style={{
|
|
|
|
|
+ display: 'flex',
|
|
|
|
|
+ justifyContent: 'space-between',
|
|
|
|
|
+ alignItems: 'center',
|
|
|
|
|
+ marginBottom: '12px',
|
|
|
|
|
+ paddingBottom: '8px',
|
|
|
|
|
+ borderBottom: '1px solid #f0f0f0'
|
|
|
|
|
+ }}>
|
|
|
|
|
+ <div style={{ display: 'flex', alignItems: 'center' }}>
|
|
|
|
|
+ <EyeOutlined style={{ color: '#1890ff', marginRight: '8px' }} />
|
|
|
|
|
+ <span style={{ fontSize: '14px', color: '#333' }}>图片预览</span>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <CloseOutlined
|
|
|
|
|
+ style={{ color: '#999', cursor: 'pointer', fontSize: '16px' }}
|
|
|
|
|
+ onClick={() => setActiveImageUrl(null)}
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+
|
|
|
|
|
+ <div style={{
|
|
|
|
|
+ flex: 1,
|
|
|
|
|
+ display: 'flex',
|
|
|
|
|
+ alignItems: 'center',
|
|
|
|
|
+ justifyContent: 'center',
|
|
|
|
|
+ overflow: 'hidden'
|
|
|
|
|
+ }}>
|
|
|
|
|
+ <img
|
|
|
|
|
+ src={activeImageUrl}
|
|
|
|
|
+ alt="示意图预览"
|
|
|
|
|
+ style={{
|
|
|
|
|
+ maxWidth: '100%',
|
|
|
|
|
+ maxHeight: '100%',
|
|
|
|
|
+ objectFit: 'contain'
|
|
|
|
|
+ }}
|
|
|
|
|
+ onError={() => setActiveImageUrl(null)}
|
|
|
|
|
+ />
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ )}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
);
|
|
);
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
|
|
|
- if (isKnownLink) {
|
|
|
|
|
- console.log('图片地址:', url);
|
|
|
|
|
- setActiveImageUrl(url);
|
|
|
|
|
- setIsHoveringLink(true);
|
|
|
|
|
|
|
+ const appApi = {
|
|
|
|
|
+ fetchList: async () => {
|
|
|
|
|
+ setListLoading(true);
|
|
|
|
|
+ try {
|
|
|
|
|
+ if (!params.sliceId || !params.knowledgeId) {
|
|
|
|
|
+ throw new Error('参数错误');
|
|
|
|
|
+ }
|
|
|
|
|
+ const res = await apis.fetchTakaiSliceDetail(params.sliceId, params.knowledgeId);
|
|
|
|
|
+ const info = res.data.data;
|
|
|
|
|
+ setEditorHtml(info.slice_text || '');
|
|
|
|
|
+ form.setFieldsValue({
|
|
|
|
|
+ slice_id: info.slice_id,
|
|
|
|
|
+ slice_text: info.slice_text,
|
|
|
|
|
+ document_id: info.document_id,
|
|
|
|
|
+ })
|
|
|
|
|
+ console.log(res, 'info');
|
|
|
|
|
+ } catch (error) {
|
|
|
|
|
+ console.error(error);
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ setListLoading(false);
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
- }
|
|
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
- const handleMouseOutLink = () => {
|
|
|
|
|
- setIsHoveringLink(false);
|
|
|
|
|
- };
|
|
|
|
|
|
|
+ const init = async () => {
|
|
|
|
|
+ if (params.sliceId && params.sliceId !== 'new') {
|
|
|
|
|
+ await appApi.fetchList();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- const container = editorContainerRef.current;
|
|
|
|
|
- container.addEventListener('mouseover', handleMouseOverLink);
|
|
|
|
|
- container.addEventListener('mouseout', handleMouseOutLink);
|
|
|
|
|
|
|
+ React.useEffect(() => {
|
|
|
|
|
+ init();
|
|
|
|
|
+ }, [])
|
|
|
|
|
|
|
|
- return () => {
|
|
|
|
|
- container.removeEventListener('mouseover', handleMouseOverLink);
|
|
|
|
|
- container.removeEventListener('mouseout', handleMouseOutLink);
|
|
|
|
|
|
|
+ const [cursorEndPosition, setCursorEndPosition] = React.useState<number>(0);
|
|
|
|
|
+
|
|
|
|
|
+ // 上传图片配置
|
|
|
|
|
+ const uploadImageConfig: UploadProps = {
|
|
|
|
|
+ name: 'files',
|
|
|
|
|
+ action: config.baseURL + `/deepseek/api/uploadSliceImage/${params.knowledgeId}/${params.documentId}`,
|
|
|
|
|
+ method: 'POST',
|
|
|
|
|
+ headers: getHeaders(),
|
|
|
|
|
+ accept: ['.png', '.jpg', '.jpeg'].join(','),
|
|
|
|
|
+ multiple: true,
|
|
|
|
|
+ maxCount: 5,
|
|
|
|
|
+ showUploadList: false,
|
|
|
};
|
|
};
|
|
|
- }, [linkDataList]);
|
|
|
|
|
|
|
|
|
|
- // 控制预览区显示/隐藏的核心逻辑
|
|
|
|
|
- useEffect(() => {
|
|
|
|
|
- // 当鼠标既不在链接上也不在预览区上时,才隐藏预览
|
|
|
|
|
- if (!isHoveringLink && !isHoveringPreview) {
|
|
|
|
|
- setActiveImageUrl(null);
|
|
|
|
|
- }
|
|
|
|
|
- }, [isHoveringLink, isHoveringPreview]);
|
|
|
|
|
-
|
|
|
|
|
- // 配置编辑器(无工具栏)
|
|
|
|
|
- const modules = {
|
|
|
|
|
- toolbar: false
|
|
|
|
|
- };
|
|
|
|
|
-
|
|
|
|
|
- return (
|
|
|
|
|
- <Form form={form} initialValues={{ slice_text: plainText }}>
|
|
|
|
|
- <Form.Item
|
|
|
|
|
- name="slice_text"
|
|
|
|
|
- rules={[{ required: true, message: '内容不能为空' }]}
|
|
|
|
|
- >
|
|
|
|
|
- {/* 容器:编辑器 + 右侧图片预览区 */}
|
|
|
|
|
- <div style={{
|
|
|
|
|
- display: 'flex',
|
|
|
|
|
- gap: '16px',
|
|
|
|
|
- alignItems: 'flex-start'
|
|
|
|
|
- }}>
|
|
|
|
|
- {/* 500x500的编辑器 */}
|
|
|
|
|
- <div
|
|
|
|
|
- ref={editorContainerRef}
|
|
|
|
|
- style={{
|
|
|
|
|
- width: '500px',
|
|
|
|
|
- height: '500px',
|
|
|
|
|
- border: '1px solid #d9d9d9',
|
|
|
|
|
- borderRadius: '4px',
|
|
|
|
|
- overflow: 'hidden'
|
|
|
|
|
- }}
|
|
|
|
|
- >
|
|
|
|
|
- <ReactQuill
|
|
|
|
|
- value={editorHtml}
|
|
|
|
|
- onChange={handleEditorChange}
|
|
|
|
|
- modules={modules}
|
|
|
|
|
- style={{ height: '100%' }}
|
|
|
|
|
- placeholder="请输入内容..."
|
|
|
|
|
- />
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- {/* 右侧图片预览区(固定位置) */}
|
|
|
|
|
- {activeImageUrl && (
|
|
|
|
|
- <div
|
|
|
|
|
- ref={previewRef}
|
|
|
|
|
- style={{
|
|
|
|
|
- width: '300px',
|
|
|
|
|
- height: '500px',
|
|
|
|
|
- backgroundColor: 'white',
|
|
|
|
|
- borderRadius: '8px',
|
|
|
|
|
- boxShadow: '0 4px 16px rgba(0, 0, 0, 0.15)',
|
|
|
|
|
- padding: '12px',
|
|
|
|
|
- border: '1px solid #eee',
|
|
|
|
|
- display: 'flex',
|
|
|
|
|
- flexDirection: 'column'
|
|
|
|
|
- }}
|
|
|
|
|
- onMouseOver={() => setIsHoveringPreview(true)}
|
|
|
|
|
- onMouseOut={() => setIsHoveringPreview(false)}
|
|
|
|
|
- >
|
|
|
|
|
- <div style={{
|
|
|
|
|
- display: 'flex',
|
|
|
|
|
- justifyContent: 'space-between',
|
|
|
|
|
- alignItems: 'center',
|
|
|
|
|
- marginBottom: '12px',
|
|
|
|
|
- paddingBottom: '8px',
|
|
|
|
|
- borderBottom: '1px solid #f0f0f0'
|
|
|
|
|
- }}>
|
|
|
|
|
- <div style={{ display: 'flex', alignItems: 'center' }}>
|
|
|
|
|
- <EyeOutlined style={{ color: '#1890ff', marginRight: '8px' }} />
|
|
|
|
|
- <span style={{ fontSize: '14px', color: '#333' }}>图片预览</span>
|
|
|
|
|
|
|
+ return (
|
|
|
|
|
+ <div>
|
|
|
|
|
+ {/* <ImgPre></ImgPre> */}
|
|
|
|
|
+ <div className='questionAnswerList'>
|
|
|
|
|
+ <div style={{ height: '100%', marginLeft: '10px' }}>
|
|
|
|
|
+ <Form
|
|
|
|
|
+ style={{ paddingTop: '20px', height: '100%' }}
|
|
|
|
|
+ form={form}
|
|
|
|
|
+ layout='vertical'
|
|
|
|
|
+ >
|
|
|
|
|
+ <Form.Item
|
|
|
|
|
+ name="slice_text"
|
|
|
|
|
+ rules={[{ required: true, message: '内容不能为空' }]}
|
|
|
|
|
+ >
|
|
|
|
|
+ {RichTextImageRight()}
|
|
|
|
|
+
|
|
|
|
|
+ </Form.Item>
|
|
|
|
|
+ {false && <FormItem
|
|
|
|
|
+ name="slice_text"
|
|
|
|
|
+ rules={[{ required: true, message: '切片内容不能为空' }]}>
|
|
|
|
|
+ <TextArea
|
|
|
|
|
+ style={{
|
|
|
|
|
+ width: '50%',
|
|
|
|
|
+ height: '200px',
|
|
|
|
|
+ overflow: 'auto',
|
|
|
|
|
+ resize: 'both',
|
|
|
|
|
+ boxSizing: 'border-box',
|
|
|
|
|
+
|
|
|
|
|
+ }}
|
|
|
|
|
+ placeholder=""
|
|
|
|
|
+ autoSize={{ minRows: 20, maxRows: 5000 }}
|
|
|
|
|
+ onBlur={(e) => {
|
|
|
|
|
+ const target = e.target as HTMLTextAreaElement;
|
|
|
|
|
+ // 更新光标终点位置
|
|
|
|
|
+ setCursorEndPosition(target.selectionEnd);
|
|
|
|
|
+ }}
|
|
|
|
|
+ />
|
|
|
|
|
+ </FormItem>}
|
|
|
|
|
+ <Upload
|
|
|
|
|
+ {...uploadImageConfig}
|
|
|
|
|
+ onChange={(info) => {
|
|
|
|
|
+ const insertToSliceText = (text: string) => {
|
|
|
|
|
+ const { slice_text } = form.getFieldsValue();
|
|
|
|
|
+
|
|
|
|
|
+ // 获取当前光标位置
|
|
|
|
|
+ const position = cursorEndPosition;
|
|
|
|
|
+
|
|
|
|
|
+ let newValue = '';
|
|
|
|
|
+
|
|
|
|
|
+ if (!slice_text) {
|
|
|
|
|
+ newValue = text;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ newValue = slice_text.slice(0, position) + text + slice_text.slice(position);
|
|
|
|
|
+ }
|
|
|
|
|
+ setEditorHtml(newValue || '');
|
|
|
|
|
+ form.setFieldsValue({ slice_text: newValue });
|
|
|
|
|
+ }
|
|
|
|
|
+ const file = info.file;
|
|
|
|
|
+ if (file.status === 'done') {// 上传成功
|
|
|
|
|
+ const { code, msg, data } = file.response;
|
|
|
|
|
+ if (code === 200) {
|
|
|
|
|
+ const text = data.join('');
|
|
|
|
|
+ insertToSliceText(text);
|
|
|
|
|
+ message.success('上传成功');
|
|
|
|
|
+ } else {
|
|
|
|
|
+ message.error(msg);
|
|
|
|
|
+ }
|
|
|
|
|
+ } else if (file.status === 'error') {// 上传失败
|
|
|
|
|
+ message.error('上传失败');
|
|
|
|
|
+ }
|
|
|
|
|
+ }}
|
|
|
|
|
+ >
|
|
|
|
|
+ <Button type='primary'>
|
|
|
|
|
+ 解析图片
|
|
|
|
|
+ </Button>
|
|
|
|
|
+ </Upload>
|
|
|
|
|
+ <Button
|
|
|
|
|
+ style={{ margin: '0 16px' }}
|
|
|
|
|
+ type='primary'
|
|
|
|
|
+ icon={<ArrowLeftOutlined />}
|
|
|
|
|
+ onClick={() => {
|
|
|
|
|
+ const path = generatePath('/deepseek/knowledgeLib/:knowledgeId/slice/:documentId/:embeddingId', {
|
|
|
|
|
+ knowledgeId: params.knowledgeId as string,
|
|
|
|
|
+ documentId: params.documentId as string,
|
|
|
|
|
+ embeddingId: params.embeddingId as string,
|
|
|
|
|
+ });
|
|
|
|
|
+ router.navigate({ pathname: path }, { state: { text: text, page } });
|
|
|
|
|
+ }}
|
|
|
|
|
+ >
|
|
|
|
|
+ 返回
|
|
|
|
|
+ </Button>
|
|
|
|
|
+ <Button
|
|
|
|
|
+ type='primary'
|
|
|
|
|
+ onClick={() => {
|
|
|
|
|
+ form.validateFields().then(async (values) => {
|
|
|
|
|
+ console.log('values', values)
|
|
|
|
|
+ // 验证参数是否存在
|
|
|
|
|
+ if (!params.knowledgeId || !params.sliceId) {
|
|
|
|
|
+ message.error('知识库ID或切片ID无效');
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ const data = values;
|
|
|
|
|
+ const info = {
|
|
|
|
|
+ knowledgeId: params.knowledgeId,
|
|
|
|
|
+ sliceId: params.sliceId === null ? '' : params.sliceId,
|
|
|
|
|
+ sliceText: values.slice_text,
|
|
|
|
|
+ documentId: params.documentId,
|
|
|
|
|
+ };
|
|
|
|
|
+ let res = null;
|
|
|
|
|
+ if (params.sliceId && params.sliceId !== 'null' && params.sliceId !== 'new') {
|
|
|
|
|
+ // 编辑应用
|
|
|
|
|
+ res = await apis.modifyTakaiSliceInfo(info as any);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ res = await apis.addTakaiSlice(info as any);
|
|
|
|
|
+ }
|
|
|
|
|
+ if (res.code === 200 && res.data === 1) {
|
|
|
|
|
+ if (params.sliceId && params.sliceId !== 'null' && params.sliceId !== 'new') {
|
|
|
|
|
+ message.success('修改成功');
|
|
|
|
|
+ } else {
|
|
|
|
|
+ message.success('新增成功');
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ if (params.sliceId && params.sliceId !== 'null' && params.sliceId !== 'new') {
|
|
|
|
|
+ message.error('修改失败');
|
|
|
|
|
+ } else {
|
|
|
|
|
+ message.error('新增失败');
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ const path = generatePath('/deepseek/knowledgeLib/:knowledgeId/slice/:documentId/:embeddingId', {
|
|
|
|
|
+ knowledgeId: params.knowledgeId as string,
|
|
|
|
|
+ documentId: params.documentId as string,
|
|
|
|
|
+ embeddingId: params.embeddingId as string,
|
|
|
|
|
+ });
|
|
|
|
|
+ router.navigate({ pathname: path }, { state: { text: text, page } });
|
|
|
|
|
+ }).catch((error) => {
|
|
|
|
|
+ console.error(error);
|
|
|
|
|
+ });
|
|
|
|
|
+ }}
|
|
|
|
|
+ >
|
|
|
|
|
+ 保存
|
|
|
|
|
+ </Button>
|
|
|
|
|
+ </Form>
|
|
|
</div>
|
|
</div>
|
|
|
- <CloseOutlined
|
|
|
|
|
- style={{ color: '#999', cursor: 'pointer', fontSize: '16px' }}
|
|
|
|
|
- onClick={() => setActiveImageUrl(null)}
|
|
|
|
|
- />
|
|
|
|
|
- </div>
|
|
|
|
|
-
|
|
|
|
|
- <div style={{
|
|
|
|
|
- flex: 1,
|
|
|
|
|
- display: 'flex',
|
|
|
|
|
- alignItems: 'center',
|
|
|
|
|
- justifyContent: 'center',
|
|
|
|
|
- overflow: 'hidden'
|
|
|
|
|
- }}>
|
|
|
|
|
- <img
|
|
|
|
|
- src={activeImageUrl}
|
|
|
|
|
- alt="示意图预览"
|
|
|
|
|
- style={{
|
|
|
|
|
- maxWidth: '100%',
|
|
|
|
|
- maxHeight: '100%',
|
|
|
|
|
- objectFit: 'contain'
|
|
|
|
|
- }}
|
|
|
|
|
- onError={() => setActiveImageUrl(null)}
|
|
|
|
|
- />
|
|
|
|
|
- </div>
|
|
|
|
|
</div>
|
|
</div>
|
|
|
- )}
|
|
|
|
|
</div>
|
|
</div>
|
|
|
- </Form.Item>
|
|
|
|
|
- </Form>
|
|
|
|
|
- );
|
|
|
|
|
|
|
+ )
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
-export default PersistentImagePreviewEditor;
|
|
|
|
|
|
|
+export default observer(SliceDetail);
|