chore(组件:微信视频号): 基本功能实现

This commit is contained in:
2026-01-14 08:58:19 +08:00
parent bd53937ead
commit 8cc352621d
8 changed files with 762 additions and 134 deletions

View File

@@ -40,6 +40,15 @@ create table if not exists lucky_diy_view_util
```sql ```sql
insert into lucky_diy_view_util (name, title, type, value, addon_name, sort, support_diy_view, max_count, is_delete, icon, icon_type) insert into lucky_diy_view_util (name, title, type, value, addon_name, sort, support_diy_view, max_count, is_delete, icon, icon_type)
values ('test', '测试', 'SYSTEM', '{"test": "test"}', '', 0, '', 0, 0, '', 0); values ('test', '测试', 'SYSTEM', '{"test": "test"}', '', 0, '', 0, 0, '', 0);
--- 微信视频号
-- 仅当WechatChannel不存在时添加记录
INSERT INTO lucky_diy_view_util (`name`, `title`, `type`, `value`, `addon_name`, `sort`, `support_diy_view`, `max_count`, `is_delete`, `icon`, `icon_type`)
SELECT 'WechatChannel', '微信视频号', 'SYSTEM', '{ "list": [{ "channelName":"", "finderUserName": "", "avatarImageType": "url", "avatarUrl": "", "videoTitle": "", "coverImageType": "url", "coverUrl": "", "feedId": "", "feedToken": "", "viewCount": 0, "showViewCount": true, "embedMode": false, "channelType":"wechat" }], "rowCount": 2, "showStyle": "fixed", "aspectRatio":"16:9", "titleLineClamp": 1, "showPlayBtn": true}', '', 100110, '', 0, 0, '/public/static/img/svg/xuanxiangka.svg', 0
WHERE NOT EXISTS (
SELECT 1 FROM lucky_diy_view_util WHERE name = 'WechatChannel'
);
``` ```

View File

@@ -1,65 +1,249 @@
.wechat_channel-box .preview-draggable {padding: 0;} .wechat_channel-box .preview-draggable {
.wechat_channel-box .video-wrap{position: relative;} padding: 15px;
.wechat_channel-box .video-wrap .channel-preview img{width: 100%;display: block;}
.wechat_channel-box .video-wrap .channel-info {
position: absolute;
bottom: 0;
left: 0;
right: 0;
background: linear-gradient(to top, rgba(0,0,0,0.7), transparent);
padding: 20px 10px 10px;
color: #fff;
display: flex;
align-items: center;
} }
.wechat_channel-box .video-wrap .channel-avatar {
.wechat_channel-box .video-list-wrap {
position: relative;
}
.wechat_channel-box .video-list {
display: grid;
grid-template-columns: repeat(var(--row-count, 2), 1fr);
gap: 8px;
padding: 16px 16px 0px;
}
.wechat_channel-box .video-item {
position: relative;
width: 100%;
}
.wechat_channel-box .video-wrap {
position: relative;
}
.wechat_channel-box .video-wrap .channel-preview {
position: relative;
width: 100%;
overflow: hidden;
border-radius: 5px;
}
.wechat_channel-box .video-item .channel-preview img {
display: block;
width: 100%;
object-fit: cover;
border-radius: 5px;
}
.wechat_channel-box .video-item .channel-preview {
position: relative;
width: 100%;
overflow: hidden;
border-radius: 5px;
}
.wechat_channel-box .video-item .play-btn {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
width: 40px; width: 40px;
height: 40px; height: 40px;
background: rgba(0, 0, 0, 0.6);
border-radius: 50%; border-radius: 50%;
overflow: hidden; display: flex;
margin-right: 10px; align-items: center;
flex-shrink: 0; justify-content: center;
z-index: 10;
} }
.wechat_channel-box .video-wrap .channel-avatar img {
width: 100%; .wechat_channel-box .video-item .play-btn::before {
height: 100%; content: '';
object-fit: cover; width: 0;
height: 0;
border-style: solid;
border-width: 10px 0 10px 16px;
border-color: transparent transparent transparent #fff;
margin-left: 2px;
} }
.wechat_channel-box .video-wrap .channel-text {
.wechat_channel-box .video-item .view-count {
position: absolute;
right: 2px;
font-size: 12px;
color: #fff;
padding: 4px 4px;
/* border-radius: 16px; */
margin-bottom: 0;
z-index: 10;
text-shadow: 2px 2px 3px rgba(0, 0, 0, 0.5);
}
.wechat_channel-box .video-item .channel-info {
position: relative;
padding: 8px;
background: #fff;
border-radius: 0 0 8px 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
margin-top: -4px;
}
.wechat_channel-box .video-item .channel-avatar {
display: none;
}
.wechat_channel-box .video-item .channel-text {
flex: 1; flex: 1;
overflow: hidden; overflow: hidden;
} }
.wechat_channel-box .video-wrap .channel-name {
font-size: 14px; .wechat_channel-box .video-item .channel-name {
font-weight: bold; display: none;
}
.wechat_channel-box .video-item .video-title {
font-size: 13px;
font-weight: 500;
color: #333;
overflow: hidden;
margin-bottom: 3px;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
word-break: break-all;
word-wrap: break-word;
white-space: normal;
}
/* 1行标题 */
.wechat_channel-box .video-item .video-title.title-max-lines-1 {
-webkit-line-clamp: 1;
}
/* 2行标题 */
.wechat_channel-box .video-item .video-title.title-max-lines-2 {
-webkit-line-clamp: 2;
margin-bottom: 4px; margin-bottom: 4px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
} }
.wechat_channel-box .video-wrap .video-title {
font-size: 12px; /* 3行标题 */
opacity: 0.9; .wechat_channel-box .video-item .video-title.title-max-lines-3 {
white-space: nowrap; -webkit-line-clamp: 3;
overflow: hidden; margin-bottom: 4px;
text-overflow: ellipsis;
} }
.wechat_channel-box .video-wrap .video-placeholder {
width: 100%; .wechat_channel-box .video-item .bottom-text {
font-size: 14px;
color: #666;
margin-top: 8px;
line-height: 1.4;
font-style: normal;
}
.wechat_channel-box .video-item .video-placeholder {
height: 200px; height: 200px;
background: #f5f5f5; background: #f5f5f5;
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: center; justify-content: center;
border: 1px dashed #ddd; border: 1px dashed #ddd;
border-radius: 5px;
} }
.wechat_channel-box .video-wrap .placeholder-text {
.wechat_channel-box .video-item .placeholder-text {
color: #999; color: #999;
font-size: 14px; font-size: 14px;
} }
.wechat_channel-box .edit-attribute .attr-wrap .restore-wrap .video-add-box .img-block {width: 200px !important;height: 125px !important;margin-bottom: 30px;margin-right: 0;position: relative;}
.wechat_channel-box .edit-attribute .attr-wrap .restore-wrap .video-add-box .img-block > div {line-height: 125px;height: 125px !important;width: 100%;text-align: center;} /* 16:9比例下的固定高度 */
.wechat_channel-box .edit-attribute .attr-wrap .restore-wrap .video-add-box .img-block video {width: 100% !important;height: 125px !important;} /* 一行显示1个 */
.wechat_channel-box .edit-attribute .attr-wrap .restore-wrap .video-add-box .img-block span{position: absolute;top: 50%;left: 50%;transform: translate(-50%, -50%);} .wechat_channel-box[data-ratio="16:9"] {
.wechat_channel-box .video-zhezhao {position: absolute;background: #fff;width: 61%;height: 125px;top: 1px;right: 32px;text-align: center;line-height: 105px;display: none;} --image-height: 212px;
.wechat_channel-box .video-zhezhao span {position: absolute;top: 35px;left: 80px;color: #909399;} }
/* 一行显示2个 */
.wechat_channel-box[data-ratio="16:9"][data-row-count="2"] {
--image-height: 104px;
}
/* 一行显示3个 */
.wechat_channel-box[data-ratio="16:9"][data-row-count="3"] {
--image-height: 65px;
}
/* 一行显示4个 */
.wechat_channel-box[data-ratio="16:9"][data-row-count="4"] {
--image-height: 47px;
}
/* 3:4比例下的固定高度 */
/* 一行显示1个 */
.wechat_channel-box[data-ratio="3:4"] {
--image-height: 408px;
}
/* 一行显示2个 */
.wechat_channel-box[data-ratio="3:4"][data-row-count="2"] {
--image-height: 200px;
}
/* 一行显示3个 */
.wechat_channel-box[data-ratio="3:4"][data-row-count="3"] {
--image-height: 130px;
}
/* 一行显示4个 */
.wechat_channel-box[data-ratio="3:4"][data-row-count="4"] {
--image-height: 96px;
}
.wechat_channel-box .video-item .channel-preview img {
height: var(--image-height);
}
.wechat_channel-box .video-item .view-count {
top: calc(var(--image-height) - 32px);
}
.wechat_channel-box .edit-attribute .attr-wrap .restore-wrap .video-add-box .img-block {
width: 200px !important;
height: 125px !important;
margin-bottom: 30px;
margin-right: 0;
position: relative;
}
.wechat_channel-box .edit-attribute .attr-wrap .restore-wrap .video-add-box .img-block>div {
line-height: 125px;
height: 125px !important;
width: 100%;
text-align: center;
}
.wechat_channel-box .edit-attribute .attr-wrap .restore-wrap .video-add-box .img-block video {
width: 100% !important;
height: 125px !important;
}
.wechat_channel-box .edit-attribute .attr-wrap .restore-wrap .video-add-box .img-block span {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
.wechat_channel-box .video-zhezhao {
position: absolute;
background: #fff;
width: 61%;
height: 125px;
top: 1px;
right: 32px;
text-align: center;
line-height: 105px;
display: none;
}
.wechat_channel-box .video-zhezhao span {
position: absolute;
top: 35px;
left: 80px;
color: #909399;
}
/* .wechat_channel-box .layui-form-label + .layui-input-block {margin-left: 0 !important;} */

View File

@@ -1,26 +1,43 @@
<nc-component :data="data[index]" class="wechat_channel-box"> <nc-component :data="data[index]" class="wechat_channel-box" :data-ratio="nc.aspectRatio || '16:9'" :data-row-count="nc.rowCount || 2">
<!-- 预览 --> <!-- 预览 -->
<template slot="preview" > <template slot="preview" >
<div class="video-wrap"> <div class="video-list-wrap" :style="{ '--row-count': nc.rowCount || 2 }">
<div class="channel-preview" v-if="nc.coverUrl"> <div v-if="nc.list && nc.list.length > 0" class="video-list">
<img :src="changeImgUrl(nc.coverUrl)" style="width:100%;display:block;" /> <div v-for="(item, idx) in nc.list" :key="idx" class="video-item">
<div class="channel-info" v-if="nc.channelName || nc.videoTitle"> <div class="channel-preview" v-if="item.coverUrl" :style="{
<div class="channel-avatar" v-if="nc.avatarUrl"> borderTopLeftRadius: (nc.componentAngle == 'round' ? nc.topAroundRadius + 'px' : 0),
<img :src="changeImgUrl(nc.avatarUrl)" /> borderTopRightRadius: (nc.componentAngle == 'round' ? nc.topAroundRadius + 'px' : 0),
borderBottomLeftRadius: (nc.componentAngle == 'round' ? nc.bottomAroundRadius + 'px' : 0),
borderBottomRightRadius: (nc.componentAngle == 'round' ? nc.bottomAroundRadius + 'px' : 0) }">
<img :src="changeImgUrl(item.coverUrl)" style="width:100%;display:block;" mode="aspectFill"/>
<div v-if="nc.showPlayBtn" class="play-btn"></div>
<div class="channel-info" v-if="item.finderUserName || item.videoTitle">
<div class="channel-avatar" v-if="item.avatarUrl">
<img :src="changeImgUrl(item.avatarUrl)" mode="aspectFill"/>
</div> </div>
<div class="channel-text"> <div class="channel-text">
<div class="channel-name" v-if="nc.channelName">{{ nc.channelName }}</div> <!-- <div class="channel-name" v-if="item.finderUserName">{{ item.finderUserName }}</div> -->
<div class="video-title" v-if="nc.videoTitle">{{ nc.videoTitle }}</div> <div class="video-title" :class="'title-max-lines-' + (nc.titleLineClamp || 1)">{{ item.videoTitle || '视频标题' }}</div>
</div> </div>
</div> </div>
<div class="view-count" v-if="item.showViewCount">{{ item.viewCount }}次观看</div>
</div> </div>
<div class="video-placeholder" v-else :style="{ <div class="video-placeholder" v-else :style="{
borderTopLeftRadius: (nc.componentAngle == 'round' ? nc.topAroundRadius + 'px' : 0), borderTopLeftRadius: (nc.componentAngle == 'round' ? nc.topAroundRadius + 'px' : 0),
borderTopRightRadius: (nc.componentAngle == 'round' ? nc.topAroundRadius + 'px' : 0), borderTopRightRadius: (nc.componentAngle == 'round' ? nc.topAroundRadius + 'px' : 0),
borderBottomLeftRadius: (nc.componentAngle == 'round' ? nc.bottomAroundRadius + 'px' : 0), borderBottomLeftRadius: (nc.componentAngle == 'round' ? nc.bottomAroundRadius + 'px' : 0),
borderBottomRightRadius: (nc.componentAngle == 'round' ? nc.bottomAroundRadius + 'px' : 0) }"> borderBottomRightRadius: (nc.componentAngle == 'round' ? nc.bottomAroundRadius + 'px' : 0) }">
<div class="placeholder-text">上传封面图</div> <div class="placeholder-text">配置视频号信息</div>
</div>
</div>
</div>
<div v-else class="video-placeholder" :style="{
borderTopLeftRadius: (nc.componentAngle == 'round' ? nc.topAroundRadius + 'px' : 0),
borderTopRightRadius: (nc.componentAngle == 'round' ? nc.topAroundRadius + 'px' : 0),
borderBottomLeftRadius: (nc.componentAngle == 'round' ? nc.bottomAroundRadius + 'px' : 0),
borderBottomRightRadius: (nc.componentAngle == 'round' ? nc.bottomAroundRadius + 'px' : 0) }">
<div class="placeholder-text">请添加视频号</div>
</div> </div>
</div> </div>
</template> </template>
@@ -37,7 +54,7 @@
<!-- 资源 --> <!-- 资源 -->
<template slot="resource"> <template slot="resource">
<js></js> <js src="__STATIC__/js/Sortable.min.js"></js>
<css src="{$resource_path}/css/design.css"></css> <css src="{$resource_path}/css/design.css"></css>
<js src="{$resource_path}/js/design.js"></js> <js src="{$resource_path}/js/design.js"></js>
</template> </template>

View File

@@ -1,91 +1,508 @@
var tpl = '<div class="wechat_channel-edit">'; Vue.component("wechat_channel-edit", {
template: `<div class="wechat_channel-edit">
<div class="template-edit-title">
<h3>容器设置</h3>
tpl += '<div class="template-edit-title">'; <!-- 每行显示数量 -->
tpl += '<h3>基础设置</h3>'; <div class="layui-form-item">
<label class="layui-form-label sm">每行显示数量</label>
<div class="layui-input-block">
<select name="row_count" lay-filter="rowCount" v-model="data.rowCount">
<option value="1">1个</option>
<option value="2" :selected="data.rowCount == 2 || !data.rowCount">2个</option>
<option value="3" :selected="data.rowCount == 3">3个</option>
<option value="4" :selected="data.rowCount == 4">4个</option>
</select>
</div>
</div>
// 根据微信视频号组件的要求,填写视频号的标题和描述 <!-- 显示风格 -->
<div class="layui-form-item">
<label class="layui-form-label sm">显示风格</label>
<div class="layui-input-block">
<input type="radio" name="show_style" lay-filter="showStyle" value="fixed" title="固定高度" :checked="data.showStyle == 'fixed' || !data.showStyle">
<input type="radio" name="show_style" lay-filter="showStyle" value="auto" title="自适应高度" :checked="data.showStyle == 'auto'">
</div>
</div>
// 填写视频号名称 <!-- 显示比例 -->
tpl += '<div class="layui-form-item">'; <div class="layui-form-item">
tpl += '<label class="layui-form-label sm">视频号</label>'; <label class="layui-form-label sm"><span class="required">*</span>显示比例</label>
tpl += '<div class="layui-input-block">'; <div class="layui-input-block">
tpl += '<input type="text" v-model="data.channelName" placeholder="请输入视频号名称" class="layui-input">'; <input type="radio" name="aspect_ratio" lay-filter="aspectRatio" value="16:9" title="16:9" :checked="data.aspectRatio == '16:9' || !data.aspectRatio">
tpl += '</div>'; <input type="radio" name="aspect_ratio" lay-filter="aspectRatio" value="3:4" title="3:4" :checked="data.aspectRatio == '3:4'">
tpl += '</div>'; </div>
</div>
// 填写视频号头像URL <!-- 标题最多行数 -->
tpl += '<div class="layui-form-item">'; <div class="layui-form-item">
tpl += '<label class="layui-form-label sm">头像URL</label>'; <label class="layui-form-label sm">标题最多行数</label>
tpl += '<div class="layui-input-block">'; <div class="layui-input-block">
tpl += '<input type="text" v-model="data.avatarUrl" placeholder="请输入视频号头像URL" class="layui-input">'; <select name="title_max_lines" lay-filter="titleLineClamp" v-model="data.titleLineClamp">
tpl += '</div>'; <option value="1" :selected="data.titleLineClamp == 1 || !data.titleLineClamp">1行</option>
tpl += '</div>'; <option value="2" :selected="data.titleLineClamp == 2">2行</option>
<option value="3" :selected="data.titleLineClamp == 3">3行</option>
</select>
</div>
</div>
// 填写视频标题 <!-- 显示播放按钮 -->
tpl += '<div class="layui-form-item">'; <div class="layui-form-item">
tpl += '<label class="layui-form-label sm">视频标题</label>'; <label class="layui-form-label sm">显示播放按钮</label>
tpl += '<div class="layui-input-block">'; <div class="layui-input-block">
tpl += '<input type="text" v-model="data.videoTitle" placeholder="请输入视频标题" class="layui-input">'; <input type="checkbox" name="show_play_btn" lay-filter="showPlayBtn" lay-skin="switch" :checked="data.showPlayBtn == true || !data.showPlayBtn">
tpl += '</div>'; </div>
tpl += '</div>'; </div>
</div>
// 填写视频号内容ID <div class="template-edit-title">
tpl += '<div class="layui-form-item">'; <h3>视频列表</h3>
tpl += '<label class="layui-form-label sm">内容ID</label>';
tpl += '<div class="layui-input-block">';
tpl += '<input type="text" v-model="data.feedId" placeholder="请输入视频号内容ID" class="layui-input">';
tpl += '</div>';
tpl += '</div>';
<!-- 视频号列表 -->
<div class="video-list-edit" id="videoListEdit">
<div v-for="(item, index) in data.list" :key="index" class="video-item-edit" :data-index="index">
<div class="drag-handle" style="cursor: move; padding: 10px; background: #f5f5f5; margin: -10px -10px 10px; text-align: center; color: #999;">
<span class="layui-icon layui-icon-up">拖拽排序</span>
</div>
<div class="video-item-header">
<h4>视频 {{ index + 1 }}</h4>
<button type="button" class="layui-btn layui-btn-danger layui-btn-xs" @click="removeVideoItem(index)">删除</button>
</div>
// 视频封面支持选择类型,直接上传或选择 <!-- 填写视频号 -->
tpl += '<div class="layui-form-item">'; <div class="layui-form-item">
tpl += '<label class="layui-form-label sm">封面类型</label>'; <label class="layui-form-label sm"><span class="required">*</span>视频号ID</label>
tpl += '<div class="layui-input-block">'; <div class="layui-input-block">
tpl += '<select v-model="data.coverImageType" class="layui-select">'; <input type="text" v-model="item.finderUserName" placeholder="请输入视频号ID(FinderUserName)的值" class="layui-input">
tpl += '<option value="upload">上传图片</option>'; </div>
tpl += '<option value="url">URL</option>'; </div>
tpl += '</select>';
tpl += '</div>';
tpl += '</div>';
// 填写视频封面URL <!-- 填写视频号内容ID -->
tpl += '<div class="layui-form-item" v-if="data.coverImageType == \'url\'">'; <div class="layui-form-item">
tpl += '<label class="layui-form-label sm">封面URL</label>'; <label class="layui-form-label sm"><span class="required">*</span>视频标识</label>
tpl += '<div class="layui-input-block">'; <div class="layui-input-block">
tpl += '<input type="text" v-model="data.coverUrl" placeholder="请输入视频封面URL" class="layui-input">'; <input type="text" v-model="item.feedId" placeholder="请输入视频唯一标识FeedID的值" class="layui-input">
tpl += '</div>'; </div>
tpl += '</div>'; </div>
// 上传视频封面 <!-- 填写视频标题 -->
tpl += '<div class="layui-form-item" v-if="data.coverImageType == \'upload\'">'; <div class="layui-form-item">
tpl += '<label class="layui-form-label sm">封面图</label>'; <label class="layui-form-label sm"><span class="required">*</span>视频标题</label>
tpl += '<img-upload :data="{data : data, field : \'coverUrl\'}"></img-upload>'; <div class="layui-input-block">
tpl += '</div>'; <input type="text" v-model="item.videoTitle" placeholder="请输入视频标题" class="layui-input">
</div>
</div>
tpl += '</div>'; <!-- 视频封面支持选择类型,直接上传或选择 -->
<div class="layui-form-item">
<label class="layui-form-label sm"><span class="required">*</span>封面类型</label>
<div class="layui-input-block">
<input type="radio" :name="'cover_image_type_' + index" lay-filter="coverImageType" :data-index="index" value="url" title="URL链接" :checked="item.coverImageType == 'url' || !item.coverImageType">
<input type="radio" :name="'cover_image_type_' + index" lay-filter="coverImageType" :data-index="index" value="upload" title="上传图片" :checked="item.coverImageType == 'upload'">
</div>
</div>
tpl += '</div>'; <!-- 填写视频封面URL -->
<div class="layui-form-item" v-if="item.coverImageType == 'url'">
<label class="layui-form-label sm"><span class="required">*</span>封面URL</label>
<div class="layui-input-block">
<input type="text" v-model="item.coverUrl" placeholder="请输入视频封面URL" class="layui-input">
</div>
</div>
Vue.component("wechat_channel-edit",{ <!-- 上传视频封面 -->
<div class="layui-form-item" :ref="'coverTypeUpload_' + index" v-if="item.coverImageType == 'upload'">
<label class="layui-form-label sm"><span class="required">*</span>封面图:</label>
<div class="layui-input-block img-upload" @click="uploadCover(index)">
<div class="upload-img-block simple-uploading">
<div class="upload-img-box" :id="'coverImg_' + index">
<div class="upload-default">
<i class="iconfont iconshangchuan"></i>
<p>点击上传</p>
</div>
</div>
<input type="hidden" :name="'cover_url_upload_' + index" value="" />
<i class="del">x</i>
</div>
</div>
</div>
<!-- 填写观看次数 -->
<div class="layui-form-item">
<label class="layui-form-label sm">观看次数</label>
<div class="layui-input-block">
<input type="text" v-model="item.viewCount" placeholder="请输入观看次数" class="layui-input">
</div>
</div>
<!-- 显示观看次数 -->
<div class="layui-form-item">
<label class="layui-form-label sm">显示观看次数</label>
<div class="layui-input-block">
<input type="checkbox" v-model="item.showViewCount" lay-skin="switch">
</div>
</div>
<!-- 头像类型 -->
<div class="layui-form-item">
<label class="layui-form-label sm">头像类型:</label>
<div class="layui-input-block">
<input type="radio" :name="'avatar_image_type_' + index" lay-filter="avatarType" :data-index="index" value="url" title="URL链接" :checked="item.avatarImageType == 'url' || !item.avatarImageType">
<input type="radio" :name="'avatar_image_type_' + index" lay-filter="avatarType" :data-index="index" value="upload" title="上传图片" :checked="item.avatarImageType == 'upload'">
</div>
</div>
<!-- 填写头像URL -->
<div class="layui-form-item" v-if="item.avatarImageType == 'url'">
<label class="layui-form-label sm">头像URL</label>
<div class="layui-input-block">
<input type="text" v-model="item.avatarUrl" maxlength="255" autocomplete="off" placeholder="请输入头像URL" class="layui-input len-long">
</div>
</div>
<!-- 上传头像 -->
<div class="layui-form-item" v-if="item.avatarImageType == 'upload'">
<label class="layui-form-label sm">头像图:</label>
<div class="layui-input-block img-upload" @click="uploadAvatar(index)">
<div class="upload-img-block square simple-uploading">
<div class="upload-img-box" :id="'avatarImg_' + index">
<div class="upload-default">
<i class="iconfont iconshangchuan"></i>
<p>点击上传</p>
</div>
</div>
<input type="hidden" :name="'avatar_url_upload_' + index" value="" />
<i class="del">x</i>
</div>
</div>
<div class="word-aux sm">推荐使用 200x200 像素的图片</div>
</div>
</div>
</div>
<!-- 添加视频号按钮 -->
<button type="button" class="layui-btn layui-btn-normal" @click="addVideoItem">添加视频</button>
</div>
</div>`,
data: function () { data: function () {
return { return {
data: this.$parent.data, data: this.$parent.data
}; };
}, },
created : function(){ created: function () {
if(!this.$parent.data.verify) this.$parent.data.verify = []; if (!this.$parent.data.verify) this.$parent.data.verify = [];
this.$parent.data.verify.push(this.verify);//加载验证方法 this.$parent.data.verify.push(this.verify);//加载验证方法
this.$parent.data.ignore = ['textColor','componentBgColor','elementBgColor','elementAngle'];//加载忽略内容 -- 其他设置中的属性设置 this.$parent.data.ignore = ['textColor', 'componentBgColor', 'elementBgColor', 'elementAngle'];//加载忽略内容 -- 其他设置中的属性设置
this.$parent.data.ignoreLoad = true; // 等待忽略数组赋值后加载 this.$parent.data.ignoreLoad = true; // 等待忽略数组赋值后加载
}, },
mounted: function () {
console.log("wechat_channel-edit", this.data)
// 初始化数据结构
if (!this.data.list) {
this.data.list = [];
}
if (!this.data.rowCount) {
this.data.rowCount = 2;
}
if (!this.data.showStyle) {
this.data.showStyle = 'fixed';
}
if (!this.data.aspectRatio) {
this.data.aspectRatio = '16:9';
}
if (!this.data.titleLineClamp) {
this.data.titleLineClamp = 1;
}
if (this.data.showPlayBtn === undefined) {
this.data.showPlayBtn = true;
}
// 初始化列表项
this.data.list.forEach((item, index) => {
if (!item.avatarImageType) {
item.avatarImageType = 'url';
} else if (item.avatarUrl && item.avatarImageType == 'upload') {
var val = '<img src="' + ns.img(item.avatarUrl) + '" alt="头像">';
$("#avatarImg_" + index).html(val);
}
if (!item.coverImageType) {
item.coverImageType = 'url';
} else if (item.coverUrl && item.coverImageType == 'upload') {
var val = '<img src="' + ns.img(item.coverUrl) + '" alt="封面">';
$("#coverImg_" + index).html(val);
}
if (item.showViewCount === undefined) {
item.showViewCount = true;
}
});
layui.use(['form'], () => {
var form = layui.form;
form.render();
// 头像类型切换
form.on('radio(avatarType)', (data) => {
const elem = data.elem;
const type = elem.value;
const index = elem.dataset.index;
if (index !== undefined && this.data.list[index]) {
this.data.list[index].avatarImageType = type;
this.$nextTick(() => {
if (type == 'upload' && this.data.list[index].avatarUrl) {
var val = '<img src="' + ns.img(this.data.list[index].avatarUrl) + '" alt="头像">';
$("#avatarImg_" + index).html(val);
}
});
}
});
// 封面类型切换
form.on('radio(coverImageType)', (data) => {
const elem = data.elem;
const type = elem.value;
const index = elem.dataset.index;
if (index !== undefined && this.data.list[index]) {
this.data.list[index].coverImageType = type;
this.$nextTick(() => {
if (type == 'upload' && this.data.list[index].coverUrl) {
var val = '<img src="' + ns.img(this.data.list[index].coverUrl) + '" alt="封面">';
$("#coverImg_" + index).html(val);
}
});
}
});
// 封面比例切换
form.on('radio(aspectRatio)', (data) => {
const elem = data.elem;
const ratio = elem.value;
this.data.aspectRatio = ratio;
});
// 标题最多行数切换
form.on('select(titleLineClamp)', (data) => {
const lines = parseInt(data.value);
this.data.titleLineClamp = lines;
});
// 每行显示数量切换
form.on('select(rowCount)', (data) => {
const count = parseInt(data.value);
this.data.rowCount = count;
});
// 显示风格切换
form.on('radio(showStyle)', (data) => {
const elem = data.elem;
const style = elem.value;
this.data.showStyle = style;
});
// 显示播放按钮切换
form.on('switch(showPlayBtn)', (data) => {
const elem = data.elem;
const checked = elem.checked;
this.data.showPlayBtn = checked;
});
// 显示观看次数切换
form.on('switch', (data) => {
const elem = data.elem;
const checked = elem.checked;
// 查找对应的视频号索引
const videoItem = elem.closest('.video-item-edit');
if (videoItem) {
const index = videoItem.dataset.index;
if (index !== undefined && this.data.list[index]) {
this.data.list[index].showViewCount = checked;
this.$forceUpdate();
}
}
});
});
// 初始化拖拽排序
this.initSortable();
},
methods: { methods: {
verify : function () { verify: function () {
var res = { code : true, message : "" }; var res = { code: true, message: "" };
// 微信视频号组件暂不需要强制验证 // 微信视频号组件暂不需要强制验证
return res; return res;
},
// 上传封面图片
uploadCover: function (index) {
// 从媒体库中选择
openAlbum((data) => {
const imgUrl = data[0].pic_path;
var val = '<img src="' + ns.img(imgUrl) + '" alt="封面">';
$("#coverImg_" + index).html(val);
if (this.data.list[index]) {
this.data.list[index].coverUrl = imgUrl;
}
}, 1);
},
// 上传头像图片
uploadAvatar: function (index) {
// 从媒体库中选择
openAlbum((data) => {
const imgUrl = data[0].pic_path;
var val = '<img src="' + ns.img(imgUrl) + '" alt="头像">';
$("#avatarImg_" + index).html(val);
if (this.data.list[index]) {
this.data.list[index].avatarUrl = imgUrl;
}
}, 1);
},
// 添加视频号
addVideoItem: function () {
this.data.list.push({
"channelType": "wechat",
"channelName": "",
"finderUserName": "",
"avatarImageType": "url",
"avatarUrl": "",
"videoTitle": "",
"coverImageType": "url",
"coverUrl": "",
"feedId": "",
"feedToken": "",
"viewCount": 0,
"showViewCount": true,
"embedMode": false
});
this.$forceUpdate();
this.$parent.$forceUpdate();
// 重新渲染表单并绑定事件
this.$nextTick(() => {
layui.use(['form'], () => {
var form = layui.form;
form.render();
// 重新绑定头像类型切换事件
form.on('radio(avatarType)', (data) => {
const elem = data.elem;
const type = elem.value;
const index = elem.dataset.index;
if (index !== undefined && this.data.list[index]) {
this.data.list[index].avatarImageType = type;
this.$forceUpdate();
this.$nextTick(() => {
if (type == 'upload' && this.data.list[index].avatarUrl) {
var val = '<img src="' + ns.img(this.data.list[index].avatarUrl) + '" alt="头像">';
$("#avatarImg_" + index).html(val);
}
});
}
});
// 重新绑定封面类型切换事件
form.on('radio(coverImageType)', (data) => {
const elem = data.elem;
const type = elem.value;
const index = elem.dataset.index;
if (index !== undefined && this.data.list[index]) {
this.data.list[index].coverImageType = type;
this.$forceUpdate();
this.$nextTick(() => {
if (type == 'upload' && this.data.list[index].coverUrl) {
var val = '<img src="' + ns.img(this.data.list[index].coverUrl) + '" alt="封面">';
$("#coverImg_" + index).html(val);
}
});
}
});
});
// 重新初始化拖拽排序
this.initSortable();
});
},
// 删除视频号
removeVideoItem: function (index) {
this.data.list.splice(index, 1);
this.$forceUpdate();
this.$parent.$forceUpdate();
// 重新初始化拖拽排序
this.$nextTick(() => {
this.initSortable();
});
},
// 初始化拖拽排序
initSortable: function () {
// 检查Sortable库是否已加载
if (typeof Sortable !== 'undefined') {
const videoList = document.getElementById('videoListEdit');
if (videoList) {
// 销毁现有实例
if (this.sortableInstance) {
this.sortableInstance.destroy();
}
// 创建新的Sortable实例
this.sortableInstance = new Sortable(videoList, {
handle: '.drag-handle',
animation: 150,
onEnd: (evt) => {
// 获取拖拽前后的索引
const oldIndex = evt.oldIndex;
const newIndex = evt.newIndex;
// 重新排序数组
if (oldIndex !== newIndex) {
const [movedItem] = this.data.list.splice(oldIndex, 1);
this.data.list.splice(newIndex, 0, movedItem);
// 强制更新视图
this.$forceUpdate();
this.$parent.$forceUpdate();
// 重新渲染表单
layui.use(['form'], () => {
var form = layui.form;
form.render();
});
}
}
});
}
} else {
// 如果Sortable库未加载尝试动态加载
const script = document.createElement('script');
script.src = 'https://cdnjs.cloudflare.com/ajax/libs/Sortable/1.15.0/Sortable.min.js';
script.onload = () => {
this.initSortable();
};
document.head.appendChild(script);
}
} }
}, },
template: tpl
}); });

View File

@@ -65,7 +65,7 @@
}, },
iconStyle(){ iconStyle(){
if (!this.value) return {}; if (!this.value) return {};
console.log(this.value) // console.log(this.value)
var style = { var style = {
fontSize: this.value.fontSize + '%' fontSize: this.value.fontSize + '%'
} }

View File

@@ -149,7 +149,7 @@
/* 选中弹窗广告组件 */ /* 选中弹窗广告组件 */
.pop-window-wrap.selected .edit-attribute{display: block;} .pop-window-wrap.selected .edit-attribute{display: block;}
.edit-attribute .attr-wrap {width: 392px;overflow-x: hidden;overflow-y: auto;height: 600px;} .edit-attribute .attr-wrap {width: 392px;overflow-x: hidden;overflow-y: auto;min-height: 200px;max-height: calc(100vh - 150px);}
.edit-attribute .attr-wrap .restore-wrap {width: 360px;} .edit-attribute .attr-wrap .restore-wrap {width: 360px;}
.edit-attribute .attr-wrap .restore-wrap .layui-form-label {color: #666 !important;} .edit-attribute .attr-wrap .restore-wrap .layui-form-label {color: #666 !important;}
.edit-attribute .attr-wrap .restore-wrap .attr-title {padding: 10px 0 15px 10px;border-bottom: 2px solid #f2f4f6;margin-bottom: 10px;color: #303133;display: flex;justify-content: space-between;align-items: center;} .edit-attribute .attr-wrap .restore-wrap .attr-title {padding: 10px 0 15px 10px;border-bottom: 2px solid #f2f4f6;margin-bottom: 10px;color: #303133;display: flex;justify-content: space-between;align-items: center;}

View File

@@ -476,8 +476,6 @@ var vue = new Vue({
// 如果当前编辑的组件不存在了,则选中最后一个 // 如果当前编辑的组件不存在了,则选中最后一个
if (parseInt(self.currentIndex) >= self.data.length) self.currentIndex--; if (parseInt(self.currentIndex) >= self.data.length) self.currentIndex--;
$(".draggable-element[data-index=" + self.currentIndex + "] .edit-attribute .attr-wrap").css("height", ($(window).height() - 135) + "px");
}, 50); }, 50);
}, },
@@ -630,7 +628,8 @@ function fullScreenSize(isFull) {
}); });
$('.loading-layer').css('height', (commonHeight - 70) + "px"); // 70px是头部高度 $('.loading-layer').css('height', (commonHeight - 70) + "px"); // 70px是头部高度
$(".component-list nav").css("height", (commonHeight + 20 - 55) + "px");// 20px是自定义模板区域上内边距55px是标准/第三方组件tab切换高度 $(".component-list nav").css("height", (commonHeight + 20 - 55) + "px");// 20px是自定义模板区域上内边距55px是标准/第三方组件tab切换高度
$(".edit-attribute .attr-wrap").css("height", (commonHeight - 1) + "px");// 1px是上边框 // 设置属性编辑器的最大高度,与预览容器保持一致
$(".edit-attribute .attr-wrap").css("max-height", (commonHeight) + "px");
$(".preview-block").css("min-height", (commonHeight - 104) + "px"); // 公式:高度 - 自定义模板区域上内边距20px - 自定义模板区域下外编辑20px- 自定义模板头部64px $(".preview-block").css("min-height", (commonHeight - 104) + "px"); // 公式:高度 - 自定义模板区域上内边距20px - 自定义模板区域下外编辑20px- 自定义模板头部64px
} }

2
src/public/static/js/Sortable.min.js vendored Normal file

File diff suppressed because one or more lines are too long