diff --git a/frontend/miniapp/pages/demand/contract/index.js b/frontend/miniapp/pages/demand/contract/index.js
index b1ad317..10c376a 100644
--- a/frontend/miniapp/pages/demand/contract/index.js
+++ b/frontend/miniapp/pages/demand/contract/index.js
@@ -240,6 +240,19 @@ Page({
});
},
+ // 取文件扩展名对应的 openDocument 类型(默认 pdf)
+ getFileType(url) {
+ const ext = String(url || '').split('?')[0].split('#')[0].split('.').pop().toLowerCase();
+ const support = ['doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'pdf'];
+ return support.indexOf(ext) > -1 ? ext : 'pdf';
+ },
+
+ // 取域名,用于下载失败时提示配置合法域名
+ getUrlHost(url) {
+ const match = String(url || '').match(/^https?:\/\/([^/:?#]+)/);
+ return match ? match[1] : '';
+ },
+
// 附件预览:客户点击开发者上传的附件进行查看
handlePreviewAttach(e) {
// 仅客户侧可预览;开发者侧附件可删除,避免删除时误触预览
@@ -260,16 +273,22 @@ Page({
return;
}
// 其他文件下载后打开
+ const fileType = this.getFileType(fileUrl);
+ // 指定带扩展名的保存路径:安卓/鸿蒙靠扩展名识别文件类型,默认 tempFilePath 无扩展名易失败
+ const filePath = `${wx.env.USER_DATA_PATH}/contract_file_${Date.now()}.${fileType}`;
wx.showLoading({ title: '加载中...' });
wx.downloadFile({
url: fileUrl,
+ filePath: filePath,
success: (res) => {
wx.hideLoading();
if (res.statusCode === 200) {
wx.openDocument({
- filePath: res.tempFilePath,
+ filePath: res.filePath || res.tempFilePath || filePath,
+ fileType: fileType,
showMenu: true,
- fail: () => {
+ fail: (err) => {
+ console.error('openDocument fail', err);
wx.showToast({ title: '该文件类型暂不支持预览', icon: 'none' });
}
});
@@ -277,9 +296,16 @@ Page({
wx.showToast({ title: '附件加载失败', icon: 'none' });
}
},
- fail: () => {
+ fail: (err) => {
wx.hideLoading();
- wx.showToast({ title: '附件加载失败', icon: 'none' });
+ console.error('downloadFile fail', err);
+ // 真机常见原因:downloadFile 合法域名未配置
+ const host = this.getUrlHost(fileUrl);
+ wx.showModal({
+ title: '预览失败',
+ showCancel: false,
+ content: '附件下载失败,请检查网络' + (host ? ';若为体验版/真机,请确认已配置 downloadFile 合法域名:' + host : '')
+ });
}
});
},
diff --git a/frontend/miniapp/pages/demand/detail/index.js b/frontend/miniapp/pages/demand/detail/index.js
index 6788168..c344926 100644
--- a/frontend/miniapp/pages/demand/detail/index.js
+++ b/frontend/miniapp/pages/demand/detail/index.js
@@ -12,7 +12,8 @@ Page({
remark: '',
actualAccount: 0,
activeValues: [0, 1, 2, 3, 4],
- demandInfo: null
+ demandInfo: null,
+ copyUrl: '' // 剪贴板复制失败时,弹出可长按选择的链接
},
/**
@@ -41,41 +42,131 @@ Page({
);
});
},
+ // 取文件扩展名对应的 openDocument 类型(默认 pdf)
+ getFileType(url) {
+ const ext = String(url || '').split('?')[0].split('#')[0].split('.').pop().toLowerCase();
+ const support = ['doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'pdf'];
+ return support.indexOf(ext) > -1 ? ext : 'pdf';
+ },
+
+ // 取域名,用于下载失败时提示配置合法域名
+ getUrlHost(url) {
+ const match = String(url || '').match(/^https?:\/\/([^/:?#]+)/);
+ return match ? match[1] : '';
+ },
+
//文件预览
fileView(e) {
const fileUrl = e.currentTarget.dataset.url;
+ if (!fileUrl) {
+ wx.showToast({
+ title: '文件链接为空',
+ icon: 'none'
+ });
+ return;
+ }
+ const fileType = this.getFileType(fileUrl);
+ // 指定带扩展名的保存路径:安卓/鸿蒙靠扩展名识别文件类型,
+ // downloadFile 默认的 tempFilePath 无扩展名,容易打开失败
+ const filePath = `${wx.env.USER_DATA_PATH}/demand_file_${Date.now()}.${fileType}`;
+ wx.showLoading({
+ title: '加载中...',
+ mask: true
+ });
wx.downloadFile({
url: fileUrl,
- success: function (res) {
- const filePath = res.tempFilePath
+ filePath: filePath,
+ success: (res) => {
+ wx.hideLoading();
+ if (res.statusCode !== 200) {
+ wx.showToast({
+ title: '文件加载失败(' + res.statusCode + ')',
+ icon: 'none'
+ });
+ return;
+ }
+ const openPath = res.filePath || res.tempFilePath || filePath;
+ console.log('openDocument', { openPath, fileType });
wx.openDocument({
- filePath: filePath,
- success: function (res) {
- console.log('打开文档成功')
+ filePath: openPath,
+ fileType: fileType,
+ showMenu: true, // 支持转发/用其他应用打开
+ success: () => {
+ console.log('打开文档成功');
+ },
+ fail: (err) => {
+ console.error('openDocument fail', err);
+ // 安卓/鸿蒙部分机型无法直接预览,降级为复制链接到浏览器打开
+ wx.showModal({
+ title: '预览失败',
+ content: '当前系统无法直接预览该文件,是否复制链接后在浏览器中打开?',
+ confirmText: '复制链接',
+ cancelText: '关闭',
+ success: (res2) => {
+ if (res2.confirm) {
+ this.copyUrlToClipboard(fileUrl);
+ }
+ }
+ });
}
- })
- }
- })
- },
- //文件复制
- fileUrlCopy(e) {
- const url = e.currentTarget.dataset.url;
- wx.setClipboardData({
- data: url,
- success: function () {
- wx.showToast({
- title: '下载链接已复制',
- icon: 'success'
});
},
- fail: function () {
- wx.showToast({
- title: '下载链接复制失败',
- icon: 'none'
+ fail: (err) => {
+ wx.hideLoading();
+ console.error('downloadFile fail', err);
+ // 真机/体验版常见原因:downloadFile 合法域名未配置(开发者工具可关闭校验,真机不行)
+ const host = this.getUrlHost(fileUrl);
+ wx.showModal({
+ title: '预览失败',
+ showCancel: false,
+ content: '文件下载失败(' + (err && err.errMsg ? err.errMsg : '未知错误') + '),请检查网络' + (host ? ';请确认已配置 downloadFile 合法域名:' + host : '')
});
}
});
},
+
+ // 复制链接(失败自动重试一次,仍失败则弹出可长按选择的链接供手动复制)
+ copyUrlToClipboard(url) {
+ if (!url) {
+ wx.showToast({
+ title: '文件链接为空',
+ icon: 'none'
+ });
+ return;
+ }
+ const doCopy = (retry) => {
+ wx.setClipboardData({
+ data: String(url),
+ success: () => {
+ wx.showToast({
+ title: '下载链接已复制',
+ icon: 'success'
+ });
+ },
+ fail: (err) => {
+ console.error('setClipboardData fail', err);
+ if (retry > 0) {
+ // 系统剪贴板偶发写入失败,短暂延迟后重试一次
+ setTimeout(() => doCopy(retry - 1), 200);
+ return;
+ }
+ // 降级:展示可长按选择的链接,用户手动复制
+ this.setData({ copyUrl: String(url) });
+ }
+ });
+ };
+ doCopy(1);
+ },
+
+ //文件复制
+ fileUrlCopy(e) {
+ this.copyUrlToClipboard(e.currentTarget.dataset.url);
+ },
+
+ // 关闭手动复制弹层
+ closeCopyModal() {
+ this.setData({ copyUrl: '' });
+ },
handleChange(e) {
console.log(e.detail.value);
this.setData({
diff --git a/frontend/miniapp/pages/demand/detail/index.wxml b/frontend/miniapp/pages/demand/detail/index.wxml
index 8ea14ef..6c80469 100644
--- a/frontend/miniapp/pages/demand/detail/index.wxml
+++ b/frontend/miniapp/pages/demand/detail/index.wxml
@@ -114,4 +114,14 @@
确认收货
待发货(等待开发者交付)
+
+
+
+
+
+ 自动复制失败,请长按下方链接手动复制
+ {{copyUrl}}
+ 可粘贴到手机浏览器打开下载
+ 关闭
+
\ No newline at end of file
diff --git a/frontend/miniapp/pages/demand/detail/index.wxss b/frontend/miniapp/pages/demand/detail/index.wxss
index a66971a..de9da7e 100644
--- a/frontend/miniapp/pages/demand/detail/index.wxss
+++ b/frontend/miniapp/pages/demand/detail/index.wxss
@@ -102,6 +102,58 @@ page {
word-break: break-all;
}
+/* 复制失败兜底弹层 */
+.copy-mask {
+ position: fixed;
+ left: 0;
+ right: 0;
+ top: 0;
+ bottom: 0;
+ z-index: 100;
+ background: rgba(0, 0, 0, 0.5);
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ padding: 48rpx;
+ box-sizing: border-box;
+}
+
+.copy-dialog {
+ width: 100%;
+ background: var(--bg-card);
+ border-radius: var(--radius-lg);
+ padding: 32rpx;
+ box-sizing: border-box;
+}
+
+.copy-title {
+ font-size: 28rpx;
+ font-weight: 600;
+ line-height: 44rpx;
+ color: var(--text-primary);
+ margin-bottom: 20rpx;
+}
+
+.copy-url {
+ display: block;
+ max-height: 320rpx;
+ overflow-y: auto;
+ padding: 20rpx;
+ background: var(--bg-grey);
+ border-radius: 12rpx;
+ font-size: 24rpx;
+ line-height: 36rpx;
+ color: var(--text-secondary);
+ word-break: break-all;
+}
+
+.copy-tip {
+ margin: 16rpx 0 24rpx;
+ font-size: 24rpx;
+ line-height: 36rpx;
+ color: var(--text-tertiary);
+}
+
.button-submit {
margin: 32rpx;
}