Merge branch 'feat/personnel_channel' into dev/1.0

This commit is contained in:
2026-01-15 18:14:50 +08:00
20 changed files with 2381 additions and 798 deletions

View File

@@ -4,6 +4,7 @@
<view class="bg border-top"></view>
<view class="list_cotact padding-top">
<view class="container">
<!-- 专属客服 + 在线留言 -->
<!-- #ifdef H5 -->
<view class="view_ul view_ul_one clearfix">
<view @click="tapMessage" class="view_li w50_li text-center"
@@ -14,7 +15,6 @@
</view>
</view>
</view>
<!-- <view @click="test()">Test</view> -->
<!-- #endif -->
<!-- #ifdef MP-WEIXIN -->
<view class="view_ul view_ul_one clearfix">
@@ -33,6 +33,8 @@
</view>
</view>
<!-- #endif -->
<!-- 人员信息/名片 -->
<view class="view_ul_100" v-for="(item, index) in dataList" :key="index"
style="margin-bottom: 20rpx;">
@@ -64,20 +66,50 @@
</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="contact_name" v-if="item.position">{{item.position_text}}{{item.position}}</view>
<view class="contact_name">{{item.title_text}}{{item.realname}}</view>
<view class="contact_name">{{item.mobile_text}}{{item.mobile}}</view>
<view class="contact_name" style="padding-bottom:10rpx;white-space: nowrap;text-overflow: ellipsis;overflow: hidden;">{{item.address_text}}{{item.address}}</view>
<view class="contact_name" style="padding-top:10rpx;padding-bottom:0rpx;border-top: solid 1px #eee;position: absolute;bottom: 0;display: flex">
<image mode="widthFix":src="$util.img('public/static/img/boda.png')"></image>
<view style="margin-top: -6rpx;margin-left: 10rpx;font-size: 24rpx;color:rgba(71,71,71,.79)">点击拨打电话</view>
</view>
</view>
</view>
</view> -->
<!-- 企业文件 -->
<view class="view_files_container" v-if="showFileListDiy && fileList.length > 0">
<view class="section-title">企业文件</view>
<view class="files-list">
<view v-for="(file, index) in fileList" :key="index" class="file-item">
<image mode="aspectFill" :src="$util.img('public/static/img/pdf-icon.png')" class="file-icon"></image>
<view class="file-info">
<view class="file-name">{{ file.name }}</view>
<view class="file-actions">
<button class="file-btn share-btn" @click="shareFile(file)">{{ $lang('share') }}</button>
</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"
style="width: 100%; height:400rpx" scale="12" :markers="markers" bindupdated="bindupdated"
:longitude="shop.longitude" :latitude="shop.latitude" show-location>
@@ -88,19 +120,7 @@
</map>
</view>
</view>
<!-- <view wx:if="landline !=0">
<button bindtap="contact">
<view class="message">
<image mode="widthFix" src="{{contactbg}}"></image>
</view>
</button>
</view> -->
</view>
<!--留言弹窗-->
<view class="goods-sku">
@@ -166,19 +186,49 @@
<to-top v-if="showTop" @toTop="scrollToTopNative()"></to-top>
<hover-nav></hover-nav>
<diy-bottom-nav></diy-bottom-nav>
<!-- 视频播放弹窗 -->
<ns-video-player-popup
ref="videoPlayerPopup"
:current-video="currentVideo"
@popup-change="onVideoPopupChange"
></ns-video-player-popup>
</view>
</template>
<script>
import scroll from '@/common/js/scroll-view.js';
import shareUtil from '@/common/js/share.js';
export default {
mixins: [scroll],
data() {
return {
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,
currentVideo: null, // 当前播放的视频
Form: {
realname: '',
mobile: '',
@@ -214,7 +264,23 @@ export default {
url: '/api/member/personnel',
success: res => {
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.personnel_bg = res.set.personnel_bg ? res.set.personnel_bg : 'public/static/img/diy_view/member_info_bg.png';
this.markers = [{
@@ -222,29 +288,69 @@ export default {
latitude: this.shop.latitude,
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 => {}
});
},
methods: {
test() {
// channelReady(function(bAvailable) {
// alert('是否存在框架服务:' + bAvailable)
// })
// window.location.href = 'https://hapjs.org/app//[path][?key=value] hap://app//[path][?key=value] '
//参数说明: package: 应用包名,必选 path: 应用内页面的 path可选默认为首页 key-value: 希望传给页面的参数,可选,可以有多个
// 分享文件
shareFile(file) {
shareUtil.shareFile(file);
},
// const isQuickAppEnv = navigator.userAgent.includes('HuaweiQuickApp');
// let quickAppBridge;
// console.log(window.quickapp)
// if (isQuickAppEnv) {
// quickAppBridge = window.quickapp;
// }
// quickAppBridge.invoke('startQuickAppPage', { page: '/NativePage' });
// window.postMessage({ action: 'someAction', data: 'some data' }, '*');
// window.open('https://xcx10.5g-quickapp.com/test.php')
},
onChannelVideoClickPlay(item) {
this.$api.sendRequest({
url: '/api/member/incrementChannelViewCount',
data: {
channel_id: item.channelId
},
success: res => {
},
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() {
if (!this.Form.realname.trim()) {
uni.showToast({ title: '请填写姓名', icon: 'none' });
@@ -655,6 +761,132 @@ image {
</style>
<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-container {
padding: 0rpx !important;
}
/deep/ .mescroll-totop {
right: 27rpx !important;
/* #ifdef H5 */
@@ -664,4 +896,5 @@ image {
bottom: 180rpx !important;
/* #endif */
}
</style>

View 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>