diff --git a/components/ChatSidebar.vue b/components/ChatSidebar.vue
index 13c2ab2..ede9aec 100644
--- a/components/ChatSidebar.vue
+++ b/components/ChatSidebar.vue
@@ -24,7 +24,7 @@
- + 聊天列表
+ + 更多聊天列表
diff --git a/manifest.json b/manifest.json
index 2812c1e..298fdd7 100644
--- a/manifest.json
+++ b/manifest.json
@@ -1,5 +1,5 @@
{
- "name" : "test1",
+ "name" : "宇恒一号",
"appid" : "__UNI__923FD9E",
"description" : "",
"versionName" : "1.1.1",
diff --git a/pages/Chat/Chat.css b/pages/Chat/Chat.css
index 63021be..9280a21 100644
--- a/pages/Chat/Chat.css
+++ b/pages/Chat/Chat.css
@@ -105,11 +105,13 @@
font-size: 36rpx;
font-weight: 700;
color: #1a1a2e;
+ align-items: center;
}
.ncd-header uni-icons {
display: flex;
flex-shrink: 0;
+ align-items: center;
}
.ncd-options {
@@ -325,6 +327,62 @@
color: #ff4d4f;
}
+/* ==========WYXD 文件入口横幅========== */
+.wyxd-banner {
+ display: flex;
+ align-items: center;
+ padding: 14rpx 24rpx;
+ margin: 6rpx 20rpx;
+ background: linear-gradient(135deg, #e8f4fd, #f0e6ff);
+ border: 1.5rpx solid rgba(120, 120, 220, 0.25);
+ border-radius: 16rpx;
+ box-shadow: 0 2rpx 8rpx rgba(100, 100, 200, 0.08);
+}
+
+.wyxd-banner:active {
+ background: linear-gradient(135deg, #d8eafb, #e6d8ff);
+ transform: scale(0.98);
+}
+
+.wyxd-banner-icon {
+ font-size: 32rpx;
+ color: #6c6ce0;
+ margin-right: 12rpx;
+}
+
+.wyxd-banner-text {
+ flex: 1;
+ font-size: 26rpx;
+ font-weight: 600;
+ color: #4a4ab8;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+}
+
+.wyxd-banner-arrow {
+ font-size: 24rpx;
+ color: #9999cc;
+}
+
+.wyxd-banner-close {
+ width: 36rpx;
+ height: 36rpx;
+ line-height: 36rpx;
+ text-align: center;
+ font-size: 28rpx;
+ color: #9999cc;
+ border-radius: 50%;
+ background: rgba(100, 100, 200, 0.08);
+ margin-left: 8rpx;
+ flex-shrink: 0;
+}
+
+.wyxd-banner-close:active {
+ background: rgba(100, 100, 200, 0.18);
+ color: #6c6ce0;
+}
+
/* ==========聊天主体部分========== */
.main-chat {
display: flex;
diff --git a/pages/Chat/Chat.vue b/pages/Chat/Chat.vue
index 1ebadfa..58fd89e 100644
--- a/pages/Chat/Chat.vue
+++ b/pages/Chat/Chat.vue
@@ -64,6 +64,15 @@
+
+
+
+
+ 查看: {{ wyxdFileName }}
+ ›
+ ×
+
+
@@ -210,10 +219,6 @@
-
@@ -297,6 +302,7 @@
watch
} from 'vue';
import {
+ onLoad,
onShow,
onUnload
} from '@dcloudio/uni-app';
@@ -340,6 +346,7 @@
chooseFile
} from '@/uni_modules/lime-choose-file'
import socketManager from '../../utils/socket';
+import { openFile as openFileNative, downloadAndOpen } from '@/utils/fileOpener.js'
const friendSocketStore = useFriendSocketStore()
// 连接实时通讯eriendsocket
@@ -360,6 +367,13 @@
// 聊天类型(ai,好友,群聊)
const ChatType = ref(0)
+ // WYXD 关联数据(从 WorkSpace 的「AI修改」进入时携带)
+ const wyxdWorkspaceId = ref('')
+ const wyxdFilePath = ref('')
+ const wyxdFileName = ref('')
+ const wyxdUserToken = ref('')
+ const hasWyxdFile = ref(false)
+
// marked.setOptions({
// breaks: true, // 自动把换行符转成
换行
@@ -453,6 +467,35 @@
return text
}
+ // 判断链接是否为可下载文件
+ const isDownloadLink = (url) => {
+ const supportedExtensions = ['html', 'htm', 'xlsx', 'xls', 'pdf', 'zip', 'rar', '7z', 'md', 'markdown',
+ 'docx', 'doc', 'pptx', 'ppt', 'txt', 'csv', 'json', 'xml', 'yaml', 'yml',
+ 'jpg', 'jpeg', 'png', 'gif', 'bmp', 'svg', 'webp', 'ico', 'tiff', 'tif', 'dxf'
+ ]
+ const extPattern = supportedExtensions.join('|')
+ const extRegex = new RegExp(`\\.(${extPattern})(?:\\?|$)`, 'i')
+ return extRegex.test(url)
+ }
+
+ // 渲染下载按钮 HTML
+ const renderDownloadBtn = (href, displayName) => {
+ const ext = ((href.split('.').pop() || '').split('?')[0] || '').toLowerCase()
+ const fileInfo = getFileTypeInfo(ext)
+ const name = displayName || extractFileNameFromUrl(href)
+ return ''
+ + '' + fileInfo.emoji + ''
+ + '' + name + ''
+ + ''
+ }
+
+ // 支持的文件扩展名(用于原始 URL 检测)
+ const supportedExtensions = ['html', 'htm', 'xlsx', 'xls', 'pdf', 'zip', 'rar', '7z', 'md', 'markdown',
+ 'docx', 'doc', 'pptx', 'ppt', 'txt', 'csv', 'json', 'xml', 'yaml', 'yml',
+ 'jpg', 'jpeg', 'png', 'gif', 'bmp', 'svg', 'webp', 'ico', 'tiff', 'tif', 'dxf'
+ ]
+ const extPattern = supportedExtensions.join('|')
+
// Markdown转HTML节点
const pareseMarkdown = (content) => {
if (!content) return ''
@@ -462,14 +505,60 @@
try {
// 转义单词中的单下划线,防止 snarkdown 误解析为斜体
content = escapeLoneUnderscores(content)
+
+ // 修复1: URL 中的中文括号编码,防止 snarkdown 截断URL
+ // [text](http://xxx/高二(1)班.xlsx) → [text](http://xxx/高二%281%29班.xlsx)
+ content = content.replace(
+ /\[([^\]]+)\]\((https?:\/\/[^\s]+)\)/g,
+ (m, name, url) => '[' + name + '](' + url.replace(/\(/g, '%28').replace(/\)/g, '%29') + ')'
+ )
+
+ // 修复2: 误用括号 [高二](1)班成绩表 → 高二(1)班成绩表
+ // 当 (数字/短文本) 后紧跟中文时,说明不是真实URL,还原为纯文本
+ content = content.replace(
+ /\[([^\]]+)\]\((\d{1,2}|[^\s)]{1,3})\)(?=[\u4e00-\u9fff])/g,
+ '$1($2)'
+ )
+
// 先转换表格,再用 snarkdown 处理其他 Markdown
content = convertMarkdownTable(content)
let html = snarkdown(content)
- // 将 替换为自定义 span,避免 WebView 触发默认下载
+
+ // 步骤1:暂存 标签,避免 raw URL 替换误伤 href 内的链接
+ const linkPlaceholders = []
html = html.replace(
/]*>(.*?)<\/a>/gi,
- '$2'
+ (match, href, innerText) => {
+ const idx = linkPlaceholders.length
+ linkPlaceholders.push({ href, innerText })
+ return '\x00LINK_' + idx + '\x00'
+ }
)
+
+ // 步骤2:将正文中的原始文件 URL 替换为下载按钮
+ const rawUrlRegex = new RegExp(
+ `https?://[^\\s<>"'']+\\.(${extPattern})(?:[?#][^\\s<>"'']*)?`,
+ 'gi'
+ )
+ html = html.replace(rawUrlRegex, (url) => {
+ return renderDownloadBtn(url)
+ })
+
+ // 步骤3:恢复 标签并分类处理
+ html = html.replace(/\x00LINK_(\d+)\x00/g, (_, idx) => {
+ const { href, innerText } = linkPlaceholders[idx]
+ if (isDownloadLink(href)) {
+ const plainText = innerText
+ .replace(/<[^>]*>/g, ' ')
+ .replace(/ /gi, ' ')
+ .replace(/\s+/g, ' ')
+ .trim()
+ return renderDownloadBtn(href, plainText)
+ }
+ // 普通链接保持原有样式
+ return '' + innerText + ''
+ })
+
return html
} catch (e) {
console.error('解析失败', e)
@@ -510,15 +599,7 @@
}
// 打开文件
const openFile = (url) => {
- uni.downloadFile({
- url: url,
- success: (res) => {
- uni.openDocument({
- filePath: res.tempFilePath,
- showMenu: true
- })
- }
- })
+ downloadAndOpen(url);
}
// 文件大小格式化
const formatFileSize = (size) => {
@@ -625,6 +706,17 @@
})
}
+ // 跳转到 WYXD 查看器(强制重新下载,不走缓存)
+ const goWyxdViewer = () => {
+ uni.navigateTo({
+ url: '/pages/WYXD/WYXD?workspaceId=' + encodeURIComponent(wyxdWorkspaceId.value)
+ + '&filePath=' + encodeURIComponent(wyxdFilePath.value)
+ + '&fileName=' + encodeURIComponent(wyxdFileName.value)
+ + '&userToken=' + encodeURIComponent(wyxdUserToken.value)
+ + '&forceDownload=1'
+ })
+ }
+
// 跳转到云端数据库
const goCloudDatabase = () => {
// 保存当前会话id,确保从工作区返回时能恢复
@@ -872,6 +964,50 @@
}
return urlStr
}
+
+ // 文件类型 → 图标和标签映射
+ const getFileTypeInfo = (ext) => {
+ const map = {
+ // 文档类
+ pdf: { emoji: '📕', label: 'PDF', color: '#e74c3c' },
+ doc: { emoji: '📘', label: 'Word', color: '#2b579a' },
+ docx: { emoji: '📘', label: 'Word', color: '#2b579a' },
+ xls: { emoji: '📗', label: 'Excel', color: '#217346' },
+ xlsx: { emoji: '📗', label: 'Excel', color: '#217346' },
+ ppt: { emoji: '📙', label: 'PPT', color: '#d24726' },
+ pptx: { emoji: '📙', label: 'PPT', color: '#d24726' },
+ txt: { emoji: '📄', label: '文本', color: '#666' },
+ md: { emoji: '📝', label: 'Markdown', color: '#333' },
+ markdown: { emoji: '📝', label: 'Markdown', color: '#333' },
+ csv: { emoji: '📊', label: 'CSV', color: '#217346' },
+ json: { emoji: '📋', label: 'JSON', color: '#f0a500' },
+ xml: { emoji: '📋', label: 'XML', color: '#e67e22' },
+ yaml: { emoji: '📋', label: 'YAML', color: '#e67e22' },
+ yml: { emoji: '📋', label: 'YAML', color: '#e67e22' },
+ // 压缩包
+ zip: { emoji: '📦', label: 'ZIP', color: '#f39c12' },
+ rar: { emoji: '📦', label: 'RAR', color: '#f39c12' },
+ '7z': { emoji: '📦', label: '7Z', color: '#f39c12' },
+ // 网页
+ html: { emoji: '🌐', label: '网页', color: '#e44d26' },
+ htm: { emoji: '🌐', label: '网页', color: '#e44d26' },
+ // 图片
+ jpg: { emoji: '🖼️', label: '图片', color: '#9b59b6' },
+ jpeg: { emoji: '🖼️', label: '图片', color: '#9b59b6' },
+ png: { emoji: '🖼️', label: '图片', color: '#9b59b6' },
+ gif: { emoji: '🖼️', label: 'GIF', color: '#9b59b6' },
+ bmp: { emoji: '🖼️', label: '图片', color: '#9b59b6' },
+ svg: { emoji: '🖼️', label: 'SVG', color: '#9b59b6' },
+ webp: { emoji: '🖼️', label: '图片', color: '#9b59b6' },
+ ico: { emoji: '🖼️', label: '图标', color: '#9b59b6' },
+ tiff: { emoji: '🖼️', label: '图片', color: '#9b59b6' },
+ tif: { emoji: '🖼️', label: '图片', color: '#9b59b6' },
+ // CAD/设计类
+ dxf: { emoji: '📐', label: 'CAD/DXF', color: '#2c3e50' },
+ }
+ return map[ext] || { emoji: '📎', label: ext.toUpperCase(), color: '#888' }
+ }
+
const showMessageDetail = (message) => {
detailLinks.value = extractLinks(message.content || '')
showMessageModal.value = true
@@ -932,12 +1068,8 @@
success: (downloadRes) => {
if (downloadRes.statusCode === 200) {
hideDownloadToast()
- uni.openDocument({
- filePath: downloadRes.tempFilePath,
- showMenu: true,
- fail: () => {
- showDownloadToast('error', '无法打开此文件', 2500)
- }
+ openFileNative(downloadRes.tempFilePath, () => {
+ showDownloadToast('error', '无法打开此文件', 2500)
})
} else {
showDownloadToast('error', '下载失败', 2500)
@@ -1185,14 +1317,16 @@
if (reason === 'pc offline') {
isThinking.value = false
socketStore.authFailReason = ''
- nextTick(() => {
- uni.showModal({
- title: '提示',
- content: 'PC端不在线,不可聊天',
- showCancel: false,
- confirmText: '知道了'
- })
+ uni.showToast({
+ title: 'PC端不在线,请先登录',
+ icon: 'none',
+ duration: 500
})
+ setTimeout(() => {
+ uni.reLaunch({
+ url: '/pages/Login/Login'
+ })
+ }, 500)
}
})
@@ -1387,7 +1521,8 @@
const takeConversationMessages = async () => {
try {
allmessages.value = await getConversationMessages(userToken.value, currentSessionId.value) || [];
- // console.log("获取到的所有消息:", JSON.stringify(allmessages.value) );
+ console.log("获取到的所有消息:", JSON.stringify(allmessages.value) );
+ console.log("获取到的所有消息:", allmessages.value);
// currentMessages.value = allmessages.value.slice(-pageInfoNumber);
// 数据获取后执行滚动
scrollToBottom();
@@ -1505,6 +1640,7 @@
if (oldId && oldId !== newId) {
isLoadingMessages.value = true
allmessages.value = []
+ hasWyxdFile.value = false
}
switch (ChatType.value) {
case 0:
@@ -1528,6 +1664,17 @@
immediate: true
})
+ // 接收 WYXD 参数(从工作区「AI修改」进入时由 navigateTo 传入)
+ onLoad((options) => {
+ if (options.wyxdFilePath) {
+ wyxdWorkspaceId.value = options.wyxdWorkspaceId ? decodeURIComponent(options.wyxdWorkspaceId) : ''
+ wyxdFilePath.value = decodeURIComponent(options.wyxdFilePath)
+ wyxdFileName.value = options.wyxdFileName ? decodeURIComponent(options.wyxdFileName) : '未命名文档'
+ wyxdUserToken.value = options.wyxdUserToken ? decodeURIComponent(options.wyxdUserToken) : ''
+ hasWyxdFile.value = true
+ }
+ })
+
onMounted(() => {
// currentSessionId.value = getCurrentSessionId()
})
@@ -1648,6 +1795,43 @@
text-decoration: underline;
word-break: break-all;
}
+
+ /* 可下载文件链接 → 按钮卡片样式 */
+ .chat-download-btn {
+ display: inline-flex !important;
+ align-items: center;
+ gap: 8rpx;
+ padding: 8rpx 14rpx;
+ margin: 4rpx 0;
+ background: #f5f7fb;
+ border: 2rpx solid #e4e8f0;
+ border-radius: 8rpx;
+ text-decoration: none !important;
+ color: #333 !important;
+ word-break: break-all;
+ transition: all 0.15s ease;
+
+ &:active {
+ background: #eef1f7;
+ border-color: #c8cde0;
+ transform: scale(0.98);
+ }
+
+ .chat-dl-icon {
+ font-size: 26rpx !important;
+ line-height: 1;
+ flex-shrink: 0;
+ }
+
+ .chat-dl-name {
+ font-size: 16px !important;
+ font-weight: 500;
+ color: #333;
+ overflow: hidden;
+ text-overflow: ellipsis;
+ white-space: nowrap;
+ }
+ }
}
/* 流式气泡中的加载动画 */
diff --git a/pages/CloudDatabase/CloudDatabase.vue b/pages/CloudDatabase/CloudDatabase.vue
index 030b8a0..057d889 100644
--- a/pages/CloudDatabase/CloudDatabase.vue
+++ b/pages/CloudDatabase/CloudDatabase.vue
@@ -475,6 +475,7 @@
uploadCloudDbFile,
deleteCloudDb
} from '@/utils/cloud-api.js';
+import { openFile as openFileNative } from '@/utils/fileOpener.js'
const userToken = ref('');
const userId = ref('');
@@ -826,19 +827,10 @@
}
};
- // 使用系统默认应用打开
+ // 打开文件(弹出应用选择菜单)
const openFile = (filePath) => {
- uni.openDocument({
- filePath: filePath,
- success: () => uni.showToast({
- title: '打开成功',
- icon: 'success'
- }),
- fail: (err) => {
- console.error('打开文件失败:', err);
- // 如果默认应用打开失败,尝试弹出选择菜单
- openFileWithMenu(filePath);
- }
+ openFileNative(filePath, () => {
+ openFileWithMenu(filePath);
});
};
diff --git a/pages/WYXD/WYXD.vue b/pages/WYXD/WYXD.vue
index 402eb4f..2f78bf9 100644
--- a/pages/WYXD/WYXD.vue
+++ b/pages/WYXD/WYXD.vue
@@ -32,9 +32,14 @@
-
-
-
+
+
+
+
+
+
+
@@ -89,10 +94,12 @@ const statusText = computed(() => {
const displayName = computed(() => fileName.value || '未命名文档')
-const currentSlide = computed(() => slides.value[currentIndex.value] || { html: '' })
-
const pad = (n) => String(n).padStart(2, '0')
+const onSwiperChange = (e) => {
+ currentIndex.value = e.detail.current
+}
+
const goBack = () => {
uni.navigateBack()
}
@@ -181,7 +188,8 @@ onLoad((options) => {
userToken: userToken.value
})
- if (isWyxdCached(workspaceId.value, filePath.value)) {
+ // forceDownload 强制重新下载不走缓存
+ if (options.forceDownload !== '1' && isWyxdCached(workspaceId.value, filePath.value)) {
loadFromCache()
} else {
startDownload()
@@ -247,6 +255,11 @@ onLoad((options) => {
/* padding: 20rpx; */
}
+.wyxd-swiper {
+ width: 100%;
+ height: 100%;
+}
+
.wyxd-slide-scroll {
height: 100%;
background: #fff;
diff --git a/pages/WorkSpace/WorkSpace.css b/pages/WorkSpace/WorkSpace.css
index 20ce011..8fd29d9 100644
--- a/pages/WorkSpace/WorkSpace.css
+++ b/pages/WorkSpace/WorkSpace.css
@@ -380,3 +380,164 @@
.workspace-footer .iconfont {
font-size: 40rpx !important;
}
+
+/* ===== 标签切换 ===== */
+.workspace-tabs {
+ display: flex;
+ width: 100%;
+ padding: 0 24rpx;
+ box-sizing: border-box;
+ gap: 20rpx;
+}
+
+.tab-item {
+ flex: 1;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ padding: 18rpx 0;
+ font-size: 28rpx;
+ font-weight: 500;
+ color: #999;
+ border-bottom: 4rpx solid transparent;
+ transition: all 0.2s ease;
+ position: relative;
+}
+
+.tab-item.active {
+ color: #6c63ff;
+ border-bottom-color: #6c63ff;
+}
+
+.tab-badge {
+ margin-left: 8rpx;
+ background: #ff5e57;
+ color: #fff;
+ font-size: 20rpx;
+ padding: 2rpx 10rpx;
+ border-radius: 20rpx;
+ line-height: 1.4;
+}
+
+/* ===== 下载列表 ===== */
+.download-content {
+ flex: 1;
+ overflow: auto;
+}
+
+.download-empty {
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: center;
+ padding-top: 200rpx;
+ color: #bbb;
+ font-size: 28rpx;
+}
+
+.download-empty .iconfont {
+ font-size: 140rpx;
+ color: #ddd;
+ margin-bottom: 20rpx;
+}
+
+.download-list {
+ padding: 20rpx 24rpx;
+}
+
+.download-header {
+ display: flex;
+ justify-content: space-between;
+ align-items: center;
+ padding: 12rpx 8rpx 20rpx;
+ font-size: 24rpx;
+ color: #999;
+}
+
+.clear-btn {
+ color: #ff5e57;
+ font-size: 24rpx;
+ font-weight: 500;
+ padding: 4rpx 12rpx;
+}
+
+.download-item {
+ display: flex;
+ align-items: center;
+ padding: 24rpx 20rpx;
+ background: rgba(255, 255, 255, 0.85);
+ border-radius: 20rpx;
+ margin-bottom: 16rpx;
+ transition: all 0.15s ease;
+}
+
+.download-item:active {
+ transform: scale(0.98);
+ background: rgba(255, 255, 255, 0.95);
+}
+
+.di-icon {
+ width: 80rpx;
+ height: 80rpx;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ background: rgba(108, 99, 255, 0.08);
+ border-radius: 18rpx;
+ margin-right: 20rpx;
+ flex-shrink: 0;
+}
+
+.di-icon .iconfont {
+ font-size: 40rpx;
+ color: #6c63ff;
+}
+
+.di-info {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ gap: 6rpx;
+ min-width: 0;
+}
+
+.di-name {
+ font-size: 28rpx;
+ font-weight: 500;
+ color: #333;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.di-meta {
+ font-size: 22rpx;
+ color: #999;
+}
+
+.di-path {
+ font-size: 20rpx;
+ color: #bbb;
+ white-space: nowrap;
+ overflow: hidden;
+ text-overflow: ellipsis;
+}
+
+.di-delete {
+ width: 56rpx;
+ height: 56rpx;
+ display: flex;
+ justify-content: center;
+ align-items: center;
+ flex-shrink: 0;
+}
+
+.di-delete .iconfont {
+ font-size: 36rpx;
+ color: #ccc;
+ transition: color 0.15s ease;
+}
+
+.di-delete:active .iconfont {
+ color: #ff5e57;
+}
diff --git a/pages/WorkSpace/WorkSpace.vue b/pages/WorkSpace/WorkSpace.vue
index 13b431b..f93d98a 100644
--- a/pages/WorkSpace/WorkSpace.vue
+++ b/pages/WorkSpace/WorkSpace.vue
@@ -66,8 +66,20 @@
取消
+
+
+
+ 工作区
+
+
+ 下载列表
+ {{ downloadList.length }}
+
+
-
+
+
+
文件夹为空
@@ -89,6 +101,34 @@
+
+
+
+
+
+ 暂无下载文件
+
+
+
+
+
+
+
+
+ {{ item.name }}
+ {{ formatDownloadTime(item.time) }} · {{ formatDownloadSize(item.size) }}
+ {{ item.displayPath || item.path }}
+
+
+
+
+
+
+
+