From bdeacb9f02dfa74bac74296a4a2c989a8e0d45ff Mon Sep 17 00:00:00 2001
From: xusd <330628789@qq.com>
Date: Thu, 26 Jun 2025 17:52:12 +0800
Subject: [PATCH] feature:AI演示

---
 src/views/register/index.jsx                  |   74 ++++++++-
 src/views/register/visit/index.jsx            |   10 
 src/views/register/visit/component/AiChat.jsx |  273 ++++++++++++++++++++++++++++++++++
 src/views/register/visit/SelfInspection.jsx   |   68 +++++++-
 4 files changed, 402 insertions(+), 23 deletions(-)

diff --git a/src/views/register/index.jsx b/src/views/register/index.jsx
index 5a040c7..3ece25e 100644
--- a/src/views/register/index.jsx
+++ b/src/views/register/index.jsx
@@ -9,6 +9,7 @@
 import React, { useEffect, useState, useMemo, useRef } from 'react';
 import { useNavigate } from 'react-router-dom';
 import { tab0, tab1, tab2, tab3, tab4, tab5, tab6, allSign, tab1bg } from '@/assets/images';
+import { aiMainTitle, Matter, applyRecord, transfer, transfer_1, Aimge, examine } from '@/assets/images'
 import TableView from '../../components/TableView';
 import NewPage from '../../components/NewPage';
 import TableDraftSearch from '../../components/TableDraftSearch';
@@ -32,7 +33,9 @@
 import './index.less';
 import ResponseDetail from './matterDetail/responseDetail';
 import SupervisingViews from './matterDetail/SupervisingViews';
-import { AiQuestion } from './visit/component/levelDetail';
+// import { AiQuestion } from './visit/component/levelDetail';
+import AiChat from './visit/component/AiChat';
+import { IconSend } from '@arco-design/web-react/icon'; // 顶部加上
 const FormItem = Form.Item;
 const TabPane = Tabs.TabPane;
 const { RangePicker } = DatePicker;
@@ -2617,10 +2620,43 @@
 	const [aiLawData, setAiLawData] = useState([]);
 	const [caseDetailAi, setCaseDetailAi] = useState({});
 	const [caseClaim, setCaseClaim] = useState('');
+	const [aiResult, setAiResult] = useState(null); // AI分析结果
+	const [caseDes, setCaseDes] = useState(''); // 案件描述
+
+	// AI相关函数
+	const handleAi = () => {
+		setCaseDes('');
+		setAiQuestionView(true);
+	};
+
 	useEffect(() => {
 		getCountData();
 		const visitWorkBenchStore = $$.getSessionStorage('visitWorkBench'); //缓存数据
 		const toKey = $$.getQueryString('tabActivekey'); //跳转的活跃key
+		
+		// 获取aiResult参数
+		const aiResultParam = $$.getQueryString('aiResult');
+		if (aiResultParam) {
+			try {
+				const aiResultData = JSON.parse(decodeURIComponent(aiResultParam));
+				setAiResult(aiResultData);
+				console.log('获取到AI分析结果:', aiResultData);
+			} catch (error) {
+				console.error('解析aiResult参数失败:', error);
+			}
+		}
+		
+		// 从sessionStorage获取caseDes参数
+		const caseDesFromStorage = sessionStorage.getItem('ai_chat_caseDes');
+		if (caseDesFromStorage) {
+			// 将案件描述包装成指定的查询格式
+			const formattedCaseDes = `我有一个案件:${caseDesFromStorage},请给我一些调解策略和相关法条信息`;
+			setCaseDes(formattedCaseDes);
+			console.log('获取到案件描述:', formattedCaseDes);
+			// 使用后立即删除,避免重复使用
+			sessionStorage.removeItem('ai_chat_caseDes');
+		}
+		
 		setMoutedFlag(true);
 		if (visitWorkBenchStore) {
 			setTabActivekey(visitWorkBenchStore.tabActivekey);
@@ -2633,6 +2669,17 @@
 		}
 		window.addEventListener('beforeunload', handleBeforeUnload);
 	}, []);
+
+	// 监听caseDes变化,如果有caseDes则自动打开AiChat弹窗
+	useEffect(() => {
+		if (caseDes && caseDes.trim()) {
+			console.log('检测到案件描述,1秒后自动打开AiChat弹窗:', caseDes);
+			// 延迟1秒等页面渲染完毕后再打开弹窗
+			setTimeout(() => {
+				setAiQuestionView(true);
+			}, 1000);
+		}
+	}, [caseDes]);
 
 	useEffect(() => {
 		if (moutedFlag) {
@@ -4179,14 +4226,23 @@
 						</Button>
 					</div>
 				</Modal>
-				<AiQuestion
-					visible={AiQuestionView}
-					onClose={() => setAiQuestionView(false)}
-					aiData={aiData}
-					aiLawData={aiLawData}
-					caseDetailAi={caseDetailAi}
-					caseClaim={caseClaim}
-				/>
+				{/* 解纷数智人图标 */}
+				<div className="gradient-box" onClick={() => handleAi()} style={{ top: '50%', zIndex: 1000 }}>
+					<div><img src={Aimge} alt='' style={{ width: '111px', height: '120px' }} /></div>
+					<div className="gradient-box-mainTitle">解纷数智人</div>
+					<div className="gradient-box-mainTitle"><img src={aiMainTitle} alt='' style={{ width: '14px', height: '14px', marginTop: '4px' }} /></div>
+				</div>
+				{AiQuestionView && (
+					<div style={{position: 'fixed', top: 0, left: 0, width: '100vw', height: '100vh', background: 'rgba(0,0,0,0.15)', zIndex: 2000, display: 'flex', alignItems: 'center', justifyContent: 'center'}}>
+						<div style={{position: 'relative'}}>
+							<AiChat user="hugeinfo" onClose={() => {
+								setAiQuestionView(false)
+								setCaseDes('')
+								sessionStorage.removeItem('ai_chat_caseDes');
+							}} initialQuery={caseDes} visible={AiQuestionView} />
+						</div>
+					</div>
+				)}
 			</div>
 		</NewPage>
 	);
diff --git a/src/views/register/visit/SelfInspection.jsx b/src/views/register/visit/SelfInspection.jsx
index 5e95d76..190cff3 100644
--- a/src/views/register/visit/SelfInspection.jsx
+++ b/src/views/register/visit/SelfInspection.jsx
@@ -34,6 +34,22 @@
 	return $$.ax.request({ urlAi: `case-law/getRiskResult`, data, typeAi: 'post', service: 'mediate' });
 }
 
+//获取久拖未决结果
+function getLongRiskResultApi(data) {
+	// 构造AI请求参数格式
+	const aiRequestData = {
+		inputs: {
+			caseId: data.caseId,
+			caseText: data.caseText
+		},
+		response_mode: "blocking",
+		user: "system",
+		query: "分析案件是否为久拖未决案件",
+		conversation_id: data.caseId
+	};
+	return $$.ax.request({ url: `ai/chat/risk`, data: aiRequestData, type: 'post', service: 'sys' });
+}
+
 function embeddingTextToMilvusApi(data) {
 	return $$.ax.request({ urlAi: `case-law/embeddingTextToMilvus`, data, typeAi: 'post', service: 'mediate' });
 }
@@ -170,7 +186,7 @@
 		setIsReview(!isReview);
 	};
 
-  	//提交信息,需要校验规则
+	//提交信息,需要校验规则
 	const handleSubmit = async () => {
 		if (formRef.current) {
 			formRef.current.validate(undefined, (errors, values) => {
@@ -285,15 +301,18 @@
 			plaintiffsCertiNo,
 			defendantsCertiNo,
 		});
-		global.setSpinning(false);
+	
 		if (res.type) {
-		  if (res.data?.length < 1) {
-		    submitDisputeApi(data, isSelfAccept)
-		  } else {
-		    setRepeatData({ hisData: res.data, isSelfAccept, newData: data, visible: true })
-		    console.log(repeatData, 'repeatData');
-		  }
+			if (res.data?.length < 1) {
+				submitDisputeApi(data, isSelfAccept)
+			} else {
+				setRepeatData({ hisData: res.data, isSelfAccept, newData: data, visible: true })
+				global.setSpinning(false);
+				console.log(repeatData, 'repeatData');
+				return;
+			}
 		}
+	
 	};
 
 	async function submitDisputeApi(data, isSelfAccept) {
@@ -302,7 +321,7 @@
 		if (response.type) {
 			if (isSelfAccept) {
 				getRiskResult({ caseId: id, caseText: (data.caseDes || '') + '/n' + (data.caseClaim || '') });
-				embeddingTextToMilvus({ caseId: id, caseDes: data.caseDes || '', caseClaim: data.caseClaim || '' });
+				// embeddingTextToMilvus({ caseId: id, caseDes: data.caseDes || '', caseClaim: data.caseClaim || '' });
 				//自行受理
 				Message.success({
 					content: (
@@ -316,12 +335,30 @@
 					position: 'bottom',
 				});
 				navigate(`/mediate/visit/handleFeedback?caseTaskId=${response.data}&caseId=${id}`);
+				// 跳转后关闭转圈效果
+				global.setSpinning(false);
 			} else {
 				getRiskResult({ caseId: id, caseText: (data.caseDes || '') + '/n' + (data.caseClaim || '') });
-				embeddingTextToMilvus({ caseId: id, caseDes: data.caseDes || '', caseClaim: data.caseClaim || '' });
+				// 获取久拖未决分析结果并传入跳转页面
+				const longRiskResult = await getLongRiskResult({ caseId: id, caseText: (data.caseDes || '') + '/n' + (data.caseClaim || '') });
+				// embeddingTextToMilvus({ caseId: id, caseDes: data.caseDes || '', caseClaim: data.caseClaim || '' });
 				Message.success('提交成功!');
-				navigate(`/mediate/visit/visitWorkBench`, { replace: true });
+				
+				// 只有当longRiskResult === '1'时才带上参数
+				if (longRiskResult === '1') {
+					// 将AI分析结果作为参数传入跳转页面
+					const aiResultParam = longRiskResult ? `?aiResult=${longRiskResult}` : '';
+					// 当longRiskResult=='1'时,将caseDes存储到sessionStorage
+					if (data.caseDes) {
+						sessionStorage.setItem('ai_chat_caseDes', data.caseDes);
+					}
+					navigate(`/mediate/visit/visitWorkBench${aiResultParam}`, { replace: true });
+				} else {
+					navigate(`/mediate/visit/visitWorkBench`, { replace: true });
+				}
 				setCurrent(2);
+				// 跳转后关闭转圈效果
+				global.setSpinning(false);
 			}
 		}
 	}
@@ -333,6 +370,15 @@
 		}
 	}
 
+	// ai分析是否为久拖未决案件
+	async function getLongRiskResult(data) {
+		const res = await getLongRiskResultApi(data);
+		if (res.type) {
+			return res.data; // 返回AI分析结果
+		}
+		return null;
+	}
+
 	// 向量化处理
 	async function embeddingTextToMilvus(data) {
 		const res = await embeddingTextToMilvusApi(data);
diff --git a/src/views/register/visit/component/AiChat.jsx b/src/views/register/visit/component/AiChat.jsx
new file mode 100644
index 0000000..a25a4e8
--- /dev/null
+++ b/src/views/register/visit/component/AiChat.jsx
@@ -0,0 +1,273 @@
+import React, { useState, useRef, useEffect } from 'react';
+import { Input, Button, Spin } from '@arco-design/web-react';
+import * as $$ from '@/utils/utility';
+import { IconSend } from '@arco-design/web-react/icon';
+const { TextArea } = Input;
+
+const SESSION_KEY = 'ai_conversation_id';
+const MSG_KEY = 'ai_chat_messages';
+
+const AiChat = ({ user = 'system', onClose, initialQuery = '', visible = false }) => {
+  // 初始化时从sessionStorage恢复消息
+  const [messages, setMessages] = useState(() => {
+    try {
+      const msgStr = sessionStorage.getItem(MSG_KEY);
+      return msgStr ? JSON.parse(msgStr) : [];
+    } catch {
+      return [];
+    }
+  });
+  const [input, setInput] = useState('');
+  const [loading, setLoading] = useState(false);
+  const scrollRef = useRef(null);
+  const [conversationId, setConversationId] = useState(() => sessionStorage.getItem(SESSION_KEY) || '');
+  const [typing, setTyping] = useState(false);
+  const [hasInitialized, setHasInitialized] = useState(false);
+
+  // 每次messages变化时保存到sessionStorage
+  useEffect(() => {
+    sessionStorage.setItem(MSG_KEY, JSON.stringify(messages));
+    // 每次消息变化时自动滚动到底部
+    if (scrollRef.current) {
+      setTimeout(() => {
+        scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
+      }, 50);
+    }
+  }, [messages]);
+
+  // 初始化时如果有initialQuery,自动发送
+  useEffect(() => {
+    console.log('AiChat useEffect - initialQuery:', initialQuery, 'hasInitialized:', hasInitialized);
+    if (initialQuery && !hasInitialized) {
+      console.log('准备自动发送initialQuery:', initialQuery);
+      setHasInitialized(true);
+      setInput(initialQuery);
+      // 延迟一下确保组件完全加载
+      setTimeout(() => {
+        console.log('开始自动发送initialQuery:', initialQuery);
+        handleSend(initialQuery);
+      }, 100);
+    }
+  }, [initialQuery, hasInitialized]);
+
+  // 弹窗显示时自动滚动到底部
+  useEffect(() => {
+    if (visible && scrollRef.current) {
+      setTimeout(() => {
+        scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
+      }, 100);
+    }
+  }, [visible]);
+
+  // 关闭时不清除会话id和消息
+  // useEffect(() => {
+  //   return () => {
+  //     sessionStorage.removeItem(SESSION_KEY);
+  //     sessionStorage.removeItem(MSG_KEY);
+  //   };
+  // }, []);
+
+  // 发送消息
+  const handleSend = async (customInput = null) => {
+    const query = customInput || input.trim();
+    if (!query) return;
+    
+    setMessages([...messages, { role: 'user', text: query }]);
+    // 无论是否为自定义输入,都清空输入框
+    setInput('');
+    setLoading(true);
+
+    // 请求AI接口
+    const res = await $$.ax.request({
+      url: 'ai/chat/chat',
+      type: 'post',
+      service: 'sys',
+      data: {
+        inputs: {},
+        response_mode: 'blocking',
+        user,
+        query,
+        conversation_id: conversationId || '',
+      },
+    });
+
+    setLoading(false);
+
+    if (res.type && res.data && res.data.answer) {
+      // 打字机效果
+      let answer = res.data.answer;
+      let i = 0;
+      setTyping(true);
+      setMessages(msgs => [...msgs, { role: 'ai', text: '' }]);
+      const typeWriter = () => {
+        setMessages(msgs => {
+          const newMsgs = [...msgs];
+          newMsgs[newMsgs.length - 1] = { role: 'ai', text: answer.slice(0, i + 1) };
+          return newMsgs;
+        });
+        i++;
+        // 每次打字后自动滚动到底部
+        setTimeout(() => {
+          if (scrollRef.current) {
+            scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
+          }
+        }, 0);
+        if (i < answer.length) {
+          setTimeout(typeWriter, 15); // 速度可调
+        } else {
+          setTyping(false);
+          // 结束后再滚动一次
+          setTimeout(() => {
+            if (scrollRef.current) {
+              scrollRef.current.scrollTop = scrollRef.current.scrollHeight;
+            }
+          }, 0);
+        }
+      };
+      typeWriter();
+
+      // 首次返回有conversationId则保存
+      if (res.data.conversationId && !conversationId) {
+        setConversationId(res.data.conversationId);
+        sessionStorage.setItem(SESSION_KEY, res.data.conversationId);
+      }
+    }
+  };
+
+  // 回车发送
+  const handleKeyDown = (e) => {
+    if (e.key === 'Enter' && !e.shiftKey) {
+      e.preventDefault();
+      handleSend();
+    }
+  };
+
+  // 关闭对话框时清除会话id
+  const handleClose = () => {
+    // sessionStorage.removeItem(SESSION_KEY);
+    // setConversationId('');
+    if (onClose) onClose();
+  };
+
+  return (
+    <div style={{ width: '60vw', height: '80vh', margin: '40px', display: 'flex', flexDirection: 'column', background: '#fff', borderRadius: 8, boxShadow: '0 2px 16px #ddd', overflow: 'hidden' }}>
+      {/* 顶部标题栏 */}
+      <div style={{ height: 48, background: '#fff', display: 'flex', alignItems: 'center', justifyContent: 'space-between', padding: '0 24px', borderTopLeftRadius: 8, borderTopRightRadius: 8, borderBottom: '1px solid #f0f0f0' }}>
+        <span style={{ color: '#222', fontWeight: 500, fontSize: 16, letterSpacing: 1 }}>解纷数智人</span>
+        <span
+          onClick={handleClose}
+          style={{
+            width: 28,
+            height: 28,
+            borderRadius: '50%',
+            background: '#f0f1f2',
+            display: 'flex',
+            alignItems: 'center',
+            justifyContent: 'center',
+            cursor: 'pointer',
+            color: '#6b7280',
+            fontSize: 18,
+            transition: 'background 0.2s',
+          }}
+          onMouseOver={e => (e.currentTarget.style.background = '#e5e7eb')}
+          onMouseOut={e => (e.currentTarget.style.background = '#f0f1f2')}
+        >
+          ×
+        </span>
+      </div>
+      {/* 对话内容区 */}
+      <div
+        ref={scrollRef}
+        style={{
+          flex: 1,
+          overflowY: 'auto',
+          padding: 32,
+          borderBottom: '1px solid #f0f0f0',
+        }}
+      >
+        {messages.map((msg, idx) => (
+          <div
+            key={idx}
+            style={{
+              marginBottom: 18,
+              display: 'flex',
+              justifyContent: 'flex-start',
+            }}
+          >
+            <div
+              style={{
+                background: msg.role === 'user' ? '#e6f7ff' : '#f7f8fa',
+                color: '#1a6fb8',
+                borderRadius: 8,
+                padding: '10px 20px',
+                maxWidth: '95%',
+                minWidth: 60,
+                wordBreak: 'break-all',
+                textAlign: 'left',
+                boxShadow: msg.role === 'ai' ? '0 2px 8px #f0f1f2' : 'none',
+              }}
+            >
+              {msg.text}
+            </div>
+          </div>
+        ))}
+        {loading && (
+          <div style={{ textAlign: 'left', marginBottom: 18 }}>
+            <Spin />
+          </div>
+        )}
+      </div>
+      {/* 优化后的输入区 */}
+      <div style={{
+        background: '#fff',
+        borderRadius: 16,
+        boxShadow: '0 2px 8px #f0f1f2',
+        padding: 20,
+        margin: 24,
+        display: 'flex',
+        alignItems: 'flex-end',
+        minHeight: 80
+      }}>
+        <TextArea
+          value={input}
+          onChange={setInput}
+          onKeyDown={handleKeyDown}
+          placeholder="您想咨询什么..."
+          autoSize={{ minRows: 2, maxRows: 4 }}
+          style={{
+            border: 'none',
+            outline: 'none',
+            resize: 'none',
+            background: 'transparent',
+            fontSize: 16,
+            flex: 1,
+            boxShadow: 'none',
+            padding: '12px 0 12px 8px',
+            borderRadius: 12,
+            minHeight: 48
+          }}
+        />
+        <Button
+          type="primary"
+          icon={<IconSend />}
+          onClick={handleSend}
+          disabled={loading || !input.trim()}
+          style={{
+            marginLeft: 16,
+            borderRadius: 8,
+            height: 40,
+            minWidth: 64,
+            fontSize: 16,
+            display: 'flex',
+            alignItems: 'center',
+            justifyContent: 'center'
+          }}
+        >
+          发送
+        </Button>
+      </div>
+    </div>
+  );
+};
+
+export default AiChat;
diff --git a/src/views/register/visit/index.jsx b/src/views/register/visit/index.jsx
index ab6ff9b..8b36df4 100644
--- a/src/views/register/visit/index.jsx
+++ b/src/views/register/visit/index.jsx
@@ -52,6 +52,11 @@
 	return $$.ax.request({ urlAi: `case-law/getRepeatResult`, data, typeAi: 'post', service: 'mediate' });
 }
 
+//获取久拖未决结果
+function getLongRiskResultApi(data) {
+	return $$.ax.request({ url: `ai/chat/risk`, data, type: 'post', service: 'sys' });
+}
+
 function getCaseAndEventInfoApi(id) {
 	return $$.ax.request({ url: `thGridCitizenEvent/getCaseAndEventInfo?caseId=${id}`, type: 'get', service: 'mediate' });
 }
@@ -319,7 +324,6 @@
 			if (res.type) {
 				// setIsModalResult(res.data);
 				let userInfo = $$.getSessionStorage('customerSystemUser');
-				console.log(userInfo, 'userInfo');
 				setIsModalEventInfo({
 					...res.data,
 					mediateUnitName: userInfo?.unit || '',
@@ -599,7 +603,7 @@
 					setIsShowModal(false);
 					if (isModalSelfAccept) {
 						getRiskResult({ caseId: id, caseText: (isShowModalData.caseDes || '') + '/n' + (isShowModalData.caseClaim || '') });
-						embeddingTextToMilvus({ caseId: id, caseDes: isShowModalData.caseDes || '', caseClaim: isShowModalData.caseClaim || '' });
+						// embeddingTextToMilvus({ caseId: id, caseDes: isShowModalData.caseDes || '', caseClaim: isShowModalData.caseClaim || '' });
 						//自行受理
 						Message.success({
 							content: (
@@ -615,7 +619,7 @@
 						navigate(`/mediate/visit/handleFeedback?caseTaskId=${isModalResult.data}&caseId=${id}`);
 					} else {
 						getRiskResult({ caseId: id, caseText: (isShowModalData.caseDes || '') + '/n' + (isShowModalData.caseClaim || '') });
-						embeddingTextToMilvus({ caseId: id, caseDes: isShowModalData.caseDes || '', caseClaim: isShowModalData.caseClaim || '' });
+						// embeddingTextToMilvus({ caseId: id, caseDes: isShowModalData.caseDes || '', caseClaim: isShowModalData.caseClaim || '' });
 						Message.success('提交成功!');
 						navigate(`/mediate/visit/visitWorkBench`, { replace: true });
 						setCurrent(2);

--
Gitblit v1.8.0