注意:scrollTo方法,动画属性在ios不生效,在移动端不建议用
<template>
<div class="template-page">
<div class="header">
<form class='header-input' action="javascript:;">
<span class='input-icon'></span>
<input class='input-box'
type="search"
v-model.trim='searchValue'
placeholder="搜索海量模板"
ref='searchRef'
:class='{isAndroid: !isIOS()}'
@keyup='handleKeyChange($event)'>
</form>
</div>
<div class="main">
<section class="category-wrapper"
ref="categoryWrapper">
<div class="category-list"
ref="categoryList">
<van-sidebar
v-model="activeIndex"
@change="onChange">
<van-sidebar-item
v-for="(info, i) in categoryList"
:key="`sidebar${i}`"
:title="info.name"/>
</van-sidebar>
</div>
</section>
<section class="cont-wrapper">
<div class="poster-wrapper">
<scroll
:isEnd='isEnd'
@pullup='loadMore'
:isLoading='true'
:preventDefaultException='{ tagName: /^(INPUT|TEXTAREA|BUTTON|SELECT|AUDIO)$/ }'
:tagException='{ className: /(^|\s)chat-box(\s|$)/ }'
ref='scrollBox'>
<ul class="poster-list"
v-if="templateList.length">
<li class="poster-item"
v-for="(tempInfo, i) in templateList"
:key="`poster${i}`"
@click.stop="editPoster(tempInfo, i)">
<div class="item-header">
<p class="name">{{tempInfo.name}}</p>
</div>
<div class="item-cont"
:style="{height: `${(132 / tempInfo.content.width) * tempInfo.content.height}px` }">
<img :src="tempInfo.content.thumb" alt=""/>
</div>
<div class="item-footer">{{formatDate(tempInfo.updatedAt)}}</div>
</li>
</ul>
</scroll>
</div>
</section>
</div>
<div class="footer-wrapper">
<tabBar/>
</div>
</div>
</template>
<script>
import { Sidebar, SidebarItem } from 'vant'
import scroll from '@/components/scroll'
import tabBar from '@/views/template/components/tabBar'
import axios from 'axios'
import ZhihuiEditor from 'zhihui-editor-mobile-sdk'
import { sigCode, getTimeStamp } from '@/utils/zhihuiSig'
export default {
name: 'templateCenter',
components: {
[Sidebar.name]: Sidebar,
[SidebarItem.name]: SidebarItem,
scroll,
tabBar
},
data () {
return {
// 调用腾讯云智绘API所需参数
// 获取账户信息
editorUserInfo: {
secretId: '',
secretKey: '',
uin: '',
userId: ''
},
// 获取access_token用的sig
accessTokenSig: '',
accessToken: '', // access_token
// 获取存储信息
editorAppInfo: {
appid: '',
appKey: '',
channel: '',
stamp: 'testUserId',
timestamp: getTimeStamp(),
scope: 'all'
},
// 获取编辑器信息
editorSig: '',
editorToken: '',
// 左侧分类
activeIndex: 0,
categoryList: [],
searchValue: '',
currPage: 1,
pageSize: 20,
currCategoryId: [],
templateList: [],
isEnd: true,
// 流媒体布局
waterFallOptions: {
containerSelector: '.poster-list',
cardSelector: '.poster-item',
paddingLeft: 0,
paddingRight: 0,
paddingTop: 0,
paddingBottom: 10,
distanceX: 10,
distanceY: 10,
cardWidth: 132,
animation: true
}
}
},
methods: {
// 编辑模板
editPoster (info, index) {
this.currTemplateInfo = info
this.currTemplateIndex = index
this.initEditor()
},
/**
* 参数说明
* @stamp 业务方用户id
* @templateId 模板ID
* 1、来源于智绘系统提供的模版id,如果需要可以联系管理员。2、如果是用户自定义的模版数据,也就是from为userConfig时,那么此字段为时间戳
*/
initEditor () {
if (!this.editorToken) {
return this.$toast('token获取失败,请刷新页面')
}
// let templateId = this.currTemplateInfo && this.currTemplateInfo.recordId ? this.currTemplateInfo.recordId : '6254db7bccc34b01db8666ec'
let templateId = this.currTemplateInfo && this.currTemplateInfo._id ? this.currTemplateInfo._id : '6254db7bccc34b01db8666ec'
// templateId: 61277c23c96d087b42c7276b
let option = {
appid: this.editorAppInfo.appid,
channel: this.editorAppInfo.channel,
stamp: this.editorAppInfo.stamp,
timestamp: this.editorAppInfo.timestamp,
scope: this.editorAppInfo.scope, // 注意此处的scope是按照字符串传入
templateId: templateId,
from: 'material',
// 添加时通过修改模板创建
// from: this.currTemplateInfo ? 'record' : 'material',
// 添加时通过自定义模板创建
// from: this.currTemplateInfo ? 'record' : 'userConfig',
// // from为'userConfig'时,templateDataConfig必须填写
// templateDataConfig: [
// // 初始化模板大小
// {
// id: 'workarea', // 必须包含此id
// top: 0,
// left: 0,
// width: 1242,
// height: 2208
// }
// ],
token: this.editorToken,
/**
* loading配置,修改加载时logo的显示
*/
loadingConfig: {
logoUrl: 'https://medchat.yuemia.com/storage/b55c/e68c/png/facea3c43fa3dc95a464e83d6f30ad71.png'
},
/**
* 头部配置
*/
headConfig: {
logoUrl: 'https://medchat.yuemia.com/storage/b55c/e68c/png/facea3c43fa3dc95a464e83d6f30ad71.png', // 头部的logo
downloadName: '完成', // 下载按钮的名称
// 用户点击下载按钮callback
onClickDownload: res => {
// console.log('res', res)
this.zhihuiEditor.closeIframe()
// 完成后进入做题记录页
this.$router.push({ name: 'templateRecord' })
},
isDownloadImg: false // 点击下载按钮后是否下载图片
}
}
// console.log('option', option)
this.zhihuiEditor = new ZhihuiEditor(option)
this.zhihuiEditor.openIframe()
},
// 切换分类
onChange (index) {
this.isEnd = false
this.activeIndex = index
let categoryInfo = this.categoryList[index]
let categoryId = ''
categoryId = categoryInfo ? categoryInfo['_id'] : ''
this.currCategoryId = [categoryId]
this.currPage = 1
this.templateList = []
this.fetchTemplateList()
},
// 关键词搜索
handleKeyChange (event) {
if (event.keyCode === 13) {
if (!this.searchValue) {
return this.$toast('请输入关键词')
}
this.isEnd = false
this.currPage = 1
this.templateList = []
this.fetchTemplateList()
}
},
loadMore () {
this.lazyloadId = this.createUniqueId()
this.currPage += 1
this.fetchTemplateList()
},
refreshScroll () {
setTimeout(() => {
if (this.$refs.scrollBox) {
this.$refs.scrollBox.refresh()
}
}, 0)
},
// 获取智慧云账号和用户信息
fetchEditorUserInfo () {
return new Promise((resolve, reject) => {
let url = `/zhihui/index`
this.$get(url).then(res => {
if (res.code === 200) {
resolve(res)
} else {
this.$_load.hide()
reject(res.messgae)
}
}).catch(err => {
this.$_load.hide()
reject(err)
})
})
},
// 获取access_token用的sig
fetchAccessTokenSig () {
let params = {
id: this.editorUserInfo.userId, // 用户 userId
secretId: this.editorUserInfo.secretId // 分配的密钥 Id
}
let key = this.editorUserInfo.secretKey
// console.log('sig-params', params)
// console.log('sig-key', key)
return sigCode(params, key)
},
// 获取 access_token
fetchAccessToken () {
return new Promise((resolve, reject) => {
let url = `获取token的接口`
let params = {
id: this.editorUserInfo.userId, // 用户 userId
secretId: this.editorUserInfo.secretId, // 分配的密钥 Id
sig: this.accessTokenSig
}
// console.log('params', params)
axios.post(url, params).then(res => {
// console.log('res', res)
if (res) {
resolve(res)
} else {
this.$_load.hide()
// reject('无返回值')
}
}).catch(err => {
this.$_load.hide()
reject(err)
})
})
},
// 初始化获取模板素材
initZhihui () {
this.$_load.show()
this.fetchEditorUserInfo().then(res => {
let data = res.data
// console.log('info-data', data)
if (data) {
this.editorUserInfo = {
secretId: data.secretId,
secretKey: data.secretKey,
uin: data.uin,
userId: data.userId
}
let accessTokenSig = this.fetchAccessTokenSig()
this.accessTokenSig = accessTokenSig
// console.log('accessTokenSig', accessTokenSig)
this.fetchAccessToken().then(res => {
// console.log('fetchAccessToken', res)
let data = res.data.data
if (data) {
this.accessToken = data.access_token
this.fetchCategoryList()
}
})
}
})
},
// 获取分类
fetchCategoryList () {
if (!this.accessToken) {
return this.$toast('token获取失败,请刷新页面')
}
let url = `获取分类的接口`
axios.get(url, {
headers: {
'Authorization': 'Bearer ' + this.accessToken
}
}).then(res => {
let data = res.data.data
if (data) {
this.categoryList = data
if (!this.currCategoryId) {
let categoryId = ''
let categoryInfo = this.categoryList[0]
categoryId = categoryInfo ? categoryInfo['_id'] : ''
this.currCategoryId = [categoryId]
} else {
let currIndex = this.categoryList.findIndex(item => item._id === this.currCategoryId[0])
this.activeIndex = currIndex < 1 ? 0 : currIndex
this.initCurrCategory()
}
this.fetchTemplateList()
}
}).catch(err => {
this.$_load.hide()
this.handleError(err)
})
},
// 获取模板列表
fetchTemplateList () {
if (!this.accessToken) {
return this.$toast('token获取失败,请刷新页面')
}
let url = `获取模板的接口`
let params = {
filter: { state: 'success', type: 'image' }, // 不需要更改
filterTermArr: this.currCategoryId, // 根据检索的分类id进行修改
hideObjects: true, // 不需要更改
keyword: this.searchValue || null, // 不需要更改
pageNumber: this.currPage, // 当前页码
pageSize: this.pageSize, // 每页数量
sort: { id: 'desc' } // 排序
}
if (this.loading) {
return
}
this.loading = true
axios.post(url, params, {
headers: {
'Authorization': 'Bearer ' + this.accessToken
}
}).then(res => {
this.loading = false
let data = res.data.data
if (data) {
this.count = data.info.total
let templateList = data.items
this.templateList = this.templateList.concat(templateList)
this.$nextTick(() => {
this.handleWaterFall()
})
if (this.count > this.templateList.length) {
this.isEnd = false
} else {
this.isEnd = true
}
this.refreshScroll()
}
this.$_load.hide()
}).catch(err => {
this.loading = false
this.$_load.hide()
this.handleError(err)
})
},
initZhihui2 () {
// 获取appid、channel、scope、appKey
this.fetchEditorUserInfo().then(res => {
let data = res.data
// console.log('data', data)
if (data) {
this.editorAppInfo = {
appid: data.appid,
channel: data.channel,
stamp: 'testUserId',
timestamp: getTimeStamp(),
// timestamp: 1629711353224,
scope: data.scope,
appKey: data.appKey
}
let sig = this.fetchSig()
this.editorSig = sig
// console.log('sig', sig)
this.fetchEditorToken().then(res => {
let data = res.data.data
if (data) {
let token = data.access_token
this.editorToken = token
// console.log('token', token)
}
})
}
})
},
// 获取sig签名
fetchSig () {
let params = Object.assign({}, this.editorAppInfo)
let key = this.editorAppInfo.appKey
// 注意此处scope参数是按照数组形式传入
params.scope = [this.editorAppInfo.scope]
delete params.appKey
// console.log('sig-params', params)
// console.log('sig-key', key)
return sigCode(params, key)
},
// 发送请求获取token
fetchEditorToken () {
return new Promise((resolve, reject) => {
let url = `获取token的接口`
let params = Object.assign({}, this.editorAppInfo)
// 注意此处scope参数是按照数组形式传入
params.scope = [this.editorAppInfo.scope]
params.sig = this.editorSig
delete params.appKey
// console.log('token', params)
axios.post(url, params).then(res => {
// console.log('res', res)
if (res) {
resolve(res)
} else {
// reject('无返回值')
}
}).catch(err => {
reject(err)
})
})
},
// 初始化分类,滚动到高亮位置
initCurrCategory () {
this.$nextTick(() => {
// 获取bScroll的外层容器及高度
let wrapperBox = this.$refs.categoryWrapper
let wrapperDomOffsetTop = wrapperBox.offsetTop
// 获取当前高亮元素
// 计算出栏目列表的总长度
let categoryList = this.$refs.categoryList
let listWrapper = categoryList.getElementsByClassName('van-sidebar')[0]
let listItemsArr = Array.prototype.slice.call(listWrapper.childNodes)
let currIndex = this.activeIndex
let currLabelDom = listItemsArr[currIndex]
let currDomOffsetTop = currLabelDom.offsetTop
wrapperBox.scrollTo({ top: currDomOffsetTop - wrapperDomOffsetTop - 50, behavior: 'smooth' })
})
}
},
created () {
if (this.$route.query.categoryId) {
this.currCategoryId = [this.$route.query.categoryId]
}
this.initZhihui()
this.initZhihui2()
this.handleWaterFall = this.waterFall.bind(this, this.waterFallOptions)
window.addEventListener('resize', this.handleWaterFall)
},
beforeDestroy () {
window.removeEventListener('resize', this.handleWaterFall)
// 跳出该页面关闭编辑器
if (this.zhihuiEditor) {
this.zhihuiEditor.closeIframe()
}
}
}
</script>
<style lang="stylus" scoped>
.template-page
width 100%
height 100vh
background #fff
padded_box(border-box, 0 0 50px)
position relative
.header
width 100%
height 56px
padded_box(border-box, 10px)
.header-input
height: 36px;
background: #F0F1F3;
// border-radius: 18px;
border-radius 6px
display flex
align-items center
padding 0 12px
.input-icon
flex none
width 20px
height 20px
margin-right 3px
background url('~assets/img/home_ic_search@3x.png') no-repeat center / 100%
.input-box
flex 1
height 24px
line-height 24px
color: #646363
font-size 14px
border none
outline none
&::-webkit-input-placeholder
color rgba(100, 99, 99, 0.5)
&::-webkit-search-cancel-button
-webkit-appearance: none
&.isAndroid
position relative
top -1px
&::-webkit-input-placeholder
position relative
top 1px
.main
width 100%
height: calc(100% - 56px)
display: flex
.category-wrapper
width auto
height 100%
overflow-x hidden
overflow-y: auto
.category-list
width auto
height: 100%
.cont-wrapper
flex: 1
height 100%
padded_box(border-box, 0 10px 10px)
.poster-wrapper
width 100%
height 100%
overflow: auto
position: relative
.poster-list
width 100%
display flex
flex-wrap wrap
.poster-item
width: 132px;
// max-height: 500px;
background: #FFFFFF;
border: 1px solid #D0D0D1;
border-radius: 4px;
margin 0 0 10px 0
overflow: hidden
position relative
&:nth-of-type(2n+1)
margin-right 10px
.item-header
display: flex
justify-content: space-between
align-items: center
width 100%
height: 32px;
line-height 16px
font-size 12px
padded_box(border-box,8px)
.name
width 100%
height 16px
line-height 16px
noWrap()
.item-cont
width 100%
img
width 100%
// max-height 454px
height 100%
.item-footer
width 100%
height: 32px;
line-height 16px
font-size 12px
color #fff
padded_box(border-box, 8px)
background-image: linear-gradient(180deg, rgba(0,0,0,0.00) 0%, rgba(0,0,0,0.50) 100%);
position absolute
left 0
right 0
bottom 0
.footer-wrapper
width 100%
height 50px
padded_box(border-box, 0 10px)
position: absolute
left 0
right 0
bottom: 0
z-index 1000
</style>
14785216-d567c66033844f3b(1).png