+
@@ -13,6 +13,9 @@
import mdKatex from '@traptitech/markdown-it-katex';
import mila from 'markdown-it-link-attributes';
import hljs from 'highlight.js';
+ import './style/github-markdown.less';
+ import './style/highlight.less';
+ import './style/style.less';
const props = defineProps(['dateTime', 'text', 'inversion', 'error', 'loading']);
const textRef = ref();
@@ -34,7 +37,7 @@
const text = computed(() => {
const value = props.text ?? '';
- if (!props.inversion) return mdi.render(value);
+ if (props.inversion != 'user') return mdi.render(value);
return value;
});
diff --git a/jeecgboot-vue3/src/components/jeecg/AiChat/hooks/useChat.ts b/jeecgboot-vue3/src/views/super/airag/aiapp/chat/hooks/useChat.ts
similarity index 56%
rename from jeecgboot-vue3/src/components/jeecg/AiChat/hooks/useChat.ts
rename to jeecgboot-vue3/src/views/super/airag/aiapp/chat/hooks/useChat.ts
index 1eb9fcb2..d6a23698 100644
--- a/jeecgboot-vue3/src/components/jeecg/AiChat/hooks/useChat.ts
+++ b/jeecgboot-vue3/src/views/super/airag/aiapp/chat/hooks/useChat.ts
@@ -1,28 +1,28 @@
-import { useChatStore } from '@/store'
+import { useChatStore } from '@/store';
export function useChat() {
- const chatStore = useChatStore()
+ const chatStore = useChatStore();
const getChatByUuidAndIndex = (uuid: number, index: number) => {
- return chatStore.getChatByUuidAndIndex(uuid, index)
- }
+ return chatStore.getChatByUuidAndIndex(uuid, index);
+ };
const addChat = (uuid: number, chat: Chat.Chat) => {
- chatStore.addChatByUuid(uuid, chat)
- }
+ chatStore.addChatByUuid(uuid, chat);
+ };
const updateChat = (uuid: number, index: number, chat: Chat.Chat) => {
- chatStore.updateChatByUuid(uuid, index, chat)
- }
+ chatStore.updateChatByUuid(uuid, index, chat);
+ };
const updateChatSome = (uuid: number, index: number, chat: Partial
) => {
- chatStore.updateChatSomeByUuid(uuid, index, chat)
- }
+ chatStore.updateChatSomeByUuid(uuid, index, chat);
+ };
return {
addChat,
updateChat,
updateChatSome,
getChatByUuidAndIndex,
- }
+ };
}
diff --git a/jeecgboot-vue3/src/views/super/airag/aiapp/chat/hooks/useScroll.ts b/jeecgboot-vue3/src/views/super/airag/aiapp/chat/hooks/useScroll.ts
new file mode 100644
index 00000000..c71b6bbe
--- /dev/null
+++ b/jeecgboot-vue3/src/views/super/airag/aiapp/chat/hooks/useScroll.ts
@@ -0,0 +1,41 @@
+import type { Ref } from 'vue';
+import { nextTick, ref } from 'vue';
+
+type ScrollElement = HTMLDivElement | null;
+
+interface ScrollReturn {
+ scrollRef: Ref;
+ scrollToBottom: () => Promise;
+ scrollToTop: () => Promise;
+ scrollToBottomIfAtBottom: () => Promise;
+}
+
+export function useScroll(): ScrollReturn {
+ const scrollRef = ref(null);
+
+ const scrollToBottom = async () => {
+ await nextTick();
+ if (scrollRef.value) scrollRef.value.scrollTop = scrollRef.value.scrollHeight;
+ };
+
+ const scrollToTop = async () => {
+ await nextTick();
+ if (scrollRef.value) scrollRef.value.scrollTop = 0;
+ };
+
+ const scrollToBottomIfAtBottom = async () => {
+ await nextTick();
+ if (scrollRef.value) {
+ const threshold = 100; // Threshold, indicating the distance threshold to the bottom of the scroll bar.
+ const distanceToBottom = scrollRef.value.scrollHeight - scrollRef.value.scrollTop - scrollRef.value.clientHeight;
+ if (distanceToBottom <= threshold) scrollRef.value.scrollTop = scrollRef.value.scrollHeight;
+ }
+ };
+
+ return {
+ scrollRef,
+ scrollToBottom,
+ scrollToTop,
+ scrollToBottomIfAtBottom,
+ };
+}
diff --git a/jeecgboot-vue3/src/views/super/airag/aiapp/chat/js/chat.js b/jeecgboot-vue3/src/views/super/airag/aiapp/chat/js/chat.js
new file mode 100644
index 00000000..26259f3a
--- /dev/null
+++ b/jeecgboot-vue3/src/views/super/airag/aiapp/chat/js/chat.js
@@ -0,0 +1,159 @@
+// iframe-widget.js
+(function () {
+ let widgetInstance = null;
+ const defaultConfig = {
+ // ֧'top-left', 'top-right', 'bottom-left', 'bottom-right'
+ iconPosition: 'bottom-right',
+ //ͼĴС
+ iconSize: '30px',
+ //ͼɫ
+ iconColor: '#155eef',
+ //
+ appId: '',
+ //쵯Ŀ
+ chatWidth: '800px',
+ //쵯ĸ߶
+ chatHeight: '700px',
+ };
+
+ /**
+ * aiͼ
+ * @param config
+ */
+ function createAiChat(config) {
+ // ģʽȷֻһʵ
+ if (widgetInstance) {
+ return;
+ }
+
+ // ϲ
+ const finalConfig = { ...defaultConfig, ...config };
+
+ if (!finalConfig.appId) {
+ console.error('appIdΪգ');
+ return;
+ }
+ //
+ const container = document.createElement('div');
+ container.style.cssText = `
+ position: fixed;
+ z-index: 998;
+ ${getPositionStyles(finalConfig.iconPosition)}
+ cursor: pointer;
+ `;
+ // ͼ
+ const icon = document.createElement('div');
+ icon.style.cssText = `
+ width: ${finalConfig.iconSize};
+ height: ${finalConfig.iconSize};
+ background-color: ${finalConfig.iconColor};
+ border-radius: 50%;
+ box-shadow: rgba(0, 0, 0, 0.2) 0 4px 8px 0;
+ padding: 12px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ color: white;
+ `;
+ icon.innerHTML =
+ '';
+
+ // iframe
+ const iframeContainer = document.createElement('div');
+ iframeContainer.style.cssText = `
+ position: absolute;
+ right: 10px;
+ bottom: 10px;
+ width: ${finalConfig.chatWidth} !important;
+ height: ${finalConfig.chatHeight} !important;
+ background: white;
+ border-radius: 8px;
+ box-shadow: 0 0 20px rgba(0,0,0,0.2);
+ display: none;
+ z-index: 10000;
+ `;
+
+ // iframe
+ const iframe = document.createElement('iframe');
+ iframe.style.cssText = `
+ width: 100%;
+ height: 100%;
+ border: none;
+ border-radius: 8px;
+ `;
+
+ iframe.id = 'ai-app-chat-document';
+ iframe.src = getIframeSrc(finalConfig) + '/ai/app/chat/' + finalConfig.appId;
+ // رհť
+ const closeBtn = document.createElement('div');
+ closeBtn.innerHTML =
+ '';
+ closeBtn.style.cssText = `
+ position: absolute;
+ right: -3px;
+ top: -10px;
+ cursor: pointer;
+ background: white;
+ width: 25px;
+ height: 25px;
+ border-radius: 50%;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ box-shadow: 0 2px 5px rgba(0,0,0,0.2);
+ `;
+
+ // װԪ
+ iframeContainer.appendChild(closeBtn);
+ iframeContainer.appendChild(iframe);
+ document.body.appendChild(iframeContainer);
+ container.appendChild(icon);
+ document.body.appendChild(container);
+
+ // ¼
+ icon.addEventListener('click', () => {
+ iframeContainer.style.display = 'block';
+ });
+
+ closeBtn.addEventListener('click', () => {
+ iframeContainer.style.display = 'none';
+ });
+
+ // ʵ
+ widgetInstance = {
+ remove: () => {
+ container.remove();
+ iframeContainer.remove();
+ },
+ };
+ }
+
+ /**
+ * ȡλϢ
+ *
+ * @param position
+ * @returns {*|string}
+ */
+ function getPositionStyles(position) {
+ const positions = {
+ 'top-left': 'top: 20px; left: 20px;',
+ 'top-right': 'top: 20px; right: 20px;',
+ 'bottom-left': 'bottom: 20px; left: 20px;',
+ 'bottom-right': 'bottom: 20px; right: 20px;',
+ };
+ return positions[position] || positions['bottom-right'];
+ }
+
+ /**
+ * ȡsrcַ
+ */
+ function getIframeSrc(finalConfig) {
+ const specificScript = document.getElementById("e7e007dd52f67fe36365eff636bbffbd");
+ if (specificScript) {
+ return specificScript.src.substring(0, specificScript.src.indexOf('/', specificScript.src.indexOf('://') + 3));
+ }
+ }
+
+ // ¶ȫַ
+ window.createAiChat = createAiChat;
+})();
diff --git a/jeecgboot-vue3/src/views/super/airag/aiapp/chat/js/useScroll.ts b/jeecgboot-vue3/src/views/super/airag/aiapp/chat/js/useScroll.ts
new file mode 100644
index 00000000..c71b6bbe
--- /dev/null
+++ b/jeecgboot-vue3/src/views/super/airag/aiapp/chat/js/useScroll.ts
@@ -0,0 +1,41 @@
+import type { Ref } from 'vue';
+import { nextTick, ref } from 'vue';
+
+type ScrollElement = HTMLDivElement | null;
+
+interface ScrollReturn {
+ scrollRef: Ref;
+ scrollToBottom: () => Promise;
+ scrollToTop: () => Promise;
+ scrollToBottomIfAtBottom: () => Promise;
+}
+
+export function useScroll(): ScrollReturn {
+ const scrollRef = ref(null);
+
+ const scrollToBottom = async () => {
+ await nextTick();
+ if (scrollRef.value) scrollRef.value.scrollTop = scrollRef.value.scrollHeight;
+ };
+
+ const scrollToTop = async () => {
+ await nextTick();
+ if (scrollRef.value) scrollRef.value.scrollTop = 0;
+ };
+
+ const scrollToBottomIfAtBottom = async () => {
+ await nextTick();
+ if (scrollRef.value) {
+ const threshold = 100; // Threshold, indicating the distance threshold to the bottom of the scroll bar.
+ const distanceToBottom = scrollRef.value.scrollHeight - scrollRef.value.scrollTop - scrollRef.value.clientHeight;
+ if (distanceToBottom <= threshold) scrollRef.value.scrollTop = scrollRef.value.scrollHeight;
+ }
+ };
+
+ return {
+ scrollRef,
+ scrollToBottom,
+ scrollToTop,
+ scrollToBottomIfAtBottom,
+ };
+}
diff --git a/jeecgboot-vue3/src/components/jeecg/AiChat/components/presetQuestion.vue b/jeecgboot-vue3/src/views/super/airag/aiapp/chat/presetQuestion.vue
similarity index 63%
rename from jeecgboot-vue3/src/components/jeecg/AiChat/components/presetQuestion.vue
rename to jeecgboot-vue3/src/views/super/airag/aiapp/chat/presetQuestion.vue
index 5ce39dd0..8a2227e8 100644
--- a/jeecgboot-vue3/src/components/jeecg/AiChat/components/presetQuestion.vue
+++ b/jeecgboot-vue3/src/views/super/airag/aiapp/chat/presetQuestion.vue
@@ -19,7 +19,13 @@