Commit 819117c1 authored by jyx's avatar jyx

代码优化

parent c9610869
......@@ -18,10 +18,10 @@ function gotoBookSearchPage(searchType = ENUM_SEARCH_TYPE.WAREHOUSE, keyword) {
// 文章详情
function gotoBookContentPage(bookId, shortis) {
if (shortis && shortis == 1) {
gotoLongBookContentPage(bookId)
} else {
if (shortis && shortis == 0) {
gotoShortBookContentPage(bookId)
} else {
gotoLongBookContentPage(bookId)
}
}
......
......@@ -3,7 +3,7 @@
<swiper :autoplay="true" :interval="2000" :duration="500">
<swiper-item @click="handleBanner(item)" v-for='(item, index) in bannerList' :key='index'
style="border-radius: 20rpx;">
<image class="banner-img" :src="item.carouselUrl" mode="scaleToFill"></image>
<image class="banner-img" :src="item.carouselUrl" mode="aspectFill"></image>
</swiper-item>
</swiper>
</view>
......@@ -51,11 +51,11 @@
<style lang="scss">
.banneritem {
margin: 30rpx 30rpx 0 30rpx;
margin: 20rpx 30rpx 0 30rpx;
// height: 310rpx;
.banner-img {
height: 310rpx;
height: 320rpx;
}
}
</style>
\ No newline at end of file
<template>
<scroll-view class="c-list" :style="[scrollStyle]" scroll-y :refresher-enabled='ableRefresh'
refresher-threshold='100' :enable-flex='true' :refresher-triggered='refreshTrigger' @refresherrefresh='refresh'
@scrolltolower='onLoadMore'>
refresher-default-style="black" refresher-threshold='100' :enable-flex='true'
:refresher-triggered='refreshTrigger' @refresherrefresh='refresh' @scrolltolower='onLoadMore'>
<view class="content" :style="[contentStyle]">
<template v-if='showCover'>
<slot name="cover"></slot>
......@@ -18,7 +18,7 @@
</template>
</view>
<uni-load-more v-if='ableLoadMore' :status="loadMoreStatus.value" :contentText="loadMoreContentText"
iconType="snow"></uni-load-more>
iconType="circle"></uni-load-more>
</scroll-view>
</template>
......@@ -279,7 +279,7 @@
}
},
/** customeRequst 不会使用的接口*/
requestData() {
requestData(noLoading) {
if (this.needLogin && !this.userInfo) {
this.loading = false;
this.closeTrigger();
......@@ -304,7 +304,9 @@
header: this.header || {},
callback: (success, data) => {
this.loading = false;
if (!noLoading) {
this.closeTrigger();
}
if (success) {
let rows = data.records;
let total = data.total;
......
......@@ -54,7 +54,7 @@
this.$nextTick(() => {
let ref = this.$refs.list;
if (ref) {
ref.onPullRefreshing();
ref.requestData(1);
}
})
},
......
......@@ -2,7 +2,7 @@
<view :style="[bgStyle]">
<c-empty v-if='showEmpty'></c-empty>
<template v-else>
<detail-content-flip :detail='bookData' :userInfo='userInfo' @tabVip2="tapVipPop"></detail-content-flip>
<detail-content :detail='bookData' :userInfo='userInfo' @tabVip2="tapVipPop"></detail-content>
<detail-bottom :detail='bookData' :userInfo='userInfo' @tapBottomItem='tapBottomItem'></detail-bottom>
<setting-pop :show='showSetting' @close='closePop'></setting-pop>
<catalogue-pop :detail='bookData' :show='showCatalogue' @close='closeCataPop'
......@@ -11,6 +11,9 @@
<bean-pop v-if="bookData.isUnlock==0 && userInfo.bookLegumes<bookData.bookLegumes" :show='showBean'
@close='closeBeanPop'></bean-pop> -->
<recommend-pop :show='showRecommend' @close='closeRecommendPop' :bookId="bookId"></recommend-pop>
<coin-popup v-if="bookData.isUnlock==0 && !isVip()" :show="showVip" @close="closeVipPop"
:vedioId="bookData.id" @paySuccess="paySuccess">
</coin-popup>
</template>
<!-- <c-login :isShareLink="true"></c-login> -->
<popup :show="showMoibleLogin" @close="showMoibleLogin=false">
......@@ -41,7 +44,6 @@
import DetailWarn from "./components/detail-warn.vue";
import DetailThumb from "./components/detail-thumb.vue";
import DetailContent from "./components/detail-content.vue";
import DetailContentFlip from "./components/detail-content-flip.vue"
import DetailBottom from "./components/detail-bottom.vue";
import DetailNewBuy from "./components/detail-new-buy.vue";
import config from "../../../config/index.js";
......@@ -64,8 +66,7 @@
noticeCollectionListChange,
startCountReadTime,
endCountReadTime,
getOpens,
getChapterinfoData
getOpens
} from "../../../common/services/index.js"
import {
saveStorage,
......@@ -77,7 +78,6 @@
DetailWarn,
DetailThumb,
DetailContent,
DetailContentFlip,
DetailNewBuy,
DetailBottom,
SettingPop,
......@@ -112,8 +112,6 @@
this.bookId = info.bookId;
})
}
this.mobileLoginLock = readStorage("KEY_NEED_PHONE")
},
onReady() {
// 监听样式变动
......@@ -216,8 +214,6 @@
console.log(e.detail.errMsg) // 回调信息(成功失败都会返回)
console.log(e.detail.errno) // 错误码(失败时返回)
this.showMoibleLogin = false
if (e.detail.code == undefined || e.detail.code == '') {
uni.showModal({
title: "提示",
......@@ -254,13 +250,13 @@
// this.showVipOpen = data.openVips
// })
getBookDetailData(bookId, (success, data) => {
setTimeout(() => {
// setTimeout(() => {
uni.stopPullDownRefresh();
if (success) {
this.bookData = new BookDetail(data);
console.log('bookData=' + JSON.stringify(this.bookData));
}
}, 1000)
// }, 300)
})
},
// 点击底部按钮
......@@ -305,19 +301,10 @@
},
// 展示充值VIP弹框
tapVipPop() {
if (this.mobileLoginLock) {
this.showMoibleLogin = true
return
}
this.showVip = true;
},
// 展示充值书豆弹框
tapBeanPop() {
if (this.mobileLoginLock) {
this.showMoibleLogin = true
return
}
this.showBean = true;
},
......@@ -332,10 +319,9 @@
// 关闭会员弹窗
closeVipPop(e) {
this.showVip = false;
// setTimeout(() => {
// this.showRecommend = true;
// }, 300);
setTimeout(() => {
this.showRecommend = true;
}, 300);
},
// 关闭书豆弹窗
closeBeanPop(e) {
......
<template>
<uni-popup ref='pop' type="left" :is-mask-click='false' :safe-area='false' @maskClick='tapMask'>
<scroll-view scroll-y style="background-color: white;">
<view @click="tapMask">
<scroll-view scroll-y style="background-color: white;width: 400rpx;max-width: 400rpx;" @click.stop="">
<view class="setting-box">
<block v-for='(item, index) in catalogueList' :key='index' :item='item'>
<view @click="changeCatalogue(item.chapterNum)">
<view @click.stop="changeCatalogue(item.chapterNum)">
<view
style="width: 400rpx;max-width: 400rpx;display: flex;flex-direction: row;justify-content: space-between;align-items: center;">
style="display: flex;flex-direction: row;justify-content: space-between;align-items: center;">
<text class="text"
:style="{'width: 350rpx;color': item.chapterNum==currentCatalogue?'#f00':'#000'}">
{{item.chapterNum}}{{item.chapterName}}</text>
<image v-if="(index+1)>detail.freeNum" style="width: 30rpx;height: 30rpx;"
src="https://mints-pkg.oss-cn-beijing.aliyuncs.com/pkg/img/lock.png"></image>
</view>
<view style="background-color: gainsboro;height: 1rpx;width: 400rpx;margin: 20rpx 0;"></view>
<view style="background-color: gainsboro;height: 1rpx;width: 400rpx;margin: 20rpx 0;">
</view>
</view>
</block>
</view>
</scroll-view>
</view>
</uni-popup>
</template>
......@@ -109,6 +112,8 @@
})
}
this.currentCatalogue = e
saveContentFormat(this.currentContentFormat);
this.close()
},
......@@ -126,7 +131,7 @@
display: flex;
flex-direction: column;
padding: 40rpx;
background: #fff;
// background: #fff;
height: 100vh;
.title {
......
<template>
<view class="detail-content">
<template>
<yingbing-ReadPage ref="page" :fontSize="fontSize" :bgColor="backgroundColor" style="height: 100vh;"
@loadmore="onLoadmore">
</yingbing-ReadPage>
</template>
</view>
</template>
<script>
import {
watchContentFormatChange,
removeContentFormatChangeWatch,
getChapterinfoData
} from "../services/index.js"
import cataloguePopVue from "./catalogue-pop.vue";
export default {
props: {
userInfo: {
type: Object,
default: function() {
return null
}
},
detail: {
type: Object,
default: function() {
return {}
}
}
},
data: function() {
return {
fontSize: '18',
backgroundColor: "#fff",
catalogue: 1,
}
},
mounted() {
// 监听样式变动
watchContentFormatChange((data) => {
let result = data.getFormatValue();
if (this.fontSize != result.fontSize) {
this.fontSize = result.fontSize.substring(0, 2)
}
if (this.backgroundColor != result.backgroundColor) {
this.backgroundColor = result.backgroundColor
}
let resultCatalogue = 1
for (let i in result.catalogueList) {
if (result.catalogueList[i].id == this.detail.id) {
resultCatalogue = result.catalogueList[i].catalogue
}
}
if (this.catalogue != resultCatalogue) {
this.catalogue = resultCatalogue
this.refreshChapterinfoData(this.catalogue)
}
}, this)
},
destroyed() {
removeContentFormatChangeWatch(this);
},
onReady() {
this.refreshChapterinfoData(this.catalogue)
},
methods: {
onLoadmore(chapter, callback) {
if (chapter >= this.detail.freeNum) {
this.tabVip()
return
}
getChapterinfoData(this.detail.id, chapter, (success, data) => {
if (success) {
this.catalogue = chapter
let mycontent = data.content.replace(/<(\/)?p>/g, '').replace(/<(\/)?sub>/g, '').replace(
/<(\/)?pre>/g, '')
.replace(/<(\/)?code>/g, '')
let content = {
chapter: chapter,
content: mycontent,
title: '第' + chapter + '章',
isStart: chapter == 1,
isEnd: chapter == this.detail.articleChapterList.length
}
callback('success', content)
}
})
},
// 文章数据刷新
refreshChapterinfoData(chapterId, callback) {
this.catalogue = chapterId
let id = this.detail.articleChapterList[chapterId].id
getChapterinfoData(this.detail.id, id, (success, data) => {
if (success) {
this.changeChapterinfo(chapterId, data.content)
}
})
},
changeChapterinfo(chapterId, content) {
this.catalogue = chapterId
let mycontent = content.replace(/<(\/)?p>/g, '').replace(/<(\/)?sub>/g, '').replace(/<(\/)?pre>/g, '')
.replace(/<(\/)?code>/g, '')
let contents = [{
chapter: chapterId, //章节序号
content: mycontent,
title: '第' + chapterId + '章', //章节标题
isStart: chapterId == 1, //是否是第一章节
isEnd: false, //是否是最后一个章节
}]
const {
page
} = this.$refs;
page.change({
contents: contents,
currentChapter: chapterId, //从第二章节开始阅读 和章节chapter对应
start: 0 //从章节的第100个字开始阅读
})
},
tabVip() {
setTimeout(() => {
this.$emit('tabVip2')
}, 1000)
}
}
}
</script>
<style lang="scss" scoped>
.detail-content {
display: flex;
flex-direction: column;
.content-paragraph {
text-indent: 1em;
word-wrap: break-word;
word-break: break-all;
white-space: normal;
line-height: 1.5em;
margin-bottom: 1em;
color: #333;
}
.content-paragraph:last-child {
margin-bottom: 0;
}
.limit-button-box {
text-indent: 0;
height: 120rpx;
line-height: 120rpx;
text-align: center;
color: goldenrod;
font-size: 28rpx;
}
}
</style>
\ No newline at end of file
<template>
<view class="detail-content">
<template v-if='showVIPContent'>
<view class="content-paragraph" :style="[contentStyle]" v-for='(item, index) in contentSources'
:key='index'>
{{item}}
</view>
</template>
<template v-else>
<view class="content-paragraph" :style="[contentStyle]" v-for='(item, index) in preContentSources'
:key="index">
{{item}}
</view>
<view class="limit-button-box" @click="tapVip">
{{limitButtonTitle}}
</view>
<view class="pages">
<yingbing-text-reader ref="reader" :total-chapter="detail.articleChapterList.length" preloadable selectable
:background="background" :fontSize="fontSize" :lineGap="30" :color="color" :page-type="pageType"
:loadPageNum="1" @back="handleBack" @change="handleChange" @loadmore="handleLoadmore">
<template v-for="(item, index) in chapters" :slot="`vip:${item.index}`">
<view class="slots">{{item.index}}章节付费阅读提示页</view>
</template>
<!-- <template v-for="(item, index) in chapters" :slot="`end:${item.index}`">
<view class="slots">第{{item.index}}章节阅读完成提示页</view>
</template> -->
</yingbing-text-reader>
</view>
</template>
<script>
import {
watchContentFormatChange,
removeContentFormatChangeWatch
removeContentFormatChangeWatch,
getChapterinfoData
} from "../services/index.js"
import {
showLoginView
} from "../../../../common/services/userServices.js"
import {
gotoVIPApplyPage
} from "../../../../common/services/page-route.js"
import User from "../../../../common/models/User.js";
export default {
props: {
......@@ -46,128 +36,155 @@
}
}
},
data: function() {
data() {
return {
user: null,
fontSize: `18px`,
PRECENT_VALUE:40
chapters: [],
pageType: 'real',
split: '',
background: '#fff',
color: '#333',
fontSize: 18,
pageType: 'cover',
catalogue: 1,
isFisrt: true
}
},
onReady() {
let total = this.detail.articleChapterList.length
for (let i = 0; i < total; i++) this.chapters.push(this.getChapter(i + 1))
},
destroyed() {
removeContentFormatChangeWatch(this);
},
mounted() {
// 监听样式变动
watchContentFormatChange((data) => {
let result = data.getFormatValue();
if (this.fontSize != result.fontSize) {
this.fontSize = result.fontSize
this.fontSize = parseInt(result.fontSize.substring(0, 2))
}
}, this)
},
destroyed() {
removeContentFormatChangeWatch(this);
},
watch: {
userInfo: {
handler: function(n) {
if (n) {
this.user = new User(n)
} else {
this.user = null
if (this.background != result.backgroundColor) {
this.background = result.backgroundColor
}
let resultCatalogue = this.catalogue
for (let i in result.catalogueList) {
if (result.catalogueList[i].id == this.detail.id) {
resultCatalogue = result.catalogueList[i].catalogue
}
},
deep: true,
immediate: true
}
if (this.catalogue != resultCatalogue) {
this.catalogue = resultCatalogue
this.refreshChapterinfoData(this.catalogue)
}
}, this)
},
computed: {
contentSources: function() {
return this.detail && this.detail.content ? this.detail.content : [];
methods: {
// 文章数据刷新
refreshChapterinfoData(chapterId) {
let id = this.detail.articleChapterList[chapterId - 1].id
getChapterinfoData(this.detail.id, id, (success, data) => {
if (success) {
this.changeChapterinfo(chapterId, data.contentMd)
}
})
},
preContentSources: function() {
let result = this.detail && this.detail.content ? [...this.detail.content] : [];
let length = result.length;
if (length >= 2) {
if(this.detail.lockRate!=null&&this.detail.lockRate>0){
this.PRECENT_VALUE=this.detail.lockRate;
}
console.log('this.PRECENT_VALUE='+this.PRECENT_VALUE);
let precent = this.PRECENT_VALUE / 100;
let preLength = Math.floor(length * precent);
return result.splice(0, preLength);
changeChapterinfo(chapterId, content) {
this.catalogue = chapterId
// let mycontent = content.replace(/<(\/)?p>/g, '').replace(/<(\/)?sub>/g, '').replace(/<(\/)?pre>/g, '')
// .replace(/<(\/)?code>/g, '')
if (this.isFisrt) {
// for (let i = 0; i < this.chapters.length; i++) {
// this.chapters[chapterId - 1].content = content
// }
this.chapters = [this.getChapter(chapterId, content)]
this.isFisrt = false
this.$refs.reader.init({
chapters: this.chapters,
title: this.detail.title,
current: chapterId,
start: 1
})
} else {
return result;
this.chapters.push(this.getChapter(chapterId, content))
// for (let i = 0; i < this.chapters.length; i++) {
// this.chapters[chapterId - 1].content = content
// }
this.$refs.reader.change({
chapters: this.chapters,
current: chapterId
})
}
},
limitButtonTitle: function() {
let result = `~剩余${100-this.PRECENT_VALUE}%内容`;
if (!this.user) {
result = `${result},点击登录后继续阅读`;
} else if (!this.user.isVip()) {
result = `${result}为会员章节内容,请前往购买会员`
}
result = `${result}~`;
return result;
},
contentStyle: function() {
return {
fontSize: this.fontSize
}
handleChange(e) {
console.log('change', e);
},
showVIPContent: function() {
return (this.user && this.user.isVip()) || this.detail.isUnlock
handleLoadmore(current, callback) {
let id = this.detail.articleChapterList[current - 1].id
getChapterinfoData(this.detail.id, id, (success, data) => {
if (success) {
callback('success', this.getChapter(current, data.contentMd))
} else {
if (current < this.detail.freeNum + 1) {
callback('success', this.getChapter(current, ""))
}
},
methods: {
tapLimitButton() {
if (!this.user) {
showLoginView()
} else if (!this.user.isVip()) {
gotoVIPApplyPage()
}
},
tapVip() {
let isIOS = wx.getSystemInfoSync().platform;
if (isIOS === 'ios') {
uni.showToast({
title: '暂不支持IOS系统',
icon: 'none'
})
return
},
getChapter(index, content) {
const showVip = index > this.detail.freeNum
return {
title: '第' + index + '章',
content: content,
frontSlots: showVip ? ['vip'] : [],
// backSlots: ['end'],
index: index,
isStart: index == 1,
isEnd: index == 10
}
// 展示充值VIP弹框
this.$emit('tapVip')
}
}
}
</script>
<style lang="scss" scoped>
.detail-content {
paading: 20rpx 30rpx;
margin: 20rpx 30rpx;
display: flex;
flex-direction: column;
.content-paragraph {
text-indent: 1em;
word-wrap: break-word;
word-break: break-all;
white-space: normal;
line-height: 1.5em;
margin-bottom: 1em;
color: #333;
<style>
.pages {
/* #ifdef APP-NVUE */
flex: 1;
/* #endif */
/* #ifndef APP-NVUE */
width: 100vw;
height: 100vh;
/* #endif */
}
.content-paragraph:last-child {
margin-bottom: 0;
.slots {
position: absolute;
width: 100%;
height: 100%;
top: 0;
left: 0;
right: 0;
bottom: 0;
color: blue;
/* #ifndef APP-NVUE */
display: flex;
flex-direction: column;
/* #endif */
align-items: center;
justify-content: center;
}
.limit-button-box {
text-indent: 0;
height: 120rpx;
line-height: 120rpx;
text-align: center;
color: goldenrod;
font-size: 28rpx;
}
.btns {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
position: fixed;
left: 0;
right: 0;
}
</style>
\ No newline at end of file
......@@ -33,6 +33,13 @@
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
}
}, {
"path": "pages/index",
"style": {
"navigationStyle": "default",
"navigationBarBackgroundColor": "#ffffff",
"navigationBarTextStyle": "black"
}
}
],
// 分包配置
......
This diff is collapsed.
## 1.6.4(2024-07-30)
* 增加加载提示文字的属性配置
## 1.6.3(2024-07-29)
* 替换滚动模式上拉加载为触底加载,解决app端上拉加载无法触发的问题
## 1.6.2(2024-06-21)
* 修复yingbing-text-reader空格不显示的问题
* 修复yingbing-text-reader底部文本会被切割的问题
## 1.6.1(2024-05-28)
* 这是更新为重构插件,不想折腾的同学可以不用更新
* 兼容了vue3
* 将插件功能细分为了3个组件,具体看下面的文档
* 这个版本必须下载最新版的yingbing-flip插件才能使用,如果你想使用翻页阅读的话
## 1.6.0(2023-12-22)
* measureSize增加藏文字符测量
* 增加charSize属性单独定义字符尺寸
......
......@@ -4,27 +4,31 @@
'border-color': color
}">
<view class="yingbing-battery-content">
<view :style="{
flex: 1,
<view class="yingbing-battery-content-value" :style="{
'background-color': color,
width: value + 'rpx'
}"></view>
width: value + 'px'
}">
</view>
</view>
</view>
<view class="yingbing-battery-top" :style="{
'background-color': color
}"></view>
</view>
</template>
<script>
const max = 16
export default {
props: {
color: {
type: String,
default: '#333'
inject: ['getColor'],
computed: {
color () {
return this.getColor()
}
},
data () {
return {
value: 54
value: max
}
},
// #ifdef APP-PLUS
......@@ -43,13 +47,13 @@
//window.navigator.getBattery只能在安全环境下(比如:https file:///url)使用,判断一下避免报错
window.navigator.getBattery && window.navigator.getBattery().then((res) => {
// 电池电量在0到1之间,因此我们将其乘以100得出百分比
this.value = res.level * 54
this.value = res.level * max
});
// #endif
// #ifdef MP-WEIXIN
wx.getBatteryInfo({
success: (res) => {
this.value = (res.level / 100) * 54
this.value = (res.level / 100) * max
}
})
// #endif
......@@ -64,7 +68,7 @@
       let action = intent.getAction(); 
       if (action == Intent.ACTION_BATTERY_CHANGED) { 
            let level = intent.getIntExtra("level", 0); //电量 B5教程网 
this.value = (level / 100) * 54
this.value = (level / 100) * max
main.unregisterReceiver(this.recevier)//销毁注册广播
//let voltage = intent.getIntExtra("voltage", 0); //电池电压 
//let temperature = intent.getIntExtra("temperature", 0); //电池温度 
......@@ -81,7 +85,7 @@
    dev.setBatteryMonitoringEnabled(true); 
} 
let level = dev.batteryLevel();
this.value = level * 54
this.value = level * max
}
}
})
......@@ -92,12 +96,20 @@
</script>
<style scoped>
.yingbing-battery {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
opacity: 0.5;
}
.yingbing-battery-wrapper {
width: 60rpx;
height: 24rpx;
width: 20px;
height: 9px;
border-width: 1px;
border-style: solid;
padding: 2rpx;
padding: 1px;
/* #ifndef APP-NVUE */
display: flex;
flex-direction: column;
......@@ -114,4 +126,14 @@
overflow: hidden;
/* #endif */
}
.yingbing-battery-content-value {
height: 5px;
}
.yingbing-battery-content-value-text {
font-size: 8px;
}
.yingbing-battery-top {
width: 2px;
height: 5px;
}
</style>
\ No newline at end of file
<template>
<view class="yingbing-reader-footer">
<view class="yingbing-reader-footer-left">
<battery ref="battery"></battery>
<text class="yingbing-reader-footer-text" :style="{color: color}">{{time}}</text>
</view>
<text class="yingbing-reader-footer-text" :style="{color: color}"> {{progress ? progress + '%' : current + ' / ' + total }}</text>
</view>
</template>
<script>
import Battery from '../battery/battery.vue'
export default {
inject: ['getColor'],
components: {
Battery
},
props: {
total: {
type: [Number, String],
default: 0
},
current: {
type: [Number, String],
default: 0
},
progress: {
type: [Number, String],
default: 0
}
},
computed: {
color () {
return this.getColor()
}
},
data () {
return {
time: ''
}
},
created() {
this.getTime()
},
methods: {
zeroize (value) {
return value >= 10 ? value : '0' + value
},
getTime () {
const d = new Date()
this.time = this.zeroize(d.getHours()) + ':' + this.zeroize(d.getMinutes())
},
refresh () {
this.$refs.battery.getBattery()
this.getTime()
}
}
}
</script>
<style scoped>
.yingbing-reader-footer {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
height: 20px;
margin-top: 10px;
}
.yingbing-reader-footer-left {
display: flex;
flex-direction: row;
align-items: center;
}
.yingbing-reader-footer-text {
font-size: 10px;
opacity: 0.5;
margin-left: 10px;
}
</style>
\ No newline at end of file
<template>
<view class="yingbing-reader-header">
<view class="yingbing-reader-header-left">
<view class="yingbing-reader-header-back" :style="{'border-top-color': color, 'border-left-color': color}" v-if="backShow"
@touchstart.stop.prevent @touchmove.stop.prevent @touchend.stop.prevent="handleBack"
@mousedown.stop.prevent @mousemove.stop.prevent @mouseup.stop.prevent="handleBack"></view>
<text class="yingbing-reader-header-text" :style="{color: color}">{{title}}</text>
</view>
<text class="yingbing-reader-header-text" :style="{color: color}" v-if="progress">{{progress.toFixed(2)}}%</text>
</view>
</template>
<script>
import Battery from '../battery/battery.vue'
export default {
inject: ['getColor', 'getBackShow'],
props: {
title: {
type: String,
default: ''
},
progress: {
type: [Number, String],
default: 0
}
},
computed: {
color () {
return this.getColor()
},
backShow () {
return this.getBackShow()
}
},
methods: {
handleBack (e) {
e.preventDefault && e.preventDefault()
e.stopPropagation && e.stopPropagation()
uni.$emit('yingbing-reader-back')
}
}
}
</script>
<style>
.yingbing-reader-header {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
margin-bottom: 10px;
height: 20px;
}
.yingbing-reader-header-left {
display: flex;
flex-direction: row;
align-items: center;
}
.yingbing-reader-header-text {
font-size: 10px;
opacity: 0.5;
}
.yingbing-reader-header-back {
width: 6px;
height: 6px;
border-top-width: 1px;
border-left-width: 1px;
border-top-style: solid;
border-left-style: solid;
transform: rotateZ(-45deg);
margin-right: 5px;
opacity: 0.5;
}
</style>
\ No newline at end of file
<template>
<view class="yb-flex">
<view class="yb-loading yb-flex yb-align-center">
<view v-if="visible" class="circle yb-flex" ref="loading" :style="{
<view v-if="visible" class="circle y-flex" ref="loading" :style="{
width: pixelSize + 'px',
height: pixelSize + 'px',
'border-radius': pixelSize + 'px'
}">
<view
class="line yb-flex"
class="line y-flex"
:style="{
'border-top-width': (pixelSize / 4) + 'px',
'border-bottom-width': (pixelSize / 4) + 'px',
......@@ -19,21 +17,17 @@
:class="'line_' + index"
v-for="(item, index) in rgbs" :key="index"></view>
</view>
<text class="loading-text" :style="{color: color}" v-if="text && visible">{{text}}</text>
</view>
</view>
</template>
<script>
import Util from '@/uni_modules/yingbing-ReadPage/js_sdk/util.js'
// #ifdef APP-NVUE
const Binding = uni.requireNativePlugin('bindingx')
const Binding = uni.requireNativePlugin('bindingx');
// #endif
export default {
props: {
visible: {
type: Boolean,
default: false
default: true
},
size: {
type: [Number, String],
......@@ -42,15 +36,11 @@
color: {
type: String,
default: '#333333'
},
text: {
type: String,
default: ''
}
},
computed: {
rgbs () {
let rgb = Util.hex2rgb(this.color).replace('rgb(', '').replace(')', '')
const rgb = this.hexToRgb(this.color).replace('rgb(', '').replace(')', '')
return [{
top: `rgba(${rgb}, 1)`,
bottom: `rgba(${rgb}, .4)`
......@@ -72,7 +62,7 @@
}]
},
pixelSize () {
return Util.unitpixel(this.size)
return this.unitpixel(this.size)
}
},
data () {
......@@ -82,10 +72,8 @@
},
mounted() {
// #ifdef APP-NVUE
this.$nextTick(() => {
if ( this.visible ) {
this.start()
}
this.$nextTick( function () {
if ( this.visible ) this.start()
})
// #endif
},
......@@ -102,7 +90,7 @@
},
methods: {
start () {
let loading = Util.getEl(this.$refs.loading);
const loading = this.getEl(this.$refs.loading);
this.loading_binding = Binding.bind({
eventType: 'timing',
props: [{
......@@ -111,6 +99,27 @@
expression: 'floor(t/100)*30'
}]
});
},
unitpixel (size) {
const text = size.toString()
return text.indexOf('rpx') > -1 ? uni.upx2px(text.replace('rpx', '')) : text.indexOf('px') > -1 ? parseFloat(text.replace('px', '')) : uni.upx2px(text)
},
hexToRgb (hex) {
hex = hex.length == 7 ? hex : '#' + hex.slice(1, 4) + hex.slice(1, 4)
let str="rgb("
const r = parseInt(hex.slice(1,3),16).toString(); //ff slice不包括end
const g = parseInt(hex.slice(3,5),16).toString(); //00
const b = parseInt(hex.slice(5,7),16).toString(); //ff
str += r+","+g+","+b+")";
return str
},
getEl (el) {
if (typeof el === 'string' || typeof el === 'number') return el;
if (WXEnvironment) {
return el.ref;
} else {
return el instanceof HTMLElement ? el : el.$el;
}
}
},
watch: {
......@@ -136,7 +145,6 @@
</script>
<style scoped>
@import url(@/uni_modules/yingbing-ReadPage/css/common.css);
/* #ifndef APP-NVUE */
@keyframes loading{
0% {
......@@ -177,10 +185,6 @@
}
}
/* #endif */
.yb-loading .loading-text {
margin-top: 15rpx;
font-size: 28rpx;
}
.circle {
position: relative;
/* #ifndef APP-NVUE */
......
export default {
provide () {
return {
getPrevChapterDefaultText: () => this.prevChapterDefaultText,
getNextChapterDefaultText: () => this.nextChapterDefaultText,
getChapterReadyText: () => this.chapterReadyText,
getChapterLoadingText: () => this.chapterLoadingText,
getChapterSuccessText: () => this.chapterSuccessText,
getChapterFailText: () => this.chapterFailText,
getPrevChapterEndText: () => this.prevChapterEndText,
getNextChapterEndText: () => this.nextChapterEndText
}
},
props: {
loadingText: {
type: String,
default: '内容排版中'
},
errorText: {
type: String,
default: '渲染失败\n\n点击重试'
},
chapterReadyText: {
type: String,
default: '松开加载'
},
chapterLoadingText: {
type: String,
default: '正在加载'
},
chapterSuccessText: {
type: String,
default: '加载成功'
},
chapterFailText: {
type: String,
default: '加载失败'
},
prevChapterDefaultText: {
type: String,
default: '加载上一章'
},
prevChapterEndText: {
type: String,
default: '前面没有了'
},
nextChapterDefaultText: {
type: String,
default: '加载下一章'
},
nextChapterEndText: {
type: String,
default: '后面没有了'
}
}
}
\ No newline at end of file
const threshold = 50
export default {
props: {
//点击事件位置设置
clickOption: {
type: Object,
default () {
return {
width: uni.upx2px(200),
height: uni.upx2px(200),
left: 'auto',
top: 'auto'
}
}
}
},
computed: {
//点击区域左边位置
clickLeft () {
return typeof this.clickOption.left == 'number' ? this.clickOption.left : (this.windowWidth / 2) - (this.clickOption.width / 2)
},
//点击区域右边位置
clickRight () {
return this.clickLeft + this.clickOption.width
},
//点击区域顶部位置
clickTop () {
return typeof this.clickOption.top == 'number' ? this.clickOption.top : (this.windowHeight / 2) - (this.clickOption.height / 2)
},
//点击区域底部位置
clickBottom () {
return this.clickTop + this.clickOption.height
}
},
data () {
return {
autoplaySync: false,
windowHeight: 0,//组件高度
windowWidth: 0,//组件宽度
clickTime: 0,//点击次数
touchstartX: 0,//触摸开始x轴位置
touchstartY: 0,//触摸开始y轴位置
touchmoveX: 0,//触摸滑动x轴距离
touchmoveY: 0,//触摸滑动y轴距离
touchTime: 0//触摸时间
}
},
methods: {
touchstart (e) {
if ( this.pageType != 'scroll' ) this.autoplaySync = false//如果是翻页阅读关闭自动播放
if ( this.touchTimer ) {//清楚定时任务
clearTimeout(this.touchTimer)
this.touchTimer = null
}
if ( this.longTimer ) {//清楚定时任务
clearTimeout(this.longTimer)
this.longTimer = null
}
const touch = e.touches[0]
this.touchstartX = touch.pageX
this.touchstartY = touch.pageY
//点击在规定区域中
if ( this.touchstartX >= this.clickLeft && this.touchstartX <= this.clickRight && this.touchstartY >= this.clickTop && this.touchstartY <= this.clickBottom) {
this.clickTime++
if ( this.clickTime == 1 ) {//第一次点击
this.touchTimer = setTimeout(() => {
this.touchTime = 300
}, 300)
this.longTimer = setTimeout(() => {
this.touchTime = 500
if ( this.touchmoveX <= threshold && this.touchmoveY <= threshold ) this.$emit('long-click')//抛出长按事件
this.resetTouch()
}, 500)
}
} else {//没有点击在规定区域中直接重置触摸事件
this.resetTouch()
}
},
touchmove (e) {
if ( this.clickTime > 0 ) {
const touch = e.touches[0]
this.touchmoveX = Math.abs(touch.pageX - this.touchstartX)
this.touchmoveY = Math.abs(touch.pageY - this.touchstartY)
}
},
touchend (e) {
if ( this.pageType != 'scroll' ) this.autoplaySync = this.autoplay//如果是横向翻页开启自动播放
if ( this.touchTimer ) {//清楚按下定时任务
clearTimeout(this.touchTimer);
this.touchTimer = null
}
if ( this.longTimer ) {//清楚长按定时任务
clearTimeout(this.longTimer)
this.longTimer = null
}
//点击次数为零或者触摸时间超过300,或者滑动距离超过阙值
if ( this.clickTime == 0 || this.touchTime > 300 || this.touchmoveX > threshold || this.touchmoveY > threshold ) {
this.resetTouch()
return
}
if ( this.clickTime == 1 ) {//第一次点击
this.touchTimer = setTimeout(() => {
this.$emit('single-click')//抛出单击事件
this.resetTouch()
}, 300)
}
if ( this.clickTime == 2 ) {//第二次点击
this.$emit('double-click')//抛出双击事件
this.resetTouch()
}
},
resetTouch () {
this.touchstartX = 0
this.touchstartY = 0
this.touchmoveX = 0
this.touchmoveY = 0
this.touchTime = 0
this.clickTime = 0
}
}
}
\ No newline at end of file
.computed {
position: fixed;
top: -1000rpx;
left: 0;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
flex-wrap: wrap;
}
.computed-text {
font-size: 20px;
flex-shrink: 0;
}
\ No newline at end of file
.yingbing-read-page-flip {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.flip-item-wrapper {
/* #ifndef APP-NVUE */
display: flex;
flex-direction: column;
box-sizing: border-box;
/* #endif */
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
.flip-item-header {
/* #ifndef APP-NVUE */
display: flex;
flex-direction: column;
box-sizing: border-box;
/* #endif */
justify-content: center;
height: 50rpx;
overflow: hidden;
}
.flip-item-header-text {
font-size: 24rpx;
opacity: .4;
font-weight: bold;
/* #ifdef APP-NVUE */
lines: 1;
text-overflow: ellipsis;
/* #endif */
/* #ifndef APP-NVUE */
display: -webkit-box !important;
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
-webkit-box-orient:vertical;
-webkit-line-clamp: 1;
/* #endif */
}
.flip-item-footer {
/* #ifndef APP-NVUE */
display: flex;
box-sizing: border-box;
/* #endif */
align-items: center;
flex-direction: row;
justify-content: space-between;
height: 50rpx;
overflow: hidden;
}
.flip-item-footer-text {
font-size: 24rpx;
opacity: .4;
font-weight: bold;
}
.flip-item-content {
/* #ifndef APP-NVUE */
box-sizing: border-box;
overflow: hidden;
display: flex;
flex-direction: column;
/* #endif */
flex: 1;
}
.flip-item-text {
/* #ifndef APP-NVUE */
display: flex;
flex-direction: column;
/* #endif */
},
.flip-text {
/* #ifndef APP-NVUE */
box-sizing: border-box;
white-space: pre-wrap;
/* #endif */
}
.flip-loading {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
align-items: center;
justify-content: center;
}
.flip-loading-text {
font-size: 20px;
}
/* #ifndef APP-NVUE */
view, image, input, scroll-view, swiper, swiper-item, text, textarea, video {
position: static;
}
/* #endif */
\ No newline at end of file
import Util from '../../../js_sdk/util.js'
export default {
computed: {
current () {
return this.pages.findIndex(item => item.dataId == this.currentDataId)
},
prevDataId () {
return this.pages[this.current - 1] && this.pages[this.current - 1].dataId
},
nextDataId () {
return this.pages[this.current + 1] && this.pages[this.current + 1].dataId
}
},
data() {
return {
currentDataId: -1,
isShow: false,
viewWidth: 0,
moreLoading: false
}
},
methods: {
//翻往上一页
pagePrevFlip () {
this.$refs.flip.flipToPrev()
},
//翻往下一页
pageNextFlip () {
this.$refs.flip.flipToNext()
},
reloadLoadmoreFlip (p) {
let loadIndex = this.pages.findIndex(page => p.dataId == page.dataId)
this.$set(this.pages[loadIndex], 'type', 'loading')
let nextChapter = p.direction == 'next' ? p.chapter + 1 : p.chapter - 1
this.loadmoreFlip(nextChapter, p.direction == 'next' ? 1 : -1);
},
loadmoreFlip (chapter, value) {
this.$emit('loadmore', chapter, (status, content) => {
this.moreLoading = false;
if (status == 'success') {
const index = this.contents.findIndex(item => item.chapter == content.chapter)
if (index > -1) {
this.contents[index] = content;
} else {
this.contents.push(content);
}
this.computedPage({
content: content,
type: value > 0 ? 'next' : 'prev'
});
this.preload(chapter)
} else {
let loadIndex = this.pages.findIndex(page => page.type == 'loading' && page.direction == (value > 0 ? 'next' : 'prev'))
this.$set(this.pages[loadIndex], 'type', status)
}
})
},
handleFlipChangeRender (e) {
this.handleFlipChange(e.detail.dataId)
},
handleFlipChange (dataId) {
const value = dataId < this.currentDataId ? -1 : 1
this.currentDataId = dataId
const index = this.pages.findIndex(page => page.dataId == dataId);
let pageInfo = this.pages[index]
const nowChapters = this.pages.filter(item => item.chapter == pageInfo.chapter && (item.type == 'text' || item.type == 'custom' || item.type == 'slot'))
let contentIndex = this.contents.findIndex(content => content.chapter == pageInfo.chapter)
pageInfo.totalPage = nowChapters.length
pageInfo.currentPage = nowChapters.findIndex(item => item.dataId == pageInfo.dataId) + 1
if ( this.contents[contentIndex].title ) pageInfo.title = this.contents[contentIndex].title
this.pageInfo = pageInfo
this._emitPageInfo(pageInfo, this.pages)
const nextType = this.pages[index + value] && this.pages[index + value].type
if ( this.pages[index].type == 'loading' || nextType == 'loading') {
if (this.moreLoading) return
this.moreLoading = true;
const loadChapter = this.pages[index].chapter + value;
contentIndex = this.contents.findIndex(content => content.chapter == loadChapter)
if (contentIndex > -1) {
this.computedPage({
content: this.contents[contentIndex],
type: value > 0 ? 'next' : 'prev'
});
this.preload(loadChapter)
this.moreLoading = false;
} else {
this.loadmoreFlip(loadChapter, value)
}
} else {
this.startAutoplay()
}
}
}
}
\ No newline at end of file
<template>
<view class="yb-list yb-flex">
<!-- #ifdef APP-VUE || H5 || MP-WEIXIN || MP-QQ -->
<view class="yb-refresh yb-flex yb-flex-1"
:prop="pulldownProp" :change:prop="pulldownwxs.propWatcher"
@touchstart="pulldownwxs.touchstart"
@touchmove="pulldownwxs.touchmove"
@touchend="pulldownwxs.touchend">
<!-- #endif -->
<!-- #ifdef APP-NVUE -->
<view class="yb-refresh yb-flex yb-flex-1" ref="ybRefresh">
<!-- #endif -->
<!-- #ifdef APP-VUE || H5 || MP-WEIXIN || MP-QQ -->
<view ref="ybListPulldown" class="yb-list-pulldown yb-flex">
<list-pulldown :status="pulldownStatus" :options="pulldownOptionsSync">
<template v-slot:pulldown-symbol>
<slot name="pulldown-symbol"></slot>
</template>
<template v-slot:pulldown-loading>
<slot name="pulldown-loading"></slot>
</template>
<template v-slot:pulldown-success>
<slot name="pulldown-success"></slot>
</template>
<template v-slot:pulldown-fail>
<slot name="pulldown-fail"></slot>
</template>
<template v-slot:pulldown-end>
<slot name="pulldown-end"></slot>
</template>
</list-pulldown>
</view>
<!-- #endif -->
<scroll-view
scroll-y
:render-whole="true"
@scroll="onScroll"
:scroll-top="scrollTop"
:scroll-with-animation="scrollWithAnimation"
ref="ybPulldownScroller"
class="yb-pulldown-scroller yb-pulldown-scroll-view"
@scrolltoupper="onScrolltoupper"
@scrolltolower="onScrolltolower">
<!-- #ifdef APP-NVUE -->
<template v-if="pulldownOptionsSync.show">
<refresh :display="display" @pullingdown="pullingdown($event.pullingDistance)" @refresh="refresh">
<view class="yb-flex" style="height: 30rpx;"></view>
<list-pulldown :status="pulldownStatus" :options="pulldownOptionsSync">
<template v-slot:pulldown-symbol>
<slot name="pulldown-symbol"></slot>
</template>
<template v-slot:pulldown-loading>
<slot name="pulldown-loading"></slot>
</template>
<template v-slot:pulldown-success>
<slot name="pulldown-success"></slot>
</template>
<template v-slot:pulldown-fail>
<slot name="pulldown-fail"></slot>
</template>
<template v-slot:pulldown-end>
<slot name="pulldown-end"></slot>
</template>
</list-pulldown>
<view class="yb-flex" style="height: 20rpx;"></view>
</refresh>
</template>
<!-- #endif -->
<slot></slot>
<template v-if="loadmoreOptionsSync.show">
<list-loadmore :status="loadmoreStatus" :options="loadmoreOptionsSync" @reload="reload">
<template v-slot:loadmore-symbol>
<slot name="loadmore-symbol"></slot>
</template>
<template v-slot:loadmore-loading>
<slot name="loadmore-loading"></slot>
</template>
<template v-slot:loadmore-fail>
<slot name="loadmore-fail"></slot>
</template>
<template v-slot:loadmore-end>
<slot name="loadmore-end"></slot>
</template>
</list-loadmore>
</template>
</scroll-view>
</view>
</view>
</template>
<script>
import Util from '@/uni_modules/yingbing-ReadPage/js_sdk/util.js'
import ListPulldown from './modules/pulldown/pulldown.vue'
import ListLoadmore from './modules/loadmore/loadmore.vue'
import PulldownMixin from './modules/pulldown/pulldown.js'
import LoadmoreMixin from './modules/loadmore/loadmore.js'
export default {
mixins: [PulldownMixin, LoadmoreMixin],
options: {
addGlobalClass: true,
virtualHost: true, // 将自定义节点设置成虚拟的,更加接近Vue组件的表现。我们不希望自定义组件的这个节点本身可以设置样式、响应 flex 布局等,而是希望自定义组件内部的第一层节点能够响应 flex 布局或者样式由自定义组件本身完全决定
},
components: {
ListPulldown,
ListLoadmore
},
props: {
//下拉加载配置
pulldown: {
type: [Object,Boolean],
default () {
return new Object
}
},
//触底加载更多配置
loadmore: {
type: [Object,Boolean],
default () {
return new Object
}
}
},
data () {
return {
scrollTop: 0,
scrollWithAnimation: false
}
},
computed: {
Util () {
return Util
}
},
methods: {
onScrolltoupper () {
this.$emit('scrolltoupper')
},
scrollTo (offset, animated = false) {
this.scrollWithAnimation = animated
this.$nextTick(function() {
this.scrollTop = offset - 1
this.$nextTick(function () {
this.scrollTop = offset
})
})
},
onScroll (e) {
this.$emit('scroll', {
scrollTop: e.detail.scrollTop,
scrollHeight: e.detail.scrollHeight,
scrollWidth: e.detail.scrollWidth
})
}
}
}
</script>
<!-- #ifdef APP-VUE || H5 || MP-WEIXIN || MP-QQ -->
<script module="pulldownwxs" lang="wxs" src="./modules/pulldown/pulldown.wxs"></script>
<!-- #endif -->
<style scoped>
@import url(@/uni_modules/yingbing-ReadPage/css/common.css);
.yb-list {
position: relative;
flex: 1;
}
.yb-list .yb-refresh {
position: relative;
/* #ifndef APP-NVUE */
overflow: visible;
/* #endif */
}
.yb-list .yb-list-pulldown {
height: 400rpx;
margin-top: -400rpx;
justify-content: flex-end;
padding: 40rpx 0;
}
.yb-pulldown-scroller {
/* #ifndef APP-NVUE */
overflow: visible;
/* #endif */
}
.yb-list .yb-pulldown-scroll-view {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
}
</style>
import Util from '@/uni_modules/yingbing-ReadPage/js_sdk/util.js'
export default {
data () {
return {
isLoadmore: false,//是否触底
loadmoreStatus: '',//触底状态
isPageFirst: false//判断页面滚动时是否首次加载
}
},
computed: {
loadmoreOptionsSync () {
return Object.assign({}, {
//是否展示加载更多
show: false,
//是否采用row布局
row: false,
//默认文本
defaultText: '上拉或点击加载',
//刷新中的提示文本
refreshingText: '正在加载',
//刷新成功的文本
successText: '加载成功,点击继续',
//刷新失败的提示文本
failText: '加载失败,点击重试',
//数据全部加载完毕的提示文本
endText: '数据加载完毕',
//文本颜色
color: '#333333',
//距底部的距离
bottom: 0
}, Util.typeof(this.loadmore) == 'Object' ? this.loadmore : Util.typeof(this.loadmore) == 'Boolean' ? { show: this.loadmore } : {})
}
},
mounted () {
//页面滚动通过判断上拉加载组件是否出现在屏幕内来触发触发触底事件,组件初始化时不触发触底事件,延迟一秒后才能触发
this.$nextTick(function () {
setTimeout(() => {
this.isPageFirst = true
}, 1000)
})
},
methods: {
onScrolltolower () {
if ( !this.isPageFirst && this.typeSync == 'page' ) {
return
}
if ( this.isLoadmore ) {
return
}
this.isLoadmore = true
this.loadmoreStatus = 'loading'
this.$emit('loadmore', (state) => {
this.loadmoreStatus = state
if ( state != 'fail' && state != 'end') {
this.isLoadmore = false
// #ifdef APP-NVUE
this.$refs.ybPulldownScroller.resetLoadmore()
// #endif
}
})
},
//重置加载更多状态
resetLoadmore () {
this.loadmoreStatus = ''
this.isLoadmore = false
// #ifdef APP-NVUE
this.$refs.ybPulldownScroller.resetLoadmore()
// #endif
},
//设置加载更多为完毕状态
setLoadmoreEnd () {
this.loadmoreStatus = 'end'
this.isLoadmore = true
},
//设置加载更多为成功状态
setLoadmoreSuccess () {
this.loadmoreStatus = 'success'
this.isLoadmore = false
},
//设置加载更多为失败状态
setLoadmoreFail () {
this.loadmoreStatus = 'fail'
this.isLoadmore = false
},
//重加载更多
reload () {
this.isLoadmore = false
this.onScrolltolower()
}
}
}
\ No newline at end of file
<template>
<view class="yb-loadmore yb-flex yb-align-center yb-justify-center"
:class="{
'yb-row': options.row
}"
@tap="onTap" v-if="options.show">
<view class="yb-flex" style="height: 20rpx;"></view>
<view class="yb-flex yb-align-center yb-justify-center" :style="{'margin-left': options.row ? '-70rpx' : 0}">
<view class="yb-flex indicator-icon yb-align-center yb-justify-center">
<template v-if="status == ''">
<slot name="loadmore-symbol">
<slot name="loadmore-symbol">
<list-icon
name="arrow-up"
:size="50"
:color="options.color"></list-icon>
</slot>
</slot>
</template>
<template v-if="status == 'loading'">
<slot name="loadmore-loading">
<list-loading :visible="status == 'loading'" :color="options.color"></list-loading>
</slot>
</template>
<template v-if="status == 'fail'">
<slot name="loadmore-fail">
<list-icon
name="fork-circle"
:size="50"
:color="options.color"></list-icon>
</slot>
</template>
<template v-if="status == 'success'">
<slot name="loadmore-success">
<list-icon
name="hook-circle"
:size="50"
:color="options.color"></list-icon>
</slot>
</template>
<template v-if="status == 'end'">
<slot name="loadmore-end">
<list-icon
name="hook-circle"
:size="50"
:color="options.color"></list-icon>
</slot>
</template>
</view>
</view>
<view class="yb-flex indicator-text yb-align-center">
<text class="refresh-text" :style="{color: options.color}">{{refreshText}}</text>
</view>
<view class="yb-flex" style="height: 20rpx;"></view>
<view class="yb-flex" :style="{
height: Util.pixelunit(options.bottom)
}"></view>
</view>
</template>
<script>
import Util from '@/uni_modules/yingbing-ReadPage/js_sdk/util.js'
import ListIcon from '../common/icon.vue'
import ListLoading from '../common/loading.vue'
export default {
components: {
ListIcon,
ListLoading
},
props: {
options: {
type: Object,
default () {
return new Object
}
},
status: {
type: String,
default: ''
}
},
computed: {
refreshText () {
return this.status == 'loading' ? this.options.refreshingText : this.status == 'success' ? this.options.successText : this.status == 'fail' ? this.options.failText : this.status == 'end' ? this.options.endText : this.options.defaultText
},
Util () {
return Util
}
},
methods: {
onTap () {
if ( this.status != 'end' && this.status != 'loading' ) {
this.$emit('reload')
}
}
}
}
</script>
<style scoped>
@import url(@/uni_modules/yingbing-ReadPage/css/common.css);
.yb-loadmore .indicator-icon {
width: 70rpx;
height: 70rpx;
}
.yb-loadmore .refresh-text {
text-align: center;
font-size: 24rpx;
}
.yb-loadmore .indicator-symbol {
transition: transform .1s;
}
.yb-loadmore .refresh-time {
font-size: 23rpx;
}
</style>
\ No newline at end of file
import Util from '@/uni_modules/yingbing-ReadPage/js_sdk/util.js'
export default {
data () {
return {
pulldownStatus: '',//下拉状态
pulldownRestore: false,//下拉复位
display: 'show'
}
},
computed: {
pulldownOptionsSync () {
return Object.assign({}, {
show: false,
//是否采用row布局
row: false,
//是否显示刷新时间
enableRefreshTime: true,
//默认文本
defaultText: '下拉刷新',
//准备刷新
readyText: '释放刷新',
//刷新中的提示文本
refreshingText: '正在刷新',
//刷新成功的提示文本
successText: '刷新成功',
//刷新失败的提示文本
failText: '刷新失败',
//刷新结束的提示文本
endText: '刷新完毕',
//文本颜色
color: '#333333',
//刷新完成后的隐藏周期
duration: 300
}, Util.typeof(this.pulldown) == 'Object' ? this.pulldown : Util.typeof(this.pulldown) == 'Boolean' ? { show: this.pulldown } : {})
},
pulldownProp () {
return {
pulldownRestore: this.pulldownRestore,
enablePulldown: this.pulldownOptionsSync.show
}
}
},
methods: {
refresh () {
if ( !this.pulldownOptionsSync.show ) {
return
}
if ( this.pulldownStatus != 'end' ) {
this.pulldownStatus = 'loading'
this.$emit('pulldown', (state) => {
this.pulldownStatus = state
this.pulldownTimer = setTimeout(() => {
this.pulldownRestore = true
clearTimeout(this.pulldownTimer)
this.pulldownTimer = null
// #ifdef APP-NVUE
this.display = 'hide';
this.pulldownTimer = setTimeout(() => {
this.display = 'show'
clearTimeout(this.pulldownTimer)
this.pulldownTimer = null
}, this.pulldownOptionsSync.duration)
// #endif
}, this.pulldownOptionsSync.duration)
})
} else {
this.pulldownRestore = true
// #ifdef APP-NVUE
this.display = 'hide';
this.pulldownTimer = setTimeout(() => {
this.display = 'show'
clearTimeout(this.pulldownTimer)
this.pulldownTimer = null
}, this.pulldownOptionsSync.duration)
// #endif
}
},
pullingdown (threshold) {
if ( !this.pulldownOptionsSync.show ) {
return
}
if ( this.pulldownStatus != 'end' ) {
// #ifndef APP-NVUE
if ( threshold >= 120 ) {
this.pulldownStatus = 'ready'
} else {
this.pulldownStatus = ''
}
// #endif
// #ifdef APP-NVUE
if ( threshold >= 195 ) {
this.pulldownStatus = 'ready'
} else {
this.pulldownStatus = ''
}
// #endif
}
this.$emit('pullingdown', threshold)
},
pullingup (threshold) {
this.$emit('pullingup', threshold)
},
resetPulldownIns () {
this.pulldownTimer = setTimeout(() => {
if ( this.pulldownStatus != 'end' ) {
this.pulldownStatus = ''
}
this.pulldownRestore = false
clearTimeout(this.pulldownTimer)
this.pulldownTimer = null
}, this.pulldownOptionsSync.duration)
},
resetPulldown () {
this.pulldownStatus = ''
}
}
}
\ No newline at end of file
<template>
<view class="yb-pulldown yb-flex yb-align-center yb-justify-center"
:class="{
'yb-row': options.row
}" v-if="options.show">
<view class="yb-flex yb-align-center yb-justify-center" :style="{'margin-left': options.row ? '-70rpx' : 0}">
<view class="yb-flex indicator-icon yb-align-center yb-justify-center">
<template v-if="status == 'ready' || status == ''">
<view class="yb-flex indicator-symbol"
:style="{
'transform': 'rotateZ(' + (status == 'ready' ? '180deg' : 0) + ')'
}">
<slot name="pulldown-symbol">
<list-icon
name="arrow-down"
:size="50"
:color="options.color"></list-icon>
</slot>
</view>
</template>
<template v-if="status == 'loading'">
<slot name="pulldown-loading">
<list-loading :visible="status == 'loading'" :color="options.color"></list-loading>
</slot>
</template>
<template v-if="status == 'success'">
<slot name="pulldown-success">
<list-icon
name="hook-circle"
:size="50"
:color="options.color"></list-icon>
</slot>
</template>
<template v-if="status == 'fail'">
<slot name="pulldown-fail">
<list-icon
name="fork-circle"
:size="50"
:color="options.color"></list-icon>
</slot>
</template>
<template v-if="status == 'end'">
<slot name="pulldown-end">
<list-icon
name="hook-circle"
:size="50"
:color="options.color"></list-icon>
</slot>
</template>
</view>
</view>
<view class="yb-flex indicator-text yb-align-center">
<text class="refresh-text" :style="{color: options.color}">{{pulldownText}}</text>
<text class="refresh-time" :style="{color: options.color}" v-if="options.enableRefreshTime">上次更新 {{lastTime}}</text>
</view>
</view>
</template>
<script>
import Util from '@/uni_modules/yingbing-ReadPage/js_sdk/util.js'
import ListIcon from '../common/icon.vue'
import ListLoading from '../common/loading.vue'
export default {
components: {
ListIcon,
ListLoading
},
props: {
options: {
type: Object,
default () {
return new Object
}
},
status: {
type: String,
default: ''
}
},
data () {
return {
lastTime: '刷新时间'
}
},
computed: {
pulldownText () {
return this.status == 'ready' ? this.options.readyText : this.status == 'loading' ? this.options.refreshingText : this.status == 'success' ? this.options.successText : this.status == 'fail' ? this.options.failText : this.status == 'end' ? this.options.endText : this.options.defaultText
}
},
mounted() {
this.lastTime = this.getTime()
},
methods: {
getTime () {
let d = new Date()
return (d.getMonth() + 1) + '-' + d.getDate() + ' ' + Util.zeroize(d.getHours()) + ':' + Util.zeroize(d.getMinutes())
}
},
watch: {
status (newVal) {
if ( newVal == 'success' || newVal == 'fail' ) {
this.lastTime = this.getTime()
}
}
}
}
</script>
<style scoped>
@import url(@/uni_modules/yingbing-ReadPage/css/common.css);
.yb-pulldown .indicator-icon {
width: 70rpx;
height: 70rpx;
}
.yb-pulldown .refresh-text {
text-align: center;
font-size: 24rpx;
}
.yb-pulldown .indicator-symbol {
transition: transform .1s;
}
.yb-pulldown .refresh-time {
font-size: 23rpx;
}
</style>
\ No newline at end of file
export default {
methods: {
computedNochapter (data) {
const reg = new RegExp(/(第+[一二两三四五六七八九十○零百千万亿0-91234567890※✩★☆]{1,6}[章回卷节折篇幕集部]?[、.-\s][^\n]*)[_,-]?/g)
let match = ''
let catalog = []
let chapter = 0
let content = data.content
while ((match = reg.exec(content)) != null) {
chapter++
if ( chapter == 1 && match.index > 0 ) {
catalog.push({
title: content.slice(0, 10).replace(/[\r\n\t]/g, ''),
start: 0,
end: match.index,
content: content.slice(0, match.index),
isStart: true,
isEnd: false,
chapter: chapter
})
chapter++
}
catalog.push({
title: match[0].replace(/[\r\n\t]/g, '').slice(0, 10),
start: match.index,
isStart: false,
isEnd: false,
chapter: chapter
})
if ( chapter > 1 && !catalog[chapter-2].content ) {
catalog[chapter-2].content = content.slice(catalog[chapter-2].start, match.index)
catalog[chapter-2].end = match.index
}
}
if ( catalog.length == 1 ) {
catalog[0].content = content
catalog[0].end = content.length
}
if ( catalog.length > 0 ) {
catalog[0].isStart = true
catalog[catalog.length-1].isEnd = true
catalog[catalog.length-1].content = content.slice(catalog[catalog.length-1].start)
catalog[catalog.length-1].end = content.length
}
if ( data.content.length / catalog.length <= 10000 ) {
this.contents = catalog
if (catalog[catalog.length-1].content.length > 50000) {
let lastContent = catalog[catalog.length-1].content
this.contents.pop()
this.cutChapter({
content: lastContent,
currentChapter: data.currentChapter,
start: data.start
}, 0, 3000)
} else {
this.initLoading = true;
this.resetPage({
start: parseInt(data.start || 0),
currentChapter: parseInt(data.currentChapter),
title: data.title || null
});
this.$emit('setCatalog', this.contents)
}
} else {
this.contents = []
this.cutChapter(data, 0, 3000)
}
},
//分割章节
cutChapter (data, start, length) {
let end = start + length
let str = data.content.slice(start, end)
let index1 = str.lastIndexOf('\r')
let index2 = str.lastIndexOf('\n')
let index = Math.max(index1, index2)
index > -1 ? str = str.slice(0, index+1) : null
end = start + str.length
let chapter = this.contents.length + 1
this.contents.push({
title: '第' + chapter + '节 ' + str.replace(/[\r\n\t\s]/g, '').slice(0, 10),
chapter: chapter,
isStart: false,
isEnd: false,
start: start,
end: end,
content: str
})
if ( end < data.content.length ) {
this.cutChapter(data, end, length)
} else {
this.contents[0].isStart = true
this.contents[this.contents.length-1].isEnd = true
this.initLoading = true;
this.resetPage({
start: parseInt(data.start || 0),
currentChapter: parseInt(data.currentChapter || 1),
title: data.title || null
});
this.$emit('setCatalog', this.contents)
}
}
}
}
\ No newline at end of file
<template>
<!-- #ifndef APP-NVUE -->
<view class="read-rich-text" :style="richTextStyle" v-html="richtext">
</view>
<!-- #endif -->
<!-- #ifdef APP-NVUE -->
<view class="read-rich-text" :style="richTextStyle">
<web-view
ref="webview"
@onPostMessage="onPostMessage"
:style="webviewStyle"
:src="'/uni_modules/yingbing-ReadPage/hybrid/html/richtext.html?rich=' + encodeURIComponent(JSON.stringify(richtext)) + '&pageType=' + pageType"></web-view>
</view>
<!-- #endif -->
</template>
<script>
export default {
props: {
richtext: {
type: String,
default: ''
},
pageType: {
type: String,
default: ''
},
fontFace: {
type: Array,
default () {
return new Array
}
}
},
computed: {
webviewStyle () {
return this.pageType == 'scroll' ? {
height: this.customWebviewHeight + 'px'
} : {
flex: 1
}
},
richTextStyle () {
return this.pageType == 'scroll' ? {
'padding-bottom': '20rpx'
} : {
flex: 1
}
}
},
data () {
return {
customWebviewHeight: 0
}
},
mounted() {
// #ifdef APP-NVUE
if ( this.fontFace.length > 0 ) {
this.$nextTick(function () {
setTimeout(() => {
this.setFontFace()
}, 100)
})
}
// #endif
},
methods: {
onPostMessage (e) {
e.detail.data.forEach(item => {
if ( item.customClick ) {
this.$emit('customClick', item.customClick)
}
if ( item.height ) {
this.customWebviewHeight = item.height
}
})
},
setFontFace () {
this.$refs.webview && this.$refs.webview.evalJS("setFontFace(" + encodeURIComponent(JSON.stringify(this.fontFace)) + ")")
}
},
watch: {
FontFace (newVal) {
if ( newVal.length > 0 ) {
this.$nextTick(function () {
setTimeout(() => {
this.setFontFace()
}, 100)
})
}
}
}
}
</script>
<style scoped>
.read-rich-text {
/* #ifndef APP-NVUE */
box-sizing: border-box;
overflow: hidden;
display: flex;
flex-direction: column;
/* #endif */
}
</style>
\ No newline at end of file
.yingbing-scroll {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
/* #ifndef APP-NVUE */
box-sizing: border-box;
overflow: hidden;
display: flex;
flex-direction: column;
/* #endif */
}
.scroll-wrapper {
/* #ifndef APP-NVUE */
box-sizing: border-box;
overflow: hidden;
display: flex;
flex-direction: column;
/* #endif */
flex: 1;
}
.scroll-item {
/* #ifndef APP-NVUE */
box-sizing: border-box;
overflow: hidden;
display: flex;
flex-direction: column;
/* #endif */
}
.scroll-item-content {
/* #ifndef APP-NVUE */
box-sizing: border-box;
overflow: hidden;
display: flex;
flex-direction: column;
/* #endif */
padding-bottom: 20rpx;
}
.scroll-text {
/* #ifndef APP-NVUE */
box-sizing: border-box;
white-space: pre-wrap;
/* #endif */
}
.scroll-item-header {
/* #ifndef APP-NVUE */
display: flex;
box-sizing: border-box;
overflow: hidden;
/* #endif */
padding: 0 40rpx;
position: fixed;
left: 20rpx;
align-items: center;
flex-direction: row;
justify-content: space-between;
height: 50rpx;
background-color: rgba(0,0,0,.4);
border-radius: 50rpx;
}
.scroll-item-header-text {
font-size: 24rpx;
color: #fff;
/* #ifdef APP-NVUE */
lines: 1;
text-overflow: ellipsis;
/* #endif */
/* #ifndef APP-NVUE */
display: -webkit-box !important;
overflow: hidden;
text-overflow: ellipsis;
word-break: break-all;
-webkit-box-orient:vertical;
-webkit-line-clamp: 1;
/* #endif */
}
.scroll-item-footer {
/* #ifndef APP-NVUE */
display: flex;
box-sizing: border-box;
overflow: hidden;
/* #endif */
padding: 0 40rpx;
position: fixed;
left: 20rpx;
right: 20rpx;
align-items: center;
flex-direction: row;
justify-content: space-between;
height: 50rpx;
background-color: rgba(0,0,0,.4);
border-radius: 50rpx;
}
.scroll-item-footer-text {
font-size: 24rpx;
color: #fff;
}
\ No newline at end of file
import Util from '../../../js_sdk/util.js'
export default {
data () {
return {
pageTo: 0,
scrollTop: 0,
scrolling: false,
scrollDate: ''
}
},
beforeDestroy() {
if ( this.scrollTimer ) {
clearTimeout(this.scrollTimer)
this.scrollTimer = null
}
},
mounted () {
this.scrollDate = this.filterDate()
},
methods: {
scrollNext () {
if ( this.scrolling ) {
return
}
this.scrolling = true
this.$refs.list.scrollTo(this.scrollTop + (this.windowHeight / 2), true)
this.scrollTimer = setTimeout(() => {
this.scrolling = false
clearTimeout(this.scrollTimer)
this.scrollTimer = null
}, 300)
},
scrollPrev () {
if ( this.scrolling ) {
return
}
this.scrolling = true
this.$refs.list.scrollTo(this.scrollTop - (this.windowHeight / 2), true)
this.scrollTimer = setTimeout(() => {
this.scrolling = false
clearTimeout(this.scrollTimer)
this.scrollTimer = null
}, 300)
},
pulldownScroll (callback) {
let contentsIndex = this.contents.findIndex(content => content.chapter == this.pages[0].chapter)
if ( this.contents[contentsIndex].isStart ) {
callback('end')
} else {
this.scroll_loadmore({
chapter: this.pages[0].chapter - 1,
type: 'prev'
}, callback)
this.$refs.list.resetLoadmore()
}
},
scrolltoupper () {
let contentsIndex = this.contents.findIndex(content => content.chapter == (this.pages[0].chapter - 1))
if ( contentsIndex > -1 ) {
this.scroll_loadmore({
chapter: this.pages[0].chapter - 1,
type: 'prev'
})
this.$refs.list.resetLoadmore()
}
},
loadmoreScroll (callback) {
this.stopAutoplay()
let contentsIndex = this.contents.findIndex(content => content.chapter == this.pages[this.pages.length - 1].chapter)
if ( this.contents[contentsIndex].isEnd ) {
callback('end')
} else {
this.scroll_loadmore({
chapter: this.pages[this.pages.length - 1].chapter + 1,
type: 'next'
}, callback)
this.$refs.list.resetPulldown()
}
},
//加载更多章节
scroll_loadmore (load, callback) {
const chapter = load.chapter;
const type = load.type;
const contentIndex = this.contents.findIndex(item => item.chapter == chapter);
if ( contentIndex > -1 ) {
this.computedPage({
content: this.contents[contentIndex],
type: type
});
this.preload(chapter)
callback && callback('success')
} else {
this.$emit('loadmore', chapter, (status, content) => {
if (status == 'success') {
const index = this.contents.findIndex(item => item.chapter == content.chapter)
if (index > -1) {
this.contents[index] = content;
} else {
this.contents.push(content);
}
this.computedPage({
content: content,
type: type
});
this.preload(chapter)
}
callback && callback(status)
})
}
},
async scrollEnd(e) {
const size = await this.getRect()
let rate = Math.floor(e.scrollTop / size.height)
let maybe = this.pages[rate] ? rate : this.pages.length-1
let top = -1
let pageInfo = null
while ( top < 0 ) {
let rect = await this.getScrollItemRect(this.pages[maybe].dataId)
top = rect.top
pageInfo = this.pages[maybe]
maybe++
}
if ( top >= 0 ) {
const nowChapters = this.pages.filter(item => item.chapter == pageInfo.chapter && (item.type == 'text' || item.type == 'custom' || item.type == 'slot'))
let contentIndex = this.contents.findIndex(content => content.chapter == pageInfo.chapter)
pageInfo.totalPage = nowChapters.length
pageInfo.currentPage = nowChapters.findIndex(item => item.dataId == pageInfo.dataId) + 1
this.pageInfo = pageInfo
//刷新当前时间和设备电量
this.scrollDate = this.filterDate()
this.$refs.scrollBattery.getBattery()
this._emitPageInfo(pageInfo, this.pages)
this.startAutoplay()
}
},
getScrollItemRect (dataId) {
return new Promise(resolve => {
Util.getRect('#scroll-item_' + dataId, Util.getRefs(this, 'scrollItem_' + dataId, 0), this).then(res => {
resolve(res)
})
})
},
onScroll (e) {
this.stopAutoplay()
if ( this.options.pageType == 'scroll' ) {
this.scrollTop = e.scrollTop
if ( this.scrollTimer ) {
clearTimeout(this.scrollTimer)
this.scrollTimer = null
}
this.scrollTimer = setTimeout(() => {
this.scrolling = false
this.scrollEnd(e)
}, 300)
}
}
}
}
\ No newline at end of file
var max = 200
var refreshHeight = 80
function touchstart(event, ins) {
console.log('touchstart');
var state = ins.getState()
if ( !state.enablePulldown ) {
return
}
if ( state.refresh ) return
var touch = event.touches[0] || event.changedTouches[0]
state.startX = touch.pageX
state.startY = touch.pageY
}
function touchmove(event, ins) {
console.log('touchmove');
var state = ins.getState()
if ( state.refresh ) return
if ( state.startY > 0) {
var touch = event.touches[0] || event.changedTouches[0]
if ((Math.abs(touch.pageY - state.startY) > Math.abs(touch.pageX - state.startX)) && Math.abs(touch.pageY -
state.startY) > 20) {
if ((Math.abs(touch.pageY - state.startY) > Math.abs(touch.pageX - state.startX)) && Math.abs(touch.pageY - state.startY) > 20) {
var pageY = touch.pageY
var rate = max / (max + Math.abs(pageY - state.startY))
state.threshold = rate * (pageY - state.startY)
if ( state.threshold > 0 && !state.pulldownable ) return
if ( state.threshold < 0 && !state.pullupable ) return
if ( state.threshold > max ) {
state.threshold = max
}
if ( state.threshold < -max ) {
state.threshold = -max
}
ins.selectComponent('.yb-pulldown-scroller').setStyle({
transform: 'translateY(' + state.threshold + 'px)',
transition: ''
})
ins.selectComponent('.yb-list-pulldown').setStyle({
ins.selectComponent('.yingbing-scroller-wrapper').setStyle({
transform: 'translateY(' + state.threshold + 'px)',
transition: ''
})
......@@ -40,56 +39,78 @@ function touchmove(event, ins) {
}
}
function touchend(event, ins) {
touchaction(event, ins)
}
function touchcancel(event, ins) {
touchaction(event, ins)
}
function touchaction (event, ins) {
var state = ins.getState()
if ( state.threshold > 120 && state.enablePulldown ) {
ins.selectComponent('.yb-pulldown-scroller').setStyle({
transform: 'translateY(120px)',
transition: 'transform .1s'
})
ins.selectComponent('.yb-list-pulldown').setStyle({
transform: 'translateY(120px)',
if ( state.refresh ) return
if ( state.threshold > 0 && state.pulldownable ) {//下拉
if ( state.threshold > refreshHeight ) {//满足下拉条件
ins.selectComponent('.yingbing-scroller-wrapper').setStyle({
transform: 'translateY(' + refreshHeight + 'px)',
transition: 'transform .1s'
})
ins.callMethod('refresh')
} else {
ins.selectComponent('.yb-pulldown-scroller').setStyle({
transform: 'translateY(0)',
transition: 'transform .1s'
})
ins.selectComponent('.yb-list-pulldown').setStyle({
transform: 'translateY(0)',
ins.callMethod('pulldown')
state.refresh = true
} else {//不满足下拉条件恢复样式
stop(ins)
}
}
if ( state.threshold < 0 && state.pullupable ) {//上拉
if ( Math.abs(state.threshold) > refreshHeight ) {//满足上拉条件
ins.selectComponent('.yingbing-scroller-wrapper').setStyle({
transform: 'translateY(-' + refreshHeight + 'px)',
transition: 'transform .1s'
})
ins.callMethod('pullup')
state.refresh = true
} else {//不满足上拉条件恢复样式
stop(ins)
}
}
}
function stop (ins) {
var state = ins.getState()
ins.selectComponent('.yb-pulldown-scroller').setStyle({
transform: 'translateY(0)',
transition: 'transform .1s'
})
ins.selectComponent('.yb-list-pulldown').setStyle({
ins.selectComponent('.yingbing-scroller-wrapper').setStyle({
transform: 'translateY(0)',
transition: 'transform .1s'
})
state.threshold = 0
state.startY = 0
ins.callMethod('resetPulldownIns')
state.refresh = false
}
function propWatcher (newVal, oldVal, ins) {
ins.setTimeout(function () {
function refreshStateWatcher (newVal, oldVal, ins) {
if ( newVal == 'pulldown' ) {
var state = ins.getState()
state.enablePulldown = (newVal && newVal.enablePulldown)
if ( (newVal && newVal.pulldownRestore) != (oldVal && oldVal.pulldownRestore) ) {
if ( newVal.pulldownRestore ) {
stop(ins)
state.threshold = refreshHeight + 1
state.refresh = false
touchaction(null, ins)
}
if ( newVal == 'pullup' ) {
var state = ins.getState()
state.threshold = -(refreshHeight + 1)
state.refresh = false
touchaction(null, ins)
}
}, 100)
if ( newVal == 'stop' ) stop(ins)
}
function pulldownableWatcher (newVal, oldVal, ins) {
var state = ins.getState()
if ( state ) state.pulldownable = newVal
}
function pullupableWatcher (newVal, oldVal, ins) {
var state = ins.getState()
if ( state ) state.pullupable = newVal
}
module.exports = {
propWatcher: propWatcher,
refreshStateWatcher: refreshStateWatcher,
pulldownableWatcher: pulldownableWatcher,
pullupableWatcher: pullupableWatcher,
touchstart: touchstart,
touchmove: touchmove,
touchend: touchend
touchend: touchend,
touchcancel: touchcancel
}
\ No newline at end of file
<template>
<view class="yingbing-computed" :style="[computedStyle]">
<reader-header :text="chapter.title || ''" v-if="headerShowSync"></reader-header>
<web-view ref="webview" style="flex:1" src="/uni_modules/yingbing-ReadPage/hybrid/html/computed.html" @onPostMessage="onPostMessage"></web-view>
<reader-footer v-if="footerShowSync"></reader-footer>
</view>
</template>
<script>
import ReaderHeader from '../../header/header.vue'
import ReaderFooter from '../../footer/footer.vue'
export default {
inject: ['fontSize', 'fontFamily', 'lineHeight', 'topGap', 'bottomGap', 'slide', 'headerShow', 'footerShow', 'totalChapter'],
components: {
ReaderHeader,
ReaderFooter
},
computed: {
computedStyle () {
return {
'padding-top': this.topGap + 'px',
'padding-bottom': this.bottomGap + 'px',
'padding-left': this.slide + 'px',
'padding-right': this.slide + 'px'
}
}
},
data () {
return {
chapter: {},//章节内容临时存储
success: null,//成功回调
fail: null,//失败回调,
headerShowSync: false,//展示头部
footerShowSync: false//展示底部
}
},
beforeDestroy() {
this.$refs.webview && this.$refs.webview.evalJS("destroyHandleTimer()")
},
methods: {
start ({chapter, success, fail}) {
this.chapter = chapter
this.success = success
this.fail = fail
this.headerShowSync = typeof chapter.headerShow == 'boolean' ? chapter.headerShow : this.headerShow//判断是否显示头部
this.footerShowSync = typeof chapter.footerShow == 'boolean' ? chapter.footerShow : this.footerShow//判断是否显示底部
const style = {fontSize: this.fontSize, fontFamily: this.fontFamily, lineHeight: this.lineHeight, totalChapter: this.totalChapter}
this.$nextTick(function () {
setTimeout(() => {
this.$refs.webview && this.$refs.webview.evalJS("start(" + encodeURIComponent(JSON.stringify(chapter)) + ', ' + encodeURIComponent(JSON.stringify(style)) + ")")
}, 100)
})
},
onPostMessage (e) {
e.detail.data.forEach(item => {
if ( item.handleSuccess ) {
this.success && this.success(item.handleSuccess)
this.success = null
this.fail = null
this.chapter = {}
}
})
}
}
}
</script>
<style scoped>
.yingbing-computed {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
transform: translateX(-1000px);
}
</style>
\ No newline at end of file
<template>
<web-view :src="'/uni_modules/yingbing-ReadPage/hybrid/html/content.html?content=' + encodeURIComponent(contentSync)"></web-view>
</template>
<script>
export default {
inject: ['color', 'fontSize', 'fontFamily', 'lineHeight', 'selectable'],
props: {
item: {
type: Object,
default () {
return new Object
}
}
},
computed: {
contentSync () {
let content = this.item.content || ''
content = content.replace('<whole-render', '<div').replace('<\/whole-render>', '<\/div>')
return `<div class="yingbing-reader-content-html ${this.selectable ? 'user-selectable' : 'user-selectclose'}" style="box-sizing: border-box;color:${this.color};font-family:${this.fontFamily};font-size:${this.fontSize}px;line-height:${this.lineHeight}px;">` + content + '</div>'
}
}
}
</script>
<style>
</style>
\ No newline at end of file
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment