diff --git a/common/js/util.js b/common/js/util.js index 9405da8..4ba077c 100644 --- a/common/js/util.js +++ b/common/js/util.js @@ -1254,6 +1254,21 @@ export default { link.wap_url = resolvedUrl; } + // ===== 仅微信小程序: 装修"文件"链接直开, 不进 files/detail 中间页 ===== + // 命中: page_key=PAGE_FILES_DETAIL / name=FILES_xx / wap_url 指向 files/detail + // 且能提取到 files_id。拉取或打开失败时自动降级回原详情页(openDiyFileDirect 内部处理)。 + // #ifdef MP-WEIXIN + if (link && (link.page_key === 'PAGE_FILES_DETAIL' + || (link.name && /^FILES_\d+$/i.test(String(link.name))) + || String(link.wap_url || '').indexOf('files/detail') !== -1)) { + const filesId = this._extractFilesIdFromLink(link); + if (filesId) { + this.openDiyFileDirect(link, filesId); + return; + } + } + // #endif + // 修复: 内部页面被后台存成【绝对 URL】(如 https://host/hwappx/common/pages_tool/form/formdata) // 时,下方 `indexOf('http') != -1` 会把它误判为"外部链接": // - H5 走 window.location.href → 整页刷新,点返回时整个 App 重启、启动屏重播、状态/滚动丢失 @@ -1427,6 +1442,123 @@ export default { }); } }, + /** + * 从装修"文件"链接中提取 files_id(仅微信小程序直开链路使用)。 + * 兼容三种位置: 顶层字段(files_id/file_id/id) / page_params(JSON字符串或对象) / wap_url query。 + * @returns {String} 拿不到返回 '' + */ + _extractFilesIdFromLink(link) { + if (!link) return ''; + let id = link.files_id || link.file_id || link.id || ''; + if (!id) { + const params = _parsePageParams(link.page_params); + id = params.files_id || params.file_id || params.id || ''; + } + if (!id) { + const url = String(link.wap_url || ''); + const qIdx = url.indexOf('?'); + if (qIdx !== -1) { + url.slice(qIdx + 1).split('&').forEach(kv => { + if (id) return; + const eq = kv.indexOf('='); + const k = eq === -1 ? kv : kv.slice(0, eq); + if (k === 'files_id' || k === 'file_id' || k === 'id') { + id = eq === -1 ? '' : kv.slice(eq + 1); + } + }); + } + } + return id === '' || id === undefined || id === null ? '' : String(id); + }, + /** + * 微信小程序: 装修"文件"链接直开, 不进 files/detail 中间页。 + * 链路: /api/files/getfile(files_url 已由后端 img() 补全域名) + * → downloadFile → saveFile 用文件名重命名 → openDocument(原生 PDF 阅读器) + * 任一环节失败 → 降级跳原详情页(files/detail), 保持旧体验兜底。 + * @param {Object} link 装修链接对象(用于取标题) + * @param {String} filesId 文件 id + */ + openDiyFileDirect(link, filesId) { + const fallback = () => { + this.redirectTo(PAGE_KEY_MAP.PAGE_FILES_DETAIL, { + files_id: filesId, + title: link.title || '', + enable_set_title: link.enable_set_title ? 1 : 0, + page_params: Object.assign({}, _parsePageParams(link.page_params), { files_id: filesId }) + }); + }; + const safeTitle = String(link.title || '').replace(/[\\/:*?"<>|\r\n\t]/g, '_'); + Http.sendRequest({ + url: '/api/files/getfile', + data: { files_id: filesId }, + noRetry: true, + silentError: true, + success: res => { + if (!res || res.code !== 0 || !res.data || !res.data.files_url) { + fallback(); + return; + } + let url = String(res.data.files_url); + // 相对路径补全域名($util.img 对含逗号路径会按多图截断, 规避) + if (!/^https?:\/\//i.test(url) && url.indexOf(',') === -1) { + url = this.img(url); + } + if (!/^https?:\/\//i.test(url)) { + fallback(); + return; + } + const rawTitle = String(res.data.files_title || safeTitle || 'file'); + const title = rawTitle.replace(/[\\/:*?"<>|\r\n\t]/g, '_'); + const matched = url.split('?')[0].match(/\.([a-zA-Z0-9]+)$/); + const ext = matched ? matched[1].toLowerCase() : 'pdf'; + + uni.showLoading({ title: Lang.lang('files.list.loading') || 'Loading', mask: true }); + uni.downloadFile({ + url, + success: dres => { + if (dres.statusCode !== 200) { + uni.hideLoading(); + fallback(); + return; + } + const fs = uni.getFileSystemManager(); + const targetPath = `${wx.env.USER_DATA_PATH}/fs_${Date.now()}_${title}.${ext}`; + fs.saveFile({ + tempFilePath: dres.tempFilePath, + filePath: targetPath, + success: r => { + uni.hideLoading(); + uni.openDocument({ + filePath: r.savedFilePath, + fileType: ext, + showMenu: true, + fail: () => fallback(), + complete: () => { + try { fs.removeSavedFile({ filePath: r.savedFilePath }); } catch (e) { } + } + }); + }, + fail: () => { + // saveFile 失败(配额等), 直接用临时文件打开兜底 + uni.hideLoading(); + uni.openDocument({ + filePath: dres.tempFilePath, + fileType: ext, + showMenu: true, + fail: () => fallback() + }); + } + }); + }, + fail: () => { + uni.hideLoading(); + fallback(); + } + }); + }, + fail: () => fallback() + }); + }, /** * 获取默认图 */