<template>
<view style="width: 100%;height: 100%;background-color: white;display: flex;justify-content: center;">
<view :style="'width: 96%;margin-top: '+statusBarHeight+'px;'">
<!-- 标题栏 -->
<view style="height: 40px;display: flex;align-items: center;justify-content: center;">
<u-icon @click="goBack" name="arrow-left" color="black" size="19" :bold="true"
style="position: absolute;left: 13px;"></u-icon>
<span style="font-size: 18px;">产品详情</span>
</view>
<u-gap height="10px"></u-gap>
<view style="display: flex; flex-direction: column; width: 100%;">
<view>
<u-swiper :list="list2" keyName="image" showTitle img-mode="aspectFit" :autoplay="false" circular height="200px"
style="width: 93%;padding: 0% 3%;">
</u-swiper>
</view>
<u-gap height="15px"></u-gap>
<view style="width: 100%;">
<u-tabs :list="list1" :activeStyle="activeTabStyle" :inactiveStyle="inactiveTabStyle"
lineColor="#16A1BA" :current="tabCurrent" @change="tabsChange"></u-tabs>
</view>
</view>
<scroll-view scroll-y="true" scroll-x="true" :style="'height:' + scrollViewHeight +'px;'">
<view v-if="tabCurrent === 0">
<view v-html="introduce" class="rich-text-container"></view>
</view>
<view v-if="tabCurrent === 1">
<view v-html="parameter" class="rich-text-container"></view>
</view>
<view v-if="tabCurrent === 2">
<view v-for="(item, index) in documentList" :key="index"
style="display: flex;justify-content: center;align-items: center;margin-top: 4%;">
<view v-if="item.documentType === 'pdf'"><u-image width="50px" height="50px"
src="../../static/images/pdf.png"></u-image></view>
<view v-if="item.documentType === 'doc' || item.documentType === 'docx'"><u-image width="50px" height="50px"
src="../../static/images/word.jpg"></u-image></view>
<view v-if="item.documentType === 'xlsx' || item.documentType === 'xls'"><u-image width="50px" height="50px"
src="../../static/images/excel.jpg"></u-image></view>
<view v-if="item.documentType === 'ppt' || item.documentType === 'pptx'"><u-image width="50px" height="50px"
src="../../static/images/ppt.webp"></u-image></view>
<view @click="downloadFile(item)" style="margin-left: 2%;font-size: 16px;color: #229EC7;text-decoration: underline;">
{{item.documentName || item.documentUrl.split('/').pop()}}
<text style="font-size:12px;color:#999;">(点击下载)</text>
</view>
</view>
</view>
<view v-if="tabCurrent === 3">
<view v-for="(item, index) in videoList" :key="index"
style="margin-top: 10px;padding: 10px;background-color: #f5f5f5;border-radius: 5px;">
<view style="font-size: 16px;margin-bottom: 5px;">视频 {{index + 1}}</view>
<view @click="downloadVideo(item)" style="color: #229EC7;text-decoration: underline;">
点击下载视频 ({{item.documentName || item.videoUrl.split('/').pop()}})
</view>
<view v-if="item.error" style="color: red;font-size: 12px;margin-top: 5px;">
{{ item.error }}
</view>
</view>
</view>
</scroll-view>
</view>
</view>
</template>
<script>
import { request } from "../../common/requset.js"
import { systemInfo } from "../../mixin.js"
export default {
mixins: [systemInfo],
data() {
return {
currentNum: 0,
tabCurrent: 0,
list1: [
{ name: '设备介绍' },
{ name: '设备参数' },
{ name: '文档' },
{ name: '视频' }
],
list2: [],
productForm: [],
activeTabStyle: {
color: '#229EC7',
},
inactiveTabStyle: {
color: '#929FB5',
},
productDeivece: {
name: '',
},
path: process.env.VUE_APP_BASE_API || '', // 兼容未配置环境变量的情况
statusBarHeight: 0,
windowHeight: 0,
introduce: '',
parameter: '',
documentList: [],
videoList: [],
scrollViewHeight: 0,
}
},
onLoad() {
this.productDeivece.name = uni.getStorageSync("productDevice");
this.getProductCurrent();
this.statusBarHeight = getApp().globalData.statusBarHeight;
this.windowHeight = uni.getSystemInfoSync().windowHeight;
// 计算滚动区域高度(兼容不同设备)
this.scrollViewHeight = this.windowHeight - this.statusBarHeight - 265 - (uni.getSystemInfoSync().platform === 'h5' ? 0 : 44);
// 调试日志:检查基础URL
console.log('当前基础URL:', this.path);
},
methods: {
// 视频下载方法(优化版)
async downloadVideo(item) {
try {
this.$set(item, 'error', null);
const videoUrl = this.processVideoUrl(item.videoUrl);
// 调试日志:输出完整视频URL
console.log('尝试下载视频URL:', videoUrl);
// 检查URL有效性
if (!videoUrl) throw new Error('视频URL为空');
if (!await this.checkUrlAvailable(videoUrl)) {
throw new Error('视频资源不存在(404)');
}
// 检查存储权限
const hasPermission = await this.checkStoragePermission();
if (!hasPermission) {
// Android 10+ 特殊处理
if (uni.getSystemInfoSync().platform === 'android' && uni.getSystemInfoSync().sdkVersion >= '29') {
await this.saveVideoToAlbumAndroid10Plus(videoUrl, item);
return;
}
throw new Error('无存储权限,请前往设置开启');
}
uni.showLoading({ title: '准备下载...', mask: true });
// 开始下载
const downloadResult = await this.startDownload(videoUrl);
await this.saveVideoToAlbum(downloadResult.tempFilePath, item);
uni.showToast({
title: '视频已保存到相册',
icon: 'success',
duration: 2000
});
} catch (error) {
console.error('视频下载失败:', error);
this.$set(item, 'error', this.getErrorMessage(error));
uni.showToast({
title: this.getErrorMessage(error),
icon: 'none'
});
} finally {
uni.hideLoading();
}
},
// 处理视频URL(兼容相对路径和绝对路径)
processVideoUrl(rawUrl) {
if (!rawUrl) return '';
// 处理以//开头的URL
if (rawUrl.startsWith('//')) {
return `https:${rawUrl}`;
}
// 处理相对路径
if (!rawUrl.startsWith('http')) {
return `${this.path}${rawUrl.startsWith('/') ? rawUrl : '/' + rawUrl}`;
}
return rawUrl;
},
// 检查URL是否可访问(HEAD请求)
checkUrlAvailable(url) {
return new Promise((resolve) => {
uni.request({
url,
method: 'HEAD',
timeout: 5000,
success: (res) => resolve(res.statusCode === 200),
fail: () => resolve(false)
});
});
},
// 通用下载方法(带进度提示)
startDownload(url) {
return new Promise((resolve, reject) => {
const downloadTask = uni.downloadFile({
url,
timeout: 45000,
success: (res) => {
if (res.statusCode === 200) {
resolve(res);
} else {
reject(new Error(`服务器响应异常[${res.statusCode}]`));
}
},
fail: (err) => {
reject(new Error(`下载失败: ${err.errMsg}`));
}
});
// 监听下载进度
downloadTask.onProgressUpdate((res) => {
uni.showLoading({
title: `下载中 ${res.progress}%`,
mask: true
});
});
});
},
// 保存视频到相册(通用方法)
saveVideoToAlbum(filePath, item) {
return new Promise((resolve, reject) => {
uni.saveVideoToPhotosAlbum({
filePath,
success: resolve,
fail: (err) => {
console.error('保存到相册失败:', err);
// 尝试保存到下载目录(Android专用)
if (uni.getSystemInfoSync().platform === 'android') {
this.saveToDownloadsAndroid(filePath, `video_${Date.now()}.mp4`)
.then(resolve)
.catch(reject);
} else {
reject(new Error('保存失败,请检查存储权限'));
}
}
});
});
},
// Android专用:保存到下载目录(适配Android 10+)
saveToDownloadsAndroid(filePath, fileName) {
return new Promise((resolve, reject) => {
if (plus.os.name.toLowerCase() !== 'android') {
return reject(new Error('仅Android支持此方法'));
}
try {
const dtask = uni.downloadFile({
url: filePath, // 这里需要重新下载以确保获取临时文件
success: (res) => {
if (res.statusCode !== 200) {
return reject(new Error(`下载失败,状态码:${res.statusCode}`));
}
// 使用MediaStore插入到下载目录
const Context = plus.android.importClass("android.content.Context");
const ContentResolver = plus.android.importClass("android.content.ContentResolver");
const ContentValues = plus.android.importClass("android.content.ContentValues");
const Environment = plus.android.importClass("android.os.Environment");
const MediaStore = plus.android.importClass("android.provider.MediaStore");
const resolver = plus.android.runtimeMainActivity().getContentResolver();
const values = new ContentValues();
values.put(MediaStore.Video.Media.DISPLAY_NAME, fileName);
values.put(MediaStore.Video.Media.MIME_TYPE, "video/mp4");
values.put(MediaStore.Video.Media.RELATIVE_PATH, Environment.DIRECTORY_DOWNLOADS + "/");
const uri = resolver.insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, values);
if (uri) {
const os = resolver.openOutputStream(uri);
const file = new plus.android.bridges.File(res.tempFilePath);
const input = new plus.android.bridges.FileInputStream(file);
const buffer = new ArrayBuffer(1024);
let length;
while ((length = input.read(buffer)) > 0) {
os.write(buffer, 0, length);
}
os.flush();
os.close();
input.close();
resolve();
} else {
reject(new Error('无法创建媒体存储条目'));
}
},
fail: (err) => reject(new Error(`重新下载失败: ${err.errMsg}`))
});
dtask.onProgressUpdate(() => {}); // 静默处理进度
} catch (error) {
reject(error);
}
});
},
// 文档下载方法(优化版)
downloadFile(item) {
try {
let fileUrl = item.documentUrl;
// 处理URL格式
if (!fileUrl.startsWith('http')) {
fileUrl = this.processVideoUrl(fileUrl); // 复用视频URL处理逻辑
}
const fileName = item.documentName || fileUrl.split('/').pop() || '未命名文件';
uni.showLoading({
title: '准备下载...',
mask: true
});
// 先检查文件是否存在
this.checkUrlAvailable(fileUrl).then(async (exists) => {
if (!exists) {
throw new Error('文件资源不存在(404)');
}
// 开始下载
uni.downloadFile({
url: fileUrl,
success: (downloadResult) => {
uni.hideLoading();
if (downloadResult.statusCode === 200) {
// 尝试直接打开
uni.openDocument({
filePath: downloadResult.tempFilePath,
fileType: this.getFileType(fileName),
showMenu: true,
success: () => {
console.log('文档打开成功');
},
fail: (err) => {
console.error('打开文档失败:', err);
// 打开失败则保存到下载目录
this.saveFileToDownloads(downloadResult.tempFilePath, fileName)
.then(() => {
uni.showToast({
title: '文件已保存到下载目录',
icon: 'success'
});
})
.catch(() => {
uni.showToast({
title: '保存失败,请重试',
icon: 'none'
});
});
}
});
} else {
uni.hideLoading();
uni.showToast({
title: `下载失败(状态码:${downloadResult.statusCode})`,
icon: 'none'
});
}
},
fail: (err) => {
uni.hideLoading();
console.error('下载失败:', err);
uni.showToast({
title: `下载失败:${err.errMsg}`,
icon: 'none'
});
}
});
}).catch((error) => {
uni.hideLoading();
uni.showToast({
title: error.message,
icon: 'none'
});
});
} catch (error) {
uni.showToast({
title: `下载异常:${error.message}`,
icon: 'none'
});
}
},
// 保存文件到下载目录(通用方法)
saveFileToDownloads(tempPath, fileName) {
return new Promise((resolve, reject) => {
if (uni.getSystemInfoSync().platform === 'android') {
// Android使用plus API保存
const Context = plus.android.importClass("android.content.Context");
const ContentResolver = plus.android.importClass("android.content.ContentResolver");
const ContentValues = plus.android.importClass("android.content.ContentValues");
const Environment = plus.android.importClass("android.os.Environment");
const resolver = plus.android.runtimeMainActivity().getContentResolver();
const values = new ContentValues();
values.put(ContentResolver.COLUMN_DISPLAY_NAME, fileName);
values.put(ContentResolver.COLUMN_MIME_TYPE, "*/*");
values.put(Environment.DIRECTORY_DOWNLOADS, Environment.DIRECTORY_DOWNLOADS);
const uri = resolver.insert(ContentResolver.URI_CONTENT_DOCUMENTS_URI, values);
if (uri) {
const os = resolver.openOutputStream(uri);
const file = new plus.android.bridges.File(tempPath);
const input = new plus.android.bridges.FileInputStream(file);
const buffer = new ArrayBuffer(1024);
let length;
while ((length = input.read(buffer)) > 0) {
os.write(buffer, 0, length);
}
os.flush();
os.close();
input.close();
resolve();
} else {
reject(new Error('无法创建下载条目'));
}
} else {
// iOS使用系统API保存
uni.saveFile({
tempFilePath,
success: (res) => {
uni.showToast({
title: '文件已保存',
icon: 'success'
});
resolve();
},
fail: reject
});
}
});
},
// 获取文件类型
getFileType(fileName) {
const ext = fileName.split('.').pop().toLowerCase();
switch(ext) {
case 'pdf': return 'pdf';
case 'doc':
case 'docx': return 'doc';
case 'xls':
case 'xlsx': return 'xls';
case 'ppt':
case 'pptx': return 'ppt';
case 'zip':
case 'rar': return 'zip';
case 'jpg':
case 'jpeg':
case 'png':
case 'gif': return 'image';
default: return 'unknown';
}
},
// 格式化富文本(增强版)
formatRichText(html) {
if (!html) return '';
let content = html
// 修复图片样式
.replace(/<img[^>]*>/gi, (match) => {
return match
.replace(/(style|width|height)="[^"]*"/gi, '')
.replace(/<img/, '<img style="max-width:100% !important;height:auto !important;display:block;margin:10px auto;border-radius:4px;"');
})
// 修复视频/iframe样式
.replace(/<iframe[^>]*>/gi, '<iframe style="width:100%;height:200px;margin:10px 0;border:none;border-radius:4px;" allowfullscreen>')
// 修复段落样式
.replace(/<p[^>]*>/gi, '<p style="margin:10px 0;line-height:1.6;text-align:justify;font-size:16px;color:#333;">');
return content;
},
// 获取产品详情
getProductCurrent() {
request('/labdev/product/list0', this.productDeivece,
res => {
if (res.data.code !== 200) {
uni.showToast({
mask: true,
icon: 'none',
title: '查询失败:' + (res.data.msg || '未知错误')
});
return;
}
this.productForm = res.data.rows;
if (!this.productForm || this.productForm.length === 0) {
uni.showToast({
title: '未找到产品信息',
icon: 'none'
});
return;
}
// 处理文档列表
const docStr = this.productForm[0].document;
let documentList = [];
if (docStr) {
documentList = docStr.split(',').map(item => {
if (!item) return null;
const type = item.split('.').pop().toLowerCase();
return {
documentType: type,
documentUrl: this.processVideoUrl(item), // 复用URL处理逻辑
documentName: item.split('/').pop() || '未命名文档'
};
}).filter(item => item !== null); // 过滤空值
}
this.documentList = documentList;
// 处理视频列表
const videoUrl = this.productForm[0].video;
if (videoUrl) {
this.videoList = [{
videoUrl: this.processVideoUrl(videoUrl),
documentName: videoUrl.split('/').pop() || '未命名视频',
error: null
}];
} else {
this.videoList = [];
}
// 处理轮播图
this.getPicture();
// 处理富文本内容
this.introduce = this.formatRichText(this.productForm[0].introduce || '');
this.parameter = this.formatRichText(this.productForm[0].parameter || '');
},
err => {
uni.showToast({
mask: true,
icon: 'none',
title: '查询信息获取失败:' + (err.message || '未知错误')
});
},
'GET'
)
},
// 其他原有方法(略作调整)
async saveVideoToAlbumAndroid10Plus(videoUrl, item) {
try {
const fileName = item.documentName || videoUrl.split('/').pop();
// 使用plus API处理高版本Android
if (uni.getSystemInfoSync().platform === 'android') {
const dtask = uni.downloadFile({
url: videoUrl,
success: (res) => {
if (res.statusCode === 200) {
// 使用MediaStore插入视频
const Context = plus.android.importClass("android.content.Context");
const ContentResolver = plus.android.importClass("android.content.ContentResolver");
const ContentValues = plus.android.importClass("android.content.ContentValues");
const Environment = plus.android.importClass("android.os.Environment");
const MediaStore = plus.android.importClass("android.provider.MediaStore");
const resolver = plus.android.runtimeMainActivity().getContentResolver();
const values = new ContentValues();
values.put(MediaStore.Video.Media.DISPLAY_NAME, fileName);
values.put(MediaStore.Video.Media.MIME_TYPE, "video/mp4");
values.put(MediaStore.Video.Media.RELATIVE_PATH, Environment.DIRECTORY_MOVIES + "/Download/");
const uri = resolver.insert(MediaStore.Video.Media.EXTERNAL_CONTENT_URI, values);
if (uri) {
const os = resolver.openOutputStream(uri);
const file = new plus.android.bridges.File(res.tempFilePath);
const input = new plus.android.bridges.FileInputStream(file);
const buffer = new ArrayBuffer(1024);
let length;
while ((length = input.read(buffer)) > 0) {
os.write(buffer, 0, length);
}
os.flush();
os.close();
input.close();
uni.showToast({
title: '视频已保存到相册',
icon: 'success'
});
} else {
throw new Error('无法创建媒体存储条目');
}
} else {
throw new Error(`下载失败,状态码: ${res.statusCode}`);
}
},
fail: (err) => {
throw new Error(`下载失败: ${err.errMsg}`);
}
});
// 监听下载进度
dtask.onProgressUpdate((res) => {
uni.showLoading({
title: `下载中 ${res.progress}%`,
mask: true
});
});
} else {
throw new Error('当前平台不支持此方法');
}
} catch (error) {
console.error('保存视频失败:', error);
throw error;
}
},
// 其他工具方法(保持原有逻辑)
async checkStoragePermission() {
try {
if (uni.getSystemInfoSync().platform === 'android') {
const result = await uni.getSetting();
if (!result.authSetting['scope.writePhotosAlbum']) {
const res = await uni.authorize({
scope: 'scope.writePhotosAlbum'
});
return !!res;
}
return true;
}
return true;
} catch (error) {
console.error('权限检查失败:', error);
return false;
}
},
getErrorMessage(error) {
if (error.message.includes('404')) return '资源不存在';
if (error.message.includes('timeout')) return '下载超时,请重试';
if (error.message.includes('permission')) return '无存储权限,请在设置中开启';
if (error.message.includes('HEAD')) return '无法连接到服务器';
return '下载失败,请稍后重试';
},
tabsChange(index) {
this.tabCurrent = index.index;
},
goBack() {
uni.removeStorageSync("productDeivce");
uni.navigateBack({
delta: 1
});
},
getPicture() {
let matches = this.productForm[0]?.picture?.match(/data:image\/[^;]+;base64,[^,]+/g) || [];
this.list2 = matches;
}
}
}
</script>
<style scoped>
page {
background-size: cover;
background-repeat: no-repeat;
background-attachment: fixed;
position: fixed;
left: 0;
top: 0;
right: 0;
bottom: 0;
}
.rich-text-container {
width: 100%;
padding: 0 10px;
box-sizing: border-box;
overflow: hidden;
word-wrap: break-word;
}
.rich-text-container img {
max-width: 100% !important;
height: auto !important;
display: block;
margin: 10px auto;
border-radius: 4px;
}
.rich-text-container p {
margin: 10px 0;
line-height: 1.6;
text-align: justify;
font-size: 16px;
color: #333;
}
.rich-text-container iframe {
width: 100%;
height: 200px;
margin: 10px 0;
border-radius: 4px;
border: none;
}
.rich-text-container table {
width: 100% !important;
border-collapse: collapse;
margin: 10px 0;
}
.rich-text-container table,
.rich-text-container th,
.rich-text-container td {
border: 1px solid #ddd;
padding: 8px;
}
.rich-text-container th {
background-color: #f2f2f2;
}
</style>这里的各种文件和视频的下载有问题,在手机端无法下载,需要在手机端可以下载到文件管理里面
最新发布