chore:打开PDF右边新加一个分享按钮
This commit is contained in:
@@ -185,6 +185,10 @@ const localDevConfig = ({
|
||||
domain: 'http://192.168.8.118:8050/',
|
||||
// domain: 'http://localhost:8050/',
|
||||
},
|
||||
'5g-59': { // 合成孔径雷达,一级分类背景字体颜色
|
||||
uniacid: 59,
|
||||
domain: 'https://5g.aigc-quickapp.com/',
|
||||
},
|
||||
// })['5g-237']; // 商品分类,没有立即咨询文字
|
||||
// })['5g-97']; // 大宗商品贸易,支持自定义字体大小、颜色
|
||||
// })['5g-50']; // 燃气安全产品机器人,购物车无反应
|
||||
@@ -198,7 +202,7 @@ const localDevConfig = ({
|
||||
// })['2724']; // 生物菌肥
|
||||
// })['2811']; // 选择要使用的环境配置
|
||||
// })['local-11']; // 选择要使用的环境配置
|
||||
})['2811']; // 本地
|
||||
})['5g-59']; // 本地
|
||||
// })['local-2']; // 本地
|
||||
// })['local-11']; // 本地
|
||||
// })['1-test']; // test
|
||||
|
||||
@@ -2445,7 +2445,8 @@
|
||||
{
|
||||
"path": "file-preview/file-preview",
|
||||
"style": {
|
||||
"navigationBarTitleText": "文件预览"
|
||||
"navigationBarTitleText": "文件预览",
|
||||
"navigationStyle": "custom"
|
||||
}
|
||||
}
|
||||
]
|
||||
|
||||
@@ -1,32 +1,59 @@
|
||||
<template>
|
||||
<view class="file-preview-container" :style="themeColor">
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<!-- 微信小程序:web-view 是原生组件,必须用 cover-view 盖在上面;cover-view 不支持 iconfont,改用 cover-image PNG -->
|
||||
<cover-view class="mp-header">
|
||||
<cover-view class="mp-header-inner">
|
||||
<cover-view class="nav-btn back-btn" @click="goBack">
|
||||
<cover-image src="/static/img/icon-back.png" class="nav-icon"></cover-image>
|
||||
</cover-view>
|
||||
<cover-view class="header-title">{{ headerTitle }}</cover-view>
|
||||
<button class="nav-btn share-btn" open-type="share" hover-class="none">
|
||||
<cover-image src="/static/img/icon-share.png" class="nav-icon"></cover-image>
|
||||
</button>
|
||||
</cover-view>
|
||||
<!-- 状态栏占位(navigationStyle custom 时,避免被时间/电量遮挡) -->
|
||||
<cover-view class="mp-status-bar" :style="{ height: statusBarHeight + 'px' }"></cover-view>
|
||||
</cover-view>
|
||||
<!-- #endif -->
|
||||
|
||||
<!-- #ifndef MP-WEIXIN -->
|
||||
<!-- 非小程序:普通 view + iconfont 即可 -->
|
||||
<view class="header">
|
||||
<view class="back-btn" @click="goBack">
|
||||
<view class="nav-btn back-btn" @click="goBack">
|
||||
<text class="iconfont icon-back"></text>
|
||||
</view>
|
||||
<view class="header-title">{{ fileName }}</view>
|
||||
<view class="placeholder"></view>
|
||||
<view class="header-title">{{ headerTitle }}</view>
|
||||
<button class="nav-btn share-btn" open-type="share" hover-class="none" @click.stop>
|
||||
<text class="iconfont icon-fenxiang"></text>
|
||||
</button>
|
||||
</view>
|
||||
|
||||
<!-- #endif -->
|
||||
|
||||
<!-- #ifdef MP-WEIXIN -->
|
||||
<!-- 微信小程序 web-view 是全屏原生组件,父容器 padding/margin 对它无效,必须在它前面放一个等高的占位 view 把它顶下去 -->
|
||||
<view class="mp-content-spacer" :style="{ height: headerTotalHeightPx + 'px' }"></view>
|
||||
<!-- #endif -->
|
||||
|
||||
<view class="content">
|
||||
<!-- PDF 文件预览 -->
|
||||
<view v-if="fileType === 'pdf'" class="file-viewer">
|
||||
<web-view v-if="fileUrl" :src="fileUrl"></web-view>
|
||||
<view v-else class="error-message">{{ $lang('file-preview.file-preview.fileUrlNotExist') }}</view>
|
||||
</view>
|
||||
|
||||
|
||||
<!-- Word 文件预览 -->
|
||||
<view v-else-if="fileType === 'word'" class="file-viewer">
|
||||
<web-view v-if="fileUrl" :src="fileUrl"></web-view>
|
||||
<view v-else class="error-message">{{ $lang('file-preview.file-preview.fileUrlNotExist') }}</view>
|
||||
</view>
|
||||
|
||||
|
||||
<!-- 视频文件预览 -->
|
||||
<view v-else-if="fileType === 'video'" class="video-viewer">
|
||||
<video v-if="fileUrl" :src="fileUrl" controls autoplay class="video-player"></video>
|
||||
<video v-if="fileUrl" id="filePreviewVideo" :src="fileUrl" controls class="video-player"></video>
|
||||
<view v-else class="error-message">{{ $lang('file-preview.file-preview.videoUrlNotExist') }}</view>
|
||||
</view>
|
||||
|
||||
|
||||
<!-- 其他文件类型 -->
|
||||
<view v-else class="file-viewer">
|
||||
<view class="error-message">{{ $lang('file-preview.file-preview.unsupportedFileType') }}</view>
|
||||
@@ -41,32 +68,114 @@ export default {
|
||||
return {
|
||||
fileName: '',
|
||||
fileUrl: '',
|
||||
fileType: ''
|
||||
fileType: '',
|
||||
statusBarHeight: 20,
|
||||
windowWidth: 375
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
// #ifdef MP-WEIXIN
|
||||
// 整个自定义 header 高度(状态栏 + 标题栏 88rpx),单位 px —— 用于占位 view 和内容区下移
|
||||
headerTotalHeightPx() {
|
||||
const rpxToPx = this.windowWidth / 750;
|
||||
return this.statusBarHeight + Math.round(88 * rpxToPx);
|
||||
},
|
||||
// #endif
|
||||
// 页面标题栏显示的短标题(避免超长挤压分享按钮;对用户无意义的tmp名统一显示通用"文件预览")
|
||||
headerTitle() {
|
||||
const name = (this.fileName || '').trim();
|
||||
// 如果是临时文件名(tmp_ 开头 或 带路径hash的长串),统一显示通用"文件预览"
|
||||
if (/^tmp_/i.test(name) || (name.length > 30 && /[a-f0-9]{20,}/i.test(name))) {
|
||||
return this.$lang('file-preview.file-preview.title');
|
||||
}
|
||||
// 否则显示原始文件名(列表里传入的,如"乐瑞固企业介绍",本身就短)
|
||||
if (name) {
|
||||
// JS 层保险截断(超过16字 + …),应对 cover-view CSS 省略号不生效
|
||||
if (name.length > 16) return name.slice(0, 16) + '…';
|
||||
return name;
|
||||
}
|
||||
return this.$lang('file-preview.file-preview.title');
|
||||
},
|
||||
// 分享的真实标题(好友收到的卡片显示真实文件名,不截断)
|
||||
shareTitle() {
|
||||
const name = (this.fileName || '').trim();
|
||||
if (name && !/^tmp_/i.test(name)) return name;
|
||||
return this.$lang('file-preview.file-preview.title');
|
||||
},
|
||||
// 分享路径:只带文件三要素,不挂推荐码/关系
|
||||
sharePath() {
|
||||
const query = [];
|
||||
if (this.fileName) query.push(`fileName=${encodeURIComponent(this.fileName)}`);
|
||||
if (this.fileUrl) query.push(`fileUrl=${encodeURIComponent(this.fileUrl)}`);
|
||||
if (this.fileType) query.push(`fileType=${encodeURIComponent(this.fileType)}`);
|
||||
return `/pages_tool/file-preview/file-preview${query.length ? '?' + query.join('&') : ''}`;
|
||||
},
|
||||
themeColor() {
|
||||
if (this.$shop && this.$shop.themeColor) {
|
||||
return `--theme-color: ${this.$shop.themeColor};`;
|
||||
}
|
||||
return '';
|
||||
}
|
||||
},
|
||||
onLoad(option) {
|
||||
// 解析传递的参数
|
||||
if (option.fileName) {
|
||||
this.fileName = decodeURIComponent(option.fileName);
|
||||
}
|
||||
|
||||
if (option.fileUrl) {
|
||||
this.fileUrl = decodeURIComponent(option.fileUrl);
|
||||
}
|
||||
|
||||
if (option.fileType) {
|
||||
this.fileType = decodeURIComponent(option.fileType);
|
||||
} else {
|
||||
// 根据文件名后缀推断文件类型
|
||||
this.inferFileType();
|
||||
}
|
||||
// #ifdef MP-WEIXIN
|
||||
const sys = uni.getSystemInfoSync();
|
||||
this.statusBarHeight = sys.statusBarHeight || 20;
|
||||
this.windowWidth = sys.windowWidth || 375;
|
||||
// #endif
|
||||
},
|
||||
onReady() {
|
||||
// 视频加载后手动触发一次播放占位(去掉 autoplay 防自动播放被系统拦截)
|
||||
// #ifndef H5
|
||||
if (this.fileType === 'video' && this.fileUrl) {
|
||||
try {
|
||||
const ctx = uni.createVideoContext('filePreviewVideo', this);
|
||||
if (ctx && typeof ctx.play === 'function') { /* 留给用户点击,不自动播 */ }
|
||||
} catch (e) {}
|
||||
}
|
||||
// #endif
|
||||
},
|
||||
// #ifdef MP-WEIXIN
|
||||
// 微信小程序:右上角胶囊"分享给朋友" / button[open-type=share] 触发
|
||||
onShareAppMessage() {
|
||||
return {
|
||||
title: this.shareTitle,
|
||||
path: this.sharePath
|
||||
};
|
||||
},
|
||||
// 微信小程序:分享到朋友圈
|
||||
onShareTimeline() {
|
||||
return {
|
||||
title: this.shareTitle,
|
||||
query: this.sharePath.split('?')[1] || ''
|
||||
};
|
||||
},
|
||||
// #endif
|
||||
methods: {
|
||||
/**
|
||||
* 根据文件名后缀推断文件类型
|
||||
*/
|
||||
inferFileType() {
|
||||
if (this.fileName) {
|
||||
if (this.fileUrl) {
|
||||
const clean = String(this.fileUrl).split('?')[0];
|
||||
const ext = clean.split('.').pop().toLowerCase();
|
||||
if (['pdf'].includes(ext)) {
|
||||
this.fileType = 'pdf';
|
||||
} else if (['doc', 'docx'].includes(ext)) {
|
||||
this.fileType = 'word';
|
||||
} else if (['mp4', 'avi', 'mov', 'wmv', 'flv', 'mkv'].includes(ext)) {
|
||||
this.fileType = 'video';
|
||||
}
|
||||
} else if (this.fileName) {
|
||||
const ext = this.fileName.split('.').pop().toLowerCase();
|
||||
if (['pdf'].includes(ext)) {
|
||||
this.fileType = 'pdf';
|
||||
@@ -77,12 +186,15 @@ export default {
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 返回上一页
|
||||
*/
|
||||
goBack() {
|
||||
uni.navigateBack();
|
||||
const pages = getCurrentPages();
|
||||
if (pages && pages.length > 1) {
|
||||
uni.navigateBack();
|
||||
} else {
|
||||
uni.switchTab({ url: '/pages/index/index' }).catch(() => {
|
||||
uni.navigateBack();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -92,7 +204,10 @@ export default {
|
||||
.file-preview-container {
|
||||
min-height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
/* =================== 非 MP 端 header =================== */
|
||||
.header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@@ -101,22 +216,34 @@ export default {
|
||||
padding: 0 30rpx;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
|
||||
|
||||
.back-btn {
|
||||
flex-shrink: 0;
|
||||
z-index: 10;
|
||||
|
||||
.nav-btn {
|
||||
flex-shrink: 0;
|
||||
width: 60rpx;
|
||||
height: 60rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
line-height: 1;
|
||||
border: none;
|
||||
margin: 0;
|
||||
&::after { border: none; }
|
||||
}
|
||||
|
||||
.back-btn {
|
||||
.icon-back {
|
||||
font-size: 32rpx;
|
||||
font-size: 36rpx;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.header-title {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
@@ -124,17 +251,91 @@ export default {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
margin: 0 20rpx;
|
||||
}
|
||||
|
||||
.placeholder {
|
||||
width: 60rpx;
|
||||
|
||||
.share-btn {
|
||||
.icon-fenxiang {
|
||||
font-size: 34rpx;
|
||||
color: #333;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* =================== MP 端 header (cover-view 浮层) =================== */
|
||||
/* #ifdef MP-WEIXIN */
|
||||
.mp-header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 9999;
|
||||
background-color: #ffffff;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
|
||||
|
||||
.mp-status-bar {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.mp-header-inner {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
height: 88rpx;
|
||||
padding: 0 20rpx;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.nav-btn {
|
||||
flex-shrink: 0;
|
||||
width: 64rpx;
|
||||
height: 64rpx;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 0;
|
||||
background: transparent;
|
||||
line-height: 1;
|
||||
border: none;
|
||||
margin: 0;
|
||||
&::after { border: none; }
|
||||
}
|
||||
|
||||
.nav-icon {
|
||||
width: 40rpx;
|
||||
height: 40rpx;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.header-title {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #333;
|
||||
text-align: center;
|
||||
margin: 0 16rpx;
|
||||
/* cover-view 下 ellipsis 支持有限,JS 层(headerTitle computed)已经做了截断,这里再冗余防守 */
|
||||
overflow: hidden;
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
/* #endif */
|
||||
|
||||
/* =================== 内容区 =================== */
|
||||
.content {
|
||||
flex: 1;
|
||||
padding: 20rpx;
|
||||
|
||||
/* #ifdef MP-WEIXIN */
|
||||
/* 给浮层 header(statusBar+88rpx)留位置,避免内容顶上去 */
|
||||
padding-top: calc(var(--mp-header-h, 120rpx));
|
||||
/* #endif */
|
||||
padding-left: 20rpx;
|
||||
padding-right: 20rpx;
|
||||
padding-bottom: 20rpx;
|
||||
box-sizing: border-box;
|
||||
|
||||
.file-viewer {
|
||||
width: 100%;
|
||||
min-height: 80vh;
|
||||
@@ -142,7 +343,7 @@ export default {
|
||||
border-radius: 12rpx;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
|
||||
|
||||
|
||||
.error-message {
|
||||
width: 100%;
|
||||
height: 80vh;
|
||||
@@ -153,7 +354,7 @@ export default {
|
||||
font-size: 28rpx;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
.video-viewer {
|
||||
width: 100%;
|
||||
min-height: 80vh;
|
||||
@@ -161,12 +362,12 @@ export default {
|
||||
border-radius: 12rpx;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
|
||||
|
||||
|
||||
.video-player {
|
||||
width: 100%;
|
||||
height: 80vh;
|
||||
}
|
||||
|
||||
|
||||
.error-message {
|
||||
width: 100%;
|
||||
height: 80vh;
|
||||
|
||||
@@ -115,41 +115,79 @@ export default {
|
||||
})
|
||||
},
|
||||
toDetail(item) {
|
||||
uni.showLoading({
|
||||
title: this.$lang('files.list.loading')
|
||||
})
|
||||
try {
|
||||
uni.downloadFile({
|
||||
url: item.files_url,
|
||||
success: res => {
|
||||
if (res.statusCode === 200) {
|
||||
uni.openDocument({
|
||||
filePath: res.tempFilePath,
|
||||
success: () => {
|
||||
},
|
||||
fail: () => {
|
||||
},
|
||||
complete: () => {
|
||||
uni.hideLoading()
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
fail: () => {
|
||||
uni.hideLoading()
|
||||
},
|
||||
complete: res => {
|
||||
if (res.statusCode === 404) {
|
||||
uni.showToast({
|
||||
title: this.$lang('files.list.fileNotExist'),
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
} catch (e) {
|
||||
uni.hideLoading()
|
||||
const title = (item.files_title || '文件').toString().replace(/[\\/:*?"<>|\r\n\t]/g, '_');
|
||||
// 根据 URL 推断后缀(若无扩展名则默认 pdf)
|
||||
let ext = 'pdf';
|
||||
if (item.files_url) {
|
||||
const clean = item.files_url.split('?')[0];
|
||||
const matched = clean.match(/\.([a-zA-Z0-9]+)$/);
|
||||
if (matched) ext = matched[1].toLowerCase();
|
||||
}
|
||||
uni.showLoading({ title: this.$lang('files.list.loading'), mask: true });
|
||||
uni.downloadFile({
|
||||
url: item.files_url,
|
||||
success: res => {
|
||||
if (res.statusCode !== 200) {
|
||||
uni.hideLoading();
|
||||
uni.showToast({ title: this.$lang('files.list.fileNotExist'), icon: 'none' });
|
||||
return;
|
||||
}
|
||||
const fs = uni.getFileSystemManager();
|
||||
// 重命名临时文件到用户目录,用短文件名 —— openDocument 的标题会取 filePath 的文件名部分,从而显示短名"乐瑞固企业介绍"而非 tmp_xxx
|
||||
const targetPath = `${wx.env.USER_DATA_PATH}/fs_${Date.now()}_${title}.${ext}`;
|
||||
try {
|
||||
// saveFile:复制临时文件到持久目录并改名(临时文件在某些基础库上禁止直接 rename)
|
||||
fs.saveFile({
|
||||
tempFilePath: res.tempFilePath,
|
||||
filePath: targetPath,
|
||||
success: r => {
|
||||
uni.hideLoading();
|
||||
uni.openDocument({
|
||||
filePath: r.savedFilePath,
|
||||
fileType: ext,
|
||||
showMenu: true,
|
||||
fail: () => {
|
||||
uni.showToast({ title: this.$lang('files.list.fileNotExist'), icon: 'none' });
|
||||
},
|
||||
complete: () => {
|
||||
// 预览结束后清理,避免占用持久空间(失败忽略)
|
||||
try { fs.removeSavedFile({ filePath: r.savedFilePath }); } catch (e) {}
|
||||
}
|
||||
});
|
||||
},
|
||||
fail: () => {
|
||||
// saveFile 失败(配额等),直接用临时文件 openDocument 兜底
|
||||
uni.hideLoading();
|
||||
uni.openDocument({
|
||||
filePath: res.tempFilePath,
|
||||
fileType: ext,
|
||||
showMenu: true,
|
||||
fail: () => uni.showToast({ title: this.$lang('files.list.fileNotExist'), icon: 'none' })
|
||||
});
|
||||
}
|
||||
});
|
||||
} catch (e) {
|
||||
// 极端异常兜底
|
||||
uni.hideLoading();
|
||||
uni.openDocument({
|
||||
filePath: res.tempFilePath,
|
||||
fileType: ext,
|
||||
showMenu: true,
|
||||
fail: () => uni.showToast({ title: this.$lang('files.list.fileNotExist'), icon: 'none' })
|
||||
});
|
||||
}
|
||||
},
|
||||
fail: () => {
|
||||
uni.hideLoading();
|
||||
uni.showToast({ title: this.$lang('files.list.fileNotExist'), icon: 'none' });
|
||||
},
|
||||
complete: res => {
|
||||
if (res && res.statusCode === 404) {
|
||||
uni.hideLoading();
|
||||
uni.showToast({ title: this.$lang('files.list.fileNotExist'), icon: 'none' });
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
imgError(index) {
|
||||
if (this.list[index]) {
|
||||
|
||||
Reference in New Issue
Block a user