Compare commits
36 Commits
xindeznkf
...
support/we
| Author | SHA1 | Date | |
|---|---|---|---|
| b3cc86d088 | |||
| 0ed0789528 | |||
| 16d1a3a945 | |||
| 392a8e2952 | |||
| db7a8d7202 | |||
| bb87f4b1d2 | |||
| fe039ed126 | |||
| 03c1a8d71a | |||
| 9e0cab7ec4 | |||
| a31c004508 | |||
| ee8c24cdf8 | |||
| b043601a77 | |||
| e20968aee2 | |||
| 8ceeca33b4 | |||
| 02e9cfa313 | |||
| 3f894bcb20 | |||
| 8d5d6e3da8 | |||
| ce1ea6eadf | |||
| 191c803f66 | |||
| 4e1448d2c4 | |||
| ce26a9be5f | |||
| 8b01d8f8ec | |||
| 6ab2834fdc | |||
| 85816e416c | |||
| bf8e177c30 | |||
| 57111a00c2 | |||
| aa7c7cbe60 | |||
| d012c7fff7 | |||
| 128d75b921 | |||
| 7e0d61c7e5 | |||
| 26af083839 | |||
| 68bc853dff | |||
| 13166132c7 | |||
| f87c7d963e | |||
| 6f6899deaa | |||
| 7f54666629 |
@@ -38,6 +38,10 @@ const localDevConfig = ({
|
|||||||
uniacid: 1,
|
uniacid: 1,
|
||||||
domain: 'https://test.aigc-quickapp.com',
|
domain: 'https://test.aigc-quickapp.com',
|
||||||
},
|
},
|
||||||
|
'local-2': { // 测试平台
|
||||||
|
uniacid: 2,
|
||||||
|
domain: 'http://localhost:8050/',
|
||||||
|
},
|
||||||
})['2811']; // 选择要使用的环境配置
|
})['2811']; // 选择要使用的环境配置
|
||||||
|
|
||||||
export default localDevConfig;
|
export default localDevConfig;
|
||||||
100
common/js/share.js
Normal file
100
common/js/share.js
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
// 文件分享工具函数
|
||||||
|
export default {
|
||||||
|
/**
|
||||||
|
* 生成文件预览链接
|
||||||
|
* @param {Object} file - 文件对象,包含 name 和 url 属性
|
||||||
|
* @returns {string} - 生成的文件预览链接
|
||||||
|
*/
|
||||||
|
generateFilePreviewUrl(file) {
|
||||||
|
// 推断文件类型
|
||||||
|
let fileType = '';
|
||||||
|
if (file.name) {
|
||||||
|
const ext = file.name.split('.').pop().toLowerCase();
|
||||||
|
if (['pdf'].includes(ext)) {
|
||||||
|
fileType = 'pdf';
|
||||||
|
} else if (['doc', 'docx'].includes(ext)) {
|
||||||
|
fileType = 'word';
|
||||||
|
} else if (['mp4', 'avi', 'mov', 'wmv', 'flv', 'mkv'].includes(ext)) {
|
||||||
|
fileType = 'video';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 构建文件预览链接
|
||||||
|
const previewUrl = `/pages_tool/file-preview/file-preview?fileName=${encodeURIComponent(file.name)}&fileUrl=${encodeURIComponent(file.url || '')}&fileType=${encodeURIComponent(fileType)}`;
|
||||||
|
return previewUrl;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分享文件
|
||||||
|
* @param {Object} file - 文件对象,包含 name 和 url 属性
|
||||||
|
* @param {string} title - 分享标题,默认使用文件名
|
||||||
|
* @param {string} desc - 分享描述,默认使用固定文案
|
||||||
|
*/
|
||||||
|
shareFile(file, title = file.name, desc = '查看企业文件:' + file.name) {
|
||||||
|
// 生成文件预览链接
|
||||||
|
const filePreviewUrl = this.generateFilePreviewUrl(file);
|
||||||
|
|
||||||
|
// #ifdef H5
|
||||||
|
// H5 平台分享
|
||||||
|
if (navigator.share) {
|
||||||
|
// 使用 Web Share API
|
||||||
|
navigator.share({
|
||||||
|
title: title,
|
||||||
|
text: desc,
|
||||||
|
url: filePreviewUrl
|
||||||
|
}).catch(err => {
|
||||||
|
console.error('分享失败:', err);
|
||||||
|
uni.showToast({ title: '分享失败', icon: 'none' });
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 不支持 Web Share API 的浏览器
|
||||||
|
uni.setClipboardData({
|
||||||
|
data: filePreviewUrl,
|
||||||
|
success: () => {
|
||||||
|
uni.showToast({ title: '链接已复制,请粘贴分享', icon: 'success' });
|
||||||
|
},
|
||||||
|
fail: () => {
|
||||||
|
uni.showToast({ title: '复制失败', icon: 'none' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifdef MP-WEIXIN
|
||||||
|
// 微信小程序分享
|
||||||
|
uni.showActionSheet({
|
||||||
|
itemList: ['发送给朋友', '分享到朋友圈'],
|
||||||
|
success: (res) => {
|
||||||
|
if (res.tapIndex === 0) {
|
||||||
|
// 发送给朋友
|
||||||
|
uni.shareAppMessage({
|
||||||
|
title: title,
|
||||||
|
path: filePreviewUrl,
|
||||||
|
success: () => {
|
||||||
|
uni.showToast({ title: '分享成功', icon: 'success' });
|
||||||
|
},
|
||||||
|
fail: () => {
|
||||||
|
uni.showToast({ title: '分享失败', icon: 'none' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if (res.tapIndex === 1) {
|
||||||
|
// 分享到朋友圈
|
||||||
|
uni.shareTimeline({
|
||||||
|
title: desc,
|
||||||
|
path: filePreviewUrl,
|
||||||
|
success: () => {
|
||||||
|
uni.showToast({ title: '分享成功', icon: 'success' });
|
||||||
|
},
|
||||||
|
fail: () => {
|
||||||
|
uni.showToast({ title: '分享失败', icon: 'none' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
fail: () => {
|
||||||
|
// 用户取消分享
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
};
|
||||||
@@ -628,7 +628,6 @@ export default {
|
|||||||
|
|
||||||
} else if (link.appid) {
|
} else if (link.appid) {
|
||||||
// 跳转其他小程序
|
// 跳转其他小程序
|
||||||
|
|
||||||
uni.navigateToMiniProgram({
|
uni.navigateToMiniProgram({
|
||||||
appId: link.appid,
|
appId: link.appid,
|
||||||
path: link.page
|
path: link.page
|
||||||
@@ -636,7 +635,6 @@ export default {
|
|||||||
|
|
||||||
} else if (link.name == 'MOBILE' && !link.wap_url) {
|
} else if (link.name == 'MOBILE' && !link.wap_url) {
|
||||||
// 拨打电话
|
// 拨打电话
|
||||||
|
|
||||||
uni.makePhoneCall({
|
uni.makePhoneCall({
|
||||||
phoneNumber: link.mobile,
|
phoneNumber: link.mobile,
|
||||||
success: (res) => {
|
success: (res) => {
|
||||||
@@ -1180,7 +1178,7 @@ export default {
|
|||||||
// 但是会包含uniacid参数,所以这里通过uniacid参数来判断是否为快应用环境
|
// 但是会包含uniacid参数,所以这里通过uniacid参数来判断是否为快应用环境
|
||||||
_isQuickApp = ua.indexOf('uniacid=') !== -1;
|
_isQuickApp = ua.indexOf('uniacid=') !== -1;
|
||||||
}
|
}
|
||||||
} catch (e) {}
|
} catch (e) { }
|
||||||
|
|
||||||
if (!_isQuickApp) {
|
if (!_isQuickApp) {
|
||||||
const systemInfo = this.getDeviceInfo();
|
const systemInfo = this.getDeviceInfo();
|
||||||
@@ -1201,8 +1199,35 @@ export default {
|
|||||||
// #ifndef QUICKAPP-WEBVIEW-HUAWEI
|
// #ifndef QUICKAPP-WEBVIEW-HUAWEI
|
||||||
const systemInfo = this.getDeviceInfo();
|
const systemInfo = this.getDeviceInfo();
|
||||||
return systemInfo.brand === 'HUAWEI' || systemInfo.manufacturer === 'HUAWEI' ||
|
return systemInfo.brand === 'HUAWEI' || systemInfo.manufacturer === 'HUAWEI' ||
|
||||||
(typeof qh !== 'undefined' && qh.platform === 'huawei');
|
(typeof qh !== 'undefined' && qh.platform === 'huawei');
|
||||||
// #endif
|
// #endif
|
||||||
return true;
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 蛇形命名转驼峰命名
|
||||||
|
* @param {string} str 蛇形命名字符串
|
||||||
|
* @returns {string} 驼峰命名字符串
|
||||||
|
*/
|
||||||
|
snakeToCamel(str) {
|
||||||
|
return str.replace(/_([a-z])/g, (_, letter) => letter.toUpperCase());
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 蛇形命名转驼峰命名(递归处理对象中的所有属性名)
|
||||||
|
* @param {Object} obj 包含蛇形命名字符串属性的对象
|
||||||
|
* @returns {Object} 驼峰命名字符串属性的对象
|
||||||
|
*/
|
||||||
|
snakeToCamelForObj(obj) {
|
||||||
|
if (typeof obj !== 'object' || obj === null) return obj;
|
||||||
|
if (Array.isArray(obj)) return obj.map(this.snakeToCamelForObj.bind(this));
|
||||||
|
const newObj = {};
|
||||||
|
for (const key in obj) {
|
||||||
|
if (Object.hasOwnProperty.call(obj, key)) {
|
||||||
|
const newKey = this.snakeToCamel(key);
|
||||||
|
newObj[newKey] = this.snakeToCamelForObj(obj[key]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return newObj;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
143
components-diy/css/common-channel.scss
Normal file
143
components-diy/css/common-channel.scss
Normal file
@@ -0,0 +1,143 @@
|
|||||||
|
// 公共微信视频号样式
|
||||||
|
|
||||||
|
// CSS 变量
|
||||||
|
:root {
|
||||||
|
// 尺寸变量
|
||||||
|
--channel-play-btn-size: 80rpx;
|
||||||
|
--channel-play-btn-small-size: 60rpx;
|
||||||
|
--channel-play-btn-icon-size: 40rpx;
|
||||||
|
--channel-play-btn-icon-small-size: 30rpx;
|
||||||
|
--channel-border-radius: 12rpx;
|
||||||
|
--channel-stats-padding: 10rpx;
|
||||||
|
--channel-info-wrap-padding: 10rpx 0;
|
||||||
|
|
||||||
|
// 字体变量
|
||||||
|
--channel-name-font-size: 28rpx;
|
||||||
|
--video-title-font-size: 28rpx;
|
||||||
|
--video-title-small-font-size: 24rpx;
|
||||||
|
--video-stats-font-size: 24rpx;
|
||||||
|
--channel-stats-font-size: 20rpx;
|
||||||
|
|
||||||
|
// 颜色变量
|
||||||
|
--channel-name-color: #333;
|
||||||
|
--video-title-color: #333;
|
||||||
|
--video-title-small-color: #666;
|
||||||
|
--video-stats-color: #999;
|
||||||
|
--channel-stats-color: #fff;
|
||||||
|
--channel-border-color: #f0f0f0;
|
||||||
|
--channel-play-btn-bg: rgba(0, 0, 0, 0.4);
|
||||||
|
--channel-stats-bg: linear-gradient(transparent, rgba(0, 0, 0, 0.6));
|
||||||
|
|
||||||
|
// 间距变量
|
||||||
|
--channel-name-margin-bottom: 4rpx;
|
||||||
|
--video-title-margin-bottom: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 响应式设计
|
||||||
|
@media (max-width: 375px) {
|
||||||
|
:root {
|
||||||
|
--channel-play-btn-size: 70rpx;
|
||||||
|
--channel-play-btn-small-size: 50rpx;
|
||||||
|
--channel-play-btn-icon-size: 35rpx;
|
||||||
|
--channel-play-btn-icon-small-size: 25rpx;
|
||||||
|
--channel-name-font-size: 24rpx;
|
||||||
|
--video-title-font-size: 24rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 750px) {
|
||||||
|
:root {
|
||||||
|
--channel-play-btn-size: 90rpx;
|
||||||
|
--channel-play-btn-small-size: 70rpx;
|
||||||
|
--channel-play-btn-icon-size: 45rpx;
|
||||||
|
--channel-play-btn-icon-small-size: 35rpx;
|
||||||
|
--channel-name-font-size: 32rpx;
|
||||||
|
--video-title-font-size: 32rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 播放按钮样式
|
||||||
|
.channel-play-btn {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
width: var(--channel-play-btn-size);
|
||||||
|
height: var(--channel-play-btn-size);
|
||||||
|
background-color: var(--channel-play-btn-bg);
|
||||||
|
border-radius: 50%;
|
||||||
|
|
||||||
|
.play-icon {
|
||||||
|
width: var(--channel-play-btn-icon-size);
|
||||||
|
height: var(--channel-play-btn-icon-size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 小尺寸播放按钮(用于列表)
|
||||||
|
.channel-play-btn.small {
|
||||||
|
width: var(--channel-play-btn-small-size);
|
||||||
|
height: var(--channel-play-btn-small-size);
|
||||||
|
|
||||||
|
.play-icon {
|
||||||
|
width: var(--channel-play-btn-icon-small-size);
|
||||||
|
height: var(--channel-play-btn-icon-small-size);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 视频统计信息
|
||||||
|
.channel-stats {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
background: var(--channel-stats-bg);
|
||||||
|
padding: var(--channel-stats-padding);
|
||||||
|
color: var(--channel-stats-color);
|
||||||
|
font-size: var(--channel-stats-font-size);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 视频信息容器
|
||||||
|
.channel-info-wrap {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
flex: 1;
|
||||||
|
padding: var(--channel-info-wrap-padding);
|
||||||
|
|
||||||
|
.channel-name {
|
||||||
|
margin-bottom: var(--channel-name-margin-bottom);
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
font-size: var(--channel-name-font-size);
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--channel-name-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-title {
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
overflow: hidden;
|
||||||
|
font-size: var(--video-title-small-font-size);
|
||||||
|
color: var(--video-title-small-color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 视频标题
|
||||||
|
.video-title {
|
||||||
|
font-size: var(--video-title-font-size);
|
||||||
|
font-weight: 500;
|
||||||
|
color: var(--video-title-color);
|
||||||
|
margin-bottom: var(--video-title-margin-bottom);
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 视频统计信息(非绝对定位版本)
|
||||||
|
.video-stats {
|
||||||
|
font-size: var(--video-stats-font-size);
|
||||||
|
color: var(--video-stats-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
<view class="article-wrap" :style="warpCss">
|
<view class="article-wrap" :style="warpCss">
|
||||||
<view :class="['list-wrap', value.style]" :style="warpCss">
|
<view :class="['list-wrap', value.style]" :style="warpCss">
|
||||||
<!-- 自动垂直滚动 -->
|
<!-- 自动垂直滚动 -->
|
||||||
<swiper class="auto-scroll-swiper" :style="swiperStyle" :vertical="swiperConfig.vertical !== false"
|
<swiper class="auto-scroll-swiper" :style="[swiperStyle]" :vertical="swiperConfig.vertical !== false"
|
||||||
:autoplay="swiperConfig.autoplay !== false" :circular="swiperConfig.circular !== false"
|
:autoplay="swiperConfig.autoplay !== false" :circular="swiperConfig.circular !== false"
|
||||||
:interval="swiperConfig.interval || 3000" :duration="swiperConfig.duration || 500"
|
:interval="swiperConfig.interval || 3000" :duration="swiperConfig.duration || 500"
|
||||||
:display-multiple-items="safeDisplayMultipleItems">
|
:display-multiple-items="safeDisplayMultipleItems">
|
||||||
|
|||||||
481
components-diy/diy-channel-list.vue
Normal file
481
components-diy/diy-channel-list.vue
Normal file
@@ -0,0 +1,481 @@
|
|||||||
|
<template>
|
||||||
|
<view :style="[componentStyle, { '--row-count': value.rowCount }]">
|
||||||
|
<!-- 轮播模式 -->
|
||||||
|
<swiper v-if="value.showStyle == 'carousel' && carouselConfig.type != 'hide'" :indicator-dots="isIndicatorDots"
|
||||||
|
:autoplay="carouselConfig.autoplay || false" :interval="carouselConfig.interval || 3000"
|
||||||
|
:duration="carouselConfig.duration || 500" :circular="carouselConfig.circular || false"
|
||||||
|
:style="swiperHeight" class="channel-swiper">
|
||||||
|
<swiper-item v-for="(slide, slideIndex) in swiperSlides" :key="slideIndex">
|
||||||
|
<view :class="['swiper-slide-content', 'row1-of' + value.rowCount]">
|
||||||
|
<view v-for="(item, index) in slide" :key="index" :class="['channel-item', value.mode]">
|
||||||
|
<!-- 视频号视频卡片,轮播模式 -->
|
||||||
|
<diy-channel-video :value="item" @video-play="playVideo" :list-mode="true"
|
||||||
|
:title-line-clamp="value.titleLineClamp" :show-play-btn="value.showPlayBtn"
|
||||||
|
:cover-style="computedCoverStyle" :play-btn-style="value.playBtnStyle"
|
||||||
|
:aspect-ratio="value.aspectRatio" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</swiper-item>
|
||||||
|
</swiper>
|
||||||
|
|
||||||
|
<!-- 固定布局模式 -->
|
||||||
|
<view v-else-if="value.showStyle == 'fixed'" :class="['channel-list', 'row1-of' + value.rowCount]">
|
||||||
|
<view v-for="(item, index) in value.list" :key="index" class="channel-item">
|
||||||
|
<!-- 视频号视频卡片,列表模式 -->
|
||||||
|
<diy-channel-video :value="item" @video-play="playVideo" :list-mode="true"
|
||||||
|
:title-line-clamp="value.titleLineClamp" :show-play-btn="value.showPlayBtn"
|
||||||
|
:cover-style="computedCoverStyle" :play-btn-style="value.playBtnStyle"
|
||||||
|
:aspect-ratio="value.aspectRatio" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 其他布局模式(如滚动布局) -->
|
||||||
|
<scroll-view v-else
|
||||||
|
:class="['channel-nav', value.showStyle == 'fixed' ? 'fixed-layout' : value.showStyle, 'row1-of' + value.rowCount]"
|
||||||
|
:scroll-x="value.showStyle == 'singleSlide'">
|
||||||
|
<view class="uni-scroll-view-content">
|
||||||
|
<view v-for="(item, index) in value.list" :key="index" :class="['channel-nav-item', value.mode]">
|
||||||
|
<!-- 视频号视频卡片,滚动模式 -->
|
||||||
|
<diy-channel-video :value="item" @video-play="playVideo" :list-mode="true"
|
||||||
|
:title-line-clamp="value.titleLineClamp" :show-play-btn="value.showPlayBtn"
|
||||||
|
:cover-style="computedCoverStyle" :play-btn-style="value.playBtnStyle"
|
||||||
|
:aspect-ratio="value.aspectRatio" />
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</scroll-view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
/**
|
||||||
|
* 微信视频号列表组件
|
||||||
|
* 支持多种布局模式,包括固定布局和滚动布局
|
||||||
|
* 可配置列数、视频高度、标题行数等属性
|
||||||
|
* 用于在页面中展示多个微信视频号视频
|
||||||
|
*/
|
||||||
|
import DiyMinx from './minx.js'
|
||||||
|
import { wechatChannelUtil, wechatChannelConfig } from './js/wechat-channel.js'
|
||||||
|
|
||||||
|
export default {
|
||||||
|
name: 'diy-channel-list',
|
||||||
|
props: {
|
||||||
|
/**
|
||||||
|
* 组件配置数据
|
||||||
|
* @type {Object}
|
||||||
|
* @default () => ({})
|
||||||
|
* @property {string} showStyle - 显示样式,可选值:carousel, fixed, singleSlide
|
||||||
|
* @property {number} rowCount - 每行显示的视频数量
|
||||||
|
* @property {Array} list - 视频列表数据
|
||||||
|
* @property {string} componentBgColor - 组件背景色
|
||||||
|
* @property {string} componentAngle - 组件圆角类型,可选值:round
|
||||||
|
* @property {number} topAroundRadius - 顶部圆角半径
|
||||||
|
* @property {number} bottomAroundRadius - 底部圆角半径
|
||||||
|
* @property {Object} ornament - 装饰效果配置
|
||||||
|
* @property {number} titleLineClamp - 标题显示行数
|
||||||
|
* @property {string} aspectRatio - 视频比例,可选值:16:9, 3:4
|
||||||
|
* @property {boolean} showPlayBtn - 是否显示播放按钮
|
||||||
|
* @property {Object} coverStyle - 视频封面图样式
|
||||||
|
* @property {Object} playBtnStyle - 播放按钮样式
|
||||||
|
* @property {string} mode - 显示模式, 可选值:video, text, image, graphic
|
||||||
|
* @property {number} imageSize - 图片尺寸(仅在特定模式下使用)
|
||||||
|
* @property {number} pageCount - 每页显示的视频数量
|
||||||
|
* @property {Object} carousel - 轮播配置
|
||||||
|
* @property {string} carousel.type - 轮播类型,可选值:default, hide
|
||||||
|
* @property {boolean} carousel.autoplay - 是否自动播放,默认false
|
||||||
|
* @property {number} carousel.interval - 自动播放间隔,默认3000ms
|
||||||
|
* @property {number} carousel.duration - 切换动画时长,默认500ms
|
||||||
|
* @property {boolean} carousel.circular - 是否循环播放,默认false
|
||||||
|
*/
|
||||||
|
value: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
mixins: [DiyMinx],
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
pageWidth: '', // 页面宽度
|
||||||
|
indicatorDots: false, // 是否显示轮播指示器
|
||||||
|
swiperCurrent: 0 // 当前轮播索引
|
||||||
|
}
|
||||||
|
},
|
||||||
|
created() {
|
||||||
|
// 组件创建时的逻辑
|
||||||
|
// 可以在这里进行初始化操作,如获取页面宽度等
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
/**
|
||||||
|
* 组件刷新监听
|
||||||
|
* 当组件需要刷新时触发
|
||||||
|
* @param {*} newValue - 新值
|
||||||
|
*/
|
||||||
|
componentRefresh(newValue) {
|
||||||
|
// 监听组件刷新
|
||||||
|
// 可以在这里处理组件刷新时的逻辑
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
carouselConfig() {
|
||||||
|
return this.value?.carousel || {
|
||||||
|
type: 'default',
|
||||||
|
autoplay: false,
|
||||||
|
interval: 3000,
|
||||||
|
duration: 500,
|
||||||
|
circular: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 组件样式
|
||||||
|
* 根据配置动态生成样式字符串,包括背景色、圆角、阴影和边框
|
||||||
|
* @returns {string} 样式字符串
|
||||||
|
*/
|
||||||
|
componentStyle() {
|
||||||
|
let style = '';
|
||||||
|
// 背景色
|
||||||
|
if (this.value?.componentBgColor) {
|
||||||
|
style += 'background-color:' + this.value?.componentBgColor + ';';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 圆角样式
|
||||||
|
if (this.value?.componentAngle == 'round') {
|
||||||
|
style += 'border-top-left-radius:' + (2 * this.value?.topAroundRadius) + 'rpx;';
|
||||||
|
style += 'border-top-right-radius:' + (2 * this.value?.topAroundRadius) + 'rpx;';
|
||||||
|
style += 'border-bottom-left-radius:' + (2 * this.value?.bottomAroundRadius) + 'rpx;';
|
||||||
|
style += 'border-bottom-right-radius:' + (2 * this.value?.bottomAroundRadius) + 'rpx;';
|
||||||
|
}
|
||||||
|
|
||||||
|
// 装饰效果:阴影
|
||||||
|
style += 'box-shadow:' + (this.value?.ornament?.type == 'shadow' ? '0 0 10rpx ' + this.value?.ornament?.color : '') + ';';
|
||||||
|
// 装饰效果:边框
|
||||||
|
style += 'border:' + (this.value?.ornament?.type == 'stroke' ? '2rpx solid ' + this.value?.ornament?.color : '') + ';';
|
||||||
|
|
||||||
|
return style;
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 轮播高度
|
||||||
|
* 根据模式和配置计算轮播高度
|
||||||
|
* @returns {string} 轮播高度样式
|
||||||
|
*/
|
||||||
|
swiperHeight() {
|
||||||
|
let height = 0;
|
||||||
|
|
||||||
|
const pageCount = this.value?.pageCount || 1;
|
||||||
|
|
||||||
|
// 根据不同模式计算高度
|
||||||
|
if (this.value?.mode == 'graphic') {
|
||||||
|
height = (49 + this.value?.imageSize) * pageCount;
|
||||||
|
} else if (this.value?.mode == 'img') {
|
||||||
|
height = (22 + this.value?.imageSize) * pageCount;
|
||||||
|
} else if (this.value?.mode == 'text') {
|
||||||
|
height = 43 * pageCount;
|
||||||
|
} else { // 默认视频模式
|
||||||
|
if (this.value?.aspectRatio == '16:9') {
|
||||||
|
height = [250, 160, 140, 120][this.value.rowCount - 1];
|
||||||
|
} else {
|
||||||
|
height = [510, 280, 220, 180][this.value.rowCount - 1];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 'height:' + (2 * height) + 'rpx';
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否显示指示器
|
||||||
|
* 根据轮播配置和列表长度判断是否显示指示器
|
||||||
|
* @returns {boolean} 是否显示指示器
|
||||||
|
*/
|
||||||
|
isIndicatorDots() {
|
||||||
|
// 当轮播类型不是隐藏,且视频数量超过一页时显示指示器
|
||||||
|
const pageCount = this.value?.pageCount || 1;
|
||||||
|
return this.value?.carousel?.type != 'hide' && Math.ceil(this.value?.list?.length / (pageCount * this.value?.rowCount)) > 1
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 轮播幻灯片数据
|
||||||
|
* 将视频列表分割成轮播所需的幻灯片数据
|
||||||
|
* @returns {Array} 轮播幻灯片数据
|
||||||
|
*/
|
||||||
|
swiperSlides() {
|
||||||
|
const slides = [];
|
||||||
|
const list = this.value?.list || [];
|
||||||
|
const pageSize = (this.value?.pageCount || 1) * (this.value?.rowCount || 1);
|
||||||
|
|
||||||
|
// 将列表数据分割成每页显示的数量
|
||||||
|
for (let i = 0; i < list.length; i += pageSize) {
|
||||||
|
slides.push(list.slice(i, i + pageSize));
|
||||||
|
}
|
||||||
|
|
||||||
|
return slides;
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 视频封面样式
|
||||||
|
* 根据aspectRatio属性动态计算封面样式
|
||||||
|
* @returns {Object} 封面样式对象
|
||||||
|
*/
|
||||||
|
computedCoverStyle() {
|
||||||
|
const aspectRatio = this.value?.aspectRatio || '16:9';
|
||||||
|
let paddingTop = '56.25%'; // 默认 16:9 比例
|
||||||
|
|
||||||
|
if (aspectRatio === '3:4') {
|
||||||
|
paddingTop = '133.33%'; // 3:4 比例
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
width: '100%',
|
||||||
|
height: '0',
|
||||||
|
paddingTop: paddingTop
|
||||||
|
};
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* 播放视频
|
||||||
|
* 触发 video-play 事件,并在微信小程序中调用视频播放接口
|
||||||
|
* @param {Object} item - 视频数据对象
|
||||||
|
*/
|
||||||
|
async playVideo(item) {
|
||||||
|
await this.__$emitEvent({
|
||||||
|
eventName: 'video-play',
|
||||||
|
data: item,
|
||||||
|
promiseCallback: async (event, handler, awaitedResult) => {
|
||||||
|
if (!awaitedResult) return;
|
||||||
|
try {
|
||||||
|
// 发送视频被点击播放事件
|
||||||
|
this.$emit('channel-video-click-play', item);
|
||||||
|
console.log('播放视频:', item);
|
||||||
|
|
||||||
|
// #ifdef MP-WEIXIN
|
||||||
|
// 在微信小程序环境中调用视频播放接口
|
||||||
|
await wechatChannelUtil.playVideo(item);
|
||||||
|
// #endif
|
||||||
|
|
||||||
|
// #ifndef MP-WEIXIN
|
||||||
|
if (item?.channelType === 'wechat') {
|
||||||
|
uni.showToast({
|
||||||
|
title: '视频号仅支持在微信小程序环境中播放',
|
||||||
|
icon: 'none',
|
||||||
|
duration: 2000
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
// 在非微信小程序环境中,直接触发事件
|
||||||
|
this.$emit('video-play', item);
|
||||||
|
}
|
||||||
|
// #endif
|
||||||
|
} catch (err) {
|
||||||
|
console.error('打开视频号失败', err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 图片加载错误处理
|
||||||
|
* 当图片加载失败时,设置默认图片
|
||||||
|
* @param {number} index - 图片索引
|
||||||
|
*/
|
||||||
|
imgError(index) {
|
||||||
|
// 图片加载失败的处理逻辑
|
||||||
|
console.log('图片加载失败:', index);
|
||||||
|
// 为失败的图片设置默认图片
|
||||||
|
const item = this.value.list[index];
|
||||||
|
if (item) {
|
||||||
|
// 使用默认图片替代加载失败的图片
|
||||||
|
// #ifdef MP-WEIXIN
|
||||||
|
item.coverUrl = wechatChannelConfig.video.defaultCoverUrl;
|
||||||
|
// #endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
@import './css/common-channel.scss';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列表布局样式
|
||||||
|
*/
|
||||||
|
.channel-list {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(var(--row-count, 2), 1fr);
|
||||||
|
gap: 8px;
|
||||||
|
padding: 16px 16px 0px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 导航布局样式
|
||||||
|
* 支持固定布局和滚动布局
|
||||||
|
*/
|
||||||
|
.channel-nav {
|
||||||
|
padding: 16rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
.uni-scroll-view-content {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(var(--row-count, 2), 1fr);
|
||||||
|
gap: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 单滑动模式
|
||||||
|
&.singleSlide {
|
||||||
|
.uni-scroll-view-content {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: nowrap;
|
||||||
|
gap: 16rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.channel-nav-item {
|
||||||
|
flex-shrink: 0;
|
||||||
|
width: 280rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.channel-nav-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 1 列布局
|
||||||
|
&.row1-of1 {
|
||||||
|
.uni-scroll-view-content {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 2 列布局
|
||||||
|
&.row1-of2 {
|
||||||
|
.uni-scroll-view-content {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 3 列布局
|
||||||
|
&.row1-of3 {
|
||||||
|
.uni-scroll-view-content {
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 4 列布局
|
||||||
|
&.row1-of4 {
|
||||||
|
.uni-scroll-view-content {
|
||||||
|
grid-template-columns: repeat(4, 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 确保所有视频卡片高度一致
|
||||||
|
*/
|
||||||
|
.channel-item,
|
||||||
|
.channel-nav-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 轮播样式
|
||||||
|
* 支持轮播模式的布局和样式
|
||||||
|
*/
|
||||||
|
.channel-swiper {
|
||||||
|
width: 100%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
.swiper-slide-content {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: repeat(var(--row-count, 2), 1fr);
|
||||||
|
gap: 16rpx;
|
||||||
|
padding: 16rpx;
|
||||||
|
box-sizing: border-box;
|
||||||
|
|
||||||
|
.channel-item {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 轮播模式下的 1 列布局
|
||||||
|
&.row1-of1 {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 轮播模式下的 2 列布局
|
||||||
|
&.row1-of2 {
|
||||||
|
grid-template-columns: repeat(2, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 轮播模式下的 3 列布局
|
||||||
|
&.row1-of3 {
|
||||||
|
grid-template-columns: repeat(3, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 轮播模式下的 4 列布局
|
||||||
|
&.row1-of4 {
|
||||||
|
grid-template-columns: repeat(4, 1fr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 响应式调整
|
||||||
|
* 在小屏幕设备上调整布局和间距
|
||||||
|
*/
|
||||||
|
@media (max-width: 375px) {
|
||||||
|
|
||||||
|
.channel-list,
|
||||||
|
.channel-nav,
|
||||||
|
.channel-swiper .swiper-slide-content {
|
||||||
|
gap: 12rpx;
|
||||||
|
padding: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 小屏幕上的单滑动模式
|
||||||
|
.channel-nav {
|
||||||
|
&.singleSlide {
|
||||||
|
.uni-scroll-view-content {
|
||||||
|
gap: 12rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.channel-nav-item {
|
||||||
|
width: 240rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 视频比例样式调整
|
||||||
|
* 根据不同的视频比例调整布局
|
||||||
|
*/
|
||||||
|
|
||||||
|
// // 3:4 比例的视频卡片样式
|
||||||
|
// .channel-video.ratio-3-4 {
|
||||||
|
// // 调整视频卡片的整体高度
|
||||||
|
// /deep/ .video-cover-wrap {
|
||||||
|
// padding-top: 133.33%; // 3:4 比例
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // 列表模式下的3:4比例调整
|
||||||
|
// &.list-mode {
|
||||||
|
// /deep/ .video-cover-wrap {
|
||||||
|
// padding-top: 133.33%; // 3:4 比例
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // 16:9 比例的视频卡片样式(默认)
|
||||||
|
// .channel-video.ratio-16-9 {
|
||||||
|
// // 保持默认的16:9比例
|
||||||
|
// /deep/ .video-cover-wrap {
|
||||||
|
// padding-top: 56.25%; // 16:9 比例
|
||||||
|
// }
|
||||||
|
|
||||||
|
// // 列表模式下的16:9比例保持默认
|
||||||
|
// &.list-mode {
|
||||||
|
// /deep/ .video-cover-wrap {
|
||||||
|
// padding-top: 56.25%; // 16:9 比例
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }</style>
|
||||||
484
components-diy/diy-channel-video.vue
Normal file
484
components-diy/diy-channel-video.vue
Normal file
@@ -0,0 +1,484 @@
|
|||||||
|
<template>
|
||||||
|
<view class="channel-video" :class="{ 'list-mode': listMode }">
|
||||||
|
<!-- 嵌入式视频播放 -->
|
||||||
|
<view v-if="canUseEmbedMode" class="embed-video-container">
|
||||||
|
<view class="video-cover-wrap" :style="[coverStyle]">
|
||||||
|
<!-- #ifdef MP-WEIXIN -->
|
||||||
|
<native-component>
|
||||||
|
<!-- 嵌入式视频播放组件 -->
|
||||||
|
<channel-video :feed-id="value.feedId" :finder-user-name="value.finderUserName"
|
||||||
|
:feed-token="value.feedToken" :auto-play="autoPlay">
|
||||||
|
</channel-video>
|
||||||
|
</native-component>
|
||||||
|
<!-- #endif -->
|
||||||
|
</view>
|
||||||
|
<view class="video-info">
|
||||||
|
<!-- 视频标题,支持多行显示控制 -->
|
||||||
|
<view class="video-title" :style="{ '--title-line-clamp': titleLineClamp }">{{ value.videoTitle }}
|
||||||
|
</view>
|
||||||
|
<!-- 视频统计信息 -->
|
||||||
|
<view class="video-stats" v-if="value.showViewCount">{{ value.viewCount }}次观看</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 跳转式视频播放 -->
|
||||||
|
<view v-else @click.stop="playVideo" class="video-container">
|
||||||
|
<view class="video-cover-wrap" :style="[coverStyle]">
|
||||||
|
<image class="video-cover" :src="$util.img(value.coverUrl)" mode="aspectFill"></image>
|
||||||
|
<view class="channel-play-btn" v-if="showPlayBtn" :style="[playBtnStyle]">
|
||||||
|
<view class="play-icon-css"></view>
|
||||||
|
</view>
|
||||||
|
<view class="video-stats-overlay" v-if="value.showViewCount">
|
||||||
|
{{ value.viewCount }}次观看
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view class="video-info">
|
||||||
|
<!-- 视频标题,支持多行显示控制 -->
|
||||||
|
<view class="video-title" :style="{ '--title-line-clamp': titleLineClamp }">{{ value.videoTitle }}
|
||||||
|
</view>
|
||||||
|
<!-- 视频统计信息,非列表模式下显示 -->
|
||||||
|
<view class="video-stats" v-if="value.showViewCount && !listMode">{{ value.viewCount }}次观看</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
import { wechatChannelUtil, wechatChannelConfig } from './js/wechat-channel.js'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 微信视频号视频卡片组件
|
||||||
|
* 支持嵌入式播放和跳转式播放两种模式
|
||||||
|
* 可配置列表模式、视频高度、标题行数等属性
|
||||||
|
*/
|
||||||
|
export default {
|
||||||
|
name: 'diy-channel-video',
|
||||||
|
props: {
|
||||||
|
/**
|
||||||
|
* 视频数据对象
|
||||||
|
* @type {Object}
|
||||||
|
* @required
|
||||||
|
* @property {string} feedId - 视频 feedId
|
||||||
|
* @property {string} feedToken - 视频 token
|
||||||
|
* @property {string} coverUrl - 视频封面图
|
||||||
|
* @property {string} videoTitle - 视频标题
|
||||||
|
* @property {number} viewCount - 观看次数
|
||||||
|
* @property {boolean} showViewCount - 是否显示观看次数
|
||||||
|
* @property {boolean} embedMode - 是否启用嵌入式播放
|
||||||
|
*/
|
||||||
|
value: {
|
||||||
|
type: Object,
|
||||||
|
required: true
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 是否为列表模式
|
||||||
|
* @type {boolean}
|
||||||
|
* @default false
|
||||||
|
*/
|
||||||
|
listMode: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 标题显示行数
|
||||||
|
* @type {number}
|
||||||
|
* @default 1
|
||||||
|
*/
|
||||||
|
titleLineClamp: {
|
||||||
|
type: Number,
|
||||||
|
default: 1
|
||||||
|
},
|
||||||
|
/** 是否显示播放按钮 */
|
||||||
|
showPlayBtn: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 视频封面图样式
|
||||||
|
* 采用 16:9 比例的响应式高度
|
||||||
|
*/
|
||||||
|
coverStyle: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({
|
||||||
|
width: '100%',
|
||||||
|
height: '0',
|
||||||
|
paddingTop: '56.25%' // 16:9 比例
|
||||||
|
})
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 播放按钮样式
|
||||||
|
*/
|
||||||
|
playBtnStyle: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({
|
||||||
|
width: '60rpx',
|
||||||
|
height: '60rpx',
|
||||||
|
borderRadius: '30rpx',
|
||||||
|
backgroundColor: 'rgba(0, 0, 0, 0.6)'
|
||||||
|
})
|
||||||
|
},
|
||||||
|
/** 是否自动播放 */
|
||||||
|
autoPlay: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
/**
|
||||||
|
* 是否支持嵌入式播放
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
canUseEmbedMode() {
|
||||||
|
// #ifdef MP-WEIXIN
|
||||||
|
const enableEmbedMode = Boolean(this.value?.embedMode)
|
||||||
|
&& Boolean(this.value?.feedToken)
|
||||||
|
&& wechatChannelUtil.isEmbedModeSupported();
|
||||||
|
console.log('enableEmbedMode', enableEmbedMode);
|
||||||
|
return enableEmbedMode;
|
||||||
|
// #endif
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* 播放视频
|
||||||
|
* 触发 video-play 事件,由父组件处理具体播放逻辑
|
||||||
|
*/
|
||||||
|
async playVideo() {
|
||||||
|
this.$emit('video-play', this.value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
@import './css/common-channel.scss';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 视频卡片容器样式
|
||||||
|
* 包含卡片基础样式、悬停效果和列表模式样式
|
||||||
|
*/
|
||||||
|
.channel-video {
|
||||||
|
position: relative;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
|
||||||
|
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
|
||||||
|
// 悬停效果
|
||||||
|
&:hover {
|
||||||
|
transform: translateY(-4rpx);
|
||||||
|
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 列表模式样式调整
|
||||||
|
&.list-mode {
|
||||||
|
border-radius: 10rpx;
|
||||||
|
box-shadow: 0 2rpx 10rpx rgba(0, 0, 0, 0.08);
|
||||||
|
|
||||||
|
.video-info {
|
||||||
|
padding: 14rpx;
|
||||||
|
|
||||||
|
.video-title {
|
||||||
|
font-size: 24rpx;
|
||||||
|
margin-bottom: 6rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 视频容器样式
|
||||||
|
* 用于跳转式播放模式
|
||||||
|
*/
|
||||||
|
.video-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 嵌入式视频容器样式
|
||||||
|
* 与视频容器样式保持一致
|
||||||
|
*/
|
||||||
|
.embed-video-container {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
height: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 视频封面容器
|
||||||
|
*/
|
||||||
|
.video-cover-wrap {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
padding-top: 56.25%;
|
||||||
|
/* 16:9 比例 */
|
||||||
|
height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
border-radius: 12rpx 12rpx 0 0;
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
|
||||||
|
// 悬停时缩放效果
|
||||||
|
.channel-video:hover & {
|
||||||
|
transform: scale(1.02);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 列表模式下的边框圆角调整
|
||||||
|
.channel-video.list-mode & {
|
||||||
|
border-radius: 10rpx 10rpx 0 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 视频封面图片
|
||||||
|
* 绝对定位填充整个容器
|
||||||
|
*/
|
||||||
|
.video-cover {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 视频统计信息遮罩
|
||||||
|
* 显示在视频封面底部
|
||||||
|
*/
|
||||||
|
.video-stats-overlay {
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
background: linear-gradient(transparent, rgba(0, 0, 0, 0.7));
|
||||||
|
padding: 12rpx 16rpx;
|
||||||
|
color: #fff;
|
||||||
|
font-size: 20rpx;
|
||||||
|
text-align: right;
|
||||||
|
font-weight: 500;
|
||||||
|
transition: opacity 0.3s ease;
|
||||||
|
|
||||||
|
// 悬停时的透明度变化
|
||||||
|
.channel-video:hover & {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.channel-video:not(:hover) & {
|
||||||
|
opacity: 0.8;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 视频信息区域
|
||||||
|
* 包含标题和统计信息
|
||||||
|
*/
|
||||||
|
.video-info {
|
||||||
|
padding: 16rpx;
|
||||||
|
flex: 1;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
background-color: #fff;
|
||||||
|
transition: background-color 0.3s ease;
|
||||||
|
|
||||||
|
// 悬停时的背景色变化
|
||||||
|
.channel-video:hover & {
|
||||||
|
background-color: #fafafa;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 视频标题
|
||||||
|
* 支持多行显示控制和渐变遮罩效果
|
||||||
|
*/
|
||||||
|
.video-title {
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #222;
|
||||||
|
margin-bottom: 10rpx;
|
||||||
|
line-height: 1.4;
|
||||||
|
display: -webkit-box;
|
||||||
|
-webkit-line-clamp: var(--title-line-clamp, 2);
|
||||||
|
-webkit-box-orient: vertical;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
transition: color 0.3s ease;
|
||||||
|
position: relative;
|
||||||
|
word-break: break-all;
|
||||||
|
word-wrap: break-word;
|
||||||
|
white-space: normal;
|
||||||
|
|
||||||
|
// 悬停时的颜色变化
|
||||||
|
.channel-video:hover & {
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 列表模式下的样式调整
|
||||||
|
.channel-video.list-mode & {
|
||||||
|
-webkit-line-clamp: var(--title-line-clamp, 1);
|
||||||
|
font-size: 24rpx;
|
||||||
|
margin-bottom: 6rpx;
|
||||||
|
font-weight: 500;
|
||||||
|
color: #333;
|
||||||
|
line-height: 1.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 添加渐变遮罩效果,让过长的文字有柔和的结束
|
||||||
|
&::after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
width: 40rpx;
|
||||||
|
height: 1.4em;
|
||||||
|
background: linear-gradient(to right, transparent, #fff 90%);
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 列表模式下的遮罩高度调整
|
||||||
|
.channel-video.list-mode &::after {
|
||||||
|
height: 1.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根据行数调整遮罩高度
|
||||||
|
&[style*="--title-line-clamp: 1"]::after {
|
||||||
|
height: 1.4em;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[style*="--title-line-clamp: 3"]::after {
|
||||||
|
height: 4.2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
&[style*="--title-line-clamp: 4"]::after {
|
||||||
|
height: 5.6em;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 列表模式下的多行遮罩高度调整
|
||||||
|
.channel-video.list-mode &[style*="--title-line-clamp: 1"]::after {
|
||||||
|
height: 1.3em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.channel-video.list-mode &[style*="--title-line-clamp: 3"]::after {
|
||||||
|
height: 3.9em;
|
||||||
|
}
|
||||||
|
|
||||||
|
.channel-video.list-mode &[style*="--title-line-clamp: 4"]::after {
|
||||||
|
height: 5.2em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 视频统计信息
|
||||||
|
* 显示观看次数等数据
|
||||||
|
*/
|
||||||
|
.video-stats {
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #999;
|
||||||
|
margin-top: 6rpx;
|
||||||
|
transition: color 0.3s ease;
|
||||||
|
|
||||||
|
// 悬停时的颜色变化
|
||||||
|
.channel-video:hover & {
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 列表模式下的播放按钮样式
|
||||||
|
* 更小的尺寸和悬停效果
|
||||||
|
*/
|
||||||
|
.channel-video.list-mode .channel-play-btn {
|
||||||
|
width: 50rpx;
|
||||||
|
height: 50rpx;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
// 悬停效果
|
||||||
|
&:hover {
|
||||||
|
background-color: rgba(0, 0, 0, 0.7);
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 播放图标尺寸
|
||||||
|
.play-icon {
|
||||||
|
width: 25rpx;
|
||||||
|
height: 25rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通用播放按钮样式优化
|
||||||
|
* 添加悬停效果
|
||||||
|
*/
|
||||||
|
.channel-play-btn {
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
// 悬停效果
|
||||||
|
&:hover {
|
||||||
|
background-color: rgba(0, 0, 0, 0.6);
|
||||||
|
transform: scale(1.05);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 纯 CSS 播放按钮图标
|
||||||
|
*/
|
||||||
|
.play-icon-css {
|
||||||
|
width: 0;
|
||||||
|
height: 0;
|
||||||
|
border-top: 12rpx solid transparent;
|
||||||
|
border-bottom: 12rpx solid transparent;
|
||||||
|
border-left: 18rpx solid #fff;
|
||||||
|
margin-left: 4rpx;
|
||||||
|
transition: transform 0.3s ease;
|
||||||
|
|
||||||
|
// 悬停时的缩放效果
|
||||||
|
.channel-play-btn:hover & {
|
||||||
|
transform: scale(1.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 列表模式下的尺寸调整
|
||||||
|
.channel-video.list-mode & {
|
||||||
|
border-top: 10rpx solid transparent;
|
||||||
|
border-bottom: 10rpx solid transparent;
|
||||||
|
border-left: 15rpx solid #fff;
|
||||||
|
margin-left: 3rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 原生组件样式控制
|
||||||
|
* 确保 native-component 和 channel-video 正确填充容器
|
||||||
|
*/
|
||||||
|
native-component {
|
||||||
|
position: absolute;
|
||||||
|
top: 0;
|
||||||
|
left: 0;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* channel-video 组件样式控制
|
||||||
|
* 避免受到 wx-channel-video 全局样式的影响
|
||||||
|
*/
|
||||||
|
channel-video {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
min-width: 100%;
|
||||||
|
min-height: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
max-height: 100%;
|
||||||
|
object-fit: cover;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -251,14 +251,9 @@
|
|||||||
<diy-icon :value="item"></diy-icon>
|
<diy-icon :value="item"></diy-icon>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<template v-if="item.componentName == 'WechatChannel'">
|
<template v-if="['ChannelList', 'WechatChannel'].includes(item.componentName)">
|
||||||
<!-- 微信视频号 -->
|
<!-- 视频号列表 -->
|
||||||
<diy-wechat-channel :value="item"></diy-wechat-channel>
|
<diy-channel-list :value="item"></diy-channel-list>
|
||||||
</template>
|
|
||||||
|
|
||||||
<template v-if="item.componentName == 'WechatChannelList'">
|
|
||||||
<!-- 微信视频号列表 -->
|
|
||||||
<diy-wechat-channel-list :value="item"></diy-wechat-channel-list>
|
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<!-- 自定义扩展组件 -->
|
<!-- 自定义扩展组件 -->
|
||||||
|
|||||||
@@ -1,458 +0,0 @@
|
|||||||
<template>
|
|
||||||
<view :style="componentStyle">
|
|
||||||
<!-- 固定布局模式 -->
|
|
||||||
<view v-if="value.showStyle == 'fixed'" :class="['channel-list', 'row1-of' + value.rowCount]"
|
|
||||||
style="padding:20rpx;">
|
|
||||||
<view v-for="(item, index) in value.list" :key="index" class="channel-item" @tap="playVideo(item)">
|
|
||||||
<view class="channel-img-wrap">
|
|
||||||
<image class="channel-img" style="border-radius:10rpx;" :src="$util.img(item.coverUrl)"
|
|
||||||
mode="aspectFill" @error="imgError(index)"></image>
|
|
||||||
<view class="channel-play-btn">
|
|
||||||
<image class="play-icon" style="width:30rpx;" :src="$util.img('addon/personnel/shop/view/enterprise/play.png')"
|
|
||||||
mode="widthFix"></image>
|
|
||||||
</view>
|
|
||||||
<view class="channel-stats">
|
|
||||||
<text>{{ item.viewCount }}次观看</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
<view class="channel-info-wrap">
|
|
||||||
<view class="channel-name" :style="{
|
|
||||||
'font-size': (value.font.size * 2 + 'rpx') + ';',
|
|
||||||
'font-weight': value.font.weight + ';',
|
|
||||||
'color': value.font.color + ';'
|
|
||||||
}">{{ item.channelName }}</view>
|
|
||||||
<view class="video-title" :style="{
|
|
||||||
'font-size': (value.font.size * 1.8 + 'rpx') + ';',
|
|
||||||
'color': '#666;'
|
|
||||||
}">{{ item.videoTitle }}</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
|
|
||||||
<!-- 其他布局模式 -->
|
|
||||||
<scroll-view v-else :class="['channel-nav', value.showStyle == 'fixed' ? 'fixed-layout' : value.showStyle]"
|
|
||||||
:scroll-x="value.showStyle == 'singleSlide'">
|
|
||||||
<view class="uni-scroll-view-content">
|
|
||||||
<view v-for="(item, index) in value.list" :key="index" :class="['channel-nav-item', value.mode]"
|
|
||||||
:style="{ width: (100 / value.rowCount + '%') + ';' }" @tap="playVideo(item)">
|
|
||||||
<view class="channel-img">
|
|
||||||
<image :style="{
|
|
||||||
'max-width': '100%;',
|
|
||||||
'border-radius': '8rpx;'
|
|
||||||
}" :src="$util.img(item.coverUrl)" mode="aspectFill"
|
|
||||||
:show-menu-by-longpress="true"></image>
|
|
||||||
<view class="channel-play-btn">
|
|
||||||
<image class="play-icon" style="width:30rpx;" :src="$util.img('addon/personnel/shop/view/enterprise/play.png')"
|
|
||||||
mode="widthFix"></image>
|
|
||||||
</view>
|
|
||||||
<view class="channel-stats">
|
|
||||||
<text>{{ item.viewCount }}次观看</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
<view class="channel-text" :style="{
|
|
||||||
'margin-left': '16rpx;',
|
|
||||||
'font-size': (value.font.size * 2 + 'rpx') + ';',
|
|
||||||
'font-weight': value.font.weight + ';',
|
|
||||||
'color': value.font.color + ';'
|
|
||||||
}">{{ item.videoTitle }}</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</scroll-view>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
|
|
||||||
import DiyMinx from './minx.js'
|
|
||||||
export default {
|
|
||||||
name: 'diy-wechat-channel-list',
|
|
||||||
props: {
|
|
||||||
value: {
|
|
||||||
type: Object,
|
|
||||||
default: () => ({})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
mixins: [DiyMinx],
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
pageWidth: '',
|
|
||||||
indicatorDots: false,
|
|
||||||
swiperCurrent: 0
|
|
||||||
}
|
|
||||||
},
|
|
||||||
created() {
|
|
||||||
// 组件创建时的逻辑
|
|
||||||
},
|
|
||||||
watch: {
|
|
||||||
componentRefresh(newValue) {
|
|
||||||
// 监听组件刷新
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
componentStyle() {
|
|
||||||
let style = '';
|
|
||||||
style += 'background-color:' + this.value.componentBgColor + ';';
|
|
||||||
|
|
||||||
if (this.value.componentAngle == 'round') {
|
|
||||||
style += 'border-top-left-radius:' + (2 * this.value.topAroundRadius) + 'rpx;';
|
|
||||||
style += 'border-top-right-radius:' + (2 * this.value.topAroundRadius) + 'rpx;';
|
|
||||||
style += 'border-bottom-left-radius:' + (2 * this.value.bottomAroundRadius) + 'rpx;';
|
|
||||||
style += 'border-bottom-right-radius:' + (2 * this.value.bottomAroundRadius) + 'rpx;';
|
|
||||||
}
|
|
||||||
|
|
||||||
style += 'box-shadow:' + (this.value.ornament.type == 'shadow' ? '0 0 10rpx ' + this.value.ornament.color : '') + ';';
|
|
||||||
style += 'border:' + (this.value.ornament.type == 'stroke' ? '2rpx solid ' + this.value.ornament.color : '') + ';';
|
|
||||||
|
|
||||||
return style;
|
|
||||||
},
|
|
||||||
|
|
||||||
swiperHeight() {
|
|
||||||
let height = 0;
|
|
||||||
|
|
||||||
if (this.value.mode == 'graphic') {
|
|
||||||
height = (49 + this.value.imageSize) * this.value.pageCount;
|
|
||||||
} else if (this.value.mode == 'img') {
|
|
||||||
height = (22 + this.value.imageSize) * this.value.pageCount;
|
|
||||||
} else if (this.value.mode == 'text') {
|
|
||||||
height = 43 * this.value.pageCount;
|
|
||||||
}
|
|
||||||
|
|
||||||
return 'height:' + (2 * height) + 'rpx';
|
|
||||||
},
|
|
||||||
|
|
||||||
isIndicatorDots() {
|
|
||||||
return this.value.carousel.type != 'hide' &&
|
|
||||||
1 != Math.ceil(this.value.list.length / (this.value.pageCount * this.value.rowCount));
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
// 播放视频
|
|
||||||
async playVideo(item) {
|
|
||||||
await this.__$emitEvent({
|
|
||||||
eventName: 'video-play', data: item, promiseCallback: (event, handler, awaitedResult) => {
|
|
||||||
if (!awaitedResult) return;
|
|
||||||
// 检查微信环境
|
|
||||||
if (typeof wx === 'undefined') {
|
|
||||||
console.error('当前环境不是微信小程序');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 检查基础库版本
|
|
||||||
const systemInfo = wx.getSystemInfoSync();
|
|
||||||
const SDKVersion = systemInfo.SDKVersion;
|
|
||||||
const versionCompare = (v1, v2) => {
|
|
||||||
const arr1 = v1.split('.');
|
|
||||||
const arr2 = v2.split('.');
|
|
||||||
for (let i = 0; i < Math.max(arr1.length, arr2.length); i++) {
|
|
||||||
const num1 = parseInt(arr1[i] || 0);
|
|
||||||
const num2 = parseInt(arr2[i] || 0);
|
|
||||||
if (num1 > num2) return 1;
|
|
||||||
if (num1 < num2) return -1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
};
|
|
||||||
if (versionCompare(SDKVersion, '2.19.2') < 0) {
|
|
||||||
console.error('当前微信基础库版本过低,需要 2.19.2 或以上版本');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 调用微信视频号播放API
|
|
||||||
if (wx.openChannelsActivity) {
|
|
||||||
wx.openChannelsActivity({
|
|
||||||
feedId: item.feedId,
|
|
||||||
finderUserName: item.finderUserName,
|
|
||||||
success: (res) => {
|
|
||||||
console.log('打开视频号成功', res);
|
|
||||||
},
|
|
||||||
fail: (err) => {
|
|
||||||
console.error('打开视频号失败', err);
|
|
||||||
// 错误码处理
|
|
||||||
switch (err.errCode) {
|
|
||||||
case 40001:
|
|
||||||
console.error('错误:40001,检查主体要求或嵌入式打开的关联关系');
|
|
||||||
break;
|
|
||||||
case 40002:
|
|
||||||
console.error('错误:40002,参数错误,检查 feedId 和 finderUserName');
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
console.error('错误:' + err.errCode + ',' + (err.errMsg || '未知错误'));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
console.error('当前环境不支持微信视频号');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
|
|
||||||
// 图片加载错误处理
|
|
||||||
imgError(index) {
|
|
||||||
// 图片加载失败的处理逻辑
|
|
||||||
console.log('图片加载失败:', index);
|
|
||||||
// 为失败的图片设置默认图片
|
|
||||||
const item = this.value.list[index];
|
|
||||||
if (item) {
|
|
||||||
// 使用默认图片替代加载失败的图片
|
|
||||||
item.coverUrl = 'addon/personnel/shop/view/enterprise/default-video-cover.png';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
|
||||||
.channel-list {
|
|
||||||
&.row1-of3 {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
|
|
||||||
.channel-item {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
margin-top: 20rpx;
|
|
||||||
width: calc(33.3333333% - 14rpx);
|
|
||||||
box-sizing: border-box;
|
|
||||||
|
|
||||||
&:nth-child(3n + 3) {
|
|
||||||
width: calc(33.33% - 15rpx);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:nth-of-type(1),
|
|
||||||
&:nth-of-type(2),
|
|
||||||
&:nth-of-type(3) {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:nth-child(3n) {
|
|
||||||
width: calc(33.3333333% - 15rpx);
|
|
||||||
}
|
|
||||||
|
|
||||||
&:nth-child(3n-1) {
|
|
||||||
margin-left: 20rpx;
|
|
||||||
margin-right: 20rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.channel-img-wrap {
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
height: 220rpx;
|
|
||||||
border-radius: 10rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.channel-img {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.channel-play-btn {
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
width: 60rpx;
|
|
||||||
height: 60rpx;
|
|
||||||
background-color: rgba(0, 0, 0, 0.4);
|
|
||||||
border-radius: 50%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.channel-stats {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
background: linear-gradient(transparent, rgba(0, 0, 0, 0.6));
|
|
||||||
padding: 10rpx;
|
|
||||||
color: #fff;
|
|
||||||
font-size: 20rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.channel-info-wrap {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
flex: 1;
|
|
||||||
padding: 10rpx 0;
|
|
||||||
|
|
||||||
.channel-name {
|
|
||||||
margin-bottom: 4rpx;
|
|
||||||
white-space: nowrap;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.video-title {
|
|
||||||
white-space: nowrap;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.row1-of2 {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
justify-content: space-between;
|
|
||||||
|
|
||||||
.channel-item {
|
|
||||||
position: relative;
|
|
||||||
margin-top: 20rpx;
|
|
||||||
width: calc(50% - 10rpx);
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
box-sizing: border-box;
|
|
||||||
|
|
||||||
&:nth-child(2n) {
|
|
||||||
margin-right: 0 !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:nth-of-type(1),
|
|
||||||
&:nth-of-type(2) {
|
|
||||||
margin-top: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.channel-img-wrap {
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
height: 340rpx;
|
|
||||||
border-radius: 10rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.channel-img {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.channel-play-btn {
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
width: 80rpx;
|
|
||||||
height: 80rpx;
|
|
||||||
background-color: rgba(0, 0, 0, 0.4);
|
|
||||||
border-radius: 50%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.channel-stats {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
background: linear-gradient(transparent, rgba(0, 0, 0, 0.6));
|
|
||||||
padding: 10rpx;
|
|
||||||
color: #fff;
|
|
||||||
font-size: 20rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.channel-info-wrap {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
flex: 1;
|
|
||||||
padding: 10rpx 0;
|
|
||||||
|
|
||||||
.channel-name {
|
|
||||||
margin-bottom: 4rpx;
|
|
||||||
white-space: nowrap;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.video-title {
|
|
||||||
white-space: nowrap;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.channel-nav {
|
|
||||||
padding: 16rpx;
|
|
||||||
box-sizing: border-box;
|
|
||||||
|
|
||||||
&.fixed-layout {
|
|
||||||
.uni-scroll-view-content {
|
|
||||||
display: flex;
|
|
||||||
flex-wrap: wrap;
|
|
||||||
justify-content: space-between;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.singleSlide {
|
|
||||||
.uni-scroll-view-content {
|
|
||||||
display: flex;
|
|
||||||
}
|
|
||||||
|
|
||||||
.channel-nav-item {
|
|
||||||
flex-shrink: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.channel-nav-item {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
align-items: center;
|
|
||||||
padding: 14rpx 0;
|
|
||||||
box-sizing: border-box;
|
|
||||||
|
|
||||||
.channel-img {
|
|
||||||
position: relative;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
width: 100%;
|
|
||||||
height: 200rpx;
|
|
||||||
border-radius: 10rpx;
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
image {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.channel-play-btn {
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
width: 60rpx;
|
|
||||||
height: 60rpx;
|
|
||||||
background-color: rgba(0, 0, 0, 0.4);
|
|
||||||
border-radius: 50%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.channel-stats {
|
|
||||||
position: absolute;
|
|
||||||
bottom: 0;
|
|
||||||
left: 0;
|
|
||||||
right: 0;
|
|
||||||
background: linear-gradient(transparent, rgba(0, 0, 0, 0.6));
|
|
||||||
padding: 10rpx;
|
|
||||||
color: #fff;
|
|
||||||
font-size: 20rpx;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.channel-text {
|
|
||||||
padding-top: 12rpx;
|
|
||||||
line-height: 1.5;
|
|
||||||
white-space: nowrap;
|
|
||||||
text-overflow: ellipsis;
|
|
||||||
overflow: hidden;
|
|
||||||
width: 100%;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@@ -1,232 +0,0 @@
|
|||||||
<template>
|
|
||||||
<!-- #ifdef MP-WEIXIN -->
|
|
||||||
<view class="diy-wechat-channel" :style="channelWarpCss">
|
|
||||||
<view class="channel-header" @tap="handlerClick">
|
|
||||||
<image class="channel-avatar" :src="$util.img(value.avatarUrl)" mode="aspectFill"></image>
|
|
||||||
<view class="channel-info">
|
|
||||||
<view class="channel-name">{{ value.channelName }}</view>
|
|
||||||
<view class="channel-follow" v-if="value.showFollow">关注</view>
|
|
||||||
</view>
|
|
||||||
<image class="channel-arrow" :src="$util.img('addon/personnel/shop/view/enterprise/arrow.png')" mode="aspectFill"></image>
|
|
||||||
</view>
|
|
||||||
<!-- 嵌入式视频播放 -->
|
|
||||||
<native-component v-if="value.embedMode && typeof wx !== 'undefined' && wx.canIUse('component.channel-video')">
|
|
||||||
<channel-video
|
|
||||||
:feed-id="value.feedId"
|
|
||||||
:finder-user-name="value.finderUserName"
|
|
||||||
:feed-token="value.feedToken"
|
|
||||||
style="width: 100%; height: 320rpx;">
|
|
||||||
</channel-video>
|
|
||||||
</native-component>
|
|
||||||
<!-- 跳转式视频播放 -->
|
|
||||||
<view v-else class="channel-video" @tap="playVideo">
|
|
||||||
<image class="video-cover" :src="$util.img(value.coverUrl)" mode="aspectFill"></image>
|
|
||||||
<view class="video-play-btn">
|
|
||||||
<image class="play-icon" :src="$util.img('addon/personnel/shop/view/enterprise/play.png')" mode="aspectFill"></image>
|
|
||||||
</view>
|
|
||||||
<view class="video-info">
|
|
||||||
<view class="video-title">{{ value.videoTitle }}</view>
|
|
||||||
<view class="video-stats">
|
|
||||||
<text>{{ value.viewCount }}次观看</text>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
</view>
|
|
||||||
<!-- #endif -->
|
|
||||||
</template>
|
|
||||||
|
|
||||||
|
|
||||||
<script>
|
|
||||||
// 微信视频号组件
|
|
||||||
import DiyMinx from './minx.js'
|
|
||||||
export default {
|
|
||||||
name: 'diy-wechat-channel',
|
|
||||||
props: {
|
|
||||||
value: {
|
|
||||||
type: Object
|
|
||||||
}
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {};
|
|
||||||
},
|
|
||||||
created() { },
|
|
||||||
mixins: [DiyMinx],
|
|
||||||
watch: {
|
|
||||||
// 组件刷新监听
|
|
||||||
componentRefresh: function (nval) { }
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
channelWarpCss: function () {
|
|
||||||
var obj = '';
|
|
||||||
if (this.value.componentAngle == 'round') {
|
|
||||||
obj += 'border-top-left-radius:' + this.value.topAroundRadius * 2 + 'rpx;';
|
|
||||||
obj += 'border-top-right-radius:' + this.value.topAroundRadius * 2 + 'rpx;';
|
|
||||||
obj += 'border-bottom-left-radius:' + this.value.bottomAroundRadius * 2 + 'rpx;';
|
|
||||||
obj += 'border-bottom-right-radius:' + this.value.bottomAroundRadius * 2 + 'rpx;';
|
|
||||||
}
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
async handlerClick() {
|
|
||||||
await this.__$emitEvent({
|
|
||||||
eventName: 'channel-tap', data: this.value, promiseCallback: (event, handler, awaitedResult) => {
|
|
||||||
if (!awaitedResult) return;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
},
|
|
||||||
async playVideo() {
|
|
||||||
await this.__$emitEvent({
|
|
||||||
eventName: 'video-play', data: this.value, promiseCallback: (event, handler, awaitedResult) => {
|
|
||||||
if (!awaitedResult) return;
|
|
||||||
// 检查微信环境
|
|
||||||
if (typeof wx === 'undefined') {
|
|
||||||
console.error('当前环境不是微信小程序');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 检查基础库版本
|
|
||||||
const systemInfo = wx.getSystemInfoSync();
|
|
||||||
const SDKVersion = systemInfo.SDKVersion;
|
|
||||||
const versionCompare = (v1, v2) => {
|
|
||||||
const arr1 = v1.split('.');
|
|
||||||
const arr2 = v2.split('.');
|
|
||||||
for (let i = 0; i < Math.max(arr1.length, arr2.length); i++) {
|
|
||||||
const num1 = parseInt(arr1[i] || 0);
|
|
||||||
const num2 = parseInt(arr2[i] || 0);
|
|
||||||
if (num1 > num2) return 1;
|
|
||||||
if (num1 < num2) return -1;
|
|
||||||
}
|
|
||||||
return 0;
|
|
||||||
};
|
|
||||||
if (versionCompare(SDKVersion, '2.19.2') < 0) {
|
|
||||||
console.error('当前微信基础库版本过低,需要 2.19.2 或以上版本');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// 调用微信视频号播放API
|
|
||||||
if (wx.openChannelsActivity) {
|
|
||||||
wx.openChannelsActivity({
|
|
||||||
feedId: this.value.feedId,
|
|
||||||
finderUserName: this.value.finderUserName,
|
|
||||||
success: (res) => {
|
|
||||||
console.log('打开视频号成功', res);
|
|
||||||
},
|
|
||||||
fail: (err) => {
|
|
||||||
console.error('打开视频号失败', err);
|
|
||||||
// 错误码处理
|
|
||||||
switch (err.errCode) {
|
|
||||||
case 40001:
|
|
||||||
console.error('错误:40001,检查主体要求或嵌入式打开的关联关系');
|
|
||||||
break;
|
|
||||||
case 40002:
|
|
||||||
console.error('错误:40002,参数错误,检查 feedId 和 finderUserName');
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
console.error('错误:' + err.errCode + ',' + (err.errMsg || '未知错误'));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
console.error('当前环境不支持微信视频号');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
.diy-wechat-channel {
|
|
||||||
width: 100%;
|
|
||||||
background-color: #fff;
|
|
||||||
border-radius: 12rpx;
|
|
||||||
overflow: hidden;
|
|
||||||
box-shadow: 0 2rpx 12rpx rgba(0, 0, 0, 0.08);
|
|
||||||
}
|
|
||||||
|
|
||||||
.channel-header {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
padding: 16rpx;
|
|
||||||
border-bottom: 1rpx solid #f0f0f0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.channel-avatar {
|
|
||||||
width: 60rpx;
|
|
||||||
height: 60rpx;
|
|
||||||
border-radius: 50%;
|
|
||||||
margin-right: 12rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.channel-info {
|
|
||||||
flex: 1;
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.channel-name {
|
|
||||||
font-size: 28rpx;
|
|
||||||
font-weight: 500;
|
|
||||||
color: #333;
|
|
||||||
margin-bottom: 4rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.channel-follow {
|
|
||||||
font-size: 24rpx;
|
|
||||||
color: #07c160;
|
|
||||||
}
|
|
||||||
|
|
||||||
.channel-arrow {
|
|
||||||
width: 24rpx;
|
|
||||||
height: 24rpx;
|
|
||||||
margin-left: 8rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.channel-video {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.video-cover {
|
|
||||||
width: 100%;
|
|
||||||
height: 320rpx;
|
|
||||||
object-fit: cover;
|
|
||||||
}
|
|
||||||
|
|
||||||
.video-play-btn {
|
|
||||||
position: absolute;
|
|
||||||
top: 50%;
|
|
||||||
left: 50%;
|
|
||||||
transform: translate(-50%, -50%);
|
|
||||||
width: 80rpx;
|
|
||||||
height: 80rpx;
|
|
||||||
background-color: rgba(0, 0, 0, 0.4);
|
|
||||||
border-radius: 50%;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.play-icon {
|
|
||||||
width: 40rpx;
|
|
||||||
height: 40rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.video-info {
|
|
||||||
padding: 16rpx;
|
|
||||||
}
|
|
||||||
|
|
||||||
.video-title {
|
|
||||||
font-size: 28rpx;
|
|
||||||
font-weight: 500;
|
|
||||||
color: #333;
|
|
||||||
margin-bottom: 8rpx;
|
|
||||||
line-height: 1.4;
|
|
||||||
}
|
|
||||||
|
|
||||||
.video-stats {
|
|
||||||
font-size: 24rpx;
|
|
||||||
color: #999;
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
188
components-diy/js/wechat-channel.js
Normal file
188
components-diy/js/wechat-channel.js
Normal file
@@ -0,0 +1,188 @@
|
|||||||
|
// 微信视频号组件配置
|
||||||
|
export const wechatChannelConfig = {
|
||||||
|
// 图标相关
|
||||||
|
icon: {
|
||||||
|
defaultSize: 80, // 默认图标尺寸(rpx)
|
||||||
|
smallSize: 60, // 小图标尺寸(rpx)
|
||||||
|
channelArrowSize: 24, // 频道箭头图标尺寸(rpx)
|
||||||
|
channelArrow: 'addon/personnel/shop/view/enterprise/arrow.png', // 频道箭头图标路径
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
// 视频相关配置
|
||||||
|
video: {
|
||||||
|
defaultHeight: 320, // 默认视频高度(rpx)
|
||||||
|
minHeight: 200, // 最小视频高度(rpx)
|
||||||
|
maxHeight: 500, // 最大视频高度(rpx)
|
||||||
|
defaultCoverUrl: 'addon/personnel/shop/view/enterprise/default-video-cover.png', // 默认视频封面
|
||||||
|
},
|
||||||
|
|
||||||
|
// 播放按钮配置
|
||||||
|
playButton: {
|
||||||
|
size: 80, // 标准尺寸(rpx)
|
||||||
|
smallSize: 60, // 小尺寸(rpx)
|
||||||
|
iconSize: 40, // 标准图标尺寸(rpx)
|
||||||
|
smallIconSize: 30, // 小图标尺寸(rpx)
|
||||||
|
background: 'rgba(0, 0, 0, 0.4)', // 背景颜色
|
||||||
|
},
|
||||||
|
|
||||||
|
// 布局配置
|
||||||
|
layout: {
|
||||||
|
borderRadius: 12, // 圆角(rpx)
|
||||||
|
padding: 16, // 内边距(rpx)
|
||||||
|
margin: 10, // 外边距(rpx)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 字体配置
|
||||||
|
font: {
|
||||||
|
channelNameSize: 28, // 频道名称字体大小(rpx)
|
||||||
|
videoTitleSize: 28, // 视频标题字体大小(rpx)
|
||||||
|
statsSize: 24, // 统计信息字体大小(rpx)
|
||||||
|
},
|
||||||
|
|
||||||
|
// 颜色配置
|
||||||
|
color: {
|
||||||
|
channelName: '#333', // 频道名称颜色
|
||||||
|
videoTitle: '#333', // 视频标题颜色
|
||||||
|
stats: '#999', // 统计信息颜色
|
||||||
|
border: '#f0f0f0', // 边框颜色
|
||||||
|
},
|
||||||
|
|
||||||
|
// 微信相关配置
|
||||||
|
wechat: {
|
||||||
|
minSdkVersion: '2.19.2', // 最小微信基础库版本
|
||||||
|
embedComponent: 'channel-video', // 嵌入式视频组件名称
|
||||||
|
},
|
||||||
|
|
||||||
|
// 错误提示配置
|
||||||
|
error: {
|
||||||
|
notWechat: '当前环境不是微信小程序',
|
||||||
|
lowVersion: '当前微信基础库版本过低,需要 2.19.2 或以上版本',
|
||||||
|
notSupported: '当前环境不支持微信视频号',
|
||||||
|
missingFields: '缺少必要字段: {field}',
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
// 获取配置项的辅助函数
|
||||||
|
export const getwechatChannelConfig = (key, defaultValue = null) => {
|
||||||
|
const keys = key.split('.');
|
||||||
|
let config = wechatChannelConfig;
|
||||||
|
|
||||||
|
for (const k of keys) {
|
||||||
|
if (config[k] === undefined) {
|
||||||
|
return defaultValue;
|
||||||
|
}
|
||||||
|
config = config[k];
|
||||||
|
}
|
||||||
|
|
||||||
|
return config;
|
||||||
|
};
|
||||||
|
|
||||||
|
// 微信视频号工具函数
|
||||||
|
export const wechatChannelUtil = {
|
||||||
|
// 版本比较
|
||||||
|
versionCompare(v1, v2) {
|
||||||
|
const arr1 = v1.split('.');
|
||||||
|
const arr2 = v2.split('.');
|
||||||
|
for (let i = 0; i < Math.max(arr1.length, arr2.length); i++) {
|
||||||
|
const num1 = parseInt(arr1[i] || 0);
|
||||||
|
const num2 = parseInt(arr2[i] || 0);
|
||||||
|
if (num1 > num2) return 1;
|
||||||
|
if (num1 < num2) return -1;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
},
|
||||||
|
|
||||||
|
// 检查是否支持嵌入式播放
|
||||||
|
isEmbedModeSupported() {
|
||||||
|
return typeof wx !== 'undefined' && wx.canIUse(wechatChannelConfig.wechat.embedComponent);
|
||||||
|
},
|
||||||
|
|
||||||
|
// 获取默认视频封面
|
||||||
|
getDefaultCoverUrl() {
|
||||||
|
return wechatChannelConfig.video.defaultCoverUrl;
|
||||||
|
},
|
||||||
|
|
||||||
|
// 数据校验
|
||||||
|
validateVideoData(item) {
|
||||||
|
const requiredFields = ['feedId', 'finderUserName'];
|
||||||
|
for (const field of requiredFields) {
|
||||||
|
if (!item[field]) {
|
||||||
|
throw new Error(`缺少必要字段: ${field}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
},
|
||||||
|
|
||||||
|
// 播放视频
|
||||||
|
playVideo(item) {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
try {
|
||||||
|
// 数据校验
|
||||||
|
this.validateVideoData(item);
|
||||||
|
} catch (err) {
|
||||||
|
this.handleError(err, item);
|
||||||
|
reject(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查微信环境
|
||||||
|
if (typeof wx === 'undefined') {
|
||||||
|
const err = new Error(wechatChannelConfig.error.notWechat);
|
||||||
|
this.handleError(err, item);
|
||||||
|
reject(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 检查基础库版本
|
||||||
|
const systemInfo = wx.getSystemInfoSync();
|
||||||
|
const SDKVersion = systemInfo.SDKVersion;
|
||||||
|
if (this.versionCompare(SDKVersion, wechatChannelConfig.wechat.minSdkVersion) < 0) {
|
||||||
|
const err = new Error(wechatChannelConfig.error.lowVersion);
|
||||||
|
this.handleError(err, item);
|
||||||
|
reject(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 调用微信视频号播放API
|
||||||
|
if (wx.openChannelsActivity) {
|
||||||
|
wx.openChannelsActivity({
|
||||||
|
feedId: item.feedId,
|
||||||
|
finderUserName: item.finderUserName,
|
||||||
|
success: (res) => {
|
||||||
|
console.log('打开视频号成功', res);
|
||||||
|
resolve(res);
|
||||||
|
},
|
||||||
|
fail: (err) => {
|
||||||
|
this.handleError(err, item);
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
const err = new Error(wechatChannelConfig.error.notSupported);
|
||||||
|
this.handleError(err, item);
|
||||||
|
reject(err);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// 统一错误处理
|
||||||
|
handleError(err, item) {
|
||||||
|
console.error('微信视频号错误:', err);
|
||||||
|
const { message = '', errMsg = '微信视频播放失败!', errCode = -1 } = err;
|
||||||
|
|
||||||
|
// 错误码5: 表示用户点击了取消. openChannelsActivity:fail cancel
|
||||||
|
if ([5].includes(errCode)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const showErrorToast = (otherMsgs = []) => {
|
||||||
|
uni.showToast({
|
||||||
|
title: [errMsg, message,...otherMsgs].join('\n'),
|
||||||
|
icon: 'none',
|
||||||
|
duration: 2000
|
||||||
|
});
|
||||||
|
}
|
||||||
|
showErrorToast();
|
||||||
|
}
|
||||||
|
};
|
||||||
145
components/ns-video-player-popup/ns-video-player-popup.vue
Normal file
145
components/ns-video-player-popup/ns-video-player-popup.vue
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
<template>
|
||||||
|
<view class="video-player-popup">
|
||||||
|
<uni-popup ref="videoPopup" type="center" :mask-click="true" @change="onVideoPopupChange">
|
||||||
|
<view class="video-popup-content">
|
||||||
|
<view class="video-popup-header">
|
||||||
|
<text class="video-popup-title">{{ videoTitle }}</text>
|
||||||
|
<text class="iconfont icon-close" @click="close"></text>
|
||||||
|
</view>
|
||||||
|
<view class="video-popup-body">
|
||||||
|
<video
|
||||||
|
v-if="videoUrl"
|
||||||
|
:src="videoUrl"
|
||||||
|
controls
|
||||||
|
autoplay
|
||||||
|
class="video-player"
|
||||||
|
></video>
|
||||||
|
<view v-else class="video-error">视频地址不存在</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</uni-popup>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'ns-video-player-popup',
|
||||||
|
props: {
|
||||||
|
/**
|
||||||
|
* 当前播放的视频信息
|
||||||
|
* @type {Object}
|
||||||
|
* @property {string} title - 视频标题
|
||||||
|
* @property {string} videoUrl - 视频地址
|
||||||
|
*/
|
||||||
|
currentVideo: {
|
||||||
|
type: Object,
|
||||||
|
default: null
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
videoUrl() {
|
||||||
|
return this.currentVideo?.videoUrl || '';
|
||||||
|
},
|
||||||
|
|
||||||
|
videoTitle() {
|
||||||
|
return this.currentVideo?.title || '';
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 视频弹窗是否显示
|
||||||
|
* @type {boolean}
|
||||||
|
*/
|
||||||
|
isVisible() {
|
||||||
|
return this.$refs.videoPopup?.visible || false;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* 打开视频弹窗
|
||||||
|
*/
|
||||||
|
open() {
|
||||||
|
if (this.$refs.videoPopup) {
|
||||||
|
this.$refs.videoPopup.open();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 关闭视频弹窗
|
||||||
|
*/
|
||||||
|
close() {
|
||||||
|
if (this.$refs.videoPopup) {
|
||||||
|
this.$refs.videoPopup.close();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 视频弹窗状态变化
|
||||||
|
* @param {Object} e - 弹窗状态变化事件
|
||||||
|
*/
|
||||||
|
onVideoPopupChange(e) {
|
||||||
|
this.$emit('popup-change', e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
/* 视频播放弹窗样式 */
|
||||||
|
.video-player-popup {
|
||||||
|
.video-popup-content {
|
||||||
|
width: 90%;
|
||||||
|
max-width: 600rpx;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-popup-header {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 20rpx;
|
||||||
|
border-bottom: 1rpx solid #f0f0f0;
|
||||||
|
background-color: #fafafa;
|
||||||
|
|
||||||
|
.video-popup-title {
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.icon-close {
|
||||||
|
font-size: 32rpx;
|
||||||
|
color: #999;
|
||||||
|
padding: 0 10rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-popup-body {
|
||||||
|
padding: 20rpx;
|
||||||
|
|
||||||
|
.video-player {
|
||||||
|
width: 100%;
|
||||||
|
height: 400rpx;
|
||||||
|
background-color: #000;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-error {
|
||||||
|
width: 100%;
|
||||||
|
height: 400rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
color: #999;
|
||||||
|
font-size: 28rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -102,58 +102,83 @@
|
|||||||
|
|
||||||
## 3. 组件使用
|
## 3. 组件使用
|
||||||
|
|
||||||
### 3.1 单个视频号组件 (diy-wechat-channel.vue)
|
### 3.1 视频号视频卡片组件 (diy-channel-video.vue)
|
||||||
|
|
||||||
#### 基本用法
|
#### 基本用法
|
||||||
|
|
||||||
```vue
|
```vue
|
||||||
<diy-wechat-channel :value="channelData" />
|
<diy-channel-video :value="videoData" @video-play="handlerVideoPlay" />
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 属性说明
|
#### 属性说明
|
||||||
|
|
||||||
| 属性名 | 类型 | 默认值 | 说明 |
|
| 属性名 | 类型 | 默认值 | 说明 |
|
||||||
|--------|------|--------|------|
|
|--------|------|--------|------|
|
||||||
| channelName | String | "" | 视频号名称 |
|
| value | Object | 必填 | 视频数据对象 |
|
||||||
| avatarUrl | String | "" | 视频号头像URL |
|
| listMode | Boolean | false | 是否为列表模式 |
|
||||||
|
| videoHeight | Number | 220 | 视频高度(仅适用于嵌入式播放) |
|
||||||
|
| titleLineClamp | Number | 1 | 标题显示行数 |
|
||||||
|
| showPlayBtn | Boolean | true | 是否显示播放按钮 |
|
||||||
|
| aspectRatio | String | '16:9' | 视频比例,可选值:'16:9', '3:4' |
|
||||||
|
| coverStyle | Object | {} | 视频封面图样式 |
|
||||||
|
| playBtnStyle | Object | {} | 播放按钮样式 |
|
||||||
|
| autoPlay | Boolean | false | 是否自动播放(仅适用于嵌入式播放) |
|
||||||
|
|
||||||
|
#### value对象属性说明
|
||||||
|
|
||||||
|
| 属性名 | 类型 | 默认值 | 说明 |
|
||||||
|
|--------|------|--------|------|
|
||||||
|
| feedId | String | "" | 视频 feedId |
|
||||||
|
| finderUserName | String | "" | 视频号用户名 |
|
||||||
|
| feedToken | String | "" | 视频 token |
|
||||||
|
| coverUrl | String | "" | 视频封面图 |
|
||||||
| videoTitle | String | "" | 视频标题 |
|
| videoTitle | String | "" | 视频标题 |
|
||||||
| coverUrl | String | "" | 视频封面URL |
|
| viewCount | Number | 0 | 观看次数 |
|
||||||
| feedId | String | "" | 视频号内容ID,用于播放视频 |
|
| showViewCount | Boolean | false | 是否显示观看次数 |
|
||||||
| viewCount | Number | 0 | 视频观看次数 |
|
| embedMode | Boolean | false | 是否启用嵌入式播放 |
|
||||||
| showFollow | Boolean | false | 是否显示关注按钮 |
|
|
||||||
| componentAngle | String | "" | 组件圆角类型,可选值:round |
|
|
||||||
| topAroundRadius | Number | 0 | 顶部圆角半径 |
|
|
||||||
| bottomAroundRadius | Number | 0 | 底部圆角半径 |
|
|
||||||
|
|
||||||
#### 事件说明
|
#### 事件说明
|
||||||
|
|
||||||
| 事件名 | 说明 | 参数 |
|
| 事件名 | 说明 | 参数 |
|
||||||
|--------|------|------|
|
|--------|------|------|
|
||||||
| channel-tap | 点击视频号头像或名称时触发 | 视频号数据对象 |
|
| video-play | 点击视频播放时触发 | 视频数据对象 |
|
||||||
| video-play | 点击视频播放时触发 | 视频号数据对象 |
|
|
||||||
|
|
||||||
### 3.2 视频号列表组件 (diy-wechat-channel-list.vue)
|
### 3.2 视频号列表组件 (diy-channel-list.vue)
|
||||||
|
|
||||||
#### 基本用法
|
#### 基本用法
|
||||||
|
|
||||||
```vue
|
```vue
|
||||||
<diy-wechat-channel-list :value="channelListData" />
|
<diy-channel-list :value="channelListData" />
|
||||||
```
|
```
|
||||||
|
|
||||||
#### 属性说明
|
#### 属性说明
|
||||||
|
|
||||||
| 属性名 | 类型 | 默认值 | 说明 |
|
| 属性名 | 类型 | 默认值 | 说明 |
|
||||||
|--------|------|--------|------|
|
|--------|------|--------|------|
|
||||||
| list | Array | [] | 视频号列表数据 |
|
| value | Object | 必填 | 组件配置数据 |
|
||||||
| rowCount | Number | 2 | 每行显示的视频号数量 |
|
| value.list | Array | [] | 视频号列表数据 |
|
||||||
| showStyle | String | "fixed" | 显示样式,可选值:fixed, singleSlide |
|
| value.rowCount | Number | 2 | 每行显示的视频号数量 |
|
||||||
| mode | String | "" | 显示模式 |
|
| value.showStyle | String | "fixed" | 显示样式,可选值:fixed, singleSlide |
|
||||||
| font | Object | {} | 字体样式配置 |
|
| value.mode | String | "" | 显示模式 |
|
||||||
| componentBgColor | String | "#fff" | 组件背景颜色 |
|
| value.font | Object | {} | 字体样式配置 |
|
||||||
| componentAngle | String | "" | 组件圆角类型,可选值:round |
|
| value.componentBgColor | String | "#fff" | 组件背景颜色 |
|
||||||
| topAroundRadius | Number | 0 | 顶部圆角半径 |
|
| value.componentAngle | String | "" | 组件圆角类型,可选值:round |
|
||||||
| bottomAroundRadius | Number | 0 | 底部圆角半径 |
|
| value.topAroundRadius | Number | 0 | 顶部圆角半径 |
|
||||||
| ornament | Object | {} | 装饰样式配置 |
|
| value.bottomAroundRadius | Number | 0 | 底部圆角半径 |
|
||||||
|
| value.ornament | Object | {} | 装饰样式配置 |
|
||||||
|
| value.titleLineClamp | Number | 1 | 标题显示行数 |
|
||||||
|
| value.showPlayBtn | Boolean | true | 是否显示播放按钮 |
|
||||||
|
| value.aspectRatio | String | '16:9' | 视频比例,可选值:'16:9', '3:4' |
|
||||||
|
| value.coverStyle | Object | {} | 视频封面图样式 |
|
||||||
|
| value.playBtnStyle | Object | {} | 播放按钮样式 |
|
||||||
|
| value.imageSize | Number | 0 | 图片尺寸(仅在特定模式下使用) |
|
||||||
|
| value.pageCount | Number | 1 | 每页显示的视频数量 |
|
||||||
|
| value.carousel | Object | {} | 轮播配置 |
|
||||||
|
| value.carousel.type | String | "" | 轮播类型,可选值:default, hide |
|
||||||
|
| value.carousel.autoplay | Boolean | false | 是否自动播放 |
|
||||||
|
| value.carousel.interval | Number | 3000 | 自动播放间隔,单位毫秒 |
|
||||||
|
| value.carousel.duration | Number | 500 | 切换动画时长,单位毫秒 |
|
||||||
|
| value.carousel.circular | Boolean | false | 是否循环播放 |
|
||||||
|
|
||||||
#### list数组项说明
|
#### list数组项说明
|
||||||
|
|
||||||
@@ -164,6 +189,10 @@
|
|||||||
| videoTitle | String | "" | 视频标题 |
|
| videoTitle | String | "" | 视频标题 |
|
||||||
| feedId | String | "" | 视频号内容ID,用于播放视频 |
|
| feedId | String | "" | 视频号内容ID,用于播放视频 |
|
||||||
| viewCount | Number | 0 | 视频观看次数 |
|
| viewCount | Number | 0 | 视频观看次数 |
|
||||||
|
| embedMode | Boolean | false | 是否启用嵌入式播放 |
|
||||||
|
| showViewCount | Boolean | false | 是否显示观看次数 |
|
||||||
|
| finderUserName | String | "" | 视频号ID,用于嵌入式播放 |
|
||||||
|
| feedToken | String | "" | 视频token,用于嵌入式播放 |
|
||||||
|
|
||||||
## 4. 视频播放实现
|
## 4. 视频播放实现
|
||||||
|
|
||||||
@@ -290,7 +319,19 @@ if (typeof wx !== 'undefined' && wx.openChannelsActivity) {
|
|||||||
- 头像:200x200px
|
- 头像:200x200px
|
||||||
- 封面:640x360px
|
- 封面:640x360px
|
||||||
|
|
||||||
### 5.4 权限和关联
|
### 5.4 播放按钮图标
|
||||||
|
|
||||||
|
- 组件支持两种播放按钮图标方式:
|
||||||
|
1. **图片图标**:通过 `playIcon` 属性设置,优先级较高
|
||||||
|
2. **纯 CSS 图标**:当 `playIcon` 不可用时,组件会自动使用纯 CSS 生成的播放图标,确保播放按钮始终可见
|
||||||
|
|
||||||
|
- 纯 CSS 图标特点:
|
||||||
|
- 不依赖外部图片资源,加载更快
|
||||||
|
- 支持响应式调整大小
|
||||||
|
- 具有与图片图标相同的悬停效果
|
||||||
|
- 在所有环境中都能正常显示
|
||||||
|
|
||||||
|
### 5.5 权限和关联
|
||||||
|
|
||||||
- 确保在微信小程序后台开通了「打开视频号内容」权限
|
- 确保在微信小程序后台开通了「打开视频号内容」权限
|
||||||
- 确保小程序已与视频号成功关联
|
- 确保小程序已与视频号成功关联
|
||||||
@@ -331,6 +372,7 @@ if (typeof wx !== 'undefined' && wx.openChannelsActivity) {
|
|||||||
- 可通过组件的 `componentAngle`、`topAroundRadius`、`bottomAroundRadius` 等属性调整组件样式
|
- 可通过组件的 `componentAngle`、`topAroundRadius`、`bottomAroundRadius` 等属性调整组件样式
|
||||||
- 可通过 `componentBgColor` 属性调整组件背景颜色
|
- 可通过 `componentBgColor` 属性调整组件背景颜色
|
||||||
- 可通过 `font` 属性调整字体样式
|
- 可通过 `font` 属性调整字体样式
|
||||||
|
- 可通过 `aspectRatio` 属性调整视频比例,支持 '16:9' 和 '3:4' 两种比例
|
||||||
|
|
||||||
### 6.6 环境兼容性问题
|
### 6.6 环境兼容性问题
|
||||||
|
|
||||||
@@ -392,25 +434,66 @@ if (typeof wx !== 'undefined' && wx.openChannelsActivity) {
|
|||||||
|
|
||||||
## 7. 示例代码
|
## 7. 示例代码
|
||||||
|
|
||||||
### 7.1 单个视频号示例
|
### 7.1 视频号视频卡片组件示例
|
||||||
|
|
||||||
|
```javascript
|
||||||
|
// 视频卡片数据
|
||||||
|
const videoData = {
|
||||||
|
channelName: "示例视频号",
|
||||||
|
coverUrl: "https://example.com/cover.jpg",
|
||||||
|
videoTitle: "这是一个示例视频,标题可能会很长,需要测试多行显示效果",
|
||||||
|
feedId: "v02004g10000c3f7l7j5u87l33n8f160",
|
||||||
|
viewCount: 12345,
|
||||||
|
embedMode: false,
|
||||||
|
showViewCount: true,
|
||||||
|
finderUserName: "example_finder",
|
||||||
|
feedToken: "example_feed_token"
|
||||||
|
};
|
||||||
|
|
||||||
|
// 视频播放事件处理
|
||||||
|
function handlerVideoPlay(data) {
|
||||||
|
console.log("视频播放", data);
|
||||||
|
// 可以在这里处理视频播放逻辑
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 7.2 单个视频号示例
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// 单个视频号数据
|
// 单个视频号数据
|
||||||
const channelData = {
|
const channelData = {
|
||||||
channelName: "示例视频号",
|
channelName: "示例视频号",
|
||||||
avatarUrl: "https://example.com/avatar.jpg",
|
avatarUrl: "https://example.com/avatar.jpg",
|
||||||
videoTitle: "这是一个示例视频",
|
videoTitle: "这是一个示例视频,标题可能会很长,需要测试多行显示效果",
|
||||||
coverUrl: "https://example.com/cover.jpg",
|
coverUrl: "https://example.com/cover.jpg",
|
||||||
feedId: "v02004g10000c3f7l7j5u87l33n8f160",
|
feedId: "v02004g10000c3f7l7j5u87l33n8f160",
|
||||||
viewCount: 12345,
|
viewCount: 12345,
|
||||||
showFollow: true,
|
showFollow: true,
|
||||||
componentAngle: "round",
|
componentAngle: "round",
|
||||||
topAroundRadius: 10,
|
topAroundRadius: 10,
|
||||||
bottomAroundRadius: 10
|
bottomAroundRadius: 10,
|
||||||
|
embedMode: false,
|
||||||
|
showViewCount: true,
|
||||||
|
finderUserName: "example_finder",
|
||||||
|
feedToken: "example_feed_token",
|
||||||
|
titleLineClamp: 2,
|
||||||
|
showPlayBtn: true,
|
||||||
|
aspectRatio: "16:9",
|
||||||
|
coverStyle: {
|
||||||
|
width: "100%",
|
||||||
|
height: "0",
|
||||||
|
paddingTop: "56.25%" // 16:9 比例
|
||||||
|
},
|
||||||
|
playBtnStyle: {
|
||||||
|
width: "60rpx",
|
||||||
|
height: "60rpx",
|
||||||
|
borderRadius: "30rpx",
|
||||||
|
backgroundColor: "rgba(0, 0, 0, 0.6)"
|
||||||
|
}
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
### 7.2 视频号列表示例
|
### 7.3 视频号列表示例
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
// 视频号列表数据
|
// 视频号列表数据
|
||||||
@@ -419,20 +502,29 @@ const channelListData = {
|
|||||||
{
|
{
|
||||||
channelName: "示例视频号1",
|
channelName: "示例视频号1",
|
||||||
coverUrl: "https://example.com/cover1.jpg",
|
coverUrl: "https://example.com/cover1.jpg",
|
||||||
videoTitle: "这是示例视频1",
|
videoTitle: "这是示例视频1,标题可能会很长,需要测试多行显示效果",
|
||||||
feedId: "v02004g10000c3f7l7j5u87l33n8f160",
|
feedId: "v02004g10000c3f7l7j5u87l33n8f160",
|
||||||
viewCount: 12345
|
viewCount: 12345,
|
||||||
|
embedMode: false,
|
||||||
|
showViewCount: true,
|
||||||
|
finderUserName: "example_finder1",
|
||||||
|
feedToken: "example_feed_token1"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
channelName: "示例视频号2",
|
channelName: "示例视频号2",
|
||||||
coverUrl: "https://example.com/cover2.jpg",
|
coverUrl: "https://example.com/cover2.jpg",
|
||||||
videoTitle: "这是示例视频2",
|
videoTitle: "这是示例视频2,标题可能会很长,需要测试多行显示效果",
|
||||||
feedId: "v02004g10000c3f7m7j5u87l33n8f161",
|
feedId: "v02004g10000c3f7m7j5u87l33n8f161",
|
||||||
viewCount: 67890
|
viewCount: 67890,
|
||||||
|
embedMode: false,
|
||||||
|
showViewCount: true,
|
||||||
|
finderUserName: "example_finder2",
|
||||||
|
feedToken: "example_feed_token2"
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
rowCount: 2,
|
rowCount: 2,
|
||||||
showStyle: "fixed",
|
showStyle: "fixed",
|
||||||
|
mode: "",
|
||||||
font: {
|
font: {
|
||||||
size: 14,
|
size: 14,
|
||||||
weight: "normal",
|
weight: "normal",
|
||||||
@@ -441,7 +533,31 @@ const channelListData = {
|
|||||||
componentBgColor: "#fff",
|
componentBgColor: "#fff",
|
||||||
componentAngle: "round",
|
componentAngle: "round",
|
||||||
topAroundRadius: 10,
|
topAroundRadius: 10,
|
||||||
bottomAroundRadius: 10
|
bottomAroundRadius: 10,
|
||||||
|
ornament: {},
|
||||||
|
titleLineClamp: 2,
|
||||||
|
showPlayBtn: true,
|
||||||
|
aspectRatio: "16:9",
|
||||||
|
coverStyle: {
|
||||||
|
width: "100%",
|
||||||
|
height: "0",
|
||||||
|
paddingTop: "56.25%" // 16:9 比例
|
||||||
|
},
|
||||||
|
playBtnStyle: {
|
||||||
|
width: "60rpx",
|
||||||
|
height: "60rpx",
|
||||||
|
borderRadius: "30rpx",
|
||||||
|
backgroundColor: "rgba(0, 0, 0, 0.6)"
|
||||||
|
},
|
||||||
|
imageSize: 150,
|
||||||
|
pageCount: 1,
|
||||||
|
carousel: {
|
||||||
|
type: "default",
|
||||||
|
autoplay: true,
|
||||||
|
interval: 3000,
|
||||||
|
duration: 500,
|
||||||
|
circular: true
|
||||||
|
}
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|||||||
@@ -66,6 +66,7 @@
|
|||||||
"minified" : true
|
"minified" : true
|
||||||
},
|
},
|
||||||
"usingComponents" : true,
|
"usingComponents" : true,
|
||||||
|
"lazyCodeLoading": "requiredComponents",
|
||||||
"permission" : {
|
"permission" : {
|
||||||
"scope.userLocation" : {
|
"scope.userLocation" : {
|
||||||
"desc" : "为了更好地为您提供服务"
|
"desc" : "为了更好地为您提供服务"
|
||||||
@@ -103,7 +104,7 @@
|
|||||||
},
|
},
|
||||||
"router" : {
|
"router" : {
|
||||||
"mode" : "history",
|
"mode" : "history",
|
||||||
"base" : "/hwappx/2811/"
|
"base" : "/hwappx/common/"
|
||||||
},
|
},
|
||||||
"title" : "",
|
"title" : "",
|
||||||
"devServer" : {
|
"devServer" : {
|
||||||
|
|||||||
24
pages.json
24
pages.json
@@ -912,16 +912,22 @@
|
|||||||
},
|
},
|
||||||
//******************文件模块******************
|
//******************文件模块******************
|
||||||
{
|
{
|
||||||
"path": "files/list",
|
"path": "files/list",
|
||||||
"style": {
|
"style": {
|
||||||
// #ifdef APP-PLUS
|
// #ifdef APP-PLUS
|
||||||
"navigationStyle": "custom",
|
"navigationStyle": "custom",
|
||||||
// #endif
|
// #endif
|
||||||
"navigationBarTitleText": "查看文件"
|
"navigationBarTitleText": "查看文件"
|
||||||
}
|
|
||||||
}
|
}
|
||||||
]
|
},
|
||||||
}
|
{
|
||||||
|
"path": "file-preview/file-preview",
|
||||||
|
"style": {
|
||||||
|
"navigationBarTitleText": "文件预览"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
],
|
],
|
||||||
"globalStyle": {
|
"globalStyle": {
|
||||||
"navigationBarTextStyle": "black",
|
"navigationBarTextStyle": "black",
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
<view class="bg border-top"></view>
|
<view class="bg border-top"></view>
|
||||||
<view class="list_cotact padding-top">
|
<view class="list_cotact padding-top">
|
||||||
<view class="container">
|
<view class="container">
|
||||||
|
<!-- 专属客服 + 在线留言 -->
|
||||||
<!-- #ifdef H5 -->
|
<!-- #ifdef H5 -->
|
||||||
<view class="view_ul view_ul_one clearfix">
|
<view class="view_ul view_ul_one clearfix">
|
||||||
<view @click="tapMessage" class="view_li w50_li text-center"
|
<view @click="tapMessage" class="view_li w50_li text-center"
|
||||||
@@ -14,7 +15,6 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<!-- <view @click="test()">Test</view> -->
|
|
||||||
<!-- #endif -->
|
<!-- #endif -->
|
||||||
<!-- #ifdef MP-WEIXIN -->
|
<!-- #ifdef MP-WEIXIN -->
|
||||||
<view class="view_ul view_ul_one clearfix">
|
<view class="view_ul view_ul_one clearfix">
|
||||||
@@ -33,6 +33,8 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<!-- #endif -->
|
<!-- #endif -->
|
||||||
|
|
||||||
|
<!-- 人员信息/名片 -->
|
||||||
<view class="view_ul_100" v-for="(item, index) in dataList" :key="index"
|
<view class="view_ul_100" v-for="(item, index) in dataList" :key="index"
|
||||||
style="margin-bottom: 20rpx;">
|
style="margin-bottom: 20rpx;">
|
||||||
|
|
||||||
@@ -64,20 +66,50 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<!-- <view class="view_ul view_ul_two clearfix " v-if="dataList.length > 3" >
|
|
||||||
<view @click="Tel(item.mobile)" class="view_li w50_li" v-for="(item,index) in dataList" >
|
<!-- 企业文件 -->
|
||||||
<view class="bl bor" hoverClass="none" url="" style="box-shadow: 5rpx 5rpx 10rpx #dcdcdc;height: 230rpx;position: relative;">
|
<view class="view_files_container" v-if="showFileListDiy && fileList.length > 0">
|
||||||
<view class="contact_name" v-if="item.position">{{item.position_text}}:{{item.position}}</view>
|
<view class="section-title">企业文件</view>
|
||||||
<view class="contact_name">{{item.title_text}}:{{item.realname}}</view>
|
<view class="files-list">
|
||||||
<view class="contact_name">{{item.mobile_text}}:{{item.mobile}}</view>
|
<view v-for="(file, index) in fileList" :key="index" class="file-item">
|
||||||
<view class="contact_name" style="padding-bottom:10rpx;white-space: nowrap;text-overflow: ellipsis;overflow: hidden;">{{item.address_text}}:{{item.address}}</view>
|
<image mode="aspectFill" :src="$util.img('public/static/img/pdf-icon.png')" class="file-icon"></image>
|
||||||
<view class="contact_name" style="padding-top:10rpx;padding-bottom:0rpx;border-top: solid 1px #eee;position: absolute;bottom: 0;display: flex">
|
<view class="file-info">
|
||||||
<image mode="widthFix":src="$util.img('public/static/img/boda.png')"></image>
|
<view class="file-name">{{ file.name }}</view>
|
||||||
<view style="margin-top: -6rpx;margin-left: 10rpx;font-size: 24rpx;color:rgba(71,71,71,.79)">点击拨打电话</view>
|
<view class="file-actions">
|
||||||
</view>
|
<button class="file-btn share-btn" @click="shareFile(file)">{{ $lang('share') }}</button>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</view> -->
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<!-- 企业视频 -->
|
||||||
|
<view class="view_videos_container" v-if="showVideoListDiy && videoList.length > 0">
|
||||||
|
<view class="section-title">企业视频</view>
|
||||||
|
<view class="videos-list">
|
||||||
|
<view v-for="(video, index) in videoList" :key="index" class="video-item" @click="playVideo(video)">
|
||||||
|
<image mode="aspectFill" :src="video.coverUrl" class="video-cover"></image>
|
||||||
|
<view class="video-play-btn">
|
||||||
|
<image mode="aspectFill" :src="$util.img('public/static/img/play-icon.png')" class="play-icon"></image>
|
||||||
|
</view>
|
||||||
|
<view class="video-title">{{ video.title }}</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 视频号视频列表 -->
|
||||||
|
<view class="view_channel_container" v-if="showChannelListDiy && channelList.length > 0">
|
||||||
|
<diy-channel-list :value="{
|
||||||
|
list: channelList,
|
||||||
|
showStyle: diyChannelSettings.channel_display_style,
|
||||||
|
rowCount: diyChannelSettings.channel_row_count,
|
||||||
|
aspectRatio: diyChannelSettings.channel_aspect_ratio,
|
||||||
|
showViewCount: diyChannelSettings.channel_show_view_count,
|
||||||
|
titleLineClamp: diyChannelSettings.channel_title_line_clamp,
|
||||||
|
showPlayBtn: diyChannelSettings.channel_show_play_btn,
|
||||||
|
}" @channel-video-click-play="onChannelVideoClickPlay"/>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 地图 -->
|
||||||
<map v-if="shop.longitude > 0 && shop.latitude > 0 && ismessage == 0" id="map"
|
<map v-if="shop.longitude > 0 && shop.latitude > 0 && ismessage == 0" id="map"
|
||||||
style="width: 100%; height:400rpx" scale="12" :markers="markers" bindupdated="bindupdated"
|
style="width: 100%; height:400rpx" scale="12" :markers="markers" bindupdated="bindupdated"
|
||||||
:longitude="shop.longitude" :latitude="shop.latitude" show-location>
|
:longitude="shop.longitude" :latitude="shop.latitude" show-location>
|
||||||
@@ -88,19 +120,7 @@
|
|||||||
</map>
|
</map>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</view>
|
</view>
|
||||||
<!-- <view wx:if="landline !=0">
|
|
||||||
<button bindtap="contact">
|
|
||||||
<view class="message">
|
|
||||||
<image mode="widthFix" src="{{contactbg}}"></image>
|
|
||||||
</view>
|
|
||||||
</button>
|
|
||||||
</view> -->
|
|
||||||
</view>
|
</view>
|
||||||
<!--留言弹窗-->
|
<!--留言弹窗-->
|
||||||
<view class="goods-sku">
|
<view class="goods-sku">
|
||||||
@@ -166,19 +186,49 @@
|
|||||||
<to-top v-if="showTop" @toTop="scrollToTopNative()"></to-top>
|
<to-top v-if="showTop" @toTop="scrollToTopNative()"></to-top>
|
||||||
<hover-nav></hover-nav>
|
<hover-nav></hover-nav>
|
||||||
<diy-bottom-nav></diy-bottom-nav>
|
<diy-bottom-nav></diy-bottom-nav>
|
||||||
|
|
||||||
|
<!-- 视频播放弹窗 -->
|
||||||
|
<ns-video-player-popup
|
||||||
|
ref="videoPlayerPopup"
|
||||||
|
:current-video="currentVideo"
|
||||||
|
@popup-change="onVideoPopupChange"
|
||||||
|
></ns-video-player-popup>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import scroll from '@/common/js/scroll-view.js';
|
import scroll from '@/common/js/scroll-view.js';
|
||||||
|
import shareUtil from '@/common/js/share.js';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
mixins: [scroll],
|
mixins: [scroll],
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
minScrollTop: 100,
|
minScrollTop: 100,
|
||||||
dataList: [],
|
|
||||||
|
showKefuDiy: true, // 是否显示客服及留言
|
||||||
|
showContactListDiy: true, // 是否显示联系人列表
|
||||||
|
showMapDiy: false, // 是否显示地图
|
||||||
|
showVideoListDiy: false, // 是否显示企业视频
|
||||||
|
showFileListDiy: false, // 是否显示企业文件
|
||||||
|
showChannelListDiy: false, // 是否显示视频号
|
||||||
|
|
||||||
|
dataList: [], // 电子名片信息
|
||||||
|
fileList: [], // 企业文件
|
||||||
|
videoList: [], // 企业视频
|
||||||
|
channelList: [], // 视频号
|
||||||
|
|
||||||
|
diyChannelSettings: {
|
||||||
|
channel_display_style: 'fixed',
|
||||||
|
channel_aspect_ratio: '16:9',
|
||||||
|
channel_show_view_count: true,
|
||||||
|
channel_row_count: 2,
|
||||||
|
channel_title_line_clamp: 2,
|
||||||
|
channel_show_play_btn: true,
|
||||||
|
}, // 自定义设置
|
||||||
|
|
||||||
ismessage: 0,
|
ismessage: 0,
|
||||||
|
currentVideo: null, // 当前播放的视频
|
||||||
Form: {
|
Form: {
|
||||||
realname: '',
|
realname: '',
|
||||||
mobile: '',
|
mobile: '',
|
||||||
@@ -214,7 +264,23 @@ export default {
|
|||||||
url: '/api/member/personnel',
|
url: '/api/member/personnel',
|
||||||
success: res => {
|
success: res => {
|
||||||
if (res.code == 0) {
|
if (res.code == 0) {
|
||||||
this.dataList = res.data;
|
this.dataList = res.data; // 电子名片信息
|
||||||
|
this.fileList = res.file_list; // 企业文件
|
||||||
|
this.videoList = res.video_list; // 企业视频
|
||||||
|
this.channelList = res.channel_list.map(item => this.$util.snakeToCamelForObj(item)).map(item => {
|
||||||
|
if (item.coverImageType === 'upload') {
|
||||||
|
item.coverUrl = this.$util.img(item.coverUrl);
|
||||||
|
}
|
||||||
|
if (item.avatarImageType === 'upload') {
|
||||||
|
item.avatarUrl = this.$util.img(item.avatarUrl);
|
||||||
|
}
|
||||||
|
// #ifdef MP-WEIXIN
|
||||||
|
// item.embedMode = Boolean(item.feedToken);
|
||||||
|
item.finderUserName = item.channelName;
|
||||||
|
// #endif
|
||||||
|
return item;
|
||||||
|
}); // 视频号
|
||||||
|
|
||||||
this.shop = res.shop;
|
this.shop = res.shop;
|
||||||
this.personnel_bg = res.set.personnel_bg ? res.set.personnel_bg : 'public/static/img/diy_view/member_info_bg.png';
|
this.personnel_bg = res.set.personnel_bg ? res.set.personnel_bg : 'public/static/img/diy_view/member_info_bg.png';
|
||||||
this.markers = [{
|
this.markers = [{
|
||||||
@@ -222,29 +288,69 @@ export default {
|
|||||||
latitude: this.shop.latitude,
|
latitude: this.shop.latitude,
|
||||||
longitude: this.shop.longitude
|
longitude: this.shop.longitude
|
||||||
}];
|
}];
|
||||||
|
|
||||||
|
// 设置是否展示各模块
|
||||||
|
this.showKefuDiy = res.diy.is_kefu == 1;
|
||||||
|
this.showContactListDiy = res.diy.is_contact == 1;
|
||||||
|
this.showMapDiy = res.diy.is_map == 1;
|
||||||
|
this.showVideoListDiy = res.diy.is_video == 1;
|
||||||
|
this.showFileListDiy = res.diy.is_file == 1;
|
||||||
|
this.showChannelListDiy = res.diy.is_channel == 1;
|
||||||
|
|
||||||
|
// 遍历res.diy所有key,然后判断key前缀包含channel_,如果包含,就赋值给diyChannelSettings
|
||||||
|
for (let key in res.diy) {
|
||||||
|
if (key.startsWith('channel_')) {
|
||||||
|
this.diyChannelSettings[key] = res.diy[key];
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
fail: res => {}
|
fail: res => {}
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
test() {
|
// 分享文件
|
||||||
// channelReady(function(bAvailable) {
|
shareFile(file) {
|
||||||
// alert('是否存在框架服务:' + bAvailable)
|
shareUtil.shareFile(file);
|
||||||
// })
|
},
|
||||||
// window.location.href = 'https://hapjs.org/app//[path][?key=value] hap://app//[path][?key=value] '
|
|
||||||
//参数说明: package: 应用包名,必选 path: 应用内页面的 path,可选,默认为首页 key-value: 希望传给页面的参数,可选,可以有多个
|
|
||||||
|
|
||||||
// const isQuickAppEnv = navigator.userAgent.includes('HuaweiQuickApp');
|
onChannelVideoClickPlay(item) {
|
||||||
// let quickAppBridge;
|
this.$api.sendRequest({
|
||||||
// console.log(window.quickapp)
|
url: '/api/member/incrementChannelViewCount',
|
||||||
// if (isQuickAppEnv) {
|
data: {
|
||||||
// quickAppBridge = window.quickapp;
|
channel_id: item.channelId
|
||||||
// }
|
},
|
||||||
// quickAppBridge.invoke('startQuickAppPage', { page: '/NativePage' });
|
success: res => {
|
||||||
// window.postMessage({ action: 'someAction', data: 'some data' }, '*');
|
|
||||||
// window.open('https://xcx10.5g-quickapp.com/test.php')
|
},
|
||||||
},
|
fail: res => {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
// 播放视频
|
||||||
|
playVideo(video) {
|
||||||
|
// 实现视频播放逻辑
|
||||||
|
if (video.videoUrl) {
|
||||||
|
this.currentVideo = video;
|
||||||
|
this.$refs.videoPlayerPopup.open();
|
||||||
|
} else {
|
||||||
|
uni.showToast({ title: '视频地址不存在', icon: 'none' });
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
// 关闭视频弹窗
|
||||||
|
closeVideoPopup() {
|
||||||
|
this.$refs.videoPlayerPopup.close();
|
||||||
|
},
|
||||||
|
|
||||||
|
// 视频弹窗状态变化
|
||||||
|
onVideoPopupChange(e) {
|
||||||
|
if (!e.show) {
|
||||||
|
// 弹窗关闭时重置当前视频
|
||||||
|
this.currentVideo = null;
|
||||||
|
}
|
||||||
|
},
|
||||||
save() {
|
save() {
|
||||||
if (!this.Form.realname.trim()) {
|
if (!this.Form.realname.trim()) {
|
||||||
uni.showToast({ title: '请填写姓名', icon: 'none' });
|
uni.showToast({ title: '请填写姓名', icon: 'none' });
|
||||||
@@ -655,6 +761,132 @@ image {
|
|||||||
</style>
|
</style>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
/* 企业文件样式 */
|
||||||
|
.view_files_container {
|
||||||
|
margin-top: 30rpx;
|
||||||
|
padding: 0 30rpx;
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.files-list {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-item {
|
||||||
|
width: calc(50% - 10rpx);
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 10rpx;
|
||||||
|
padding: 20rpx;
|
||||||
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.05);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-icon {
|
||||||
|
width: 60rpx;
|
||||||
|
height: 80rpx;
|
||||||
|
margin-right: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-info {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-name {
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 10rpx;
|
||||||
|
line-height: 1.3;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 10rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.file-btn {
|
||||||
|
font-size: 20rpx;
|
||||||
|
padding: 6rpx 12rpx;
|
||||||
|
border-radius: 4rpx;
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.share-btn {
|
||||||
|
background-color: #f0f0f0;
|
||||||
|
color: #666;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 企业视频样式 */
|
||||||
|
.view_videos_container {
|
||||||
|
margin-top: 30rpx;
|
||||||
|
padding: 0 30rpx;
|
||||||
|
|
||||||
|
.section-title {
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.videos-list {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 20rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-item {
|
||||||
|
width: calc(33.333% - 14rpx);
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-cover {
|
||||||
|
width: 100%;
|
||||||
|
aspect-ratio: 16/9;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-play-btn {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
width: 60rpx;
|
||||||
|
height: 60rpx;
|
||||||
|
background-color: rgba(0, 0, 0, 0.5);
|
||||||
|
border-radius: 50%;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.play-icon {
|
||||||
|
width: 24rpx;
|
||||||
|
height: 24rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-title {
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #333;
|
||||||
|
margin-top: 10rpx;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 1.3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/deep/ .channel-list {
|
||||||
|
padding: 0rpx !important;
|
||||||
|
}
|
||||||
|
|
||||||
/deep/ .mescroll-totop {
|
/deep/ .mescroll-totop {
|
||||||
right: 27rpx !important;
|
right: 27rpx !important;
|
||||||
/* #ifdef H5 */
|
/* #ifdef H5 */
|
||||||
@@ -664,4 +896,5 @@ image {
|
|||||||
bottom: 180rpx !important;
|
bottom: 180rpx !important;
|
||||||
/* #endif */
|
/* #endif */
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
183
pages_tool/file-preview/file-preview.vue
Normal file
183
pages_tool/file-preview/file-preview.vue
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
<template>
|
||||||
|
<view class="file-preview-container" :style="themeColor">
|
||||||
|
<view class="header">
|
||||||
|
<view class="back-btn" @click="goBack">
|
||||||
|
<text class="iconfont icon-back"></text>
|
||||||
|
</view>
|
||||||
|
<view class="header-title">{{ fileName }}</view>
|
||||||
|
<view class="placeholder"></view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<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">文件地址不存在</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">文件地址不存在</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 视频文件预览 -->
|
||||||
|
<view v-else-if="fileType === 'video'" class="video-viewer">
|
||||||
|
<video v-if="fileUrl" :src="fileUrl" controls autoplay class="video-player"></video>
|
||||||
|
<view v-else class="error-message">视频地址不存在</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 其他文件类型 -->
|
||||||
|
<view v-else class="file-viewer">
|
||||||
|
<view class="error-message">不支持的文件类型</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
fileName: '',
|
||||||
|
fileUrl: '',
|
||||||
|
fileType: ''
|
||||||
|
};
|
||||||
|
},
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
/**
|
||||||
|
* 根据文件名后缀推断文件类型
|
||||||
|
*/
|
||||||
|
inferFileType() {
|
||||||
|
if (this.fileName) {
|
||||||
|
const ext = this.fileName.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';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回上一页
|
||||||
|
*/
|
||||||
|
goBack() {
|
||||||
|
uni.navigateBack();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.file-preview-container {
|
||||||
|
min-height: 100vh;
|
||||||
|
background-color: #f5f5f5;
|
||||||
|
|
||||||
|
.header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
height: 100rpx;
|
||||||
|
padding: 0 30rpx;
|
||||||
|
background-color: #fff;
|
||||||
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
|
.back-btn {
|
||||||
|
width: 60rpx;
|
||||||
|
height: 60rpx;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
|
||||||
|
.icon-back {
|
||||||
|
font-size: 32rpx;
|
||||||
|
color: #333;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.header-title {
|
||||||
|
flex: 1;
|
||||||
|
font-size: 32rpx;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #333;
|
||||||
|
text-align: center;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.placeholder {
|
||||||
|
width: 60rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.content {
|
||||||
|
flex: 1;
|
||||||
|
padding: 20rpx;
|
||||||
|
|
||||||
|
.file-viewer {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 80vh;
|
||||||
|
background-color: #fff;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 2rpx 8rpx rgba(0, 0, 0, 0.1);
|
||||||
|
|
||||||
|
.error-message {
|
||||||
|
width: 100%;
|
||||||
|
height: 80vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: #999;
|
||||||
|
font-size: 28rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.video-viewer {
|
||||||
|
width: 100%;
|
||||||
|
min-height: 80vh;
|
||||||
|
background-color: #000;
|
||||||
|
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;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: #999;
|
||||||
|
font-size: 28rpx;
|
||||||
|
background-color: #000;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
</view>
|
</view>
|
||||||
<view v-if="visible" id="mask" class="uni-datetime-picker-mask" @click="tiggerTimePicker"></view>
|
<view v-if="visible" id="mask" class="uni-datetime-picker-mask" @click="tiggerTimePicker"></view>
|
||||||
<view v-if="visible" class="uni-datetime-picker-popup" :class="[dateShow && timeShow ? '' : 'fix-nvue-height']"
|
<view v-if="visible" class="uni-datetime-picker-popup" :class="[dateShow && timeShow ? '' : 'fix-nvue-height']"
|
||||||
:style="fixNvueBug">
|
:style="[fixNvueBug]">
|
||||||
<view class="uni-title">
|
<view class="uni-title">
|
||||||
<text class="uni-datetime-picker-text">{{selectTimeText}}</text>
|
<text class="uni-datetime-picker-text">{{selectTimeText}}</text>
|
||||||
</view>
|
</view>
|
||||||
|
|||||||
@@ -28,7 +28,7 @@
|
|||||||
|
|
||||||
<view v-show="popup" class="uni-date-mask" @click="close"></view>
|
<view v-show="popup" class="uni-date-mask" @click="close"></view>
|
||||||
<view v-if="!isPhone" ref="datePicker" v-show="popup" class="uni-date-picker__container">
|
<view v-if="!isPhone" ref="datePicker" v-show="popup" class="uni-date-picker__container">
|
||||||
<view v-if="!isRange" class="uni-date-single--x" :style="popover">
|
<view v-if="!isRange" class="uni-date-single--x" :style="[popover]">
|
||||||
<view class="uni-popper__arrow"></view>
|
<view class="uni-popper__arrow"></view>
|
||||||
<view v-if="hasTime" class="uni-date-changed popup-x-header">
|
<view v-if="hasTime" class="uni-date-changed popup-x-header">
|
||||||
<input class="uni-date__input t-c" type="text" v-model="tempSingleDate"
|
<input class="uni-date__input t-c" type="text" v-model="tempSingleDate"
|
||||||
@@ -49,7 +49,7 @@
|
|||||||
<view class="uni-date-popper__arrow"></view>
|
<view class="uni-date-popper__arrow"></view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view v-else class="uni-date-range--x" :style="popover">
|
<view v-else class="uni-date-range--x" :style="[popover]">
|
||||||
<view class="uni-popper__arrow"></view>
|
<view class="uni-popper__arrow"></view>
|
||||||
<view v-if="hasTime" class="popup-x-header uni-date-changed">
|
<view v-if="hasTime" class="popup-x-header uni-date-changed">
|
||||||
<view class="popup-x-header--datetime">
|
<view class="popup-x-header--datetime">
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<template>
|
<template>
|
||||||
<view v-if="showPopup" class="uni-popup" :class="customClass" :style="{'top': top}">
|
<view v-if="showPopup" class="uni-popup" :class="customClass" :style="[{'top': top}]">
|
||||||
<view :class="[ani, animation ? 'ani' : '', !custom ? 'uni-custom' : '']" class="uni-popup__mask" @click="close(true)" ></view>
|
<view :class="[ani, animation ? 'ani' : '', !custom ? 'uni-custom' : '']" class="uni-popup__mask" @click="close(true)" ></view>
|
||||||
<view :class="[type, ani, animation ? 'ani' : '', !custom ? 'uni-custom' : '']" class="uni-popup__wrapper goodslist-uni-popup safe-area" @click="close(true)" v-if="isIphoneX">
|
<view :class="[type, ani, animation ? 'ani' : '', !custom ? 'uni-custom' : '']" class="uni-popup__wrapper goodslist-uni-popup safe-area" @click="close(true)" v-if="isIphoneX">
|
||||||
<view class="uni-popup__wrapper-box goodslist-uni-popup-box" @click.stop="clear">
|
<view class="uni-popup__wrapper-box goodslist-uni-popup-box" @click.stop="clear">
|
||||||
|
|||||||
Reference in New Issue
Block a user