diff --git a/pages.json b/pages.json index 59b75b2..20220fa 100644 --- a/pages.json +++ b/pages.json @@ -78,16 +78,6 @@ } } }, - { - "path" : "pages/text/text", - "style" : { - "navigationBarTitleText" : "" - } - }, - { - "path" : "pages/text/text2", - "style" : {} - }, { "path": "pages/CloudDatabase/CloudDatabase", "style": { @@ -111,6 +101,18 @@ "softinputMode": "adjustResize" } } + }, + { + "path" : "pages/text/text", + "style" : { + "navigationBarTitleText" : "" + } + }, + { + "path" : "pages/text/IntuitiveAipptViewer", + "style" : { + "navigationBarTitleText" : "" + } } ], "uniIdRouter" : {}, diff --git a/pages/text/IntuitiveAipptViewer.vue b/pages/text/IntuitiveAipptViewer.vue new file mode 100644 index 0000000..a3ecaf0 --- /dev/null +++ b/pages/text/IntuitiveAipptViewer.vue @@ -0,0 +1,804 @@ + + + + + diff --git a/pages/text/text.vue b/pages/text/text.vue index 30a53d2..e53e8b2 100644 --- a/pages/text/text.vue +++ b/pages/text/text.vue @@ -1,160 +1,1732 @@ \ No newline at end of file + const showNewChatModal = ref(false) + const closeNewChatModal = () => { + showNewChatModal.value = false; + } + + // 侧边栏相关 + const isChatSidebar = ref(false) + const handleChatSidebar = () => { + isChatSidebar.value = !isChatSidebar.value + } + + // 跳转到工作区 + const goWorkSpace = () => { + // 保存当前会话id,确保从工作区返回时能恢复 + if (currentSessionId.value) { + uni.setStorageSync('currentSessionId', currentSessionId.value) + } + uni.navigateTo({ + url: '/pages/WorkSpace/WorkSpace' + }) + } + + // 跳转到云端数据库 + const goCloudDatabase = () => { + // 保存当前会话id,确保从工作区返回时能恢复 + if (currentSessionId.value) { + uni.setStorageSync('currentSessionId', currentSessionId.value) + } + uni.navigateTo({ + url: '/pages/CloudDatabase/CloudDatabase' + }) + } + + const logOut = () => { + uni.reLaunch({ + url: '/pages/Login/Login' + }) + } + + const previewFileArray = ref([]) + const uploadPhoto = () => { + console.log('点击了拍照上传'); + uni.chooseImage({ + count: 1, + sourceType: ['camera', 'album'], + success: (res) => { + const tempFiles = res.tempFiles; + const tempFilePaths = res.tempFilePaths; + tempFiles.forEach((file, index) => { + previewFileArray.value.push({ + path: tempFilePaths[index], // 图片路径(用于显示) + name: file.name || `photo_${Date.now()}_${index}.jpg`, // 文件名 + size: file.size, // 文件大小 + type: 'image', // 文件类型标识 + tempFile: file // 原始文件对象 + }); + }); + }, + fail: (err) => { + console.error('选择图片失败', err); + } + }) + } + // 删除指定图片 + const deleteImage = (index) => { + previewFileArray.value.splice(index, 1); + }; + + const fileList = ref([]) + const uploadFile = () => { + console.log('点击了上传文件'); + chooseFile({ + count: 5, + type: 'all', + success: (res) => { + console.log("成功了"); + // 赋值文件列表 + fileList.value = res.tempFiles; + // 把所有文件 push 到预览数组(展开运算符 ...) + previewFileArray.value.push(...res.tempFiles); + uni.showToast({ + title: `已选择 ${res.tempFiles.length} 个文件`, + icon: 'success' + }) + }, + fail: (err) => { + console.error('选择失败:', err) + uni.showToast({ + title: '选择失败', + icon: 'error' + }) + } + }) + } + // 连接socket + const handleConnect = () => { + return new Promise((resolve, reject) => { + if (socketStore.isConnected) return resolve() + + // 监听连接成功 + const unwatch = watch(() => socketStore.isConnected, (connected) => { + if (connected) { + unwatch() + resolve() + } + }) + + // 监听连接错误 + const unwatchError = watch(() => socketStore.connectionStatus, (status) => { + if (status === 'error') { + unwatch() + unwatchError() + reject(new Error('连接失败')) + } + }) + + socketStore.connect({ + token: userToken.value, + conversationId: currentSessionId.value + }) + }) + } + + // // 监听socket连接是否成功 + // watch(()=>socketStore.isConnected,(newVal)=>{ + // if(newVal&&) + // }) + + const isThinking = ref(false) + const isSelfSent = ref(false) // 是否当前设备发起的 AI 对话(用于区分弹窗/仅锁输入) + const isUploading = ref(false) + const uploadedFiles = ref([]) + const streamingMessageId = ref(null) // 流式消息的临时 ID,用于实时更新 AI 回复 + + // 消息详情弹窗 + const showMessageModal = ref(false) + const detailLinks = ref([]) + // 点击气泡内容:拦截链接点击,否则 AI 对话弹出详情 + const handleChatContentClick = (e, message) => { + // 在事件路径中向上查找 .chat-inline-link 元素 + let el = e.target + while (el && el.classList) { + if (el.classList.contains('chat-inline-link')) { + const url = el.getAttribute('data-url') || (el.dataset && el.dataset.url) + if (url) { + openLink(url) + } + return + } + el = el.parentElement + } + // 非链接点击:仅 AI 对话弹出详情窗 + if (message.role) { + showMessageDetail(message) + } + } + + const extractLinks = (text) => { + const seen = new Set() + const result = [] + // 1. 匹配 Markdown 链接 [文件名](url) + const mdLinkRegex = /\[([^\]]*)\]\((https?:\/\/[^\s<>"{}|\\^`\[\]()]+)\)/gi + const mdLinks = [...text.matchAll(mdLinkRegex)] + mdLinks.forEach(m => { + const name = m[1].trim() + const url = m[2] + if (!seen.has(url)) { + seen.add(url) + result.push({ + url, + name: name || url + }) + } + }) + // 2. 去除已匹配的 [xxx](url) 后,再搜裸 URL + let cleaned = text.replace(mdLinkRegex, '') + const bareRegex = /https?:\/\/[^\s<>"{}|\\^`\[\]()]+/gi + const bareMatches = cleaned.match(bareRegex) + if (bareMatches) { + bareMatches.forEach(url => { + if (!seen.has(url)) { + seen.add(url) + result.push({ + url, + name: extractFileNameFromUrl(url) + }) + } + }) + } + return result + } + const extractFileNameFromUrl = (urlStr) => { + try { + const segments = new URL(urlStr).pathname.split('/').filter(s => s) + if (segments.length > 0) { + return decodeURIComponent(segments[segments.length - 1]) + } + } catch (e) { + /* fallback */ + } + const parts = urlStr.split('/').filter(s => s) + if (parts.length > 0) { + const last = parts[parts.length - 1] + const qIdx = last.indexOf('?') + return qIdx > -1 ? last.substring(0, qIdx) : last + } + return urlStr + } + const showMessageDetail = (message) => { + detailLinks.value = extractLinks(message.content || '') + showMessageModal.value = true + } + const closeMessageDetail = () => { + showMessageModal.value = false + detailLinks.value = [] + } + const downloadToast = ref({ + show: false, + type: 'loading', + message: '' + }) + let downloadToastTimer = null + + const showDownloadToast = (type, message, duration) => { + if (downloadToastTimer) clearTimeout(downloadToastTimer) + downloadToast.value = { + show: true, + type, + message + } + if (duration > 0) { + downloadToastTimer = setTimeout(() => { + downloadToast.value = { + show: false, + type: 'loading', + message: '' + } + }, duration) + } + } + const hideDownloadToast = () => { + if (downloadToastTimer) clearTimeout(downloadToastTimer) + downloadToast.value = { + show: false, + type: 'loading', + message: '' + } + } + + const openLink = (url) => { + downloadAndHandle(url) + } + + const downloadAndHandle = async (url) => { + try { + showDownloadToast('loading', '下载中...', 0) + const workspaceId = uni.getStorageSync('workspace_id') || '' + const result = await conversationMessageDownload(userToken.value, workspaceId, url) + const downloadUrl = typeof result === 'string' ? result : (result?.url || result?.download_url || '') + if (!downloadUrl) { + showDownloadToast('error', '获取下载地址失败', 2500) + return + } + uni.downloadFile({ + url: downloadUrl, + success: (downloadRes) => { + if (downloadRes.statusCode === 200) { + hideDownloadToast() + uni.openDocument({ + filePath: downloadRes.tempFilePath, + showMenu: true, + fail: () => { + showDownloadToast('error', '无法打开此文件', 2500) + } + }) + } else { + showDownloadToast('error', '下载失败', 2500) + } + }, + fail: () => { + showDownloadToast('error', '下载失败', 2500) + } + }) + } catch (err) { + showDownloadToast('error', '下载失败: ' + (err.message || err), 2500) + } + } + + + const sendMessage = async () => { + const message = textMessage.value.trim() + if (!message && previewFileArray.value.length === 0) return + + // 有文件时先上传 + if (previewFileArray.value.length > 0) { + isUploading.value = true + uni.showLoading({ + title: '上传文件中...', + mask: true + }) + try { + const results = [] + for (const item of previewFileArray.value) { + const result = await uploadFileToServer(item.path) + results.push(result) + } + uploadedFiles.value = results + previewFileArray.value = [] + } catch (err) { + uni.hideLoading() + console.error('文件上传失败:', err) + uni.showToast({ + title: '文件上传失败,请重试', + icon: 'error' + }) + isUploading.value = false + return + } + uni.hideLoading() + isUploading.value = false + } + + if (ChatType.value === 0) sendAIMessage(); + if (ChatType.value === 1) sendFriendMessage(); + if (ChatType.value === 2) sendGroupMessage(); + } + // 发送好友消息 + const sendFriendMessage = async () => { + if (!friendSocketStore.isConnecting) await handleFriendConnect(); + const receiverId = getReceiverId() + const hasFiles = uploadedFiles.value.length > 0 + const body = { + "command": 1, + "sender": UserId.value, + "receiver": receiverId, + "avatar": "", + "sessionId": currentSessionId.value, + "message": textMessage.value, + "callBackMessage": false + } + if (hasFiles) { + body.contentType = 1 + body.uploadVos = uploadedFiles.value + } + const data = { + "version": "1.1", + "body": body + } + friendSocketStore.send(data) + const newMessageData = { + "sender": UserId.value, + "receiver": receiverId, + "content": textMessage.value, + "taskId": null, + "avatar": null, + "createTime": "", + "messageType": "null", + "contentJson": hasFiles ? JSON.stringify(uploadedFiles.value) : 'null' + } + allmessages.value = [...allmessages.value, newMessageData] + textMessage.value = '' + uploadedFiles.value = [] + setTimeout(() => { + takeFriendMessages() + }, 1000) + } + + // 发送群聊消息 + const sendGroupMessage = async () => { + if (!friendSocketStore.isConnecting) await handleFriendConnect(); + const hasFiles = uploadedFiles.value.length > 0 + const body = { + "command": 9, + "groupId": currentSessionId.value, + "message": textMessage.value || "", + "callBackMessage": hasFiles ? false : true, + "messageType": hasFiles ? 1 : 0, + "sender": UserId.value + } + if (hasFiles) { + body.uploadVos = uploadedFiles.value + } + const data = { + "version": "1.1", + "body": body + } + friendSocketStore.send(data) + const newMessageData = { + "id": allmessages.value.length + 1, + "message": textMessage.value, + "messageType": hasFiles ? 1 : 0, + "groupId": null, + "createTime": '', + "contentJson": hasFiles ? JSON.stringify(uploadedFiles.value) : '', + "sender": UserId.value + } + allmessages.value = [...allmessages.value, newMessageData] + textMessage.value = '' + uploadedFiles.value = [] + setTimeout(() => { + takeGroupMessages() + }, 1000) + } + // // 发送AI消息 + // const sendAIMessage = async () => { + // try { + // handleConnect(); + // let messageContent = textMessage.value + // if (uploadedFiles.value.length) { + // const fileJson = JSON.stringify(uploadedFiles.value) + // messageContent = messageContent ? + // `${messageContent}\n[文件信息:${fileJson}]` : + // `[文件信息:${fileJson}]` + // } + // const message_id = await addMessageDict(userToken.value, 'user', currentSessionId.value, + // messageContent) + // if (message_id) { + // textMessage.value = '' + // uploadedFiles.value = [] + // isThinking.value = true + // } + // await waitForAIResponse(); + // } catch (error) { + // isThinking.value = false + // uni.showToast({ + // title: `用户发送消息失败${error}`, + // icon: 'error' + // }) + // } + // } + // AI 回复超时定时器 + // let aiResponseTimer = null + + // 发送AI消息 + const sendAIMessage = async () => { + try { + await handleConnect(); + let messageContent = textMessage.value + if (uploadedFiles.value.length) { + const fileJson = JSON.stringify(uploadedFiles.value) + messageContent = messageContent ? + `${messageContent}\n[文件信息:${fileJson}]` : + `[文件信息:${fileJson}]` + } + const data = { + "type": "chat", + "conversation_id": currentSessionId.value, + "content": messageContent + } + socketStore.send(data) + isThinking.value = true + isSelfSent.value = true + // 立即添加用户消息到列表(使用完整的 messageContent,包含文件信息) + allmessages.value = [...allmessages.value, { + role: 'user', + content: messageContent + }] + // 添加流式消息气泡(加载中状态) + streamingMessageId.value = 'streaming-' + Date.now() + allmessages.value = [...allmessages.value, { + role: 'assistant', + content: '', + _streaming: true, + _id: streamingMessageId.value + }] + textMessage.value = '' + uploadedFiles.value = [] + scrollToBottom() + } catch (error) { + isThinking.value = false + uni.showToast({ + title: `用户发送消息失败${error}`, + icon: 'error' + }) + } + } + + // 中断 AI 对话 + const stopConversation = async () => { + console.log('中断对话 - 当前会话ID:', currentSessionId.value) + try { + await socketStore.send({ + type: 'stop', + conversation_id: currentSessionId.value + }) + console.log('中断指令已发送成功') + } catch (err) { + console.error('中断指令发送失败:', err) + } + // 移除流式消息占位 + if (streamingMessageId.value) { + allmessages.value = allmessages.value.filter(m => m._id !== streamingMessageId.value) + streamingMessageId.value = null + } + // 立即关闭弹窗,清除超时 + // clearTimeout(aiResponseTimer) + isThinking.value = false + socketStore.isThinking = false + isSelfSent.value = false + textMessage.value = '' + uploadedFiles.value = [] + // takeConversationMessages 由后端中断确认驱动(见 handleBusinessMessage) + uni.showToast({ + title: '已中断', + icon: 'none', + duration: 1500 + }) + } + + // 全局监听 socketStore.isThinking:跨设备同步思考状态,锁/解锁输入框 + watch(() => socketStore.isThinking, (newVal, oldVal) => { + console.log("isThinking 变化:", oldVal, "→", newVal, "messageString:", socketStore.messageString); + // false → true:后端开始回复,锁定输入框 + if (!oldVal && newVal) { + console.log('AI开始回复(跨设备同步),锁定输入框'); + isThinking.value = true + // 非本设备发起时,也需要添加流式气泡 + if (!streamingMessageId.value) { + streamingMessageId.value = 'streaming-' + Date.now() + allmessages.value = [...allmessages.value, { + role: 'assistant', + content: '', + _streaming: true, + _id: streamingMessageId.value + }] + } + return + } + // true → false:AI 回复结束或中断,解锁输入框并刷新消息列表 + if (oldVal && !newVal) { + console.log('AI回复结束(正常/中断),解锁并刷新消息列表'); + // 移除流式消息气泡,从服务器加载完整消息 + if (streamingMessageId.value) { + allmessages.value = allmessages.value.filter(m => m._id !== streamingMessageId.value) + streamingMessageId.value = null + } + takeConversationMessages() + isThinking.value = false + isSelfSent.value = false + textMessage.value = '' + uploadedFiles.value = [] + } + }) + + // 监听 auth_fail,当 pc 端不在线时弹窗提示并取消思考状态 + watch(() => socketStore.authFailReason, (reason) => { + if (reason === 'pc offline') { + isThinking.value = false + socketStore.authFailReason = '' + nextTick(() => { + uni.showModal({ + title: '提示', + content: 'PC端不在线,不可聊天', + showCancel: false, + confirmText: '知道了' + }) + }) + } + }) + + // 监听流式消息内容,实时更新 AI 回复(流式输出核心) + watch(() => socketStore.messageString, (newContent) => { + if (!streamingMessageId.value || !socketStore.isThinking) return + + const idx = allmessages.value.findIndex(m => m._id === streamingMessageId.value) + if (idx >= 0) { + // 使用 splice 保证 Vue 响应式更新 + const updated = { + ...allmessages.value[idx], + content: newContent + } + allmessages.value.splice(idx, 1, updated) + scrollToBottom() + } + }) + + const textMessage = ref('') + + // 获取用户历史会话列表 + const UserConversations = ref([]) + // 当前会话id + const currentSessionId = ref('') + const userToken = ref('') + const UserId = ref('') + const UserAvatar = ref('') + const UserData = ref(null) + + // 获取当前用户信息(提前加载,用于显示头像等) + const takeUserInfo = async () => { + try { + userToken.value = getToken(); + UserData.value = await getUserInfo(userToken.value) + UserId.value = UserData.value._id; + UserAvatar.value = UserData.value.avatar || '' + console.log("用户信息已加载:", UserId.value, UserAvatar.value); + } catch (error) { + console.error("获取用户信息失败:", error); + } + } + + // 获取对话列表 + const takeUserConversations = async () => { + try { + userToken.value = getToken(); + console.log("token:", userToken.value); + UserConversations.value = await getUserConversations(userToken.value) || []; + // console.log("UserConversations:", JSON.stringify(UserConversations.value) ); + // 优先恢复已保存的会话id,避免刷新到列表第一个 + const savedSessionId = getCurrentSessionId(); + if (savedSessionId && UserConversations.value.some(c => c._id === savedSessionId)) { + currentSessionId.value = savedSessionId; + } else if (UserConversations.value.length > 0) { + currentSessionId.value = UserConversations.value[0]._id; + } else { + currentSessionId.value = ''; + } + console.log('保存会话id:', currentSessionId.value); + uni.setStorageSync('currentSessionId', currentSessionId.value) + } catch (error) { + uni.showToast({ + title: `获取会话列表失败${error}`, + icon: 'error' + }) + } + } + // 好友列表 + // 好友消息列表(不包括头像) + const FriendInfoList = ref([]) + // 获取用户好友列表 + const takeFriendList = async () => { + try { + console.log("开始获取好友列表"); + // UserId 已在 takeUserInfo 中获取 + const friendList = await getChatFriend(UserId.value) + console.log("friendList:", friendList); + if (friendList && friendList.length) { + FriendInfoList.value = await takeUserAvatar(friendList) + } else { + FriendInfoList.value = [] + console.log("好友列表为空"); + } + } catch (error) { + console.error("获取好友列表失败:", error); + FriendInfoList.value = [] + } finally { + UserConversations.value = FriendInfoList.value + } + } + + // 从好友列表中获取单个用户头像 + const takeTalkUserAvatar = (userId) => { + if (!userId) return null + const user = FriendInfoList.value.find(item => item.receiver === userId) + return user?.avatar || null + } + // 获取好友头像 + const takeUserAvatar = async (friendList) => { + if (!friendList?.length) return [] + try { + const friendIds = friendList.map(item => item.receiver) + const friendAvatarList = await getUserAvatar(userToken.value, friendIds) + // 创建map,用于快速查找 + const userMap = new Map(friendAvatarList.map(user => [user.user_id, user]) || []) + // 合并数据 + return friendList.map(friend => { + const userInfo = userMap.get(friend.receiver) + return { + ...friend, + avatar: userInfo?.avatar || null + } + }) + } catch (err) { + console.error('获取好友头像失败', err); + return friendList + } + } + + // 群聊列表 + const GroupList = ref([]) + // 获取用户群聊列表 + const takeGroupList = async () => { + try { + console.log("开始获取群聊列表"); + // UserId 已在 takeUserInfo 中获取 + GroupList.value = await getGroup(UserId.value) + // console.log("GroupList1:", JSON.stringify(GroupList.value)); + } catch (error) { + console.error("获取群聊列表失败:", error); + GroupList.value = [] + } finally { + UserConversations.value = GroupList.value + } + } + + // 群成员列表(包含头像) + const groupMemberList = ref([]) + // 获取群成员头像 + const takeGroupMemberAvatar = async (memberList) => { + if (!memberList?.length) return [] + try { + const memberIds = memberList.map(item => item.groupContactId) + console.log("请求头像的ID列表:", memberIds) + const memberAvatarList = await getUserAvatar(userToken.value, memberIds) + console.log("头像接口返回数据:", memberAvatarList) + // 创建map,用于快速查找 + const userMap = new Map(memberAvatarList.map(user => [user.user_id, user]) || []) + console.log("userMap的keys:", Array.from(userMap.keys())) + // 合并数据 + return memberList.map(member => { + const memberId = member.groupContactId + const userInfo = userMap.get(memberId) + console.log(`查找 ${memberId} 的头像:`, userInfo) + return { + ...member, + avatar: userInfo?.avatar || null + } + }) + } catch (err) { + console.error('获取群成员头像失败', err); + return memberList + } + } + + // 从群成员列表中获取单个用户头像 + const getGroupMemberAvatarById = (userId) => { + if (!userId) return null + const member = groupMemberList.value.find(item => item.groupContactId === userId) + return member?.avatar || null + } + + // 当前会话的消息 + // const currentMessages = ref([]) + const allmessages = ref([]) + // const pageInfoNumber = 10; + + const scrollToView = ref('') + const bottomToggle = ref(false) + // 加载状态和分页相关变量 + const isLoadingMore = ref(false) // 是否正在加载更多 + const currentPage = ref(1) // 当前页码(如果后端支持分页) + // 计算属性:自动根据 allmessages 和 currentPage 计算显示消息 + const currentMessages = computed(() => { + // return allmessages.value.slice(-pageInfoNumber); + return allmessages.value; + }) + + + // 获取对话消息 + const takeConversationMessages = async () => { + try { + allmessages.value = await getConversationMessages(userToken.value, currentSessionId.value) || []; + // console.log("获取到的所有消息:", JSON.stringify(allmessages.value) ); + // currentMessages.value = allmessages.value.slice(-pageInfoNumber); + // 数据获取后执行滚动 + scrollToBottom(); + } catch (error) { + uni.showToast({ + title: `获取ai会话内容失败${error}`, + icon: 'none' + }) + } + } + const takeFriendMessages = async () => { + try { + allmessages.value = await getFriendMessages(currentSessionId.value) || []; + // console.log("好友消息:", JSON.stringify(allmessages.value)); + // 数据获取后执行滚动 + scrollToBottom(); + } catch (error) { + uni.showToast({ + title: `获取好友会话消息失败${error}`, + icon: 'none' + }) + } + } + const takeGroupMessages = async () => { + try { + allmessages.value = await getGroupMessages(currentSessionId.value) || []; + console.log("群聊消息:", allmessages.value); + // 获取群成员列表并获取头像 + const memberList = await getGroupMemberList(currentSessionId.value) + console.log("获取到的群成员列表:", memberList); + if (memberList && memberList.length) { + groupMemberList.value = await takeGroupMemberAvatar(memberList) + console.log("群成员列表(带头像):", groupMemberList.value); + } else { + groupMemberList.value = [] + } + // 数据获取后执行滚动 + scrollToBottom(); + } catch (error) { + uni.showToast({ + title: `获取群聊会话失败${error}`, + icon: 'none' + }) + } + } + // // 加载更多消息 + // const loadMoreMessages = async () => { + // console.log('加载更多信息'); + // if (allmessages.value.length > pageInfoNumber * currentPage.value) { + // currentPage.value += 1; + // const newMessages = allmessages.value.slice(-pageInfoNumber * currentPage.value); + // const addMessage = newMessages.length - currentMessages.value.length + // currentMessages.value = newMessages + // // 等待DOM更新 + // await nextTick() + // scrollToView.value = 'msg-' + (addMessage + 1); + + // } + // // currentMessages.value = allmessages.value.slice(-pageInfoNumber); + // } + + + + // 监听滚动 + const onScroll = (e) => {} + // 滑动到底部 + const scrollToBottom = async () => { + await nextTick(); + if (currentMessages.value.length > 0) { + // 两个锚点交替切换,确保每次调用都能触发滚动 + bottomToggle.value = !bottomToggle.value + scrollToView.value = bottomToggle.value ? 'chat-bottom-a' : 'chat-bottom-b' + } + } + + // 监听好友消息 + watch(() => friendSocketStore.MessageReceived, (newId) => { + console.log("收到了好友消息"); + if (newId) { + console.log("ChatType:", ChatType.value); + switch (ChatType.value) { + case 0: + takeConversationMessages(); + friendSocketStore.MessageReceived = false + break; + case 1: + takeFriendMessages(); + friendSocketStore.MessageReceived = false + break; + case 2: + takeGroupMessages(); + friendSocketStore.MessageReceived = false + break; + // 可选:默认兜底 + default: + console.log("default 分支"); + friendSocketStore.MessageReceived = false + break; + } + } + }, { + immediate: true + }) + + watch(currentSessionId, (newId) => { + if (newId) { + uni.setStorageSync('currentSessionId', currentSessionId.value) + switch (ChatType.value) { + case 0: + takeConversationMessages(); + // 切换 AI 会话时重新连接 socket(绑定新 conversationId) + socketStore.disconnect() + handleConnect() + break; + case 1: + takeFriendMessages(); + break; + case 2: + takeGroupMessages(); + break; + default: + takeConversationMessages(); + break; + } + } + }, { + immediate: true + }) + + onMounted(() => { + // currentSessionId.value = getCurrentSessionId() + }) + onShow(async () => { + // 优先获取用户信息(用于显示头像等) + await takeUserInfo(); + + const chatType = getChatType() + ChatType.value = chatType + const sessionId = getCurrentSessionId() + + // 只有在会话ID存在时才获取消息 + if (sessionId) { + currentSessionId.value = sessionId + } + + if (chatType === 0) { + await takeUserConversations(); + // 只有在会话ID存在时才获取消息 + if (currentSessionId.value) { + takeConversationMessages(); + } + // AI 对话页面:主动建立 socket 连接,确保消息实时推送 + if (userToken.value && currentSessionId.value && !socketStore.isConnected) { + handleConnect() + } + } else if (chatType === 1) { + await takeFriendList() + takeFriendMessages(); + handleFriendConnect() + } else if (chatType === 2) { + await takeGroupList() + await handleFriendConnect() + await takeGroupMessages() + } + }) + + // 离开 AI 对话页面时断开 socket + onUnload(() => { + socketStore.disconnect() + }) + + + + + + \ No newline at end of file diff --git a/pages/text/text2.vue b/pages/text/text2.vue deleted file mode 100644 index bad66fb..0000000 --- a/pages/text/text2.vue +++ /dev/null @@ -1,538 +0,0 @@ - - - - \ No newline at end of file diff --git a/unpackage/dist/dev/app-plus/app-config-service.js b/unpackage/dist/dev/app-plus/app-config-service.js index 84270da..3dd7454 100644 --- a/unpackage/dist/dev/app-plus/app-config-service.js +++ b/unpackage/dist/dev/app-plus/app-config-service.js @@ -1,8 +1,8 @@ ;(function(){ let u=void 0,isReady=false,onReadyCallbacks=[],isServiceReady=false,onServiceReadyCallbacks=[]; - const __uniConfig = {"pages":[],"globalStyle":{"backgroundColor":"#F8F8F8","navigationBar":{"backgroundColor":"#F8F8F8","titleText":"uni-app","type":"default","titleColor":"#000000"},"isNVue":false},"nvue":{"compiler":"uni-app","styleCompiler":"uni-app","flex-direction":"column"},"renderer":"auto","appname":"test1","splashscreen":{"alwaysShowBeforeRender":true,"autoclose":true},"compilerVersion":"5.07","entryPagePath":"pages/Login/Login","entryPageQuery":"","realEntryPagePath":"","networkTimeout":{"request":60000,"connectSocket":60000,"uploadFile":60000,"downloadFile":60000},"locales":{},"darkmode":false,"themeConfig":{}}; - const __uniRoutes = [{"path":"pages/Login/Login","meta":{"isQuit":true,"isEntry":true,"titleNView":false,"bounce":"none","softinputMode":"adjustResize","navigationBar":{"titleText":"登录页面","style":"custom","type":"default"},"isNVue":false}},{"path":"pages/Chat/Chat","meta":{"titleNView":false,"bounce":"none","softinputMode":"adjustResize","navigationBar":{"titleText":"聊天页面(主页面)","style":"custom","type":"default"},"isNVue":false}},{"path":"pages/WorkSpace/WorkSpace","meta":{"titleNView":false,"bounce":"none","softinputMode":"adjustResize","navigationBar":{"titleText":"工作区文件管理","style":"custom","type":"default"},"isNVue":false}},{"path":"pages/UserProfileModal/UserProfileModal","meta":{"titleNView":false,"bounce":"none","softinputMode":"adjustResize","navigationBar":{"titleText":"","style":"custom","type":"default"},"isNVue":false}},{"path":"pages/ContactPages/ContactPages","meta":{"titleNView":false,"bounce":"none","softinputMode":"adjustResize","navigationBar":{"titleText":"","style":"custom","type":"default"},"isNVue":false}},{"path":"pages/WorkSpace/TemplateSpace/TemplateSpace","meta":{"titleNView":false,"bounce":"none","softinputMode":"adjustResize","navigationBar":{"titleText":"","style":"custom","type":"default"},"isNVue":false}},{"path":"pages/text/text","meta":{"navigationBar":{"titleText":"","type":"default"},"isNVue":false}},{"path":"pages/text/text2","meta":{"navigationBar":{"type":"default"},"isNVue":false}},{"path":"pages/CloudDatabase/CloudDatabase","meta":{"titleNView":false,"bounce":"none","softinputMode":"adjustResize","navigationBar":{"titleText":"云端数据","style":"custom","type":"default"},"isNVue":false}},{"path":"pages/CloudDbDetail/CloudDbDetail","meta":{"titleNView":false,"bounce":"none","softinputMode":"adjustResize","navigationBar":{"titleText":"数据库详情","style":"custom","type":"default"},"isNVue":false}}].map(uniRoute=>(uniRoute.meta.route=uniRoute.path,__uniConfig.pages.push(uniRoute.path),uniRoute.path='/'+uniRoute.path,uniRoute)); + const __uniConfig = {"pages":[],"globalStyle":{"backgroundColor":"#F8F8F8","navigationBar":{"backgroundColor":"#F8F8F8","titleText":"uni-app","type":"default","titleColor":"#000000"},"isNVue":false},"nvue":{"compiler":"uni-app","styleCompiler":"uni-app","flex-direction":"column"},"renderer":"auto","appname":"test1","splashscreen":{"alwaysShowBeforeRender":true,"autoclose":true},"compilerVersion":"5.07","entryPagePath":"pages/text/IntuitiveAipptViewer","entryPageQuery":"","realEntryPagePath":"pages/Login/Login","networkTimeout":{"request":60000,"connectSocket":60000,"uploadFile":60000,"downloadFile":60000},"locales":{},"darkmode":false,"themeConfig":{}}; + const __uniRoutes = [{"path":"pages/Login/Login","meta":{"isQuit":true,"isEntry":true,"titleNView":false,"bounce":"none","softinputMode":"adjustResize","navigationBar":{"titleText":"登录页面","style":"custom","type":"default"},"isNVue":false}},{"path":"pages/Chat/Chat","meta":{"titleNView":false,"bounce":"none","softinputMode":"adjustResize","navigationBar":{"titleText":"聊天页面(主页面)","style":"custom","type":"default"},"isNVue":false}},{"path":"pages/WorkSpace/WorkSpace","meta":{"titleNView":false,"bounce":"none","softinputMode":"adjustResize","navigationBar":{"titleText":"工作区文件管理","style":"custom","type":"default"},"isNVue":false}},{"path":"pages/UserProfileModal/UserProfileModal","meta":{"titleNView":false,"bounce":"none","softinputMode":"adjustResize","navigationBar":{"titleText":"","style":"custom","type":"default"},"isNVue":false}},{"path":"pages/ContactPages/ContactPages","meta":{"titleNView":false,"bounce":"none","softinputMode":"adjustResize","navigationBar":{"titleText":"","style":"custom","type":"default"},"isNVue":false}},{"path":"pages/WorkSpace/TemplateSpace/TemplateSpace","meta":{"titleNView":false,"bounce":"none","softinputMode":"adjustResize","navigationBar":{"titleText":"","style":"custom","type":"default"},"isNVue":false}},{"path":"pages/CloudDatabase/CloudDatabase","meta":{"titleNView":false,"bounce":"none","softinputMode":"adjustResize","navigationBar":{"titleText":"云端数据","style":"custom","type":"default"},"isNVue":false}},{"path":"pages/CloudDbDetail/CloudDbDetail","meta":{"titleNView":false,"bounce":"none","softinputMode":"adjustResize","navigationBar":{"titleText":"数据库详情","style":"custom","type":"default"},"isNVue":false}},{"path":"pages/text/text","meta":{"navigationBar":{"titleText":"","type":"default"},"isNVue":false}},{"path":"pages/text/IntuitiveAipptViewer","meta":{"navigationBar":{"titleText":"","type":"default"},"isNVue":false}}].map(uniRoute=>(uniRoute.meta.route=uniRoute.path,__uniConfig.pages.push(uniRoute.path),uniRoute.path='/'+uniRoute.path,uniRoute)); __uniConfig.styles=[];//styles __uniConfig.onReady=function(callback){if(__uniConfig.ready){callback()}else{onReadyCallbacks.push(callback)}};Object.defineProperty(__uniConfig,"ready",{get:function(){return isReady},set:function(val){isReady=val;if(!isReady){return}const callbacks=onReadyCallbacks.slice(0);onReadyCallbacks.length=0;callbacks.forEach(function(callback){callback()})}}); __uniConfig.onServiceReady=function(callback){if(__uniConfig.serviceReady){callback()}else{onServiceReadyCallbacks.push(callback)}};Object.defineProperty(__uniConfig,"serviceReady",{get:function(){return isServiceReady},set:function(val){isServiceReady=val;if(!isServiceReady){return}const callbacks=onServiceReadyCallbacks.slice(0);onServiceReadyCallbacks.length=0;callbacks.forEach(function(callback){callback()})}}); diff --git a/unpackage/dist/dev/app-plus/app-service.js b/unpackage/dist/dev/app-plus/app-service.js index b7ba86e..4c470f8 100644 --- a/unpackage/dist/dev/app-plus/app-service.js +++ b/unpackage/dist/dev/app-plus/app-service.js @@ -9766,524 +9766,6 @@ This will fail in production.`); } const PagesWorkSpaceTemplateSpaceTemplateSpace = /* @__PURE__ */ _export_sfc(_sfc_main$5, [["render", _sfc_render$4], ["__scopeId", "data-v-769edd25"], ["__file", "D:/Projects/uniapp/app-test/test1/pages/WorkSpace/TemplateSpace/TemplateSpace.vue"]]); const _sfc_main$4 = { - __name: "text", - setup(__props, { expose: __expose }) { - __expose(); - const formHtml = vue.ref(""); - const sanitizeContent = (str) => { - return str.replace(/<\/?script>/gi, (match) => { - let result = ""; - for (let i = 0; i < match.length; i++) { - result += "\\u" + match.charCodeAt(i).toString(16).padStart(4, "0"); - } - return result; - }); - }; - vue.onMounted(() => { - formHtml.value = ` -明白了!您希望在我的回复中直接嵌入可编辑的表单卡片。让我试试: - ---- - -
-

-✏️ 直接在下方编辑任务 -

- -
-
- - -
- -
- - -
- -
-
- - -
-
- - -
-
- -
- - -
- -
- -
- - - -
-
- -
- -
- - - - -
-
- -
- - -
-
-
- - - ---- - -**✅ 您现在可以直接在上方的卡片中填写任务信息,点击"添加任务"即可!** - -填写完成后告诉我"已填好"或"添加",我会帮您确认是否成功! -`; - }); - const initForm = () => { - setTimeout(() => { - const today = (/* @__PURE__ */ new Date()).toISOString().split("T")[0]; - const startInput = document.getElementById("inlineStart"); - const endInput = document.getElementById("inlineEnd"); - if (startInput) - startInput.value = today; - if (endInput) - endInput.value = today; - const clearBtn = document.getElementById("clearBtn"); - const submitBtn = document.getElementById("submitBtn"); - if (clearBtn) { - clearBtn.onclick = () => { - const titleInput = document.getElementById("inlineTitle"); - const descInput = document.getElementById("inlineDesc"); - if (titleInput) - titleInput.value = ""; - if (descInput) - descInput.value = ""; - }; - } - if (submitBtn) { - submitBtn.onclick = () => { - const titleInput = document.getElementById("inlineTitle"); - const title = titleInput == null ? void 0 : titleInput.value.trim(); - if (!title) { - alert("请输入任务名称"); - return; - } - alert(`任务已添加:${title}`); - if (titleInput) - titleInput.value = ""; - if (document.getElementById("inlineDesc")) - document.getElementById("inlineDesc").value = ""; - }; - } - }, 100); - }; - const __returned__ = { formHtml, sanitizeContent, initForm, ref: vue.ref, onMounted: vue.onMounted }; - Object.defineProperty(__returned__, "__isScriptSetup", { enumerable: false, value: true }); - return __returned__; - } - }; - function _sfc_render$3(_ctx, _cache, $props, $setup, $data, $options) { - return vue.openBlock(), vue.createElementBlock("view", null, [ - vue.createElementVNode("view", { - innerHTML: $setup.sanitizeContent($setup.formHtml) - }, null, 8, ["innerHTML"]) - ]); - } - const PagesTextText = /* @__PURE__ */ _export_sfc(_sfc_main$4, [["render", _sfc_render$3], ["__file", "D:/Projects/uniapp/app-test/test1/pages/text/text.vue"]]); - const _sfc_main$3 = { - __name: "text2", - setup(__props, { expose: __expose }) { - __expose(); - const socketStore = useSocketStore(); - const config = vue.ref({ - token: getToken(), - conversationId: getCurrentSessionId() - }); - const sendMessage = vue.ref(""); - const quickMessages = vue.ref([ - { - label: "Ping", - data: { - ws_event: "ping" - } - }, - { - label: "认证", - data: { - ws_event: "auth", - data: { - token: "", - conversation_id: "" - } - } - }, - { - label: "测试消息", - data: { - ws_event: "message", - data: { - task_call_id: getTaskCallId(), - token: getToken(), - conversation_id: getCurrentSessionId() - } - } - } - ]); - const connectionStatusClass = vue.computed(() => { - const status = socketStore.connectionStatus; - return { - "status-connected": status === "connected", - "status-connecting": status === "connecting", - "status-error": status === "error", - "status-disconnected": status === "disconnected" - }; - }); - const handleConnect = () => { - if (!config.value.token) { - uni.showToast({ - title: "请输入Token", - icon: "none" - }); - return; - } - socketStore.connect({ - token: config.value.token, - conversationId: getCurrentSessionId() - }); - }; - const handleDisconnect = () => { - socketStore.disconnect(); - }; - const handleSendPing = async () => { - try { - await socketStore.sendPing(); - } catch (e2) { - uni.showToast({ - title: "发送失败", - icon: "none" - }); - } - }; - const handleSend = async () => { - if (!sendMessage.value.trim()) - return; - try { - const messageData = { - ws_event: "message", - data: { - task_call_id: getTaskCallId(), - result: { - tools: [] - } - } - }; - await socketStore.send(messageData); - sendMessage.value = ""; - } catch (e2) { - uni.showToast({ - title: "发送失败", - icon: "none" - }); - } - }; - const sendQuickMessage = async (template) => { - const messageData = { - ws_event: "message", - data: { - task_call_id: getTaskCallId(), - token: getToken(), - conversation_id: getCurrentSessionId() - } - }; - try { - await socketStore.send(messageData); - } catch (e2) { - uni.showToast({ - title: "发送失败", - icon: "none" - }); - } - }; - const formatJson = () => { - if (!sendMessage.value.trim()) - return; - try { - const obj = JSON.parse(sendMessage.value); - sendMessage.value = JSON.stringify(obj, null, 2); - } catch { - uni.showToast({ - title: "不是有效的JSON", - icon: "none" - }); - } - }; - const loadMoreLogs = () => { - }; - vue.watch(() => socketStore.isDisconnected, (newVal) => { - if (newVal && socketStore.messageString) { - formatAppLog("log", "at pages/text/text2.vue:253", socketStore.messageString); - } - }); - vue.onMounted(() => { - socketStore.addLog("info", "=== Socket测试页面 ==="); - socketStore.addLog("info", "页面已加载"); - if (socketStore.isConnected) { - socketStore.addLog("info", "当前已处于连接状态"); - } - }); - vue.onUnmounted(() => { - socketStore.addLog("info", "页面卸载"); - }); - const __returned__ = { socketStore, config, sendMessage, quickMessages, connectionStatusClass, handleConnect, handleDisconnect, handleSendPing, handleSend, sendQuickMessage, formatJson, loadMoreLogs, ref: vue.ref, computed: vue.computed, onMounted: vue.onMounted, onUnmounted: vue.onUnmounted, watch: vue.watch, get useSocketStore() { - return useSocketStore; - }, get getToken() { - return getToken; - }, get getTaskCallId() { - return getTaskCallId; - }, get getCurrentSessionId() { - return getCurrentSessionId; - } }; - Object.defineProperty(__returned__, "__isScriptSetup", { enumerable: false, value: true }); - return __returned__; - } - }; - function _sfc_render$2(_ctx, _cache, $props, $setup, $data, $options) { - return vue.openBlock(), vue.createElementBlock("view", { class: "container" }, [ - vue.createElementVNode("view", { class: "status-card" }, [ - vue.createElementVNode("text", { class: "status-label" }, "连接状态"), - vue.createElementVNode( - "view", - { - class: vue.normalizeClass(["status-value", $setup.connectionStatusClass]) - }, - [ - vue.createElementVNode( - "text", - null, - vue.toDisplayString($setup.socketStore.getStatusText()), - 1 - /* TEXT */ - ) - ], - 2 - /* CLASS */ - ), - $setup.socketStore.reconnectAttempts > 0 ? (vue.openBlock(), vue.createElementBlock("view", { - key: 0, - class: "status-info" - }, [ - vue.createElementVNode( - "text", - { class: "reconnect-info" }, - "重连次数: " + vue.toDisplayString($setup.socketStore.reconnectAttempts), - 1 - /* TEXT */ - ) - ])) : vue.createCommentVNode("v-if", true) - ]), - vue.createElementVNode("view", { class: "config-card" }, [ - vue.createElementVNode("text", { class: "section-title" }, "连接配置"), - vue.createElementVNode("view", { class: "input-group" }, [ - vue.createElementVNode("text", { class: "input-label" }, "Token"), - vue.withDirectives(vue.createElementVNode( - "input", - { - class: "input-field", - "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => $setup.config.token = $event), - placeholder: "请输入Token" - }, - null, - 512 - /* NEED_PATCH */ - ), [ - [vue.vModelText, $setup.config.token] - ]) - ]), - vue.createElementVNode("view", { class: "input-group" }, [ - vue.createElementVNode("text", { class: "input-label" }, "Conversation ID"), - vue.withDirectives(vue.createElementVNode( - "input", - { - class: "input-field", - "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => $setup.config.conversationId = $event), - placeholder: "请输入会话ID(可选)" - }, - null, - 512 - /* NEED_PATCH */ - ), [ - [vue.vModelText, $setup.config.conversationId] - ]) - ]) - ]), - vue.createElementVNode("view", { class: "control-card" }, [ - vue.createElementVNode("button", { - class: "btn btn-primary", - disabled: $setup.socketStore.isConnected || $setup.socketStore.isConnecting, - onClick: $setup.handleConnect - }, vue.toDisplayString($setup.socketStore.isConnecting ? "连接中..." : "连接"), 9, ["disabled"]), - vue.createElementVNode("button", { - class: "btn btn-danger", - disabled: !$setup.socketStore.isConnected, - onClick: $setup.handleDisconnect - }, " 断开 ", 8, ["disabled"]), - vue.createElementVNode("button", { - class: "btn btn-secondary", - onClick: $setup.handleSendPing, - disabled: !$setup.socketStore.isConnected - }, " Ping ", 8, ["disabled"]) - ]), - vue.createElementVNode("view", { class: "send-card" }, [ - vue.createElementVNode("text", { class: "section-title" }, "发送消息"), - vue.createElementVNode("view", { class: "send-input-wrapper" }, [ - vue.withDirectives(vue.createElementVNode("textarea", { - class: "send-input", - "onUpdate:modelValue": _cache[2] || (_cache[2] = ($event) => $setup.sendMessage = $event), - placeholder: "输入要发送的消息(JSON格式)", - disabled: !$setup.socketStore.isConnected - }, null, 8, ["disabled"]), [ - [vue.vModelText, $setup.sendMessage] - ]), - vue.createElementVNode("view", { class: "send-buttons" }, [ - vue.createElementVNode("button", { - class: "btn btn-primary btn-small", - disabled: !$setup.socketStore.isConnected || !$setup.sendMessage.trim(), - onClick: $setup.handleSend - }, " 发送 ", 8, ["disabled"]), - vue.createElementVNode("button", { - class: "btn btn-outline btn-small", - disabled: !$setup.sendMessage.trim(), - onClick: $setup.formatJson - }, " 格式化 ", 8, ["disabled"]) - ]) - ]), - vue.createElementVNode("view", { class: "quick-messages" }, [ - vue.createElementVNode("text", { class: "quick-label" }, "快捷消息:"), - vue.createElementVNode("view", { class: "quick-btns" }, [ - (vue.openBlock(true), vue.createElementBlock( - vue.Fragment, - null, - vue.renderList($setup.quickMessages, (item) => { - return vue.openBlock(), vue.createElementBlock("button", { - key: item.label, - class: "quick-btn", - disabled: !$setup.socketStore.isConnected, - onClick: ($event) => $setup.sendQuickMessage(item.data) - }, vue.toDisplayString(item.label), 9, ["disabled", "onClick"]); - }), - 128 - /* KEYED_FRAGMENT */ - )) - ]) - ]) - ]), - vue.createElementVNode("view", { class: "log-card" }, [ - vue.createElementVNode("view", { class: "log-header" }, [ - vue.createElementVNode("view", { class: "log-title-wrapper" }, [ - vue.createElementVNode("text", { class: "section-title" }, "日志"), - vue.createElementVNode( - "text", - { class: "log-count" }, - "(" + vue.toDisplayString($setup.socketStore.logs.length) + ")", - 1 - /* TEXT */ - ) - ]), - vue.createElementVNode("view", { class: "log-actions" }, [ - vue.createElementVNode("button", { - class: "btn btn-small btn-secondary", - onClick: _cache[3] || (_cache[3] = (...args) => $setup.socketStore.clearLogs && $setup.socketStore.clearLogs(...args)) - }, "清空") - ]) - ]), - vue.createElementVNode( - "scroll-view", - { - class: "log-list", - "scroll-y": "", - onScrolltoupper: $setup.loadMoreLogs - }, - [ - (vue.openBlock(true), vue.createElementBlock( - vue.Fragment, - null, - vue.renderList($setup.socketStore.logs, (log, index) => { - return vue.openBlock(), vue.createElementBlock( - "view", - { - key: index, - class: vue.normalizeClass(["log-item", "log-" + log.type]) - }, - [ - vue.createElementVNode( - "text", - { class: "log-time" }, - vue.toDisplayString(log.time), - 1 - /* TEXT */ - ), - vue.createElementVNode( - "text", - { class: "log-content" }, - vue.toDisplayString(log.content), - 1 - /* TEXT */ - ) - ], - 2 - /* CLASS */ - ); - }), - 128 - /* KEYED_FRAGMENT */ - )) - ], - 32 - /* NEED_HYDRATION */ - ) - ]) - ]); - } - const PagesTextText2 = /* @__PURE__ */ _export_sfc(_sfc_main$3, [["render", _sfc_render$2], ["__scopeId", "data-v-cd5869b7"], ["__file", "D:/Projects/uniapp/app-test/test1/pages/text/text2.vue"]]); - const _sfc_main$2 = { __name: "CloudDatabase", setup(__props, { expose: __expose }) { __expose(); @@ -11276,7 +10758,7 @@ This will fail in production.`); return __returned__; } }; - function _sfc_render$1(_ctx, _cache, $props, $setup, $data, $options) { + function _sfc_render$3(_ctx, _cache, $props, $setup, $data, $options) { const _component_uni_icons = resolveEasycom(vue.resolveDynamicComponent("uni-icons"), __easycom_0); return vue.openBlock(), vue.createElementBlock( vue.Fragment, @@ -12327,8 +11809,8 @@ This will fail in production.`); /* STABLE_FRAGMENT */ ); } - const PagesCloudDatabaseCloudDatabase = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["render", _sfc_render$1], ["__scopeId", "data-v-51006ffb"], ["__file", "D:/Projects/uniapp/app-test/test1/pages/CloudDatabase/CloudDatabase.vue"]]); - const _sfc_main$1 = { + const PagesCloudDatabaseCloudDatabase = /* @__PURE__ */ _export_sfc(_sfc_main$4, [["render", _sfc_render$3], ["__scopeId", "data-v-51006ffb"], ["__file", "D:/Projects/uniapp/app-test/test1/pages/CloudDatabase/CloudDatabase.vue"]]); + const _sfc_main$3 = { __name: "CloudDbDetail", props: { databaseId: { @@ -12817,7 +12299,7 @@ This will fail in production.`); return __returned__; } }; - function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) { + function _sfc_render$2(_ctx, _cache, $props, $setup, $data, $options) { const _component_uni_icons = resolveEasycom(vue.resolveDynamicComponent("uni-icons"), __easycom_0); return vue.openBlock(), vue.createElementBlock("view", { class: "cloud-db-detail-wrapper" }, [ vue.createElementVNode("view", { class: "status-bar" }), @@ -13287,17 +12769,2232 @@ This will fail in production.`); ]) ]); } - const PagesCloudDbDetailCloudDbDetail = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["render", _sfc_render], ["__scopeId", "data-v-96fa4671"], ["__file", "D:/Projects/uniapp/app-test/test1/pages/CloudDbDetail/CloudDbDetail.vue"]]); + const PagesCloudDbDetailCloudDbDetail = /* @__PURE__ */ _export_sfc(_sfc_main$3, [["render", _sfc_render$2], ["__scopeId", "data-v-96fa4671"], ["__file", "D:/Projects/uniapp/app-test/test1/pages/CloudDbDetail/CloudDbDetail.vue"]]); + const _sfc_main$2 = { + __name: "text", + setup(__props, { expose: __expose }) { + __expose(); + const friendSocketStore = useFriendSocketStore(); + const handleFriendConnect = () => { + if (friendSocketStore.isConnected) + return; + if (!userToken.value || !UserId.value) { + formatAppLog("warn", "at pages/text/text.vue:334", "Token或UserId未准备好"); + return; + } + friendSocketStore.connect({ + token: userToken.value, + UserId: UserId.value + }); + }; + const socketStore = useSocketStore(); + const ChatType = vue.ref(0); + const convertMarkdownTable = (text) => { + const lines = text.split("\n"); + let result = []; + let inTable = false; + let tableRows = []; + let alignments = []; + for (let i = 0; i < lines.length; i++) { + const line = lines[i].trim(); + const isTableLine = /^\|.+\|$/.test(line); + if (isTableLine) { + if (/^\|[\s\-:]+\|[\s\-:]+\|$/.test(line)) { + alignments = line.split("|").filter((c) => c.trim()).map((c) => { + if (c.trim().startsWith(":") && c.trim().endsWith(":")) + return "center"; + if (c.trim().endsWith(":")) + return "right"; + return "left"; + }); + continue; + } + tableRows.push(line); + inTable = true; + } else { + if (inTable && tableRows.length > 0) { + result.push(renderTable(tableRows, alignments)); + tableRows = []; + alignments = []; + inTable = false; + } + result.push(line); + } + } + if (inTable && tableRows.length > 0) { + result.push(renderTable(tableRows, alignments)); + } + return result.join("\n"); + }; + const renderTable = (rows, alignments) => { + let html = ''; + rows.forEach((row, index) => { + const tag = index === 0 ? "th" : "td"; + const cells = row.split("|").filter((c) => c.trim() !== ""); + html += ""; + cells.forEach((cell, ci) => { + const align = alignments[ci] ? ` style="text-align:${alignments[ci]}"` : ""; + let cellContent = cell.trim().replace(/_/g, "_").replace(/\*/g, "*"); + html += `<${tag}${align} style="padding:6px 10px;border:1px solid #ddd">${cellContent}`; + }); + html += ""; + }); + html += "
"; + return html; + }; + const escapeLoneUnderscores = (text) => { + text = text.replace(/__/g, "\0\0"); + text = text.replace(/_/g, "_"); + text = text.replace(/\x00\x00/g, "__"); + return text; + }; + const pareseMarkdown = (content) => { + if (!content) + return ""; + content = sanitizeContent(content); + try { + content = escapeLoneUnderscores(content); + content = convertMarkdownTable(content); + let html = t(content); + html = html.replace( + /]*>(.*?)<\/a>/gi, + '$2' + ); + return html; + } catch (e2) { + formatAppLog("error", "at pages/text/text.vue:455", "解析失败", e2); + return content; + } + }; + const sanitizeContent = (str) => { + const hasScript = /]*>([\s\S]*?)<\/script>/i.test(str); + const hasFormTags = /<(form|input|select|textarea|button)\b[^>]*>/i.test(str); + let cleanedStr = str.replace(/]*>([\s\S]*?)<\/script>/gi, ""); + cleanedStr = cleanedStr.replace(/<\/?minimax:tool_call>/g, (m) => m.replace(//g, ">")).replace(/<\|[\w]+?\|>/g, (m) => m.replace(//g, ">")); + if (hasScript || hasFormTags) { + const warningHtml = '

表单仅预览,不可操作!!!

'; + return warningHtml + cleanedStr; + } + return cleanedStr; + }; + const previewImage = (url) => { + uni.previewImage({ + urls: [url] + }); + }; + const openFile = (url) => { + uni.downloadFile({ + url, + success: (res) => { + uni.openDocument({ + filePath: res.tempFilePath, + showMenu: true + }); + } + }); + }; + const formatFileSize = (size) => { + if (!size) + return "0KB"; + if (size < 1) { + return (size * 1024).toFixed(0) + "KB"; + } + return size.toFixed(2) + "MB"; + }; + const parseFileInfo = (content) => { + if (!content || typeof content !== "string") + return { + textContent: content || "", + files: [] + }; + const match = content.match(/\[文件信息:(\[.*?\])\]/); + if (!match) + return { + textContent: content, + files: [] + }; + try { + const files = JSON.parse(match[1]); + const textContent = content.replace(match[0], "").trim(); + return { + textContent, + files + }; + } catch (e2) { + formatAppLog("error", "at pages/text/text.vue:532", "解析文件信息失败:", e2); + return { + textContent: content, + files: [] + }; + } + }; + const selectNormalChat = async () => { + try { + const chars = "0123456789abcdef"; + let name2 = ""; + for (let i = 0; i < 24; i++) { + name2 += chars[Math.floor(Math.random() * chars.length)]; + } + const workspaceId = await createWorkspace(userToken.value, name2); + if (!workspaceId) { + uni.showToast({ + title: "工作区创建失败", + icon: "none" + }); + return; + } + const loginInfo = uni.getStorageSync("yxd_login_info"); + let userName = ""; + if (loginInfo) { + userName = loginInfo.username || ""; + } + const conversationId = await createConversation(userToken.value, workspaceId, "新会话", userName); + if (!conversationId) { + uni.showToast({ + title: "新会话创建失败", + icon: "none" + }); + return; + } + takeUserConversations(); + } catch (error) { + formatAppLog("error", "at pages/text/text.vue:577", "新建普通会话失败:", error); + uni.showToast({ + title: "创建会话失败,请重试", + icon: "none" + }); + } + }; + const showNewChatModal = vue.ref(false); + const closeNewChatModal = () => { + showNewChatModal.value = false; + }; + const isChatSidebar = vue.ref(false); + const handleChatSidebar = () => { + isChatSidebar.value = !isChatSidebar.value; + }; + const goWorkSpace = () => { + if (currentSessionId.value) { + uni.setStorageSync("currentSessionId", currentSessionId.value); + } + uni.navigateTo({ + url: "/pages/WorkSpace/WorkSpace" + }); + }; + const goCloudDatabase = () => { + if (currentSessionId.value) { + uni.setStorageSync("currentSessionId", currentSessionId.value); + } + uni.navigateTo({ + url: "/pages/CloudDatabase/CloudDatabase" + }); + }; + const logOut = () => { + uni.reLaunch({ + url: "/pages/Login/Login" + }); + }; + const previewFileArray = vue.ref([]); + const uploadPhoto = () => { + formatAppLog("log", "at pages/text/text.vue:626", "点击了拍照上传"); + uni.chooseImage({ + count: 1, + sourceType: ["camera", "album"], + success: (res) => { + const tempFiles = res.tempFiles; + const tempFilePaths = res.tempFilePaths; + tempFiles.forEach((file, index) => { + previewFileArray.value.push({ + path: tempFilePaths[index], + // 图片路径(用于显示) + name: file.name || `photo_${Date.now()}_${index}.jpg`, + // 文件名 + size: file.size, + // 文件大小 + type: "image", + // 文件类型标识 + tempFile: file + // 原始文件对象 + }); + }); + }, + fail: (err) => { + formatAppLog("error", "at pages/text/text.vue:644", "选择图片失败", err); + } + }); + }; + const deleteImage = (index) => { + previewFileArray.value.splice(index, 1); + }; + const fileList = vue.ref([]); + const uploadFile = () => { + formatAppLog("log", "at pages/text/text.vue:655", "点击了上传文件"); + chooseFile({ + count: 5, + type: "all", + success: (res) => { + formatAppLog("log", "at pages/text/text.vue:660", "成功了"); + fileList.value = res.tempFiles; + previewFileArray.value.push(...res.tempFiles); + uni.showToast({ + title: `已选择 ${res.tempFiles.length} 个文件`, + icon: "success" + }); + }, + fail: (err) => { + formatAppLog("error", "at pages/text/text.vue:671", "选择失败:", err); + uni.showToast({ + title: "选择失败", + icon: "error" + }); + } + }); + }; + const handleConnect = () => { + return new Promise((resolve, reject) => { + if (socketStore.isConnected) + return resolve(); + const unwatch = vue.watch(() => socketStore.isConnected, (connected) => { + if (connected) { + unwatch(); + resolve(); + } + }); + const unwatchError = vue.watch(() => socketStore.connectionStatus, (status) => { + if (status === "error") { + unwatch(); + unwatchError(); + reject(new Error("连接失败")); + } + }); + socketStore.connect({ + token: userToken.value, + conversationId: currentSessionId.value + }); + }); + }; + const isThinking = vue.ref(false); + const isSelfSent = vue.ref(false); + const isUploading = vue.ref(false); + const uploadedFiles = vue.ref([]); + const streamingMessageId = vue.ref(null); + const showMessageModal = vue.ref(false); + const detailLinks = vue.ref([]); + const handleChatContentClick = (e2, message) => { + let el = e2.target; + while (el && el.classList) { + if (el.classList.contains("chat-inline-link")) { + const url = el.getAttribute("data-url") || el.dataset && el.dataset.url; + if (url) { + openLink(url); + } + return; + } + el = el.parentElement; + } + if (message.role) { + showMessageDetail(message); + } + }; + const extractLinks = (text) => { + const seen = /* @__PURE__ */ new Set(); + const result = []; + const mdLinkRegex = /\[([^\]]*)\]\((https?:\/\/[^\s<>"{}|\\^`\[\]()]+)\)/gi; + const mdLinks = [...text.matchAll(mdLinkRegex)]; + mdLinks.forEach((m) => { + const name2 = m[1].trim(); + const url = m[2]; + if (!seen.has(url)) { + seen.add(url); + result.push({ + url, + name: name2 || url + }); + } + }); + let cleaned = text.replace(mdLinkRegex, ""); + const bareRegex = /https?:\/\/[^\s<>"{}|\\^`\[\]()]+/gi; + const bareMatches = cleaned.match(bareRegex); + if (bareMatches) { + bareMatches.forEach((url) => { + if (!seen.has(url)) { + seen.add(url); + result.push({ + url, + name: extractFileNameFromUrl(url) + }); + } + }); + } + return result; + }; + const extractFileNameFromUrl = (urlStr) => { + try { + const segments = new URL(urlStr).pathname.split("/").filter((s) => s); + if (segments.length > 0) { + return decodeURIComponent(segments[segments.length - 1]); + } + } catch (e2) { + } + const parts = urlStr.split("/").filter((s) => s); + if (parts.length > 0) { + const last = parts[parts.length - 1]; + const qIdx = last.indexOf("?"); + return qIdx > -1 ? last.substring(0, qIdx) : last; + } + return urlStr; + }; + const showMessageDetail = (message) => { + detailLinks.value = extractLinks(message.content || ""); + showMessageModal.value = true; + }; + const closeMessageDetail = () => { + showMessageModal.value = false; + detailLinks.value = []; + }; + const downloadToast = vue.ref({ + show: false, + type: "loading", + message: "" + }); + let downloadToastTimer = null; + const showDownloadToast = (type, message, duration) => { + if (downloadToastTimer) + clearTimeout(downloadToastTimer); + downloadToast.value = { + show: true, + type, + message + }; + if (duration > 0) { + downloadToastTimer = setTimeout(() => { + downloadToast.value = { + show: false, + type: "loading", + message: "" + }; + }, duration); + } + }; + const hideDownloadToast = () => { + if (downloadToastTimer) + clearTimeout(downloadToastTimer); + downloadToast.value = { + show: false, + type: "loading", + message: "" + }; + }; + const openLink = (url) => { + downloadAndHandle(url); + }; + const downloadAndHandle = async (url) => { + try { + showDownloadToast("loading", "下载中...", 0); + const workspaceId = uni.getStorageSync("workspace_id") || ""; + const result = await conversationMessageDownload(userToken.value, workspaceId, url); + const downloadUrl = typeof result === "string" ? result : (result == null ? void 0 : result.url) || (result == null ? void 0 : result.download_url) || ""; + if (!downloadUrl) { + showDownloadToast("error", "获取下载地址失败", 2500); + return; + } + uni.downloadFile({ + url: downloadUrl, + success: (downloadRes) => { + if (downloadRes.statusCode === 200) { + hideDownloadToast(); + uni.openDocument({ + filePath: downloadRes.tempFilePath, + showMenu: true, + fail: () => { + showDownloadToast("error", "无法打开此文件", 2500); + } + }); + } else { + showDownloadToast("error", "下载失败", 2500); + } + }, + fail: () => { + showDownloadToast("error", "下载失败", 2500); + } + }); + } catch (err) { + showDownloadToast("error", "下载失败: " + (err.message || err), 2500); + } + }; + const sendMessage = async () => { + const message = textMessage.value.trim(); + if (!message && previewFileArray.value.length === 0) + return; + if (previewFileArray.value.length > 0) { + isUploading.value = true; + uni.showLoading({ + title: "上传文件中...", + mask: true + }); + try { + const results = []; + for (const item of previewFileArray.value) { + const result = await uploadFileToServer(item.path); + results.push(result); + } + uploadedFiles.value = results; + previewFileArray.value = []; + } catch (err) { + uni.hideLoading(); + formatAppLog("error", "at pages/text/text.vue:895", "文件上传失败:", err); + uni.showToast({ + title: "文件上传失败,请重试", + icon: "error" + }); + isUploading.value = false; + return; + } + uni.hideLoading(); + isUploading.value = false; + } + if (ChatType.value === 0) + sendAIMessage(); + if (ChatType.value === 1) + sendFriendMessage(); + if (ChatType.value === 2) + sendGroupMessage(); + }; + const sendFriendMessage = async () => { + if (!friendSocketStore.isConnecting) + await handleFriendConnect(); + const receiverId = getReceiverId(); + const hasFiles = uploadedFiles.value.length > 0; + const body = { + "command": 1, + "sender": UserId.value, + "receiver": receiverId, + "avatar": "", + "sessionId": currentSessionId.value, + "message": textMessage.value, + "callBackMessage": false + }; + if (hasFiles) { + body.contentType = 1; + body.uploadVos = uploadedFiles.value; + } + const data = { + "version": "1.1", + "body": body + }; + friendSocketStore.send(data); + const newMessageData = { + "sender": UserId.value, + "receiver": receiverId, + "content": textMessage.value, + "taskId": null, + "avatar": null, + "createTime": "", + "messageType": "null", + "contentJson": hasFiles ? JSON.stringify(uploadedFiles.value) : "null" + }; + allmessages.value = [...allmessages.value, newMessageData]; + textMessage.value = ""; + uploadedFiles.value = []; + setTimeout(() => { + takeFriendMessages(); + }, 1e3); + }; + const sendGroupMessage = async () => { + if (!friendSocketStore.isConnecting) + await handleFriendConnect(); + const hasFiles = uploadedFiles.value.length > 0; + const body = { + "command": 9, + "groupId": currentSessionId.value, + "message": textMessage.value || "", + "callBackMessage": hasFiles ? false : true, + "messageType": hasFiles ? 1 : 0, + "sender": UserId.value + }; + if (hasFiles) { + body.uploadVos = uploadedFiles.value; + } + const data = { + "version": "1.1", + "body": body + }; + friendSocketStore.send(data); + const newMessageData = { + "id": allmessages.value.length + 1, + "message": textMessage.value, + "messageType": hasFiles ? 1 : 0, + "groupId": null, + "createTime": "", + "contentJson": hasFiles ? JSON.stringify(uploadedFiles.value) : "", + "sender": UserId.value + }; + allmessages.value = [...allmessages.value, newMessageData]; + textMessage.value = ""; + uploadedFiles.value = []; + setTimeout(() => { + takeGroupMessages(); + }, 1e3); + }; + const sendAIMessage = async () => { + try { + await handleConnect(); + let messageContent = textMessage.value; + if (uploadedFiles.value.length) { + const fileJson = JSON.stringify(uploadedFiles.value); + messageContent = messageContent ? `${messageContent} +[文件信息:${fileJson}]` : `[文件信息:${fileJson}]`; + } + const data = { + "type": "chat", + "conversation_id": currentSessionId.value, + "content": messageContent + }; + socketStore.send(data); + isThinking.value = true; + isSelfSent.value = true; + allmessages.value = [...allmessages.value, { + role: "user", + content: messageContent + }]; + streamingMessageId.value = "streaming-" + Date.now(); + allmessages.value = [...allmessages.value, { + role: "assistant", + content: "", + _streaming: true, + _id: streamingMessageId.value + }]; + textMessage.value = ""; + uploadedFiles.value = []; + scrollToBottom(); + } catch (error) { + isThinking.value = false; + uni.showToast({ + title: `用户发送消息失败${error}`, + icon: "error" + }); + } + }; + const stopConversation = async () => { + formatAppLog("log", "at pages/text/text.vue:1064", "中断对话 - 当前会话ID:", currentSessionId.value); + try { + await socketStore.send({ + type: "stop", + conversation_id: currentSessionId.value + }); + formatAppLog("log", "at pages/text/text.vue:1070", "中断指令已发送成功"); + } catch (err) { + formatAppLog("error", "at pages/text/text.vue:1072", "中断指令发送失败:", err); + } + if (streamingMessageId.value) { + allmessages.value = allmessages.value.filter((m) => m._id !== streamingMessageId.value); + streamingMessageId.value = null; + } + isThinking.value = false; + socketStore.isThinking = false; + isSelfSent.value = false; + textMessage.value = ""; + uploadedFiles.value = []; + uni.showToast({ + title: "已中断", + icon: "none", + duration: 1500 + }); + }; + vue.watch(() => socketStore.isThinking, (newVal, oldVal) => { + formatAppLog("log", "at pages/text/text.vue:1096", "isThinking 变化:", oldVal, "→", newVal, "messageString:", socketStore.messageString); + if (!oldVal && newVal) { + formatAppLog("log", "at pages/text/text.vue:1099", "AI开始回复(跨设备同步),锁定输入框"); + isThinking.value = true; + if (!streamingMessageId.value) { + streamingMessageId.value = "streaming-" + Date.now(); + allmessages.value = [...allmessages.value, { + role: "assistant", + content: "", + _streaming: true, + _id: streamingMessageId.value + }]; + } + return; + } + if (oldVal && !newVal) { + formatAppLog("log", "at pages/text/text.vue:1115", "AI回复结束(正常/中断),解锁并刷新消息列表"); + if (streamingMessageId.value) { + allmessages.value = allmessages.value.filter((m) => m._id !== streamingMessageId.value); + streamingMessageId.value = null; + } + takeConversationMessages(); + isThinking.value = false; + isSelfSent.value = false; + textMessage.value = ""; + uploadedFiles.value = []; + } + }); + vue.watch(() => socketStore.authFailReason, (reason) => { + if (reason === "pc offline") { + isThinking.value = false; + socketStore.authFailReason = ""; + vue.nextTick(() => { + uni.showModal({ + title: "提示", + content: "PC端不在线,不可聊天", + showCancel: false, + confirmText: "知道了" + }); + }); + } + }); + vue.watch(() => socketStore.messageString, (newContent) => { + if (!streamingMessageId.value || !socketStore.isThinking) + return; + const idx = allmessages.value.findIndex((m) => m._id === streamingMessageId.value); + if (idx >= 0) { + const updated = { + ...allmessages.value[idx], + content: newContent + }; + allmessages.value.splice(idx, 1, updated); + scrollToBottom(); + } + }); + const textMessage = vue.ref(""); + const UserConversations = vue.ref([]); + const currentSessionId = vue.ref(""); + const userToken = vue.ref(""); + const UserId = vue.ref(""); + const UserAvatar = vue.ref(""); + const UserData = vue.ref(null); + const takeUserInfo = async () => { + try { + userToken.value = getToken(); + UserData.value = await getUserInfo(userToken.value); + UserId.value = UserData.value._id; + UserAvatar.value = UserData.value.avatar || ""; + formatAppLog("log", "at pages/text/text.vue:1179", "用户信息已加载:", UserId.value, UserAvatar.value); + } catch (error) { + formatAppLog("error", "at pages/text/text.vue:1181", "获取用户信息失败:", error); + } + }; + const takeUserConversations = async () => { + try { + userToken.value = getToken(); + formatAppLog("log", "at pages/text/text.vue:1189", "token:", userToken.value); + UserConversations.value = await getUserConversations(userToken.value) || []; + const savedSessionId = getCurrentSessionId(); + if (savedSessionId && UserConversations.value.some((c) => c._id === savedSessionId)) { + currentSessionId.value = savedSessionId; + } else if (UserConversations.value.length > 0) { + currentSessionId.value = UserConversations.value[0]._id; + } else { + currentSessionId.value = ""; + } + formatAppLog("log", "at pages/text/text.vue:1201", "保存会话id:", currentSessionId.value); + uni.setStorageSync("currentSessionId", currentSessionId.value); + } catch (error) { + uni.showToast({ + title: `获取会话列表失败${error}`, + icon: "error" + }); + } + }; + const FriendInfoList = vue.ref([]); + const takeFriendList = async () => { + try { + formatAppLog("log", "at pages/text/text.vue:1216", "开始获取好友列表"); + const friendList = await getChatFriend(UserId.value); + formatAppLog("log", "at pages/text/text.vue:1219", "friendList:", friendList); + if (friendList && friendList.length) { + FriendInfoList.value = await takeUserAvatar(friendList); + } else { + FriendInfoList.value = []; + formatAppLog("log", "at pages/text/text.vue:1224", "好友列表为空"); + } + } catch (error) { + formatAppLog("error", "at pages/text/text.vue:1227", "获取好友列表失败:", error); + FriendInfoList.value = []; + } finally { + UserConversations.value = FriendInfoList.value; + } + }; + const takeTalkUserAvatar = (userId) => { + if (!userId) + return null; + const user = FriendInfoList.value.find((item) => item.receiver === userId); + return (user == null ? void 0 : user.avatar) || null; + }; + const takeUserAvatar = async (friendList) => { + if (!(friendList == null ? void 0 : friendList.length)) + return []; + try { + const friendIds = friendList.map((item) => item.receiver); + const friendAvatarList = await getUserAvatar(userToken.value, friendIds); + const userMap = new Map(friendAvatarList.map((user) => [user.user_id, user]) || []); + return friendList.map((friend) => { + const userInfo = userMap.get(friend.receiver); + return { + ...friend, + avatar: (userInfo == null ? void 0 : userInfo.avatar) || null + }; + }); + } catch (err) { + formatAppLog("error", "at pages/text/text.vue:1257", "获取好友头像失败", err); + return friendList; + } + }; + const GroupList = vue.ref([]); + const takeGroupList = async () => { + try { + formatAppLog("log", "at pages/text/text.vue:1267", "开始获取群聊列表"); + GroupList.value = await getGroup(UserId.value); + } catch (error) { + formatAppLog("error", "at pages/text/text.vue:1272", "获取群聊列表失败:", error); + GroupList.value = []; + } finally { + UserConversations.value = GroupList.value; + } + }; + const groupMemberList = vue.ref([]); + const takeGroupMemberAvatar = async (memberList) => { + if (!(memberList == null ? void 0 : memberList.length)) + return []; + try { + const memberIds = memberList.map((item) => item.groupContactId); + formatAppLog("log", "at pages/text/text.vue:1286", "请求头像的ID列表:", memberIds); + const memberAvatarList = await getUserAvatar(userToken.value, memberIds); + formatAppLog("log", "at pages/text/text.vue:1288", "头像接口返回数据:", memberAvatarList); + const userMap = new Map(memberAvatarList.map((user) => [user.user_id, user]) || []); + formatAppLog("log", "at pages/text/text.vue:1291", "userMap的keys:", Array.from(userMap.keys())); + return memberList.map((member) => { + const memberId = member.groupContactId; + const userInfo = userMap.get(memberId); + formatAppLog("log", "at pages/text/text.vue:1296", `查找 ${memberId} 的头像:`, userInfo); + return { + ...member, + avatar: (userInfo == null ? void 0 : userInfo.avatar) || null + }; + }); + } catch (err) { + formatAppLog("error", "at pages/text/text.vue:1303", "获取群成员头像失败", err); + return memberList; + } + }; + const getGroupMemberAvatarById = (userId) => { + if (!userId) + return null; + const member = groupMemberList.value.find((item) => item.groupContactId === userId); + return (member == null ? void 0 : member.avatar) || null; + }; + const allmessages = vue.ref([]); + const scrollToView = vue.ref(""); + const bottomToggle = vue.ref(false); + const isLoadingMore = vue.ref(false); + const currentPage = vue.ref(1); + const currentMessages = vue.computed(() => { + return allmessages.value; + }); + const takeConversationMessages = async () => { + try { + allmessages.value = await getConversationMessages(userToken.value, currentSessionId.value) || []; + scrollToBottom(); + } catch (error) { + uni.showToast({ + title: `获取ai会话内容失败${error}`, + icon: "none" + }); + } + }; + const takeFriendMessages = async () => { + try { + allmessages.value = await getFriendMessages(currentSessionId.value) || []; + scrollToBottom(); + } catch (error) { + uni.showToast({ + title: `获取好友会话消息失败${error}`, + icon: "none" + }); + } + }; + const takeGroupMessages = async () => { + try { + allmessages.value = await getGroupMessages(currentSessionId.value) || []; + formatAppLog("log", "at pages/text/text.vue:1363", "群聊消息:", allmessages.value); + const memberList = await getGroupMemberList(currentSessionId.value); + formatAppLog("log", "at pages/text/text.vue:1366", "获取到的群成员列表:", memberList); + if (memberList && memberList.length) { + groupMemberList.value = await takeGroupMemberAvatar(memberList); + formatAppLog("log", "at pages/text/text.vue:1369", "群成员列表(带头像):", groupMemberList.value); + } else { + groupMemberList.value = []; + } + scrollToBottom(); + } catch (error) { + uni.showToast({ + title: `获取群聊会话失败${error}`, + icon: "none" + }); + } + }; + const onScroll = (e2) => { + }; + const scrollToBottom = async () => { + await vue.nextTick(); + if (currentMessages.value.length > 0) { + bottomToggle.value = !bottomToggle.value; + scrollToView.value = bottomToggle.value ? "chat-bottom-a" : "chat-bottom-b"; + } + }; + vue.watch(() => friendSocketStore.MessageReceived, (newId) => { + formatAppLog("log", "at pages/text/text.vue:1414", "收到了好友消息"); + if (newId) { + formatAppLog("log", "at pages/text/text.vue:1416", "ChatType:", ChatType.value); + switch (ChatType.value) { + case 0: + takeConversationMessages(); + friendSocketStore.MessageReceived = false; + break; + case 1: + takeFriendMessages(); + friendSocketStore.MessageReceived = false; + break; + case 2: + takeGroupMessages(); + friendSocketStore.MessageReceived = false; + break; + default: + formatAppLog("log", "at pages/text/text.vue:1432", "default 分支"); + friendSocketStore.MessageReceived = false; + break; + } + } + }, { + immediate: true + }); + vue.watch(currentSessionId, (newId) => { + if (newId) { + uni.setStorageSync("currentSessionId", currentSessionId.value); + switch (ChatType.value) { + case 0: + takeConversationMessages(); + socketStore.disconnect(); + handleConnect(); + break; + case 1: + takeFriendMessages(); + break; + case 2: + takeGroupMessages(); + break; + default: + takeConversationMessages(); + break; + } + } + }, { + immediate: true + }); + vue.onMounted(() => { + }); + onShow(async () => { + await takeUserInfo(); + const chatType = getChatType(); + ChatType.value = chatType; + const sessionId = getCurrentSessionId(); + if (sessionId) { + currentSessionId.value = sessionId; + } + if (chatType === 0) { + await takeUserConversations(); + if (currentSessionId.value) { + takeConversationMessages(); + } + if (userToken.value && currentSessionId.value && !socketStore.isConnected) { + handleConnect(); + } + } else if (chatType === 1) { + await takeFriendList(); + takeFriendMessages(); + handleFriendConnect(); + } else if (chatType === 2) { + await takeGroupList(); + await handleFriendConnect(); + await takeGroupMessages(); + } + }); + onUnload(() => { + socketStore.disconnect(); + }); + const __returned__ = { friendSocketStore, handleFriendConnect, socketStore, ChatType, convertMarkdownTable, renderTable, escapeLoneUnderscores, pareseMarkdown, sanitizeContent, previewImage, openFile, formatFileSize, parseFileInfo, selectNormalChat, showNewChatModal, closeNewChatModal, isChatSidebar, handleChatSidebar, goWorkSpace, goCloudDatabase, logOut, previewFileArray, uploadPhoto, deleteImage, fileList, uploadFile, handleConnect, isThinking, isSelfSent, isUploading, uploadedFiles, streamingMessageId, showMessageModal, detailLinks, handleChatContentClick, extractLinks, extractFileNameFromUrl, showMessageDetail, closeMessageDetail, downloadToast, get downloadToastTimer() { + return downloadToastTimer; + }, set downloadToastTimer(v) { + downloadToastTimer = v; + }, showDownloadToast, hideDownloadToast, openLink, downloadAndHandle, sendMessage, sendFriendMessage, sendGroupMessage, sendAIMessage, stopConversation, textMessage, UserConversations, currentSessionId, userToken, UserId, UserAvatar, UserData, takeUserInfo, takeUserConversations, FriendInfoList, takeFriendList, takeTalkUserAvatar, takeUserAvatar, GroupList, takeGroupList, groupMemberList, takeGroupMemberAvatar, getGroupMemberAvatarById, allmessages, scrollToView, bottomToggle, isLoadingMore, currentPage, currentMessages, takeConversationMessages, takeFriendMessages, takeGroupMessages, onScroll, scrollToBottom, computed: vue.computed, getCurrentInstance: vue.getCurrentInstance, nextTick: vue.nextTick, onMounted: vue.onMounted, ref: vue.ref, watch: vue.watch, get onShow() { + return onShow; + }, get onUnload() { + return onUnload; + }, ChatSidebar, get getUserConversations() { + return getUserConversations; + }, get getConversationMessages() { + return getConversationMessages; + }, get addMessageDict() { + return addMessageDict; + }, get createWorkspace() { + return createWorkspace; + }, get createConversation() { + return createConversation; + }, get getUserInfo() { + return getUserInfo; + }, get getUserAvatar() { + return getUserAvatar; + }, get conversationMessageDownload() { + return conversationMessageDownload; + }, get getToken() { + return getToken; + }, get getCurrentSessionId() { + return getCurrentSessionId; + }, get getTaskCallId() { + return getTaskCallId; + }, get getChatType() { + return getChatType; + }, get getReceiverId() { + return getReceiverId; + }, get snarkdown() { + return t; + }, get useSocketStore() { + return useSocketStore; + }, get getChatFriend() { + return getChatFriend; + }, get getGroup() { + return getGroup; + }, get getFriendMessages() { + return getFriendMessages; + }, get getGroupMessages() { + return getGroupMessages; + }, get getGroupMemberList() { + return getGroupMemberList; + }, get uploadFileToServer() { + return uploadFileToServer; + }, get useFriendSocketStore() { + return useFriendSocketStore; + }, get chooseFile() { + return chooseFile; + }, get socketManager() { + return socketManager; + } }; + Object.defineProperty(__returned__, "__isScriptSetup", { enumerable: false, value: true }); + return __returned__; + } + }; + function _sfc_render$1(_ctx, _cache, $props, $setup, $data, $options) { + const _component_uni_icons = resolveEasycom(vue.resolveDynamicComponent("uni-icons"), __easycom_0); + return vue.openBlock(), vue.createElementBlock( + vue.Fragment, + null, + [ + vue.createElementVNode("view", { class: "status-bar" }), + $setup.showNewChatModal ? (vue.openBlock(), vue.createElementBlock("view", { + key: 0, + class: "ncd-overlay", + onClick: $setup.closeNewChatModal + }, [ + vue.createElementVNode("view", { + class: "ncd-card", + onClick: _cache[0] || (_cache[0] = vue.withModifiers(() => { + }, ["stop"])) + }, [ + vue.createElementVNode("view", { class: "ncd-header" }, [ + vue.createElementVNode("view", { class: "ncd-title-eng" }, [ + vue.createElementVNode("text", null, "NEW") + ]), + vue.createElementVNode("text", { class: "ncd-title-zh" }, "创建对话"), + vue.createVNode(_component_uni_icons, { + type: "closeempty", + color: "#ff0000", + size: "24", + onClick: $setup.closeNewChatModal + }) + ]), + vue.createElementVNode("text", null, "选择对话类型,开启全新会话体验"), + vue.createElementVNode("view", { class: "ncd-options" }, [ + vue.createElementVNode("view", { class: "ncd-option ncd-normal" }, [ + vue.createElementVNode("view", { class: "ncd-opt-icon" }, [ + vue.createVNode(_component_uni_icons, { + type: "chat", + color: "#000000", + size: "30" + }) + ]), + vue.createElementVNode("view", { + class: "ncd-opt-title", + onClick: $setup.selectNormalChat + }, [ + vue.createElementVNode("view", { class: "ncd-opt-title-1" }, "普通会话"), + vue.createElementVNode("view", { class: "ncd-opt-title-2" }, "与AI自由对话,探索任何话题") + ]), + vue.createVNode(_component_uni_icons, { + type: "arrow-right", + class: "arrow-right-style" + }) + ]), + vue.createElementVNode("view", { class: "ncd-option ncd-intelligence" }, [ + vue.createElementVNode("view", { class: "ncd-opt-icon" }, [ + vue.createVNode(_component_uni_icons, { + type: "star", + color: "#ffffff", + size: "30" + }) + ]), + vue.createElementVNode("view", { class: "ncd-opt-title" }, [ + vue.createElementVNode("view", { class: "ncd-opt-title-1" }, "智能体会话"), + vue.createElementVNode("view", { class: "ncd-opt-title-2" }, "选择专属智能体,获得精准专业服务") + ]), + vue.createVNode(_component_uni_icons, { + type: "arrow-right", + class: "arrow-right-style" + }) + ]) + ]) + ]) + ])) : vue.createCommentVNode("v-if", true), + vue.createElementVNode("view", { class: "chat-page page-container" }, [ + $setup.isChatSidebar ? (vue.openBlock(), vue.createElementBlock("view", { + key: 0, + class: "mask", + onClick: $setup.handleChatSidebar + })) : vue.createCommentVNode("v-if", true), + vue.createElementVNode( + "view", + { + class: vue.normalizeClass(["chat-sidebar", { "sidebar-show": $setup.isChatSidebar }]) + }, + [ + vue.createVNode($setup["ChatSidebar"], { + chatList: $setup.UserConversations, + showNewChatModal: $setup.showNewChatModal, + "onUpdate:showNewChatModal": _cache[1] || (_cache[1] = ($event) => $setup.showNewChatModal = $event), + currentSessionId: $setup.currentSessionId, + "onUpdate:currentSessionId": _cache[2] || (_cache[2] = ($event) => $setup.currentSessionId = $event), + chatType: $setup.ChatType, + "onUpdate:chatType": _cache[3] || (_cache[3] = ($event) => $setup.ChatType = $event), + onRefreshConversations: $setup.takeUserConversations + }, null, 8, ["chatList", "showNewChatModal", "currentSessionId", "chatType"]) + ], + 2 + /* CLASS */ + ), + vue.createElementVNode("view", { class: "chat-wrapper" }, [ + vue.createElementVNode("view", { class: "chat-hearder" }, [ + vue.createElementVNode("view", { class: "chat-btn-group" }, [ + vue.createElementVNode("view", { + class: "head-btn", + onClick: $setup.handleChatSidebar + }, [ + vue.createElementVNode("view", { class: "iconfont icon-caidan" }) + ]), + vue.createElementVNode("view", { + class: "head-btn", + onClick: $setup.goWorkSpace + }, [ + vue.createElementVNode("view", { class: "iconfont icon-wenjianjia" }) + ]), + vue.createElementVNode("view", { + class: "head-btn", + onClick: $setup.goCloudDatabase + }, [ + vue.createElementVNode("view", { class: "iconfont icon-cloud" }) + ]), + vue.createElementVNode("view", { + class: "head-btn log-out", + onClick: $setup.logOut + }, [ + vue.createElementVNode("view", { class: "iconfont icon-tuichu" }) + ]) + ]) + ]), + vue.createElementVNode("view", { class: "main-chat" }, [ + vue.createElementVNode("scroll-view", { + class: "chat-messages", + direction: "vertical", + "scroll-y": "", + "scroll-into-view": $setup.scrollToView, + "upper-threshold": 0, + "scroll-with-animation": true, + onScroll: $setup.onScroll + }, [ + (vue.openBlock(true), vue.createElementBlock( + vue.Fragment, + null, + vue.renderList($setup.currentMessages, (message, index) => { + var _a; + return vue.openBlock(), vue.createElementBlock( + vue.Fragment, + { key: index }, + [ + $setup.ChatType === 0 && message.role !== "tool" ? (vue.openBlock(), vue.createElementBlock("view", { + key: 0, + class: vue.normalizeClass(["chat-message", { "message-user": message.role === "user" }]), + id: "msg-" + index + }, [ + message.role === "user" ? (vue.openBlock(), vue.createElementBlock( + "view", + { + key: 0, + class: vue.normalizeClass(["chat-avatar", { "chat-avatar-user": message.role === "user" }]) + }, + [ + message.role === "user" && ((_a = $setup.UserData) == null ? void 0 : _a.avatar) ? (vue.openBlock(), vue.createElementBlock("image", { + key: 0, + src: $setup.UserData.avatar, + class: "friend-avatar", + mode: "aspectFill" + }, null, 8, ["src"])) : (vue.openBlock(), vue.createElementBlock("view", { + key: 1, + class: "iconfont icon-yonghuziliao" + })) + ], + 2 + /* CLASS */ + )) : vue.createCommentVNode("v-if", true), + message.content && String(message.content).trim() !== "" || message._streaming ? (vue.openBlock(), vue.createElementBlock("view", { + key: 1, + class: vue.normalizeClass(["chat-content", { "chat-content-user": message.role === "user", "chat-content-assistant": message.role === "assistant" }]), + onClick: ($event) => $setup.handleChatContentClick($event, message) + }, [ + message._streaming && (!message.content || String(message.content).trim() === "") ? (vue.openBlock(), vue.createElementBlock("view", { + key: 0, + class: "bubble-loading-dots" + }, [ + vue.createElementVNode("view", { class: "bubble-dot" }), + vue.createElementVNode("view", { class: "bubble-dot" }), + vue.createElementVNode("view", { class: "bubble-dot" }) + ])) : vue.createCommentVNode("v-if", true), + $setup.parseFileInfo(message.content).textContent ? (vue.openBlock(), vue.createElementBlock("view", { + key: 1, + innerHTML: $setup.pareseMarkdown($setup.parseFileInfo(message.content).textContent) + }, null, 8, ["innerHTML"])) : vue.createCommentVNode("v-if", true), + $setup.parseFileInfo(message.content).files.length > 0 ? (vue.openBlock(), vue.createElementBlock("view", { + key: 2, + class: "message-file-list" + }, [ + (vue.openBlock(true), vue.createElementBlock( + vue.Fragment, + null, + vue.renderList($setup.parseFileInfo(message.content).files, (file, idx) => { + return vue.openBlock(), vue.createElementBlock("view", { + key: idx, + class: "file-item" + }, [ + ["jpg", "jpeg", "png", "gif", "webp", "bmp"].includes(file.extendName.toLowerCase()) ? (vue.openBlock(), vue.createElementBlock("image", { + key: 0, + src: file.url, + mode: "widthFix", + class: "message-image", + onClick: ($event) => $setup.previewImage(file.url) + }, null, 8, ["src", "onClick"])) : (vue.openBlock(), vue.createElementBlock("view", { + key: 1, + class: "message-file", + onClick: ($event) => $setup.openFile(file.url) + }, [ + vue.createElementVNode("view", { class: "file-icon" }, "📄"), + vue.createElementVNode( + "view", + { class: "file-name" }, + vue.toDisplayString(file.name), + 1 + /* TEXT */ + ), + vue.createElementVNode( + "view", + { class: "file-size" }, + vue.toDisplayString($setup.formatFileSize(file.fileSize)), + 1 + /* TEXT */ + ) + ], 8, ["onClick"])) + ]); + }), + 128 + /* KEYED_FRAGMENT */ + )) + ])) : vue.createCommentVNode("v-if", true) + ], 10, ["onClick"])) : vue.createCommentVNode("v-if", true) + ], 10, ["id"])) : vue.createCommentVNode("v-if", true) + ], + 64 + /* STABLE_FRAGMENT */ + ); + }), + 128 + /* KEYED_FRAGMENT */ + )), + $setup.ChatType === 1 ? (vue.openBlock(true), vue.createElementBlock( + vue.Fragment, + { key: 0 }, + vue.renderList($setup.currentMessages, (message, index) => { + var _a; + return vue.openBlock(), vue.createElementBlock("view", { + class: vue.normalizeClass(["chat-message", { "message-user": message.sender === $setup.UserId }]), + key: index, + id: "msg-" + index + }, [ + vue.createElementVNode( + "view", + { + class: vue.normalizeClass(["chat-avatar", { "chat-avatar-user": message.sender === $setup.UserId }]) + }, + [ + message.sender === $setup.UserId && ((_a = $setup.UserData) == null ? void 0 : _a.avatar) ? (vue.openBlock(), vue.createElementBlock("image", { + key: 0, + src: $setup.UserData.avatar, + class: "friend-avatar", + mode: "aspectFill" + }, null, 8, ["src"])) : $setup.takeTalkUserAvatar(message.sender) ? (vue.openBlock(), vue.createElementBlock("image", { + key: 1, + src: $setup.takeTalkUserAvatar(message.sender), + class: "friend-avatar", + mode: "aspectFill" + }, null, 8, ["src"])) : (vue.openBlock(), vue.createElementBlock("view", { + key: 2, + class: "iconfont icon-yonghuziliao" + })) + ], + 2 + /* CLASS */ + ), + vue.createElementVNode("view", { + class: vue.normalizeClass(["chat-content", { "chat-content-user": message.sender === $setup.UserId }]), + onClick: ($event) => $setup.handleChatContentClick($event, message) + }, [ + message.content && String(message.content).trim() !== "" ? (vue.openBlock(), vue.createElementBlock("view", { + key: 0, + innerHTML: $setup.pareseMarkdown(message.content) + }, null, 8, ["innerHTML"])) : vue.createCommentVNode("v-if", true), + message.contentJson ? (vue.openBlock(), vue.createElementBlock("view", { + key: 1, + class: "message-file-list" + }, [ + (vue.openBlock(true), vue.createElementBlock( + vue.Fragment, + null, + vue.renderList(JSON.parse(message.contentJson), (file, idx) => { + return vue.openBlock(), vue.createElementBlock("view", { + key: idx, + class: "file-item" + }, [ + ["jpg", "jpeg", "png", "gif", "webp", "bmp"].includes(file.extendName.toLowerCase()) ? (vue.openBlock(), vue.createElementBlock("image", { + key: 0, + src: file.url, + mode: "widthFix", + class: "message-image", + onClick: ($event) => $setup.previewImage(file.url) + }, null, 8, ["src", "onClick"])) : (vue.openBlock(), vue.createElementBlock("view", { + key: 1, + class: "message-file", + onClick: ($event) => $setup.openFile(file.url) + }, [ + vue.createElementVNode("view", { class: "file-icon" }, "📄"), + vue.createElementVNode( + "view", + { class: "file-name" }, + vue.toDisplayString(file.name), + 1 + /* TEXT */ + ), + vue.createElementVNode( + "view", + { class: "file-size" }, + vue.toDisplayString($setup.formatFileSize(file.fileSize)), + 1 + /* TEXT */ + ) + ], 8, ["onClick"])) + ]); + }), + 128 + /* KEYED_FRAGMENT */ + )) + ])) : vue.createCommentVNode("v-if", true) + ], 10, ["onClick"]) + ], 10, ["id"]); + }), + 128 + /* KEYED_FRAGMENT */ + )) : vue.createCommentVNode("v-if", true), + $setup.ChatType === 2 ? (vue.openBlock(true), vue.createElementBlock( + vue.Fragment, + { key: 1 }, + vue.renderList($setup.currentMessages, (message, index) => { + var _a; + return vue.openBlock(), vue.createElementBlock("view", { + class: vue.normalizeClass(["chat-message", { "message-user": message.sender === $setup.UserId }]), + key: index, + id: "msg-" + index + }, [ + vue.createElementVNode( + "view", + { + class: vue.normalizeClass(["chat-avatar", { "chat-avatar-user": message.sender === $setup.UserId }]) + }, + [ + message.sender === $setup.UserId && ((_a = $setup.UserData) == null ? void 0 : _a.avatar) ? (vue.openBlock(), vue.createElementBlock("image", { + key: 0, + src: $setup.UserData.avatar, + class: "friend-avatar", + mode: "aspectFill" + }, null, 8, ["src"])) : $setup.groupMemberList.length > 0 && $setup.getGroupMemberAvatarById(message.sender) ? (vue.openBlock(), vue.createElementBlock("image", { + key: 1, + src: $setup.getGroupMemberAvatarById(message.sender), + class: "friend-avatar", + mode: "aspectFill" + }, null, 8, ["src"])) : (vue.openBlock(), vue.createElementBlock("view", { + key: 2, + class: "iconfont icon-yonghuziliao" + })) + ], + 2 + /* CLASS */ + ), + vue.createElementVNode("view", { + class: vue.normalizeClass(["chat-content", { "chat-content-user": message.sender === $setup.UserId }]), + onClick: ($event) => $setup.handleChatContentClick($event, message) + }, [ + message.message && String(message.message).trim() !== "" ? (vue.openBlock(), vue.createElementBlock("view", { + key: 0, + innerHTML: $setup.pareseMarkdown(message.message) + }, null, 8, ["innerHTML"])) : vue.createCommentVNode("v-if", true), + message.contentJson ? (vue.openBlock(), vue.createElementBlock("view", { + key: 1, + class: "message-file-list" + }, [ + (vue.openBlock(true), vue.createElementBlock( + vue.Fragment, + null, + vue.renderList(JSON.parse(message.contentJson), (file, idx) => { + return vue.openBlock(), vue.createElementBlock("view", { + key: idx, + class: "file-item" + }, [ + ["jpg", "jpeg", "png", "gif", "webp", "bmp"].includes(file.extendName.toLowerCase()) ? (vue.openBlock(), vue.createElementBlock("image", { + key: 0, + src: file.url, + mode: "widthFix", + class: "message-image", + onClick: ($event) => $setup.previewImage(file.url) + }, null, 8, ["src", "onClick"])) : (vue.openBlock(), vue.createElementBlock("view", { + key: 1, + class: "message-file", + onClick: ($event) => $setup.openFile(file.url) + }, [ + vue.createElementVNode("view", { class: "file-icon" }, "📄"), + vue.createElementVNode( + "view", + { class: "file-name" }, + vue.toDisplayString(file.name), + 1 + /* TEXT */ + ), + vue.createElementVNode( + "view", + { class: "file-size" }, + vue.toDisplayString($setup.formatFileSize(file.fileSize)), + 1 + /* TEXT */ + ) + ], 8, ["onClick"])) + ]); + }), + 128 + /* KEYED_FRAGMENT */ + )) + ])) : vue.createCommentVNode("v-if", true) + ], 10, ["onClick"]) + ], 10, ["id"]); + }), + 128 + /* KEYED_FRAGMENT */ + )) : vue.createCommentVNode("v-if", true), + vue.createElementVNode("view", { + id: "chat-bottom-a", + style: { "height": "1rpx" } + }), + vue.createElementVNode("view", { + id: "chat-bottom-b", + style: { "height": "1rpx" } + }) + ], 40, ["scroll-into-view"]), + vue.createElementVNode( + "view", + { + class: vue.normalizeClass(["chat-interactive-container", { "interactive-disabled": $setup.isThinking }]) + }, + [ + vue.createElementVNode("view", { class: "chat-interactive-group" }, [ + vue.createElementVNode( + "view", + { + class: vue.normalizeClass(["chat-interactive-btn", { disabled: $setup.isThinking }]), + onClick: _cache[4] || (_cache[4] = ($event) => !$setup.isThinking && $setup.uploadPhoto()) + }, + "拍照上传", + 2 + /* CLASS */ + ), + vue.createElementVNode( + "view", + { + class: vue.normalizeClass(["chat-interactive-btn", { disabled: $setup.isThinking }]), + onClick: _cache[5] || (_cache[5] = ($event) => !$setup.isThinking && $setup.uploadFile()) + }, + "上传文件", + 2 + /* CLASS */ + ) + ]), + $setup.previewFileArray.length > 0 ? (vue.openBlock(), vue.createElementBlock("view", { + key: 0, + class: "image-list" + }, [ + vue.createElementVNode("view", { class: "image-tags" }, [ + (vue.openBlock(true), vue.createElementBlock( + vue.Fragment, + null, + vue.renderList($setup.previewFileArray, (item, index) => { + return vue.openBlock(), vue.createElementBlock("view", { + class: "image-tag", + key: index + }, [ + vue.createElementVNode( + "text", + { class: "tag-name" }, + vue.toDisplayString(item.name), + 1 + /* TEXT */ + ), + vue.createElementVNode("text", { + class: "tag-close", + onClick: vue.withModifiers(($event) => $setup.deleteImage(index), ["stop"]) + }, "×", 8, ["onClick"]) + ]); + }), + 128 + /* KEYED_FRAGMENT */ + )) + ]) + ])) : vue.createCommentVNode("v-if", true), + vue.createElementVNode( + "view", + { + class: vue.normalizeClass(["chat-input-container", { "input-disabled": $setup.isThinking }]) + }, + [ + vue.withDirectives(vue.createElementVNode("textarea", { + class: "message-input", + placeholder: $setup.isThinking ? $setup.isSelfSent ? "AI 正在回复中..." : "电脑正在运行,请稍后" : "输入消息...", + disabled: $setup.isThinking, + "onUpdate:modelValue": _cache[6] || (_cache[6] = ($event) => $setup.textMessage = $event), + "auto-height": "" + }, null, 8, ["placeholder", "disabled"]), [ + [vue.vModelText, $setup.textMessage] + ]), + $setup.isThinking ? (vue.openBlock(), vue.createElementBlock("view", { + key: 0, + class: "input-btn-group stop-message-btn", + onClick: $setup.stopConversation + }, [ + vue.createElementVNode("view", { class: "iconfont icon-tingzhi" }) + ])) : (vue.openBlock(), vue.createElementBlock("view", { + key: 1, + class: "input-btn-group send-message-btn", + onClick: _cache[7] || (_cache[7] = ($event) => $setup.sendMessage()) + }, [ + vue.createElementVNode("view", { class: "iconfont icon-fasong" }) + ])) + ], + 2 + /* CLASS */ + ) + ], + 2 + /* CLASS */ + ) + ]) + ]) + ]), + $setup.showMessageModal ? (vue.openBlock(), vue.createElementBlock("view", { + key: 1, + class: "ncd-overlay", + onClick: $setup.closeMessageDetail + }, [ + vue.createElementVNode("view", { + class: "ncd-card message-detail-card", + onClick: _cache[8] || (_cache[8] = vue.withModifiers(() => { + }, ["stop"])) + }, [ + vue.createElementVNode("view", { class: "ncd-header" }, [ + vue.createElementVNode("text", { class: "ncd-title-zh" }, "消息详情"), + vue.createVNode(_component_uni_icons, { + type: "closeempty", + color: "#ff0000", + size: "24", + onClick: $setup.closeMessageDetail + }) + ]), + vue.createElementVNode("scroll-view", { + class: "message-detail-content", + "scroll-y": "" + }, [ + $setup.detailLinks.length ? (vue.openBlock(), vue.createElementBlock("view", { + key: 0, + class: "detail-links-section" + }, [ + vue.createElementVNode( + "view", + { class: "detail-links-title" }, + "文件 (" + vue.toDisplayString($setup.detailLinks.length) + ")", + 1 + /* TEXT */ + ), + (vue.openBlock(true), vue.createElementBlock( + vue.Fragment, + null, + vue.renderList($setup.detailLinks, (link, i) => { + return vue.openBlock(), vue.createElementBlock("view", { + key: i, + class: "detail-link-item", + onClick: ($event) => $setup.openLink(link.url) + }, [ + vue.createElementVNode( + "text", + { class: "link-text" }, + vue.toDisplayString(link.name), + 1 + /* TEXT */ + ) + ], 8, ["onClick"]); + }), + 128 + /* KEYED_FRAGMENT */ + )) + ])) : (vue.openBlock(), vue.createElementBlock("view", { + key: 1, + class: "detail-empty" + }, [ + vue.createElementVNode("text", null, "无可下载文件") + ])) + ]) + ]) + ])) : vue.createCommentVNode("v-if", true), + $setup.downloadToast.show ? (vue.openBlock(), vue.createElementBlock( + "view", + { + key: 2, + class: vue.normalizeClass(["download-toast-overlay", $setup.downloadToast.show ? "" : ""]) + }, + [ + vue.createElementVNode( + "view", + { + class: vue.normalizeClass(["download-toast-card", "download-toast-" + $setup.downloadToast.type]) + }, + [ + $setup.downloadToast.type === "loading" ? (vue.openBlock(), vue.createElementBlock("view", { + key: 0, + class: "download-toast-icon" + }, [ + vue.createElementVNode("view", { class: "toast-dot" }), + vue.createElementVNode("view", { class: "toast-dot" }), + vue.createElementVNode("view", { class: "toast-dot" }) + ])) : (vue.openBlock(), vue.createElementBlock( + "text", + { + key: 1, + class: "download-toast-icon" + }, + vue.toDisplayString($setup.downloadToast.type === "success" ? "✓" : "✕"), + 1 + /* TEXT */ + )), + vue.createElementVNode( + "text", + { class: "download-toast-text" }, + vue.toDisplayString($setup.downloadToast.message), + 1 + /* TEXT */ + ) + ], + 2 + /* CLASS */ + ) + ], + 2 + /* CLASS */ + )) : vue.createCommentVNode("v-if", true) + ], + 64 + /* STABLE_FRAGMENT */ + ); + } + const PagesTextText = /* @__PURE__ */ _export_sfc(_sfc_main$2, [["render", _sfc_render$1], ["__scopeId", "data-v-fdf84df1"], ["__file", "D:/Projects/uniapp/app-test/test1/pages/text/text.vue"]]); + const _sfc_main$1 = { + name: "IntuitiveAipptViewer", + props: { + visible: { + type: Boolean, + default: true + }, + htmlContent: { + type: String, + default: '\n \n \n \n
\n \n
\n
\n \n

\n 三國歷史\n

\n \n

\n 從黃巾起義到三分歸晉\n

\n \n
\n
\n \n

\n 東漢末年 • 群雄並起 • 三分天下\n

\n

\n 公元184年 — 280年\n

\n \n
\n
\n
\n \n
\n

\n 目 录\n

\n
    \n
  • \n 第一章 东汉末年与黄巾起义3\n
  • \n
  • \n 第二章 群雄割据与董卓乱政5\n
  • \n
  • \n 第三章 曹操崛起与官渡之战8\n
  • \n
  • \n 第四章 孙刘联盟与赤壁之战12\n
  • \n
  • \n 第五章 三分天下格局的形成16\n
  • \n
  • \n 第六章 蜀汉政权的建立与发展20\n
  • \n
  • \n 第七章 曹魏政权的巩固与传承24\n
  • \n
  • \n 第八章 东吴政权的繁荣与衰落28\n
  • \n
  • \n 第九章 诸葛亮北伐与三国相持32\n
  • \n
  • \n 第十章 三国归晋与历史终结37\n
  • \n
  • \n 附录 三国主要人物简介42\n
  • \n
  • \n 附录 三国时期著名战役45\n
  • \n
  • \n 附录 三国疆域与行政区划48\n
  • \n
\n
\n \n

\n 第一章 东汉末年与黄巾起义\n

\n

\n 东汉末年形势图\n

\n

\n 1.1 东汉王朝的衰落\n

\n

\n 东汉王朝自光武帝刘秀建立以来,历经明帝、章帝的盛世,至和帝以后,外戚与宦官交替专权,政治日趋腐败。东汉后期,皇帝的寿命普遍较短,多为幼年即位,导致太后临朝称制,外戚势力趁机掌控朝政。桓灵二帝时期,宦官势力达到顶峰“十常侍”把持朝政,卖官鬻爵,横征暴敛,导致民不聊生。\n

\n

\n 东汉末年,中央政府的统治力急剧衰落。地方官员各自为政,中央对地方的控制力减弱。土地兼并严重,大量农民失去土地,成为流民。赋税沉重,徭役频繁,加上自然灾害频发,百姓生活陷入绝境。这种局面为后来的农民起义和社会动荡埋下了伏笔。\n

\n \n

\n 此外,东汉的军事制度也出现了严重问题。边防军队逐渐废弛,地方军阀割据一方,中央对军队的控制力减弱。各州郡的太守、刺史不仅掌握行政权,还拥有相当规模的私人武装,形成了割据一方的势力。这些因素共同促成了东汉王朝的最终崩溃。\n

\n

\n 1.2 黄巾起义的爆发\n

\n

\n 中平元年(公元184年),张角、张梁、张宝三兄弟在冀州钜鹿(今河北巨鹿)发动了大规模农民起义。张角创立“太平道”,以道教为外衣,以治病救人为号召,在短短十余年间发展信徒数十万人,遍布东汉十三州。\n

\n

\n 黄巾军以“苍天已死,黄天当立,岁在甲子,天下大吉”为口号,头戴黄巾,象征对汉朝统治的否定。起义军分为十路,同时在全国各地发动进攻,短时间内攻克了许多郡县,东汉王朝的统治基础受到严重冲击。\n

\n

\n 面对来势汹汹的黄巾起义,东汉朝廷惊慌失措。灵帝急忙下诏各地募兵抵抗,并启用皇甫嵩、朱儁、卢植等名将率军镇压。同时,允许地方州郡自行组织军队,这为后来军阀割据提供了合法依据。\n

\n

\n 1.3 黄巾起义的历史影响\n

\n

\n 黄巾起义虽然最终被镇压,但它对东汉王朝的统治造成了致命打击。\n

\n

\n 首先,它彻底瓦解了东汉中央政府的权威,使得地方割据成为可能。其次,它消耗了东汉政府大量的军事和经济资源,国库空虚。再次,它打破了士族对仕途的垄断,为寒门子弟提供了上升通道。\n

\n

\n 更重要的是,黄巾起义锻炼出了一批军事人才。曹操、刘备、孙坚等人都是在镇压黄巾起义中崭露头角,逐渐成长为割据一方的军阀。可以说,没有黄巾起义,就没有后来的三国鼎立局面。\n

\n

\n 黄巾起义虽然失败了,但它揭开了三国时代的序幕,是中国历史上一次重要的农民起义。它的历史意义在于:打破了东汉末年的政治格局,为群雄逐鹿提供了舞台;促进了思想的解放,为各种政治势力的兴起创造了条件。\n

\n

\n 1.4 董卓进京与废立\n

\n

\n 中平六年(公元189年),汉灵帝刘辨去世,少帝刘辩即位。外戚何进与宦官十常侍的矛盾激化,双方展开激烈的权力斗争。何进为了对抗宦官,\n

\n \n

\n 密召凉州军阀董卓率军入京。\n

\n

\n 董卓率军进入洛阳后,立即控制了朝政。他先设计诛杀了宦官集团,随后废黜少帝刘辩,改立陈留王刘协为帝,是为汉献帝。董卓自任相国,总揽朝政,权倾朝野。为了巩固权力,董卓还纵容军队烧杀抢掠,洛阳城陷入一片混乱。\n

\n

\n 董卓的倒行逆施引起了天下诸侯的强烈不满。曹操、袁绍、袁术、公孙瓒等十八路诸侯联合讨伐董卓,推举袁绍为盟主。虽然联军最终未能消灭董卓,但董卓被迫迁都长安,洛阳城被付之一炬。这次讨董战争标志着东汉王朝的正式分裂,群雄割据的时代正式开始。\n

\n

\n 第二章 群雄割据与董卓乱政\n

\n

\n 2.1 董卓的暴政与灭亡\n

\n

\n 董卓迁都长安后,更加骄横跋扈。他自封为“太师”,地位在诸侯王之上,佩剑上殿,目中无人。为了镇压反对意见,董卓滥杀无辜,残害忠良,朝野上下敢怒而不敢言。\n

\n

\n 初平三年(公元192年),司徒王允利用董卓与吕布之间的矛盾,设计除掉了董卓。王允先是拉拢吕布为内应,然后趁董卓入宫朝拜之时,\n

\n

\n 让吕布将其刺杀。董卓死后,他的部将李傕、郭汜率军攻入长安,杀死王允,挟持汉献帝,关中地区再次陷入混乱。\n

\n

\n 董卓的灭亡并没有结束混乱局面,反而使局势更加复杂。李傕、郭汜之间互相攻杀,长安城遭到严重破坏,汉献帝沦为傀儡,各地割据势力更加嚣张,东汉王朝名存实亡。\n

\n

\n 2.2 曹操的初步崛起\n

\n

\n 曹操(字孟德,沛国谯县人)是三国时期最重要的政治家、军事家和文学家。曹操的祖父曹腾是东汉末年的宦官,父亲曹嵩是曹腾的养子,曾任太尉。曹操少年时期就表现出非凡的才干,他机智过人,胸有大志。\n

\n

\n 中平元年,曹操参加镇压黄巾起义,因功升任济南相。后来董卓进京,曹操逃出洛阳,在陈留起兵讨伐董卓。曹操是少数几个真正出击董卓的诸侯之一,虽然战败,但名声大振。\n

\n \n

\n 初平三年,曹操击破青州黄巾军,收降卒三十余万,人口百余万,创建了著名的“青州兵”。这是曹操第一支正规的私家军队,成为他逐鹿中原的主力。此后,曹操相继击败袁术、陶谦、吕布等割据势力,逐渐成为北方最强大的军阀。\n

\n

\n 2.3 刘备的艰难创业\n

\n

\n 刘备(字玄德,涿郡涿县人)是汉景帝之子中山靖王刘胜的后代,具有皇室血统。但到了刘备这一代,家道已经衰落,只能靠织席贩履为生。刘备仪表非凡,胸怀大志结交豪杰,在当地小有名气。\n

\n

\n 黄巾起义爆发后,刘备与关羽、张飞桃园结义,组成最早的创业团队。刘备率领义军参加镇压黄巾起义,因功被封为安喜县尉。此后,刘备先后依附于公孙瓒、陶谦、曹操、袁绍、刘表等诸侯,颠沛流离,寄人篱下。\n

\n

\n 建安六年(公元201年),刘备投奔荆州牧刘表,被安排在新野驻守。在荆州的七年里,刘备广招贤才,诸葛亮就在此时加入刘备集团。诸葛亮隆中对策,为刘备制定了夺取天下的大政方针,刘备的霸业从此开始起步。\n

\n

\n 2.4 孙坚与孙策的基业\n

\n

\n 孙坚(字文台,吴郡富春人)是东吴政权的奠基人之一。他出身寒门,但英勇善战,在镇压黄巾起义和讨伐董卓的战争中表现出色,\n

\n

\n 被封为长沙太守、乌程侯。\n

\n

\n 初平二年(公元191年),袁术派孙坚进攻荆州刘表。孙坚在襄阳之战中击败刘表部将黄祖,乘胜追击时中箭身亡,年仅三十七岁。孙坚死后,其部众由其侄孙贲率领,继续依附于袁术。\n

\n

\n 孙策(字伯符)是孙坚的长子,他继承父亲的遗志,在袁术麾下效力。建安五年(公元200年),孙策率军渡过长江,攻占江东地区,先后击败严白虎、王朗等地方势力,迅速占领了吴郡、会稽、豫章、庐江、丹阳等郡。孙策善用人才,周瑜、张昭等人纷纷来投,东吴基业初具规模。\n

\n

\n 2.5 官渡之战前的北方局势\n

\n

\n 建安五年(公元200年)前后,中国北方形成了曹操与袁绍两大集团对峙的局面。曹操占据兖州、豫州、徐州部分地区,挟天子以令诸侯,实力日益强盛。袁绍占据冀州、青州、并州、幽州,拥有精兵数十万,地广人多,\n

\n \n

\n 势力最强大。\n

\n

\n 袁绍手下的谋士郭图、审配等人主张早日发动对曹操的进攻,而田丰、沮授则认为应该先巩固后方,等待时机。袁绍最终采纳了主战派的建议,决定发兵南下。与此同时,曹操也积极备战,在官渡一线构筑防线。\n

\n

\n 官渡之战是三国时期最关键的战役之一,此战决定了北方的归属,也奠定了三国鼎立的基础。战争的胜负虽然取决于许多因素,但曹操的军事才能和战略眼光无疑是决定性的。\n

\n

\n 第三章 曹操崛起与官渡之战\n

\n

\n 3.1 曹操的政治举措\n

\n

\n 曹操之所以能够在群雄中脱颖而出,不仅仅依靠军事才能,更重要的是他的政治举措。建安元年(公元196年),曹操迎接汉献帝迁都许昌,开始了“挟天子以令诸侯”的政治策略。\n

\n

\n 曹操挟持汉献帝后,打着皇帝的旗号征讨四方,取得了政治上的主动权。他先后消灭了袁术、吕布、刘表等割据势力,又迫使韩遂、马超等西北军阀臣服。在军事胜利的基础上,曹操推行了一系列政治改革。\n

\n

\n 在用人方面,曹操打破门第观念,实行“唯才是举”的政策。他多次下诏求贤,只要有才能的人,无论出身高低,都能得到重用。\n

\n

\n 这使得曹操麾下聚集了大量人才,谋士如荀彧、郭嘉、贾诩、荀攸、程昱等,武将如张辽、徐晃、于禁、乐进、李典等。\n

\n

\n 3.2 曹操的经济与军事改革\n

\n

\n 曹操在经济方面推行“屯田制”,让士兵和流民开垦荒地,生产粮食。屯田制分为军屯和民屯两种,有效地解决了粮食问题,为军事行动提供了充足的物资保障。\n

\n

\n 在军事上,曹操改革兵制,建立了一支训练有素、纪律严明的军队。他重视军队的训练和装备,注重发挥各将领的特长,形成了一套完整的军事指挥体系。曹操还著有《孙子略解》《兵书接要》等军事著作,展现了他深厚的军事理论素养。\n

\n

\n 曹操还善于利用政治和外交手段瓦解对手。他分化瓦解敌对势力,\n

\n \n

\n 必要时使用离间计、反间计等计谋,以最小的代价换取最大的胜利。这些综合手段的运用,使曹操成为三国时期最杰出的政治家和军事家。\n

\n

\n 3.3 官渡之战的背景\n

\n

\n 建安五年(公元200年)二月,袁绍率十万大军南下攻打曹操。袁绍的军队人数众多,装备精良,士气旺盛。而曹操的兵力只有两三万,双方实力悬殊。\n

\n

\n 面对强敌,曹操采取了正确的战略战术。他先发制人,击败了刘备,消除了侧翼威胁,然后集中兵力在官渡一线设防。曹操知道自己的兵力不足以与袁绍正面对抗,因此采取了以逸待劳、坚壁不战的策略。\n

\n

\n 袁绍虽然兵力占优,但指挥失当,多次错失良机。他的谋士之间互相倾轧,不能形成统一的战略。而曹操则能够虚心纳谏,灵活应对战场变化。两军的对峙持续了半年多,战争进入相持阶段。\n

\n

\n 3.4 官渡之战的经过\n

\n

\n 建安五年八月,袁绍率军逼近官渡,两军在此展开决战。袁绍依仗兵力优势,采取强攻战术,但曹操防守严密,几次进攻都未能得逞。\n

\n

\n 九月,曹操率军袭击袁绍的运粮车队,烧毁了袁绍的大量粮草。十月,袁绍派淳于琼率万余人护送粮草,驻守在乌巢。曹操得知这一情报后,亲率五千精兵连夜突袭乌巢,一把火烧掉了袁绍的全部粮草。\n

\n

\n 乌巢失守后,袁绍军心动摇。曹操乘机发动总攻,大败袁绍。袁绍仓皇逃回冀州,其主力部队损失殆尽。这场战役,曹操以少胜多,创造了军事史上的经典战例。\n

\n

\n 3.5 官渡之战的结果与影响\n

\n

\n 官渡之战后,袁绍势力一蹶不振。建安七年(公元202年),袁绍病逝,他的两个儿子袁尚、袁谭互相攻击,最终被曹操各个击破。建安十二年(公元207年),曹操彻底平定北方,占据了幽州、冀州、青州、并州、兖州、豫州、徐州等广大地区。\n

\n

\n 官渡之战的胜利,使曹操成为北方实际的统治者,为他统一北方奠定了坚实基础。此战后,曹操将主要精力用于巩固后方、发展经济、扩充军队,为南下统一全国做准备。\n

\n \n

\n 从历史角度看,官渡之战是中国古代军事史上的经典战例。曹操以少胜多、以弱胜强的成功经验,为后世军事家提供了宝贵的借鉴。此战也确立了曹操在三国鼎立格局中的主导地位。\n

\n

\n 3.6 曹操统一北方\n

\n

\n 击败袁绍后,曹操开始了统一北方的战争。建安九年(公元204年),曹操攻占邺城,吞并了袁尚的势力。建安十年(公元205年),曹操击败袁谭,收取青州。建安十一年(公元206年),曹操攻占并州,消灭了高干。\n

\n

\n 建安十二年(公元207年),曹操率军北征乌桓,在白狼山之战中大败乌桓军队,斩首蹋顿单于,彻底平定了北方边境。此战过后,北方地区获得了相对稳定的和平环境。\n

\n

\n 在平定北方的过程中,曹操注意招抚当地人才,北方社会经济得到了恢复和发展。曹操的统治为后来的曹魏政权奠定了基础,也为中国南北朝的统一创造了条件。\n

\n

\n 第四章 孙刘联盟与赤壁之战\n

\n

\n 4.1 曹操南征与荆州之变\n

\n

\n 建安十三年(公元208年),曹操平定北方后,率大军南下,目标是荆州和江东。荆州的刘表年老多病,其子刘琮继位。曹操大军压境时,刘琮在蒯越、韩嵩等人的劝说下投降曹操。\n

\n

\n 刘备当时驻守在新野,得知曹操南下的消息后,仓促撤退。曹操亲率五千精兵追击,在当阳长坂坡大败刘备。刘备被迫弃妻小而逃,在赵云的护卫下才得以脱险。\n

\n

\n 诸葛亮建议刘备联合孙权,共同抵抗曹操。当时孙权占据江东已有九年时间,根基牢固,兵精粮足。刘备派诸葛亮前往柴桑(今江西九江)说服孙权,促成孙刘联盟的形成。\n

\n

\n 4.2 孙刘联盟的形成\n

\n

\n 孙权接到曹操的劝降书后,犹豫不决。他的谋士鲁肃主张联合刘备抵抗曹操,而张昭等文臣则主张投降。鲁肃建议孙权召回周瑜,共同商讨对策。\n

\n

\n 周瑜从鄱阳湖赶回柴桑,与诸葛亮一起分析形势。周瑜认为,\n

\n \n

\n 曹操虽然兵多,但北方军队不习水战,而且后方不稳,马超、韩遂等人还在关西虎视眈眈。孙刘联军有战胜的可能。\n

\n

\n 孙权采纳了周瑜的建议,决定联合刘备抵抗曹操。他任命周瑜为左都督,程普为右都督,鲁肃为赞军校尉,率三万大军与刘备汇合。孙刘联盟的建立,为赤壁之战的胜利奠定了基础。\n

\n

\n 4.3 赤壁之战的准备\n

\n

\n 建安十三年冬,周瑜率军与刘备在江夏汇合。两军加起来共有五万余人,在赤壁与曹操大军隔江对峙。曹操的北方军队不习水战,为了克服晕船问题,他们将战船用铁索连接起来,以减少风浪的颠簸。\n

\n

\n 周瑜的部将黄盖发现曹操战船相连的弱点,建议采用火攻。他亲自写信向曹操假投降,麻痹敌人。曹操信以为真,没有防备。\n

\n

\n 战前,周瑜进行了周密的部署。他派黄盖率十艘装满易燃物品的战船靠近曹军舰队,准备实施火攻。同时命令主力部队待命,随时准备出击。\n

\n

\n 4.4 赤壁之战的经过\n

\n

\n 建安十三年十一月的一个夜晚,江面上刮起了东南风。黄盖率领十艘满载柴草、油脂的战船,向曹军舰队驶去。接近曹军时,黄盖下令点火,然后率部跳上小船离开。\n

\n

\n 着火的战船顺风冲入曹军舰队,由于战船用铁索相连,无法分散,\n

\n

\n 火势迅速蔓延。曹操的战船和岸上营地陷入一片火海,士兵死伤无数。\n

\n

\n 周瑜趁势率军渡江发动总攻,曹操大军大败。曹操被迫率残部撤退,在华容道又遭到关羽的伏击,损失惨重。此战,曹操损失了大部分军队和战船,基本丧失了统一南方的能力。\n

\n

\n 4.5 赤壁之战的结果与影响\n

\n

\n 赤壁之战后,曹操退回北方,短期内无力南下。孙权和刘备各自发展势力,形成了三国鼎立的雏形。刘备趁机夺取了荆州的江南四郡有了立足之地。\n

\n

\n 赤壁之战是中国古代军事史上最著名的以少胜多的战例之一。周瑜和黄盖的火攻计策,成为军事史上的经典战例。此战改变了三国的历史走向,\n

\n \n

\n 奠定了三国鼎立的格局。\n

\n

\n 从政治角度看,赤壁之战的胜利孙刘联盟的胜利,也是曹操“挟天子以令诸侯”战略的失败。此战后曹操不得不调整战略,转而巩固北方经营西北,为日后篡汉建魏做准备。\n

\n

\n 4.6 赤壁之战后的局势\n

\n

\n 赤壁之战后,刘备向孙权借荆州南郡,开始了建立蜀汉政权的进程。建安十四年(公元209年),周瑜率军攻占江陵,刘备表荐刘表长子刘琦为荆州刺史,自己以军师中郎将的身份治理荆州。\n

\n

\n 建安十五年(公元210年),周瑜病逝,孙权任命鲁肃为都督。鲁肃主张将荆州借给刘备,以共同对抗曹操。刘备得到荆州后,实力大增,开始向益州发展。\n

\n

\n 建安十六年(公元211年),刘璋邀请刘备入蜀对抗张鲁。刘备趁机率军入益州,拉开了夺取益州的序幕。建安十九年(公元214年),刘备包围成都,刘璋投降,刘备占据了益州。\n

\n

\n 第五章 三分天下格局的形成\n

\n

\n 5.1 刘备取益州与汉中\n

\n

\n 建安十六年(公元211年),益州牧刘璋听说曹操要攻打汉中张鲁,心中恐惧。谋士张松建议刘璋邀请刘备入蜀,共同抵御曹操。\n

\n

\n 刘璋采纳了这个建议,派法正迎请刘备。\n

\n

\n 刘备率军入蜀后,与刘璋的关系逐渐恶化。建安十七年(公元212年),刘备在葭萌关起兵,进攻成都。双方在涪城、绵竹等地展开激战,刘备进展顺利。\n

\n

\n 建安十九年(公元214年),刘备包围成都,刘璋被迫投降。刘备自领益州牧,封诸葛亮为军师将军,负责治理益州。从此,刘备拥有了荆州和益州两块根据地,三分天下有其一。\n

\n

\n 建安二十三年(公元218年),刘备率军进攻汉中,与曹操展开激战。建安二十四年(公元219年),刘备在定军山击败曹操,杀死曹操部将夏候渊,攻占汉中。刘备自称汉中王,实现了“隆中对”中跨有荆益的战略目标。\n

\n \n

\n 5.2 关羽失荆州\n

\n

\n 建安二十四年(公元219年),关羽在刘备攻取汉中的同时,发动了北伐襄樊的战争。关羽率军围攻樊城的曹仁,曹操派于禁率七军前来救援。\n

\n

\n 秋天的连绵大雨导致汉水泛滥,于禁的七军被洪水淹没,于禁被迫投降。关羽乘势进攻,曹操的大将于禁被俘,庞德被杀,曹操震动。关羽威震华夏,曹操甚至一度考虑迁都以避其锋芒。\n

\n

\n 然而就在关羽北伐胜利之际,孙权派吕蒙白衣渡江,袭取了荆州。关羽得知后方失守后,仓促回撤,但为时已晚。关羽最终在麦城被孙权部将马忠擒杀,荆州落入东吴手中。\n

\n

\n 关羽失荆州是三国时期的重大事件,它打破了孙刘联盟,也使刘备失去了北伐的通道。蜀汉的势力范围从此被压缩在益州一地,难以对外发展。\n

\n

\n 5.3 曹丕篡汉与曹魏建立\n

\n

\n 建安二十五年(公元220年),曹操病逝于洛阳,终年六十五岁。曹操死后,其子曹丕继承魏王爵位,掌控了魏国的军政大权。\n

\n

\n 同年十月,曹丕迫使汉献帝禅让帝位,建立魏国,是为魏文帝。东汉王朝正式灭亡,三国时代正式开始。曹丕定都洛阳,改元黄初。\n

\n

\n 曹丕称帝后,追尊曹操为魏武帝,庙号太祖。曹魏政权控制了北方中原地区,拥有最广阔的土地和最多的人口,是三国中实力最强的政权。\n

\n

\n 5.4 刘备称帝与蜀汉建立\n

\n

\n 章武元年(公元221年),刘备在成都称帝,建立蜀汉政权,是为蜀汉昭烈帝。刘备定都成都,以诸葛亮为丞相,许靖为司徒。\n

\n

\n 刘备称帝后做的第一件事就是发兵伐吴,为关羽报仇。同年,刘备亲率大军进攻东吴,试图夺回荆州。孙权派陆逊率军抵御。\n

\n

\n 蜀汉与东吴的夷陵之战爆发于章武二年(公元222年)。刘备率军在夷陵一带扎营连营数百里,与吴军相持。陆逊采用火攻,大败蜀军。刘备逃回白帝城,蜀汉元气大伤。\n

\n

\n 5.5 三国鼎立格局的正式形成\n

\n

\n 夷陵之战后,刘备病逝于白帝城,终年六十三岁。诸葛亮辅佐后主刘禅,\n

\n \n

\n 继续治理蜀汉。蜀汉与东吴重新修好,恢复了孙刘联盟。\n

\n

\n 黄初二年(公元221年),孙权也称王,建都建业,国号吴,但未正式称帝。直到黄龙元年(公元229年),孙权才正式称帝,建立东吴政权。\n

\n

\n 至此,三国鼎立的格局正式形成:曹魏占据北方中原地区,拥有洛阳、长安等大城市,人口众多,经济发达;蜀汉占据益州和汉中,地势险要,易守难攻;东吴占据江东六郡,经济繁荣,水军强大。三国之间形成了相对稳定的对峙局面。\n

\n

\n 5.6 三国鼎立时期的特点\n

\n

\n 三国鼎立时期,各国都注重发展经济、整军经武。曹魏实行九品中正制,蜀汉推行依法治国,东吴则大力发展海外贸易。三个政权都在各自的统治区域内建立了一套相对完整的政治制度。\n

\n

\n 在军事方面,三国都面临外部威胁。曹魏需要防御蜀汉和东吴的进攻,同时还要应对北方少数民族的侵扰;蜀汉和东吴则需要联合起来共同对抗曹魏。这种军事对峙持续了四十多年。\n

\n

\n 在文化方面,三国时期出现了许多杰出的文学家、艺术家和科学家。曹植的文学成就极高,诸葛亮的智慧成为后世楷模,东吴的航海技术领先世界。三国文化成为中国传统文化的重要组成部分。\n

\n

\n 第六章 蜀汉政权的建立与发展\n

\n

\n 6.1 诸葛亮辅政\n

\n

\n 章武三年(公元223年),刘备病逝于白帝城,临终前托孤于诸葛亮。刘备对诸葛亮说:“君才十倍于曹丕,必能安国,终定大事。若嗣子可辅,辅之;如其不才,君可自取。”诸葛亮感激涕零,表示“臣敢竭股肱之力,效忠贞之节,继之以死”。\n

\n

\n 诸葛亮被后主刘禅封为武乡侯,开府治事。实际上,诸葛亮成为蜀汉政权的最高执政者,总揽军政大权。他内修政治,外整军务,殚精竭虑,维护蜀汉政权的生存和发展。\n

\n

\n 在政治上,诸葛亮推行法治,注重选拔人才。他制定蜀科,依法治国,使得蜀汉政治较为清明。在经济上,诸葛亮重视农业生产,鼓励蚕桑,发展蜀锦等手工业。在军事上,诸葛亮训练军队,改进武器,\n

\n \n

\n 提升蜀军的战斗力。\n

\n

\n 6.2 蜀汉的内政与经济\n

\n

\n 诸葛亮治理蜀汉期间,实行了一系列政治经济改革。在政治制度方面,诸葛亮完善了官制,建立了较为完整的官僚体系。他注重选拔人才,不看出身门第,只看才能德行。\n

\n

\n 在经济方面,诸葛亮推行屯田制,让士兵和民众开垦荒地,增加粮食产量。他重视水利建设,维护都江堰等水利工程,保障农业生产的顺利进行。蜀锦是蜀汉的重要特产,诸葛亮大力发展蜀锦生产,通过对外贸易换取曹魏和东吴的物资。\n

\n

\n 在民族政策方面,诸葛亮采取“以夷制夷”的策略,妥善处理与西南少数民族的关系。他七擒七纵孟获,获得了少数民族的拥护,确保了蜀汉后方的稳定。\n

\n

\n 6.3 蜀汉的对外关系\n

\n

\n 刘备去世后,诸葛亮派邓芝出使东吴,修复了孙刘联盟。邓芝不辱使命,说服孙权与蜀汉重新结盟,共同对抗曹魏。此后,蜀汉与东吴的关系一直保持良好,直到蜀汉灭亡。\n

\n

\n 对于曹魏,诸葛亮采取敌对态度,但同时也保持了表面的外交关系。他多次派人出使东吴和曹魏,了解两国情况,寻求有利的外交环境。\n

\n

\n 在处理与西南少数民族的关系方面,诸葛亮表现出了高超的政治智慧。他尊重少数民族的风俗习惯,任用当地首领为官,赢得了少数民族的拥护。这为蜀汉政权的稳定提供了重要保障。\n

\n

\n 6.4 蜀汉的著名人物\n

\n

\n 蜀汉政权人才济济,文武兼备。诸葛亮是蜀汉最重要的政治家和军事家,他上知天文,下知地理,足智多谋,被誉为“千古第一相”。\n

\n

\n 在文臣方面,蜀汉有法正、董和、蒋琬、费祎、姜维等人。法正善于奇谋,是刘备的得力谋士;蒋琬、费祎继承诸葛亮的事业,治理蜀汉;姜维是诸葛亮指定的接班人,继续坚持北伐。\n

\n

\n 在武将方面,蜀汉有关羽、张飞、赵云、马超、黄忠“五虎上将”,\n

\n \n

\n 以及魏延、王平、廖化、向宠等将领。关羽、张飞是刘备的结义兄弟,英勇善战;赵云是刘备的护卫,多次救主于危难之中;马超、黄忠是后来归降的猛将。\n

\n

\n 6.5 蜀汉的政治制度\n

\n

\n 蜀汉的政体基本沿袭汉制,但也有所创新。刘禅虽然是皇帝,但实际权力掌握在丞相手中。诸葛亮开府治事,有自己的丞相府僚属,形成了与皇帝并列的行政体系。\n

\n

\n 蜀汉的官制分为中央和地方两级。中央有丞相、太尉、司徒、司空等官职,地方有州、郡、县三级。诸葛亮实行依法治蜀,制定了蜀科,作为基本法律。\n

\n

\n 蜀汉的选拔制度主要是察举和考试相结合。诸葛亮注重人才的选拔和培养,建立了完善的官员考核制度。他推荐的人才,大都德才兼备,为蜀汉的发展做出了贡献。\n

\n

\n 6.6 蜀汉的社会文化\n

\n

\n 蜀汉时期,成都成为西南地区的文化中心。诸葛亮重视教育,设立学校,培养人才。蜀汉的学术氛围浓厚,出现了许多著名的学者和文人。\n

\n

\n 蜀汉的文学艺术也有较高成就。陈寿的《三国志》是史学名著,记载了三国时期的历史。诸葛亮的前后《出师表》是千古名文,表达了他对国家的忠诚。\n

\n

\n 蜀汉的科技也有一定发展。诸葛亮发明了木牛流马、连弩等军事器械,提高了蜀军的战斗力。他还将先进的农业技术传授给西南少数民族,促进了当地经济社会的发展。\n

\n

\n 第七章 曹魏政权的巩固与传承\n

\n

\n 7.1 曹丕的政治措施\n

\n

\n 曹丕继位魏王后,实行了一系列政治改革来巩固自己的统治。他推行九品中正制,兼顾了士族和寒门的利益,暂时缓解了统治阶级内部的矛盾。这一制度成为魏晋南北朝时期的主要选官制度。\n

\n

\n 在军事方面,曹丕继续推行曹操的军事政策,加强中央对军队的控制。\n

\n \n

\n 他多次率军伐吴,虽然没有取得重大胜利,但巩固了魏国在江淮地区的防线。\n

\n

\n 在经济方面,曹丕继续推行屯田制,重视农业生产。他下令兴修水利,鼓励垦荒,使得北方经济得到恢复和发展。曹丕还注重商业贸易,促进了物资流通。\n

\n

\n 7.2 曹丕的对外战争\n

\n

\n 黄初三年(公元222年),曹丕率军伐吴,发动了第一次濡须口之战。魏军与吴军相持数月,互有胜负,最终无功而返。\n

\n

\n 黄初五年(公元224年),曹丕第二次伐吴,这次他亲自率军进攻广陵(今江苏扬州),但因为长江水涨,无功而返。黄初六年(公元225年),曹丕第三次伐吴,再次失败。\n

\n

\n 三次伐吴的失败,使曹丕认识到短时间内无法消灭东吴。他调整了战略,将主要精力放在国内建设上,发展经济,整顿内政,为最终统一天下做准备。\n

\n

\n 7.3 曹叡继位与辅政\n

\n

\n 黄初七年(公元226年),曹丕病逝于洛阳,终年四十岁。曹丕死后,其子曹叡继位,是为魏明帝。曹叡继位时只有二十二岁,资历较浅,需要依靠老臣辅政。\n

\n

\n 曹叡继位后面临的主要问题是辅政大臣的选择。曹丕临终前指定曹真、\n

\n

\n 陈群、司马懿为辅政大臣,辅佐曹叡。这三人都是曹魏的重臣各有特长。\n

\n

\n 曹叡虽然年轻,但很有才能。他能够驾驭群臣,保持政局的稳定。在军事上,他多次击退蜀汉和东吴的进攻,保卫了魏国的安全。在内政上,他注重发展经济,使得魏国实力不断增强。\n

\n

\n 7.4 司马懿的崛起\n

\n

\n 司马懿(字仲达,河内温县人)是三国时期最重要的政治家和军事家之一,也是晋朝的奠基人。他出身于司马氏家族,是东汉末年的名士。\n

\n

\n 曹操时期,司马懿被征召为官,但长期得不到重用。曹丕继位后,司马懿开始受到重用,逐渐升任要职。曹叡时期,司马懿多次率军出征,\n

\n \n

\n 屡立战功,成为魏国最重要的军事统帅之一。\n

\n

\n 司马懿曾多次抵御诸葛亮的北伐,功绩卓著。他采取的正确战略战术,有效地消耗了蜀汉的国力。司马懿还率军平定辽东,消灭了公孙渊的割据势力,为魏国解除了后顾之忧。\n

\n

\n 7.5 曹魏后期的政治\n

\n

\n 曹叡去世后,其养子曹芳继位,年仅八岁。曹爽和司马懿被任命为辅政大臣,两人共同执掌朝政。曹爽是曹真的儿子,代表曹氏宗亲的利益;司马懿代表士族集团的利益。\n

\n

\n 曹爽与司马懿之间存在尖锐的矛盾。曹爽排挤司马懿,将他明升暗降,剥夺了军权。司马懿假装生病,不问政事,暗中积蓄力量准备反击。\n

\n

\n 正始十年(公元249年),司马懿乘曹爽陪同曹芳出祭高平陵之机,发动政变,诛杀曹爽及其党羽。从此,司马家族掌控了曹魏的朝政大权,为后来的司马氏代魏建立了基础。\n

\n

\n 7.6 曹魏的灭亡\n

\n

\n 司马懿死后,其子司马师、司马昭继续执掌朝政。司马师废黜曹芳,改立曹奂为帝。司马昭弑杀魏帝曹髦,另立曹奂为帝。曹魏政权已经名存实亡。\n

\n

\n 咸熙二年(公元265年),司马昭之子司马炎代魏称帝,建立晋朝,是为晋武帝。曹魏政权至此灭亡。从曹丕称帝到曹魏灭亡,历时四十五年。\n

\n

\n 曹魏虽然灭亡了,但它为晋朝的统一奠定了基础。曹操、曹丕、曹叡祖孙三代经营北方,使得中原地区经济繁荣,社会稳定,为晋朝灭吴统一全国创造了条件。\n

\n

\n 第八章 东吴政权的繁荣与衰落\n

\n

\n 8.1 孙权的统治政策\n

\n

\n 孙权(字仲谋,吴郡富春人)是三国时期在位时间最长的君主之一。他从建安五年(公元200年)继承兄业,到太元二年(公元252年)去世,执政长达五十二年。\n

\n

\n 孙权善于用人,能够发挥各人的长处。他重用周瑜、鲁肃、吕蒙、\n

\n \n

\n 陆逊等名将,形成了东吴的四大都督系统。在文臣方面,他重用张昭、顾雍、诸葛瑾等人,治理国家。\n

\n

\n 在经济方面,孙权大力发展江南经济,鼓励农民开垦荒地,推广先进的农业生产技术。他重视海上贸易,与辽东、东南亚等地有贸易往来。东吴的造船业和航海业在当时处于世界领先地位。\n

\n

\n 8.2 东吴的对外扩张\n

\n

\n 孙权在位期间,积极向外扩张领土。建安十三年(公元208年),东吴与刘备联军在赤壁击败曹操,解除了北方的威胁。此后,东吴开始向岭南地区扩张。\n

\n

\n 建安二十五年(公元220年),孙权派步骘率军平定岭南,占据了交州(今广东、广西、越南北部)。黄龙二年(公元230年),孙权派卫温、诸葛直率军一万出海,寻找夷洲(今台湾),虽然损失较大,但加强了对台湾的联系。\n

\n

\n 嘉禾六年(公元237年),东吴派陆胤率军平定海南岛的叛乱,将海南岛纳入东吴版图。东吴的疆域扩展到了极盛时期。\n

\n

\n 8.3 东吴与蜀魏的关系\n

\n

\n 东吴与蜀汉的关系经历了合作与冲突的过程。赤壁之战后,孙刘联盟共同抗曹,保持了较长时期的合作。但荆州问题一直是两国之间的矛盾焦点。\n

\n

\n 关羽失荆州后,刘备伐吴,夷陵之战爆发,东吴击败蜀汉。两败俱伤后,\n

\n

\n 双方重新修好。诸葛亮执政期间,蜀吴关系一直保持良好,共同对抗曹魏。\n

\n

\n 东吴与曹魏的关系则比较复杂。曹丕时期,东吴曾两次向魏称臣,接受魏国的封号。但孙权并不真正服从曹魏,只是利用魏国的支持来发展自己的势力。\n

\n

\n 8.4 东吴的四大都督\n

\n

\n 东吴历史上最著名的是四位都督:周瑜、鲁肃、吕蒙、陆逊。他们在不同的历史时期担任最高军事指挥官,为东吴的发展做出了重要贡献。\n

\n

\n 周瑜是东吴的开国元勋,他辅佐孙策平定江东,\n

\n \n

\n 又在赤壁之战中击败曹操,奠定了东吴的基业。鲁肃是东吴的战略家,他主张联合刘备共同抗曹,为东吴制定了正确的战略方针。\n

\n

\n 吕蒙是东吴的名将,他白衣渡江,袭取荆州,杀死关羽,为东吴夺得了梦寐以求的荆州。陆逊是东吴后期的支柱,他火烧连营,击败刘备,又多次抵御曹魏的进攻,保卫了东吴的安全。\n

\n

\n 8.5 东吴的经济与文化\n

\n

\n 东吴时期,江南经济得到了快速发展。孙权推行一系列发展农业的措施,使得粮食产量大幅提高。江南的丝织业、制瓷业、造船业都有较大发展。\n

\n

\n 东吴的造船业在当时处于世界领先地位。东吴拥有当时世界上最庞大的船队,能够制造大型海船。东吴的航海家曾到达夷洲、澶洲等地,与东南亚诸国建立了贸易关系。\n

\n

\n 在文化方面,东吴出现了许多著名的学者和文人。虞翻、王朗、张温等人是当时的经学大师。东吴的书法、绘画艺术也有较高成就,对后世产生了深远影响。\n

\n

\n 8.6 东吴的衰落与灭亡\n

\n

\n 孙权去世后,东吴的政治开始混乱。孙亮的继位引发了内部的权力斗争,诸葛恪、孙峻、孙綝等人先后专权,政治腐败严重。\n

\n

\n 孙皓继位后,实行暴政,残害忠良,荒淫无道。他多次发动对晋的战争,但都以失败告终。天纪四年(公元280年),晋军南下,东吴灭亡。\n

\n

\n 从孙权称王到东吴灭亡,东吴历时五十九年。\n

\n

\n 东吴的灭亡标志着三国时代的结束。晋朝完成了对全国的统一,结束了自黄巾起义以来近一百年的分裂局面。\n

\n

\n 第九章 诸葛亮北伐与三国相持\n

\n

\n 9.1 诸葛亮北伐的背景\n

\n

\n 蜀汉建立后,诸葛亮制定了“北伐中原,复兴汉室”的战略方针。他认为,如果不主动出击,只会导致蜀汉与曹魏的差距越来越大,最终会被消灭。只有通过北伐,才能实现刘备“兴复汉室”的遗愿。\n

\n

\n 从客观条件看,北伐也具有一定的可行性。\n

\n \n

\n 曹魏的主要敌人是蜀汉和东吴,需要两面作战。而蜀汉有汉中这个战略要地,可以威胁关中地区。诸葛亮认为,如果能够占领关中地区,就可以进而统一天下。\n

\n

\n 当然,北伐也面临着巨大的困难。蜀汉人口稀少,国力有限,难以支撑长期战争。从益州到关中道路险阻,运输困难。曹魏实力强大,不是轻易能够击败的。\n

\n

\n 9.2 第一次北伐\n

\n

\n 建兴六年(公元228年),诸葛亮发动了第一次北伐。他率军出祁山,攻击曹魏的陇右地区。蜀军进展顺利,天水、南安、安定三郡相继投降。\n

\n

\n 曹魏方面非常震惊,派出张郃率军抵御。诸葛亮派马谡驻守街亭,但马谡违反诸葛亮的部署,被张郃击败。街亭失守后,诸葛亮被迫退兵,第一次北伐失败。\n

\n

\n 第一次北伐虽然失败,但诸葛亮展示了自己的军事才能,也锻炼了蜀军。诸葛亮挥泪斩马谡,表明他执法严明,不徇私情。\n

\n

\n 9.3 后续北伐\n

\n

\n 建兴六年冬天,诸葛亮发动第二次北伐,攻击陈仓。但由于陈仓防守严密,蜀军久攻不下,最终退兵。建兴七年(公元229年),诸葛亮发动第三次北伐,攻占武都、阴平二郡。\n

\n

\n 建兴九年(公元231年),诸葛亮发动第四次北伐,包围祁山。司马懿率军抵御,但采取了守势,不与蜀军决战。诸葛亮因为粮尽退兵,\n

\n

\n 在退兵途中射杀了魏将张郃。\n

\n

\n 建兴十二年(公元234年),诸葛亮发动第五次北伐,这是他最后一次北伐。诸葛亮率军出斜谷,与司马懿相持于五丈原。诸葛亮积劳成疾,于同年八月病逝于五丈原,终年五十四岁。\n

\n

\n 9.4 姜维北伐\n

\n

\n 诸葛亮去世后,姜维继承了北伐的事业。姜维是蜀汉后期的军事统帅,他继承了诸葛亮的遗志,多次率军北伐。\n

\n

\n 从延熙十六年(公元253年)开始,姜维多次发动北伐战争,但胜少败多。\n

\n \n

\n 蜀汉国力有限,无法支撑长期的战争。姜维的北伐虽然没有取得重大胜利,但也牵制了曹魏的兵力。\n

\n

\n 景耀六年(公元263年),曹魏发动灭蜀之战,钟会、邓艾率军进攻蜀汉。姜维在剑阁抵御钟会,但邓艾偷渡阴平,逼近成都。后主刘禅投降,蜀汉灭亡。姜维试图复国,但最终失败被杀。\n

\n

\n 9.5 三国相持时期的战争\n

\n

\n 诸葛亮去世后,三国进入了相持阶段。各方虽然时有战争,但都没有能力消灭对方。这种相持局面持续了四十多年。\n

\n

\n 在东线,曹魏与东吴之间战争不断。曹爽曾率军伐吴,但失败而归。司马懿父子也多次进攻东吴,但都没有取得决定性胜利。\n

\n

\n 在西线,蜀汉的北伐虽然失败,但有效地牵制了曹魏的兵力。曹魏需要部署大量军队防守西部边境,消耗了大量资源。\n

\n

\n 9.6 三国相持时期的政治\n

\n

\n 三国相持时期,各国内部都出现了一些政治问题。蜀汉方面,诸葛亮去世后,蒋琬、费祎先后执政,政治较为稳定。但后主刘禅宠信黄皓,朝廷政治开始腐败。\n

\n

\n 曹魏方面,司马氏逐渐掌控了朝政。司马懿、司马师、司马昭父子三人相继执掌大权,曹魏皇帝成为傀儡。曹魏后期的政治腐败严重,阶级矛盾尖锐。\n

\n

\n 东吴方面,孙权晚年立储问题处理不当,引发了严重的宫廷斗争。\n

\n

\n 孙皓继位后,政治更加腐败,民不聊生。这为东吴的灭亡埋下了伏笔。\n

\n

\n 9.7 三国相持时期的经济\n

\n

\n 三国相持时期,各国都比较注重发展经济。曹魏继续推行屯田制,中原地区的农业得到恢复。司马氏掌权后,实行了一些改革,促进了经济发展。\n

\n

\n 蜀汉在诸葛亮的治理下,经济有所发展。但蜀汉人口较少,国力有限,难以支撑长期的战争消耗。后期连年北伐,加重了民众的负担。\n

\n

\n 东吴的经济发展较快,江南地区的农业和商业都有较大发展。但东吴的赋税较重,民众负担较大,影响了经济的可持续发展。\n

\n \n

\n 第十章 三国归晋与历史终结\n

\n

\n 10.1 晋朝的建立\n

\n

\n 司马懿发动高平陵政变后,司马氏逐渐掌控了曹魏的朝政大权。司马懿、司马师、司马昭父子三人相继执掌朝政,为代魏称帝做好了准备。\n

\n

\n 咸熙二年(公元265年),司马昭去世,其子司马炎继为晋王。同年十二月,司马炎代魏称帝,建立晋朝,是为晋武帝。晋朝定都洛阳,改元泰始。\n

\n

\n 晋武帝称帝后,追尊祖父司马懿为晋宣帝,父亲司马昭为晋文帝,伯父司马师为晋景帝。晋朝的建立,标志着三国时代的结束和中国历史上的西晋时期开始。\n

\n

\n 10.2 晋灭蜀汉\n

\n

\n 晋朝建立后,晋武帝开始准备统一天下。当时蜀汉已经衰落,后主刘禅宠信黄皓,朝廷政治腐败。姜维虽然多次北伐,但无功而返,国力消耗严重。\n

\n

\n 景耀六年(公元263年),晋武帝派钟会、邓艾率军伐蜀。钟会率主力进攻汉中,牵制姜维;邓艾率军偷渡阴平,直取成都。\n

\n

\n 姜维在剑阁抵御钟会,互有胜负。但邓艾偷渡阴平成功后,刘禅惊慌失措,不战而降。蜀汉灭亡,历时四十三年。姜维试图复国,但最终失败被杀。\n

\n

\n 10.3 晋灭东吴\n

\n

\n 蜀汉灭亡后,晋朝开始了灭吴的准备。晋武帝采纳羊祜的建议,积极准备灭吴战争。羊祜镇守荆州,与东吴名将陆抗相持,互有胜负。\n

\n

\n 羊祜去世后,杜预、张华等人继续推动灭吴战争。杜预镇守荆州,积极训练水军,准备渡江作战。天纪四年(公元280年),晋武帝下诏灭吴,派杜预、王浑率军进攻。\n

\n

\n 晋军分兵多路,全面进攻。王浑率军渡过长江,击败东吴主力。杜预率军攻占江陵,张悌率军渡江攻入建业。孙皓见大势已去,投降晋朝。东吴灭亡,历时五十九年。\n

\n \n

\n 10.4 三国归晋的历史意义\n

\n

\n 三国归晋,结束了自黄巾起义以来近一百年的分裂局面,实现了中国的统一。这是中国历史上又一次重要的大统一,对中国社会的发展产生了深远影响。\n

\n

\n 晋朝的统一,为社会经济的恢复和发展创造了条件。战乱结束,人民得以休养生息,中原地区和江南地区都得到了开发。晋朝初期出现了“太康盛世”,经济繁荣,社会安定。\n

\n

\n 然而,晋朝的统一并没有持续太久。晋武帝死后,发生了“八王之乱”,中原地区陷入混乱。随后是“五胡乱华”,西晋灭亡,中国再次陷入分裂。\n

\n

\n 10.5 三国时期的历史遗产\n

\n

\n 三国时期虽然只有短短的四十多年,但留下了丰富的历史遗产。在政治制度方面,三国时期的选官制度、法律制度等对后世产生了影响。曹魏的九品中正制成为魏晋南北朝时期的主要选官制度。\n

\n

\n 在文学艺术方面,三国时期出现了许多杰出的人才。曹操、曹植父子是文学史上重要的人物,“建安风骨”成为中国文学的典范。诸葛亮的智慧成为后世推崇的楷模。\n

\n

\n 在军事方面,三国时期的许多战例成为军事教材。官渡之战、赤壁之战、夷陵之战等都是经典的以少胜多的战例,为后世军事家所借鉴。\n

\n

\n 10.6 三国历史的后世影响\n

\n

\n 三国历史对后世产生了深远影响。《三国演义》是中国的四大名著之一,塑造了许多深入人心的人物形象。诸葛亮、关羽、曹操、周瑜等人物成为家喻户晓的历史人物。\n

\n

\n 三国的故事在民间广为流传,形成了丰富的文化传统。桃园结义、借东风、空城计等故事成为人们茶余饭后的谈资。三国文化已经成为中国传统文化的重要组成部分。\n

\n

\n 在海外,三国文化也有广泛影响。日本、韩国、东南亚等地区都有三国故事的传播。三国文化成为中国软实力的重要组成部分,在世界范围内产生了影响。\n

\n \n

\n 附录一 三国主要人物简介\n

\n

\n 魏国主要人物\n

\n

\n 曹操(155年-220年),字孟德,沛国谯县人。三国时期最杰出的政治家、军事家、文学家。曹操挟天子以令诸侯,统一北方,为曹魏政权的建立奠定了基础。曹操还是杰出的文学家,与儿子曹丕、曹植并称“三曹”。\n

\n

\n 曹丕(187年-226年),字子桓,曹操次子。220年代汉称帝,建立魏国,是为魏文帝。曹丕推行九品中正制,确立了魏晋南北朝时期的选官制度。\n

\n

\n 曹叡(204年-239年),字元仲,曹丕之子。魏明帝,在位期间多次击退蜀汉和东吴的进攻,保卫了魏国的安全。\n

\n

\n 司马懿(179年-251年),字仲达,河内温县人。三国时期最重要的政治家、军事家之一,晋朝的奠基人。司马懿辅佐曹魏四代,抵御诸葛亮北伐,平定辽东,最终掌握朝政大权。\n

\n

\n 张辽(169年-222年),字文远,雁门马邑人。曹操麾下著名将领,合肥之战中大败孙权,威震江东。\n

\n

\n 郭嘉(170年-207年),字奉孝,颍川阳翟人。曹操最重要的谋士之一,英年早逝,曹操曾感叹“郭奉孝在,不使孤至此”。\n

\n

\n 蜀汉主要人物\n

\n

\n 刘备(161年-223年),字玄德,涿郡涿县人。汉景帝之后,\n

\n

\n 三国时期蜀汉政权的建立者。刘备以仁德著称,三顾茅庐请诸葛亮,形成三国鼎立的局面。\n

\n

\n 诸葛亮(181年-234年),字孔明,琅琊阳都人。三国时期最杰出的政治家、军事家。诸葛亮辅佐刘备建立蜀汉,后又辅佐后主刘禅,实行依法治蜀,多次北伐。诸葛亮被誉为“千古第一相”。\n

\n

\n 关羽(162年或160年-220年),字云长,河东解人。刘备的结义兄弟,蜀汉著名将领。关羽忠义无双,被后人尊为“武圣”。\n

\n

\n 张飞(165年-221年),字益德,涿郡涿县人。刘备的结义兄弟,蜀汉著名将领。张飞勇猛异常,但性格暴躁。\n

\n

\n 赵云(?-229年),字子龙,常山真定人。刘备的护卫,蜀汉著名将领。\n

\n \n

\n 赵云英勇善战,多次救主于危难之中。\n

\n

\n 姜维(202年-264年),字伯约,天水冀县人。诸葛亮之后的蜀汉军事统帅,继承诸葛亮北伐的事业,但最终失败被杀。\n

\n

\n 东吴主要人物\n

\n

\n 孙权(182年-252年),字仲谋,吴郡富春人。三国时期东吴政权的建立者,在位五十三年,是三国时期在位时间最长的君主。\n

\n

\n 周瑜(175年-210年),字公瑾,庐江舒县人。东吴开国元勋,赤壁之战中击败曹操,奠定东吴基业。\n

\n

\n 鲁肃(172年-217年),字子敬,临淮东城人。东吴战略家,主张联合刘备共同抗曹,为东吴制定了正确的战略方针。\n

\n

\n 吕蒙(178年-220年),字子明,汝南富陂人。东吴名将,白衣渡江袭取荆州,杀死关羽。\n

\n

\n 陆逊(183年-245年),字伯言,吴郡吴县人。东吴后期支柱,火烧连营击败刘备,多次抵御曹魏进攻。\n

\n

\n 张昭(156年-236年),字子布,彭城人。东吴重臣,孙策的托孤大臣之一,长期辅佐孙权。\n

\n

\n 附录二 三国时期著名战役\n

\n

\n 官渡之战\n

\n

\n 官渡之战发生在建安五年(公元200年),是曹操与袁绍之间决定北方归属的战争。曹操以少胜多,击败袁绍的十万大军,奠定了统一北方的基础。此战是三国时期最经典的以少胜多的战例之一。\n

\n

\n 赤壁之战\n

\n

\n 赤壁之战发生在建安十三年(公元208年),是孙刘联军与曹操大军之间的战争。孙刘联军在周瑜的指挥下,采用火攻,大败曹军,奠定了三国鼎立的格局。此战是中国古代军事史上最著名的战例之一。\n

\n

\n 夷陵之战\n

\n

\n 夷陵之战发生在章武二年(公元222年),是刘备伐吴的战争。\n

\n \n

\n 陆逊采用火攻,大败刘备,蜀汉从此一蹶不振。此战与官渡之战、赤壁之战并称为三国三大战役。\n

\n

\n 街亭之战\n

\n

\n 街亭之战发生在建兴六年(公元228年),是诸葛亮第一次北伐中的关键战役。马谡违反诸葛亮的部署,被张郃击败,导致蜀军被迫退兵。此战成为诸葛亮挥泪斩马谡的典故。\n

\n

\n 合肥之战\n

\n

\n 合肥之战发生在建安二十年(公元215年),是孙权进攻曹操的战争。张辽以八百精兵大败孙权十万大军,差点活捉孙权。此战奠定了张辽“威震江东”的威名。\n

\n

\n 石亭之战\n

\n

\n 石亭之战发生在黄武七年(公元228年),是东吴与曹魏之间的战争。陆逊率军大败曹休,斩获万余人。此战巩固了东吴在江东的统治。\n

\n

\n 附录三 三国疆域与行政区划\n

\n

\n 魏国疆域\n

\n

\n 魏国占据北方中原地区,包括司隶、豫州、兖州、徐州、青州、冀州、幽州、并州、凉州、雍州等地区。魏国定都洛阳,后来迁都许昌,再迁回洛阳。魏国是三国中疆域最大、人口最多的政权。\n

\n

\n 蜀汉疆域\n

\n

\n 蜀汉占据益州和荆州部分地区,包括益州全境以及荆州的武陵、零陵、桂阳、长沙等郡。蜀汉定都成都。蜀汉是三国中疆域最小、人口最少的政权,但地势险要,易守难攻。\n

\n

\n 东吴疆域\n

\n

\n 东吴占据江东六郡以及荆州、扬州、交州等地,包括今江苏、浙江、安徽、江西、福建、广东、广西、海南以及越南北部等地区。东吴定都建业(今江苏南京)。东吴是三国中海岸线最长的政权,航海业发达。\n

\n

\n 三国行政区划比较\n

\n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n \n
\n 政权\n \n 都城\n \n 主要州郡\n \n 人口(估计)\n
\n 曹魏\n \n 洛阳/许昌\n \n 司隶、豫州、兖州等\n \n 约400万\n
\n 蜀汉\n \n 成都\n \n 益州、荆州\n \n 约100万\n
\n 东吴\n \n 建业\n \n 扬州、交州、荆州\n \n 约200万\n
\n

\n 三国时期的地理概念\n

\n

\n 三国时期的“州”与现代的省份概念不同,是一个较大的行政区划。三国时期的州数量比东汉时期有所减少,但每个州的管辖范围仍然很大。\n

\n

\n 三国时期,中原地区是经济最发达的地区,人口密集。江南地区虽然开发较晚,但在东吴的治理下,经济有了较大发展。西南地区地形复杂,交通不便,开发程度较低。\n

\n

\n 结语\n

\n

\n 三国时期是中国历史上一个非常重要的时期,从黄巾起义(184年)到东吴灭亡(280年),历时近百年。这段历史充满了英雄人物、经典战例和传奇故事,对后世产生了深远影响。\n

\n

\n 曹操、刘备、孙权三大枭雄在乱世中崛起各自建立了政权,形成了三国鼎立的局面。他们之间的政治斗争、军事较量、外交斡旋,构成了三国历史的主要内容。\n

\n

\n 诸葛亮、关羽、周瑜、司马懿等历史人物以其卓越的才能和高尚的品格,\n

\n

\n 成为后世楷模。他们的故事被后人传颂不衰,成为中国传统文化的重要组成部分。\n

\n

\n 《三国演义》是中国的四大名著之一,它以历史为素材,融入民间传说和文学创造,塑造了许多深入人心的人物形象。诸葛亮的多智近妖、关羽的忠义无双、曹操的奸雄形象,都对大众认知产生了深远影响。\n

\n

\n 三国历史虽然已经过去了一千多年,但它的影响仍在继续。三国文化已经成为中国文化的的重要组成部分,在文学、艺术、游戏、影视等各个领域都有广泛的应用。三国的故事将继续传承下去,影响着一代又一代的中国人。\n

\n \n

\n 回顾三国历史,我们可以看到:在乱世中,人才是决定性的因素;\n

\n

\n 在竞争中,战略眼光至关重要;在治理中,民心向背决定了政权的兴衰。这些历史经验对于今天仍然具有借鉴意义。\n

\n

\n —— 完 ——\n

\n \n \n' + }, + loading: { + type: Boolean, + default: false + }, + errorMessage: { + type: String, + default: "" + }, + filename: { + type: String, + default: "" + } + }, + emits: ["close", "refresh"], + data() { + return { + currentSlideIndex: 0, + pageTurnDirection: 1, + slides: [], + isParsing: false, + parseErrorMessage: "", + parsedHeadContent: "", + slideHtmlCache: {}, + _vietualData: { + "success": true, + "message": "提取成功", + "html_content": '\n \n \n \n
\n
\n
\n

\n 三國歷史\n

\n

\n 從黃巾起義到三分歸晉\n

\n
\n
\n

\n 東漢末年 • 群雄並起 • 三分天下\n

\n

\n 公元184年 — 280年\n

\n
\n
\n
\n \n
\n

\n 目 录\n

\n
    \n
  • \n 第一章 东汉末年与黄巾起义3\n
  • \n
  • \n 第二章 群雄割据与董卓乱政5\n
  • \n
  • \n 第三章 曹操崛起与官渡之战8\n
  • \n
  • \n 第四章 孙刘联盟与赤壁之战12\n
  • \n
  • \n 第五章 三分天下格局的形成16\n
  • \n
  • \n 第六章 蜀汉政权的建立与发展20\n
  • \n
  • \n 第七章 曹魏政权的巩固与传承24\n
  • \n
  • \n 第八章 东吴政权的繁荣与衰落28\n
  • \n
  • \n 第九章 诸葛亮北伐与三国相持32\n
  • \n
  • \n 第十章 三国归晋与历史终结37\n
  • \n
  • \n 附录 三国主要人物简介42\n
  • \n
  • \n 附录 三国时期著名战役45\n
  • \n
  • \n 附录 三国疆域与行政区划48\n
  • \n
\n
\n ', + "image_base_url": "/api/aippt/三" + }, + touchStartX: 0, + touchStartY: 0, + touchEndX: 0, + touchEndY: 0, + slideAccents: ["#2563eb", "#23b9ad", "#d7b56b", "#4a93b8"], + parseJobId: 0, + parseTimer: null, + parseIdleTimer: null + }; + }, + computed: { + displayFilename() { + const raw = this.filename || "未命名 AIPPT"; + return raw.replace(/\\/g, "/").split("/").pop(); + }, + totalSlides() { + return Math.max(this.slides.length, 1); + }, + currentSlide() { + return this.slides[this.currentSlideIndex] || this.slides[0] || null; + }, + currentSlideHtml() { + if (!this.currentSlide) + return ""; + const key = this.currentSlide.id || "slide-1"; + if (!this.slideHtmlCache[key]) { + this.slideHtmlCache[key] = this.getSlideHtml(this.currentSlide); + } + return this.slideHtmlCache[key]; + }, + currentPageNumber() { + return Math.min(this.currentSlideIndex + 1, this.totalSlides); + }, + currentPageLabel() { + return String(this.currentPageNumber).padStart(2, "0"); + }, + totalPageLabel() { + return String(this.totalSlides).padStart(2, "0"); + }, + currentSlideTitle() { + var _a; + return ((_a = this.currentSlide) == null ? void 0 : _a.title) || this.displayFilename || "AIPPT"; + }, + frameKey() { + var _a; + return `${((_a = this.currentSlide) == null ? void 0 : _a.id) || "empty"}-${this.pageTurnDirection}`; + }, + canPresent() { + return !!this.htmlContent && !this.loading && !this.errorMessage && !this.isParsing && !!this.currentSlideHtml; + }, + canSwipe() { + return this.slides.length > 1; + }, + swipeHintText() { + return "← 左右滑动翻页 →"; + }, + showEmptyState() { + return !this.canPresent; + }, + emptyTitle() { + if (this.errorMessage) + return "AIPPT 加载失败"; + if (this.loading) + return "正在提取 AIPPT"; + if (this.isParsing) + return "正在准备第一页"; + if (this.parseErrorMessage) + return "AIPPT 解析失败"; + return "暂无 AIPPT 内容"; + }, + emptyDescription() { + if (this.errorMessage) + return this.errorMessage; + if (this.loading) + return "正在读取演示文件..."; + if (this.isParsing) + return "正在解析 PPT 内容..."; + if (this.parseErrorMessage) + return this.parseErrorMessage; + return "打开 .aippt 文件后会在这里显示预览。"; + }, + emptyIconText() { + if (this.errorMessage || this.parseErrorMessage) + return "⚠"; + if (this.loading || this.isParsing) + return "⏳"; + return "📄"; + }, + slideStatusText() { + var _a; + if (this.errorMessage) + return "加载失败"; + if (this.loading) + return "正在提取..."; + if (this.isParsing) + return "正在准备..."; + if (!this.htmlContent) + return "等待打开文件"; + return ((_a = this.currentSlide) == null ? void 0 : _a.fullDocument) ? "单页预览" : `已识别 ${this.slides.length} 页`; + } + }, + watch: { + visible(newVal) { + if (newVal) { + this.$nextTick(() => { + this.loadVirtualData(); + }); + } + }, + htmlContent: { + immediate: true, + handler(newVal) { + if (newVal) { + this.scheduleParseSlides(newVal); + } + } + }, + slides() { + if (this.currentSlideIndex > this.slides.length - 1) { + this.currentSlideIndex = 0; + } + } + }, + mounted() { + this.loadVirtualData(); + }, + beforeDestroy() { + this.clearSchedules(); + }, + methods: { + loadVirtualData() { + if (!this.htmlContent && this._vietualData.html_content) { + this.scheduleParseSlides(this._vietualData.html_content); + } + }, + handleTouchStart(e2) { + const touch = e2.touches[0]; + this.touchStartX = touch.clientX; + this.touchStartY = touch.clientY; + }, + handleTouchEnd(e2) { + const touch = e2.changedTouches[0]; + this.touchEndX = touch.clientX; + this.touchEndY = touch.clientY; + this.handleSwipe(); + }, + handleSwipe() { + const deltaX = this.touchEndX - this.touchStartX; + const deltaY = this.touchEndY - this.touchStartY; + if (Math.abs(deltaX) < Math.abs(deltaY)) + return; + if (Math.abs(deltaX) < 50) + return; + if (deltaX > 0) { + this.prevSlide(); + } else { + this.nextSlide(); + } + }, + getHeadContent(html = "") { + const match = String(html).match(/]*>([\s\S]*?)<\/head>/i); + return match ? match[1] : ""; + }, + getBodyContent(html = "") { + const match = String(html).match(/]*>([\s\S]*?)<\/body>/i); + return match ? match[1] : html; + }, + getNodeTitle(html, fallbackIndex) { + const hMatch = html.match(/]*>([\s\S]*?)<\/h[1-6]>/i); + if (hMatch) { + const text = hMatch[1].replace(/<[^>]+>/g, "").replace(/\s+/g, " ").trim(); + return text.slice(0, 36); + } + return `第 ${fallbackIndex + 1} 页`; + }, + isMeaningfulSlide(html) { + const text = html.replace(/<[^>]+>/g, "").replace(/\s+/g, ""); + return text.length > 8 || /]*\/?>/gi; + const pageTags = bodyHtml.match(pageRegex) || []; + if (pageTags.length === 0) { + const sections = bodyHtml.split(/(?= s.trim()); + if (sections.length > 1) { + return sections.map((section, index) => ({ + id: `slide-${index + 1}`, + title: this.getNodeTitle(section, index), + html: `
${section}
`, + headContent, + fullDocument: false + })); + } + return [{ + id: "slide-1", + title: this.displayFilename, + html: `
${bodyHtml}
`, + headContent, + fullDocument: true + }]; + } + const parts = bodyHtml.split(pageRegex).filter((s) => s.trim()); + const slides = parts.map((part, index) => ({ + id: `slide-${index + 1}`, + title: this.getNodeTitle(part, index), + html: `
${part.trim()}
`, + headContent, + fullDocument: false + })); + const meaningfulSlides = slides.filter((s) => this.isMeaningfulSlide(s.html)); + return meaningfulSlides.length > 0 ? meaningfulSlides : slides; + } catch (error) { + formatAppLog("warn", "at pages/text/IntuitiveAipptViewer.vue:346", "[AIPPT] slide parse failed:", error); + this.parseErrorMessage = error.message || "AIPPT 解析失败"; + return [{ + id: "slide-1", + title: this.displayFilename, + html: `
${rawHtml}
`, + headContent: this.getHeadContent(rawHtml), + fullDocument: true + }]; + } + }, + getSlideHtml(slide) { + if (!slide) + return ""; + const headStyle = slide.headContent ? `` : ""; + return ` +
+ ${headStyle} + ${slide.html || ""} +
+ `; + }, + clearSchedules() { + if (this.parseTimer) { + clearTimeout(this.parseTimer); + this.parseTimer = null; + } + if (this.parseIdleTimer) { + clearTimeout(this.parseIdleTimer); + this.parseIdleTimer = null; + } + }, + scheduleParseSlides(html = "") { + const rawHtml = String(html || ""); + const jobId = ++this.parseJobId; + this.clearSchedules(); + this.slideHtmlCache = {}; + this.currentSlideIndex = 0; + this.parseErrorMessage = ""; + if (!rawHtml.trim()) { + this.isParsing = false; + this.parsedHeadContent = ""; + this.slides = []; + return; + } + this.isParsing = true; + this.parsedHeadContent = this.getHeadContent(rawHtml); + this.slides = []; + this.parseTimer = setTimeout(() => { + var _a; + if (jobId !== this.parseJobId) + return; + try { + const nextSlides = this.parseSlides(rawHtml); + if (jobId !== this.parseJobId) + return; + this.slides = nextSlides; + this.parsedHeadContent = ((_a = nextSlides[0]) == null ? void 0 : _a.headContent) || this.parsedHeadContent; + } catch (e2) { + formatAppLog("error", "at pages/text/IntuitiveAipptViewer.vue:409", "[AIPPT] parse error:", e2); + } finally { + if (jobId === this.parseJobId) { + this.isParsing = false; + } + } + }, 100); + }, + pulseStage() { + this.pageTurnDirection = this.pageTurnDirection >= 0 ? 1 : -1; + }, + goToSlide(index) { + if (!this.canPresent) + return; + const target = Math.max(0, Math.min(Number(index) || 0, this.slides.length - 1)); + if (target === this.currentSlideIndex) + return; + this.pageTurnDirection = target >= this.currentSlideIndex ? 1 : -1; + this.currentSlideIndex = target; + this.pulseStage(); + }, + prevSlide() { + this.goToSlide(this.currentSlideIndex - 1); + }, + nextSlide() { + this.goToSlide(this.currentSlideIndex + 1); + }, + onRefresh() { + this.$emit("refresh"); + }, + onClose() { + this.$emit("close"); + }, + // 暴露给父组件的方法 + exposedNextSlide() { + this.nextSlide(); + }, + exposedPrevSlide() { + this.prevSlide(); + } + } + }; + function _sfc_render(_ctx, _cache, $props, $setup, $data, $options) { + return vue.withDirectives((vue.openBlock(), vue.createElementBlock( + "view", + { + class: "ia-root", + onTouchstart: _cache[4] || (_cache[4] = (...args) => $options.handleTouchStart && $options.handleTouchStart(...args)), + onTouchend: _cache[5] || (_cache[5] = (...args) => $options.handleTouchEnd && $options.handleTouchEnd(...args)) + }, + [ + vue.createElementVNode("view", { class: "ia-toolbar" }, [ + vue.createElementVNode("view", { class: "ia-title" }, [ + vue.createElementVNode("text", { class: "ia-badge" }, "PPT"), + vue.createElementVNode("view", { class: "ia-title-copy" }, [ + vue.createElementVNode( + "text", + { class: "ia-title-strong" }, + vue.toDisplayString($options.displayFilename), + 1 + /* TEXT */ + ), + vue.createElementVNode( + "text", + { class: "ia-title-em" }, + vue.toDisplayString($options.slideStatusText), + 1 + /* TEXT */ + ) + ]) + ]), + vue.createElementVNode("view", { class: "ia-actions" }, [ + vue.createElementVNode("view", { + class: "ia-icon-btn", + onClick: _cache[0] || (_cache[0] = (...args) => $options.onRefresh && $options.onRefresh(...args)) + }, [ + vue.createElementVNode("text", { class: "ia-icon" }, "↻") + ]), + vue.createElementVNode("view", { + class: "ia-icon-btn ia-close-btn", + onClick: _cache[1] || (_cache[1] = (...args) => $options.onClose && $options.onClose(...args)) + }, [ + vue.createElementVNode("text", { class: "ia-icon" }, "✕") + ]) + ]) + ]), + vue.createElementVNode("view", { class: "ia-canvas" }, [ + $options.showEmptyState ? (vue.openBlock(), vue.createElementBlock( + "view", + { + key: 0, + class: vue.normalizeClass(["ia-empty", { "is-loading": $props.loading || $data.isParsing, "is-error": !!$props.errorMessage }]) + }, + [ + vue.createElementVNode( + "text", + { class: "ia-empty-icon" }, + vue.toDisplayString($options.emptyIconText), + 1 + /* TEXT */ + ), + vue.createElementVNode( + "text", + { class: "ia-empty-title" }, + vue.toDisplayString($options.emptyTitle), + 1 + /* TEXT */ + ), + vue.createElementVNode( + "text", + { class: "ia-empty-desc" }, + vue.toDisplayString($options.emptyDescription), + 1 + /* TEXT */ + ) + ], + 2 + /* CLASS */ + )) : (vue.openBlock(), vue.createElementBlock("view", { + key: 1, + class: "ia-slide-area" + }, [ + (vue.openBlock(), vue.createElementBlock("view", { + class: "ia-frame-shell", + key: $options.frameKey + }, [ + vue.createElementVNode("scroll-view", { + class: "ia-frame-scroll", + "scroll-y": "", + "show-scrollbar": false + }, [ + vue.createElementVNode("view", { + class: "ia-frame", + innerHTML: $options.currentSlideHtml + }, null, 8, ["innerHTML"]) + ]) + ])), + $options.canPresent && $data.slides.length > 1 ? (vue.openBlock(), vue.createElementBlock("scroll-view", { + key: 0, + class: "ia-thumb-strip", + "scroll-x": "", + "show-scrollbar": false, + "scroll-into-view": "thumb-" + $data.currentSlideIndex + }, [ + vue.createElementVNode("view", { class: "ia-thumb-list" }, [ + (vue.openBlock(true), vue.createElementBlock( + vue.Fragment, + null, + vue.renderList($data.slides, (item, index) => { + return vue.openBlock(), vue.createElementBlock("view", { + key: item.id, + id: "thumb-" + index, + class: vue.normalizeClass(["ia-thumb-item", { active: index === $data.currentSlideIndex }]), + onClick: ($event) => $options.goToSlide(index) + }, [ + vue.createElementVNode( + "text", + { class: "ia-thumb-num" }, + vue.toDisplayString(String(index + 1).padStart(2, "0")), + 1 + /* TEXT */ + ), + vue.createElementVNode( + "text", + { class: "ia-thumb-title" }, + vue.toDisplayString(item.title), + 1 + /* TEXT */ + ) + ], 10, ["id", "onClick"]); + }), + 128 + /* KEYED_FRAGMENT */ + )) + ]) + ], 8, ["scroll-into-view"])) : vue.createCommentVNode("v-if", true) + ])) + ]), + $options.canPresent ? (vue.openBlock(), vue.createElementBlock("view", { + key: 0, + class: "ia-pager" + }, [ + vue.createElementVNode("view", { class: "ia-pager-shell" }, [ + vue.createElementVNode( + "view", + { + class: vue.normalizeClass(["ia-page-btn", { disabled: $data.currentSlideIndex <= 0 }]), + onClick: _cache[2] || (_cache[2] = (...args) => $options.prevSlide && $options.prevSlide(...args)) + }, + [ + vue.createElementVNode("text", { class: "ia-nav-icon" }, "❮") + ], + 2 + /* CLASS */ + ), + vue.createElementVNode("view", { class: "ia-page-count" }, [ + vue.createElementVNode( + "text", + { class: "ia-current-page" }, + vue.toDisplayString($options.currentPageLabel), + 1 + /* TEXT */ + ), + vue.createElementVNode("text", { class: "ia-page-divider" }, "/"), + vue.createElementVNode( + "text", + { class: "ia-total-page" }, + vue.toDisplayString($options.totalPageLabel), + 1 + /* TEXT */ + ) + ]), + vue.createElementVNode( + "view", + { + class: vue.normalizeClass(["ia-page-btn ia-next-btn", { disabled: $data.currentSlideIndex >= $options.totalSlides - 1 }]), + onClick: _cache[3] || (_cache[3] = (...args) => $options.nextSlide && $options.nextSlide(...args)) + }, + [ + vue.createElementVNode("text", { class: "ia-nav-icon" }, "❯") + ], + 2 + /* CLASS */ + ) + ]) + ])) : vue.createCommentVNode("v-if", true) + ], + 544 + /* NEED_HYDRATION, NEED_PATCH */ + )), [ + [vue.vShow, $props.visible] + ]); + } + const PagesTextIntuitiveAipptViewer = /* @__PURE__ */ _export_sfc(_sfc_main$1, [["render", _sfc_render], ["__scopeId", "data-v-2050f472"], ["__file", "D:/Projects/uniapp/app-test/test1/pages/text/IntuitiveAipptViewer.vue"]]); __definePage("pages/Login/Login", PagesLoginLogin); __definePage("pages/Chat/Chat", PagesChatChat); __definePage("pages/WorkSpace/WorkSpace", PagesWorkSpaceWorkSpace); __definePage("pages/UserProfileModal/UserProfileModal", PagesUserProfileModalUserProfileModal); __definePage("pages/ContactPages/ContactPages", PagesContactPagesContactPages); __definePage("pages/WorkSpace/TemplateSpace/TemplateSpace", PagesWorkSpaceTemplateSpaceTemplateSpace); - __definePage("pages/text/text", PagesTextText); - __definePage("pages/text/text2", PagesTextText2); __definePage("pages/CloudDatabase/CloudDatabase", PagesCloudDatabaseCloudDatabase); __definePage("pages/CloudDbDetail/CloudDbDetail", PagesCloudDbDetailCloudDbDetail); + __definePage("pages/text/text", PagesTextText); + __definePage("pages/text/IntuitiveAipptViewer", PagesTextIntuitiveAipptViewer); const _sfc_main = { onLaunch: function() { formatAppLog("log", "at App.vue:4", "App Launch"); diff --git a/unpackage/dist/dev/app-plus/manifest.json b/unpackage/dist/dev/app-plus/manifest.json index bb75db3..e85fc86 100644 --- a/unpackage/dist/dev/app-plus/manifest.json +++ b/unpackage/dist/dev/app-plus/manifest.json @@ -124,7 +124,7 @@ "style": "dark", "background": "#F8F8F8" }, - "arguments": "{\"name\":\"\",\"path\":\"\",\"query\":\"\"}", + "arguments": "{\"path\":\"pages/text/IntuitiveAipptViewer\"}", "uniStatistics": { "enable": false }, diff --git a/unpackage/dist/dev/app-plus/pages/text/BookViewer.css b/unpackage/dist/dev/app-plus/pages/text/BookViewer.css new file mode 100644 index 0000000..3eb7e36 --- /dev/null +++ b/unpackage/dist/dev/app-plus/pages/text/BookViewer.css @@ -0,0 +1,526 @@ + +.book-viewer[data-v-dc6aee90] { + position: fixed; + top: 0; + left: 0; + width: 100vw; + height: 100vh; + background: #1a1a2e; + z-index: 9999; + display: flex; + flex-direction: column; + overflow: hidden; +} + + /* ========== 工具栏 ========== */ +.toolbar[data-v-dc6aee90] { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0 0.625rem; + height: 2.1875rem; + background: rgba(26, 26, 46, 0.95); + border-bottom: 0.0625rem solid rgba(255, 255, 255, 0.08); + flex-shrink: 0; +} +.toolbar-left[data-v-dc6aee90] { + display: flex; + align-items: center; + gap: 0.5rem; + flex: 1; + min-width: 0; +} +.toolbar-brand[data-v-dc6aee90] { + font-size: 0.6875rem; + font-weight: 800; + color: #0ea5e9; + letter-spacing: 0.0625rem; + flex-shrink: 0; +} +.toolbar-title[data-v-dc6aee90] { + font-size: 0.8125rem; + color: #e2e8f0; + font-weight: 500; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.toolbar-right[data-v-dc6aee90] { + display: flex; + align-items: center; + gap: 0.25rem; + flex-shrink: 0; +} +.toolbar-btn[data-v-dc6aee90] { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 0.25rem 0.4375rem; + border-radius: 0.25rem; + background: rgba(255, 255, 255, 0.06); + min-width: 2.125rem; +} +.toolbar-btn[data-v-dc6aee90]:active { + background: rgba(255, 255, 255, 0.15); +} +.toolbar-close[data-v-dc6aee90] { + background: rgba(239, 68, 68, 0.15); + margin-left: 0.25rem; +} +.toolbar-close[data-v-dc6aee90]:active { + background: rgba(239, 68, 68, 0.3); +} +.toolbar-icon[data-v-dc6aee90] { + font-size: 0.875rem; + color: #cbd5e1; +} +.toolbar-label[data-v-dc6aee90] { + font-size: 0.5625rem; + color: #94a3b8; + margin-top: 0.0625rem; +} +.page-display[data-v-dc6aee90] { + display: flex; + align-items: center; + gap: 0.1875rem; + padding: 0 0.3125rem; + font-size: 0.75rem; + color: #e2e8f0; + font-weight: 600; + min-width: 2.5rem; + justify-content: center; +} + + /* ========== 目录侧边栏 ========== */ +.toc-overlay[data-v-dc6aee90] { + position: fixed; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: rgba(0, 0, 0, 0.5); + z-index: 10001; +} +.toc-sidebar[data-v-dc6aee90] { + position: absolute; + left: 0; + top: 0; + bottom: 0; + width: 13.75rem; + background: #16213e; + display: flex; + flex-direction: column; + box-shadow: 0.25rem 0 1.25rem rgba(0, 0, 0, 0.4); +} +.toc-header[data-v-dc6aee90] { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0.75rem 0.875rem; + border-bottom: 0.0625rem solid rgba(255, 255, 255, 0.08); + flex-shrink: 0; +} +.toc-header-title[data-v-dc6aee90] { + font-size: 0.9375rem; + font-weight: 700; + color: #e2e8f0; +} +.toc-close[data-v-dc6aee90] { + width: 1.625rem; + height: 1.625rem; + display: flex; + align-items: center; + justify-content: center; + border-radius: 50%; + background: rgba(255, 255, 255, 0.08); + font-size: 0.875rem; + color: #94a3b8; +} +.toc-close[data-v-dc6aee90]:active { + background: rgba(255, 255, 255, 0.2); +} +.toc-list[data-v-dc6aee90] { + flex: 1; + overflow: hidden; +} +.toc-item[data-v-dc6aee90] { + display: flex; + align-items: center; + padding: 0.625rem 0.875rem; + border-bottom: 0.03125rem solid rgba(255, 255, 255, 0.04); + gap: 0.4375rem; +} +.toc-item[data-v-dc6aee90]:active { + background: rgba(255, 255, 255, 0.05); +} +.toc-active[data-v-dc6aee90] { + background: rgba(14, 165, 233, 0.12); + border-left: 0.1875rem solid #0ea5e9; +} +.toc-level-1 .toc-text[data-v-dc6aee90] { + font-weight: 700; + font-size: 0.8125rem; + color: #e2e8f0; +} +.toc-level-2 .toc-text[data-v-dc6aee90] { + padding-left: 0.75rem; + font-size: 0.75rem; + color: #cbd5e1; +} +.toc-level-3 .toc-text[data-v-dc6aee90] { + padding-left: 1.5rem; + font-size: 0.6875rem; + color: #94a3b8; +} +.toc-bullet[data-v-dc6aee90] { + font-size: 0.5rem; + color: #0ea5e9; + flex-shrink: 0; +} +.toc-text[data-v-dc6aee90] { + flex: 1; + min-width: 0; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.toc-page[data-v-dc6aee90] { + font-size: 0.625rem; + color: #64748b; + flex-shrink: 0; +} + + /* ========== 书本舞台 ========== */ +.book-stage[data-v-dc6aee90] { + flex: 1; + position: relative; + display: flex; + align-items: center; + justify-content: center; + overflow: hidden; + background: #1a1a2e; +} + + /* ========== 3D 书本容器 ========== */ +.book-flip-container[data-v-dc6aee90] { + width: 92%; + height: 88%; + position: relative; + perspective: 1600px; + display: flex; +} + + /* ========== 展开页(底层) ========== */ +.book-spread[data-v-dc6aee90] { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + display: flex; + z-index: 1; +} +.book-page[data-v-dc6aee90] { + flex: 1; + height: 100%; + position: relative; +} +.book-page-left[data-v-dc6aee90] { + border-radius: 0.375rem 0 0 0.375rem; +} +.book-page-right[data-v-dc6aee90] { + border-radius: 0 0.375rem 0.375rem 0; +} + + /* ========== 翻页层 ========== */ +.book-flipper[data-v-dc6aee90] { + position: absolute; + top: 0; + width: 50%; + height: 100%; + transform-style: preserve-3d; + z-index: 10; +} +.flipper-right[data-v-dc6aee90] { + right: 0; + border-radius: 0 0.375rem 0.375rem 0; +} +.flipper-left[data-v-dc6aee90] { + left: 0; + border-radius: 0.375rem 0 0 0.375rem; +} +.flipper-face[data-v-dc6aee90] { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100%; + backface-visibility: hidden; + -webkit-backface-visibility: hidden; + overflow: hidden; +} +.flipper-front[data-v-dc6aee90] { + z-index: 2; + background: linear-gradient(135deg, #fefefe 0%, #f5f5f4 100%); +} +.flipper-right .flipper-front[data-v-dc6aee90] { + border-radius: 0 0.375rem 0.375rem 0; + box-shadow: -0.125rem 0 0.9375rem rgba(0, 0, 0, 0.3); +} +.flipper-left .flipper-front[data-v-dc6aee90] { + border-radius: 0.375rem 0 0 0.375rem; + box-shadow: 0.125rem 0 0.9375rem rgba(0, 0, 0, 0.3); +} +.flipper-back[data-v-dc6aee90] { + transform: rotateY(180deg); + background: linear-gradient(135deg, #fefefe 0%, #f5f5f4 100%); +} +.flipper-right .flipper-back[data-v-dc6aee90] { + border-radius: 0.375rem 0 0 0.375rem; + box-shadow: 0.125rem 0 0.9375rem rgba(0, 0, 0, 0.3); +} +.flipper-left .flipper-back[data-v-dc6aee90] { + border-radius: 0 0.375rem 0.375rem 0; + box-shadow: -0.125rem 0 0.9375rem rgba(0, 0, 0, 0.3); +} + + /* ========== 书脊线 ========== */ +.book-spine[data-v-dc6aee90] { + position: absolute; + top: 4%; + left: 50%; + bottom: 4%; + width: 0.0625rem; + background: linear-gradient(180deg, + transparent 0%, + rgba(0,0,0,0.15) 15%, + rgba(0,0,0,0.15) 85%, + transparent 100%); + z-index: 5; + pointer-events: none; +} + + /* ========== 页面内容通用 ========== */ +.page-scroll[data-v-dc6aee90] { + height: 100%; + box-sizing: border-box; +} +.page-content[data-v-dc6aee90] { + min-height: 100%; + padding: 1.25rem; + background: linear-gradient(135deg, #fefefe 0%, #f5f5f4 100%); + border-radius: 0.375rem; + box-shadow: 0 0.25rem 1.25rem rgba(0, 0, 0, 0.35); + color: #1e293b; + font-size: 0.875rem; + line-height: 1.8; + word-break: break-word; +} + + /* ========== 底部翻页提示 ========== */ +.page-hint[data-v-dc6aee90] { + display: flex; + align-items: center; + justify-content: space-between; + padding: 0.4375rem 1rem; + flex-shrink: 0; + background: rgba(26, 26, 46, 0.95); + border-top: 0.0625rem solid rgba(255, 255, 255, 0.08); +} +.hint-btn[data-v-dc6aee90] { + padding: 0.3125rem 0.875rem; + border-radius: 0.625rem; + background: rgba(14, 165, 233, 0.15); + color: #0ea5e9; + font-size: 0.75rem; +} +.hint-btn[data-v-dc6aee90]:active { + background: rgba(14, 165, 233, 0.3); +} +.hint-disabled[data-v-dc6aee90] { + background: rgba(255, 255, 255, 0.05); + color: #475569; + pointer-events: none; +} +.hint-center[data-v-dc6aee90] { + font-size: 0.75rem; + color: #94a3b8; + padding: 0.3125rem 0.75rem; + border-radius: 0.625rem; + background: rgba(255, 255, 255, 0.05); +} +.hint-center[data-v-dc6aee90]:active { + background: rgba(255, 255, 255, 0.12); +} + + /* 页面内部元素自适应 */ +.book-stage[data-v-dc6aee90] img { + max-width: 100%; + height: auto; + border-radius: 0.25rem; +} +.book-stage[data-v-dc6aee90] table { + width: 100%; + border-collapse: collapse; + font-size: 0.75rem; +} +.book-stage[data-v-dc6aee90] th, + .book-stage[data-v-dc6aee90] td { + border: 0.03125rem solid #cbd5e1; + padding: 0.375rem 0.5rem; + text-align: left; +} +.book-stage[data-v-dc6aee90] th { + background: #f1f5f9; + font-weight: 600; +} +.book-stage[data-v-dc6aee90] pre { + background: #1e293b; + color: #e2e8f0; + padding: 0.75rem; + border-radius: 0.3125rem; + overflow-x: auto; + font-size: 0.75rem; + line-height: 1.5; +} +.book-stage[data-v-dc6aee90] code { + font-family: 'SF Mono', 'Menlo', monospace; +} +.book-stage[data-v-dc6aee90] blockquote { + border-left: 0.1875rem solid #0ea5e9; + padding: 0.5rem 0.875rem; + margin: 0.625rem 0; + background: #f0f9ff; + border-radius: 0 0.25rem 0.25rem 0; + color: #334155; +} +.book-stage[data-v-dc6aee90] h1 { + font-size: 1.25rem; + font-weight: 800; + color: #0f172a; + margin: 0.5rem 0 0.75rem; + padding-bottom: 0.375rem; + border-bottom: 0.09375rem solid #0ea5e9; +} +.book-stage[data-v-dc6aee90] h2 { + font-size: 1.0625rem; + font-weight: 700; + color: #1e293b; + margin: 0.5rem 0 0.625rem; +} +.book-stage[data-v-dc6aee90] h3 { + font-size: 0.9375rem; + font-weight: 600; + color: #334155; + margin: 0.4375rem 0 0.5rem; +} +.book-stage[data-v-dc6aee90] h4, + .book-stage[data-v-dc6aee90] h5, + .book-stage[data-v-dc6aee90] h6 { + font-size: 0.875rem; + font-weight: 600; + color: #475569; + margin: 0.375rem 0 0.4375rem; +} +.book-stage[data-v-dc6aee90] ul, + .book-stage[data-v-dc6aee90] ol { + padding-left: 1.25rem; +} +.book-stage[data-v-dc6aee90] li { + margin: 0.3125rem 0; +} +.book-stage[data-v-dc6aee90] p { + margin: 0.4375rem 0; +} +.book-stage[data-v-dc6aee90] a { + color: #0ea5e9; + text-decoration: none; +} +.book-stage[data-v-dc6aee90] hr { + border: none; + border-top: 0.0625rem solid #e2e8f0; + margin: 0.875rem 0; +} +.book-stage[data-v-dc6aee90] uni-video { + max-width: 100%; + height: auto; + border-radius: 0.25rem; +} + + /* ========== 加载状态 ========== */ +.book-loading[data-v-dc6aee90] { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 0.9375rem; +} +.loading-spinner[data-v-dc6aee90] { + width: 1.875rem; + height: 1.875rem; + border: 0.125rem solid rgba(14, 165, 233, 0.2); + border-top-color: #0ea5e9; + border-radius: 50%; + animation: spin-dc6aee90 0.8s linear infinite; +} +@keyframes spin-dc6aee90 { +to { + transform: rotate(360deg); +} +} +.loading-text[data-v-dc6aee90] { + font-size: 0.8125rem; + color: #94a3b8; +} + + /* ========== 空状态 ========== */ +.book-empty[data-v-dc6aee90] { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 0.75rem; +} +.empty-icon[data-v-dc6aee90] { + font-size: 2.5rem; +} +.empty-text[data-v-dc6aee90] { + font-size: 0.875rem; + color: #64748b; +} + + /* ========== 页码跳转浮层 ========== */ +.page-jump-bar[data-v-dc6aee90] { + position: absolute; + top: 0.5rem; + right: 0.9375rem; + z-index: 99; +} +.jump-input[data-v-dc6aee90] { + width: 6.25rem; + height: 1.875rem; + background: rgba(255, 255, 255, 0.95); + border: 0.0625rem solid #0ea5e9; + border-radius: 0.3125rem; + padding: 0 0.625rem; + font-size: 0.8125rem; + color: #1e293b; + text-align: center; +} + + /* ========== 错误提示 ========== */ +.error-toast[data-v-dc6aee90] { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background: rgba(239, 68, 68, 0.92); + color: #fff; + padding: 0.625rem 1.25rem; + border-radius: 0.375rem; + font-size: 0.8125rem; + z-index: 10010; + max-width: 80vw; + text-align: center; +} diff --git a/unpackage/dist/dev/app-plus/pages/text/IntuitiveAipptViewer.css b/unpackage/dist/dev/app-plus/pages/text/IntuitiveAipptViewer.css new file mode 100644 index 0000000..eaf4ec1 --- /dev/null +++ b/unpackage/dist/dev/app-plus/pages/text/IntuitiveAipptViewer.css @@ -0,0 +1,303 @@ + +.ia-root[data-v-2050f472] { + display: flex; + flex-direction: column; + height: 100vh; + width: 100%; + background: #f0f0f3; + font-family: -apple-system, BlinkMacSystemFont, "PingFang SC", "HarmonyOS Sans SC", "Microsoft YaHei", + sans-serif; + font-size: 0.875rem; + color: #0f172a; + overflow: hidden; +} + + /* ===== 顶部工具栏 ===== */ +.ia-toolbar[data-v-2050f472] { + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + padding: 0.5rem 0.75rem; + background: #fff; + border-bottom: 0.0625rem solid rgba(15, 23, 42, 0.08); + flex-shrink: 0; +} +.ia-title[data-v-2050f472] { + display: flex; + flex-direction: row; + align-items: center; + flex: 1; + min-width: 0; +} +.ia-badge[data-v-2050f472] { + flex-shrink: 0; + background: #eef4ff; + color: #2563eb; + font-size: 0.6875rem; + font-weight: 900; + padding: 0.1875rem 0.5rem; + border-radius: 1.25rem; + margin-right: 0.5rem; +} +.ia-title-copy[data-v-2050f472] { + flex: 1; + min-width: 0; + display: flex; + flex-direction: column; +} +.ia-title-strong[data-v-2050f472] { + font-size: 0.875rem; + font-weight: 700; + color: #0f172a; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} +.ia-title-em[data-v-2050f472] { + font-size: 0.6875rem; + color: #94a3b8; +} +.ia-actions[data-v-2050f472] { + display: flex; + flex-direction: row; + align-items: center; + flex-shrink: 0; + margin-left: 0.5rem; +} +.ia-icon-btn[data-v-2050f472] { + width: 1.875rem; + height: 1.875rem; + display: flex; + align-items: center; + justify-content: center; + margin-left: 0.375rem; + border-radius: 1.25rem; + background: rgba(37, 99, 235, 0.06); + transition: background 0.15s; +} +.ia-icon-btn[data-v-2050f472]:active { + background: rgba(37, 99, 235, 0.15); +} +.ia-close-btn[data-v-2050f472] { + background: rgba(239, 68, 68, 0.06); +} +.ia-close-btn[data-v-2050f472]:active { + background: rgba(239, 68, 68, 0.15); +} +.ia-icon[data-v-2050f472] { + font-size: 1.125rem; + color: #2563eb; +} +.ia-close-btn .ia-icon[data-v-2050f472] { + color: #ef4444; +} + + /* ===== 主舞台 ===== */ +.ia-canvas[data-v-2050f472] { + flex: 1; + min-height: 0; + display: flex; + flex-direction: column; + overflow: hidden; + background: linear-gradient(180deg, #f8fafc, #e9eef6); +} + + /* ===== 空状态 ===== */ +.ia-empty[data-v-2050f472] { + flex: 1; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 1.25rem; + text-align: center; +} +.ia-empty-icon[data-v-2050f472] { + font-size: 2.5rem; + margin-bottom: 0.75rem; +} +.ia-empty-title[data-v-2050f472] { + font-size: 1.0625rem; + font-weight: 700; + color: #0f172a; + margin-bottom: 0.375rem; +} +.ia-empty-desc[data-v-2050f472] { + font-size: 0.8125rem; + color: #64748b; + max-width: 12.5rem; + line-height: 1.6; +} + + /* ===== 幻灯片区域 ===== */ +.ia-slide-area[data-v-2050f472] { + flex: 1; + display: flex; + flex-direction: column; + min-height: 0; + padding: 0.75rem; +} +.ia-frame-shell[data-v-2050f472] { + flex: 1; + min-height: 0; + border-radius: 0.5rem; + background: #fff; + box-shadow: 0 0.125rem 0.625rem rgba(15, 23, 42, 0.08); + overflow: hidden; + display: flex; + flex-direction: column; + position: relative; +} +.ia-frame-scroll[data-v-2050f472] { + flex: 1; + width: 100%; + height: 100%; +} +.ia-frame[data-v-2050f472] { + width: 100%; + min-height: 100%; +} + + /* 滑动提示 */ +.ia-swipe-hint[data-v-2050f472] { + position: absolute; + bottom: 0.375rem; + left: 50%; + transform: translateX(-50%); + background: rgba(15, 23, 42, 0.55); + padding: 0.25rem 0.75rem; + border-radius: 1.25rem; +} +.ia-swipe-hint-text[data-v-2050f472] { + color: #fff; + font-size: 0.6875rem; +} + + /* ===== 缩略图条 ===== */ +.ia-thumb-strip[data-v-2050f472] { + flex-shrink: 0; + margin-top: 0.5rem; + width: 100%; + white-space: nowrap; +} +.ia-thumb-list[data-v-2050f472] { + display: flex; + flex-direction: row; + padding: 0.125rem 0; +} +.ia-thumb-item[data-v-2050f472] { + flex-shrink: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + min-width: 3.75rem; + height: 2.5rem; + padding: 0.25rem 0.5rem; + margin-right: 0.375rem; + border-radius: 0.375rem; + background: rgba(255, 255, 255, 0.7); + border: 0.0625rem solid rgba(15, 23, 42, 0.06); + transition: all 0.2s; +} +.ia-thumb-item.active[data-v-2050f472] { + background: linear-gradient(135deg, #2563eb, #23b9ad); + border-color: transparent; +} +.ia-thumb-num[data-v-2050f472] { + font-size: 0.6875rem; + font-weight: 900; + color: #1d4ed8; +} +.ia-thumb-item.active .ia-thumb-num[data-v-2050f472] { + color: #fff; +} +.ia-thumb-title[data-v-2050f472] { + font-size: 0.625rem; + color: #64748b; + margin-top: 0.125rem; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; + max-width: 5rem; +} +.ia-thumb-item.active .ia-thumb-title[data-v-2050f472] { + color: rgba(255, 255, 255, 0.85); +} + + /* ===== 底部导航栏 ===== */ +.ia-pager[data-v-2050f472] { + flex-shrink: 0; + padding: 0.5rem 0.75rem; + padding-bottom: calc(0.5rem + env(safe-area-inset-bottom)); + background: rgba(248, 251, 255, 0.92); + border-top: 0.0625rem solid rgba(15, 23, 42, 0.06); +} +.ia-pager-shell[data-v-2050f472] { + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + gap: 0.75rem; +} +.ia-page-btn[data-v-2050f472] { + width: 2.5rem; + height: 2.25rem; + display: flex; + align-items: center; + justify-content: center; + border-radius: 1.25rem; + background: rgba(37, 99, 235, 0.08); + transition: all 0.15s; +} +.ia-page-btn[data-v-2050f472]:active:not(.disabled) { + background: rgba(37, 99, 235, 0.18); + transform: scale(0.95); +} +.ia-page-btn.disabled[data-v-2050f472] { + opacity: 0.35; +} +.ia-next-btn[data-v-2050f472] { + background: linear-gradient(135deg, #2563eb, #23b9ad); +} +.ia-next-btn.disabled[data-v-2050f472] { + background: rgba(37, 99, 235, 0.08); +} +.ia-nav-icon[data-v-2050f472] { + font-size: 1.125rem; + color: #1e40af; + font-weight: bold; +} +.ia-next-btn .ia-nav-icon[data-v-2050f472] { + color: #fff; +} +.ia-next-btn.disabled .ia-nav-icon[data-v-2050f472] { + color: #1e40af; +} +.ia-page-count[data-v-2050f472] { + display: flex; + flex-direction: row; + align-items: center; + justify-content: center; + padding: 0.375rem 0.875rem; + border-radius: 1.25rem; + background: rgba(238, 244, 255, 0.78); + border: 0.0625rem solid rgba(37, 99, 235, 0.08); +} +.ia-current-page[data-v-2050f472] { + font-size: 1.25rem; + font-weight: 900; + color: #1d4ed8; + font-family: "SFMono-Regular", Consolas, monospace; +} +.ia-page-divider[data-v-2050f472] { + font-size: 0.875rem; + color: rgba(51, 65, 85, 0.5); + margin: 0 0.25rem; +} +.ia-total-page[data-v-2050f472] { + font-size: 0.875rem; + color: rgba(51, 65, 85, 0.5); + font-family: "SFMono-Regular", Consolas, monospace; +} diff --git a/unpackage/dist/dev/app-plus/pages/text/text.css b/unpackage/dist/dev/app-plus/pages/text/text.css new file mode 100644 index 0000000..4b009c2 --- /dev/null +++ b/unpackage/dist/dev/app-plus/pages/text/text.css @@ -0,0 +1,1841 @@ +/** + * 这里是uni-app内置的常用样式变量 + * + * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量 + * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App + * + */ +/** + * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能 + * + * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件 + */ +/* 颜色变量 */ +/* 行为相关颜色 */ +/* 文字基本颜色 */ +/* 背景颜色 */ +/* 边框颜色 */ +/* 尺寸变量 */ +/* 文字尺寸 */ +/* 图片尺寸 */ +/* Border Radius */ +/* 水平间距 */ +/* 垂直间距 */ +/* 透明度 */ +/* 文章场景相关 */ +.uniui-cart-filled[data-v-d31e1c47]:before { + content: "\e6d0"; +} +.uniui-gift-filled[data-v-d31e1c47]:before { + content: "\e6c4"; +} +.uniui-color[data-v-d31e1c47]:before { + content: "\e6cf"; +} +.uniui-wallet[data-v-d31e1c47]:before { + content: "\e6b1"; +} +.uniui-settings-filled[data-v-d31e1c47]:before { + content: "\e6ce"; +} +.uniui-auth-filled[data-v-d31e1c47]:before { + content: "\e6cc"; +} +.uniui-shop-filled[data-v-d31e1c47]:before { + content: "\e6cd"; +} +.uniui-staff-filled[data-v-d31e1c47]:before { + content: "\e6cb"; +} +.uniui-vip-filled[data-v-d31e1c47]:before { + content: "\e6c6"; +} +.uniui-plus-filled[data-v-d31e1c47]:before { + content: "\e6c7"; +} +.uniui-folder-add-filled[data-v-d31e1c47]:before { + content: "\e6c8"; +} +.uniui-color-filled[data-v-d31e1c47]:before { + content: "\e6c9"; +} +.uniui-tune-filled[data-v-d31e1c47]:before { + content: "\e6ca"; +} +.uniui-calendar-filled[data-v-d31e1c47]:before { + content: "\e6c0"; +} +.uniui-notification-filled[data-v-d31e1c47]:before { + content: "\e6c1"; +} +.uniui-wallet-filled[data-v-d31e1c47]:before { + content: "\e6c2"; +} +.uniui-medal-filled[data-v-d31e1c47]:before { + content: "\e6c3"; +} +.uniui-fire-filled[data-v-d31e1c47]:before { + content: "\e6c5"; +} +.uniui-refreshempty[data-v-d31e1c47]:before { + content: "\e6bf"; +} +.uniui-location-filled[data-v-d31e1c47]:before { + content: "\e6af"; +} +.uniui-person-filled[data-v-d31e1c47]:before { + content: "\e69d"; +} +.uniui-personadd-filled[data-v-d31e1c47]:before { + content: "\e698"; +} +.uniui-arrowthinleft[data-v-d31e1c47]:before { + content: "\e6d2"; +} +.uniui-arrowthinup[data-v-d31e1c47]:before { + content: "\e6d3"; +} +.uniui-arrowthindown[data-v-d31e1c47]:before { + content: "\e6d4"; +} +.uniui-back[data-v-d31e1c47]:before { + content: "\e6b9"; +} +.uniui-forward[data-v-d31e1c47]:before { + content: "\e6ba"; +} +.uniui-arrow-right[data-v-d31e1c47]:before { + content: "\e6bb"; +} +.uniui-arrow-left[data-v-d31e1c47]:before { + content: "\e6bc"; +} +.uniui-arrow-up[data-v-d31e1c47]:before { + content: "\e6bd"; +} +.uniui-arrow-down[data-v-d31e1c47]:before { + content: "\e6be"; +} +.uniui-arrowthinright[data-v-d31e1c47]:before { + content: "\e6d1"; +} +.uniui-down[data-v-d31e1c47]:before { + content: "\e6b8"; +} +.uniui-bottom[data-v-d31e1c47]:before { + content: "\e6b8"; +} +.uniui-arrowright[data-v-d31e1c47]:before { + content: "\e6d5"; +} +.uniui-right[data-v-d31e1c47]:before { + content: "\e6b5"; +} +.uniui-up[data-v-d31e1c47]:before { + content: "\e6b6"; +} +.uniui-top[data-v-d31e1c47]:before { + content: "\e6b6"; +} +.uniui-left[data-v-d31e1c47]:before { + content: "\e6b7"; +} +.uniui-arrowup[data-v-d31e1c47]:before { + content: "\e6d6"; +} +.uniui-eye[data-v-d31e1c47]:before { + content: "\e651"; +} +.uniui-eye-filled[data-v-d31e1c47]:before { + content: "\e66a"; +} +.uniui-eye-slash[data-v-d31e1c47]:before { + content: "\e6b3"; +} +.uniui-eye-slash-filled[data-v-d31e1c47]:before { + content: "\e6b4"; +} +.uniui-info-filled[data-v-d31e1c47]:before { + content: "\e649"; +} +.uniui-reload[data-v-d31e1c47]:before { + content: "\e6b2"; +} +.uniui-micoff-filled[data-v-d31e1c47]:before { + content: "\e6b0"; +} +.uniui-map-pin-ellipse[data-v-d31e1c47]:before { + content: "\e6ac"; +} +.uniui-map-pin[data-v-d31e1c47]:before { + content: "\e6ad"; +} +.uniui-location[data-v-d31e1c47]:before { + content: "\e6ae"; +} +.uniui-starhalf[data-v-d31e1c47]:before { + content: "\e683"; +} +.uniui-star[data-v-d31e1c47]:before { + content: "\e688"; +} +.uniui-star-filled[data-v-d31e1c47]:before { + content: "\e68f"; +} +.uniui-calendar[data-v-d31e1c47]:before { + content: "\e6a0"; +} +.uniui-fire[data-v-d31e1c47]:before { + content: "\e6a1"; +} +.uniui-medal[data-v-d31e1c47]:before { + content: "\e6a2"; +} +.uniui-font[data-v-d31e1c47]:before { + content: "\e6a3"; +} +.uniui-gift[data-v-d31e1c47]:before { + content: "\e6a4"; +} +.uniui-link[data-v-d31e1c47]:before { + content: "\e6a5"; +} +.uniui-notification[data-v-d31e1c47]:before { + content: "\e6a6"; +} +.uniui-staff[data-v-d31e1c47]:before { + content: "\e6a7"; +} +.uniui-vip[data-v-d31e1c47]:before { + content: "\e6a8"; +} +.uniui-folder-add[data-v-d31e1c47]:before { + content: "\e6a9"; +} +.uniui-tune[data-v-d31e1c47]:before { + content: "\e6aa"; +} +.uniui-auth[data-v-d31e1c47]:before { + content: "\e6ab"; +} +.uniui-person[data-v-d31e1c47]:before { + content: "\e699"; +} +.uniui-email-filled[data-v-d31e1c47]:before { + content: "\e69a"; +} +.uniui-phone-filled[data-v-d31e1c47]:before { + content: "\e69b"; +} +.uniui-phone[data-v-d31e1c47]:before { + content: "\e69c"; +} +.uniui-email[data-v-d31e1c47]:before { + content: "\e69e"; +} +.uniui-personadd[data-v-d31e1c47]:before { + content: "\e69f"; +} +.uniui-chatboxes-filled[data-v-d31e1c47]:before { + content: "\e692"; +} +.uniui-contact[data-v-d31e1c47]:before { + content: "\e693"; +} +.uniui-chatbubble-filled[data-v-d31e1c47]:before { + content: "\e694"; +} +.uniui-contact-filled[data-v-d31e1c47]:before { + content: "\e695"; +} +.uniui-chatboxes[data-v-d31e1c47]:before { + content: "\e696"; +} +.uniui-chatbubble[data-v-d31e1c47]:before { + content: "\e697"; +} +.uniui-upload-filled[data-v-d31e1c47]:before { + content: "\e68e"; +} +.uniui-upload[data-v-d31e1c47]:before { + content: "\e690"; +} +.uniui-weixin[data-v-d31e1c47]:before { + content: "\e691"; +} +.uniui-compose[data-v-d31e1c47]:before { + content: "\e67f"; +} +.uniui-qq[data-v-d31e1c47]:before { + content: "\e680"; +} +.uniui-download-filled[data-v-d31e1c47]:before { + content: "\e681"; +} +.uniui-pyq[data-v-d31e1c47]:before { + content: "\e682"; +} +.uniui-sound[data-v-d31e1c47]:before { + content: "\e684"; +} +.uniui-trash-filled[data-v-d31e1c47]:before { + content: "\e685"; +} +.uniui-sound-filled[data-v-d31e1c47]:before { + content: "\e686"; +} +.uniui-trash[data-v-d31e1c47]:before { + content: "\e687"; +} +.uniui-videocam-filled[data-v-d31e1c47]:before { + content: "\e689"; +} +.uniui-spinner-cycle[data-v-d31e1c47]:before { + content: "\e68a"; +} +.uniui-weibo[data-v-d31e1c47]:before { + content: "\e68b"; +} +.uniui-videocam[data-v-d31e1c47]:before { + content: "\e68c"; +} +.uniui-download[data-v-d31e1c47]:before { + content: "\e68d"; +} +.uniui-help[data-v-d31e1c47]:before { + content: "\e679"; +} +.uniui-navigate-filled[data-v-d31e1c47]:before { + content: "\e67a"; +} +.uniui-plusempty[data-v-d31e1c47]:before { + content: "\e67b"; +} +.uniui-smallcircle[data-v-d31e1c47]:before { + content: "\e67c"; +} +.uniui-minus-filled[data-v-d31e1c47]:before { + content: "\e67d"; +} +.uniui-micoff[data-v-d31e1c47]:before { + content: "\e67e"; +} +.uniui-closeempty[data-v-d31e1c47]:before { + content: "\e66c"; +} +.uniui-clear[data-v-d31e1c47]:before { + content: "\e66d"; +} +.uniui-navigate[data-v-d31e1c47]:before { + content: "\e66e"; +} +.uniui-minus[data-v-d31e1c47]:before { + content: "\e66f"; +} +.uniui-image[data-v-d31e1c47]:before { + content: "\e670"; +} +.uniui-mic[data-v-d31e1c47]:before { + content: "\e671"; +} +.uniui-paperplane[data-v-d31e1c47]:before { + content: "\e672"; +} +.uniui-close[data-v-d31e1c47]:before { + content: "\e673"; +} +.uniui-help-filled[data-v-d31e1c47]:before { + content: "\e674"; +} +.uniui-paperplane-filled[data-v-d31e1c47]:before { + content: "\e675"; +} +.uniui-plus[data-v-d31e1c47]:before { + content: "\e676"; +} +.uniui-mic-filled[data-v-d31e1c47]:before { + content: "\e677"; +} +.uniui-image-filled[data-v-d31e1c47]:before { + content: "\e678"; +} +.uniui-locked-filled[data-v-d31e1c47]:before { + content: "\e668"; +} +.uniui-info[data-v-d31e1c47]:before { + content: "\e669"; +} +.uniui-locked[data-v-d31e1c47]:before { + content: "\e66b"; +} +.uniui-camera-filled[data-v-d31e1c47]:before { + content: "\e658"; +} +.uniui-chat-filled[data-v-d31e1c47]:before { + content: "\e659"; +} +.uniui-camera[data-v-d31e1c47]:before { + content: "\e65a"; +} +.uniui-circle[data-v-d31e1c47]:before { + content: "\e65b"; +} +.uniui-checkmarkempty[data-v-d31e1c47]:before { + content: "\e65c"; +} +.uniui-chat[data-v-d31e1c47]:before { + content: "\e65d"; +} +.uniui-circle-filled[data-v-d31e1c47]:before { + content: "\e65e"; +} +.uniui-flag[data-v-d31e1c47]:before { + content: "\e65f"; +} +.uniui-flag-filled[data-v-d31e1c47]:before { + content: "\e660"; +} +.uniui-gear-filled[data-v-d31e1c47]:before { + content: "\e661"; +} +.uniui-home[data-v-d31e1c47]:before { + content: "\e662"; +} +.uniui-home-filled[data-v-d31e1c47]:before { + content: "\e663"; +} +.uniui-gear[data-v-d31e1c47]:before { + content: "\e664"; +} +.uniui-smallcircle-filled[data-v-d31e1c47]:before { + content: "\e665"; +} +.uniui-map-filled[data-v-d31e1c47]:before { + content: "\e666"; +} +.uniui-map[data-v-d31e1c47]:before { + content: "\e667"; +} +.uniui-refresh-filled[data-v-d31e1c47]:before { + content: "\e656"; +} +.uniui-refresh[data-v-d31e1c47]:before { + content: "\e657"; +} +.uniui-cloud-upload[data-v-d31e1c47]:before { + content: "\e645"; +} +.uniui-cloud-download-filled[data-v-d31e1c47]:before { + content: "\e646"; +} +.uniui-cloud-download[data-v-d31e1c47]:before { + content: "\e647"; +} +.uniui-cloud-upload-filled[data-v-d31e1c47]:before { + content: "\e648"; +} +.uniui-redo[data-v-d31e1c47]:before { + content: "\e64a"; +} +.uniui-images-filled[data-v-d31e1c47]:before { + content: "\e64b"; +} +.uniui-undo-filled[data-v-d31e1c47]:before { + content: "\e64c"; +} +.uniui-more[data-v-d31e1c47]:before { + content: "\e64d"; +} +.uniui-more-filled[data-v-d31e1c47]:before { + content: "\e64e"; +} +.uniui-undo[data-v-d31e1c47]:before { + content: "\e64f"; +} +.uniui-images[data-v-d31e1c47]:before { + content: "\e650"; +} +.uniui-paperclip[data-v-d31e1c47]:before { + content: "\e652"; +} +.uniui-settings[data-v-d31e1c47]:before { + content: "\e653"; +} +.uniui-search[data-v-d31e1c47]:before { + content: "\e654"; +} +.uniui-redo-filled[data-v-d31e1c47]:before { + content: "\e655"; +} +.uniui-list[data-v-d31e1c47]:before { + content: "\e644"; +} +.uniui-mail-open-filled[data-v-d31e1c47]:before { + content: "\e63a"; +} +.uniui-hand-down-filled[data-v-d31e1c47]:before { + content: "\e63c"; +} +.uniui-hand-down[data-v-d31e1c47]:before { + content: "\e63d"; +} +.uniui-hand-up-filled[data-v-d31e1c47]:before { + content: "\e63e"; +} +.uniui-hand-up[data-v-d31e1c47]:before { + content: "\e63f"; +} +.uniui-heart-filled[data-v-d31e1c47]:before { + content: "\e641"; +} +.uniui-mail-open[data-v-d31e1c47]:before { + content: "\e643"; +} +.uniui-heart[data-v-d31e1c47]:before { + content: "\e639"; +} +.uniui-loop[data-v-d31e1c47]:before { + content: "\e633"; +} +.uniui-pulldown[data-v-d31e1c47]:before { + content: "\e632"; +} +.uniui-scan[data-v-d31e1c47]:before { + content: "\e62a"; +} +.uniui-bars[data-v-d31e1c47]:before { + content: "\e627"; +} +.uniui-checkbox[data-v-d31e1c47]:before { + content: "\e62b"; +} +.uniui-checkbox-filled[data-v-d31e1c47]:before { + content: "\e62c"; +} +.uniui-shop[data-v-d31e1c47]:before { + content: "\e62f"; +} +.uniui-headphones[data-v-d31e1c47]:before { + content: "\e630"; +} +.uniui-cart[data-v-d31e1c47]:before { + content: "\e631"; +} +@font-face { + font-family: uniicons; + src: url("../../assets/uniicons.32e978a5.ttf"); +} +.uni-icons[data-v-d31e1c47] { + font-family: uniicons; + text-decoration: none; + text-align: center; +} +/** + * 这里是uni-app内置的常用样式变量 + * + * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量 + * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App + * + */ +/** + * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能 + * + * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件 + */ +/* 颜色变量 */ +/* 行为相关颜色 */ +/* 文字基本颜色 */ +/* 背景颜色 */ +/* 边框颜色 */ +/* 尺寸变量 */ +/* 文字尺寸 */ +/* 图片尺寸 */ +/* Border Radius */ +/* 水平间距 */ +/* 垂直间距 */ +/* 透明度 */ +/* 文章场景相关 */ +.chat-sidebar-container[data-v-0c6c7315] { + display: flex; + flex-direction: column; + align-items: center; + width: 100%; + height: 100%; + background: linear-gradient(180deg, #faf9ff 0%, #f3f2f8 50%, #eeeef5 100%); + color: black; + box-sizing: border-box; +} +.sidebar-header[data-v-0c6c7315] { + padding: 1.25rem 0.9375rem 0.9375rem; + width: 100%; + box-sizing: border-box; + display: flex; + flex-shrink: 0; + justify-content: center; + flex-direction: column; + border-bottom: 0.03125rem solid rgba(170, 170, 255, 0.18); +} +.user-profile-card[data-v-0c6c7315] { + padding: 0.5rem 0.75rem; + width: 100%; + display: flex; + align-items: center; + background: linear-gradient(135deg, #ffffff, #faf9ff); + box-sizing: border-box; + border-radius: 0.875rem; + box-shadow: 0 0.125rem 0.625rem rgba(100, 100, 150, 0.08); + border: 0.03125rem solid rgba(170, 170, 255, 0.12); + transition: all 0.2s ease; +} +.user-profile-card[data-v-0c6c7315]:active { + transform: scale(0.98); + box-shadow: 0 0.0625rem 0.3125rem rgba(100, 100, 150, 0.12); +} +.user-profile-card .left[data-v-0c6c7315] { + flex-shrink: 0; + display: flex; + justify-content: center; + align-items: center; + margin-right: 0.5rem; +} +.user-profile-card .left .avatar-preview[data-v-0c6c7315] { + display: flex; + justify-content: center; + align-items: center; + width: 3.125rem; + height: 3.125rem; + border-radius: 50%; + overflow: hidden; + border: 0.09375rem solid rgba(170, 170, 255, 0.3); + box-shadow: 0 0.0625rem 0.25rem rgba(0, 0, 0, 0.06); +} +.user-profile-card .left .avatar-preview uni-image[data-v-0c6c7315] { + width: 100%; + height: 100%; + object-fit: cover; +} +.user-profile-card .left .avatar-placeholder[data-v-0c6c7315] { + display: flex; + justify-content: center; + align-items: center; + box-sizing: border-box; + border-radius: 50%; + width: 3.125rem; + height: 3.125rem; + background: linear-gradient(135deg, #f5f3ff, #ede9fe); +} +.user-profile-card .center[data-v-0c6c7315] { + flex: 1; + height: 0; + display: flex; + flex-direction: column; + align-items: start; + justify-content: center; +} +.user-profile-card .center uni-text[data-v-0c6c7315]:first-child { + font-size: 0.6875rem; + color: #999; + margin-bottom: 0.0625rem; +} +.user-profile-card .center uni-text[data-v-0c6c7315]:last-child { + font-size: 0.9375rem; + font-weight: 600; + color: #1a1a2e; +} +.user-profile-card .right[data-v-0c6c7315] { + flex-shrink: 0; + margin-left: 0.3125rem; + display: flex; + align-items: center; + background: rgba(16, 185, 129, 0.06); + padding: 0.25rem 0.5rem; + border-radius: 0.625rem; +} +.user-profile-card .right .status-dot[data-v-0c6c7315] { + width: 0.375rem; + height: 0.375rem; + border-radius: 50%; + background-color: #10B981; + margin-right: 0.3125rem; + box-shadow: 0 0 0.25rem rgba(16, 185, 129, 0.5); + animation: statusDotPulse-0c6c7315 2s ease-in-out infinite; +} +@keyframes statusDotPulse-0c6c7315 { +0%, 100% { + box-shadow: 0 0 0.25rem rgba(16, 185, 129, 0.5); + transform: scale(1); +} +50% { + box-shadow: 0 0 0.5rem rgba(16, 185, 129, 0.6); + transform: scale(1.15); +} +} +.user-profile-card .right .status-text uni-text[data-v-0c6c7315] { + font-size: 0.6875rem; + color: #059669; + font-weight: 500; +} +.new-chat-btn[data-v-0c6c7315] { + margin-top: 0.75rem; + width: 100%; + padding: 0.8125rem; + box-sizing: border-box; + display: flex; + justify-content: center; + align-items: center; + background: linear-gradient(135deg, #6c63ff, #4a45d1); + border-radius: 0.75rem; + box-shadow: 0 0.1875rem 0.625rem rgba(108, 99, 255, 0.3); + transition: all 0.2s ease; +} +.new-chat-btn[data-v-0c6c7315]:active { + transform: scale(0.97); + box-shadow: 0 0.09375rem 0.3125rem rgba(108, 99, 255, 0.2); +} +.new-chat-btn uni-text[data-v-0c6c7315] { + font-size: 0.875rem; + font-weight: 600; + color: #ffffff; + letter-spacing: 0.03125rem; +} +.chat-history[data-v-0c6c7315] { + width: 100%; + padding: 0.625rem 0; + box-sizing: border-box; + flex: 1; + height: 0; + overflow-y: auto; + overflow-x: hidden; +} +.chat-history[data-v-0c6c7315]::-webkit-scrollbar { + width: 0.1875rem; +} +.chat-history[data-v-0c6c7315]::-webkit-scrollbar-thumb { + background: rgba(108, 99, 255, 0.25); + border-radius: 0.09375rem; +} +.chat-history[data-v-0c6c7315]::-webkit-scrollbar-track { + background: transparent; +} +.history-header[data-v-0c6c7315] { + width: 100%; + padding: 0.625rem 0.9375rem; + box-sizing: border-box; + border-bottom: 0.03125rem solid rgba(170, 170, 255, 0.15); + display: flex; + justify-content: space-between; + align-items: center; +} +.history-header .history-title-section[data-v-0c6c7315] { + display: flex; + flex-direction: column; +} +.history-header .history-title-section uni-text[data-v-0c6c7315]:first-child { + font-size: 1rem; + font-weight: 700; + color: #1a1a2e; +} +.history-header .history-title-section uni-text[data-v-0c6c7315]:last-child { + font-size: 0.6875rem; + color: #999; + margin-top: 0.0625rem; +} +.history-header .history-management[data-v-0c6c7315] { + display: flex; +} +.history-header .history-management .history-management-btn[data-v-0c6c7315] { + margin-left: 0.5rem; + width: 1.75rem; + height: 1.75rem; + display: flex; + justify-content: center; + align-items: center; + font-size: 0.8125rem; + background-color: rgba(221, 221, 221, 0.4); + border-radius: 0.4375rem; + transition: all 0.15s ease; +} +.history-header .history-management .history-management-btn[data-v-0c6c7315]:active { + transform: scale(0.9); +} +.history-header .history-management .icon-jia-style[data-v-0c6c7315] { + background: rgba(16, 185, 129, 0.12); + color: #059669; +} +.history-header .history-management .icon-duoxuan-style[data-v-0c6c7315] { + color: #666666; +} +.history-header .history-management .icon-saochu-style[data-v-0c6c7315] { + color: #ff4d4f; +} +.history-header .history-management .icon-quxiao-style[data-v-0c6c7315] { + color: #6c63ff; + box-sizing: border-box; +} +.history-header .history-management .icon-total_selection-style[data-v-0c6c7315] { + color: #666666; +} +.history-header .history-management .icon-total_selection-style.active[data-v-0c6c7315] { + color: #6c63ff; + background: rgba(108, 99, 255, 0.12); +} +.history-header .history-management .icon-shanchu-style[data-v-0c6c7315] { + color: #ff4d4f; +} +.history-search-wrap[data-v-0c6c7315] { + display: flex; + flex-direction: column; + width: 100%; + padding: 0.5rem 0.9375rem 0.1875rem 0.9375rem; + box-sizing: border-box; +} +.history-search-wrap .history-search-inner[data-v-0c6c7315] { + display: flex; + width: 100%; + padding: 0.375rem 0.6875rem; + box-sizing: border-box; + align-items: center; + background-color: rgba(255, 255, 255, 0.6); + border-radius: 0.875rem; + border: 0.0625rem solid transparent; + transition: all 0.25s ease; +} +.history-search-wrap .history-search-inner .search-icon[data-v-0c6c7315] { + color: #999 !important; + font-weight: bold; + font-size: 1rem !important; +} +.history-search-wrap .history-search-inner .chat-search-icon[data-v-0c6c7315] { + color: #6c63ff !important; + font-weight: bold; + font-size: 1rem !important; +} +.history-search-wrap .history-search-inner uni-input[data-v-0c6c7315] { + margin-left: 0.375rem; + box-sizing: border-box; + font-size: 0.8125rem; + color: #333; +} +.history-search-wrap .history-search-inner uni-input[data-v-0c6c7315]::-webkit-input-placeholder { + color: #c0c0d0; +} +.history-search-wrap .history-search-inner uni-input[data-v-0c6c7315]::placeholder { + color: #c0c0d0; +} +.history-search-wrap .history-search-inner.has-value[data-v-0c6c7315] { + background-color: #ffffff; + border-color: #6c63ff; + box-shadow: 0 0 0 0.09375rem rgba(108, 99, 255, 0.08); +} +.chat-history-list[data-v-0c6c7315] { + padding: 0.3125rem 0.9375rem; +} +.chat-history-list .chat-history-card[data-v-0c6c7315] { + display: flex; + align-items: center; + border: 0.04688rem solid rgba(108, 99, 255, 0.25); + background: linear-gradient(135deg, #ffffff, #faf9ff); + border-radius: 0.75rem; + padding: 0.75rem 0.75rem; + margin-top: 0.375rem; + box-sizing: border-box; + transition: all 0.2s ease; +} +.chat-history-list .chat-history-card[data-v-0c6c7315]:active { + transform: scale(0.98); +} +.chat-history-list .is-select-chat[data-v-0c6c7315] { + border-color: #6c63ff; + box-shadow: 0 0.125rem 0.5rem rgba(108, 99, 255, 0.15); + background: linear-gradient(135deg, rgba(108, 99, 255, 0.06), rgba(108, 99, 255, 0.02)); +} +.chat-history-list .history-chat-checkbox[data-v-0c6c7315] { + width: 1.125rem; + height: 1.125rem; + border-radius: 0.3125rem; + display: flex; + justify-content: center; + align-items: center; + border: 0.0625rem solid rgba(108, 99, 255, 0.5); + margin-right: 0.375rem; + flex-shrink: 0; +} +.chat-history-list .history-chat-avatar[data-v-0c6c7315] { + width: 2rem; + height: 2rem; + border-radius: 0.5625rem; + display: flex; + justify-content: center; + align-items: center; + background: linear-gradient(135deg, rgba(108, 99, 255, 0.3), rgba(108, 99, 255, 0.15)); + margin-left: 0; + margin-right: 0.5rem; + box-sizing: border-box; + flex-shrink: 0; +} +.chat-history-list .friend-avatar[data-v-0c6c7315] { + display: flex; + justify-content: center; + align-items: center; + border-radius: 0.5625rem; + width: 100%; + height: 100%; + overflow: hidden; +} +/** + * 这里是uni-app内置的常用样式变量 + * + * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量 + * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App + * + */ +/** + * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能 + * + * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件 + */ +/* 颜色变量 */ +/* 行为相关颜色 */ +/* 文字基本颜色 */ +/* 背景颜色 */ +/* 边框颜色 */ +/* 尺寸变量 */ +/* 文字尺寸 */ +/* 图片尺寸 */ +/* Border Radius */ +/* 水平间距 */ +/* 垂直间距 */ +/* 透明度 */ +/* 文章场景相关 */ +.chat-page[data-v-fdf84df1] { + position: relative; + background: linear-gradient(180deg, #f5f4ff 0%, #f8f7fc 50%, #faf9fe 100%); +} +/* 笼罩层样式 */ +.mask[data-v-fdf84df1] { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(0, 0, 0, 0.45); + backdrop-filter: blur(4px); + -webkit-backdrop-filter: blur(4px); + z-index: 998; + animation: fadeIn-fdf84df1 0.3s ease; +} +@keyframes fadeIn-fdf84df1 { +from { opacity: 0; +} +to { opacity: 1; +} +} +/* ================会话侧边栏样式================== */ +.chat-sidebar[data-v-fdf84df1] { + position: fixed; + top: 0; + left: 0; + width: calc(100% - 3.125rem); + max-width: 21.25rem; + height: 100%; + transform: translateX(-100%); + transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1); + z-index: 999; + box-shadow: 0.25rem 0 0.9375rem rgba(0, 0, 0, 0.06); +} +.chat-sidebar.sidebar-show[data-v-fdf84df1] { + transform: translateX(0); +} +/* =============================新建会话弹窗样式================== */ +.ncd-overlay[data-v-fdf84df1] { + position: fixed; + width: 100%; + height: 100%; + left: 0; + top: 0; + display: flex; + align-items: center; + justify-content: center; + background-color: rgba(30, 30, 50, 0.35); + z-index: 9999; + backdrop-filter: blur(8px); + -webkit-backdrop-filter: blur(8px); + animation: fadeIn-fdf84df1 0.25s ease; +} +.ncd-card[data-v-fdf84df1] { + width: 90%; + height: auto; + background-color: #ffffff; + border-radius: 1.25rem; + padding: 1.5625rem 1.25rem; + box-sizing: border-box; + box-shadow: 0 0.625rem 1.875rem rgba(100, 100, 200, 0.12); + animation: slideUp-fdf84df1 0.35s ease; +} +@keyframes slideUp-fdf84df1 { +from { opacity: 0; transform: translateY(1.25rem) scale(0.96); +} +to { opacity: 1; transform: translateY(0) scale(1); +} +} +.ncd-header[data-v-fdf84df1] { + display: flex; + justify-content: center; + align-items: center; + width: 100%; +} +.ncd-title-eng[data-v-fdf84df1] { + display: flex; + flex-shrink: 0; + background: linear-gradient(135deg, #6c63ff, #4a45d1); + padding: 0.1875rem 0.4375rem; + border-radius: 0.375rem; + justify-content: center; + align-items: center; +} +.ncd-title-eng uni-text[data-v-fdf84df1] { + color: #ffffff; + font-weight: bold; + font-size: 0.875rem; +} +.ncd-title-zh[data-v-fdf84df1] { + margin-left: 0.3125rem; + display: flex; + flex: 1; + height: 0; + font-size: 1.125rem; + font-weight: 700; + color: #1a1a2e; +} +.ncd-header uni-icons[data-v-fdf84df1] { + display: flex; + flex-shrink: 0; +} +.ncd-options[data-v-fdf84df1] { + display: flex; + flex-direction: column; + width: 100%; + justify-content: center; + align-items: center; + margin-top: 0.625rem; + box-sizing: border-box; +} +.ncd-option[data-v-fdf84df1] { + width: 100%; + background: #f8fafc; + padding: 1.25rem 0.9375rem; + display: flex; + border-radius: 0.875rem; + margin-top: 0.625rem; + box-sizing: border-box; + justify-content: center; + align-items: center; + transition: all 0.2s ease; + border: 0.0625rem solid transparent; +} +.ncd-option[data-v-fdf84df1]:active { + transform: scale(0.98); +} +.ncd-normal[data-v-fdf84df1] { + border: 0.0625rem solid #e8edf3; +} +.ncd-normal[data-v-fdf84df1]:active { + border-color: #c4c8d0; + background: #f0f3f7; +} +.ncd-opt-icon[data-v-fdf84df1] { + width: 2.5rem; + height: 2.5rem; + border-radius: 0.75rem; + padding: 0.3125rem; + box-sizing: border-box; + display: flex; + flex-shrink: 0; + justify-content: center; + align-items: center; +} +.ncd-normal .ncd-opt-icon[data-v-fdf84df1] { + background: linear-gradient(135deg, #e2e8f0, #d9dfe8); +} +.ncd-opt-title[data-v-fdf84df1] { + display: flex; + flex-direction: column; + margin: 0 0.5rem; + box-sizing: border-box; + justify-content: center; + flex: 1; + height: 0; +} +.ncd-opt-title-1[data-v-fdf84df1] { + font-size: 0.9375rem; + font-weight: 600; + color: #1a1a2e; +} +.ncd-opt-title-2[data-v-fdf84df1] { + font-size: 0.75rem; + color: #888; + margin-top: 0.125rem; +} +.arrow-right-style[data-v-fdf84df1] { + display: flex; + flex-shrink: 0; + font-weight: bold !important; + font-size: 0.875rem !important; + color: #999 !important; +} +.ncd-intelligence[data-v-fdf84df1] { + background: linear-gradient(135deg, #f8f5ff, #efe8ff); + border: 0.0625rem solid #c4b5fd; +} +.ncd-intelligence[data-v-fdf84df1]:active { + border-color: #a78bfa; + background: linear-gradient(135deg, #f3efff, #e8dcff); +} +.ncd-intelligence .ncd-opt-icon[data-v-fdf84df1] { + background: linear-gradient(135deg, #8b5cf6, #7c3aed); +} +/* ==========新建聊天列表相关====== */ +.close-option-card[data-v-fdf84df1] { + width: 100%; + display: flex; + justify-content: flex-end; +} +.close-option-card .iconfont[data-v-fdf84df1] { + color: #ff4d4f !important; +} +.chat-option-list[data-v-fdf84df1] { + display: flex; + width: 100%; + flex-direction: column; +} +.chat-option[data-v-fdf84df1] { + display: flex; + justify-content: flex-start; + align-items: center; + margin: 0.3125rem 0; + padding: 0.5rem 0.625rem; + border-radius: 0.625rem; + box-sizing: border-box; + transition: all 0.15s ease; +} +.chat-option[data-v-fdf84df1]:active { + background: rgba(170, 170, 255, 0.08); +} +.chat-option .iconfont[data-v-fdf84df1] { + font-size: 30px; +} +.option-name[data-v-fdf84df1] { + font-size: 17px; + font-weight: 500; + margin-left: 12px; + box-sizing: border-box; + color: #333; +} +/* ================聊天容器============== */ +.chat-wrapper[data-v-fdf84df1] { + position: relative; + width: 100%; + height: 100%; + display: flex; + flex-direction: column; +} +/* ================聊天顶部样式============== */ +.chat-hearder[data-v-fdf84df1] { + width: 100%; + height: auto; + padding: 0.5rem 0.3125rem; + box-sizing: border-box; + display: flex; + flex-shrink: 0; + justify-content: flex-start; + align-items: center; + background: linear-gradient(180deg, rgba(255,255,255,0.95), rgba(255,255,255,0.8)); + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); + border-bottom: 0.03125rem solid rgba(170, 170, 255, 0.15); +} +.chat-btn-group[data-v-fdf84df1] { + width: 100%; + height: auto; + display: flex; + justify-content: flex-start; + align-items: center; +} +.head-btn[data-v-fdf84df1] { + padding: 0.375rem 0.4375rem; + border: 0.04688rem solid rgba(170, 170, 255, 0.3); + border-radius: 0.5625rem; + margin: 0 0.3125rem; + box-sizing: border-box; + display: flex; + justify-content: center; + align-items: center; + background: rgba(255, 255, 255, 0.7); + transition: all 0.2s ease; +} +.head-btn[data-v-fdf84df1]:active { + background: rgba(170, 170, 255, 0.1); + transform: scale(0.95); +} +.chat-btn-group .head-btn:nth-child(1) .iconfont[data-v-fdf84df1] { + color: #a78bfa; +} +.chat-btn-group .head-btn:nth-child(2) .iconfont[data-v-fdf84df1] { + color: #6495ed; +} +.head-btn .iconfont[data-v-fdf84df1] { + font-size: 1.5rem; +} +.log-out[data-v-fdf84df1] { + /* background: #ffd4d4; */ +} +.log-out .iconfont[data-v-fdf84df1] { + color: #ff4d4f; +} +/* ==========聊天主体部分========== */ +.main-chat[data-v-fdf84df1] { + display: flex; + flex: 1; + height: 0; + width: 100%; + flex-direction: column; +} +/* ==========加载更多提示========== */ +.load-more-tip[data-v-fdf84df1] { + text-align: center; + padding: 0.625rem 0; + font-size: 0.75rem; + color: #999; +} +/* ==========聊天对话展示部分========== */ +.chat-messages[data-v-fdf84df1] { + display: flex; + flex: 1; + height: 0; + width: 100%; + padding: 0.625rem 0.75rem; + box-sizing: border-box; + flex-direction: column; + overflow: auto; +} +.chat-messages[data-v-fdf84df1]::-webkit-scrollbar { + width: 0.1875rem; +} +.chat-messages[data-v-fdf84df1]::-webkit-scrollbar-thumb { + background: rgba(170, 170, 255, 0.3); + border-radius: 0.09375rem; +} +.chat-messages[data-v-fdf84df1]::-webkit-scrollbar-track { + background: transparent; +} +.chat-message[data-v-fdf84df1] { + display: flex; + align-items: flex-start; + margin: 0.4375rem 0; + box-sizing: border-box; + animation: msgFadeIn-fdf84df1 0.3s ease; +} +@keyframes msgFadeIn-fdf84df1 { +from { opacity: 0; transform: translateY(0.3125rem); +} +to { opacity: 1; transform: translateY(0); +} +} +.message-user[data-v-fdf84df1] { + flex-direction: row-reverse; +} +.chat-avatar[data-v-fdf84df1] { + display: flex; + justify-content: center; + align-items: center; + width: 2.25rem; + height: 2.25rem; + box-sizing: border-box; + border-radius: 0.5rem; + margin-right: 0.375rem; + flex-shrink: 0; +} +.friend-avatar[data-v-fdf84df1] { + display: flex; + justify-content: center; + align-items: center; + border-radius: 0.5rem; + width: 100%; + height: 100%; + overflow: hidden; +} +.chat-avatar-user[data-v-fdf84df1] { + margin-right: 0; + margin-left: 0.375rem; +} +.chat-content[data-v-fdf84df1] { + width: auto; + max-width: calc(100% - 3.75rem); + height: auto; + padding: 0.625rem 0.75rem; + box-sizing: border-box; + border-radius: 0.75rem; + overflow-x: auto; + font-size: 1rem; + line-height: 1.65; + position: relative; + box-shadow: 0 0.0625rem 0.25rem rgba(0, 0, 0, 0.04); + background: #ffffff; + border: 0.03125rem solid #eeeef5; +} +.chat-content-user[data-v-fdf84df1] { + background: linear-gradient(135deg, #c4b5fd, #b8a5f0); + color: #fff; + border: none; + box-shadow: 0 0.125rem 0.375rem rgba(170, 170, 255, 0.3); + border-bottom-right-radius: 0.25rem; +} +/* AI 回复气泡满屏 */ +.chat-content-assistant[data-v-fdf84df1] { + max-width: 100%; +} +.chat-content[data-v-fdf84df1]:not(.chat-content-user) { + border-bottom-left-radius: 0.25rem; +} +/* 图片消息 */ +.message-image[data-v-fdf84df1] { + max-width: 9.375rem; + border-radius: 0.375rem; + margin: 0.25rem 0; +} +/* 文件列表 */ +.message-file-list[data-v-fdf84df1] { + margin: 0.3125rem 0; +} +/* 文件项 */ +.file-item[data-v-fdf84df1] { + margin: 0.1875rem 0; +} +/* 文件样式 */ +.message-file[data-v-fdf84df1] { + display: flex; + align-items: center; + padding: 0.4375rem 0.5625rem; + background: rgba(255, 255, 255, 0.85); + border-radius: 0.375rem; + max-width: 10.9375rem; + border: 0.03125rem solid #eeeef5; +} +.chat-content-user .message-file[data-v-fdf84df1] { + background: rgba(255, 255, 255, 0.25); + border-color: rgba(255, 255, 255, 0.3); +} +.file-icon[data-v-fdf84df1] { + font-size: 1rem; + margin-right: 0.375rem; +} +.file-name[data-v-fdf84df1] { + flex: 1; + font-size: 0.8125rem; + color: #333; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} +.chat-content-user .file-name[data-v-fdf84df1] { + color: #fff; +} +.file-size[data-v-fdf84df1] { + font-size: 0.6875rem; + color: #999; + margin-left: 0.25rem; +} +.chat-content-user .file-size[data-v-fdf84df1] { + color: rgba(255, 255, 255, 0.7); +} +/* ==========聊天输入容器部分========== */ +.chat-interactive-container[data-v-fdf84df1] { + display: flex; + flex-shrink: 0; + width: 100%; + padding: 0.5rem 0.75rem 0.75rem; + box-sizing: border-box; + flex-direction: column; + background: linear-gradient(180deg, rgba(255,255,255,0.7), rgba(255,255,255,0.95)); + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); + border-top: 0.03125rem solid rgba(170, 170, 255, 0.12); +} +.chat-interactive-group[data-v-fdf84df1] { + display: flex; + width: 100%; + padding: 0.375rem 0; + box-sizing: border-box; + justify-content: flex-start; + align-items: center; +} +.chat-interactive-btn[data-v-fdf84df1] { + display: flex; + align-items: center; + justify-content: center; + border: 0.04688rem solid #d4d4e0; + border-radius: 0.5rem; + padding: 0.375rem 0.625rem; + margin-right: 0.5rem; + box-sizing: border-box; + font-size: 0.75rem; + color: #666; + background: #ffffff; + transition: all 0.2s ease; +} +.chat-interactive-btn[data-v-fdf84df1]:active { + border-color: #aaaaff; + background: rgba(170, 170, 255, 0.08); + transform: scale(0.96); +} +/* 思考中禁用态 */ +.chat-interactive-btn.disabled[data-v-fdf84df1] { + opacity: 0.45; + pointer-events: none; + border-color: #e0dfe8; + color: #aaa; +} +.chat-input-container[data-v-fdf84df1] { + display: flex; + width: 100%; + padding: 0.25rem 0.25rem 0.25rem 0.75rem; + box-sizing: border-box; + border: 0.0625rem solid #e0dff5; + border-radius: 0.9375rem; + align-items: flex-end; + background: #ffffff; + transition: all 0.25s ease; + box-shadow: 0 0.0625rem 0.375rem rgba(100, 100, 150, 0.06); +} +.chat-input-container[data-v-fdf84df1]:focus-within { + border-color: #aaaaff; + box-shadow: 0 0 0 0.125rem rgba(170, 170, 255, 0.12), 0 0.125rem 0.5rem rgba(100, 100, 150, 0.08); +} +.message-input[data-v-fdf84df1] { + flex: 1; + max-height: 4.6875rem; + min-height: 1.875rem; + overflow-y: auto; + margin: 0.125rem 0.3125rem; + box-sizing: border-box; + font-size: 0.875rem; + color: #333; +} +.message-input[data-v-fdf84df1]::-webkit-input-placeholder { + color: #c0c0d0; +} +.message-input[data-v-fdf84df1]::placeholder { + color: #c0c0d0; +} +/* 思考中:输入容器禁态 */ +.chat-input-container.input-disabled[data-v-fdf84df1] { + border-color: #eae9f2; + background: #f8f7fc; +} +/* 思考中:文本域禁用 */ +.message-input[disabled][data-v-fdf84df1] { + opacity: 0.5; + color: #bbb; +} +/* 思考中:发送按钮禁用 */ +.input-btn-group.disabled[data-v-fdf84df1] { + opacity: 0.4; + pointer-events: none; + background: #c0c0d0; + box-shadow: none; +} +/* PC端中断发送按钮 */ +.stop-message-btn[data-v-fdf84df1] { + background: linear-gradient(135deg, #ff4d4f, #ff6b6b); + box-shadow: 0 0.125rem 0.375rem rgba(255, 77, 79, 0.35); + animation: pulseStop-fdf84df1 1.8s ease-in-out infinite; +} +.stop-message-btn[data-v-fdf84df1]:active { + transform: scale(0.92); + box-shadow: 0 0.0625rem 0.1875rem rgba(255, 77, 79, 0.25); +} +.input-btn-group[data-v-fdf84df1] { + flex-shrink: 0; + display: flex; + justify-content: center; + align-items: center; + padding: 0.4375rem; + box-sizing: border-box; + background: linear-gradient(135deg, #6c63ff, #8b7cfb); + border-radius: 0.6875rem; + width: 2.25rem; + height: 2.25rem; + box-shadow: 0 0.125rem 0.375rem rgba(108, 99, 255, 0.35); + transition: all 0.2s ease; +} +.input-btn-group[data-v-fdf84df1]:active { + transform: scale(0.92); + box-shadow: 0 0.0625rem 0.1875rem rgba(108, 99, 255, 0.25); +} +.input-btn-group .iconfont[data-v-fdf84df1] { + color: #fff; + font-size: 1.125rem; +} +/* ai思考弹窗卡片 */ +.ai-card[data-v-fdf84df1] { + width: auto; + border: 0.0625rem solid rgba(64, 158, 255, 0.3); + box-shadow: 0 0.25rem 0.9375rem rgba(170, 170, 255, 0.2); + border-radius: 1rem !important; +} +/* 内容布局 */ +.thinking-content[data-v-fdf84df1] { + display: flex; + flex-direction: column; + align-items: center; + width: 100%; +} +/* 加载点点动画 */ +.loading-dots[data-v-fdf84df1] { + display: flex; + width: 100%; + align-items: center; + justify-content: center; +} +.dot[data-v-fdf84df1] { + width: 0.4375rem; + height: 0.4375rem; + margin: 0.1875rem; + border-radius: 50%; + background: linear-gradient(135deg, #409eff, #6db2ff); + animation: dotBlink-fdf84df1 1.2s infinite ease-in-out; +} +@keyframes dotBlink-fdf84df1 { +0%, 100% { + opacity: 0.3; + transform: scale(0.7); +} +50% { + opacity: 1; + transform: scale(1.2); +} +} +.dot[data-v-fdf84df1]:nth-child(2) { animation-delay: 0.25s; +} +.dot[data-v-fdf84df1]:nth-child(3) { animation-delay: 0.5s; +} +/* 文字 */ +.thinking-text[data-v-fdf84df1] { + margin: 0.625rem 0; + font-size: 20px; + font-weight: 600; + color: #409eff; +} +/* 思考弹窗中的中断按钮 */ +.stop-thinking-btn[data-v-fdf84df1] { + display: flex; + align-items: center; + justify-content: center; + width: 100%; + padding: 0.6875rem 0; + margin-top: 0.625rem; + border-radius: 0.75rem; + font-size: 0.875rem; + font-weight: 600; + color: #ff4d4f; + background: linear-gradient(135deg, rgba(255, 77, 79, 0.06), rgba(255, 77, 79, 0.02)); + border: 0.0625rem solid rgba(255, 77, 79, 0.35); + transition: all 0.2s ease; + animation: pulseStop-fdf84df1 2s ease-in-out infinite; +} +.stop-thinking-btn[data-v-fdf84df1]:active { + background: rgba(255, 77, 79, 0.12); + border-color: #ff4d4f; + transform: scale(0.97); +} +@keyframes pulseStop-fdf84df1 { +0%, 100% { border-color: rgba(255, 77, 79, 0.35); +} +50% { border-color: rgba(255, 77, 79, 0.65); +} +} +/* ========== 文件预览标签列表 ========== */ +.image-list[data-v-fdf84df1] { + width: 100%; + padding: 0.25rem 0 0.0625rem; + animation: fadeIn-fdf84df1 0.25s ease; +} +.image-tags[data-v-fdf84df1] { + display: flex; + flex-wrap: wrap; + gap: 0.3125rem; +} +.image-tag[data-v-fdf84df1] { + display: flex; + align-items: center; + padding: 0.1875rem 0.25rem 0.1875rem 0.5rem; + background: linear-gradient(135deg, rgba(108, 99, 255, 0.07), rgba(108, 99, 255, 0.04)); + border: 0.03125rem solid rgba(108, 99, 255, 0.18); + border-radius: 0.625rem; + max-width: 6.875rem; + transition: all 0.2s ease; + box-shadow: 0 0.03125rem 0.125rem rgba(108, 99, 255, 0.04); +} +.image-tag[data-v-fdf84df1]:active { + transform: scale(0.96); + border-color: rgba(108, 99, 255, 0.4); + background: rgba(108, 99, 255, 0.1); +} +.tag-name[data-v-fdf84df1] { + font-size: 0.6875rem; + color: #5a52d5; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + flex: 1; + font-weight: 500; +} +.tag-close[data-v-fdf84df1] { + flex-shrink: 0; + width: 1rem; + height: 1rem; + display: flex; + align-items: center; + justify-content: center; + font-size: 0.875rem; + color: #bbb; + margin-left: 0.1875rem; + border-radius: 50%; + line-height: 1; + transition: all 0.15s ease; +} +.tag-close[data-v-fdf84df1]:active { + color: #fff; + background: #ff4d4f; + transform: scale(1.1); +} +/* 消息详情弹窗 */ +.message-detail-card[data-v-fdf84df1] { + width: 90%; + max-height: 80vh; + display: flex; + flex-direction: column; + border-radius: 1.25rem; + padding: 1.25rem 1.125rem; +} +.message-detail-content[data-v-fdf84df1] { + flex: 1; + min-height: 6.25rem; + max-height: 60vh; + overflow-y: auto; + padding: 0.5rem 0; + font-size: 0.875rem; + line-height: 1.7; + color: #333333; + white-space: pre-wrap; + word-break: break-all; +} +/** + * 这里是uni-app内置的常用样式变量 + * + * uni-app 官方扩展插件及插件市场(https://ext.dcloud.net.cn)上很多三方插件均使用了这些样式变量 + * 如果你是插件开发者,建议你使用scss预处理,并在插件代码中直接使用这些变量(无需 import 这个文件),方便用户通过搭积木的方式开发整体风格一致的App + * + */ +/** + * 如果你是App开发者(插件使用者),你可以通过修改这些变量来定制自己的插件主题,实现自定义主题功能 + * + * 如果你的项目同样使用了scss预处理,你也可以直接在你的 scss 代码中使用如下变量,同时无需 import 这个文件 + */ +/* 颜色变量 */ +/* 行为相关颜色 */ +/* 文字基本颜色 */ +/* 背景颜色 */ +/* 边框颜色 */ +/* 尺寸变量 */ +/* 文字尺寸 */ +/* 图片尺寸 */ +/* Border Radius */ +/* 水平间距 */ +/* 垂直间距 */ +/* 透明度 */ +/* 文章场景相关 */ +.chat-content { + /* 普通正文 */ + /* 气泡内的内联链接(替代 标签,点击由 JS 拦截处理) */ +} +.chat-content h1 { + font-size: 1.3125rem; + line-height: 1.4; + margin: 0.5rem 0 0.3125rem; +} +.chat-content h2 { + font-size: 1.1875rem; + line-height: 1.4; + margin: 0.4375rem 0 0.25rem; +} +.chat-content h3 { + font-size: 1.125rem; + line-height: 1.4; + margin: 0.375rem 0 0.25rem; +} +.chat-content h4, +.chat-content h5, +.chat-content h6 { + font-size: 1.0625rem; + line-height: 1.5; + margin: 0.3125rem 0 0.1875rem; +} +.chat-content p, +.chat-content uni-text, +.chat-content span { + font-size: 1.1875rem !important; + line-height: 1.6; + margin: 0.25rem 0; +} +.chat-content .chat-inline-link { + color: #3b86ff !important; + text-decoration: underline; + word-break: break-all; +} + +/* 流式气泡中的加载动画 */ +.bubble-loading-dots { + display: flex; + align-items: center; + justify-content: flex-start; + padding: 0.3125rem 0; +} +.bubble-dot { + width: 0.375rem; + height: 0.375rem; + border-radius: 50%; + background: #a0a0b8; + margin-right: 0.3125rem; + animation: bubbleDotBlink 1.2s infinite ease-in-out; +} +.bubble-dot:nth-child(2) { + animation-delay: 0.3s; +} +.bubble-dot:nth-child(3) { + animation-delay: 0.6s; +} +@keyframes bubbleDotBlink { +0%, 100% { + opacity: 0.25; + transform: scale(0.7); +} +50% { + opacity: 1; + transform: scale(1.1); +} +} +/* 消息详情弹窗 — 链接提取区域 */ +.detail-links-section { + margin-top: 0.75rem; + padding-top: 0.75rem; + border-top: 0.03125rem solid #eee; +} +.detail-links-title { + font-size: 0.9375rem; + font-weight: 600; + color: #666; + margin-bottom: 0.5rem; +} +.detail-link-item { + padding: 0.5rem 0.625rem; + background: #f5f7fa; + border-radius: 0.375rem; + margin-bottom: 0.375rem; +} +.link-text { + font-size: 0.875rem; + color: #007aff; + word-break: break-all; +} +.detail-empty { + display: flex; + align-items: center; + justify-content: center; + padding: 1.25rem 0; + font-size: 0.875rem; + color: #999; +} + +/* ========== 自定义下载 Toast(层级高于 ncd-overlay 的 9999)========== */ +.download-toast-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + z-index: 10001; + pointer-events: none; +} +.download-toast-card { + display: flex; + align-items: center; + justify-content: center; + padding: 0.75rem 1.25rem; + border-radius: 0.75rem; + backdrop-filter: blur(12px); + -webkit-backdrop-filter: blur(12px); + box-shadow: 0 0.375rem 1.25rem rgba(0, 0, 0, 0.15); + animation: toastFadeIn 0.25s ease; + pointer-events: auto; +} +@keyframes toastFadeIn { +from { + opacity: 0; + transform: scale(0.85); +} +to { + opacity: 1; + transform: scale(1); +} +} +.download-toast-loading { + background: rgba(40, 40, 60, 0.92); +} +.download-toast-success { + background: rgba(40, 60, 40, 0.92); +} +.download-toast-error { + background: rgba(60, 30, 30, 0.92); +} +.download-toast-icon { + font-size: 1.125rem; + color: #fff; + margin-right: 0.5rem; + display: flex; + align-items: center; +} +.download-toast-card.download-toast-loading .download-toast-icon { + gap: 0.25rem; +} +.toast-dot { + width: 0.375rem; + height: 0.375rem; + border-radius: 50%; + background: #fff; + animation: toastDotBlink 1.2s infinite ease-in-out; +} +.toast-dot:nth-child(2) { + animation-delay: 0.3s; +} +.toast-dot:nth-child(3) { + animation-delay: 0.6s; +} +@keyframes toastDotBlink { +0%, 100% { + opacity: 0.3; + transform: scale(0.75); +} +50% { + opacity: 1; + transform: scale(1.2); +} +} +.download-toast-text { + font-size: 0.875rem; + color: #fff; + font-weight: 500; +} \ No newline at end of file diff --git a/unpackage/dist/dev/app-plus/pages/text/text2.css b/unpackage/dist/dev/app-plus/pages/text/text2.css deleted file mode 100644 index c678ccf..0000000 --- a/unpackage/dist/dev/app-plus/pages/text/text2.css +++ /dev/null @@ -1,222 +0,0 @@ - -.container[data-v-cd5869b7] { - padding: 0.625rem; - background-color: #f5f5f5; - min-height: 100vh; -} -.status-card[data-v-cd5869b7], - .config-card[data-v-cd5869b7], - .control-card[data-v-cd5869b7], - .send-card[data-v-cd5869b7], - .log-card[data-v-cd5869b7] { - background-color: #fff; - border-radius: 0.5rem; - padding: 0.9375rem; - margin-bottom: 0.625rem; - box-shadow: 0 0.0625rem 0.25rem rgba(0, 0, 0, 0.05); -} -.status-label[data-v-cd5869b7] { - font-size: 0.875rem; - color: #666; - margin-bottom: 0.5rem; -} -.status-value[data-v-cd5869b7] { - display: inline-block; - padding: 0.375rem 1rem; - border-radius: 0.25rem; - font-size: 0.875rem; - font-weight: 500; -} -.status-connected[data-v-cd5869b7] { - background-color: #e6f7e6; - color: #52c41a; -} -.status-connecting[data-v-cd5869b7] { - background-color: #fff7e6; - color: #faad14; -} -.status-disconnected[data-v-cd5869b7] { - background-color: #f5f5f5; - color: #999; -} -.status-error[data-v-cd5869b7] { - background-color: #fff1f0; - color: #ff4d4f; -} -.status-info[data-v-cd5869b7] { - margin-top: 0.5rem; -} -.reconnect-info[data-v-cd5869b7] { - font-size: 0.75rem; - color: #faad14; -} -.section-title[data-v-cd5869b7] { - font-size: 1rem; - font-weight: 600; - color: #333; - margin-bottom: 0.75rem; - display: block; -} -.input-group[data-v-cd5869b7] { - margin-bottom: 0.75rem; -} -.input-label[data-v-cd5869b7] { - font-size: 0.875rem; - color: #666; - margin-bottom: 0.375rem; - display: block; -} -.input-field[data-v-cd5869b7] { - border: 0.0625rem solid #e8e8e8; - border-radius: 0.25rem; - padding: 0.625rem; - font-size: 0.875rem; - background-color: #fafafa; -} -.control-card[data-v-cd5869b7] { - display: flex; - gap: 0.625rem; -} -.btn[data-v-cd5869b7] { - flex: 1; - height: 2.75rem; - line-height: 2.75rem; - border-radius: 0.375rem; - font-size: 1rem; - text-align: center; - border: none; -} -.btn-primary[data-v-cd5869b7] { - background-color: #1890ff; - color: #fff; -} -.btn-danger[data-v-cd5869b7] { - background-color: #ff4d4f; - color: #fff; -} -.btn-secondary[data-v-cd5869b7] { - background-color: #f0f0f0; - color: #666; -} -.btn-outline[data-v-cd5869b7] { - background-color: transparent; - color: #1890ff; - border: 0.0625rem solid #1890ff; -} -.btn-small[data-v-cd5869b7] { - height: 2rem; - line-height: 2rem; - font-size: 0.8125rem; - padding: 0 0.75rem; -} -.btn[disabled][data-v-cd5869b7] { - opacity: 0.5; -} -.send-input-wrapper[data-v-cd5869b7] { - display: flex; - flex-direction: column; - gap: 0.625rem; -} -.send-input[data-v-cd5869b7] { - border: 0.0625rem solid #e8e8e8; - border-radius: 0.25rem; - padding: 0.625rem; - font-size: 0.875rem; - min-height: 5rem; - background-color: #fafafa; - box-sizing: border-box; -} -.send-buttons[data-v-cd5869b7] { - display: flex; - gap: 0.625rem; -} -.quick-messages[data-v-cd5869b7] { - margin-top: 0.75rem; - padding-top: 0.75rem; - border-top: 0.0625rem solid #f0f0f0; -} -.quick-label[data-v-cd5869b7] { - font-size: 0.8125rem; - color: #999; - margin-bottom: 0.5rem; - display: block; -} -.quick-btns[data-v-cd5869b7] { - display: flex; - flex-wrap: wrap; - gap: 0.5rem; -} -.quick-btn[data-v-cd5869b7] { - padding: 0.375rem 0.75rem; - background-color: #f0f5ff; - color: #1890ff; - border-radius: 0.25rem; - font-size: 0.8125rem; - border: none; -} -.quick-btn[disabled][data-v-cd5869b7] { - opacity: 0.5; -} -.log-header[data-v-cd5869b7] { - display: flex; - justify-content: space-between; - align-items: center; - margin-bottom: 0.75rem; -} -.log-title-wrapper[data-v-cd5869b7] { - display: flex; - align-items: center; - gap: 0.375rem; -} -.log-title-wrapper .section-title[data-v-cd5869b7] { - margin-bottom: 0; -} -.log-count[data-v-cd5869b7] { - font-size: 0.75rem; - color: #999; -} -.log-actions[data-v-cd5869b7] { - display: flex; - gap: 0.375rem; -} -.log-list[data-v-cd5869b7] { - max-height: 15.625rem; - background-color: #1e1e1e; - border-radius: 0.25rem; - padding: 0.625rem; -} -.log-item[data-v-cd5869b7] { - display: flex; - margin-bottom: 0.375rem; - font-family: 'Courier New', monospace; - font-size: 0.75rem; - line-height: 1.6; -} -.log-time[data-v-cd5869b7] { - color: #888; - margin-right: 0.5rem; - flex-shrink: 0; -} -.log-content[data-v-cd5869b7] { - word-break: break-all; - flex: 1; - color: #fff; -} -.log-info .log-content[data-v-cd5869b7] { - color: #fff; -} -.log-success .log-content[data-v-cd5869b7] { - color: #52c41a; -} -.log-error .log-content[data-v-cd5869b7] { - color: #ff4d4f; -} -.log-warn .log-content[data-v-cd5869b7] { - color: #faad14; -} -.log-send .log-content[data-v-cd5869b7] { - color: #1890ff; -} -.log-receive .log-content[data-v-cd5869b7] { - color: #52c41a; -}