优化
16
.hbuilderx/launch.json
Normal file
@ -0,0 +1,16 @@
|
||||
{ // launch.json 配置了启动调试时相关设置,configurations下节点名称可为 app-plus/h5/mp-weixin/mp-baidu/mp-alipay/mp-qq/mp-toutiao/mp-360/
|
||||
// launchtype项可配置值为local或remote, local代表前端连本地云函数,remote代表前端连云端云函数
|
||||
"version": "0.0",
|
||||
"configurations": [{
|
||||
"app-plus" :
|
||||
{
|
||||
"launchtype" : "local"
|
||||
},
|
||||
"default" :
|
||||
{
|
||||
"launchtype" : "local"
|
||||
},
|
||||
"type" : "uniCloud"
|
||||
}
|
||||
]
|
||||
}
|
@ -7,6 +7,14 @@
|
||||
"transformPx" : false,
|
||||
/* 5+App特有相关 */
|
||||
"app-plus" : {
|
||||
"safearea" : {
|
||||
//安全区域配置,仅ioS平台生效
|
||||
"background" : "#F5F6F9", //安全区域外的背景颜色,默认值为"#FFFFFF"
|
||||
"bottom" : {
|
||||
// 底部安全区域配置
|
||||
"offset" : "none|auto" // 底部安全区域偏移,"none"表示不空出安全区域,"auto"自动计算空出安全区域,默认值为"none"
|
||||
}
|
||||
},
|
||||
"usingComponents" : true,
|
||||
"nvueStyleCompiler" : "uni-app",
|
||||
"compilerVersion" : 3,
|
||||
|
980
pages/novelReading/novelReading - 副本 (4).vue
Normal file
@ -0,0 +1,980 @@
|
||||
<template>
|
||||
<view class="novelReading_content">
|
||||
<u-navbar :title="navbarTitle" @rightClick="rightClick" :autoBack="true" :fixed="true" :bgColor="mainBodyBg"
|
||||
:titleStyle="{ color: navigationBarTitleTextColor }" :leftIconColor="navigationBarTitleTextBackColor"
|
||||
:shadow="true" :safeAreaInsetTop="true" :placeholder="true" />
|
||||
<view class="novelReading_body" :style="`background:${bodyReadingBg}`" @tap="handelShowStepUp">
|
||||
<view class="novelReading_main_con">
|
||||
<!-- :style="`height:${bodyReadingHeight}px;`" -->
|
||||
<!-- :refresher-enabled="true" upper-threshold="200" @refresherrefresh="refresherrefresh" :scroll-top="isScrollTop" -->
|
||||
<view class="novelReading_main">
|
||||
<!-- @refresherpulling="refresherpulling" -->
|
||||
<!-- :scroll-top="isScrollTop" :scroll-into-view="`richText_${readDirectoryActive}`" @scrolltoupper="charactersToupper"-->
|
||||
<scroll-view scroll-y="true" :scroll-top="isScrollTop" @scrolltolower="charactersLower"
|
||||
:refresher-enabled="true" @scroll="charactersScroll" @refresherrefresh="refresherrefresh"
|
||||
class="novelReading_characters_scroll" lower-threshold="10"
|
||||
:refresher-triggered="refresherTriggered">
|
||||
<view class="novelReading_characters_main"
|
||||
:style="`color:${novelContentColor};font-size:${newCharactersSize}rpx;line-height:${defaultCharactersLineHeight};`"
|
||||
v-for="m in novelReadingContentText" :key="m.id" :data-chapterorder="m.chapterorder"
|
||||
:data-lastid="m.lastid" :data-nextid="m.nextid">
|
||||
<!-- <rich-text :nodes="m.content" :ref="`richText_${m.id}`"></rich-text> -->
|
||||
<!-- <rich-text :nodes="`<h3>${m.chaptername}</h3></br>${m.novel_content}`"
|
||||
:ref="`richText_${m.id}`"></rich-text> -->
|
||||
<rich-text :nodes="`${m.richTextNodes}`" :ref="`richText_${m.id}`"></rich-text>
|
||||
<!-- recharge_empty -->
|
||||
</view>
|
||||
<!-- <view v-if="readChapterInfoObj.chackpay == 2" class="read_chapter">
|
||||
<view class=""
|
||||
:style="`color:${novelContentColor};font-size:${newCharactersSize}rpx;line-height:${defaultCharactersLineHeight};`">
|
||||
<h3>{{readChapterInfoObj.chaptername}}</h3>
|
||||
</view>
|
||||
<u-empty icon="/static/images/recharge_empty.png" text="余额不足,请充值..." />
|
||||
</view> -->
|
||||
</scroll-view>
|
||||
</view>
|
||||
<!-- <view class="reading_schedule_box" :style="`background:${bodyReadingBg}`">
|
||||
<view class="reading_schedule_body">
|
||||
<view class="_previous_chapter"
|
||||
:style="`color:${previousChapterBbuttonTextColor};background:${previousChapterBbuttonBg}`"
|
||||
@tap="previousChapter">
|
||||
上一章
|
||||
</view>
|
||||
<view class="_next_chapter"
|
||||
:style="`color:${nextChapterBbuttonTextColor};background:${nextChapterBbuttonBg}`"
|
||||
@tap="nextChapter">下一章
|
||||
</view>
|
||||
</view>
|
||||
</view> -->
|
||||
</view>
|
||||
<view id="compute_rich_text"
|
||||
:style="`width:${computeRichTextWidth}px;color:${novelContentColor};font-size:${newCharactersSize}rpx;line-height:${defaultCharactersLineHeight}`">
|
||||
<rich-text :nodes="computeRichText"></rich-text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="u_popup_all">
|
||||
<u-popup :show="tabBarPopupShow" mode="bottom" :overlay="false" zIndex="6" :bgColor="bodyReadingBg">
|
||||
<view class="my_tabBar_Reading" :style="`background:${bodyReadingBg}`">
|
||||
<view class="tabBar_Reading_item">
|
||||
<view class="reading_item_icon" @tap="handelDirectoryPopup">
|
||||
<image class="is_images" :src="barPopupIcon[novelMainTypeColor].directory_icon"></image>
|
||||
</view>
|
||||
<view class="reading_item_name" :style="`color:${tabBarTextColor}`">目录</view>
|
||||
</view>
|
||||
<view class="tabBar_Reading_item">
|
||||
<view class="reading_item_icon" @tap="toBookshelf">
|
||||
<image class="is_images" :src="barPopupIcon[novelMainTypeColor].bookshelf_icon"></image>
|
||||
</view>
|
||||
<view class="reading_item_name" :style="`color:${tabBarTextColor}`">书城</view>
|
||||
</view>
|
||||
<view class="tabBar_Reading_item" @tap="handelSteUpPopup">
|
||||
<view class="reading_item_icon">
|
||||
<image class="is_images" :src="barPopupIcon[novelMainTypeColor].step_up_icon"></image>
|
||||
</view>
|
||||
<view class="reading_item_name" :style="`color:${tabBarTextColor}`">设置</view>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
<u-popup :show="readingPopupshow" mode="bottom" :overlay="false" zIndex="4" bgColor="transparent">
|
||||
<view class="reading_schedule_box" :style="`background:${bodyReadingBg}`">
|
||||
<view class="reading_schedule_body">
|
||||
<view class="_previous_chapter"
|
||||
:style="`color:${previousChapterBbuttonTextColor};background:${previousChapterBbuttonBg}`"
|
||||
@tap="previousChapter">
|
||||
上一章
|
||||
</view>
|
||||
<view class="_next_chapter"
|
||||
:style="`color:${nextChapterBbuttonTextColor};background:${nextChapterBbuttonBg}`"
|
||||
@tap="nextChapter">下一章
|
||||
</view>
|
||||
</view>
|
||||
<view style="height: 140rpx" />
|
||||
</view>
|
||||
</u-popup>
|
||||
<u-popup :show="stepUpPopupShow" mode="bottom" :overlay="false" bgColor="transparent" zIndex="5">
|
||||
<view class="step_up_box" :style="`background:${bodyReadingBg}`">
|
||||
<view class="step_up_box_body">
|
||||
<view class="step_up_item">
|
||||
<view class="step_up_item_name" :style="`color:${dialogTextColor}`">背景</view>
|
||||
<view :class="[`step_up_item_bg`, novelMainTypeColor == 'F3EFE9' ? 'activeBlack' : '']"
|
||||
style="background: #f3efe9" @tap="changNovelMainType('F3EFE9')"></view>
|
||||
<view :class="[`step_up_item_bg`, novelMainTypeColor == 'CCD9E2' ? 'activeBlack' : '']"
|
||||
style="background: #ccd9e2" @tap="changNovelMainType('CCD9E2')"></view>
|
||||
<view :class="[`step_up_item_bg`, novelMainTypeColor == '333333' ? 'activeWhite' : '']"
|
||||
@tap="changNovelMainType('333333')">
|
||||
<image class="is_images" :src="setUpModeBlack" />
|
||||
</view>
|
||||
</view>
|
||||
<view class="step_up_item mt40rpx">
|
||||
<view class="step_up_item_name" :style="`color:${dialogTextColor}`">字号</view>
|
||||
<view class="step_up_item_text_bg"
|
||||
:style="`color:${dialogTextColor};background:${dialogATextBg}`"
|
||||
@tap="reduceCharactersSize">
|
||||
A-</view>
|
||||
<view class="step_up_item_text_bg"
|
||||
:style="`color:${dialogTextColor};background:${dialogATextBg}`"
|
||||
@tap="addCharactersSize">A+
|
||||
</view>
|
||||
<view class="step_up_item_text_bg"
|
||||
:style="`color:${dialogTextColor};background:${dialogATextBg}`"
|
||||
@tap="handelCharactersSize">
|
||||
默认</view>
|
||||
</view>
|
||||
</view>
|
||||
<view style="height: 140rpx" />
|
||||
</view>
|
||||
</u-popup>
|
||||
<u-popup :show="directoryPopupShow" @close="directoryPopupClose" mode="right" :safeAreaInsetTop="true">
|
||||
<view class="directory_popup_box">
|
||||
<scroll-view scroll-y="true" @scrolltoupper="directoryPopupUpper"
|
||||
@scrolltolower="directoryPopupLower" :show-scrollbar="false" class="directory_scroll_y">
|
||||
<view class="_popup_box_item" v-for="m in directoryList" :key="m.id" @tap="handelDirectoryItem">
|
||||
<view :class="['_item_name', readDirectoryActive == m.chapterorder ? 'active' : '']">
|
||||
{{ m.chaptername }}
|
||||
</view>
|
||||
<view v-if="m.isvip" class="_item_chapter_lock">
|
||||
<image class="is_images" src="/static/images/chapter_lock.png"></image>
|
||||
</view>
|
||||
</view>
|
||||
</scroll-view>
|
||||
</view>
|
||||
</u-popup>
|
||||
<u-popup :show="purchaseFullShow" @close="purchaseFullClose" mode="bottom" overlayStyle="top:44px">
|
||||
<view class="purchaseFull_popup_box" :style="`background:${bodyReadingBg}`">
|
||||
<view>
|
||||
<CommBookLeftRigth :bookTips="bookInfo.category_name" :bookName="bookInfo.title"
|
||||
:bookImage="bookInfo.cover" :bookIntroduction="`${readChapterInfoObj.allprice}书币`" />
|
||||
</view>
|
||||
<view class="purchaseFull_popup_btn_box">
|
||||
<view class="purchaseFull_popup_btn" @tap="handelPurchaseFull">需要全本购买</view>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
<u-modal :show="balanceShow" :title="balanceTitle" content='余额不足,请充值...'
|
||||
:showCancelButton="!readChapterFlag" @confirm="balanceConfirm" @cancel="balanceCancel" />
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import setUpReadingColorAll from './setUpReadingColorAll.js';
|
||||
import bookshelf_333 from '@/static/images/novelReading/bookshelf_333.png';
|
||||
import directory_333 from '@/static/images/novelReading/directory_333.png';
|
||||
import step_up_333 from '@/static/images/novelReading/step_up_333.png';
|
||||
import bookshelf_ccd9e2 from '@/static/images/novelReading/bookshelf_ccd9e2.png';
|
||||
import step_up_ccd9e2 from '@/static/images/novelReading/step_up_ccd9e2.png';
|
||||
import directory_ccd9e2 from '@/static/images/novelReading/directory_ccd9e2.png';
|
||||
import bookshelf_f3efe from '@/static/images/novelReading/bookshelf_f3efe.png';
|
||||
import directory_f3efe9 from '@/static/images/novelReading/directory_f3efe9.png';
|
||||
import step_up_f3efe from '@/static/images/novelReading/step_up_f3efe.png';
|
||||
import set_up_mode_black from '@/static/images/novelReading/set_up_mode_black.png';
|
||||
import recharge_empty from '@/static/images/recharge_empty.png'
|
||||
import config from '@/config/index';
|
||||
import {
|
||||
isGetSystemInfo
|
||||
} from '@/utils/systemInfo.js';
|
||||
import {
|
||||
myGetStorage,
|
||||
mySetStorage
|
||||
} from '@/utils/storage/index.js';
|
||||
import myData from './data.js';
|
||||
import CommBookLeftRigth from '@/components/commBookLeftRigth/index.vue'
|
||||
export default {
|
||||
components: {
|
||||
CommBookLeftRigth
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
navbarTitle: '小说阅读页面',
|
||||
novelMainTypeColor: '',
|
||||
setUpColorAll: {},
|
||||
navigationBarTitleTextColor: '',
|
||||
mainBodyBg: '',
|
||||
navigationBarTitleTextBackColor: '',
|
||||
previousChapterBbuttonBg: '',
|
||||
previousChapterBbuttonTextColor: '',
|
||||
nextChapterBbuttonBg: '',
|
||||
nextChapterBbuttonTextColor: '',
|
||||
bodyReadingHeight: 0,
|
||||
scrollReadingHeight: 0,
|
||||
// 内容背景
|
||||
bodyReadingBg: '',
|
||||
// 底部安全区
|
||||
// bottomSecureHeight: 0,
|
||||
tabBarPopupShow: false,
|
||||
readingPopupshow: false,
|
||||
stepUpPopupShow: false,
|
||||
purchaseFullShow: false,
|
||||
balanceShow: false,
|
||||
balanceTitle: '下一章,付费章节',
|
||||
// bar 的icon
|
||||
barPopupIcon: {},
|
||||
dialogTextColor: '',
|
||||
progressBg: '',
|
||||
progressActiveBg: '',
|
||||
setUpModeBlack: '',
|
||||
dialogATextBg: '',
|
||||
novelContentColor: '',
|
||||
// 目录信息
|
||||
booksDirectorySid: '2387',
|
||||
bookInfo: {},
|
||||
booksDirectoryPage: 1,
|
||||
newBooksDirectoryPage: [],
|
||||
directoryList: [],
|
||||
// directoryPopupHeight: 0,
|
||||
directoryPopupShow: false,
|
||||
// 小说信息
|
||||
novelReadingContentText: [],
|
||||
defaultCharactersSize: 46,
|
||||
newCharactersSize: 46,
|
||||
defaultCharactersLineHeight: 2,
|
||||
readDirectoryActive: '',
|
||||
myData: [],
|
||||
computeRichText: '',
|
||||
computeRichTextWidth: 0,
|
||||
isScrollTop: 0,
|
||||
refresherTriggered: false,
|
||||
readChapterid: '',
|
||||
readChapterFlag: false,
|
||||
readChapterInfoObj: {},
|
||||
readChapterLastid: '',
|
||||
readChapterNextid: '',
|
||||
refresherEnabled: true,
|
||||
};
|
||||
},
|
||||
onLoad(options) {
|
||||
this.booksDirectorySid = options.sid;
|
||||
this.navbarTitle = options.n;
|
||||
this.readChapterid = options.id;
|
||||
},
|
||||
onShow() {
|
||||
// const booksDirectoryPage = this.booksDirectoryPage;
|
||||
// const newBooksDirectoryPage = [...this.newBooksDirectoryPage, booksDirectoryPage];
|
||||
// this.newBooksDirectoryPage = newBooksDirectoryPage;
|
||||
const bookSid = this.booksDirectorySid;
|
||||
const data = {
|
||||
sid: bookSid,
|
||||
}
|
||||
uni.$u.http.post('/bookdetails', data).then((res) => {
|
||||
uni.hideLoading();
|
||||
if (res.status == 1) {
|
||||
const info = res.data.info;
|
||||
const cover = info.cover.includes('http') ? info.cover : `${config.baseUrl}${info.cover}`;
|
||||
this.bookInfo = {
|
||||
...info,
|
||||
cover
|
||||
};
|
||||
}
|
||||
}).catch((err) => {
|
||||
uni.hideLoading();
|
||||
})
|
||||
this.isGetUserRead();
|
||||
},
|
||||
methods: {
|
||||
async isGetUserRead() {
|
||||
const booksDirectorySid = this.booksDirectorySid;
|
||||
const booksDirectoryPage = this.booksDirectoryPage;
|
||||
const readChapterid = this.readChapterid ? this.readChapterid : '';
|
||||
// this.readDirectoryActive = '3';
|
||||
await this.isGetDirectory(booksDirectorySid, booksDirectoryPage);
|
||||
await this.isGetBookInfo(readChapterid, '', true);
|
||||
},
|
||||
isGetBookInfo(chapterId, statusType, isShowLoading = false) {
|
||||
const booksDirectorySid = this.booksDirectorySid;
|
||||
const novelReadingContentText = this.novelReadingContentText;
|
||||
if (isShowLoading) {
|
||||
uni.showLoading({
|
||||
title: '加载中...'
|
||||
});
|
||||
}
|
||||
const data = {
|
||||
sid: booksDirectorySid,
|
||||
};
|
||||
if (chapterId) {
|
||||
data.id = chapterId;
|
||||
}
|
||||
uni.$u.http.post('/read', data).then((res) => {
|
||||
uni.hideLoading();
|
||||
if (res.status == 1) {
|
||||
const readChapterInfoObj = {
|
||||
...res.data,
|
||||
richTextNodes: `<h3>${res.data.chaptername}</h3></br>${res.data.novel_content}`
|
||||
};
|
||||
this.readChapterInfoObj = readChapterInfoObj;
|
||||
if (readChapterInfoObj.chackpay == 1) {
|
||||
this.isScrollTop = 0.01;
|
||||
if (statusType == 'refresherrefresh') {
|
||||
this.novelReadingContentText = [readChapterInfoObj, ...novelReadingContentText];
|
||||
this.computeRichText = readChapterInfoObj.richTextNodes;
|
||||
const query = uni.createSelectorQuery().in(this);
|
||||
this.$nextTick(() => {
|
||||
query.select(`#compute_rich_text`).boundingClientRect((data) => {
|
||||
this.isScrollTop = parseInt(data.height) - 20;
|
||||
}).exec();
|
||||
});
|
||||
this.refresherTriggered = false;
|
||||
} else if (statusType == 'charactersLower') {
|
||||
this.novelReadingContentText = [...novelReadingContentText, readChapterInfoObj];
|
||||
} else if (statusType == 'nextChapter') {
|
||||
this.novelReadingContentText = [readChapterInfoObj];
|
||||
this.readDirectoryActive = readChapterInfoObj.chapterorder;
|
||||
this.readChapterLastid = readChapterInfoObj.lastid;
|
||||
this.readChapterNextid = readChapterInfoObj.nextid;
|
||||
this.$nextTick(() => {
|
||||
this.isScrollTop = 0;
|
||||
})
|
||||
} else if (statusType == 'previousChapter') {
|
||||
this.novelReadingContentText = [readChapterInfoObj];
|
||||
this.readDirectoryActive = readChapterInfoObj.chapterorder;
|
||||
this.readChapterLastid = readChapterInfoObj.lastid;
|
||||
this.readChapterNextid = readChapterInfoObj.nextid;
|
||||
this.$nextTick(() => {
|
||||
this.isScrollTop = 0;
|
||||
})
|
||||
} else {
|
||||
this.novelReadingContentText = [readChapterInfoObj];
|
||||
this.readDirectoryActive = readChapterInfoObj.chapterorder;
|
||||
this.readChapterLastid = readChapterInfoObj.lastid;
|
||||
this.readChapterNextid = readChapterInfoObj.nextid;
|
||||
}
|
||||
} else if (readChapterInfoObj.chackpay == 2) {
|
||||
this.balanceShow = true;
|
||||
this.balanceTitle = '下一章,付费章节';
|
||||
if (isShowLoading) {
|
||||
this.readChapterFlag = true;
|
||||
}
|
||||
} else if (readChapterInfoObj.chackpay == 3) {
|
||||
this.purchaseFullShow = true;
|
||||
if (isShowLoading) {
|
||||
this.readChapterFlag = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}).catch((err) => {
|
||||
uni.hideLoading();
|
||||
console.log(err, '========');
|
||||
});
|
||||
},
|
||||
refresherrefresh() {
|
||||
const readChapterInfoObj = this.readChapterInfoObj;
|
||||
const readChapterLastid = this.readChapterLastid;
|
||||
this.refresherTriggered = true;
|
||||
if (!readChapterLastid) {
|
||||
setTimeout(() => {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: "已经是第一章了"
|
||||
})
|
||||
this.refresherTriggered = false;
|
||||
}, 1000)
|
||||
return
|
||||
}
|
||||
this.isGetBookInfo(readChapterLastid, 'refresherrefresh');
|
||||
},
|
||||
charactersLower() {
|
||||
const readChapterNextid = this.readChapterNextid;
|
||||
// if (readChapterInfoObj.chackpay != 1) {
|
||||
// return
|
||||
// }
|
||||
if (!readChapterNextid) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: "已经是最后一章了~"
|
||||
})
|
||||
return
|
||||
}
|
||||
this.isGetBookInfo(readChapterNextid, 'charactersLower');
|
||||
},
|
||||
charactersScroll(e) {
|
||||
const bodyReadingHeight = this.bodyReadingHeight;
|
||||
const scrollTop = e.detail.scrollTop;
|
||||
// this.isScrollTop = scrollTop;
|
||||
uni.createSelectorQuery().selectAll('.novelReading_characters_main').boundingClientRect((data) => {
|
||||
data.forEach((m) => {
|
||||
if (m.top < bodyReadingHeight) {
|
||||
this.readDirectoryActive = m.dataset.chapterorder;
|
||||
this.readChapterLastid = m.dataset.lastid;
|
||||
this.readChapterNextid = m.dataset.nextid;
|
||||
}
|
||||
});
|
||||
}).exec();
|
||||
},
|
||||
previousChapter() {
|
||||
const readChapterLastid = this.readChapterLastid
|
||||
if (!readChapterLastid) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: "已经是第一章了"
|
||||
})
|
||||
return
|
||||
}
|
||||
this.isGetBookInfo(readChapterLastid, 'previousChapter');
|
||||
},
|
||||
nextChapter() {
|
||||
const readChapterNextid = this.readChapterNextid;
|
||||
if (!readChapterNextid) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: "已经是最后一章了~"
|
||||
})
|
||||
return
|
||||
}
|
||||
this.isGetBookInfo(readChapterNextid, 'nextChapter');
|
||||
},
|
||||
handelDirectoryItem(row) {},
|
||||
handelShowStepUp() {
|
||||
this.tabBarPopupShow = !this.tabBarPopupShow;
|
||||
this.readingPopupshow = !this.readingPopupshow;
|
||||
this.stepUpPopupShow = false;
|
||||
},
|
||||
handelDirectoryPopup() {
|
||||
// this.directoryPopupShow = true;
|
||||
const readChapterInfoObj = this.readChapterInfoObj;
|
||||
const readDirectoryActive = this.readDirectoryActive;
|
||||
uni.navigateTo({
|
||||
url: `/pages/bookRecommendList/bookRecommendList?sid=${readChapterInfoObj.sid}&t=${readChapterInfoObj.title}&c=${readDirectoryActive}`
|
||||
})
|
||||
},
|
||||
directoryPopupClose() {
|
||||
// this.directoryPopupShow = false;
|
||||
// const readChapterInfoObj = this.readChapterInfoObj;
|
||||
// uni.navigateTo({
|
||||
// url: `/pages/bookRecommendList/bookRecommendList?sid=${readChapterInfoObj.sid}&t=${readChapterInfoObj.title}&c=${readChapterInfoObj.chapterorder}`
|
||||
// })
|
||||
},
|
||||
purchaseFullClose() {
|
||||
const readChapterFlag = this.readChapterFlag;
|
||||
if (readChapterFlag) {
|
||||
return;
|
||||
}
|
||||
this.purchaseFullShow = false;
|
||||
},
|
||||
handelSteUpPopup() {
|
||||
this.stepUpPopupShow = true;
|
||||
},
|
||||
balanceConfirm() {
|
||||
uni.navigateTo({
|
||||
url: `/pages/voucherCenter/index`
|
||||
})
|
||||
},
|
||||
balanceCancel() {
|
||||
this.balanceShow = false;
|
||||
},
|
||||
handelPurchaseFull() {
|
||||
const readChapterInfoObj = this.readChapterInfoObj;
|
||||
uni.showLoading({
|
||||
title: '加载中...'
|
||||
});
|
||||
const data = {
|
||||
sid: readChapterInfoObj.sid,
|
||||
id: readChapterInfoObj.id,
|
||||
allprice: readChapterInfoObj.allprice,
|
||||
};
|
||||
uni.$u.http.post('/buyall', data).then((res) => {
|
||||
uni.hideLoading();
|
||||
if (res.status == 1) {
|
||||
if (res.data.status == 2) {
|
||||
this.balanceShow = true;
|
||||
this.balanceTitle = '';
|
||||
}
|
||||
}
|
||||
}).catch((err) => {
|
||||
uni.hideLoading();
|
||||
});
|
||||
},
|
||||
// 切换页面颜色
|
||||
changNovelMainType(type) {
|
||||
|
||||
this.novelMainTypeColor = type;
|
||||
this.initPage(type);
|
||||
const novelMainObj = myGetStorage('novelMainObj') || '{}';
|
||||
const obj = {
|
||||
...JSON.parse(novelMainObj),
|
||||
novelMainTypeColor: type,
|
||||
}
|
||||
mySetStorage('novelMainObj', JSON.stringify(obj));
|
||||
},
|
||||
// 字体减小
|
||||
reduceCharactersSize() {
|
||||
const newCharactersSize = this.newCharactersSize;
|
||||
this.newCharactersSize = newCharactersSize - 2;
|
||||
const novelMainObj = myGetStorage('novelMainObj') || '{}';
|
||||
const obj = {
|
||||
...JSON.parse(novelMainObj),
|
||||
charactersSize: newCharactersSize - 2,
|
||||
}
|
||||
mySetStorage('novelMainObj', JSON.stringify(obj));
|
||||
},
|
||||
// 字体加大
|
||||
addCharactersSize() {
|
||||
const newCharactersSize = this.newCharactersSize;
|
||||
this.newCharactersSize = newCharactersSize + 2;
|
||||
const novelMainObj = myGetStorage('novelMainObj') || '{}';
|
||||
const obj = {
|
||||
...JSON.parse(novelMainObj),
|
||||
charactersSize: newCharactersSize + 2,
|
||||
}
|
||||
mySetStorage('novelMainObj', JSON.stringify(obj));
|
||||
},
|
||||
// 恢复默认字体大小
|
||||
handelCharactersSize() {
|
||||
const defaultCharactersSize = this.defaultCharactersSize;
|
||||
this.newCharactersSize = defaultCharactersSize;
|
||||
const novelMainObj = myGetStorage('novelMainObj') || '{}';
|
||||
const obj = {
|
||||
...JSON.parse(novelMainObj),
|
||||
charactersSize: defaultCharactersSize,
|
||||
}
|
||||
mySetStorage('novelMainObj', JSON.stringify(obj));
|
||||
},
|
||||
// 目录划到了最上边
|
||||
directoryPopupUpper() {
|
||||
const isBooksDirectoryPage = this.booksDirectoryPage;
|
||||
const newBooksDirectoryPage = this.newBooksDirectoryPage;
|
||||
if (isBooksDirectoryPage > 1 && newBooksDirectoryPage.indexOf(isBooksDirectoryPage) == -1) {
|
||||
const booksDirectorySid = this.booksDirectorySid;
|
||||
const booksDirectoryPage = isBooksDirectoryPage - 1;
|
||||
this.booksDirectoryPage = booksDirectoryPage;
|
||||
this.isGetDirectory(booksDirectorySid, booksDirectoryPage, 'upper');
|
||||
}
|
||||
},
|
||||
// 目录划到了最下边
|
||||
directoryPopupLower() {
|
||||
const isBooksDirectoryPage = this.booksDirectoryPage;
|
||||
const booksDirectorySid = this.booksDirectorySid;
|
||||
const booksDirectoryPage = isBooksDirectoryPage + 1;
|
||||
const newBooksDirectoryPage = [...this.newBooksDirectoryPage, booksDirectoryPage];
|
||||
this.newBooksDirectoryPage = newBooksDirectoryPage;
|
||||
this.booksDirectoryPage = booksDirectoryPage;
|
||||
this.isGetDirectory(booksDirectorySid, booksDirectoryPage, 'lower');
|
||||
},
|
||||
isGetDirectory(sid, page, type) {
|
||||
uni.showLoading({
|
||||
title: '加载中...'
|
||||
});
|
||||
const data = {
|
||||
sid,
|
||||
page
|
||||
};
|
||||
uni.$u.http.post('/getDirectory', data).then((res) => {
|
||||
uni.hideLoading();
|
||||
if (res.status == 1) {
|
||||
const directory = res.data.directory;
|
||||
let columnsLabel = '';
|
||||
if (type == 'upper') {
|
||||
this.directoryList = [...directory, ...this.directoryList];
|
||||
} else if (type == 'lower') {
|
||||
this.directoryList = [...this.directoryList, ...directory];
|
||||
} else {
|
||||
this.directoryList = directory;
|
||||
}
|
||||
}
|
||||
}).catch((err) => {
|
||||
uni.hideLoading();
|
||||
console.log(err, '========');
|
||||
});
|
||||
},
|
||||
initPage(novelMainTypeColor) {
|
||||
const mainBodyBg = setUpReadingColorAll[novelMainTypeColor].mainBodyBg;
|
||||
this.navigationBarTitleTextColor = setUpReadingColorAll[novelMainTypeColor].navigationBarTitleTextColor;
|
||||
this.mainBodyBg = mainBodyBg;
|
||||
this.bodyReadingBg = mainBodyBg;
|
||||
this.dialogTextColor = setUpReadingColorAll[novelMainTypeColor].dialogTextColor;
|
||||
this.progressBg = setUpReadingColorAll[novelMainTypeColor].progressBg;
|
||||
this.progressActiveBg = setUpReadingColorAll[novelMainTypeColor].progressActiveBg;
|
||||
this.setUpModeBlack = set_up_mode_black;
|
||||
this.dialogATextBg = setUpReadingColorAll[novelMainTypeColor].dialogATextBg;
|
||||
this.tabBarTextColor = setUpReadingColorAll[novelMainTypeColor].tabBarTextColor;
|
||||
this.novelContentColor = setUpReadingColorAll[novelMainTypeColor].novelContentColor;
|
||||
this.navigationBarTitleTextBackColor = setUpReadingColorAll[novelMainTypeColor]
|
||||
.navigationBarTitleTextBackColor;
|
||||
this.previousChapterBbuttonBg = setUpReadingColorAll[novelMainTypeColor].previousChapterBbuttonBg;
|
||||
this.previousChapterBbuttonTextColor = setUpReadingColorAll[novelMainTypeColor]
|
||||
.previousChapterBbuttonTextColor;
|
||||
this.nextChapterBbuttonBg = setUpReadingColorAll[novelMainTypeColor].nextChapterBbuttonBg;
|
||||
this.nextChapterBbuttonTextColor = setUpReadingColorAll[novelMainTypeColor].nextChapterBbuttonTextColor;
|
||||
},
|
||||
rightClick() {
|
||||
uni.navigateBack();
|
||||
},
|
||||
toBookshelf() {
|
||||
uni.reLaunch({
|
||||
url: `/pages/bookCity/bookCity/index`
|
||||
})
|
||||
}
|
||||
},
|
||||
onPullDownRefresh() {
|
||||
// const isReadDirectoryActive = this.readDirectoryActive;
|
||||
// if(isReadDirectoryActive == 0) {
|
||||
// uni.showToast({
|
||||
// icon:'none',
|
||||
// title:"已经是第一章了"
|
||||
// })
|
||||
// uni.stopPullDownRefresh()
|
||||
// return
|
||||
// }
|
||||
// const readDirectoryActive = isReadDirectoryActive- 1;
|
||||
// const novelReadingContentText = this.myData[readDirectoryActive];
|
||||
// this.novelReadingContentText = [novelReadingContentText, ...this.novelReadingContentText];
|
||||
// // setTimeout(() => {
|
||||
// this.computeRichText = this.myData[readDirectoryActive].content;
|
||||
// // this.readDirectoryActive = readDirectoryActive;
|
||||
// const query = uni.createSelectorQuery().in(this);
|
||||
// this.$nextTick(() => {
|
||||
// query.select(`#compute_rich_text`).boundingClientRect((data) => {
|
||||
// this.isScrollTop = parseInt(data.height) - 30;
|
||||
// }).exec();
|
||||
// });
|
||||
// uni.stopPullDownRefresh()
|
||||
},
|
||||
created() {
|
||||
const novelMainObj = myGetStorage('novelMainObj') || '{}';
|
||||
const novelMainTypeColor = JSON.parse(novelMainObj).novelMainTypeColor || 'F3EFE9';
|
||||
const {
|
||||
screenHeight = 0, statusBarHeight = 0, windowHeight = 0, devicePixelRatio, windowBottom, windowWidth,
|
||||
screenWidth
|
||||
} = isGetSystemInfo();
|
||||
// this.myData = myData;
|
||||
this.computeRichTextWidth = windowWidth;
|
||||
this.novelMainTypeColor = novelMainTypeColor;
|
||||
this.newCharactersSize = JSON.parse(novelMainObj).charactersSize || 46;
|
||||
this.bodyReadingHeight = screenHeight - statusBarHeight - devicePixelRatio * 22;
|
||||
// 不需要
|
||||
// this.scrollReadingHeight = screenHeight - statusBarHeight - devicePixelRatio * 38;
|
||||
// this.scrollReadingHeight = windowHeight - (screenWidth / 375) * 54;
|
||||
// // #ifdef APP-PLUS
|
||||
// this.directoryPopupHeight = screenHeight - statusBarHeight;
|
||||
// // #endif
|
||||
// // #ifdef H5 || MP-WEIXIN
|
||||
// this.directoryPopupHeight = screenHeight - statusBarHeight - devicePixelRatio * 22;
|
||||
// // #endif
|
||||
|
||||
// this.bottomSecureHeight = screenHeight - windowHeight;
|
||||
this.setUpColorAll = setUpReadingColorAll;
|
||||
this.barPopupIcon = {
|
||||
'F3EFE9': {
|
||||
bookshelf_icon: bookshelf_f3efe,
|
||||
directory_icon: directory_f3efe9,
|
||||
step_up_icon: step_up_f3efe
|
||||
},
|
||||
'CCD9E2': {
|
||||
bookshelf_icon: bookshelf_ccd9e2,
|
||||
directory_icon: directory_ccd9e2,
|
||||
step_up_icon: step_up_ccd9e2
|
||||
},
|
||||
'333333': {
|
||||
bookshelf_icon: bookshelf_333,
|
||||
directory_icon: directory_333,
|
||||
step_up_icon: step_up_333
|
||||
}
|
||||
};
|
||||
this.initPage(novelMainTypeColor);
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
page {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.is_images {
|
||||
display: block;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.mt40rpx {
|
||||
margin-top: 40rpx;
|
||||
}
|
||||
|
||||
.novelReading_content::v-deep.u-navbar__content__title {
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
.novelReading_content::v-deep.u-navbar--fixed {
|
||||
box-shadow: 0 0 8rpx rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.novelReading_content::v-deep.z-paging-content {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#compute_rich_text {
|
||||
position: fixed;
|
||||
top: 1500px;
|
||||
padding: 32rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.novelReading_content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
|
||||
.novelReading_body {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
|
||||
.novelReading_main_con {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 32rpx 32rpx;
|
||||
padding-bottom: constant(safe-area-inset-bottom);
|
||||
/* 兼容 iOS 设备 */
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
/* 兼容 iPhone X 及以上设备 */
|
||||
box-sizing: border-box;
|
||||
|
||||
.novelReading_main {
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
position: relative;
|
||||
|
||||
.novelReading_characters_scroll {
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
box-sizing: border-box;
|
||||
|
||||
.read_chapter {
|
||||
padding: 40rpx 0;
|
||||
}
|
||||
|
||||
.novelReading_characters_main {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.reading_schedule_box {
|
||||
width: 100%;
|
||||
height: 82rpx;
|
||||
// padding: 40rpx;
|
||||
// box-shadow: 0 0 8rpx rgba(0, 0, 0, 0.2);
|
||||
border-radius: 40rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.u_popup_all {
|
||||
flex: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.my_tabBar_Reading {
|
||||
width: 100%;
|
||||
height: 140rpx;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 0 40rpx;
|
||||
box-sizing: border-box;
|
||||
border-top: 1rpx solid #d6d2ce;
|
||||
// padding-bottom: constant(safe-area-inset-bottom); /* 兼容 iOS 设备 */
|
||||
// padding-bottom: env(safe-area-inset-bottom); /* 兼容 iPhone X 及以上设备 */
|
||||
|
||||
.tabBar_Reading_item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
|
||||
.reading_item_icon {
|
||||
width: 34rpx;
|
||||
height: 34rpx;
|
||||
}
|
||||
|
||||
.reading_item_name {
|
||||
font-size: 30rpx;
|
||||
line-height: 1;
|
||||
margin-top: 10rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.reading_schedule_box {
|
||||
width: 100%;
|
||||
box-shadow: 0 0 8rpx rgba(0, 0, 0, 0.2);
|
||||
border-radius: 40rpx;
|
||||
|
||||
.reading_schedule_body {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
padding: 40rpx;
|
||||
box-sizing: border-box;
|
||||
background: transparent;
|
||||
|
||||
._previous_chapter {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 192rpx;
|
||||
height: 82rpx;
|
||||
font-size: 34rpx;
|
||||
line-height: 1;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
|
||||
._next_chapter {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 442rpx;
|
||||
height: 82rpx;
|
||||
font-size: 34rpx;
|
||||
line-height: 1;
|
||||
border-radius: 12rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.step_up_box {
|
||||
width: 100%;
|
||||
box-shadow: 0 0 8rpx rgba(0, 0, 0, 0.2);
|
||||
border-radius: 40rpx;
|
||||
|
||||
.step_up_box_body {
|
||||
width: 100%;
|
||||
padding: 40rpx 24rpx;
|
||||
box-sizing: border-box;
|
||||
background: transparent;
|
||||
|
||||
.step_up_item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.step_up_item_name {
|
||||
line-height: 1;
|
||||
font-size: 30rpx;
|
||||
}
|
||||
|
||||
.step_up_item_bg {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 188rpx;
|
||||
height: 74rpx;
|
||||
border-radius: 37rpx;
|
||||
box-sizing: border-box;
|
||||
border: 2rpx solid transparent;
|
||||
}
|
||||
|
||||
.step_up_item_text_bg {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 188rpx;
|
||||
height: 74rpx;
|
||||
border-radius: 37rpx;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.step_up_item_bg.activeBlack {
|
||||
border-color: #1a1a1a;
|
||||
}
|
||||
|
||||
.step_up_item_bg.activeWhite {
|
||||
border-color: #fff;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.reading_schedule_slider::v-deep.uni-slider-handle-wrapper {
|
||||
height: 60rpx;
|
||||
border-radius: 30rpx;
|
||||
|
||||
.uni-slider-track {
|
||||
border-radius: 30rpx;
|
||||
}
|
||||
}
|
||||
|
||||
.directory_popup_box {
|
||||
position: relative;
|
||||
width: 560rpx;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
|
||||
.directory_scroll_y {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
padding-left: 32rpx;
|
||||
padding-right: 32rpx;
|
||||
box-sizing: border-box;
|
||||
padding-bottom: constant(safe-area-inset-bottom);
|
||||
/* 兼容 iOS 设备 */
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
/* 兼容 iPhone X 及以上设备 */
|
||||
// padding-top: constant(safe-area-inset-top);
|
||||
// /* 兼容 iOS 设备 */
|
||||
// padding-top: env(safe-area-inset-top);
|
||||
// /* 兼容 iPhone X 及以上设备 */
|
||||
|
||||
._popup_box_item {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 120rpx;
|
||||
border-bottom: 1rpx solid #f2f2f2;
|
||||
|
||||
._item_name {
|
||||
font-size: 32rpx;
|
||||
color: #333333;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
._item_name.active {
|
||||
color: #ff728f;
|
||||
}
|
||||
|
||||
._item_chapter_lock {
|
||||
width: 32rpx;
|
||||
height: 32rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
.purchaseFull_popup_box {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 50rpx 32rpx;
|
||||
|
||||
.purchaseFull_popup_btn_box {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
margin-top: 50rpx;
|
||||
|
||||
.purchaseFull_popup_btn {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 640rpx;
|
||||
height: 84rpx;
|
||||
font-size: 30rpx;
|
||||
color: #fff;
|
||||
background: linear-gradient(to top, #FBA676, #E95E32);
|
||||
border-radius: 24rpx;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
@ -1,55 +1,19 @@
|
||||
<template>
|
||||
<view class="novelReading_content">
|
||||
<u-navbar :title="navbarTitle" @rightClick="rightClick" :autoBack="true" :fixed="true" :bgColor="mainBodyBg"
|
||||
<!-- <u-navbar :title="navbarTitle" @rightClick="rightClick" :autoBack="true" :fixed="true" :bgColor="mainBodyBg"
|
||||
:titleStyle="{ color: navigationBarTitleTextColor }" :leftIconColor="navigationBarTitleTextBackColor"
|
||||
:shadow="true" :safeAreaInsetTop="true" :placeholder="true" />
|
||||
<view class="novelReading_body" :style="`background:${bodyReadingBg}`" @tap="handelShowStepUp">
|
||||
:shadow="true" :safeAreaInsetTop="true" :placeholder="true" /> -->
|
||||
<!-- @tap="handelShowStepUp" -->
|
||||
<view class="novelReading_body" :style="`background:${bodyReadingBg}`">
|
||||
<!-- -->
|
||||
<view class="novelReading_main_con">
|
||||
<!-- :style="`height:${bodyReadingHeight}px;`" -->
|
||||
<!-- :refresher-enabled="true" upper-threshold="200" @refresherrefresh="refresherrefresh" :scroll-top="isScrollTop" -->
|
||||
<view class="novelReading_main">
|
||||
<!-- @refresherpulling="refresherpulling" -->
|
||||
<!-- :scroll-top="isScrollTop" :scroll-into-view="`richText_${readDirectoryActive}`" @scrolltoupper="charactersToupper"-->
|
||||
<scroll-view scroll-y="true" :scroll-top="isScrollTop" @scrolltolower="charactersLower"
|
||||
:refresher-enabled="true" @scroll="charactersScroll" @refresherrefresh="refresherrefresh"
|
||||
class="novelReading_characters_scroll" lower-threshold="10"
|
||||
:refresher-triggered="refresherTriggered">
|
||||
<view class="novelReading_characters_main"
|
||||
:style="`color:${novelContentColor};font-size:${newCharactersSize}rpx;line-height:${defaultCharactersLineHeight};`"
|
||||
v-for="m in novelReadingContentText" :key="m.id" :data-chapterorder="m.chapterorder"
|
||||
:data-lastid="m.lastid" :data-nextid="m.nextid">
|
||||
<!-- <rich-text :nodes="m.content" :ref="`richText_${m.id}`"></rich-text> -->
|
||||
<!-- <rich-text :nodes="`<h3>${m.chaptername}</h3></br>${m.novel_content}`"
|
||||
:ref="`richText_${m.id}`"></rich-text> -->
|
||||
<rich-text :nodes="`${m.richTextNodes}`" :ref="`richText_${m.id}`"></rich-text>
|
||||
<!-- recharge_empty -->
|
||||
</view>
|
||||
<!-- <view v-if="readChapterInfoObj.chackpay == 2" class="read_chapter">
|
||||
<view class=""
|
||||
:style="`color:${novelContentColor};font-size:${newCharactersSize}rpx;line-height:${defaultCharactersLineHeight};`">
|
||||
<h3>{{readChapterInfoObj.chaptername}}</h3>
|
||||
</view>
|
||||
<u-empty icon="/static/images/recharge_empty.png" text="余额不足,请充值..." />
|
||||
</view> -->
|
||||
</scroll-view>
|
||||
</view>
|
||||
<!-- <view class="reading_schedule_box" :style="`background:${bodyReadingBg}`">
|
||||
<view class="reading_schedule_body">
|
||||
<view class="_previous_chapter"
|
||||
:style="`color:${previousChapterBbuttonTextColor};background:${previousChapterBbuttonBg}`"
|
||||
@tap="previousChapter">
|
||||
上一章
|
||||
</view>
|
||||
<view class="_next_chapter"
|
||||
:style="`color:${nextChapterBbuttonTextColor};background:${nextChapterBbuttonBg}`"
|
||||
@tap="nextChapter">下一章
|
||||
</view>
|
||||
</view>
|
||||
</view> -->
|
||||
</view>
|
||||
<view id="compute_rich_text"
|
||||
:style="`width:${computeRichTextWidth}px;color:${novelContentColor};font-size:${newCharactersSize}rpx;line-height:${defaultCharactersLineHeight}`">
|
||||
<rich-text :nodes="computeRichText"></rich-text>
|
||||
<yingbing-ReadPage style="height: 100%;" ref="yingbingReadPage" :footerShow="true"
|
||||
:page-type="charactersPageType" :font-size="newCharactersSize" :enableClick="true"
|
||||
:line-height="defaultCharactersLineHeight" :color="novelContentColor" :bg-color="bodyReadingBg"
|
||||
:slide="20" :enablePreload="false" @loadmore="loadmoreContent" @preload="preloadContent"
|
||||
@change="currentChange" @setCatalog="setCatalog" @clickme="clickme" @clickher="clickher"
|
||||
@clickTo="handelShowStepUp" :clickOption="{width:200,height: 400,left:'auto',top:'auto'}">
|
||||
</yingbing-ReadPage>
|
||||
</view>
|
||||
</view>
|
||||
<view class="u_popup_all">
|
||||
@ -225,14 +189,13 @@
|
||||
directoryPopupShow: false,
|
||||
// 小说信息
|
||||
novelReadingContentText: [],
|
||||
defaultCharactersSize: 46,
|
||||
newCharactersSize: 46,
|
||||
defaultCharactersLineHeight: 2,
|
||||
charactersPageType: 'real',
|
||||
defaultCharactersSize: 28,
|
||||
newCharactersSize: 28,
|
||||
defaultCharactersLineHeight: 20,
|
||||
readDirectoryActive: '',
|
||||
myData: [],
|
||||
computeRichText: '',
|
||||
computeRichTextWidth: 0,
|
||||
isScrollTop: 0,
|
||||
refresherTriggered: false,
|
||||
readChapterid: '',
|
||||
readChapterFlag: false,
|
||||
@ -248,9 +211,6 @@
|
||||
this.readChapterid = options.id;
|
||||
},
|
||||
onShow() {
|
||||
// const booksDirectoryPage = this.booksDirectoryPage;
|
||||
// const newBooksDirectoryPage = [...this.newBooksDirectoryPage, booksDirectoryPage];
|
||||
// this.newBooksDirectoryPage = newBooksDirectoryPage;
|
||||
const bookSid = this.booksDirectorySid;
|
||||
const data = {
|
||||
sid: bookSid,
|
||||
@ -276,10 +236,20 @@
|
||||
const booksDirectoryPage = this.booksDirectoryPage;
|
||||
const readChapterid = this.readChapterid ? this.readChapterid : '';
|
||||
// this.readDirectoryActive = '3';
|
||||
await this.isGetDirectory(booksDirectorySid, booksDirectoryPage);
|
||||
await this.isGetBookInfo(readChapterid, '', true);
|
||||
// await this.isGetDirectory(booksDirectorySid, booksDirectoryPage);
|
||||
const readChapterInfoObj = await this.isGetBookInfo(readChapterid, '', true);
|
||||
this.novelReadingContentText = [readChapterInfoObj];
|
||||
this.readDirectoryActive = readChapterInfoObj.chapterorder;
|
||||
this.readChapterLastid = readChapterInfoObj.lastid;
|
||||
this.readChapterNextid = readChapterInfoObj.nextid;
|
||||
this.$refs.yingbingReadPage.init({
|
||||
contents: [readChapterInfoObj],
|
||||
start: 0,
|
||||
currentChapter: readChapterInfoObj.chapterorder
|
||||
})
|
||||
},
|
||||
isGetBookInfo(chapterId, statusType, isShowLoading = false) {
|
||||
return new Promise((resolve) => {
|
||||
const booksDirectorySid = this.booksDirectorySid;
|
||||
const novelReadingContentText = this.novelReadingContentText;
|
||||
if (isShowLoading) {
|
||||
@ -296,47 +266,22 @@
|
||||
uni.$u.http.post('/read', data).then((res) => {
|
||||
uni.hideLoading();
|
||||
if (res.status == 1) {
|
||||
const resData = res.data;
|
||||
const is_novel_content = resData.novel_content.replace(/<\/p>/g, '\n')
|
||||
const readChapterInfoObj = {
|
||||
...res.data,
|
||||
richTextNodes: `<h3>${res.data.chaptername}</h3></br>${res.data.novel_content}`
|
||||
...resData,
|
||||
content: is_novel_content.replace(/<p>/g, ''),
|
||||
isStart: resData.lastid == '' ? true : false,
|
||||
isEnd: resData.nextid == '' ? true : false,
|
||||
chapter: resData.chapterorder,
|
||||
title: resData.chaptername,
|
||||
isTtitle: resData.title
|
||||
// richTextNodes: `<h3>${res.data.chaptername}</h3></br>${res.data.novel_content}`
|
||||
// richTextNodes: `<h3>${res.data.chaptername}</h3></br>${res.data.novel_content}`
|
||||
};
|
||||
this.readChapterInfoObj = readChapterInfoObj;
|
||||
if (readChapterInfoObj.chackpay == 1) {
|
||||
this.isScrollTop = 0.01;
|
||||
if (statusType == 'refresherrefresh') {
|
||||
this.novelReadingContentText = [readChapterInfoObj, ...novelReadingContentText];
|
||||
this.computeRichText = readChapterInfoObj.richTextNodes;
|
||||
const query = uni.createSelectorQuery().in(this);
|
||||
this.$nextTick(() => {
|
||||
query.select(`#compute_rich_text`).boundingClientRect((data) => {
|
||||
this.isScrollTop = parseInt(data.height) - 20;
|
||||
}).exec();
|
||||
});
|
||||
this.refresherTriggered = false;
|
||||
} else if (statusType == 'charactersLower') {
|
||||
this.novelReadingContentText = [...novelReadingContentText, readChapterInfoObj];
|
||||
} else if (statusType == 'nextChapter') {
|
||||
this.novelReadingContentText = [readChapterInfoObj];
|
||||
this.readDirectoryActive = readChapterInfoObj.chapterorder;
|
||||
this.readChapterLastid = readChapterInfoObj.lastid;
|
||||
this.readChapterNextid = readChapterInfoObj.nextid;
|
||||
this.$nextTick(() => {
|
||||
this.isScrollTop = 0;
|
||||
})
|
||||
} else if (statusType == 'previousChapter') {
|
||||
this.novelReadingContentText = [readChapterInfoObj];
|
||||
this.readDirectoryActive = readChapterInfoObj.chapterorder;
|
||||
this.readChapterLastid = readChapterInfoObj.lastid;
|
||||
this.readChapterNextid = readChapterInfoObj.nextid;
|
||||
this.$nextTick(() => {
|
||||
this.isScrollTop = 0;
|
||||
})
|
||||
} else {
|
||||
this.novelReadingContentText = [readChapterInfoObj];
|
||||
this.readDirectoryActive = readChapterInfoObj.chapterorder;
|
||||
this.readChapterLastid = readChapterInfoObj.lastid;
|
||||
this.readChapterNextid = readChapterInfoObj.nextid;
|
||||
}
|
||||
resolve(readChapterInfoObj);
|
||||
} else if (readChapterInfoObj.chackpay == 2) {
|
||||
this.balanceShow = true;
|
||||
this.balanceTitle = '下一章,付费章节';
|
||||
@ -354,50 +299,47 @@
|
||||
uni.hideLoading();
|
||||
console.log(err, '========');
|
||||
});
|
||||
},
|
||||
refresherrefresh() {
|
||||
const readChapterInfoObj = this.readChapterInfoObj;
|
||||
const readChapterLastid = this.readChapterLastid;
|
||||
this.refresherTriggered = true;
|
||||
if (!readChapterLastid) {
|
||||
setTimeout(() => {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: "已经是第一章了"
|
||||
})
|
||||
this.refresherTriggered = false;
|
||||
}, 1000)
|
||||
return
|
||||
}
|
||||
this.isGetBookInfo(readChapterLastid, 'refresherrefresh');
|
||||
|
||||
},
|
||||
charactersLower() {
|
||||
const readChapterNextid = this.readChapterNextid;
|
||||
// if (readChapterInfoObj.chackpay != 1) {
|
||||
// return
|
||||
// }
|
||||
if (!readChapterNextid) {
|
||||
uni.showToast({
|
||||
icon: 'none',
|
||||
title: "已经是最后一章了~"
|
||||
})
|
||||
return
|
||||
async loadmoreContent(chapter, callback) {
|
||||
console.log(chapter, "*****************")
|
||||
const newReadDirectoryActive = this.readDirectoryActive;
|
||||
const newReadChapterLastid = this.readChapterLastid;
|
||||
const newNovelReadingContentText = this.novelReadingContentText;
|
||||
const newReadChapterNextid = this.readChapterNextid;
|
||||
console.log(newNovelReadingContentText,"newNovelReadingContentText")
|
||||
// const itemTemp = newNovelReadingContentText.filter((m) => m.chapter == parseInt(chapter + 1));
|
||||
// console.log(itemTemp,itemTemp[0].lastid, "itemTempitemTempitemTemp")
|
||||
if (newReadDirectoryActive != 1) {
|
||||
if (chapter < newReadDirectoryActive) {
|
||||
// const readChapterInfoObj = await this.isGetBookInfo(itemTemp[0].lastid);
|
||||
const readChapterInfoObj = await this.isGetBookInfo(newReadChapterLastid);
|
||||
console.log(readChapterInfoObj, "上一章")
|
||||
this.novelReadingContentText = [...newNovelReadingContentText, readChapterInfoObj];
|
||||
this.readDirectoryActive = chapter;
|
||||
// this.readChapterLastid = readChapterInfoObj.lastid;
|
||||
// this.readChapterNextid = readChapterInfoObj.nextid;
|
||||
callback('success', readChapterInfoObj)
|
||||
}
|
||||
if (chapter > newReadDirectoryActive) {
|
||||
// const readChapterInfoObj = await this.isGetBookInfo(itemTemp[0].nextid);
|
||||
const readChapterInfoObj = await this.isGetBookInfo(newReadChapterNextid);
|
||||
console.log(readChapterInfoObj, "下一章")
|
||||
this.novelReadingContentText = [readChapterInfoObj, ...newNovelReadingContentText];
|
||||
this.readDirectoryActive = chapter;
|
||||
// this.readChapterLastid = readChapterInfoObj.lastid;
|
||||
// this.readChapterNextid = readChapterInfoObj.nextid;
|
||||
callback('success', readChapterInfoObj)
|
||||
}
|
||||
}
|
||||
this.isGetBookInfo(readChapterNextid, 'charactersLower');
|
||||
},
|
||||
charactersScroll(e) {
|
||||
const bodyReadingHeight = this.bodyReadingHeight;
|
||||
const scrollTop = e.detail.scrollTop;
|
||||
// this.isScrollTop = scrollTop;
|
||||
uni.createSelectorQuery().selectAll('.novelReading_characters_main').boundingClientRect((data) => {
|
||||
data.forEach((m) => {
|
||||
if (m.top < bodyReadingHeight) {
|
||||
this.readDirectoryActive = m.dataset.chapterorder;
|
||||
this.readChapterLastid = m.dataset.lastid;
|
||||
this.readChapterNextid = m.dataset.nextid;
|
||||
}
|
||||
});
|
||||
}).exec();
|
||||
currentChange(pageInfo) {
|
||||
const novelReadingContentText = this.novelReadingContentText;
|
||||
const chapter = pageInfo.chapter;
|
||||
const itemTemp = novelReadingContentText.filter((m) => m.chapter == chapter);
|
||||
this.readChapterLastid = itemTemp[0].lastid;
|
||||
this.readChapterNextid = itemTemp[0].nextid;
|
||||
},
|
||||
previousChapter() {
|
||||
const readChapterLastid = this.readChapterLastid
|
||||
@ -637,7 +579,7 @@
|
||||
// this.myData = myData;
|
||||
this.computeRichTextWidth = windowWidth;
|
||||
this.novelMainTypeColor = novelMainTypeColor;
|
||||
this.newCharactersSize = JSON.parse(novelMainObj).charactersSize || 46;
|
||||
this.newCharactersSize = JSON.parse(novelMainObj).charactersSize || 28;
|
||||
this.bodyReadingHeight = screenHeight - statusBarHeight - devicePixelRatio * 22;
|
||||
// 不需要
|
||||
// this.scrollReadingHeight = screenHeight - statusBarHeight - devicePixelRatio * 38;
|
||||
@ -724,11 +666,14 @@
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
padding: 32rpx 32rpx;
|
||||
// // padding: 32rpx 32rpx;
|
||||
padding-bottom: constant(safe-area-inset-bottom);
|
||||
/* 兼容 iOS 设备 */
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
/* 兼容 iPhone X 及以上设备 */
|
||||
padding-top: var(--status-bar-height);
|
||||
/* 兼容 iOS 设备 */
|
||||
// padding-top:var(–status-bar-height);
|
||||
box-sizing: border-box;
|
||||
|
||||
.novelReading_main {
|
||||
@ -922,10 +867,6 @@
|
||||
/* 兼容 iOS 设备 */
|
||||
padding-bottom: env(safe-area-inset-bottom);
|
||||
/* 兼容 iPhone X 及以上设备 */
|
||||
// padding-top: constant(safe-area-inset-top);
|
||||
// /* 兼容 iOS 设备 */
|
||||
// padding-top: env(safe-area-inset-top);
|
||||
// /* 兼容 iPhone X 及以上设备 */
|
||||
|
||||
._popup_box_item {
|
||||
display: flex;
|
||||
|
104
uni_modules/yingbing-ReadPage/changelog.md
Normal file
@ -0,0 +1,104 @@
|
||||
## 1.4.3(2023-05-25)
|
||||
* 新增fontFamily属性设置字体名称
|
||||
* 新增fontFace属性添加自定义字体
|
||||
## 1.4.2(2023-05-25)
|
||||
* 修复IOS电量显示
|
||||
## 1.4.1(2023-05-24)
|
||||
* 修复单章节内容太短可能会引起得卡死和报错
|
||||
## 1.4.0(2023-04-04)
|
||||
* 优化翻页模式,减少闪烁问题
|
||||
## 1.3.9(2023-04-03)
|
||||
* 优化APP端电量获取,修复获取电量可能会造成闪退的问题
|
||||
* 修复APP-NVUE翻页模式加载下一章节会闪烁的问题
|
||||
## 1.3.8(2023-04-01)
|
||||
* 修复APP-NVUE 向前翻页会闪烁的问题
|
||||
* 删除一些不必要的代码文件
|
||||
## 1.3.7(2023-04-01)
|
||||
* APP-NVUE使用webview替代rich-text实现自定义页面
|
||||
* APP-NVUE自定义页面支持添加自定义点击事件
|
||||
* 稍微优化了一下翻页
|
||||
## 1.3.6(2023-03-15)
|
||||
* 修复在低端机上,分页会卡住的问题
|
||||
## 1.3.5(2023-03-14)
|
||||
* 优化整书模式的分页
|
||||
## 1.3.4(2023-03-13)
|
||||
* 修复APP-NVUE一些问题
|
||||
## 1.3.3(2023-03-13)
|
||||
* 优化滚动模式定位问题
|
||||
## 1.3.2(2023-03-01)
|
||||
* 修复整书模式 init初始化 无效果的 bug
|
||||
## 1.3.1(2023-01-04)
|
||||
* 修复直接点击上一页,文字不显示的bug
|
||||
## 1.3.0(2022-12-20)
|
||||
* 新增APP-NVUE和微信小程序支持
|
||||
* 新增自定义插槽功能
|
||||
* 整书模式已整合进入章节模式,表现效果和章节模式相同
|
||||
* 新增页面顶部和底部的电池电量、章节、事件,页数显示
|
||||
## 1.2.9(2022-09-06)
|
||||
* 修复滑动过快造成翻页动画异常的bug
|
||||
* 修复调用refresh方法报错的bug
|
||||
* 修复翻页动画有时候方向判断错误的bug
|
||||
* 滚动模式不再使用better-scroll插件,改用自己写的滚动组件
|
||||
* 添加pageNext, pagePrev翻页方法
|
||||
## 1.2.8(2022-06-08)
|
||||
* 更改整书模式的翻页方法(上次更新忘记改了)
|
||||
## 1.2.7(2022-06-04)
|
||||
* 添加自定义页面功能(可用于付费章节、广告展示等)
|
||||
* 更改触摸翻页方式,现在页面上任何地方都能触摸翻页
|
||||
* 减小了翻页需要的距离
|
||||
* 减少了翻页节流时间
|
||||
## 1.2.6(2022-02-14)
|
||||
* 修复向前点击翻页出现2种动画的bug
|
||||
## 1.2.5(2021-10-19)
|
||||
* 优化整书模式一些问题
|
||||
## 1.2.4(2021-10-18)
|
||||
* 修复滚动模式定位问题
|
||||
## 1.2.3(2021-10-17)
|
||||
* 添加点击区域配置,具体见下面得介绍
|
||||
## 1.2.2(2021-10-17)
|
||||
* 优化滚动模式
|
||||
## 1.2.1(2021-10-15)
|
||||
* 优化滚动模式逻辑
|
||||
## 1.2.0(2021-10-15)
|
||||
* 修复滚动模式下得问题
|
||||
## 1.1.9(2021-10-15)
|
||||
* 修复一些显示问题
|
||||
* 优化了一下滚动模式滚动效果
|
||||
## 1.1.8(2021-10-14)
|
||||
* content对象中可以带上章节名称,方便取值
|
||||
* 增加初始化loading效果
|
||||
## 1.1.7(2021-10-14)
|
||||
* 增加整书模式返回得页面信息
|
||||
## 1.1.6(2021-10-14)
|
||||
* 更改整书模式返回章节集合得参数
|
||||
## 1.1.5(2021-10-14)
|
||||
* 优化翻页得性能,同时最多显示3张页面
|
||||
## 1.1.4(2021-10-13)
|
||||
修复一些问题
|
||||
## 1.1.3(2021-10-13)
|
||||
* 修复一些问题
|
||||
## 1.1.2(2021-10-13)
|
||||
* 修复一些问题
|
||||
## 1.1.1(2021-10-13)
|
||||
* 修复了一些问题
|
||||
## 1.1.0(2021-10-12)
|
||||
* 此次更新为重构插件
|
||||
* 首先感谢一下2位插件使用者提供的反馈
|
||||
* 主要更新如下:
|
||||
- 1、为请求事件增加了过渡效果,滚动模式下增加上拉和下拉操作
|
||||
- 2、解决了一些逻辑问题,现在preload和loadmore事件触发更合理
|
||||
- 3、其它都是些小更新,比如章节模式下currentChange事件增加了返回的页面信息
|
||||
- 4、更改了参数结构,请注意阅读使用须知
|
||||
## 1.0.5(2021-09-22)
|
||||
* 修复上次更新引起无法翻到上一页的问题
|
||||
## 1.0.4(2021-09-22)
|
||||
* 修复横向翻页部分页面不绘制的bug
|
||||
## 1.0.3(2021-09-05)
|
||||
* 修复整书模式下阅读小说到最后面,下一页会从小说最前面再次排版的bug
|
||||
## 1.0.2(2021-09-04)
|
||||
* 修复预加载章节内容无效的问题
|
||||
* 添加初始化触发currentChange事件
|
||||
## 1.0.1(2021-08-29)
|
||||
* 减少滚动模式下触底加载更多时的滚动距离
|
||||
## 1.0.0(2021-08-28)
|
||||
* 发布1.0.0版本
|
116
uni_modules/yingbing-ReadPage/components/modules/battery.vue
Normal file
@ -0,0 +1,116 @@
|
||||
<template>
|
||||
<view class="yingbing-battery">
|
||||
<view class="yingbing-battery-wrapper" :style="{
|
||||
'border-color': color
|
||||
}">
|
||||
<view class="yingbing-battery-content">
|
||||
<view :style="{
|
||||
flex: 1,
|
||||
'background-color': color,
|
||||
width: value + 'rpx'
|
||||
}"></view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
color: {
|
||||
type: String,
|
||||
default: '#333'
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
value: 54
|
||||
}
|
||||
},
|
||||
// #ifdef APP-PLUS
|
||||
beforeDestroy() {
|
||||
if ( this.recevier ) {
|
||||
plus.android.runtimeMainActivity().unregisterReceiver(this.recevier)
|
||||
}
|
||||
},
|
||||
// #endif
|
||||
mounted () {
|
||||
this.getBattery()
|
||||
},
|
||||
methods: {
|
||||
getBattery () {
|
||||
// #ifdef H5
|
||||
window.navigator.getBattery().then((res) => {
|
||||
// 电池电量在0到1之间,因此我们将其乘以100得出百分比
|
||||
this.value = res.level * 54
|
||||
});
|
||||
// #endif
|
||||
// #ifdef MP-WEIXIN
|
||||
wx.getBatteryInfo({
|
||||
success: (res) => {
|
||||
this.value = (res.level / 100) * 54
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
// #ifdef APP-PLUS
|
||||
uni.getSystemInfo({
|
||||
success: (res) => {
|
||||
if ( res.osName == 'android' ) {
|
||||
const main = plus.android.runtimeMainActivity()
|
||||
const Intent = plus.android.importClass('android.content.Intent');
|
||||
this.recevier = plus.android.implements('io.dcloud.feature.internal.reflect.BroadcastReceiver', {
|
||||
onReceive: (context, intent) => {
|
||||
let action = intent.getAction();
|
||||
if (action == Intent.ACTION_BATTERY_CHANGED) {
|
||||
let level = intent.getIntExtra("level", 0); //电量 B5教程网
|
||||
this.value = (level / 100) * 54
|
||||
main.unregisterReceiver(this.recevier)//销毁注册广播
|
||||
//let voltage = intent.getIntExtra("voltage", 0); //电池电压
|
||||
//let temperature = intent.getIntExtra("temperature", 0); //电池温度
|
||||
//如需获取别的,在这里继续写,此代码只提供获取电量
|
||||
}
|
||||
}
|
||||
});
|
||||
const filter = plus.android.newObject('android.content.IntentFilter', Intent.ACTION_BATTERY_CHANGED);
|
||||
main.registerReceiver(this.recevier, filter);
|
||||
} else if ( res.osName == 'ios' ) {
|
||||
const UIDevice = plus.ios.import("UIDevice");
|
||||
const dev = UIDevice.currentDevice();
|
||||
if (!dev.isBatteryMonitoringEnabled()) {
|
||||
dev.setBatteryMonitoringEnabled(true);
|
||||
}
|
||||
let level = dev.batteryLevel();
|
||||
this.value = level * 54
|
||||
}
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.yingbing-battery-wrapper {
|
||||
width: 60rpx;
|
||||
height: 24rpx;
|
||||
border-width: 1px;
|
||||
border-style: solid;
|
||||
padding: 2rpx;
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
/* #endif */
|
||||
}
|
||||
.yingbing-battery-content {
|
||||
flex: 1;
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
/* #endif */
|
||||
}
|
||||
</style>
|
@ -0,0 +1,366 @@
|
||||
import Util from '../../../js_sdk/util.js'
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
computedResolve: null
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
reset (data, pages = []) {
|
||||
if ( data.custom && data.custom.length > 0 ) {
|
||||
pages.length > 0 ? pages[pages.length - 1].isLastPage = false : null
|
||||
data.custom.forEach(custom => {
|
||||
let type = ''
|
||||
if ( custom.indexOf('slot:') > -1 ) {
|
||||
type = 'slot'
|
||||
custom = custom.split(':')[1]
|
||||
} else {
|
||||
type = 'custom'
|
||||
let clicks = custom.match(/onclick=\"*([\s\S]*?)\"/ig);
|
||||
if ( clicks ) {
|
||||
clicks.forEach(click => {
|
||||
let name = click.match(/onclick=\"*([\s\S]*?)(\(|\")/)[1]
|
||||
let func = click.match(/onclick=\"*([\s\S]*?)\"/)
|
||||
let args = func[1].replace(name, '')
|
||||
args = args ? args.slice(1, args.length - 1).replace(/\s/g, '') : ''
|
||||
custom = custom.replace(func[0], `onclick="triggerCustomClick('${name}', [${args}])"`)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
let end = pages.length > 0 ? pages[pages.length - 1].end : 0
|
||||
pages.push({
|
||||
chapter: data.chapter,
|
||||
title: data.title || '',
|
||||
type: type,
|
||||
dataId: data.chapter * 100000 + end,
|
||||
start: end,
|
||||
end: end + 10,
|
||||
isLastPage: false,
|
||||
text: custom
|
||||
})
|
||||
})
|
||||
pages[pages.length - 1].isLastPage = true
|
||||
}
|
||||
this.computedResolve(pages)
|
||||
this.computedResolve = null
|
||||
},
|
||||
measureText (text, fontSize=10) {
|
||||
text = new String(text);
|
||||
text = text.split('');
|
||||
let width = 0;
|
||||
text.forEach(function(item) {
|
||||
if (/[a-zA-Z]/.test(item)) {
|
||||
width += 7;
|
||||
} else if (/[0-9]/.test(item)) {
|
||||
width += 5.5;
|
||||
} else if (/\./.test(item)) {
|
||||
width += 2.7;
|
||||
} else if (/-/.test(item)) {
|
||||
width += 3.25;
|
||||
} else if (/[\u4e00-\u9fa5]/.test(item)) { //中文匹配
|
||||
width += 10;
|
||||
} else if (/\(|\)/.test(item)) {
|
||||
width += 3.73;
|
||||
} else if (/\s/.test(item)) {
|
||||
width += 2.5;
|
||||
} else if (/[`!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?~]/.test(item)) {
|
||||
width += 8;
|
||||
} else {
|
||||
width += 10;
|
||||
}
|
||||
});
|
||||
return width * fontSize / 10;
|
||||
},
|
||||
async computedText (data, start) {
|
||||
let rect = await this.getRect()
|
||||
let viewWidth = rect.width - (this.options.slide * 2)
|
||||
let viewHeight = rect.height - this.options.topGap - this.options.bottomGap
|
||||
if ( this.options.headerShow ) viewHeight = viewHeight - uni.upx2px(50)
|
||||
if ( this.options.footerShow ) viewHeight = viewHeight - uni.upx2px(50)
|
||||
let pageHeight = this.options.fontSize + this.options.lineHeight;
|
||||
let strs = [];
|
||||
let page = {
|
||||
title: data.title || '',
|
||||
chapter: data.chapter,
|
||||
type: 'text',
|
||||
dataId: data.chapter * 100000 + start,
|
||||
start: start,
|
||||
end: 0,
|
||||
isLastPage: false,
|
||||
text: []
|
||||
}
|
||||
let length = 0;
|
||||
let contentSync = data.content.substr(start);
|
||||
let lastIndex = 0;
|
||||
while ( (pageHeight + this.options.fontSize + this.options.lineHeight) <= viewHeight ) {
|
||||
strs.push('');
|
||||
let lineWidth = 0;
|
||||
for ( let i = lastIndex; i < contentSync.length; i++ ) {
|
||||
if ( JSON.stringify(contentSync[i]) == JSON.stringify('\r') || JSON.stringify(contentSync[i]) == JSON.stringify('\n') ) {
|
||||
length += 1
|
||||
page.end = page.start + length;
|
||||
lastIndex = i + 1;
|
||||
break;
|
||||
}
|
||||
lineWidth += JSON.stringify(contentSync[i]) == JSON.stringify('\t') ? 0 : this.measureText(contentSync[i], this.options.fontSize);
|
||||
if ( lineWidth >= viewWidth ) {
|
||||
lastIndex = i;
|
||||
break;
|
||||
} else {
|
||||
if ( JSON.stringify(contentSync[i]) != JSON.stringify('\t') ) {
|
||||
strs[strs.length - 1] += contentSync[i].replace(' ', ' ')
|
||||
length += 1
|
||||
page.end = page.start + length
|
||||
}
|
||||
}
|
||||
}
|
||||
pageHeight += this.options.fontSize + this.options.lineHeight;
|
||||
if ( page.end >= data.content.replace(/\t/g, '').length - 1 ) {
|
||||
page.isLastPage = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
page.text = strs;
|
||||
return page;
|
||||
},
|
||||
getRect () {
|
||||
return new Promise (resolve => {
|
||||
// #ifndef APP-NVUE
|
||||
const query = uni.createSelectorQuery().in(this);
|
||||
query.select('.yingbing-read-page').boundingClientRect(data => {
|
||||
resolve(data)
|
||||
}).exec();
|
||||
// #endif
|
||||
// #ifdef APP-NVUE
|
||||
uni.requireNativePlugin('dom').getComponentRect(this.$refs.yingbingReadPage, res => {
|
||||
resolve(res.size)
|
||||
})
|
||||
// #endif
|
||||
})
|
||||
},
|
||||
getPages (data) {
|
||||
let pages = [];
|
||||
const doWhile = (start = 0) => {
|
||||
this.computedText(data, start).then(page => {
|
||||
pages.push(page);
|
||||
if ( page.isLastPage ) {
|
||||
this.reset(data, pages)
|
||||
} else {
|
||||
doWhile(page.end);
|
||||
}
|
||||
});
|
||||
}
|
||||
doWhile();
|
||||
},
|
||||
computedChapter(data) {
|
||||
return new Promise(resolve => {
|
||||
this.computedResolve = resolve
|
||||
data.content ? this.getPages(data) : this.reset(data)
|
||||
})
|
||||
},
|
||||
//绘制页面
|
||||
resetPage(data) {
|
||||
setTimeout(() => {
|
||||
//一次最多渲染3章的内容,根据定位的章节剪切出3章内容渲染
|
||||
let currentChapter = data.currentChapter || this.contents[0].chapter
|
||||
let nowIndex = this.contents.findIndex(item => item.chapter == currentChapter);
|
||||
let prevIndex = -1;
|
||||
let nextIndex = -1;
|
||||
let contents = [];
|
||||
if (!this.contents[nowIndex].isStart) prevIndex = this.contents.findIndex(item => item.chapter == currentChapter - 1);
|
||||
if (!this.contents[nowIndex].isEnd) nextIndex = this.contents.findIndex(item => item.chapter == currentChapter + 1);
|
||||
if (prevIndex > -1) {
|
||||
contents.push(this.contents[prevIndex])
|
||||
}
|
||||
contents.push(this.contents[nowIndex])
|
||||
if (nextIndex > -1) {
|
||||
contents.push(this.contents[nextIndex])
|
||||
}
|
||||
let arr = [];
|
||||
const dowhile = (i) => {
|
||||
let item = contents[i];
|
||||
this.computedChapter(item).then(pages => {
|
||||
if (currentChapter == item.chapter) {
|
||||
let index = Object.keys(pages).findIndex(key => data.start >= pages[key].start && data.start < pages[key].end)
|
||||
this.currentDataId = pages[index > -1 ? index : 0].dataId;
|
||||
}
|
||||
arr = arr.concat(pages)
|
||||
if (i == contents.length - 1) {
|
||||
if ( this.options.pageType != 'scroll' ) {
|
||||
arr.unshift({
|
||||
title: contents[0].title || '',
|
||||
chapter: contents[0].chapter,
|
||||
type: contents[0].isStart ? 'top' : 'prevLoading',
|
||||
dataId: arr[0].dataId - 1,
|
||||
start: 0,
|
||||
end: 0
|
||||
})
|
||||
arr.push({
|
||||
title: item.title || '',
|
||||
chapter: item.chapter,
|
||||
type: item.isEnd ? 'bottom' : 'nextLoading',
|
||||
dataId: arr[arr.length - 1].dataId + 1,
|
||||
start: 0,
|
||||
end: 0
|
||||
})
|
||||
}
|
||||
this.pages = arr
|
||||
if ( this.options.pageType == 'scroll' ) {
|
||||
this.$refs.list.scrollTo(0)
|
||||
}
|
||||
this.$nextTick(() => {
|
||||
if ( this.options.pageType != 'scroll' ) {
|
||||
this.onChange(this.currentDataId);
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
Util.getRect('#scroll-item_' + this.currentDataId, Util.getRefs(this, 'scrollItem_' + this.currentDataId, 0), this).then(rect => {
|
||||
this.$refs.list.scrollTo(rect.top)
|
||||
})
|
||||
}, 50)
|
||||
}
|
||||
this.initLoading = false;
|
||||
this.preload(currentChapter);
|
||||
})
|
||||
} else {
|
||||
setTimeout(() => {
|
||||
dowhile(i + 1)
|
||||
}, 100)
|
||||
}
|
||||
})
|
||||
}
|
||||
dowhile(0)
|
||||
}, 50)
|
||||
},
|
||||
computedPage(e) {
|
||||
this.computedChapter(e.content).then((pages) => {
|
||||
let arr = [];
|
||||
let newPages = [];
|
||||
const pagesSync = e.type == 'prev' ? pages.concat(this.pages) : this.pages.concat(pages);
|
||||
pagesSync.forEach(item => {
|
||||
if (arr.indexOf(item.chapter) == -1) arr.push(item.chapter)
|
||||
})
|
||||
if (arr.length > 3) {
|
||||
let reChapter = e.type == 'prev' ? pagesSync[pagesSync.length - 1].chapter : pagesSync[0].chapter;
|
||||
newPages = pagesSync.filter(item => item.chapter != reChapter && (item.type == 'text' || item.type == 'custom' || item.type == 'slot'));
|
||||
} else {
|
||||
newPages = pagesSync.filter(item => (item.type == 'text' || item.type == 'custom' || item.type == 'slot'));
|
||||
}
|
||||
if ( this.options.pageType != 'scroll' ) {
|
||||
const prevIndex = this.contents.findIndex(content => content.chapter == newPages[0].chapter);
|
||||
const nextIndex = this.contents.findIndex(content => content.chapter == newPages[newPages.length - 1].chapter);
|
||||
newPages.unshift({
|
||||
title: this.contents[prevIndex].title || '',
|
||||
chapter: this.contents[prevIndex].chapter,
|
||||
type: this.contents[prevIndex].isStart ? 'top' : 'prevLoading',
|
||||
dataId: newPages[0].dataId - 1,
|
||||
start: 0,
|
||||
end: 0
|
||||
})
|
||||
newPages.push({
|
||||
title: this.contents[nextIndex].title || '',
|
||||
chapter: this.contents[nextIndex].chapter,
|
||||
type: this.contents[nextIndex].isEnd ? 'bottom' : 'nextLoading',
|
||||
dataId: newPages[newPages.length - 1].dataId + 1,
|
||||
start: 0,
|
||||
end: 0
|
||||
})
|
||||
this.pages = newPages
|
||||
const nowIndex = newPages.findIndex(page => page.dataId == this.currentDataId);
|
||||
if ( nowIndex == -1 ) {
|
||||
this.currentDataId = e.type == 'next' ? pages[0].dataId : pages[pages.length - 1].dataId;
|
||||
this.onChange(this.currentDataId)
|
||||
}
|
||||
} else {
|
||||
let dataId = e.type == 'prev' ? this.pages[0].dataId : this.pages[this.pages.length-1].dataId
|
||||
Util.getRect('.scroll-item-wrapper', this.$refs.scrollItemWrapper, this).then(rect => {
|
||||
let lastHeight = rect.height
|
||||
this.pages = e.type == 'prev' ? pages.concat(this.pages) : this.pages.concat(pages)
|
||||
if ( e.type == 'prev' ) {
|
||||
this.$nextTick(function () {
|
||||
setTimeout(() => {
|
||||
Util.getRect('.scroll-item-wrapper', this.$refs.scrollItemWrapper, this).then(rect => {
|
||||
this.$refs.list.scrollTo(rect.height - lastHeight)
|
||||
})
|
||||
}, 50)
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
},
|
||||
//预加载章节
|
||||
preload (chapter) {
|
||||
if ( !this.enablePreload ) return false
|
||||
const nowIndex = this.contents.findIndex(item => item.chapter == chapter);
|
||||
let prevIndex = -2;
|
||||
let nextIndex = -2;
|
||||
let chapters = [];
|
||||
if ( !this.contents[nowIndex].isStart ) prevIndex = this.contents.findIndex(item => item.chapter == chapter - 1);
|
||||
if ( !this.contents[nowIndex].isEnd ) nextIndex = this.contents.findIndex(item => item.chapter == chapter + 1);
|
||||
if ( prevIndex == -1 ) {
|
||||
chapters.push(chapter - 1);
|
||||
}
|
||||
if ( nextIndex == -1 ) {
|
||||
chapters.push(chapter + 1);
|
||||
}
|
||||
if ( chapters.length > 0 ) {
|
||||
this.$emit('preload', chapters, (status, contents) => {
|
||||
if (status == 'success') {
|
||||
contents.forEach(item => {
|
||||
const index = this.contents.findIndex(content => content.chapter == item.chapter)
|
||||
if (index > -1) {
|
||||
this.contents[index] = item;
|
||||
} else {
|
||||
this.contents.push(item);
|
||||
}
|
||||
})
|
||||
}
|
||||
})
|
||||
}
|
||||
},
|
||||
filterPage (pageInfo) {
|
||||
if ( pageInfo && pageInfo.dataId > -1 ) {
|
||||
const nowChapters = this.pages.filter(item => item.chapter == pageInfo.chapter && (item.type == 'text' || item.type == 'custom' || item.type == 'slot'))
|
||||
let currentPage = nowChapters.findIndex(item => item.dataId == pageInfo.dataId)
|
||||
if ( currentPage > -1 ) {
|
||||
return (currentPage + 1) + ' / ' + nowChapters.length
|
||||
} else {
|
||||
return pageInfo.type == 'top' ? '最前面' : pageInfo.type == 'bottom' ? '最后面' : pageInfo.type.indexOf('Loading') > -1 ? '请等待' : ''
|
||||
}
|
||||
} else {
|
||||
return '加载中'
|
||||
}
|
||||
},
|
||||
filterDate () {
|
||||
let date = new Date()
|
||||
return Util.zeroize(date.getHours()) + ':' + Util.zeroize(date.getMinutes())
|
||||
},
|
||||
//翻往上一页
|
||||
pagePrev () {
|
||||
if ( this.options.pageType != 'scroll' ) {
|
||||
// #ifndef APP-NVUE
|
||||
this.pagePrevWxs()
|
||||
// #endif
|
||||
// #ifdef APP-NVUE
|
||||
this.pagePrevBinding()
|
||||
// #endif
|
||||
} else {
|
||||
this.scrollPrev()
|
||||
}
|
||||
},
|
||||
//翻往下一页
|
||||
pageNext () {
|
||||
if ( this.options.pageType != 'scroll' ) {
|
||||
// #ifndef APP-NVUE
|
||||
this.pageNextWxs()
|
||||
// #endif
|
||||
// #ifdef APP-NVUE
|
||||
this.pageNextBinding()
|
||||
// #endif
|
||||
} else {
|
||||
this.scrollNext()
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
@ -0,0 +1,379 @@
|
||||
<template>
|
||||
<!-- #ifdef APP-VUE || H5 -->
|
||||
<div class="yingbing-computed-page" :prop="computedPageProp" :change:prop="computedPage.propWatcher" :style="{
|
||||
'padding-left': options.slide + 'px',
|
||||
'padding-right': options.slide + 'px',
|
||||
'padding-top': options.topGap + 'px',
|
||||
'padding-bottom': options.bottomGap + 'px'
|
||||
}">
|
||||
<view class="yingbing-computed-page-header" v-if="options.headerShow"></view>
|
||||
<view class="yingbing-computed-page-content"></view>
|
||||
<view class="yingbing-computed-page-footer" v-if="options.footerShow"></view>
|
||||
</div>
|
||||
<!-- #endif -->
|
||||
<!-- #ifndef APP-VUE || H5 -->
|
||||
<div class="yingbing-computed-page" :style="{
|
||||
'padding-left': options.slide + 'px',
|
||||
'padding-right': options.slide + 'px',
|
||||
'padding-top': options.topGap + 'px',
|
||||
'padding-bottom': options.bottomGap + 'px'
|
||||
}">
|
||||
<view class="yingbing-computed-page-header" v-if="options.headerShow"></view>
|
||||
<view class="yingbing-computed-page-content" ref="yingbingComputedPageContent"></view>
|
||||
<view class="yingbing-computed-page-footer" v-if="options.footerShow"></view>
|
||||
</div>
|
||||
<!-- #endif -->
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
options: {
|
||||
type: Object,
|
||||
default () {
|
||||
return new Object
|
||||
}
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
content: '',
|
||||
custom: [],
|
||||
isStart: false,
|
||||
chapter: null,
|
||||
title: '',
|
||||
resolve: null
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
computedPageProp () {
|
||||
return {
|
||||
isStart: this.isStart,
|
||||
fontSize: this.options.fontSize,
|
||||
lineHeight: this.options.lineHeight,
|
||||
pageType: this.options.pageType,
|
||||
content: this.content,
|
||||
chapter: this.chapter,
|
||||
title: this.title
|
||||
}
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
computed ({content, chapter, custom, title}) {
|
||||
return new Promise((resolve) => {
|
||||
this.content = content || '';
|
||||
this.custom = custom || [];
|
||||
this.chapter = chapter || null;
|
||||
this.title = title || '';
|
||||
this.resolve = resolve;
|
||||
// #ifdef APP-VUE || H5
|
||||
content ? this.isStart = true : this.reset();
|
||||
// #endif
|
||||
// #ifndef APP-VUE || H5
|
||||
content ? this.getPages() : this.reset();
|
||||
// #endif
|
||||
})
|
||||
},
|
||||
reset (pages = []) {
|
||||
if ( this.custom.length > 0 ) {
|
||||
pages.length > 0 ? pages[pages.length - 1].isLastPage = false : null
|
||||
this.custom.forEach(custom => {
|
||||
let type = ''
|
||||
if ( custom.indexOf('slot:') > -1 ) {
|
||||
type = 'slot'
|
||||
custom = custom.split(':')[1]
|
||||
} else {
|
||||
type = 'custom'
|
||||
let clicks = custom.match(/onclick=\"*([\s\S]*?)\"/ig);
|
||||
if ( clicks ) {
|
||||
clicks.forEach(click => {
|
||||
let name = click.match(/onclick=\"*([\s\S]*?)(\(|\")/)[1]
|
||||
let func = click.match(/onclick=\"*([\s\S]*?)\"/)
|
||||
let args = func[1].replace(name, '')
|
||||
args = args ? args.slice(1, args.length - 1).replace(/\s/g, '') : ''
|
||||
custom = custom.replace(func[0], `onclick="triggerCustomClick('${name}', [${args}])"`)
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
let end = pages.length > 0 ? pages[pages.length - 1].end : 0
|
||||
pages.push({
|
||||
chapter: this.chapter,
|
||||
title: this.title,
|
||||
type: type,
|
||||
dataId: this.chapter * 100000 + end,
|
||||
start: end,
|
||||
end: end + 10,
|
||||
isLastPage: false,
|
||||
text: custom
|
||||
})
|
||||
})
|
||||
pages[pages.length - 1].isLastPage = true
|
||||
}
|
||||
this.resolve(pages);
|
||||
this.resolve = null
|
||||
this.isStart = false
|
||||
this.content = ''
|
||||
this.title = ''
|
||||
this.chapter = null
|
||||
this.start = null
|
||||
this.end = null
|
||||
},
|
||||
// #ifndef APP-VUE || H5
|
||||
measureText (text, fontSize=10) {
|
||||
text = new String(text);
|
||||
text = text.split('');
|
||||
let width = 0;
|
||||
text.forEach(function(item) {
|
||||
if (/[a-zA-Z]/.test(item)) {
|
||||
width += 7;
|
||||
} else if (/[0-9]/.test(item)) {
|
||||
width += 5.5;
|
||||
} else if (/\./.test(item)) {
|
||||
width += 2.7;
|
||||
} else if (/-/.test(item)) {
|
||||
width += 3.25;
|
||||
} else if (/[\u4e00-\u9fa5]/.test(item)) { //中文匹配
|
||||
width += 10;
|
||||
} else if (/\(|\)/.test(item)) {
|
||||
width += 3.73;
|
||||
} else if (/\s/.test(item)) {
|
||||
width += 2.5;
|
||||
} else if (/[`!@#$%^&*()_+\-=\[\]{};':"\\|,.<>\/?~]/.test(item)) {
|
||||
width += 8;
|
||||
} else {
|
||||
width += 10;
|
||||
}
|
||||
});
|
||||
return width * fontSize / 10;
|
||||
},
|
||||
async computedText (start) {
|
||||
let rect = await this.getRect()
|
||||
let viewWidth = rect.width;
|
||||
let viewHeight = rect.height;
|
||||
let pageHeight = this.options.fontSize + this.options.lineHeight;
|
||||
let strs = [];
|
||||
let page = {
|
||||
title: this.title,
|
||||
chapter: this.chapter,
|
||||
type: 'text',
|
||||
dataId: this.chapter * 100000 + start,
|
||||
start: start,
|
||||
end: 0,
|
||||
isLastPage: false,
|
||||
text: []
|
||||
}
|
||||
let length = 0;
|
||||
let contentSync = this.content.substr(start);
|
||||
let lastIndex = 0;
|
||||
while ( (pageHeight + this.options.fontSize + this.options.lineHeight) <= viewHeight ) {
|
||||
strs.push('');
|
||||
let lineWidth = 0;
|
||||
for ( let i = lastIndex; i < contentSync.length; i++ ) {
|
||||
if ( JSON.stringify(contentSync[i]) == JSON.stringify('\r') || JSON.stringify(contentSync[i]) == JSON.stringify('\n') ) {
|
||||
length += 1
|
||||
page.end = page.start + length;
|
||||
lastIndex = i + 1;
|
||||
break;
|
||||
}
|
||||
lineWidth += JSON.stringify(contentSync[i]) == JSON.stringify('\t') ? 0 : this.measureText(contentSync[i], this.options.fontSize);
|
||||
if ( lineWidth >= viewWidth ) {
|
||||
lastIndex = i;
|
||||
break;
|
||||
} else {
|
||||
if ( JSON.stringify(contentSync[i]) != JSON.stringify('\t') ) {
|
||||
strs[strs.length - 1] += contentSync[i].replace(' ', ' ')
|
||||
length += 1
|
||||
page.end = page.start + length
|
||||
}
|
||||
}
|
||||
}
|
||||
pageHeight += this.options.fontSize + this.options.lineHeight;
|
||||
if ( page.end >= this.content.length - 1 ) {
|
||||
page.isLastPage = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
page.text = strs;
|
||||
return page;
|
||||
},
|
||||
getRect () {
|
||||
return new Promise (resolve => {
|
||||
// #ifndef APP-NVUE
|
||||
const query = uni.createSelectorQuery().in(this);
|
||||
query.select('.yingbing-computed-page-content').boundingClientRect(data => {
|
||||
resolve(data)
|
||||
}).exec();
|
||||
// #endif
|
||||
// #ifdef APP-NVUE
|
||||
uni.requireNativePlugin('dom').getComponentRect(this.$refs.yingbingComputedPageContent, res => {
|
||||
resolve(res.size)
|
||||
})
|
||||
// #endif
|
||||
})
|
||||
},
|
||||
getPages () {
|
||||
let pages = [];
|
||||
const doWhile = (start = 0) => {
|
||||
this.computedText(start).then(page => {
|
||||
pages.push(page);
|
||||
if ( page.isLastPage ) {
|
||||
this.reset(pages)
|
||||
} else {
|
||||
doWhile(page.end);
|
||||
}
|
||||
});
|
||||
}
|
||||
doWhile();
|
||||
}
|
||||
// #endif
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- #ifdef H5 || APP-VUE -->
|
||||
<script lang="renderjs" type="module" module="computedPage">
|
||||
let myComputedPageDom;
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
viewWidth: 0,
|
||||
viewHeight: 0
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.initDom.bind(this);
|
||||
},
|
||||
methods: {
|
||||
initDom () {
|
||||
myComputedPageDom = computedPage.init(document.getElementsByClassName('yingbing-computed-page')[0]);
|
||||
// 观测更新的数据在 view 层可以直接访问到
|
||||
myComputedPageDom.setOption(this.computedPageProp);
|
||||
},
|
||||
//计算页面显示文字
|
||||
computedText (start) {
|
||||
let parent = document.getElementsByClassName('yingbing-computed-page-content')[0];
|
||||
this.viewWidth = parent.offsetWidth;
|
||||
this.viewHeight = parent.offsetHeight;
|
||||
let computedCanvas = this.createComputedCanvas(parent);
|
||||
let context = computedCanvas.getContext('2d');
|
||||
context.font = this.computedPageProp.fontSize + 'px 微软雅黑';
|
||||
context.lineWidth = 1;
|
||||
let pageHeight = this.computedPageProp.fontSize + this.computedPageProp.lineHeight;
|
||||
let strs = [];
|
||||
let page = {
|
||||
title: this.computedPageProp.title,
|
||||
chapter: this.computedPageProp.chapter,
|
||||
type: 'text',
|
||||
dataId: this.computedPageProp.chapter * 100000 + start,
|
||||
start: start,
|
||||
end: 0,
|
||||
isLastPage: false,
|
||||
text: []
|
||||
}
|
||||
let length = 0;
|
||||
let contentSync = this.computedPageProp.content.substr(start);
|
||||
let lastIndex = 0;
|
||||
while ( (pageHeight + this.computedPageProp.fontSize + this.computedPageProp.lineHeight) <= this.viewHeight ) {
|
||||
strs.push('');
|
||||
let lineWidth = 0;
|
||||
for ( let i = lastIndex; i < contentSync.length; i++ ) {
|
||||
if ( JSON.stringify(contentSync[i]) == JSON.stringify('\r') || JSON.stringify(contentSync[i]) == JSON.stringify('\n') ) {
|
||||
length += 1
|
||||
page.end = page.start + length;
|
||||
lastIndex = i + 1;
|
||||
break;
|
||||
}
|
||||
lineWidth += (JSON.stringify(contentSync[i]) == JSON.stringify('\t') ? 0 : context.measureText(contentSync[i]).width);
|
||||
if ( lineWidth >= this.viewWidth ) {
|
||||
lastIndex = i;
|
||||
break;
|
||||
} else {
|
||||
if ( JSON.stringify(contentSync[i]) != JSON.stringify('\t') ) {
|
||||
strs[strs.length - 1] += contentSync[i];
|
||||
length += 1;
|
||||
page.end = page.start + length;
|
||||
}
|
||||
}
|
||||
}
|
||||
pageHeight += (this.computedPageProp.fontSize + this.computedPageProp.lineHeight);
|
||||
if ( page.end >= this.computedPageProp.content.length - 1 ) {
|
||||
page.isLastPage = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
page.text = strs;
|
||||
return page;
|
||||
},
|
||||
//创建一个独立的canvas画板,用于计算文字布局
|
||||
createComputedCanvas (el) {
|
||||
if ( el.getElementsByClassName('computedCanvas')[0] ) return el.getElementsByClassName('computedCanvas')[0];
|
||||
let canvasDom = document.createElement('canvas');
|
||||
canvasDom.width = this.viewWidth;
|
||||
canvasDom.height = this.viewHeight;
|
||||
canvasDom.style.position = 'absolute';
|
||||
canvasDom.style.top = 0;
|
||||
canvasDom.style.left = 0;
|
||||
canvasDom.setAttribute('class', 'computedCanvas');
|
||||
el.appendChild(canvasDom);
|
||||
return el.getElementsByClassName('computedCanvas')[0];
|
||||
},
|
||||
//参数改变
|
||||
propWatcher (newValue, oldValue) {
|
||||
if ( newValue.isStart != (oldValue ? oldValue.isStart : false) ) {
|
||||
if ( newValue.isStart ) {
|
||||
this.getPages();
|
||||
}
|
||||
}
|
||||
},
|
||||
getPages () {
|
||||
let pages = [];
|
||||
const doWhile = (start = 0) => {
|
||||
let page = this.computedText(start);
|
||||
pages.push(page);
|
||||
if ( page.isLastPage ) {
|
||||
this.triggerReset(pages)
|
||||
} else {
|
||||
doWhile(page.end);
|
||||
}
|
||||
}
|
||||
doWhile();
|
||||
},
|
||||
triggerReset (pages) {
|
||||
// #ifndef H5
|
||||
this.$ownerInstance.callMethod('reset', pages);
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
this.reset(pages);
|
||||
// #endif
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<!-- #endif -->
|
||||
|
||||
<style scoped>
|
||||
.yingbing-computed-page {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
opacity: 0;
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
/* #endif */
|
||||
}
|
||||
.yingbing-computed-page-header {
|
||||
height: 50rpx;
|
||||
}
|
||||
.yingbing-computed-page-footer {
|
||||
height: 50rpx;
|
||||
}
|
||||
.yingbing-computed-page-content {
|
||||
flex: 1;
|
||||
}
|
||||
</style>
|
@ -0,0 +1,250 @@
|
||||
const Binding = uni.requireNativePlugin('bindingx')
|
||||
const animation = uni.requireNativePlugin('animation')
|
||||
import Util from '../../../js_sdk/util.js'
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
disableTouch: false,
|
||||
isTouch: false,
|
||||
flipTouchTime: 0,
|
||||
interval: false,
|
||||
direction: ''
|
||||
}
|
||||
},
|
||||
beforeDestroy () {
|
||||
if ( this.flip_binding ) {
|
||||
Binding.unbind({
|
||||
token: this.flip_binding.token,
|
||||
eventType: 'pan'
|
||||
})
|
||||
this.flip_binding = null
|
||||
}
|
||||
if ( this.flip_animation_binding ) {
|
||||
Binding.unbind({
|
||||
token: this.flip_animation_binding.token,
|
||||
eventType: 'timing'
|
||||
})
|
||||
this.flip_animation_binding = null
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
//翻往上一页
|
||||
pagePrevBinding () {
|
||||
if ( !this.isTouch && !this.disableTouch ) {
|
||||
this.isTouch = true
|
||||
this.startX = 0
|
||||
this.onFilpTouchend()
|
||||
}
|
||||
},
|
||||
//翻往下一页
|
||||
pageNextBinding () {
|
||||
if ( !this.isTouch && !this.disableTouch ) {
|
||||
this.isTouch = true
|
||||
this.startX = this.viewWidth
|
||||
this.onFilpTouchend()
|
||||
}
|
||||
},
|
||||
onFilpTouchstart (event) {
|
||||
if ( this.isTouch || this.disableTouch ) {
|
||||
return
|
||||
}
|
||||
this.isTouch = true
|
||||
this.flipTouchTime = 0
|
||||
this.interval = true
|
||||
this.setInterval()
|
||||
let touch = event.touches[0]
|
||||
this.startX = touch.pageX
|
||||
this.startY = touch.pageY
|
||||
},
|
||||
async onFilpTouchmove (event) {
|
||||
if ( this.isTouch && (this.pageType == 'real' || this.pageType == 'cover') && !this.disableTouch && this.flipTouchTime > 200 ) {
|
||||
if ( !this.direction ) {
|
||||
let touch = event.touches[0]
|
||||
if ( touch.pageX < this.startX ) {
|
||||
if ( this.nextDataId ) {
|
||||
this.direction = 'next'
|
||||
}
|
||||
} else {
|
||||
if ( this.prevDataId ) {
|
||||
this.direction = 'prev'
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( this.direction ) {
|
||||
this.disableTouch = true
|
||||
let currentDataId = this.direction == 'next' ? this.currentDataId : this.prevDataId
|
||||
let props = [{
|
||||
element: Util.getEl(this.$refs['flipItem_' + currentDataId][0]),
|
||||
property: 'transform.translateX',
|
||||
expression: `${this.direction == 'next' ? ('x > 0 ? 0 : (x < -' + this.viewWidth + ' ? -' + this.viewWidth + ' : x + 0)') : ('x < 0 ? -' + this.viewWidth + ' : (x > ' + this.viewWidth + ' ? 0 : x-' + this.viewWidth + ')')}`
|
||||
}]
|
||||
if ( this.pageType == 'real' ) {
|
||||
props.push({
|
||||
element: Util.getEl(this.$refs['flipItemWrapper_' + currentDataId][0]),
|
||||
property: 'transform.translateX',
|
||||
expression: `${this.direction == 'next' ? ('x > 0 ? 0 : (x < -' + this.viewWidth + ' ? ' + this.viewWidth + ' : 0 - x)') : ('x < 0 ? ' + this.viewWidth + ' : (x > ' + this.viewWidth + ' ? 0 : ' + this.viewWidth + '-x)')}`
|
||||
})
|
||||
props.push({
|
||||
element: Util.getEl(this.$refs['flipItemBg_' + currentDataId][0]),
|
||||
property: 'transform.translateX',
|
||||
expression: `${this.direction == 'next' ? ('x > 0 ? 0 : (x < -' + this.viewWidth + ' ? -' + this.viewWidth + ' : x + 0)') : ('x < 0 ? -' + this.viewWidth + ' : (x > ' + this.viewWidth + ' ? 0 : x-' + this.viewWidth + ')')}`
|
||||
})
|
||||
let rect = await this.getRect(this.$refs['flipItemBg_' + currentDataId][0])
|
||||
let height = rect.height / 2;
|
||||
let maxDeg = height / 5;
|
||||
props.push({
|
||||
element: Util.getEl(this.$refs['flipItemBg_' + currentDataId][0]),
|
||||
property: 'transform.rotateZ',
|
||||
expression: `${this.direction == 'next' ? 'y/' + maxDeg : '-(y/' + maxDeg + ')'}`
|
||||
})
|
||||
props.push({
|
||||
element: Util.getEl(this.$refs['flipItemShadow_' + currentDataId][0]),
|
||||
property: 'width',
|
||||
expression: `${this.direction == 'next' ? 'abs(x) / 2 + 0' : this.viewWidth / 2 + '-abs(x) / 2'}`
|
||||
})
|
||||
}
|
||||
this.flip_binding = Binding.bind({
|
||||
anchor: Util.getEl(this.$refs.yingbingFlip),
|
||||
eventType: 'pan',
|
||||
props: props
|
||||
}, (e) => {
|
||||
if ((e.state == 'end' || e.state == 'cancel') && this.flip_binding) {
|
||||
this.clearInterval()
|
||||
Binding.unbind({
|
||||
token: this.flip_binding.token,
|
||||
eventType: 'pan'
|
||||
})
|
||||
this.flip_binding = null
|
||||
let value = this.direction == 'next' ? 1 : -1;
|
||||
if (this.flipTouchTime <= 200) {
|
||||
let duration = (this.pageType == 'real' || this.pageType == 'cover') ? 200 : 1
|
||||
this.pageAnimation(value, -value * this.viewWidth, duration);
|
||||
} else {
|
||||
let duration = (this.pageType == 'real' || this.pageType == 'cover') ? 200 : 1
|
||||
let deltaX = Binding.getComputedStyle(Util.getEl(this.$refs['flipItem_' + currentDataId][0])).translateX
|
||||
let late = this.direction == 'next' ? deltaX : this.viewWidth + deltaX
|
||||
if (Math.abs(late) >= this.viewWidth / 4) {
|
||||
this.pageAnimation(value, -value * this.viewWidth, duration)
|
||||
} else {
|
||||
let value = this.direction == 'next' ? 1 : -1;
|
||||
this.pageAnimation(value, 0, duration);
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
} else {
|
||||
this.resetPageBinding()
|
||||
}
|
||||
}
|
||||
},
|
||||
onFilpTouchend () {
|
||||
if ( this.isTouch && !this.disableTouch ) {
|
||||
this.disableTouch = true
|
||||
this.clearInterval()
|
||||
if ( this.flipTouchTime <= 200 ) {
|
||||
if ( !this.direction ) {
|
||||
if (this.startX > (this.viewWidth / 4) * 3) {
|
||||
if ( this.nextDataId ) {
|
||||
this.direction = 'next'
|
||||
}
|
||||
}
|
||||
if (this.startX < (this.viewWidth / 4)) {
|
||||
if ( this.prevDataId ) {
|
||||
this.direction = 'prev'
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( this.direction ) {
|
||||
let duration = (this.pageType == 'real' || this.pageType == 'cover') ? 200 : 1
|
||||
let value = this.direction == 'next' ? 1 : -1;
|
||||
this.pageAnimation(value, -value * this.viewWidth, duration);
|
||||
} else {
|
||||
this.resetPageBinding()
|
||||
}
|
||||
} else {
|
||||
this.resetPageBinding()
|
||||
}
|
||||
}
|
||||
},
|
||||
getRect (el) {
|
||||
return new Promise(resolve => {
|
||||
uni.requireNativePlugin('dom').getComponentRect(el, res => {
|
||||
resolve(res.size)
|
||||
})
|
||||
})
|
||||
},
|
||||
setInterval () {
|
||||
this.flipTouchTimer = setTimeout(() => {
|
||||
this.flipTouchTime += 10
|
||||
if ( this.interval ) {
|
||||
this.setInterval()
|
||||
}
|
||||
}, 10)
|
||||
},
|
||||
clearInterval () {
|
||||
this.interval = false
|
||||
if ( this.flipTouchTimer ) {
|
||||
clearTimeout(this.flipTouchTimer)
|
||||
this.flipTouchTimer = null
|
||||
}
|
||||
},
|
||||
pageAnimation (value, offset, duration) {
|
||||
let currentDataId = this.direction == 'next' ? this.currentDataId : this.prevDataId
|
||||
let late = this.direction == 'next' ? offset : offset - this.viewWidth;
|
||||
let flipItemTrans = Binding.getComputedStyle(Util.getEl(this.$refs['flipItem_' + currentDataId][0])).translateX
|
||||
let props = [{
|
||||
element: Util.getEl(this.$refs['flipItem_' + currentDataId][0]),
|
||||
property: 'transform.translateX',
|
||||
expression: `linear(t, ${flipItemTrans}, ${late - flipItemTrans}, ${duration})`
|
||||
}]
|
||||
if ( this.pageType == 'real' ) {
|
||||
let flipItemWrapperTrans = Binding.getComputedStyle(Util.getEl(this.$refs['flipItemWrapper_' + currentDataId][0])).translateX
|
||||
props.push({
|
||||
element: Util.getEl(this.$refs['flipItemWrapper_' + currentDataId][0]),
|
||||
property: 'transform.translateX',
|
||||
expression: `linear(t, ${flipItemWrapperTrans}, ${-late - flipItemWrapperTrans}, ${duration})`
|
||||
})
|
||||
let flipItemBgTrans = Binding.getComputedStyle(Util.getEl(this.$refs['flipItemBg_' + currentDataId][0])).translateX
|
||||
props.push({
|
||||
element: Util.getEl(this.$refs['flipItemBg_' + currentDataId][0]),
|
||||
property: 'transform.translateX',
|
||||
expression: `linear(t, ${flipItemBgTrans}, ${late - flipItemBgTrans}, ${duration})`
|
||||
})
|
||||
let flipItemShadowWidth = this.flipTouchTime <= 200 && this.direction == 'prev' ? this.viewWidth : Binding.getComputedStyle(Util.getEl(this.$refs['flipItemShadow_' + currentDataId][0])).width
|
||||
props.push({
|
||||
element: Util.getEl(this.$refs['flipItemShadow_' + currentDataId][0]),
|
||||
property: 'width',
|
||||
expression: `linear(t, ${flipItemShadowWidth}, ${-late - flipItemShadowWidth}, ${duration})`
|
||||
})
|
||||
}
|
||||
this.flip_animation_binding = Binding.bind({
|
||||
eventType: 'timing',
|
||||
exitExpression: 't>' + duration,
|
||||
props: props
|
||||
}, (e) => {
|
||||
if (e.state == 'exit' && this.flip_animation_binding && e.t > duration) {
|
||||
Binding.unbind({
|
||||
token: this.flip_animation_binding.token,
|
||||
eventType: 'timing'
|
||||
})
|
||||
this.flip_animation_binding = null
|
||||
if ( Math.abs(offset) > 0 ) {
|
||||
this.onChange(value > 0 ? this.nextDataId : this.prevDataId)
|
||||
}
|
||||
this.resetPageBinding();
|
||||
}
|
||||
})
|
||||
},
|
||||
resetPageBinding () {
|
||||
this.direction = ''
|
||||
this.flipTouchTime = 0
|
||||
this.startX = 0
|
||||
this.startY = 0
|
||||
this.$nextTick(function () {
|
||||
this.isTouch = false
|
||||
this.disableTouch = false
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
134
uni_modules/yingbing-ReadPage/components/modules/flip/flip.css
Normal file
@ -0,0 +1,134 @@
|
||||
.yingbing-read-page-flip {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
.yingbing-flip {
|
||||
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 */
|
||||
}
|
||||
.flip-item {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
/* #ifndef APP-NVUE */
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
/* #endif */
|
||||
}
|
||||
.flip-item-wrapper {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
/* #ifndef APP-NVUE */
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
/* #endif */
|
||||
}
|
||||
.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: 30rpx;
|
||||
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 {
|
||||
flex: 1;
|
||||
/* #ifndef APP-NVUE */
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
/* #endif */
|
||||
}
|
||||
.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-item-bg {
|
||||
position: absolute;
|
||||
/* #ifdef APP-NVUE */
|
||||
box-shadow: 0 20rpx 20rpx rgba(0,0,0,0.2);
|
||||
/* #endif */
|
||||
}
|
||||
.flip-item-shadow {
|
||||
position: absolute;
|
||||
width: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
/* #ifdef APP-NVUE */
|
||||
background-image: linear-gradient(to right, rgba(255,255,255, 0), rgba(0,0,0,.5));
|
||||
opacity: 0.5;
|
||||
/* #endif */
|
||||
}
|
154
uni_modules/yingbing-ReadPage/components/modules/flip/flip.js
Normal file
@ -0,0 +1,154 @@
|
||||
import Util from '../../../js_sdk/util.js'
|
||||
// #ifdef APP-NVUE
|
||||
import FlipBindingx from './bindingx.js'
|
||||
// #endif
|
||||
export default {
|
||||
// #ifdef APP-NVUE
|
||||
mixins: [FlipBindingx],
|
||||
// #endif
|
||||
computed: {
|
||||
dataReverse () {
|
||||
let data = JSON.parse(JSON.stringify(this.pages))
|
||||
return data.reverse()
|
||||
},
|
||||
current () {
|
||||
return this.dataReverse.findIndex(item => item.dataId == this.currentDataId)
|
||||
},
|
||||
prevDataId () {
|
||||
return this.dataReverse[this.current + 1] && this.dataReverse[this.current + 1].dataId
|
||||
},
|
||||
nextDataId () {
|
||||
return this.dataReverse[this.current - 1] && this.dataReverse[this.current - 1].dataId
|
||||
},
|
||||
flipProp () {
|
||||
return {
|
||||
prevDataId: this.prevDataId,
|
||||
nextDataId: this.nextDataId,
|
||||
currentDataId: this.currentDataId,
|
||||
pageType: this.options.pageType,
|
||||
pageTo: this.pageTo
|
||||
}
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
currentDataId: -1,
|
||||
isShow: false,
|
||||
viewWidth: 0,
|
||||
viewHeight: 0,
|
||||
pageTo: 0,
|
||||
moreLoading: false,
|
||||
initLoading: true,
|
||||
loadingText: '正在加载内容',
|
||||
loadStatus: 'none',
|
||||
loadChapter: -1,
|
||||
loadValue: 0
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
if ( this.pageType != 'scroll' ) {
|
||||
this.$nextTick(function () {
|
||||
setTimeout(() => {
|
||||
this.getViewRect()
|
||||
}, 50)
|
||||
})
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
//翻往上一页
|
||||
pagePrevWxs () {
|
||||
this.pageTo = 0
|
||||
this.$nextTick(function(){
|
||||
this.pageTo = -1
|
||||
})
|
||||
},
|
||||
//翻往下一页
|
||||
pageNextWxs () {
|
||||
this.pageTo = 0
|
||||
this.$nextTick(function(){
|
||||
this.pageTo = 1
|
||||
})
|
||||
},
|
||||
reload () {
|
||||
if ( this.loadStatus == 'fail' || this.loadStatus == 'timeout' ) {
|
||||
this.initLoading = false
|
||||
this.loadingText = '正在加载内容'
|
||||
this.loadStatus = 'none';
|
||||
this.loadmore(this.loadChapter, this.loadValue);
|
||||
this.loadChapter = -1;
|
||||
this.loadValue = 0;
|
||||
}
|
||||
},
|
||||
loadmore (chapter, value) {
|
||||
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: value > 0 ? 'next' : 'prev'
|
||||
});
|
||||
this.preload(chapter)
|
||||
this.moreLoading = false;
|
||||
} else if ( status == 'fail' ) {
|
||||
this.loadStatus = status;
|
||||
this.loadingText = '请求失败,点击重试'
|
||||
this.initLoading = true
|
||||
this.loadChapter = chapter;
|
||||
this.loadValue = value;
|
||||
} else {
|
||||
this.loadStatus = status;
|
||||
this.loadingText = '请求超时,点击重试'
|
||||
this.initLoading = true
|
||||
this.loadChapter = chapter;
|
||||
this.loadValue = value;
|
||||
}
|
||||
})
|
||||
},
|
||||
getViewRect () {
|
||||
return new Promise(resolve => {
|
||||
Util.getRect('.yingbing-flip', this.$refs.yingbingFlip, this).then(res => {
|
||||
this.viewWidth = res.width
|
||||
this.viewHeight = res.height
|
||||
this.isShow = true
|
||||
resolve(true)
|
||||
})
|
||||
})
|
||||
},
|
||||
onChange(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.$emit('change', pageInfo, this.pages)
|
||||
const nextType = this.pages[index + value] && this.pages[index + value].type
|
||||
const loadings = ['nextLoading', 'prevLoading']
|
||||
if ( loadings.indexOf(this.pages[index].type) >-1 || loadings.indexOf(nextType) >-1) {
|
||||
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.loadmore(loadChapter, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
569
uni_modules/yingbing-ReadPage/components/modules/flip/flip.vue
Normal file
@ -0,0 +1,569 @@
|
||||
<template>
|
||||
<view
|
||||
class="yingbing-flip"
|
||||
ref="yingbingFlip"
|
||||
:style="{
|
||||
'background-color': options.bgColor
|
||||
}"
|
||||
:prop="flipProp"
|
||||
:change:prop="flip.propWatcher"
|
||||
@touchstart="flip.touchstart"
|
||||
@touchmove="flip.touchmove"
|
||||
@touchend="flip.touchend">
|
||||
<template v-if="isShow">
|
||||
<view v-if="(item.dataId == currentDataIdSync || item.dataId == prevDataId || item.dataId == nextDataId)"
|
||||
class="flip-item"
|
||||
:class="'flip-item_' + item.dataId"
|
||||
v-for="(item, index) in dataReverse"
|
||||
:key="item.dataId"
|
||||
:style="{
|
||||
'transform': item.dataId < currentDataIdSync ? `translateX(${-viewWidth}px)` : ''
|
||||
}">
|
||||
<view
|
||||
class="flip-item-wrapper"
|
||||
:class="'flip-item-wrapper_' + item.dataId"
|
||||
:style="{
|
||||
'padding-left': options.slide + 'px',
|
||||
'padding-right': options.slide + 'px',
|
||||
'padding-top': options.topGap + 'px',
|
||||
'padding-bottom': options.bottomGap + 'px',
|
||||
'background': options.bgColor,
|
||||
'transform': item.dataId < currentDataIdSync ? options.pageType == 'real' ? `translateX(${viewWidth}px)` : 'translateX(0)' : ''
|
||||
}">
|
||||
<view class="flip-item-header" v-if="options.headerShow">
|
||||
<text class="flip-item-header-text" :style="{
|
||||
color: options.color
|
||||
}">{{item.title}}</text>
|
||||
</view>
|
||||
<template v-if="item.type == 'text'">
|
||||
<view class="flip-item-text flip-item-content"
|
||||
>
|
||||
<text class="flip-text"
|
||||
v-for="(text, i) in item.text" :key="i"
|
||||
:style="{
|
||||
'margin-top': options.lineHeight + 'px',
|
||||
'height': options.fontSize + 'px',
|
||||
'font-size': options.fontSize + 'px',
|
||||
'color': options.color
|
||||
}">{{text}}</text>
|
||||
</view>
|
||||
</template>
|
||||
<template v-else-if="item.type == 'custom'">
|
||||
<view class="flip-custom flip-item-content" v-html="item.text">
|
||||
</view>
|
||||
</template>
|
||||
<template v-else-if="item.type == 'slot'">
|
||||
<view class="flip-slot flip-item-content">
|
||||
<slot :name="item.text"></slot>
|
||||
</view>
|
||||
</template>
|
||||
<template v-else-if="item.type == 'nextLoading' || item.type == 'prevLoading' ">
|
||||
<view class="flip-loading flip-item-content">
|
||||
<text :style="{
|
||||
'color': options.color,
|
||||
'font-size': options.fontSize + 'px'
|
||||
}">正在加载内容</text>
|
||||
</view>
|
||||
</template>
|
||||
<template v-else-if="item.type == 'top' || item.type == 'bottom' ">
|
||||
<view class="flip-loading flip-item-content">
|
||||
<text :style="{
|
||||
'color': options.color,
|
||||
'font-size': options.fontSize + 'px'
|
||||
}">{{item.type == 'top' ? '前面已经没有了' : '后面已经没有了'}}</text>
|
||||
</view>
|
||||
</template>
|
||||
<template v-else>
|
||||
<view class="flip-loading flip-item-content">
|
||||
<text :style="{
|
||||
'color': options.color,
|
||||
'font-size': options.fontSize + 'px'
|
||||
}">未知类型页面</text>
|
||||
</view>
|
||||
</template>
|
||||
<view class="flip-item-footer" v-if="options.footerShow">
|
||||
<text class="flip-item-footer-text" :style="{
|
||||
color: options.color
|
||||
}">{{filterDate()}}</text>
|
||||
<text class="flip-item-footer-text" :style="{
|
||||
color: options.color
|
||||
}">{{filterPage(item)}}</text>
|
||||
<battery :color="options.color" style="opacity: 0.5"></battery>
|
||||
</view>
|
||||
</view>
|
||||
<view
|
||||
class="flip-item-bg"
|
||||
:class="'flip-item-bg_' + item.dataId"
|
||||
:style="{
|
||||
left: viewWidth + 'px',
|
||||
width: viewWidth + 'px',
|
||||
height: (viewHeight * 1.5) + 'px',
|
||||
top: (viewHeight / 2 - (viewHeight * 1.5) / 2) + 'px',
|
||||
background: options.bgColor,
|
||||
}"></view>
|
||||
<view class="flip-item-shadow" :class="'flip-item-shadow_' + item.dataId"></view>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Util from '../../../js_sdk/util.js'
|
||||
import Battery from '../battery.vue'
|
||||
export default {
|
||||
components: {
|
||||
Battery
|
||||
},
|
||||
props: {
|
||||
currentDataId: {
|
||||
type: Number,
|
||||
default: -1
|
||||
},
|
||||
data: {
|
||||
type: Array,
|
||||
default () {
|
||||
return new Array
|
||||
}
|
||||
},
|
||||
options: {
|
||||
type: Object,
|
||||
default () {
|
||||
return new Object
|
||||
}
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
dataReverse () {
|
||||
let data = JSON.parse(JSON.stringify(this.data))
|
||||
data.reverse()
|
||||
return data
|
||||
},
|
||||
prevDataId () {
|
||||
return this.dataReverse[this.current + 1] && this.dataReverse[this.current + 1].dataId
|
||||
},
|
||||
nextDataId () {
|
||||
return this.dataReverse[this.current - 1] && this.dataReverse[this.current - 1].dataId
|
||||
},
|
||||
flipProp () {
|
||||
return {
|
||||
current: this.current,
|
||||
prevDataId: this.prevDataId,
|
||||
nextDataId: this.nextDataId,
|
||||
currentDataId: this.currentDataIdSync,
|
||||
pageType: this.options.pageType,
|
||||
pageTo: this.pageTo
|
||||
}
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
isShow: false,
|
||||
viewWidth: 0,
|
||||
viewHeight: 0,
|
||||
currentDataIdSync: -1,
|
||||
current: -1,
|
||||
pageTo: 0
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.currentDataIdSync = this.currentDataId
|
||||
this.current = this.dataReverse.findIndex(item => item.dataId == this.currentDataIdSync)
|
||||
this.$nextTick(function () {
|
||||
setTimeout(() => {
|
||||
// #ifndef APP-NVUE
|
||||
const query = uni.createSelectorQuery().in(this);
|
||||
query.select('.yingbing-flip').boundingClientRect(data => {
|
||||
this.viewWidth = data.width
|
||||
this.viewHeight = data.height
|
||||
this.isShow = true
|
||||
}).exec();
|
||||
// #endif
|
||||
// #ifdef APP-NVUE
|
||||
uni.requireNativePlugin('dom').getComponentRect(this.$refs.yingbingFlip, res => {
|
||||
this.viewWidth = res.size.width
|
||||
this.viewHeight = res.size.height
|
||||
this.isShow = true
|
||||
})
|
||||
// #endif
|
||||
}, 50)
|
||||
})
|
||||
},
|
||||
methods: {
|
||||
onChange (e) {
|
||||
this.current = e.current
|
||||
this.currentDataIdSync = this.dataReverse[this.current].dataId
|
||||
this.$emit('change', this.currentDataIdSync)
|
||||
},
|
||||
pagePrev () {
|
||||
this.pageTo = 0
|
||||
this.$nextTick(function(){
|
||||
this.pageTo = -1
|
||||
})
|
||||
},
|
||||
pageNext () {
|
||||
this.pageTo = 0
|
||||
this.$nextTick(function(){
|
||||
this.pageTo = 1
|
||||
})
|
||||
},
|
||||
filterPage (pageInfo) {
|
||||
const nowChapters = this.data.filter(item => item.chapter == pageInfo.chapter && (item.type == 'text' || item.type == 'custom' || item.type == 'slot'))
|
||||
let currentPage = nowChapters.findIndex(item => item.dataId == pageInfo.dataId)
|
||||
if ( currentPage > -1 ) {
|
||||
return (currentPage + 1) + ' / ' + nowChapters.length
|
||||
} else {
|
||||
return pageInfo.type == 'top' ? '最前面' : pageInfo.type == 'bottom' ? '最后面' : pageInfo.type.indexOf('Loading') > -1 ? '请等待' : ''
|
||||
}
|
||||
},
|
||||
filterDate () {
|
||||
let date = new Date()
|
||||
return Util.zeroize(date.getHours()) + ':' + Util.zeroize(date.getMinutes())
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
currentDataId (newVal) {
|
||||
this.currentDataIdSync = newVal
|
||||
this.current = this.dataReverse.findIndex(item => item.dataId == this.currentDataIdSync)
|
||||
},
|
||||
data (newVal) {
|
||||
this.$nextTick(function () {
|
||||
this.current = this.dataReverse.findIndex(item => item.dataId == this.currentDataIdSync)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- #ifdef APP-VUE || H5 || MP-QQ || MP-WEIXIN -->
|
||||
<script lang="wxs" module="flip">
|
||||
function touchstart (event, ins) {
|
||||
var state = ins.getState()
|
||||
if ( state.isTouch || state.disableTouch ) {
|
||||
return
|
||||
}
|
||||
state.isTouch = true
|
||||
state.touchTime = 0
|
||||
state.interval = true
|
||||
setInterval(ins)
|
||||
var touch = event.touches[0]
|
||||
state.startX = touch.pageX
|
||||
state.startY = touch.pageY
|
||||
}
|
||||
function touchmove (event, ins) {
|
||||
event.preventDefault && event.preventDefault()
|
||||
var state = ins.getState()
|
||||
if ( state.isTouch && (state.pageType == 'real' || state.pageType == 'cover') && !state.disableTouch ) {
|
||||
var touch = event.touches[0]
|
||||
if (state.direction) {
|
||||
var rect = ins.getBoundingClientRect()
|
||||
var height = rect.height / 2;
|
||||
var maxDeg = height / 5;
|
||||
state.rotate = state.direction == 'next' ? ((touch.pageY - height) / maxDeg) : -((touch.pageY - height) / maxDeg);
|
||||
state.offset = touch.pageX - state.startX;
|
||||
if ( (state.offset > 0 && state.direction == 'next') || (state.offset < 0 && state.direction == 'prev') ) {
|
||||
state.offset = 0
|
||||
}
|
||||
if ( Math.abs(state.offset) <= rect.width ) {
|
||||
animation(state.offset, 0, ins);
|
||||
}
|
||||
} else {
|
||||
if ( touch.pageX < state.startX ) {
|
||||
if ( state.nextDataId ) {
|
||||
state.direction = 'next'
|
||||
}
|
||||
} else {
|
||||
if ( state.prevDataId ) {
|
||||
state.direction = 'prev'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function touchend (event, ins) {
|
||||
var state = ins.getState()
|
||||
clearInterval(ins)
|
||||
if ( state.isTouch && !state.disableTouch ) {
|
||||
var rect = ins.getBoundingClientRect()
|
||||
if ( !state.direction && state.touchTime <= 200 ) {
|
||||
//获取点击位置,判断向哪里翻页
|
||||
if (state.startX > (rect.width / 4) * 3) {
|
||||
if ( state.nextDataId ) {
|
||||
state.direction = 'next'
|
||||
}
|
||||
}
|
||||
if (state.startX < (rect.width / 4)) {
|
||||
if ( state.prevDataId ) {
|
||||
state.direction = 'prev'
|
||||
}
|
||||
}
|
||||
}
|
||||
if (state.direction) {
|
||||
state.disableTouch = true
|
||||
if (state.touchTime <= 200) {
|
||||
var duration = (state.pageType == 'real' || state.pageType == 'cover') ? 200 : 0
|
||||
var value = state.direction == 'next' ? 1 : -1;
|
||||
animation(-value * rect.width, duration, ins);
|
||||
ins.setTimeout(function () {
|
||||
reset(-value * rect.width, ins);
|
||||
state.current -= value
|
||||
ins.callMethod('onChange', {
|
||||
current: state.current
|
||||
})
|
||||
}, duration)
|
||||
} else {
|
||||
var duration = (state.pageType == 'real' || state.pageType == 'cover') ? 100 : 0
|
||||
if (Math.abs(state.offset) >= rect.width / 4) {
|
||||
var value = state.direction == 'next' ? 1 : -1;
|
||||
animation(-value * rect.width, duration, ins);
|
||||
ins.setTimeout(function () {
|
||||
reset(-value * rect.width, ins);
|
||||
state.current -= value
|
||||
ins.callMethod('onChange', {
|
||||
current: state.current
|
||||
})
|
||||
}, duration)
|
||||
} else {
|
||||
animation(0, duration, ins);
|
||||
ins.setTimeout(function () {
|
||||
reset(0, ins);
|
||||
}, duration)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
reset(0, ins)
|
||||
}
|
||||
}
|
||||
}
|
||||
function propWatcher (newVal, oldVal, ins) {
|
||||
ins.setTimeout(function () {
|
||||
var state = ins.getState()
|
||||
state.current = newVal.current
|
||||
state.currentDataId = newVal.currentDataId
|
||||
state.prevDataId = newVal.prevDataId
|
||||
state.nextDataId = newVal.nextDataId
|
||||
state.pageType = newVal.pageType
|
||||
if (newVal.pageTo != (oldVal && oldVal.pageTo)) {
|
||||
if ( !state.disableTouch ) {
|
||||
if ( newVal.pageTo == -1 && state.prevDataId ) {
|
||||
state.isTouch = true
|
||||
state.startX = 1
|
||||
state.touchTime = 0
|
||||
state.direction = 'prev'
|
||||
touchend(null, ins)
|
||||
}
|
||||
if ( newVal.pageTo == 1 && state.nextDataId ) {
|
||||
state.isTouch = true
|
||||
var rect = ins.getBoundingClientRect()
|
||||
state.startX = rect.width
|
||||
state.touchTime = 0
|
||||
state.direction = 'next'
|
||||
touchend(null, ins)
|
||||
}
|
||||
}
|
||||
}
|
||||
}, 50)
|
||||
}
|
||||
function setInterval (ins) {
|
||||
var state = ins.getState()
|
||||
state.touchTimer = ins.setTimeout(function () {
|
||||
state.touchTime += 10
|
||||
if ( state.interval ) {
|
||||
setInterval(ins)
|
||||
}
|
||||
}, 10)
|
||||
}
|
||||
function clearInterval (ins) {
|
||||
var state = ins.getState()
|
||||
state.interval = false
|
||||
if ( state.touchTimer ) {
|
||||
ins.clearTimeout(state.touchTimer)
|
||||
state.touchTimer = null
|
||||
}
|
||||
}
|
||||
function reset (offset, ins) {
|
||||
var state = ins.getState()
|
||||
var rect = ins.getBoundingClientRect()
|
||||
if ( state.direction ) {
|
||||
var late = state.direction == 'next' ? offset : offset - rect.width;
|
||||
var currentDataId = state.direction == 'next' ? state.currentDataId : state.prevDataId
|
||||
if ( currentDataId ) {
|
||||
ins.selectComponent('.flip-item_' + currentDataId).setStyle({
|
||||
transform: 'translateX(' + (-late) + 'px)',
|
||||
'box-shadow': '',
|
||||
transition: ''
|
||||
})
|
||||
if ( state.pageType == 'real' ) {
|
||||
ins.selectComponent('.flip-item-bg_' + currentDataId).setStyle({
|
||||
transform: 'translateX(' + late + 'px) rotateZ(' + state.rotate + 'deg)',
|
||||
'box-shadow': '',
|
||||
transition: ''
|
||||
})
|
||||
}
|
||||
ins.selectComponent('.flip-item-shadow_' + currentDataId).setStyle({
|
||||
'box-shadow': '',
|
||||
transition: ''
|
||||
})
|
||||
}
|
||||
}
|
||||
state.direction = null
|
||||
state.isTouch = false
|
||||
state.disableTouch = false
|
||||
state.offset = 0
|
||||
state.touchTime = 0
|
||||
state.startX = 0
|
||||
state.startY = 0
|
||||
}
|
||||
function animation (offset, duration, ins) {
|
||||
var state = ins.getState()
|
||||
var rect = ins.getBoundingClientRect()
|
||||
var late = state.direction == 'next' ? offset : offset - rect.width;
|
||||
var currentDataId = state.direction == 'next' ? state.currentDataId : state.prevDataId
|
||||
ins.selectComponent('.flip-item_' + currentDataId).setStyle({
|
||||
transform: 'translateX(' + late + 'px)',
|
||||
'box-shadow': state.pageType == 'real' ? '0 0 30px 20px rgba(0,0,0,0.4)' : state.pageType == 'cover' ? '0 0 10px 5px rgba(0,0,0,0.3)' : '',
|
||||
transition: duration > 0 ? 'transform ' + duration + 'ms' : ''
|
||||
})
|
||||
if ( state.pageType == 'real' ) {
|
||||
ins.selectComponent('.flip-item-wrapper_' + currentDataId).setStyle({
|
||||
transform: 'translateX(' + (-late) + 'px)',
|
||||
transition: duration > 0 ? 'transform ' + duration + 'ms' : ''
|
||||
})
|
||||
ins.selectComponent('.flip-item-bg_' + currentDataId).setStyle({
|
||||
transform: 'translateX(' + late + 'px) rotateZ(' + state.rotate + 'deg)',
|
||||
'box-shadow': '-5px 0 20px rgba(0,0,0,0.1)',
|
||||
transition: duration > 0 ? 'transform ' + duration + 'ms, ' + 'boxShadow ' + duration + 'ms' : ''
|
||||
})
|
||||
ins.selectComponent('.flip-item-shadow_' + currentDataId).setStyle({
|
||||
'box-shadow': '0 0 60px ' + (Math.abs(late) > 30 ? 30 : Math.abs(late)) + 'px rgba(0,0,0,0.4)',
|
||||
transition: duration > 0 ? 'boxShadow ' + duration + 'ms' : ''
|
||||
})
|
||||
} else {
|
||||
ins.selectComponent('.flip-item-wrapper_' + currentDataId).setStyle({
|
||||
transform: 'translateX(0px)',
|
||||
transition: duration > 0 ? 'transform ' + duration + 'ms' : ''
|
||||
})
|
||||
ins.selectComponent('.flip-item-shadow_' + currentDataId).setStyle({
|
||||
'box-shadow': '0 0 60px 0 rgba(0,0,0,0.5)',
|
||||
transition: duration > 0 ? 'boxShadow ' + duration + 'ms' : ''
|
||||
})
|
||||
}
|
||||
}
|
||||
module.exports = {
|
||||
touchstart: touchstart,
|
||||
touchmove: touchmove,
|
||||
touchend: touchend,
|
||||
propWatcher: propWatcher
|
||||
}
|
||||
</script>
|
||||
<!-- #endif -->
|
||||
|
||||
<style scoped>
|
||||
.yingbing-flip {
|
||||
/* #ifndef APP-NVUE */
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
/* #endif */
|
||||
/* #ifdef APP-NVUE */
|
||||
flex: 1;
|
||||
/* #endif */
|
||||
position: relative;
|
||||
}
|
||||
.flip-item {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
/* #ifndef APP-NVUE */
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
/* #endif */
|
||||
}
|
||||
.flip-item-wrapper {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
/* #ifndef APP-NVUE */
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
/* #endif */
|
||||
}
|
||||
.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;
|
||||
}
|
||||
.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 {
|
||||
flex: 1;
|
||||
/* #ifndef APP-NVUE */
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
/* #endif */
|
||||
}
|
||||
.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;
|
||||
font-family: Microsoft YaHei, 微软雅黑;
|
||||
/* #endif */
|
||||
}
|
||||
.flip-loading {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
/* #endif */
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.flip-item-bg {
|
||||
position: absolute;
|
||||
}
|
||||
.flip-item-shadow {
|
||||
position: absolute;
|
||||
width: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
}
|
||||
</style>
|
208
uni_modules/yingbing-ReadPage/components/modules/flip/flip.wxs
Normal file
@ -0,0 +1,208 @@
|
||||
function touchstart (event, ins) {
|
||||
var state = ins.getState()
|
||||
if ( state.isTouch || state.disableTouch ) {
|
||||
return
|
||||
}
|
||||
state.isTouch = true
|
||||
state.touchTime = 0
|
||||
state.interval = true
|
||||
setInterval(ins)
|
||||
var touch = event.touches[0]
|
||||
state.startX = touch.pageX
|
||||
state.startY = touch.pageY
|
||||
}
|
||||
function touchmove (event, ins) {
|
||||
event.preventDefault && event.preventDefault()
|
||||
var state = ins.getState()
|
||||
if ( state.isTouch && (state.pageType == 'real' || state.pageType == 'cover') && !state.disableTouch ) {
|
||||
var touch = event.touches[0]
|
||||
if (state.direction) {
|
||||
var rect = ins.getBoundingClientRect()
|
||||
var height = rect.height / 2;
|
||||
var maxDeg = height / 5;
|
||||
state.rotate = state.direction == 'next' ? ((touch.pageY - height) / maxDeg) : -((touch.pageY - height) / maxDeg);
|
||||
state.offset = touch.pageX - state.startX;
|
||||
if ( (state.offset > 0 && state.direction == 'next') || (state.offset < 0 && state.direction == 'prev') ) {
|
||||
state.offset = 0
|
||||
}
|
||||
if ( Math.abs(state.offset) <= rect.width ) {
|
||||
animation(state.offset, 0, ins)
|
||||
}
|
||||
} else {
|
||||
if ( touch.pageX < state.startX ) {
|
||||
if ( state.nextDataId ) {
|
||||
state.direction = 'next'
|
||||
}
|
||||
} else {
|
||||
if ( state.prevDataId ) {
|
||||
state.direction = 'prev'
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function touchend (event, ins) {
|
||||
var state = ins.getState()
|
||||
clearInterval(ins)
|
||||
if ( state.isTouch && !state.disableTouch ) {
|
||||
var rect = ins.getBoundingClientRect()
|
||||
if ( !state.direction && state.touchTime <= 200 ) {
|
||||
//获取点击位置,判断向哪里翻页
|
||||
if (state.startX > (rect.width / 4) * 3) {
|
||||
if ( state.nextDataId ) {
|
||||
state.direction = 'next'
|
||||
}
|
||||
}
|
||||
if (state.startX < (rect.width / 4)) {
|
||||
if ( state.prevDataId ) {
|
||||
state.direction = 'prev'
|
||||
}
|
||||
}
|
||||
}
|
||||
if (state.direction) {
|
||||
state.disableTouch = true
|
||||
if (state.touchTime <= 200) {
|
||||
var duration = (state.pageType == 'real' || state.pageType == 'cover') ? 200 : 0
|
||||
var value = state.direction == 'next' ? 1 : -1;
|
||||
animation(-value * rect.width, duration, ins);
|
||||
ins.setTimeout(function () {
|
||||
reset(-value * rect.width, ins);
|
||||
ins.callMethod('onChange', value > 0 ? state.nextDataId : state.prevDataId)
|
||||
}, duration)
|
||||
} else {
|
||||
var duration = (state.pageType == 'real' || state.pageType == 'cover') ? 100 : 0
|
||||
if (Math.abs(state.offset) >= rect.width / 4) {
|
||||
var value = state.direction == 'next' ? 1 : -1;
|
||||
animation(-value * rect.width, duration, ins);
|
||||
ins.setTimeout(function () {
|
||||
reset(-value * rect.width, ins);
|
||||
ins.callMethod('onChange', value > 0 ? state.nextDataId : state.prevDataId)
|
||||
}, duration)
|
||||
} else {
|
||||
animation(0, duration, ins);
|
||||
ins.setTimeout(function () {
|
||||
reset(0, ins);
|
||||
}, duration)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
reset(0, ins)
|
||||
}
|
||||
}
|
||||
}
|
||||
function propWatcher (newVal, oldVal, ins) {
|
||||
if ( oldVal ) {
|
||||
var state = ins.getState()
|
||||
state.currentDataId = newVal.currentDataId
|
||||
state.prevDataId = newVal.prevDataId
|
||||
state.nextDataId = newVal.nextDataId
|
||||
state.pageType = newVal.pageType
|
||||
if (newVal.pageTo != oldVal.pageTo) {
|
||||
if ( !state.disableTouch ) {
|
||||
if ( newVal.pageTo == -1 && state.prevDataId ) {
|
||||
state.isTouch = true
|
||||
state.startX = 1
|
||||
state.touchTime = 0
|
||||
state.direction = 'prev'
|
||||
touchend(null, ins)
|
||||
}
|
||||
if ( newVal.pageTo == 1 && state.nextDataId ) {
|
||||
state.isTouch = true
|
||||
var rect = ins.getBoundingClientRect()
|
||||
state.startX = rect.width
|
||||
state.touchTime = 0
|
||||
state.direction = 'next'
|
||||
touchend(null, ins)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function setInterval (ins) {
|
||||
var state = ins.getState()
|
||||
state.touchTimer = ins.setTimeout(function () {
|
||||
state.touchTime += 10
|
||||
if ( state.interval ) {
|
||||
setInterval(ins)
|
||||
}
|
||||
}, 10)
|
||||
}
|
||||
function clearInterval (ins) {
|
||||
var state = ins.getState()
|
||||
state.interval = false
|
||||
if ( state.touchTimer ) {
|
||||
ins.clearTimeout(state.touchTimer)
|
||||
state.touchTimer = null
|
||||
}
|
||||
}
|
||||
function reset (offset, ins) {
|
||||
var state = ins.getState()
|
||||
var rect = ins.getBoundingClientRect()
|
||||
if ( state.direction ) {
|
||||
var late = state.direction == 'next' ? offset : offset - rect.width;
|
||||
var currentDataId = state.direction == 'next' ? state.currentDataId : state.prevDataId
|
||||
if ( currentDataId ) {
|
||||
var draw = function () {
|
||||
ins.selectComponent('.flip-item_' + currentDataId).setStyle({
|
||||
transform: 'translateX(' + late + 'px)',
|
||||
'box-shadow': '',
|
||||
transition: ''
|
||||
})
|
||||
if ( state.pageType == 'real' ) {
|
||||
ins.selectComponent('.flip-item-bg_' + currentDataId).setStyle({
|
||||
transform: 'translateX(' + late + 'px) rotateZ(' + state.rotate + 'deg)',
|
||||
'box-shadow': '',
|
||||
transition: ''
|
||||
})
|
||||
}
|
||||
ins.selectComponent('.flip-item-shadow_' + currentDataId).setStyle({
|
||||
'box-shadow': '',
|
||||
transition: ''
|
||||
})
|
||||
}
|
||||
ins.requestAnimationFrame(draw)
|
||||
}
|
||||
}
|
||||
state.direction = null
|
||||
state.isTouch = false
|
||||
state.disableTouch = false
|
||||
state.offset = 0
|
||||
state.touchTime = 0
|
||||
state.startX = 0
|
||||
state.startY = 0
|
||||
}
|
||||
function animation (offset, duration, ins) {
|
||||
var state = ins.getState()
|
||||
var rect = ins.getBoundingClientRect()
|
||||
var late = state.direction == 'next' ? offset : offset - rect.width;
|
||||
var currentDataId = state.direction == 'next' ? state.currentDataId : state.prevDataId
|
||||
var draw = function () {
|
||||
ins.selectComponent('.flip-item_' + currentDataId).setStyle({
|
||||
transform: 'translateX(' + late + 'px)',
|
||||
'box-shadow': state.pageType == 'real' ? '0 0 30px 20px rgba(0,0,0,0.4)' : state.pageType == 'cover' ? '0 0 10px 5px rgba(0,0,0,0.3)' : '',
|
||||
transition: duration > 0 ? 'transform ' + duration + 'ms' : ''
|
||||
})
|
||||
if ( state.pageType == 'real' ) {
|
||||
ins.selectComponent('.flip-item-wrapper_' + currentDataId).setStyle({
|
||||
transform: 'translateX(' + (-late) + 'px)',
|
||||
transition: duration > 0 ? 'transform ' + duration + 'ms' : ''
|
||||
})
|
||||
ins.selectComponent('.flip-item-bg_' + currentDataId).setStyle({
|
||||
transform: 'translateX(' + late + 'px) rotateZ(' + state.rotate + 'deg)',
|
||||
'box-shadow': '-5px 0 20px rgba(0,0,0,0.1)',
|
||||
transition: duration > 0 ? 'transform ' + duration + 'ms, ' + 'boxShadow ' + duration + 'ms' : ''
|
||||
})
|
||||
ins.selectComponent('.flip-item-shadow_' + currentDataId).setStyle({
|
||||
'box-shadow': '0 0 60px 30px rgba(0,0,0,0.4)',
|
||||
transition: duration > 0 ? 'boxShadow ' + duration + 'ms' : ''
|
||||
})
|
||||
}
|
||||
}
|
||||
ins.requestAnimationFrame(draw)
|
||||
}
|
||||
module.exports = {
|
||||
touchstart: touchstart,
|
||||
touchmove: touchmove,
|
||||
touchend: touchend,
|
||||
propWatcher: propWatcher
|
||||
}
|
194
uni_modules/yingbing-ReadPage/components/modules/list/list.vue
Normal file
@ -0,0 +1,194 @@
|
||||
<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>
|
@ -0,0 +1,214 @@
|
||||
<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="{
|
||||
width: pixelSize + 'px',
|
||||
height: pixelSize + 'px',
|
||||
'border-radius': pixelSize + 'px'
|
||||
}">
|
||||
<view
|
||||
class="line yb-flex"
|
||||
:style="{
|
||||
'border-top-width': (pixelSize / 4) + 'px',
|
||||
'border-bottom-width': (pixelSize / 4) + 'px',
|
||||
'border-top-color': item.top,
|
||||
'border-bottom-color': item.bottom,
|
||||
width: (pixelSize / 12) + 'px',
|
||||
left: ((pixelSize / 2) - (pixelSize / 24)) + 'px',
|
||||
}"
|
||||
: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')
|
||||
// #endif
|
||||
export default {
|
||||
props: {
|
||||
visible: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
size: {
|
||||
type: [Number, String],
|
||||
default: 40
|
||||
},
|
||||
color: {
|
||||
type: String,
|
||||
default: '#333333'
|
||||
},
|
||||
text: {
|
||||
type: String,
|
||||
default: ''
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
rgbs () {
|
||||
let rgb = Util.hex2rgb(this.color).replace('rgb(', '').replace(')', '')
|
||||
return [{
|
||||
top: `rgba(${rgb}, 1)`,
|
||||
bottom: `rgba(${rgb}, .4)`
|
||||
},{
|
||||
top: `rgba(${rgb}, .4)`,
|
||||
bottom: `rgba(${rgb}, .5)`
|
||||
},{
|
||||
top: `rgba(${rgb}, .4)`,
|
||||
bottom: `rgba(${rgb}, .6)`
|
||||
},{
|
||||
top: `rgba(${rgb}, .4)`,
|
||||
bottom: `rgba(${rgb}, .7)`
|
||||
},{
|
||||
top: `rgba(${rgb}, .4)`,
|
||||
bottom: `rgba(${rgb}, .8)`
|
||||
},{
|
||||
top: `rgba(${rgb}, .4)`,
|
||||
bottom: `rgba(${rgb}, .9)`
|
||||
}]
|
||||
},
|
||||
pixelSize () {
|
||||
return Util.unitpixel(this.size)
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
loading_binding: null
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
// #ifdef APP-NVUE
|
||||
this.$nextTick(() => {
|
||||
if ( this.visible ) {
|
||||
this.start()
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
},
|
||||
beforeDestroy() {
|
||||
// #ifdef APP-NVUE
|
||||
if ( this.loading_binding ) {
|
||||
Binding.unbind({
|
||||
token: this.loading_binding.token,
|
||||
eventType: 'timing'
|
||||
})
|
||||
this.loading_binding = null
|
||||
}
|
||||
// #endif
|
||||
},
|
||||
methods: {
|
||||
start () {
|
||||
let loading = Util.getEl(this.$refs.loading);
|
||||
this.loading_binding = Binding.bind({
|
||||
eventType: 'timing',
|
||||
props: [{
|
||||
element: loading,
|
||||
property: 'transform.rotateZ',
|
||||
expression: 'floor(t/100)*30'
|
||||
}]
|
||||
});
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
visible (newVal) {
|
||||
// #ifdef APP-NVUE
|
||||
this.$nextTick(() => {
|
||||
if ( newVal ) {
|
||||
this.start()
|
||||
} else {
|
||||
if ( this.loading_binding ) {
|
||||
Binding.unbind({
|
||||
token: this.loading_binding.token,
|
||||
eventType: 'timing'
|
||||
})
|
||||
this.loading_binding = null
|
||||
}
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@import url(@/uni_modules/yingbing-ReadPage/css/common.css);
|
||||
/* #ifndef APP-NVUE */
|
||||
@keyframes loading{
|
||||
0% {
|
||||
transform: rotateZ(30deg);
|
||||
}
|
||||
9.33333%{
|
||||
transform: rotateZ(60deg);
|
||||
}
|
||||
18.66666%{
|
||||
transform: rotateZ(90deg);
|
||||
}
|
||||
27.99999%{
|
||||
transform: rotateZ(120deg);
|
||||
}
|
||||
37.33332%{
|
||||
transform: rotateZ(150deg);
|
||||
}
|
||||
46.66665%{
|
||||
transform: rotateZ(180deg);
|
||||
}
|
||||
55.99998%{
|
||||
transform: rotateZ(210deg);
|
||||
}
|
||||
65.33331%{
|
||||
transform: rotateZ(240deg);
|
||||
}
|
||||
74.66664%{
|
||||
transform: rotateZ(270deg);
|
||||
}
|
||||
83.99997%{
|
||||
transform: rotateZ(300deg);
|
||||
}
|
||||
93.33333%{
|
||||
transform: rotateZ(330deg);
|
||||
}
|
||||
100%{
|
||||
transform: rotateZ(360deg);
|
||||
}
|
||||
}
|
||||
/* #endif */
|
||||
.yb-loading .loading-text {
|
||||
margin-top: 15rpx;
|
||||
font-size: 28rpx;
|
||||
}
|
||||
.circle {
|
||||
position: relative;
|
||||
/* #ifndef APP-NVUE */
|
||||
animation: loading 1200ms step-start infinite;
|
||||
/* #endif */
|
||||
}
|
||||
.circle .line {
|
||||
position: absolute;
|
||||
border-top-style: solid;
|
||||
border-bottom-style: solid;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
}
|
||||
.circle .line_0 {
|
||||
}
|
||||
.circle .line_1 {
|
||||
transform: rotateZ(30deg);
|
||||
}
|
||||
.circle .line_2 {
|
||||
transform: rotateZ(60deg);
|
||||
}
|
||||
.circle .line_3 {
|
||||
transform: rotateZ(90deg);
|
||||
}
|
||||
.circle .line_4 {
|
||||
transform: rotateZ(120deg);
|
||||
}
|
||||
.circle .line_5 {
|
||||
transform: rotateZ(150deg);
|
||||
}
|
||||
</style>
|
@ -0,0 +1,91 @@
|
||||
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()
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,116 @@
|
||||
<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>
|
@ -0,0 +1,119 @@
|
||||
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 = ''
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,125 @@
|
||||
<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>
|
@ -0,0 +1,95 @@
|
||||
var max = 200
|
||||
function touchstart(event, ins) {
|
||||
var state = ins.getState()
|
||||
if ( !state.enablePulldown ) {
|
||||
return
|
||||
}
|
||||
var touch = event.touches[0] || event.changedTouches[0]
|
||||
state.startX = touch.pageX
|
||||
state.startY = touch.pageY
|
||||
}
|
||||
function touchmove(event, ins) {
|
||||
var state = ins.getState()
|
||||
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) {
|
||||
var pageY = touch.pageY
|
||||
var rate = max / (max + Math.abs(pageY - state.startY))
|
||||
state.threshold = rate * (pageY - state.startY)
|
||||
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({
|
||||
transform: 'translateY(' + state.threshold + 'px)',
|
||||
transition: ''
|
||||
})
|
||||
if ( state.threshold > 0 ) {
|
||||
ins.callMethod('pullingdown', state.threshold)
|
||||
} else {
|
||||
ins.callMethod('pullingup', Math.abs(state.threshold))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
function touchend(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)',
|
||||
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)',
|
||||
transition: 'transform .1s'
|
||||
})
|
||||
}
|
||||
}
|
||||
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({
|
||||
transform: 'translateY(0)',
|
||||
transition: 'transform .1s'
|
||||
})
|
||||
state.threshold = 0
|
||||
state.startY = 0
|
||||
ins.callMethod('resetPulldownIns')
|
||||
}
|
||||
function propWatcher (newVal, oldVal, ins) {
|
||||
ins.setTimeout(function () {
|
||||
var state = ins.getState()
|
||||
state.enablePulldown = (newVal && newVal.enablePulldown)
|
||||
if ( (newVal && newVal.pulldownRestore) != (oldVal && oldVal.pulldownRestore) ) {
|
||||
if ( newVal.pulldownRestore ) {
|
||||
stop(ins)
|
||||
}
|
||||
}
|
||||
}, 100)
|
||||
}
|
||||
module.exports = {
|
||||
propWatcher: propWatcher,
|
||||
touchstart: touchstart,
|
||||
touchmove: touchmove,
|
||||
touchend: touchend
|
||||
}
|
@ -0,0 +1,103 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,964 @@
|
||||
<template>
|
||||
<view class="page" :id="'page' + dataId" :prop="pageProp" :change:prop="page.pagePropChange">
|
||||
<!-- 翻页模式 -->
|
||||
<view class="box">
|
||||
<view class="content"
|
||||
:id="'content' + dataId"
|
||||
v-if="pageType != 'scroll'"
|
||||
@touchstart="page.pageTouchstart"
|
||||
@touchmove="page.pageTouchmove"
|
||||
@touchend="page.pageTouchend"></view>
|
||||
<view class="content" style="z-index: -1000;" :id="'computed' + dataId"></view>
|
||||
</view>
|
||||
<!-- 滚动模式 -->
|
||||
<view
|
||||
:id="'scroll-box' + dataId"
|
||||
class="scroll-box"
|
||||
:style="{
|
||||
'color': color,
|
||||
'padding-left': slide + 'px',
|
||||
'padding-right': slide + 'px',
|
||||
'padding-top': topGap + 'px',
|
||||
'padding-bottom': bottomGap + 'px',
|
||||
'background': bgColor}"
|
||||
v-if="pageType == 'scroll'"></view>
|
||||
|
||||
|
||||
<div class="loading" v-if="initLoading" :style="{background: bgColor, 'font-size': fontSize + 'px'}">
|
||||
<!-- <page-refresh :color="color">正在加载内容</page-refresh> -->
|
||||
</div>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
props: {
|
||||
//传入唯一标识动态命名ID用于获取dom对象(可选)默认已经生成
|
||||
dataId: {
|
||||
type: String,
|
||||
default () {
|
||||
let mydate = new Date();
|
||||
return 'cms' + mydate.getMinutes() + mydate.getSeconds() + mydate.getMilliseconds() + Math.round(Math.random() * 10000);
|
||||
}
|
||||
},
|
||||
//字体颜色
|
||||
color: {
|
||||
type: String,
|
||||
default: '#333333'
|
||||
},
|
||||
//字体大小(单位px)
|
||||
fontSize: {
|
||||
type: String | Number,
|
||||
default: 15
|
||||
},
|
||||
//背景颜色
|
||||
bgColor: {
|
||||
type: String,
|
||||
default: '#fcd281'
|
||||
},
|
||||
//翻页方式
|
||||
pageType: {
|
||||
type: String,
|
||||
default: 'scroll'
|
||||
},
|
||||
//行间距(单位px)
|
||||
lineHeight: {
|
||||
type: Number | String,
|
||||
default: 15
|
||||
},
|
||||
//页面左右边距(单位px)
|
||||
slide: {
|
||||
type: Number | String,
|
||||
default: 40
|
||||
},
|
||||
//页面上边距(单位px)
|
||||
topGap: {
|
||||
type: Number | String,
|
||||
default: 10
|
||||
},
|
||||
//页面下边距(单位px)
|
||||
bottomGap: {
|
||||
type: Number | String,
|
||||
default: 10
|
||||
}
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
title: '',
|
||||
content: '',
|
||||
start: 0,
|
||||
loading: false,//等待内容请求
|
||||
upper: false,//文章是否到最前面
|
||||
lower: false,//文章是否到最后面
|
||||
restart: false,//是否重绘页面
|
||||
preLoading: false,//等待预加载请求
|
||||
initLoading: true,
|
||||
chapters: [],
|
||||
pageTo: 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
pageProp () {
|
||||
return {
|
||||
content: this.content,
|
||||
start: this.start,
|
||||
dataId: this.dataId,
|
||||
color: this.color,
|
||||
bgColor: this.bgColor,
|
||||
slide: this.slide,
|
||||
topGap: this.topGap,
|
||||
bottomGap: this.bottomGap,
|
||||
fontSize: this.fontSize,
|
||||
pageType: this.pageType,
|
||||
lineHeight: this.lineHeight,
|
||||
restart: this.restart,
|
||||
pageTo: this.pageTo
|
||||
};
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
//初始化
|
||||
init (data) {
|
||||
this.content = data.content;
|
||||
this.start = data.start
|
||||
this.title = data.title
|
||||
this.restart = true;
|
||||
this.getCatalog(this.content);
|
||||
},
|
||||
//跳转
|
||||
change (data) {
|
||||
this.initLoading = true;
|
||||
this.start = data.start;
|
||||
this.restart = true;
|
||||
},
|
||||
showToast (e) {
|
||||
uni.showToast({
|
||||
title: e.title,
|
||||
icon: 'none'
|
||||
})
|
||||
},
|
||||
resetPage () {
|
||||
this.restart = true;
|
||||
},
|
||||
pagePrev () {
|
||||
this.pageTo = 0
|
||||
this.$nextTick(function(){
|
||||
this.pageTo = -1
|
||||
})
|
||||
},
|
||||
pageNext () {
|
||||
this.pageTo = 0
|
||||
this.$nextTick(function(){
|
||||
this.pageTo = 1
|
||||
})
|
||||
},
|
||||
//抛出阅读页面改变事件
|
||||
currentChange (e) {
|
||||
const start = e.currentInfo.start
|
||||
const chapterIndex = this.chapters.findIndex((chapter, key) => {
|
||||
if ( key < this.chapters.length - 1 ) {
|
||||
return start >= chapter.start && start < this.chapters[parseInt(key) + 1].start
|
||||
} else {
|
||||
return start >= chapter.start
|
||||
}
|
||||
})
|
||||
if ( chapterIndex > -1 ) {
|
||||
e.currentInfo.chapter = this.chapters[chapterIndex].chapter
|
||||
e.currentInfo.title = this.chapters[chapterIndex].title
|
||||
} else {
|
||||
e.currentInfo.chapter = this.chapters[0].chapter
|
||||
e.currentInfo.title = this.chapters[0].title
|
||||
}
|
||||
this.$emit('currentChange', e.currentInfo);
|
||||
},
|
||||
//重置部分变量,方便下次使用
|
||||
resetPageProp () {
|
||||
this.restart = false;
|
||||
this.isNewChapter = false;
|
||||
},
|
||||
resetInitLoading () {
|
||||
this.initLoading = false
|
||||
},
|
||||
//使用正则获取章节目录 并抛出事件
|
||||
getCatalog (content) {
|
||||
const reg = new RegExp(/(第?[一二两三四五六七八九十○零百千万亿0-91234567890※✩★☆]{1,6}[章回卷节折篇幕集部]?[、.-\s][^\n]*)[_,-]?/g);
|
||||
let match = '';
|
||||
let catalog = [];
|
||||
let chapter = 0
|
||||
while ((match = reg.exec(content)) != null) {
|
||||
chapter++
|
||||
catalog.push({
|
||||
title: match[0],
|
||||
start: match.index,
|
||||
chapter: chapter
|
||||
})
|
||||
}
|
||||
this.chapters = catalog.length > 0 ? catalog : [{
|
||||
chapter: 1,
|
||||
start: 0,
|
||||
title: this.title || '整章'
|
||||
}];
|
||||
this.$emit('setCatalog', catalog);
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<!-- #ifdef H5 || APP-VUE -->
|
||||
<script lang="renderjs" module="page" type="module">
|
||||
let myPageDom;
|
||||
export default {
|
||||
data () {
|
||||
return {
|
||||
viewHeight: 0,
|
||||
viewWidth: 0,
|
||||
updownloading: false,
|
||||
currentInfo: {
|
||||
start: 0,
|
||||
end: 0,
|
||||
text: ''
|
||||
},
|
||||
touchstart: {
|
||||
x: 0,
|
||||
y: 0
|
||||
},
|
||||
pageWating: false,//等待翻页
|
||||
moveX: 0,//翻页距离
|
||||
pageEl: '',//翻页对象
|
||||
pageDirection: 'next',//翻页方向
|
||||
touchTime: 0//触摸屏幕时间
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.initDom.bind(this);
|
||||
},
|
||||
methods: {
|
||||
initDom () {
|
||||
myPageDom = page.init(document.getElementById('page' + this.pageProp.dataId));
|
||||
// 观测更新的数据在 view 层可以直接访问到
|
||||
myPageDom.setOption(this.pageProp);
|
||||
},
|
||||
//绑定滚动事件(滚动模式下有效)
|
||||
bindScrollEvent () {
|
||||
let scrollBox = document.getElementById('scroll-box' + this.pageProp.dataId);
|
||||
if ( scrollBox ) {
|
||||
scrollBox.onscroll = () => {
|
||||
let scrollItems = scrollBox.getElementsByClassName('scroll-item');
|
||||
let scrollTop = scrollBox.scrollTop + this.pageProp.topGap + this.pageProp.bottomGap;
|
||||
for ( let i = 0; i < scrollItems.length; i++ ) {
|
||||
let offsetTop1 = scrollItems[i].offsetTop;
|
||||
let offsetTop2 = i < scrollItems.length - 1 ? scrollItems[i+1].offsetTop : offsetTop1 + 2;
|
||||
if ( scrollTop >= offsetTop1 && scrollTop < offsetTop2 ) {
|
||||
let start = parseInt(scrollItems[i].getAttribute('start'));
|
||||
let end = parseInt(scrollItems[i].getAttribute('end'));
|
||||
if ( this.currentInfo.start != start ) {
|
||||
this.currentInfo.start = start;
|
||||
this.currentInfo.end = end;
|
||||
this.currentInfo.text = scrollItems[i].innerText;
|
||||
this.triggerCurrentChange(this.currentInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( Math.ceil(scrollBox.scrollTop + scrollBox.offsetHeight) >= scrollBox.scrollHeight ) {//触底
|
||||
if ( this.updownloading ) {
|
||||
return;
|
||||
}
|
||||
this.updownloading = true;
|
||||
let end = parseInt(scrollBox.lastChild.getAttribute('end'));
|
||||
if ( end < this.pageProp.content.length - 1 ) {
|
||||
this.drawText(end, 'next');
|
||||
} else {
|
||||
this.triggerShowToast('后面已经没有了')
|
||||
}
|
||||
this.updownloading = false;
|
||||
}
|
||||
if ( scrollBox.scrollTop <= 0 ) {//触顶
|
||||
if ( this.updownloading ) {
|
||||
return;
|
||||
}
|
||||
this.updownloading = true;
|
||||
let start = parseInt(scrollBox.firstChild.getAttribute('start'));
|
||||
if ( start > 0 ) {
|
||||
this.drawText(start, 'prev');
|
||||
} else {
|
||||
this.triggerShowToast('前面已经没有了')
|
||||
}
|
||||
this.updownloading = false;
|
||||
}
|
||||
};
|
||||
}
|
||||
},
|
||||
//计算页面显示文字
|
||||
computedText (start) {
|
||||
let parent = document.getElementById('computed' + this.pageProp.dataId);
|
||||
this.viewWidth = parent.offsetWidth;
|
||||
this.viewHeight = parent.offsetHeight;
|
||||
let computedCanvas = this.createComputedCanvas(parent);
|
||||
let context = computedCanvas.getContext('2d');
|
||||
context.font = this.pageProp.fontSize + 'px 微软雅黑';
|
||||
context.fillStyle = this.pageProp.color;
|
||||
context.lineWidth = 1;
|
||||
let pageHeight = this.pageProp.fontSize + this.pageProp.lineHeight;
|
||||
let strs = [];
|
||||
let page = {
|
||||
start: start,
|
||||
end: 0,
|
||||
text: []
|
||||
}
|
||||
let length = 0;
|
||||
const contentSync = this.pageProp.content.substr(start);
|
||||
let lastIndex = 0;
|
||||
while ( pageHeight <= this.viewHeight - this.pageProp.topGap - this.pageProp.bottomGap ) {
|
||||
strs.push('');
|
||||
let lineWidth = 0;
|
||||
for ( let i = lastIndex; i < contentSync.length; i++ ) {
|
||||
lineWidth += context.measureText(contentSync[i]).width;
|
||||
if ( JSON.stringify(contentSync[i]) == JSON.stringify('\r') || JSON.stringify(contentSync[i]) == JSON.stringify('\n') ) {
|
||||
length += 1
|
||||
page.end = page.start + length;
|
||||
lastIndex = i + 1;
|
||||
break;
|
||||
} else if ( lineWidth >= this.viewWidth - (2 * this.pageProp.slide) ) {
|
||||
lastIndex = i;
|
||||
break;
|
||||
} else {
|
||||
strs[strs.length - 1] += contentSync[i];
|
||||
length += 1;
|
||||
page.end = page.start + length;
|
||||
}
|
||||
}
|
||||
pageHeight += this.pageProp.fontSize + this.pageProp.lineHeight;
|
||||
if ( page.end >= this.pageProp.content.length - 1 ) break;
|
||||
}
|
||||
page.text = strs;
|
||||
return page;
|
||||
},
|
||||
//计算当前显示页面上一页显示文字
|
||||
computedPrevText (end) {
|
||||
let parent = document.getElementById('computed' + this.pageProp.dataId);
|
||||
this.viewWidth = parent.offsetWidth;
|
||||
this.viewHeight = parent.offsetHeight;
|
||||
let computedCanvas = this.createComputedCanvas(parent);
|
||||
let context = computedCanvas.getContext('2d');
|
||||
context.font = this.pageProp.fontSize + 'px 微软雅黑';
|
||||
context.fillStyle = this.pageProp.color;
|
||||
context.lineWidth = 1;
|
||||
let pageHeight = this.pageProp.fontSize + this.pageProp.lineHeight;
|
||||
let strs = [];
|
||||
let page = {
|
||||
start: 0,
|
||||
end: end,
|
||||
text: []
|
||||
}
|
||||
let length = 0;
|
||||
let lastIndex1 = 0;
|
||||
let lastIndex2 = 0;
|
||||
while ( pageHeight <= this.viewHeight - this.pageProp.topGap - this.pageProp.bottomGap ) {
|
||||
if ( end - length > 0 ) {
|
||||
strs.unshift('');
|
||||
let lineWidth = 0;
|
||||
let contentSync = this.pageProp.content.substring(0, end);
|
||||
for ( let i = lastIndex1 || contentSync.length - 1; i >= 0; i-- ) {
|
||||
lineWidth += context.measureText(contentSync[i]).width;
|
||||
if ( JSON.stringify(contentSync[i]) == JSON.stringify('\r') || JSON.stringify(contentSync[i]) == JSON.stringify('\n') ) {
|
||||
lastIndex1 = i - 1;
|
||||
length += 1
|
||||
break;
|
||||
} else if ( lineWidth >= this.viewWidth - (2 * this.pageProp.slide) ) {
|
||||
lastIndex1 = i;
|
||||
break;
|
||||
} else {
|
||||
strs[0] = contentSync[i] + strs[0];
|
||||
length += 1
|
||||
page.start = end - length;
|
||||
}
|
||||
if ( page.start == 0 ) break;
|
||||
}
|
||||
pageHeight += this.pageProp.fontSize + this.pageProp.lineHeight;
|
||||
} else {
|
||||
if ( this.pageProp.pageType != 'scroll' ) {
|
||||
strs.push('');
|
||||
let lineWidth = 0;
|
||||
let contentSync = this.pageProp.content.substr(end);
|
||||
for ( let i = lastIndex2; i < contentSync.length; i++ ) {
|
||||
lineWidth += context.measureText(contentSync[i]).width;
|
||||
if ( JSON.stringify(contentSync[i]) == JSON.stringify('\r') || JSON.stringify(contentSync[i]) == JSON.stringify('\n') ) {
|
||||
lastIndex2 = i + 1;
|
||||
length += 1
|
||||
break;
|
||||
} else if ( lineWidth >= this.viewWidth - (2 * this.pageProp.slide) ) {
|
||||
lastIndex2 = i;
|
||||
break;
|
||||
} else {
|
||||
strs[strs.length - 1] += contentSync[i];
|
||||
length += 1;
|
||||
page.end = page.start + length;
|
||||
}
|
||||
}
|
||||
pageHeight += this.pageProp.fontSize + this.pageProp.lineHeight;
|
||||
if ( page.end >= this.pageProp.content.length - 1 ) break;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
page.text = strs;
|
||||
return page;
|
||||
},
|
||||
//绘制文字到页面上
|
||||
drawText (start, type = 'init') {
|
||||
if ( this.pageProp.pageType != 'scroll' ) {
|
||||
let parent = document.getElementById('content' + this.pageProp.dataId);
|
||||
let page = type == 'prev' ? this.computedPrevText(start) : this.computedText(start);
|
||||
let pageItem = this.createPageItem(parent, page, type);
|
||||
let el = {
|
||||
el: pageItem,
|
||||
content: pageItem.getElementsByClassName('page-item-canvas')[0],
|
||||
bg: pageItem.getElementsByClassName('page-item-bg')[0],
|
||||
shadow: pageItem.getElementsByClassName('page-item-shadow')[0]
|
||||
}
|
||||
for ( let i = 0; i < page.text.length; i++ ) {
|
||||
this.insetScrollText(el.content, page.text[i], this.pageProp.fontSize);
|
||||
// context.font = this.pageProp.fontSize + 'px 微软雅黑';
|
||||
// context.fillStyle = this.pageProp.color;
|
||||
// context.fillText(pages[i].text[j], this.pageProp.slide, ((j + 1) * (this.pageProp.fontSize + this.pageProp.lineHeight)) + this.pageProp.topGap);
|
||||
}
|
||||
this.resetProp();
|
||||
if ( type == 'init' ) {
|
||||
if ( page.start > 0 ) {
|
||||
this.drawText(page.start, 'prev')
|
||||
}
|
||||
if ( page.end < this.pageProp.content.length - 1 ) {
|
||||
this.drawText(page.end, 'next')
|
||||
}
|
||||
this.currentInfo.start = parseInt(el.el.getAttribute('start'));
|
||||
this.currentInfo.end = parseInt(el.el.getAttribute('end'));
|
||||
this.currentInfo.text = el.content.innerText;
|
||||
this.triggerCurrentChange(this.currentInfo);
|
||||
this.triggerResetInitLoading();
|
||||
} else if ( type == 'prev' ) {
|
||||
this.pageAnimation(-this.viewWidth, 0, el);
|
||||
if ( parent.getElementsByClassName('page-item').length > 3 ) parent.removeChild(parent.lastChild);
|
||||
} else {
|
||||
if ( parent.getElementsByClassName('page-item').length > 3 ) parent.removeChild(parent.firstChild);
|
||||
}
|
||||
} else {
|
||||
let scrollBox = document.getElementById('scroll-box' + this.pageProp.dataId);
|
||||
let page = type == 'prev' ? this.computedPrevText(start) : this.computedText(start);
|
||||
let scrollItem = this.createScrollItem(scrollBox, page, type);
|
||||
for ( let i = 0; i < page.text.length; i++ ) {
|
||||
this.insetScrollText(scrollItem, page.text[i], this.pageProp.fontSize);
|
||||
}
|
||||
this.resetProp();
|
||||
if ( type == 'init' ) {
|
||||
if ( page.start > 0 ) {
|
||||
this.drawText(page.start, 'prev')
|
||||
}
|
||||
if ( page.end < this.pageProp.content.length - 1 ) {
|
||||
this.drawText(page.end, 'next')
|
||||
}
|
||||
let scrollItems = scrollBox.getElementsByClassName('scroll-item')
|
||||
let offsetHeight = 0;
|
||||
for ( let i = 0; i < scrollItems.length; i++ ) {
|
||||
offsetHeight += i > 0 ? scrollItems[i - 1].offsetHeight : 0;
|
||||
if ( this.currentInfo.start >= scrollItems[i].getAttribute('start') && this.currentInfo.start < scrollItems[i].getAttribute('end') ) {
|
||||
scrollBox.scrollTop = offsetHeight;
|
||||
this.currentInfo.text = scrollItems[i].innerText;
|
||||
this.currentInfo.end = scrollItems[i].getAttribute('end');
|
||||
}
|
||||
}
|
||||
this.bindScrollEvent();
|
||||
this.triggerCurrentChange(this.currentInfo);
|
||||
this.triggerResetInitLoading();
|
||||
} else if ( type == 'prev' ) {
|
||||
scrollBox.scrollTop = scrollItem.offsetHeight;
|
||||
if ( scrollBox.getElementsByClassName('scroll-item').length > 3 ) scrollBox.removeChild(scrollBox.lastChild);
|
||||
} else {
|
||||
if ( scrollBox.getElementsByClassName('scroll-item').length > 3 ) scrollBox.removeChild(scrollBox.firstChild);
|
||||
scrollBox.scrollTop = scrollBox.scrollHeight - scrollBox.lastChild.offsetHeight - scrollBox.offsetHeight;
|
||||
}
|
||||
}
|
||||
},
|
||||
//创建一个独立的canvas画板,用于计算文字布局
|
||||
createComputedCanvas (el) {
|
||||
if ( document.getElementsByClassName('computedCanvas')[0] ) return document.getElementsByClassName('computedCanvas')[0];
|
||||
let canvasDom = document.createElement('canvas');
|
||||
canvasDom.width = this.viewWidth;
|
||||
canvasDom.height = this.viewHeight;
|
||||
canvasDom.style.position = 'absolute';
|
||||
canvasDom.style.top = 0;
|
||||
canvasDom.style.left = 0;
|
||||
canvasDom.setAttribute('class', 'computedCanvas');
|
||||
el.appendChild(canvasDom);
|
||||
return document.getElementsByClassName('computedCanvas')[0];
|
||||
},
|
||||
//创建翻页的文章盒子
|
||||
createPageItem (el, info, type) {
|
||||
let pageItem = document.createElement('div');
|
||||
pageItem.style.width = '100%';
|
||||
pageItem.style.height = '100%';
|
||||
pageItem.style.overflow = 'hidden';
|
||||
pageItem.style.position = 'absolute';
|
||||
pageItem.style.top = 0;
|
||||
pageItem.style.left = 0;
|
||||
pageItem.style.zIndex = -info.start;
|
||||
if ( this.currentInfo.start == info.start ) {
|
||||
pageItem.setAttribute('class', 'page-item page-item-actived page-item-start__' + info.start);
|
||||
} else {
|
||||
pageItem.setAttribute('class', 'page-item page-item-start__' + info.start);
|
||||
}
|
||||
pageItem.setAttribute('start', info.start);
|
||||
pageItem.setAttribute('end', info.end);
|
||||
// let canvas = document.createElement('canvas');
|
||||
// canvas.width = this.viewWidth;
|
||||
// canvas.height = this.viewHeight;
|
||||
// canvas.style.position = 'absolute';
|
||||
// canvas.style.top = 0;
|
||||
// canvas.style.left = 0;
|
||||
// // canvas.style.background = this.pageProp.bgColor;
|
||||
// canvas.style.zIndex = -1;
|
||||
// canvas.setAttribute('class', 'page-item-canvas');
|
||||
let pageContent = document.createElement('div');
|
||||
pageContent.style.width = this.viewWidth + 'px';
|
||||
pageContent.style.height = this.viewHeight + 'px';
|
||||
pageContent.style.position = 'absolute';
|
||||
pageContent.style.top = 0;
|
||||
pageContent.style.left = 0;
|
||||
pageContent.style.background = this.pageProp.bgColor;
|
||||
pageContent.style.color = this.pageProp.color;
|
||||
pageContent.style.overflow = 'hidden';
|
||||
pageContent.style.padding = `${this.pageProp.topGap}px ${this.pageProp.slide}px ${this.pageProp.bottomGap}px ${this.pageProp.slide}px`;
|
||||
pageContent.style.boxSizing = 'border-box';
|
||||
pageContent.setAttribute('class', 'page-item-canvas');
|
||||
pageItem.appendChild(pageContent);
|
||||
let pageBg = document.createElement('div');
|
||||
pageBg.style.width = '100%';
|
||||
pageBg.style.height = Math.sqrt(Math.pow(this.viewHeight, 2) + Math.pow(this.viewWidth, 2)) + 'px';
|
||||
pageBg.style.boxShadow = '-5px 0 20px rgba(0,0,0,0.2)';
|
||||
pageBg.style.position = 'absolute';
|
||||
pageBg.style.top = '50%';
|
||||
pageBg.style.left = '100%';
|
||||
pageBg.style.transform = 'translateY(-50%)';
|
||||
pageBg.style.background = this.pageProp.bgColor;
|
||||
pageBg.setAttribute('class', 'page-item-bg');
|
||||
pageItem.appendChild(pageBg);
|
||||
let pageShadow = document.createElement('div');
|
||||
pageShadow.style.width = '0';
|
||||
pageShadow.style.height = '100%';
|
||||
pageShadow.style.position = 'absolute';
|
||||
pageShadow.style.top = 0;
|
||||
pageShadow.style.right = 0;
|
||||
pageShadow.style.zIndex = '9';
|
||||
pageShadow.setAttribute('class', 'page-item-shadow');
|
||||
pageItem.appendChild(pageShadow);
|
||||
if ( type == 'prev' ) {
|
||||
el.insertBefore(pageItem, el.firstChild);
|
||||
} else {
|
||||
el.appendChild(pageItem);
|
||||
}
|
||||
return document.getElementsByClassName('page-item-start__' + info.start)[0];
|
||||
},
|
||||
//创建滚动布局下的的页面盒子
|
||||
createScrollItem (el, info, type) {
|
||||
let divDom = document.createElement('div');
|
||||
divDom.style.width = '100%';
|
||||
divDom.setAttribute('class', 'scroll-item scroll-start__' + info.start);
|
||||
divDom.setAttribute('start', info.start);
|
||||
divDom.setAttribute('end', info.end);
|
||||
if ( type == 'prev' ) {
|
||||
el.insertBefore(divDom, el.firstChild);
|
||||
} else {
|
||||
el.appendChild(divDom);
|
||||
}
|
||||
return document.getElementsByClassName('scroll-start__' + info.start)[0]
|
||||
},
|
||||
//创建滚动布局下的的文字盒子
|
||||
insetScrollText (el, text, height = 0) {
|
||||
let pDom = document.createElement('p');
|
||||
pDom.style.height = height ? height + 'px' : 'auto';
|
||||
pDom.style.marginTop = this.pageProp.lineHeight + 'px';
|
||||
pDom.style.fontSize = this.pageProp.fontSize + 'px';
|
||||
pDom.style.whiteSpace = 'pre-wrap';
|
||||
pDom.style.fontFamily = '"Microsoft YaHei",微软雅黑';
|
||||
pDom.setAttribute('class', 'scroll-text');
|
||||
pDom.innerHTML = text || ' ';
|
||||
el.appendChild(pDom);
|
||||
},
|
||||
pageTouchstart (e) {
|
||||
if ( this.pageWating ) {
|
||||
return;
|
||||
}
|
||||
if ( e.touches.length == 1 ) {
|
||||
this.touchTimer = window.setInterval(() => {
|
||||
this.touchTime += 50;
|
||||
}, 50)
|
||||
let touch = e.touches[0];
|
||||
this.touchstart.x = touch.pageX;
|
||||
this.touchstart.y = touch.pageY;
|
||||
}
|
||||
},
|
||||
pageTouchmove (e) {
|
||||
if ( this.touchstart.x == 0 || (this.pageProp.pageType != 'real' && this.pageProp.pageType != 'cover') ) {
|
||||
return;
|
||||
}
|
||||
if ( e.touches.length == 1 ) {
|
||||
if ( this.pageEl ) {
|
||||
let touch = e.touches[0];
|
||||
let height = this.viewHeight / 2;
|
||||
let maxDeg = height / 5;
|
||||
let rotateZ = this.pageDirection == 'next' ? ((touch.pageY - height) / maxDeg) : -((touch.pageY - height) / maxDeg);
|
||||
this.moveX = touch.pageX - this.touchstart.x;
|
||||
if ( this.pageDirection == 'next' ) {
|
||||
this.moveX > 0 ? this.moveX = 0 : null
|
||||
} else {
|
||||
this.moveX < 0 ? this.moveX = 0 : null
|
||||
}
|
||||
this.pageAnimation(this.moveX, rotateZ);
|
||||
} else {
|
||||
let touch = e.touches[0];
|
||||
if ( touch.pageX < this.touchstart.x ) {
|
||||
this.pageEl = this.getPageActived(0);
|
||||
this.pageDirection = 'next'
|
||||
} else {
|
||||
this.pageEl = this.getPageActived(-1);
|
||||
this.pageDirection = 'prev'
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
pageTouchend (e) {
|
||||
window.clearInterval(this.touchTimer);
|
||||
this.touchTimer = null
|
||||
if ( this.touchstart.x == 0 ) {
|
||||
return;
|
||||
}
|
||||
if ( !this.pageEl && this.touchTime <= 200 ) {
|
||||
//获取点击位置,判断向哪里翻页
|
||||
if (this.touchstart.x > (this.viewWidth / 4) * 3) {
|
||||
this.pageEl = this.getPageActived(0);
|
||||
this.pageDirection = 'next'
|
||||
}
|
||||
if (this.touchstart.x < (this.viewWidth / 4)) {
|
||||
this.pageEl = this.getPageActived(-1);
|
||||
this.pageDirection = 'prev'
|
||||
}
|
||||
}
|
||||
this.touchstart.x = 0
|
||||
if ( this.pageEl ) {
|
||||
this.pageWating = true;
|
||||
if ( this.touchTime <= 200 ) {
|
||||
let duration = (this.pageProp.pageType == 'real' || this.pageProp.pageType == 'cover') ? 200 : 0
|
||||
let value = this.pageDirection == 'next' ? 1 : -1;
|
||||
this.pageDuration(duration);
|
||||
this.$nextTick(() => {
|
||||
this.pageAnimation(-value * this.viewWidth);
|
||||
setTimeout(() => {
|
||||
this.changePageActived(value);
|
||||
this.resetPageMove();
|
||||
}, duration + 50)
|
||||
})
|
||||
} else {
|
||||
let duration = (this.pageProp.pageType == 'real' || this.pageProp.pageType == 'cover') ? 100 : 0
|
||||
if ( Math.abs(this.moveX) >= this.viewWidth / 4 ) {
|
||||
let value = this.pageDirection == 'next' ? 1 : -1;
|
||||
this.pageDuration(duration);
|
||||
this.$nextTick(() => {
|
||||
this.pageAnimation(-value * this.viewWidth);
|
||||
setTimeout(() => {
|
||||
this.changePageActived(value);
|
||||
this.resetPageMove();
|
||||
}, duration + 50)
|
||||
})
|
||||
} else {
|
||||
this.pageDuration(duration);
|
||||
this.$nextTick(() => {
|
||||
this.pageAnimation(0);
|
||||
setTimeout(() => {
|
||||
this.resetPageMove();
|
||||
}, duration + 50)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
//重置翻页数据
|
||||
resetPageMove () {
|
||||
this.pageDuration(0);
|
||||
this.isStart = false;
|
||||
this.pageWating = false;
|
||||
this.moveX = 0;
|
||||
this.pageEl = '';
|
||||
this.pageDirection = 'next';
|
||||
this.touchTime = 0;
|
||||
this.touchstart.x = 0;
|
||||
this.touchstart.y = 0;
|
||||
},
|
||||
//设置翻页对象动画效果
|
||||
pageAnimation (moveX, rotateZ = 0, el) {
|
||||
let lateX = this.pageDirection == 'next' ? moveX : moveX - this.viewWidth;
|
||||
let pageEl = el || this.pageEl;
|
||||
pageEl.el.style.transform = `translateX(${lateX}px)`;
|
||||
pageEl.content.style.transform = this.pageProp.pageType == 'real' ? `translateX(${-lateX}px)` : pageEl.content.style.transform;
|
||||
pageEl.bg.style.transform = this.pageProp.pageType == 'real' ? `translate(${lateX}px, -50%) rotateZ(${rotateZ}deg)` : pageEl.bg.style.transform;
|
||||
pageEl.shadow.style.boxShadow = '0 0 60px ' + (this.pageProp.pageType == 'real' ? Math.abs(lateX) > 30 ? 30 : Math.abs(lateX) : 0) + 'px rgba(0,0,0,0.5)';
|
||||
},
|
||||
//设置翻页对象动画时间
|
||||
pageDuration (duration) {
|
||||
this.pageEl.el.style.transition = duration > 0 ? 'transform ' + duration + 'ms' : '';
|
||||
this.pageEl.content.style.transition = duration > 0 ? 'transform ' + duration + 'ms' : '';
|
||||
this.pageEl.bg.style.transition = duration > 0 ? 'transform ' + duration + 'ms' : '';
|
||||
this.pageEl.shadow.style.transition = duration > 0 ? 'box-shadow ' + duration + 'ms' : '';
|
||||
},
|
||||
//获取翻页对象
|
||||
getPageActived (value = 0) {
|
||||
let boxs = document.getElementsByClassName('page-item');
|
||||
for ( let i = 0; i < boxs.length; i++ ) {
|
||||
if ( boxs[i].getAttribute('class').indexOf('page-item-actived') > 1 ) {
|
||||
if ( boxs[i + value + 1] && boxs[i + value] ) {
|
||||
return {
|
||||
el: boxs[i + value],
|
||||
content: boxs[i + value].getElementsByClassName('page-item-canvas')[0],
|
||||
bg: boxs[i + value].getElementsByClassName('page-item-bg')[0],
|
||||
shadow: boxs[i + value].getElementsByClassName('page-item-shadow')[0]
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
if ( value < 0 ) {
|
||||
this.triggerShowToast('前面已经没有了')
|
||||
} else {
|
||||
this.triggerShowToast('后面已经没有了')
|
||||
}
|
||||
return false;
|
||||
},
|
||||
//改变翻页对象
|
||||
changePageActived (value) {
|
||||
let content = document.getElementById('content' + this.pageProp.dataId);
|
||||
let boxs = content.getElementsByClassName('page-item');
|
||||
let index = -1
|
||||
for ( let i = 0; i < boxs.length; i++ ) {
|
||||
if ( boxs[i].getAttribute('class').indexOf('page-item-actived') > -1 ) {
|
||||
index = i;
|
||||
}
|
||||
}
|
||||
boxs[index].setAttribute('class', boxs[index].getAttribute('class').replace('page-item-actived', ''));
|
||||
boxs[index + value].setAttribute('class', boxs[index + value].getAttribute('class') + ' page-item-actived');
|
||||
this.currentInfo.start = parseInt(boxs[index + value].getAttribute('start'));
|
||||
this.currentInfo.end = parseInt(boxs[index + value].getAttribute('end'));
|
||||
this.currentInfo.text = boxs[index + value].getElementsByClassName('page-item-canvas')[0].innerText;
|
||||
this.triggerCurrentChange(this.currentInfo);
|
||||
if ( value < 0 && !boxs[index + value - 1] ) {
|
||||
if ( this.updownloading ) {
|
||||
return;
|
||||
}
|
||||
this.updownloading = true;
|
||||
let start = parseInt(content.firstChild.getAttribute('start'));
|
||||
if ( start > 0 ) {
|
||||
this.drawText(start, 'prev');
|
||||
} else {
|
||||
this.triggerShowToast('前面已经没有了')
|
||||
}
|
||||
this.updownloading = false;
|
||||
}
|
||||
if ( value > 0 && !boxs[index + value + 1] ) {
|
||||
if ( this.updownloading ) {
|
||||
return;
|
||||
}
|
||||
this.updownloading = true;
|
||||
let end = parseInt(content.lastChild.getAttribute('end'));
|
||||
if ( end < this.pageProp.content.length - 1 ) {
|
||||
this.drawText(end, 'next');
|
||||
} else {
|
||||
this.triggerShowToast('后面已经没有了')
|
||||
}
|
||||
this.updownloading = false;
|
||||
}
|
||||
if ( value < 0 ) {
|
||||
if ( boxs[index + value].getAttribute('end') != boxs[index + value + 1].getAttribute('start') ) {
|
||||
this.restartDrawText();
|
||||
}
|
||||
}
|
||||
},
|
||||
//参数改变
|
||||
pagePropChange (newValue, oldValue) {
|
||||
if ( newValue.fontSize != oldValue.fontSize ) {//字体大小改变
|
||||
this.restartDrawText();
|
||||
}
|
||||
if ( newValue.lineHeight != oldValue.lineHeight ) {//字体行高改变
|
||||
this.restartDrawText();
|
||||
}
|
||||
if ( newValue.color != oldValue.color || newValue.bgColor != oldValue.bgColor ) {//字体颜色改变
|
||||
if ( this.pageProp.pageType != 'scroll' ) {
|
||||
// this.restartDrawText(this.currentInfo.chapter);
|
||||
this.colorChange();
|
||||
}
|
||||
}
|
||||
if ( newValue.pageType != oldValue.pageType ) {//翻页模式改变
|
||||
this.restartDrawText();
|
||||
}
|
||||
if ( newValue.restart != oldValue.restart ) {//重绘页面通知
|
||||
this.restartChange(newValue.restart);
|
||||
}
|
||||
if (newValue.pageTo != oldValue.pageTo) {
|
||||
if ( this.pageProp.pageType != 'scroll' ) {
|
||||
if ( newValue.pageTo == -1 ) {
|
||||
if ( !this.pageWating ) {
|
||||
this.touchstart.x = 1
|
||||
this.pageEl = this.getPageActived(-1)
|
||||
this.pageDirection = 'prev'
|
||||
this.pageTouchend()
|
||||
}
|
||||
}
|
||||
if ( newValue.pageTo == 1 ) {
|
||||
if ( !this.pageWating ) {
|
||||
this.touchstart.x = this.viewWidth
|
||||
this.pageEl = this.getPageActived(0)
|
||||
this.pageDirection = 'next'
|
||||
this.pageTouchend()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ( newValue.pageTo == -1 ) {
|
||||
if ( this.currentInfo.start > 0 ) {
|
||||
document.getElementsByClassName('scroll-item')[0].scrollIntoView({behavior: 'smooth', 'block': 'end'})
|
||||
} else {
|
||||
document.getElementById('scroll-box' + this.pageProp.dataId).scrollTop = 0
|
||||
}
|
||||
}
|
||||
if ( newValue.pageTo == 1 ) {
|
||||
if ( this.currentInfo.end < this.pageProp.content.length ) {
|
||||
let items = document.getElementsByClassName('scroll-item')
|
||||
items[items.length - 1].scrollIntoView({behavior: 'smooth'})
|
||||
} else {
|
||||
document.getElementById('scroll-box' + this.pageProp.dataId).scrollTop = document.getElementById('scroll-box' + this.pageProp.dataId).scrollHeight
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
//重绘页面
|
||||
restartDrawText () {
|
||||
if ( this.pageProp.pageType == 'scroll' ) {
|
||||
document.getElementById('scroll-box' + this.pageProp.dataId).innerHTML = '';
|
||||
this.drawText(this.currentInfo.start);
|
||||
} else {
|
||||
document.getElementById('content' + this.pageProp.dataId).innerHTML = '';
|
||||
this.drawText(this.currentInfo.start);
|
||||
}
|
||||
},
|
||||
restartChange (newValue) {
|
||||
if ( newValue ) {
|
||||
this.currentInfo.start = this.pageProp.start;
|
||||
this.restartDrawText();
|
||||
}
|
||||
},
|
||||
colorChange () {
|
||||
let items = document.getElementsByClassName('page-item');
|
||||
for ( let i = 0; i < items.length; i++ ) {
|
||||
items[i].getElementsByClassName('page-item-canvas')[0].style.background = this.pageProp.bgColor;
|
||||
items[i].getElementsByClassName('page-item-bg')[0].style.background = this.pageProp.bgColor;
|
||||
items[i].getElementsByClassName('page-item-canvas')[0].style.color = this.pageProp.color;
|
||||
}
|
||||
},
|
||||
triggerCurrentChange (currentInfo) {
|
||||
// #ifndef H5
|
||||
UniViewJSBridge.publishHandler('onWxsInvokeCallMethod', {
|
||||
cid: this._$id,
|
||||
method: 'currentChange',
|
||||
args: {'currentInfo': currentInfo}
|
||||
})
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
this.currentChange({'currentInfo': currentInfo})
|
||||
// #endif
|
||||
},
|
||||
triggerShowToast (title) {
|
||||
// #ifndef H5
|
||||
UniViewJSBridge.publishHandler('onWxsInvokeCallMethod', {
|
||||
cid: this._$id,
|
||||
method: 'showToast',
|
||||
args: {'title': title}
|
||||
})
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
this.showToast({'title': title})
|
||||
// #endif
|
||||
},
|
||||
//重置部分传过来的属性
|
||||
resetProp () {
|
||||
// #ifndef H5
|
||||
UniViewJSBridge.publishHandler('onWxsInvokeCallMethod', {
|
||||
cid: this._$id,
|
||||
method: 'resetPageProp'
|
||||
})
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
this.resetPageProp()
|
||||
// #endif
|
||||
},
|
||||
//重置部分传过来的属性
|
||||
triggerResetInitLoading () {
|
||||
// #ifndef H5
|
||||
UniViewJSBridge.publishHandler('onWxsInvokeCallMethod', {
|
||||
cid: this._$id,
|
||||
method: 'resetInitLoading'
|
||||
})
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
this.resetInitLoading()
|
||||
// #endif
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<!-- #endif -->
|
||||
|
||||
<style scoped>
|
||||
.page {
|
||||
width: 100vw;
|
||||
height: 100vh;
|
||||
position: relative;
|
||||
}
|
||||
.scroll {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
}
|
||||
.scroll-box {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
right: 0;
|
||||
top: 0;
|
||||
bottom: 0;
|
||||
/* overflow-anchor: auto; */
|
||||
overflow-y: auto;
|
||||
}
|
||||
.box {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
box-sizing: border-box;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
overflow: hidden;
|
||||
}
|
||||
.content {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.loading {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
box-sizing: border-box;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
</style>
|
105
uni_modules/yingbing-ReadPage/components/modules/richtext.vue
Normal file
@ -0,0 +1,105 @@
|
||||
<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.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>
|
@ -0,0 +1,98 @@
|
||||
.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;
|
||||
}
|
@ -0,0 +1,154 @@
|
||||
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.options.fontSize + this.options.lineHeight, 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.options.fontSize + this.options.lineHeight), true)
|
||||
this.scrollTimer = setTimeout(() => {
|
||||
this.scrolling = false
|
||||
clearTimeout(this.scrollTimer)
|
||||
this.scrollTimer = null
|
||||
}, 300)
|
||||
},
|
||||
onPulldown (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()
|
||||
}
|
||||
},
|
||||
onLoadmore (callback) {
|
||||
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('success')
|
||||
})
|
||||
}
|
||||
},
|
||||
async scrollEnd(e) {
|
||||
let rate = Math.floor(e.scrollTop / this.viewHeight)
|
||||
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.$emit('change', pageInfo, this.pages)
|
||||
}
|
||||
},
|
||||
getScrollItemRect (dataId) {
|
||||
return new Promise(resolve => {
|
||||
Util.getRect('#scroll-item_' + dataId, Util.getRefs(this, 'scrollItem_' + dataId, 0), this).then(res => {
|
||||
resolve(res)
|
||||
})
|
||||
})
|
||||
},
|
||||
onScroll (e) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,724 @@
|
||||
<template>
|
||||
<view class="yingbing-read-page" ref="yingbingReadPage" @touchstart="touchstart" @touchmove="touchmove" @touchend="touchend">
|
||||
<!-- <computed ref="computedPage" :options="options"></computed> -->
|
||||
<!-- 翻页模式 -->
|
||||
<view class="yingbing-read-page-flip" :style="{
|
||||
'visibility': pageType != 'scroll' ? 'visible' : 'hidden'
|
||||
}">
|
||||
<template v-if="pageType != 'scroll'">
|
||||
<!-- #ifndef APP-NVUE -->
|
||||
<view
|
||||
class="yingbing-flip"
|
||||
ref="yingbingFlip"
|
||||
:style="{
|
||||
'background': options.bgColor
|
||||
}"
|
||||
:prop="flipProp"
|
||||
:change:prop="flip.propWatcher"
|
||||
@touchstart="flip.touchstart"
|
||||
@touchmove="flip.touchmove"
|
||||
@touchend="flip.touchend">
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef APP-NVUE -->
|
||||
<view
|
||||
class="yingbing-flip"
|
||||
ref="yingbingFlip"
|
||||
:style="{
|
||||
'background': options.bgColor
|
||||
}"
|
||||
@touchstart="onFilpTouchstart"
|
||||
@touchmove="onFilpTouchmove"
|
||||
@touchend="onFilpTouchend">
|
||||
<!-- #endif -->
|
||||
<template v-if="isShow">
|
||||
<view
|
||||
class="flip-item"
|
||||
v-for="(item, index) in dataReverse"
|
||||
:key="item.dataId + '_flip'"
|
||||
:style="{
|
||||
'visibility': item.dataId == currentDataId || item.dataId == prevDataId || item.dataId == nextDataId ? 'visible' : 'hidden'
|
||||
}">
|
||||
<view
|
||||
v-if="item.dataId == currentDataId || item.dataId == prevDataId || item.dataId == nextDataId"
|
||||
class="flip-item"
|
||||
:ref="'flipItem_' + item.dataId"
|
||||
:class="'flip-item_' + item.dataId"
|
||||
:style="{
|
||||
'transform': item.dataId < currentDataId ? `translateX(${-viewWidth}px)` : '',
|
||||
}">
|
||||
<view
|
||||
class="flip-item-wrapper"
|
||||
:ref="'flipItemWrapper_' + item.dataId"
|
||||
:class="'flip-item-wrapper_' + item.dataId"
|
||||
:style="{
|
||||
'padding-left': options.slide + 'px',
|
||||
'padding-right': options.slide + 'px',
|
||||
'padding-top': options.topGap + 'px',
|
||||
'padding-bottom': options.bottomGap + 'px',
|
||||
'background': options.bgColor,
|
||||
'transform': item.dataId < currentDataId ? options.pageType == 'real' ? `translateX(${viewWidth}px)` : 'translateX(0)' : ''
|
||||
}">
|
||||
<view class="flip-item-header" v-if="options.headerShow">
|
||||
<text class="flip-item-header-text" :style="{
|
||||
color: options.color,
|
||||
'font-family': options.fontFamily
|
||||
}">{{item.title}}</text>
|
||||
</view>
|
||||
<template v-if="item.type == 'text'">
|
||||
<view class="flip-item-text flip-item-content"
|
||||
>
|
||||
<text class="flip-text"
|
||||
v-for="(text, i) in item.text" :key="i"
|
||||
:style="{
|
||||
'margin-top': options.lineHeight + 'px',
|
||||
'height': options.fontSize + 'px',
|
||||
'font-size': options.fontSize + 'px',
|
||||
'color': options.color,
|
||||
'font-family': options.fontFamily
|
||||
}">{{text}}</text>
|
||||
</view>
|
||||
</template>
|
||||
<template v-else-if="item.type == 'custom'">
|
||||
<read-rich-text style="flex: 1;" :richtext="item.text" :fontFace="fontFace" :pageType="pageType" @customClick="customClick"></read-rich-text>
|
||||
</template>
|
||||
<template v-else-if="item.type == 'slot'">
|
||||
<view class="flip-slot flip-item-content">
|
||||
<slot :name="item.text"></slot>
|
||||
</view>
|
||||
</template>
|
||||
<template v-else-if="item.type == 'nextLoading' || item.type == 'prevLoading' ">
|
||||
<view class="flip-loading flip-item-content">
|
||||
<text :style="{
|
||||
'color': options.color,
|
||||
'font-size': options.fontSize + 'px',
|
||||
'font-family': options.fontFamily
|
||||
}">正在加载内容</text>
|
||||
</view>
|
||||
</template>
|
||||
<template v-else-if="item.type == 'top' || item.type == 'bottom' ">
|
||||
<view class="flip-loading flip-item-content">
|
||||
<text :style="{
|
||||
'color': options.color,
|
||||
'font-size': options.fontSize + 'px',
|
||||
'font-family': options.fontFamily
|
||||
}">{{item.type == 'top' ? '前面已经没有了' : '后面已经没有了'}}</text>
|
||||
</view>
|
||||
</template>
|
||||
<template v-else>
|
||||
<view class="flip-loading flip-item-content">
|
||||
<text :style="{
|
||||
'color': options.color,
|
||||
'font-size': options.fontSize + 'px',
|
||||
'font-family': options.fontFamily
|
||||
}">未知类型页面</text>
|
||||
</view>
|
||||
</template>
|
||||
<view class="flip-item-footer" v-if="options.footerShow">
|
||||
<!-- <text class="flip-item-footer-text" :style="{
|
||||
color: options.color,
|
||||
'font-family': options.fontFamily
|
||||
}">{{filterDate()}}</text> -->
|
||||
<text class="flip-item-footer-text" :style="{
|
||||
color: options.color,
|
||||
'font-family': options.fontFamily
|
||||
}">{{filterPage(item)}}</text>
|
||||
<!-- <battery :color="options.color" style="opacity: 0.5"></battery> -->
|
||||
</view>
|
||||
</view>
|
||||
<view
|
||||
class="flip-item-bg"
|
||||
:ref="'flipItemBg_' + item.dataId"
|
||||
:class="'flip-item-bg_' + item.dataId"
|
||||
:style="{
|
||||
left: viewWidth + 'px',
|
||||
width: viewWidth + 'px',
|
||||
height: (viewHeight * 1.5) + 'px',
|
||||
transform: item.dataId < currentDataId && options.pageType == 'real' ? 'translateX(' + viewWidth + 'px)' : '',
|
||||
top: (viewHeight / 2 - (viewHeight * 1.5) / 2) + 'px',
|
||||
background: options.bgColor,
|
||||
}"></view>
|
||||
<view
|
||||
class="flip-item-shadow"
|
||||
:ref="'flipItemShadow_' + item.dataId"
|
||||
:class="'flip-item-shadow_' + item.dataId">
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
<!-- 翻页模式 -->
|
||||
|
||||
<!-- 滚动模式 -->
|
||||
<view
|
||||
class="yingbing-scroll"
|
||||
:style="{
|
||||
'background': options.bgColor,
|
||||
'visibility': pageType == 'scroll' ? 'visible' : 'hidden'
|
||||
}">
|
||||
<template v-if="pageType == 'scroll'">
|
||||
<view :style="{height: options.topGap + 'px'}"></view>
|
||||
<view class="flip-item-header" :style="{
|
||||
'padding-left': options.slide + 'px',
|
||||
'padding-right': options.slide + 'px'
|
||||
}" v-if="options.headerShow">
|
||||
<text class="flip-item-header-text" :style="{
|
||||
color: options.color,
|
||||
'font-family': options.fontFamily
|
||||
}">{{pageInfo.title || '加载中'}}</text>
|
||||
</view>
|
||||
<scroll-list
|
||||
ref="list"
|
||||
@scroll="onScroll"
|
||||
:pulldown="{show: true, color: options.color, endText: '已经到最前面了'}"
|
||||
:loadmore="{show: true, color: options.color, endText: '已经到最后面了'}"
|
||||
@pulldown="onPulldown"
|
||||
@loadmore="onLoadmore"
|
||||
@scrolltoupper="scrolltoupper">
|
||||
<view class="scroll-item-wrapper" ref="scrollItemWrapper">
|
||||
<view
|
||||
:id="'scroll-item_' + item.dataId"
|
||||
v-for="(item, index) in pages"
|
||||
:key="item.dataId + '_scroll'"
|
||||
class="scroll-item"
|
||||
:ref="'scrollItem_' + item.dataId"
|
||||
:style="{
|
||||
'padding-left': options.slide + 'px',
|
||||
'padding-right': options.slide + 'px'
|
||||
}">
|
||||
<template v-if="item.type == 'text'">
|
||||
<view class="scroll-item-text scroll-item-content"
|
||||
>
|
||||
<text class="scroll-text"
|
||||
v-for="(text, i) in item.text" :key="i"
|
||||
:style="{
|
||||
'margin-top': options.lineHeight + 'px',
|
||||
'height': options.fontSize + 'px',
|
||||
'font-size': options.fontSize + 'px',
|
||||
'color': options.color,
|
||||
'font-family': options.fontFamily
|
||||
}">{{text}}</text>
|
||||
</view>
|
||||
</template>
|
||||
<template v-else-if="item.type == 'custom'">
|
||||
<read-rich-text :richtext="item.text" :fontFace="fontFace" :pageType="pageType" @customClick="customClick"></read-rich-text>
|
||||
</template>
|
||||
<template v-else-if="item.type == 'slot'">
|
||||
<view class="scroll-slot scroll-item-content">
|
||||
<slot :name="item.text"></slot>
|
||||
</view>
|
||||
</template>
|
||||
</view>
|
||||
</view>
|
||||
<view :style="{height: options.bottomGap + 'px'}"></view>
|
||||
</scroll-list>
|
||||
<view class="flip-item-footer" :style="{
|
||||
'padding-left': options.slide + 'px',
|
||||
'padding-right': options.slide + 'px'
|
||||
}" v-if="options.footerShow">
|
||||
<text class="flip-item-footer-text" :style="{
|
||||
color: options.color,
|
||||
'font-family': options.fontFamily
|
||||
}">{{filterDate()}}</text>
|
||||
<text class="flip-item-footer-text" :style="{
|
||||
color: options.color,
|
||||
'font-family': options.fontFamily
|
||||
}">{{filterPage(pageInfo)}}</text>
|
||||
<battery ref="scrollBattery" :color="options.color" style="opacity: 0.5"></battery>
|
||||
</view>
|
||||
<view :style="{height: options.bottomGap + 'px'}"></view>
|
||||
<!-- <view class="scroll-item-header" :style="{
|
||||
'top': options.topGap + 'px',
|
||||
}" v-if="options.headerShow">
|
||||
<text class="scroll-item-header-text" :style="{
|
||||
'font-family': options.fontFamily
|
||||
}">{{(pageInfo.title || '加载中')}}</text>
|
||||
</view>
|
||||
<view class="scroll-item-footer" :style="{
|
||||
'bottom': options.bottomGap + 'px'
|
||||
}" v-if="options.footerShow">
|
||||
<text class="scroll-item-footer-text" :style="{
|
||||
'font-family': options.fontFamily
|
||||
}">{{scrollDate}}</text>
|
||||
<text class="scroll-item-footer-text" :style="{
|
||||
'font-family': options.fontFamily
|
||||
}">{{filterPage(pageInfo)}}</text>
|
||||
<battery ref="scrollBattery" color="#fff"></battery>
|
||||
</view> -->
|
||||
</template>
|
||||
</view>
|
||||
<view class="yingbing-loading" v-if="initLoading" :style="{background: options.bgColor}" @tap="reload">
|
||||
<list-loading :size="40":visible="initLoading" :color="options.color" :text="loadingText"></list-loading>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import NochapterMixin from '../modules/nochapter/nochater.js'
|
||||
import Battery from '../modules/battery.vue'
|
||||
import ReadRichText from '../modules/richtext.vue'
|
||||
import FlipMixin from '../modules/flip/flip.js'
|
||||
import ScrollMixin from '../modules/scroll/scroll.js'
|
||||
import ComputedMixin from '../modules/computed/computed.js'
|
||||
// import Computed from '../modules/computed/computed.vue'
|
||||
import ScrollList from '../modules/list/list.vue'
|
||||
import ListLoading from '../modules/list/modules/common/loading.vue'
|
||||
import Util from '../../js_sdk/util.js'
|
||||
export default {
|
||||
mixins: [FlipMixin, ScrollMixin, ComputedMixin, NochapterMixin],
|
||||
components: {
|
||||
// Computed,
|
||||
Battery,
|
||||
ReadRichText,
|
||||
ScrollList,
|
||||
ListLoading
|
||||
},
|
||||
props: {
|
||||
//字体颜色
|
||||
color: {
|
||||
type: String,
|
||||
default: '#333333'
|
||||
},
|
||||
//字体大小(单位px)
|
||||
fontSize: {
|
||||
type: [String, Number],
|
||||
default: 15
|
||||
},
|
||||
//字体名称
|
||||
fontFamily: {
|
||||
type: String,
|
||||
default: 'Microsoft YaHei, 微软雅黑'
|
||||
},
|
||||
//第三方字体
|
||||
fontFace: {
|
||||
type: Array,
|
||||
default () {
|
||||
return new Array
|
||||
}
|
||||
},
|
||||
//背景颜色
|
||||
bgColor: {
|
||||
type: String,
|
||||
default: '#fcd281'
|
||||
},
|
||||
//翻页方式
|
||||
pageType: {
|
||||
type: String,
|
||||
default: 'real'
|
||||
},
|
||||
//行间距(单位px)
|
||||
lineHeight: {
|
||||
type: [Number, String],
|
||||
default: 15
|
||||
},
|
||||
//页面左右边距(单位px)
|
||||
slide: {
|
||||
type: [Number, String],
|
||||
default: 20
|
||||
},
|
||||
//页面上边距(单位px)
|
||||
topGap: {
|
||||
type: [Number, String],
|
||||
default: 10
|
||||
},
|
||||
//页面下边距(单位px)
|
||||
bottomGap: {
|
||||
type: [Number, String],
|
||||
default: 10
|
||||
},
|
||||
//开启预加载
|
||||
enablePreload: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
//是否开启整书模式
|
||||
noChapter: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
//开启点击事件
|
||||
enableClick: {
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
//展示顶部信息
|
||||
headerShow: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
//展示底部信息
|
||||
footerShow: {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
//点击事件位置设置
|
||||
clickOption: {
|
||||
type: Object,
|
||||
default () {
|
||||
return {
|
||||
width: uni.upx2px(200),
|
||||
height: uni.upx2px(200),
|
||||
left: 'auto',
|
||||
top: 'auto'
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
data () {
|
||||
return {
|
||||
pageInfo: {
|
||||
dataId: -1
|
||||
},
|
||||
pages: [],
|
||||
contents: [],
|
||||
isClickToTouch: false,
|
||||
touchstartX: 0,
|
||||
touchstartY: 0,
|
||||
touchmoveX: 0,
|
||||
touchmoveY: 0,
|
||||
touchTime: 0,
|
||||
windowWidth: 0,
|
||||
windowHeight: 0
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
Util () {
|
||||
return Util
|
||||
},
|
||||
options () {
|
||||
return {
|
||||
pageType: this.pageType,
|
||||
color: this.color,
|
||||
bgColor: this.bgColor,
|
||||
enablePreload: this.enablePreload,
|
||||
headerShow: this.headerShow,
|
||||
footerShow: this.footerShow,
|
||||
fontFamily: this.fontFamily,
|
||||
fontFace: this.fontFace,
|
||||
slide: this.slide > 0 ? parseInt(this.slide) : 0,
|
||||
topGap: this.topGap > 0 ? parseInt(this.topGap) : 0,
|
||||
bottomGap: this.bottomGap > 0 ? parseInt(this.bottomGap) : 0,
|
||||
fontSize: this.fontSize >= 12 ? parseInt(this.fontSize) : 12,//字体大小最小只能到12px,因为谷歌浏览器最小只支持12px
|
||||
lineHeight: this.lineHeight >= 5 ? parseInt(this.lineHeight) : 5,
|
||||
}
|
||||
}
|
||||
},
|
||||
beforeDestroy () {
|
||||
if ( this.refreshTimer ) {
|
||||
clearTimeout(this.refreshTimer)
|
||||
this.refreshTimer = null
|
||||
}
|
||||
},
|
||||
mounted () {
|
||||
this.$nextTick(function () {
|
||||
setTimeout(() => {
|
||||
Util.getRect('.yingbing-read-page', this.$refs.yingbingReadPage, this).then(res => {
|
||||
this.windowWidth = res.width
|
||||
this.windowHeight = res.height
|
||||
})
|
||||
}, 20)
|
||||
})
|
||||
this.initFont()
|
||||
},
|
||||
methods: {
|
||||
touchstart (e) {
|
||||
if ( !this.enableClick ) {
|
||||
return
|
||||
}
|
||||
if ( this.isClickToTouch ) {
|
||||
return
|
||||
}
|
||||
this.resetTouch();
|
||||
this.touchInter = setTimeout(() => {
|
||||
this.touchTime = 300
|
||||
}, 300)
|
||||
let touch = e.touches[0]
|
||||
this.touchstartX = touch.pageX;
|
||||
this.touchstartY = touch.pageY;
|
||||
},
|
||||
touchmove (e) {
|
||||
if ( !this.enableClick ) {
|
||||
return
|
||||
}
|
||||
if ( this.isClickToTouch ) {
|
||||
return
|
||||
}
|
||||
let touch = e.touches[0]
|
||||
this.touchmoveX = touch.pageX;
|
||||
this.touchmoveY = touch.pageY;
|
||||
},
|
||||
touchend (e) {
|
||||
if ( this.touchInter ) {
|
||||
clearTimeout(this.touchInter);
|
||||
this.touchInter = null
|
||||
}
|
||||
if ( !this.enableClick ) {
|
||||
return
|
||||
}
|
||||
if ( this.isClickToTouch ) {
|
||||
return
|
||||
}
|
||||
this.isClickToTouch = true
|
||||
if ( this.touchTime < 300 && (Math.abs(this.touchmoveX - this.touchmoveX) <= 50 || Math.abs(this.touchmoveY - this.touchmoveY) <= 50) ) {
|
||||
let left = 0
|
||||
let top = 0
|
||||
if ( this.clickOption.left == 'auto' ) {
|
||||
left = (this.windowWidth / 2) - (this.clickOption.width / 2)
|
||||
} else if ( typeof this.clickOption.left == 'number' ) {
|
||||
left = this.clickOption.left
|
||||
} else {
|
||||
return
|
||||
}
|
||||
if ( this.clickOption.top == 'auto' ) {
|
||||
top = (this.windowHeight / 2) - (this.clickOption.height / 2)
|
||||
} else if ( typeof this.clickOption.top == 'number' ) {
|
||||
top = this.clickOption.top
|
||||
} else {
|
||||
return
|
||||
}
|
||||
let right = left + this.clickOption.width
|
||||
let bottom = top + this.clickOption.height
|
||||
if ( this.touchstartX >= left && this.touchstartX <= right && this.touchstartY >= top && this.touchstartY <= bottom ) {
|
||||
this.$emit('clickTo')
|
||||
}
|
||||
}
|
||||
setTimeout(() => {
|
||||
this.isClickToTouch = false
|
||||
}, 50)
|
||||
},
|
||||
resetTouch () {
|
||||
this.touchstartX = 0
|
||||
this.touchstartY = 0
|
||||
this.touchmoveX = 0
|
||||
this.touchmoveY = 0
|
||||
this.touchTime = 0
|
||||
},
|
||||
customClick (e) {
|
||||
this.$emit(e.name, ...e.args)
|
||||
},
|
||||
setCatalog (e) {
|
||||
this.$emit('setCatalog', e);
|
||||
},
|
||||
//初始化
|
||||
init (data) {
|
||||
if ( this.refreshTimer ) {
|
||||
clearTimeout(this.refreshTimer)
|
||||
this.refreshTimer = null
|
||||
}
|
||||
if ( !this.noChapter ) {
|
||||
this.contents = data.contents;
|
||||
this.initLoading = true;
|
||||
this.resetPage({
|
||||
start: parseInt(data.start || 0),
|
||||
currentChapter: parseInt(data.currentChapter >= 0 ? data.currentChapter : 1)
|
||||
})
|
||||
} else {
|
||||
this.computedNochapter(data);
|
||||
}
|
||||
},
|
||||
//重计算
|
||||
refresh () {
|
||||
if ( this.refreshTimer ) {
|
||||
clearTimeout(this.refreshTimer)
|
||||
this.refreshTimer = null
|
||||
}
|
||||
this.resetPage({
|
||||
start: this.pageInfo.start,
|
||||
currentChapter: this.pageInfo.chapter
|
||||
})
|
||||
},
|
||||
//跳转
|
||||
change (data) {
|
||||
if ( this.refreshTimer ) {
|
||||
clearTimeout(this.refreshTimer)
|
||||
this.refreshTimer = null
|
||||
}
|
||||
if ( data.contents && data.contents.length > 0 ) {
|
||||
data.contents.forEach(item => {
|
||||
let index = this.contents.findIndex(content => content.chapter == item.chapter)
|
||||
if (index > -1) {
|
||||
this.contents[index] = item;
|
||||
} else {
|
||||
this.contents.push(item);
|
||||
}
|
||||
})
|
||||
}
|
||||
let index = this.contents.findIndex(content => content.chapter == data.currentChapter)
|
||||
if ( index > -1 ) {
|
||||
this.initLoading = true;
|
||||
this.resetPage({
|
||||
start: parseInt(data.start || 0),
|
||||
currentChapter: parseInt(data.currentChapter || 1)
|
||||
})
|
||||
} else {
|
||||
uni.showToast({
|
||||
title: '未找到该章节内容',
|
||||
icon: 'none'
|
||||
})
|
||||
}
|
||||
},
|
||||
initFont () {
|
||||
this.fontFace.forEach(font => {
|
||||
// #ifndef APP-NVUE
|
||||
uni.loadFontFace({
|
||||
family: font.fontFamily,
|
||||
source: 'url("' + font.src + '")',
|
||||
fail (err) {
|
||||
console.log(err);
|
||||
}
|
||||
})
|
||||
// #endif
|
||||
// #ifdef APP-NVUE
|
||||
uni.requireNativePlugin('dom').addRule('fontFace', {
|
||||
fontFamily: font.fontFamily,
|
||||
src: "url('" + font.src + "')"
|
||||
})
|
||||
// #endif
|
||||
})
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
pageType (newVal, oldVal) {
|
||||
this.$nextTick(function () {
|
||||
if ( this.refreshTimer ) {
|
||||
clearTimeout(this.refreshTimer)
|
||||
this.refreshTimer = null
|
||||
}
|
||||
this.refreshTimer = setTimeout(() => {
|
||||
if ( newVal != 'scroll' ) {
|
||||
this.getViewRect().then(res => {
|
||||
this.refresh()
|
||||
})
|
||||
} else {
|
||||
this.refresh()
|
||||
}
|
||||
}, 100)
|
||||
})
|
||||
},
|
||||
fontSize () {
|
||||
this.$nextTick(function () {
|
||||
if ( this.refreshTimer ) {
|
||||
clearTimeout(this.refreshTimer)
|
||||
this.refreshTimer = null
|
||||
}
|
||||
this.refreshTimer = setTimeout(() => {
|
||||
this.refresh()
|
||||
}, 100)
|
||||
})
|
||||
},
|
||||
lineHeight () {
|
||||
this.$nextTick(function () {
|
||||
if ( this.refreshTimer ) {
|
||||
clearTimeout(this.refreshTimer)
|
||||
this.refreshTimer = null
|
||||
}
|
||||
this.refreshTimer = setTimeout(() => {
|
||||
this.refresh()
|
||||
}, 100)
|
||||
})
|
||||
},
|
||||
slide () {
|
||||
this.$nextTick(function () {
|
||||
if ( this.refreshTimer ) {
|
||||
clearTimeout(this.refreshTimer)
|
||||
this.refreshTimer = null
|
||||
}
|
||||
this.refreshTimer = setTimeout(() => {
|
||||
this.refresh()
|
||||
}, 100)
|
||||
})
|
||||
},
|
||||
topGap () {
|
||||
this.$nextTick(function () {
|
||||
if ( this.refreshTimer ) {
|
||||
clearTimeout(this.refreshTimer)
|
||||
this.refreshTimer = null
|
||||
}
|
||||
this.refreshTimer = setTimeout(() => {
|
||||
this.refresh()
|
||||
}, 100)
|
||||
})
|
||||
},
|
||||
bottomGap () {
|
||||
this.$nextTick(function () {
|
||||
if ( this.refreshTimer ) {
|
||||
clearTimeout(this.refreshTimer)
|
||||
this.refreshTimer = null
|
||||
}
|
||||
this.refreshTimer = setTimeout(() => {
|
||||
this.refresh()
|
||||
}, 100)
|
||||
})
|
||||
},
|
||||
fontFace () {
|
||||
this.initFont()
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<!-- #ifdef H5 || APP-VUE -->
|
||||
<script lang="renderjs" type="module" module="flipPage">
|
||||
export default {
|
||||
mounted() {
|
||||
window.triggerCustomClick = (name, args) => {
|
||||
// #ifndef H5
|
||||
this.$ownerInstance.callMethod('customClick', {
|
||||
name: name,
|
||||
args: args
|
||||
});
|
||||
// #endif
|
||||
// #ifdef H5
|
||||
this.customClick({
|
||||
name: name,
|
||||
args: args
|
||||
});
|
||||
// #endif
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<!-- #endif -->
|
||||
<!-- #ifdef APP-VUE || H5 || MP-QQ || MP-WEIXIN -->
|
||||
<script lang="wxs" module="flip" src="../modules/flip/flip.wxs"></script>
|
||||
<!-- #endif -->
|
||||
|
||||
<style scoped>
|
||||
@import url(../modules/flip/flip.css);
|
||||
@import url(../modules/scroll/scroll.css);
|
||||
.yingbing-read-page {
|
||||
/* #ifndef APP-NVUE */
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
/* #endif */
|
||||
/* #ifdef APP-NVUE */
|
||||
flex: 1;
|
||||
/* #endif */
|
||||
position: relative;
|
||||
}
|
||||
.yingbing-loading {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
/* #ifndef APP-NVUE */
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
/* #endif */
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
.yingbing-slot {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
/* #endif */
|
||||
flex: 1;
|
||||
}
|
||||
</style>
|
71
uni_modules/yingbing-ReadPage/css/common.css
Normal file
@ -0,0 +1,71 @@
|
||||
.yb-flex {
|
||||
/* #ifndef APP-NVUE */
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
flex-shrink: 0;
|
||||
background: none;
|
||||
flex-wrap: nowrap;
|
||||
/* #endif */
|
||||
}
|
||||
.yb-flex-1 {
|
||||
flex: 1!important;
|
||||
}
|
||||
.yb-wrap {
|
||||
flex-wrap: wrap!important;
|
||||
}
|
||||
.yb-row {
|
||||
flex-direction: row!important;
|
||||
}
|
||||
.yb-column {
|
||||
flex-direction: column!important;
|
||||
}
|
||||
.yb-row-reverse {
|
||||
flex-direction: row-reverse!important;
|
||||
}
|
||||
.yb-column-reverse {
|
||||
flex-direction: column-reverse!important;
|
||||
}
|
||||
.yb-align-center {
|
||||
align-items: center!important;
|
||||
}
|
||||
.yb-align-start {
|
||||
align-items: flex-start!important;
|
||||
}
|
||||
.yb-align-end {
|
||||
align-items: flex-end!important;
|
||||
}
|
||||
.yb-align-between {
|
||||
align-content: space-between!important;
|
||||
}
|
||||
.yb-justify-center {
|
||||
justify-content: center!important;
|
||||
}
|
||||
.yb-justify-start {
|
||||
justify-content: flex-start!important;
|
||||
}
|
||||
.yb-justify-end {
|
||||
justify-content: flex-end!important;
|
||||
}
|
||||
.yb-justify-between {
|
||||
justify-content: space-between!important;
|
||||
}
|
||||
/* #ifdef MP */
|
||||
.scoped-ref {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
box-sizing: border-box;
|
||||
overflow: hidden;
|
||||
flex-shrink: 0;
|
||||
background: none;
|
||||
flex-wrap: nowrap;
|
||||
flex: 1;
|
||||
}
|
||||
.yb-tap view:nth-child(n+2) {
|
||||
position: relative;
|
||||
}
|
||||
.yb-tap text:nth-child(n+1) {
|
||||
position: relative;
|
||||
}
|
||||
/* #endif */
|
80
uni_modules/yingbing-ReadPage/hybrid/html/richtext.html
Normal file
@ -0,0 +1,80 @@
|
||||
<html>
|
||||
<head>
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, viewport-fit=cover"
|
||||
/>
|
||||
<title>富文本展示</title>
|
||||
<style type="text/css">
|
||||
html,body {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
}
|
||||
.content {
|
||||
color: #333;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
.content img {
|
||||
max-width: 100%!important;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<p id="content" class="content"></p>
|
||||
</body>
|
||||
<script type="text/javascript" src="./js/uni-webview-js@1.5.4.js"></script>
|
||||
<script type="text/javascript">
|
||||
var obj = {}
|
||||
window.onload = function () {
|
||||
var search = window.location.search.slice(1);
|
||||
var arr = search.split('&');
|
||||
for ( var i = 0; i < arr.length; i++ ) {
|
||||
var strs = arr[i].split('=');
|
||||
obj[strs[0]] = strs[1];
|
||||
}
|
||||
if ( obj.rich ) {
|
||||
setRichText(JSON.parse(decodeURIComponent(obj.rich)));
|
||||
if ( obj.pageType == 'scroll' ) {
|
||||
window.setTimeout(function () {
|
||||
uni.postMessage({
|
||||
data: {
|
||||
height: document.getElementById('content').scrollHeight + 100
|
||||
}
|
||||
});
|
||||
}, 100)
|
||||
}
|
||||
}
|
||||
}
|
||||
function setStyle (attribute, value) {
|
||||
document.getElementById('content').style[attribute] = value
|
||||
}
|
||||
function setRichText (richText) {
|
||||
document.getElementById('content').innerHTML = richText
|
||||
}
|
||||
function setFontFace (fontList) {
|
||||
let code = fontList.reduce((accumulator, currentValue) => {
|
||||
return accumulator + `@font-face { font-family: ${currentValue.fontFamily};src: url('${currentValue.src}'); }`;
|
||||
}, "");
|
||||
var style = document.createElement("style");
|
||||
style.type = "text/css";
|
||||
style.rel = "stylesheet";
|
||||
style.appendChild(document.createTextNode(code));
|
||||
var head = document.getElementsByTagName("head")[0];
|
||||
head.appendChild(style);
|
||||
}
|
||||
function triggerCustomClick (name, args) {
|
||||
uni.postMessage({
|
||||
data: {
|
||||
customClick: {
|
||||
name: name,
|
||||
args: args
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</html>
|
446
uni_modules/yingbing-ReadPage/js_sdk/util.js
Normal file
@ -0,0 +1,446 @@
|
||||
|
||||
export default {
|
||||
/**
|
||||
* 补零
|
||||
* @param {Number} val 数字
|
||||
**/
|
||||
zeroize (val) {
|
||||
return zeroize(val);
|
||||
},
|
||||
/**
|
||||
* 时间格式化
|
||||
* @param {String} time 时间戳or时间
|
||||
**/
|
||||
dateFormat (time, formats = 'yyyy-mm-dd hh:mm:ss') {
|
||||
let arr = formats.split(' ')
|
||||
let dateFormats = ''
|
||||
let timeFormats = ''
|
||||
arr.forEach(item => {
|
||||
if ( item.indexOf('yy') > -1 ) {
|
||||
dateFormats = item
|
||||
} else {
|
||||
timeFormats = item
|
||||
}
|
||||
})
|
||||
const d = new Date(time);
|
||||
let result = ''
|
||||
if ( dateFormats.indexOf('yyyy') > -1 ) {
|
||||
result += d.getFullYear() + '-'
|
||||
}
|
||||
if ( dateFormats.indexOf('mm') > -1 ) {
|
||||
result += zeroize(d.getMonth() + 1) + '-'
|
||||
}
|
||||
if ( dateFormats.indexOf('dd') > -1 ) {
|
||||
result += zeroize(d.getDate()) + ' '
|
||||
}
|
||||
if ( timeFormats.indexOf('hh') > -1 ) {
|
||||
result += zeroize(d.getHours()) + ':'
|
||||
}
|
||||
if ( timeFormats.indexOf('mm') > -1 ) {
|
||||
result += zeroize(d.getMinutes()) + ':'
|
||||
}
|
||||
if ( timeFormats.indexOf('ss') > -1 ) {
|
||||
result += zeroize(d.getSeconds()) + ':'
|
||||
}
|
||||
return result.substring(0, result.length - 1)
|
||||
},
|
||||
/**
|
||||
* 秒数转化为分秒
|
||||
* @param {String} value 秒数
|
||||
**/
|
||||
minutesFormat (value) {
|
||||
let minutes = Math.floor(value / 60 % 60) >= 10 ? Math.floor(value / 60 % 60) : '0' + Math.floor(value / 60 % 60);
|
||||
let seconds = Math.floor(value % 60) >= 10 ? Math.floor(value % 60) : '0' + Math.floor(value % 60);
|
||||
return minutes + ':' + seconds;
|
||||
},
|
||||
/**
|
||||
* 时间转化为秒数
|
||||
* @param {String} time 时间(HH:mm:ss)
|
||||
**/
|
||||
time2seconds (time){
|
||||
const seconds = parseInt(time.split(':')[0]) * 60 + parseInt(time.split(':')[1].split('.')[0]) + parseInt(time.split(':')[1].split('.')[1]) / 1000;
|
||||
return seconds;
|
||||
},
|
||||
/**
|
||||
* 移除url地址域名
|
||||
* @param {String} str http地址
|
||||
**/
|
||||
removeUrl (url) {
|
||||
let str = url.replace(/^http:\/\/[^/]+/, '');
|
||||
return str.substr(1);
|
||||
},
|
||||
/**
|
||||
* 获取文件后缀
|
||||
* @param {String} name 带后缀的文件名称
|
||||
**/
|
||||
suffix (name) {
|
||||
//获取图片后缀
|
||||
let fileName = name.lastIndexOf(".");
|
||||
let fileNameLength = name.length;
|
||||
let fileFormat = name.substring(fileName + 1, fileNameLength);
|
||||
return fileFormat;
|
||||
},
|
||||
|
||||
/**
|
||||
* 清除文件后缀
|
||||
* @param {String} name 带后缀的文件名称
|
||||
*/
|
||||
removeSuffix (name) {
|
||||
//获取图片后缀
|
||||
let fileName = name.lastIndexOf(".");
|
||||
if ( fileName > -1 ) {
|
||||
let fileNameFormat = name.substring(0, fileName);
|
||||
return fileNameFormat;
|
||||
} else {
|
||||
return name
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 数组查找符合条件元素并返回下标
|
||||
* @param {Array} arr 传入数组
|
||||
* @param {String} value 条件元素
|
||||
* @param {String} query 对比key值
|
||||
*/
|
||||
indexOf (arr, query, value) {
|
||||
let len = arr.length;
|
||||
for ( let i = 0; i < len; i++ ) {
|
||||
if ( arr[i][query] == value ) {
|
||||
return parseInt(i);
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
},
|
||||
|
||||
/**
|
||||
* 正则匹配
|
||||
* @param {String} type 匹配类型
|
||||
* @param {String} value 匹配值
|
||||
*/
|
||||
reg (type, value) {
|
||||
const regs = {
|
||||
//身份证证则
|
||||
idcard: new RegExp(/^[1-9]\d{7}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}$|^[1-9]\d{5}[1-9]\d{3}((0\d)|(1[0-2]))(([0|1|2]\d)|3[0-1])\d{3}([0-9]|X)$/),
|
||||
//手机正则
|
||||
mobile: new RegExp(/^1[3456789]\d{9}$/),
|
||||
//固定电话正则
|
||||
phone: new RegExp(/^(\(\d{3,4}\)|\d{3,4}-|\s)?\d{7,14}$/),
|
||||
//金额验证
|
||||
price: new RegExp(/^[1-9]\d*(,\d{3})*(\.\d{1,2})?$|^0.\d{1,2}$/),
|
||||
//邮箱验证
|
||||
email: new RegExp(/^\w+((-\w+)|(\.\w+))*\@[A-Za-z0-9]+((\.|-)[A-Za-z0-9]+)*\.[A-Za-z0-9]+$/),
|
||||
//银行卡
|
||||
bankcard: new RegExp(/^([1-9]{1})(\d{15}|\d{18})$/)
|
||||
}
|
||||
return regs[type].test(value);
|
||||
},
|
||||
/**
|
||||
* 计算2个时间差的分钟数或者秒钟数或时钟数
|
||||
* @param {datetime} time1 开始时间
|
||||
* @param {datetime} time2 结束时间
|
||||
*/
|
||||
timeMinuse (time1, time2, type = 'minutes') {
|
||||
//判断开始时间是否大于结束日期
|
||||
let date1 = new Date(time1);
|
||||
let date2 = new Date(time2);
|
||||
if ( date1 > date2 ) {
|
||||
console.log("开始时间不能大于结束时间!");
|
||||
return false;
|
||||
}
|
||||
let seconds = date2.getTime() / 1000 - date1.getTime() / 1000;
|
||||
return type == 'minutes' ? (seconds / 60) : type == 'hours' ? (seconds / 60 / 60) : seconds;
|
||||
},
|
||||
/**
|
||||
* 判断值类型返回字符
|
||||
* @param {datetime} value 需要判断类型的值
|
||||
*/
|
||||
typeof (value) {
|
||||
let type = Object.prototype.toString.call(value);
|
||||
return type.slice(8, type.length - 1)
|
||||
},
|
||||
/**
|
||||
* 生成随机字符串
|
||||
* @param {Number} len 长度
|
||||
*/
|
||||
randomString (len) {
|
||||
len = len || 32;
|
||||
var $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678'; /****默认去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1****/
|
||||
var maxPos = $chars.length;
|
||||
var pwd = '';
|
||||
for (let i = 0; i < len; i++) {
|
||||
pwd += $chars.charAt(Math.floor(Math.random() * maxPos));
|
||||
}
|
||||
return pwd;
|
||||
},
|
||||
|
||||
/**
|
||||
* 生成随机ID
|
||||
*/
|
||||
randomID () {
|
||||
let mydate = new Date();
|
||||
return mydate.getMinutes() + mydate.getSeconds() + mydate.getMilliseconds() + Math.round(Math.random() * 10000);
|
||||
},
|
||||
|
||||
/**
|
||||
* 生成随机不重复整数
|
||||
* @param {Number} len 长度
|
||||
*/
|
||||
randomSoleNumber (len) {
|
||||
let min = 0;
|
||||
let max = len - 1;
|
||||
let arr = [];
|
||||
while ( arr.length < len ) {
|
||||
let value = Math.floor(Math.random() * (max - min + 1)) + min;
|
||||
if ( arr.indexOf(value) == -1 ) {
|
||||
arr.push( value )
|
||||
}
|
||||
}
|
||||
return arr;
|
||||
},
|
||||
|
||||
/**
|
||||
* 16进制颜色转化为rgb
|
||||
* @param {String} hex 16进制颜色
|
||||
*/
|
||||
hex2rgb (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
|
||||
},
|
||||
|
||||
/**
|
||||
* 16进制颜色转化为rgba
|
||||
* @param {String} hex 16进制颜色
|
||||
*/
|
||||
hex2rgba (hex, opacity) {
|
||||
hex = hex.length == 7 ? hex : '#' + hex.slice(1, 4) + hex.slice(1, 4)
|
||||
let str="rgba("
|
||||
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+","+opacity+")";
|
||||
return str
|
||||
},
|
||||
|
||||
/**
|
||||
* byte转化为文件大小
|
||||
* @param {Number} byte 位
|
||||
*/
|
||||
byte2Size (byte) {
|
||||
let sizeString = ''
|
||||
if(byte == 0){
|
||||
sizeString = "0B";
|
||||
}else if(byte < 1024){
|
||||
sizeString = byte + "B";
|
||||
}else if(byte < 1048576){
|
||||
sizeString = (byte/1024).toFixed(2) + "KB";
|
||||
}else if (byte < 1073741824){
|
||||
sizeString = (byte/1048576).toFixed(2) + "MB";
|
||||
}else{
|
||||
sizeString = (byte/1073741824).toFixed(2) + "GB";
|
||||
}
|
||||
return sizeString;
|
||||
},
|
||||
|
||||
// 深度克隆
|
||||
deepClone (obj) {
|
||||
if(typeof obj !== "object" && typeof obj !== 'function') {
|
||||
//原始类型直接返回
|
||||
return obj;
|
||||
}
|
||||
var o = isArray(obj) ? [] : {};
|
||||
for(let i in obj) {
|
||||
if(obj.hasOwnProperty(i)){
|
||||
o[i] = typeof obj[i] === "object" ? this.deepClone(obj[i]) : obj[i];
|
||||
}
|
||||
}
|
||||
return o;
|
||||
},
|
||||
|
||||
/**
|
||||
* 将数字转为带中文单位的字符串
|
||||
* @param {Number} num 数字
|
||||
*/
|
||||
numtounit (num) {
|
||||
let units = [{
|
||||
label: '万',
|
||||
value: 10000,
|
||||
min: 1000
|
||||
},{
|
||||
label: '亿',
|
||||
value: 100000000,
|
||||
min: 100000000
|
||||
},{
|
||||
label: '兆',
|
||||
value: 10000000000000000,
|
||||
min: 100000000000000000
|
||||
}]
|
||||
let value = num
|
||||
units.forEach(unit => {
|
||||
if ( num >= unit.min ) {
|
||||
value = (num / unit.value).toFixed(2) + unit.label
|
||||
}
|
||||
})
|
||||
return value
|
||||
},
|
||||
/**
|
||||
* 判断像素单位,没有则加上rpx
|
||||
* @param {String} value 像素
|
||||
*/
|
||||
pixelunit (value) {
|
||||
if ( value.toString().indexOf('px') > -1 || value.toString().indexOf('em') > -1 || value.toString().indexOf('auto') > -1 || value.toString().indexOf('%') > -1 ) {
|
||||
return value
|
||||
} else {
|
||||
return value + 'rpx'
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 判断像素单位,全部转为px
|
||||
* @param {String} value 像素
|
||||
*/
|
||||
unitpixel (value) {
|
||||
if ( value.toString().indexOf('rpx') > -1 ) {
|
||||
return uni.upx2px(value.replace('rpx', ''))
|
||||
} else if ( value.toString().indexOf('px') > -1 ) {
|
||||
return parseFloat(value.replace('px', ''))
|
||||
} else if ( value.toString().indexOf('em') > -1 || value.toString().indexOf('auto') > -1 || value.toString().indexOf('%') > -1 ) {
|
||||
return value
|
||||
} else {
|
||||
return parseFloat(uni.upx2px(value))
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 判断像素单位,转化为rpx
|
||||
* @param {String} value 值
|
||||
* @param {String} unit 返回结果是否带上单位
|
||||
*/
|
||||
anytorpx (value, unit = true) {
|
||||
if ( value.toString().indexOf('rpx') > -1 ) {
|
||||
return unit ? value : parseFloat(value.replace('rpx', ''))
|
||||
} else if ( value.toString().indexOf('px') > -1 ) {
|
||||
return parseFloat(value.replace('px', '') * (750 / uni.getSystemInfoSync().windowWidth)) + (unit ? 'rpx' : 0)
|
||||
} else if ( value.toString().indexOf('auto') > -1 ) {
|
||||
return 'auto'
|
||||
} else if ( value.toString().indexOf('%') > -1 ) {
|
||||
return parseFloat((value.replace('%', '') / 100) * 750) + (unit ? 'rpx' : 0)
|
||||
} else if (value.toString().indexOf('em') > -1 || value.toString().indexOf('rem') > -1 ) {
|
||||
return parseFloat(value.replace('em', '').replace('rem', '') * 32) + (unit ? 'rpx' : 0)
|
||||
} else if ( /^\d+$/.test(value) ) {
|
||||
return parseFloat(value) + (unit ? 'rpx' : 0)
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 判断像素单位,转化为px
|
||||
* @param {String} value 值
|
||||
* @param {String} unit 返回结果是否带上单位
|
||||
*/
|
||||
anytopx (value, unit = false) {
|
||||
if ( value.toString().indexOf('rpx') > -1 ) {
|
||||
return uni.upx2px(value.replace('rpx', '')) + (unit ? 'px' : 0)
|
||||
} else if ( value.toString().indexOf('px') > -1 ) {
|
||||
return parseFloat(value.replace('px', '')) + (unit ? 'px' : 0)
|
||||
} else if ( value.toString().indexOf('auto') > -1 ) {
|
||||
return 'auto'
|
||||
} else if ( value.toString().indexOf('%') > -1 ) {
|
||||
return parseFloat((value.replace('%', '') / 100) * uni.getSystemInfoSync().windowWidth) + (unit ? 'px' : 0)
|
||||
} else if (value.toString().indexOf('em') > -1 || value.toString().indexOf('rem') > -1 ) {
|
||||
return parseFloat(value.replace('em', '').replace('rem', '') * uni.getSystemInfoSync().windowWidth) + (unit ? 'px' : 0)
|
||||
} else if ( /^\d+$/.test(value) ) {
|
||||
return parseFloat(value) + (unit ? 'px' : 0)
|
||||
}
|
||||
},
|
||||
getRefs (components, name, current) {
|
||||
// #ifndef MP
|
||||
return current >= 0 ? components.$refs[name][current] : components.$refs[name]
|
||||
// #endif
|
||||
// #ifdef MP
|
||||
return {}
|
||||
// #endif
|
||||
},
|
||||
//获取节点
|
||||
getEl (el) {
|
||||
if (typeof el === 'string' || typeof el === 'number') return el;
|
||||
if (WXEnvironment) {
|
||||
return el.ref;
|
||||
} else {
|
||||
return el instanceof HTMLElement ? el : el.$el;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* 获取指定父节点
|
||||
* @param {String} components 当前实例
|
||||
* @param {String} name 父节点名称
|
||||
*/
|
||||
getParent(name, components) {
|
||||
let parent = components.$parent
|
||||
if (parent) {
|
||||
let parentName = parent.$options.name
|
||||
while (parentName !== name) {
|
||||
parent = parent.$parent
|
||||
if (parent) {
|
||||
parentName = parent.$options.name
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
return parent
|
||||
}
|
||||
return null
|
||||
},
|
||||
/**
|
||||
* 获取指定子节点
|
||||
* @param {String} components 当前实例
|
||||
* @param {String} name 父节点名称
|
||||
*/
|
||||
getChildrens(names, components) {
|
||||
let arr = []
|
||||
let childs = names.split(',')
|
||||
const dowhile = (children) => {
|
||||
if ( this.typeof(children) == 'Array' ) {
|
||||
children.forEach(child => {
|
||||
if ( childs.indexOf(child.$options.name) > -1 ) {
|
||||
arr.push(child)
|
||||
}
|
||||
if ( child.$children && child.$children.length > 0 ) {
|
||||
dowhile(child.$children)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
dowhile(components.$children)
|
||||
return arr;
|
||||
},
|
||||
/**
|
||||
* 获取指定子节点
|
||||
* @param {String} selector 节点class或者id
|
||||
* @param {String} el 节点
|
||||
* @param {String} components 当前实例
|
||||
*/
|
||||
getRect (selector, el, components) {
|
||||
return new Promise(resolve => {
|
||||
// #ifdef APP-NVUE
|
||||
uni.requireNativePlugin('dom').getComponentRect(el, res => {
|
||||
resolve(res.size)
|
||||
})
|
||||
// #endif
|
||||
// #ifndef APP-NVUE
|
||||
uni.createSelectorQuery().in(components).select(selector).boundingClientRect(data => {
|
||||
resolve(data)
|
||||
}).exec();
|
||||
// #endif
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// 判断arr是否为一个数组,返回一个bool值
|
||||
function isArray (arr) {
|
||||
return Object.prototype.toString.call(arr) === '[object Array]';
|
||||
}
|
||||
|
||||
function zeroize (val) {
|
||||
return val >= 10 ? val : '0' + val;
|
||||
}
|
80
uni_modules/yingbing-ReadPage/package.json
Normal file
@ -0,0 +1,80 @@
|
||||
{
|
||||
"id": "yingbing-ReadPage",
|
||||
"displayName": "小说阅读分页插件",
|
||||
"version": "1.4.3",
|
||||
"description": "给小说分页的插件,包含翻页",
|
||||
"keywords": [
|
||||
"小说",
|
||||
"阅读",
|
||||
"翻页",
|
||||
"分页"
|
||||
],
|
||||
"repository": "https://gitee.com/yingbing-developer/yingbing-read-page.git",
|
||||
"engines": {
|
||||
"HBuilderX": "^3.1.0"
|
||||
},
|
||||
"dcloudext": {
|
||||
"sale": {
|
||||
"regular": {
|
||||
"price": "0.00"
|
||||
},
|
||||
"sourcecode": {
|
||||
"price": "0.00"
|
||||
}
|
||||
},
|
||||
"contact": {
|
||||
"qq": "1014295211"
|
||||
},
|
||||
"declaration": {
|
||||
"ads": "无",
|
||||
"data": "无",
|
||||
"permissions": "无"
|
||||
},
|
||||
"npmurl": "",
|
||||
"type": "component-vue"
|
||||
},
|
||||
"uni_modules": {
|
||||
"dependencies": [],
|
||||
"encrypt": [],
|
||||
"platforms": {
|
||||
"cloud": {
|
||||
"tcb": "y",
|
||||
"aliyun": "y"
|
||||
},
|
||||
"client": {
|
||||
"App": {
|
||||
"app-vue": "y",
|
||||
"app-nvue": "y"
|
||||
},
|
||||
"H5-mobile": {
|
||||
"Safari": "y",
|
||||
"Android Browser": "y",
|
||||
"微信浏览器(Android)": "y",
|
||||
"QQ浏览器(Android)": "y"
|
||||
},
|
||||
"H5-pc": {
|
||||
"Chrome": "n",
|
||||
"IE": "n",
|
||||
"Edge": "n",
|
||||
"Firefox": "n",
|
||||
"Safari": "n"
|
||||
},
|
||||
"小程序": {
|
||||
"微信": "y",
|
||||
"阿里": "n",
|
||||
"百度": "n",
|
||||
"字节跳动": "n",
|
||||
"QQ": "n"
|
||||
},
|
||||
"快应用": {
|
||||
"华为": "u",
|
||||
"联盟": "u"
|
||||
},
|
||||
"Vue": {
|
||||
"vue2": "y",
|
||||
"vue3": "u"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
635
uni_modules/yingbing-ReadPage/readme.md
Normal file
0
unpackage/dist/dev/.automator/app-plus/.automator.json
vendored
Normal file
1
unpackage/dist/dev/app-plus/__uniappchooselocation.js
vendored
Normal file
BIN
unpackage/dist/dev/app-plus/__uniapperror.png
vendored
Normal file
After Width: | Height: | Size: 5.7 KiB |
1
unpackage/dist/dev/app-plus/__uniappes6.js
vendored
Normal file
1
unpackage/dist/dev/app-plus/__uniappopenlocation.js
vendored
Normal file
1
unpackage/dist/dev/app-plus/__uniapppicker.js
vendored
Normal file
8
unpackage/dist/dev/app-plus/__uniappquill.js
vendored
Normal file
1
unpackage/dist/dev/app-plus/__uniappquillimageresize.js
vendored
Normal file
1
unpackage/dist/dev/app-plus/__uniappscan.js
vendored
Normal file
BIN
unpackage/dist/dev/app-plus/__uniappsuccess.png
vendored
Normal file
After Width: | Height: | Size: 2.0 KiB |
25
unpackage/dist/dev/app-plus/__uniappview.html
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<script>
|
||||
var __UniViewStartTime__ = Date.now();
|
||||
var coverSupport = 'CSS' in window && typeof CSS.supports === 'function' && (CSS.supports('top: env(a)') ||
|
||||
CSS.supports('top: constant(a)'))
|
||||
document.write(
|
||||
'<meta name="viewport" content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0' +
|
||||
(coverSupport ? ', viewport-fit=cover' : '') + '" />')
|
||||
</script>
|
||||
<title>View</title>
|
||||
<link rel="stylesheet" href="view.css" />
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div id="app"></div>
|
||||
<script src="__uniappes6.js"></script>
|
||||
<script src="view.umd.min.js"></script>
|
||||
<script src="app-view.js"></script>
|
||||
</body>
|
||||
|
||||
</html>
|
8
unpackage/dist/dev/app-plus/app-config-service.js
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
|
||||
var isReady=false;var onReadyCallbacks=[];
|
||||
var isServiceReady=false;var onServiceReadyCallbacks=[];
|
||||
var __uniConfig = {"pages":["pages/bookCity/bookCity/index","pages/bookshelf/bookshelf/index","pages/login/login","pages/loginMobile/loginMobile","pages/classification/classification/index","pages/myInfo/myInfo/index","pages/myInfo/aboutMy/index","pages/myInfo/mySetUp/index","pages/myInfo/problemList/index","pages/myInfo/problemDetail/index","pages/voucherCenter/index","pages/readingRecords/index","pages/signInBookCurrency/index","pages/booksListAll/index","pages/booksTheCharts/index","pages/voucherCenterDetail/index","pages/bookCoinDetail/bookCoinDetail","pages/giveCoinDetail/giveCoinDetail","pages/booksSearchList/booksSearchList","pages/novelReading/novelReading","pages/booksReadingDetail/booksReadingDetail","pages/bookRecommendList/bookRecommendList"],"window":{"navigationBarTextStyle":"black","navigationBarTitleText":"uni-app","navigationBarBackgroundColor":"#F8F8F8","backgroundColor":"#F8F8F8"},"tabBar":{"backgroundColor":"#FFFFFF","borderStyle":"#F5F5F5","selectedColor":"#FF5000","iconWidth":"24px","color":"#666666","fontSize":"10px","height":"68px","list":[{"pagePath":"pages/bookshelf/bookshelf/index","text":"书架","iconPath":"/static/images/tabbar/bookshelf.png","selectedIconPath":"/static/images/tabbar/bookshelfActive.png"},{"pagePath":"pages/bookCity/bookCity/index","text":"书城","iconPath":"/static/images/tabbar/bookCity.png","selectedIconPath":"/static/images/tabbar/bookCityActive.png"},{"pagePath":"pages/classification/classification/index","text":"分类","iconPath":"/static/images/tabbar/classification.png","selectedIconPath":"/static/images/tabbar/classificationActive.png"},{"pagePath":"pages/myInfo/myInfo/index","text":"我的","iconPath":"/static/images/tabbar/myInfo.png","selectedIconPath":"/static/images/tabbar/myInfoActive.png"}]},"darkmode":false,"nvueCompiler":"uni-app","nvueStyleCompiler":"uni-app","renderer":"auto","splashscreen":{"alwaysShowBeforeRender":true,"autoclose":false},"appname":"app_ancientSayings","compilerVersion":"3.8.12","entryPagePath":"pages/bookCity/bookCity/index","networkTimeout":{"request":60000,"connectSocket":60000,"uploadFile":60000,"downloadFile":60000}};
|
||||
var __uniRoutes = [{"path":"/pages/bookCity/bookCity/index","meta":{"isQuit":true,"isTabBar":true},"window":{"navigationBarTitleText":"书城","navigationStyle":"custom"}},{"path":"/pages/bookshelf/bookshelf/index","meta":{"isQuit":true,"isTabBar":true},"window":{"navigationBarTitleText":"书架","navigationStyle":"custom"}},{"path":"/pages/login/login","meta":{},"window":{"navigationBarTitleText":"","navigationStyle":"custom"}},{"path":"/pages/loginMobile/loginMobile","meta":{},"window":{"navigationBarTitleText":"","navigationStyle":"custom"}},{"path":"/pages/classification/classification/index","meta":{"isQuit":true,"isTabBar":true},"window":{"navigationBarTitleText":"分类","navigationStyle":"custom"}},{"path":"/pages/myInfo/myInfo/index","meta":{"isQuit":true,"isTabBar":true},"window":{"navigationBarTitleText":"我的"}},{"path":"/pages/myInfo/aboutMy/index","meta":{},"window":{"navigationBarTitleText":"关于我们"}},{"path":"/pages/myInfo/mySetUp/index","meta":{},"window":{"navigationBarTitleText":"设置"}},{"path":"/pages/myInfo/problemList/index","meta":{},"window":{"navigationBarTitleText":"常见问题"}},{"path":"/pages/myInfo/problemDetail/index","meta":{},"window":{"navigationBarTitleText":"常见问题"}},{"path":"/pages/voucherCenter/index","meta":{},"window":{"navigationBarTitleText":"充值中心"}},{"path":"/pages/readingRecords/index","meta":{},"window":{"navigationBarTitleText":"阅读记录"}},{"path":"/pages/signInBookCurrency/index","meta":{},"window":{"navigationBarTitleText":"签到领取书币","navigationStyle":"custom","navigationBarTextStyle":"white"}},{"path":"/pages/booksListAll/index","meta":{},"window":{"navigationBarTitleText":""}},{"path":"/pages/booksTheCharts/index","meta":{},"window":{"navigationBarTitleText":"排行榜","navigationBarBackgroundColor":"transparent","navigationStyle":"custom","titleNView":{"backgroundColor":"transparent","background":"transparent"}}},{"path":"/pages/voucherCenterDetail/index","meta":{},"window":{"navigationBarTitleText":"充值记录"}},{"path":"/pages/bookCoinDetail/bookCoinDetail","meta":{},"window":{"navigationBarTitleText":"书币明细"}},{"path":"/pages/giveCoinDetail/giveCoinDetail","meta":{},"window":{"navigationBarTitleText":"赠币明细"}},{"path":"/pages/booksSearchList/booksSearchList","meta":{},"window":{"navigationBarTitleText":"搜索"}},{"path":"/pages/novelReading/novelReading","meta":{},"window":{"navigationBarTitleText":"","navigationStyle":"custom","enablePullDownRefresh":false}},{"path":"/pages/booksReadingDetail/booksReadingDetail","meta":{},"window":{"navigationBarTitleText":""}},{"path":"/pages/bookRecommendList/bookRecommendList","meta":{},"window":{"navigationBarTitleText":""}}];
|
||||
__uniConfig.onReady=function(callback){if(__uniConfig.ready){callback()}else{onReadyCallbacks.push(callback)}};Object.defineProperty(__uniConfig,"ready",{get:function(){return isReady},set:function(val){isReady=val;if(!isReady){return}const callbacks=onReadyCallbacks.slice(0);onReadyCallbacks.length=0;callbacks.forEach(function(callback){callback()})}});
|
||||
__uniConfig.onServiceReady=function(callback){if(__uniConfig.serviceReady){callback()}else{onServiceReadyCallbacks.push(callback)}};Object.defineProperty(__uniConfig,"serviceReady",{get:function(){return isServiceReady},set:function(val){isServiceReady=val;if(!isServiceReady){return}const callbacks=onServiceReadyCallbacks.slice(0);onServiceReadyCallbacks.length=0;callbacks.forEach(function(callback){callback()})}});
|
||||
service.register("uni-app-config",{create(a,b,c){if(!__uniConfig.viewport){var d=b.weex.config.env.scale,e=b.weex.config.env.deviceWidth,f=Math.ceil(e/d);Object.assign(__uniConfig,{viewport:f,defaultFontSize:Math.round(f/20)})}return{instance:{__uniConfig:__uniConfig,__uniRoutes:__uniRoutes,global:void 0,window:void 0,document:void 0,frames:void 0,self:void 0,location:void 0,navigator:void 0,localStorage:void 0,history:void 0,Caches:void 0,screen:void 0,alert:void 0,confirm:void 0,prompt:void 0,fetch:void 0,XMLHttpRequest:void 0,WebSocket:void 0,webkit:void 0,print:void 0}}}});
|
154
unpackage/dist/dev/app-plus/app-config.js
vendored
Normal file
@ -0,0 +1,154 @@
|
||||
/******/ (function(modules) { // webpackBootstrap
|
||||
/******/ // install a JSONP callback for chunk loading
|
||||
/******/ function webpackJsonpCallback(data) {
|
||||
/******/ var chunkIds = data[0];
|
||||
/******/ var moreModules = data[1];
|
||||
/******/ var executeModules = data[2];
|
||||
/******/
|
||||
/******/ // add "moreModules" to the modules object,
|
||||
/******/ // then flag all "chunkIds" as loaded and fire callback
|
||||
/******/ var moduleId, chunkId, i = 0, resolves = [];
|
||||
/******/ for(;i < chunkIds.length; i++) {
|
||||
/******/ chunkId = chunkIds[i];
|
||||
/******/ if(Object.prototype.hasOwnProperty.call(installedChunks, chunkId) && installedChunks[chunkId]) {
|
||||
/******/ resolves.push(installedChunks[chunkId][0]);
|
||||
/******/ }
|
||||
/******/ installedChunks[chunkId] = 0;
|
||||
/******/ }
|
||||
/******/ for(moduleId in moreModules) {
|
||||
/******/ if(Object.prototype.hasOwnProperty.call(moreModules, moduleId)) {
|
||||
/******/ modules[moduleId] = moreModules[moduleId];
|
||||
/******/ }
|
||||
/******/ }
|
||||
/******/ if(parentJsonpFunction) parentJsonpFunction(data);
|
||||
/******/
|
||||
/******/ while(resolves.length) {
|
||||
/******/ resolves.shift()();
|
||||
/******/ }
|
||||
/******/
|
||||
/******/ // add entry modules from loaded chunk to deferred list
|
||||
/******/ deferredModules.push.apply(deferredModules, executeModules || []);
|
||||
/******/
|
||||
/******/ // run deferred modules when all chunks ready
|
||||
/******/ return checkDeferredModules();
|
||||
/******/ };
|
||||
/******/ function checkDeferredModules() {
|
||||
/******/ var result;
|
||||
/******/ for(var i = 0; i < deferredModules.length; i++) {
|
||||
/******/ var deferredModule = deferredModules[i];
|
||||
/******/ var fulfilled = true;
|
||||
/******/ for(var j = 1; j < deferredModule.length; j++) {
|
||||
/******/ var depId = deferredModule[j];
|
||||
/******/ if(installedChunks[depId] !== 0) fulfilled = false;
|
||||
/******/ }
|
||||
/******/ if(fulfilled) {
|
||||
/******/ deferredModules.splice(i--, 1);
|
||||
/******/ result = __webpack_require__(__webpack_require__.s = deferredModule[0]);
|
||||
/******/ }
|
||||
/******/ }
|
||||
/******/
|
||||
/******/ return result;
|
||||
/******/ }
|
||||
/******/
|
||||
/******/ // The module cache
|
||||
/******/ var installedModules = {};
|
||||
/******/
|
||||
/******/ // object to store loaded and loading chunks
|
||||
/******/ // undefined = chunk not loaded, null = chunk preloaded/prefetched
|
||||
/******/ // Promise = chunk loading, 0 = chunk loaded
|
||||
/******/ var installedChunks = {
|
||||
/******/ "app-config": 0
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ var deferredModules = [];
|
||||
/******/
|
||||
/******/ // The require function
|
||||
/******/ function __webpack_require__(moduleId) {
|
||||
/******/
|
||||
/******/ // Check if module is in cache
|
||||
/******/ if(installedModules[moduleId]) {
|
||||
/******/ return installedModules[moduleId].exports;
|
||||
/******/ }
|
||||
/******/ // Create a new module (and put it into the cache)
|
||||
/******/ var module = installedModules[moduleId] = {
|
||||
/******/ i: moduleId,
|
||||
/******/ l: false,
|
||||
/******/ exports: {}
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Execute the module function
|
||||
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
|
||||
/******/
|
||||
/******/ // Flag the module as loaded
|
||||
/******/ module.l = true;
|
||||
/******/
|
||||
/******/ // Return the exports of the module
|
||||
/******/ return module.exports;
|
||||
/******/ }
|
||||
/******/
|
||||
/******/
|
||||
/******/ // expose the modules object (__webpack_modules__)
|
||||
/******/ __webpack_require__.m = modules;
|
||||
/******/
|
||||
/******/ // expose the module cache
|
||||
/******/ __webpack_require__.c = installedModules;
|
||||
/******/
|
||||
/******/ // define getter function for harmony exports
|
||||
/******/ __webpack_require__.d = function(exports, name, getter) {
|
||||
/******/ if(!__webpack_require__.o(exports, name)) {
|
||||
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
|
||||
/******/ }
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // define __esModule on exports
|
||||
/******/ __webpack_require__.r = function(exports) {
|
||||
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
|
||||
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
||||
/******/ }
|
||||
/******/ Object.defineProperty(exports, '__esModule', { value: true });
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // create a fake namespace object
|
||||
/******/ // mode & 1: value is a module id, require it
|
||||
/******/ // mode & 2: merge all properties of value into the ns
|
||||
/******/ // mode & 4: return value when already ns object
|
||||
/******/ // mode & 8|1: behave like require
|
||||
/******/ __webpack_require__.t = function(value, mode) {
|
||||
/******/ if(mode & 1) value = __webpack_require__(value);
|
||||
/******/ if(mode & 8) return value;
|
||||
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
|
||||
/******/ var ns = Object.create(null);
|
||||
/******/ __webpack_require__.r(ns);
|
||||
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
|
||||
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
|
||||
/******/ return ns;
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // getDefaultExport function for compatibility with non-harmony modules
|
||||
/******/ __webpack_require__.n = function(module) {
|
||||
/******/ var getter = module && module.__esModule ?
|
||||
/******/ function getDefault() { return module['default']; } :
|
||||
/******/ function getModuleExports() { return module; };
|
||||
/******/ __webpack_require__.d(getter, 'a', getter);
|
||||
/******/ return getter;
|
||||
/******/ };
|
||||
/******/
|
||||
/******/ // Object.prototype.hasOwnProperty.call
|
||||
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
|
||||
/******/
|
||||
/******/ // __webpack_public_path__
|
||||
/******/ __webpack_require__.p = "/";
|
||||
/******/
|
||||
/******/ var jsonpArray = this["webpackJsonp"] = this["webpackJsonp"] || [];
|
||||
/******/ var oldJsonpFunction = jsonpArray.push.bind(jsonpArray);
|
||||
/******/ jsonpArray.push = webpackJsonpCallback;
|
||||
/******/ jsonpArray = jsonpArray.slice();
|
||||
/******/ for(var i = 0; i < jsonpArray.length; i++) webpackJsonpCallback(jsonpArray[i]);
|
||||
/******/ var parentJsonpFunction = oldJsonpFunction;
|
||||
/******/
|
||||
/******/
|
||||
/******/ // run deferred modules from other chunks
|
||||
/******/ checkDeferredModules();
|
||||
/******/ })
|
||||
/************************************************************************/
|
||||
/******/ ([]);
|
24255
unpackage/dist/dev/app-plus/app-service.js
vendored
Normal file
26959
unpackage/dist/dev/app-plus/app-view.js
vendored
Normal file
1
unpackage/dist/dev/app-plus/manifest.json
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"@platforms":["android","iPhone","iPad"],"id":"__UNI__474F351","name":"app_ancientSayings","version":{"name":"1.0.0","code":"100"},"description":"","launch_path":"__uniappview.html","developer":{"name":"","email":"","url":""},"permissions":{"UniNView":{"description":"UniNView原生渲染"}},"plus":{"useragent":{"value":"uni-app","concatenate":true},"splashscreen":{"target":"id:1","autoclose":true,"waiting":true,"delay":0},"popGesture":"close","launchwebview":{"id":"1","kernel":"WKWebview"},"statusbar":{"immersed":"supportedDevice","style":"dark","background":"#F8F8F8"},"safearea":{"background":"#F5F6F9","bottom":{"offset":"none|auto"}},"usingComponents":true,"nvueStyleCompiler":"uni-app","compilerVersion":3,"distribute":{"google":{"permissions":["<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>","<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>","<uses-permission android:name=\"android.permission.VIBRATE\"/>","<uses-permission android:name=\"android.permission.READ_LOGS\"/>","<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>","<uses-feature android:name=\"android.hardware.camera.autofocus\"/>","<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>","<uses-permission android:name=\"android.permission.CAMERA\"/>","<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>","<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>","<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>","<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>","<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>","<uses-feature android:name=\"android.hardware.camera\"/>","<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"]},"apple":{},"plugins":{"audio":{"mp3":{"description":"Android平台录音支持MP3格式文件"}}}},"uniStatistics":{"enable":false},"allowsInlineMediaPlayback":true,"uni-app":{"compilerVersion":"3.8.12","control":"uni-v3","nvueCompiler":"uni-app","renderer":"auto","nvue":{"flex-direction":"column"},"nvueLaunchMode":"normal"},"tabBar":{"backgroundColor":"#FFFFFF","borderStyle":"#F5F5F5","selectedColor":"#FF5000","iconWidth":"24px","color":"#666666","fontSize":"10px","height":"68px","list":[{"pagePath":"pages/bookshelf/bookshelf/index","text":"书架","iconPath":"/static/images/tabbar/bookshelf.png","selectedIconPath":"/static/images/tabbar/bookshelfActive.png"},{"pagePath":"pages/bookCity/bookCity/index","text":"书城","iconPath":"/static/images/tabbar/bookCity.png","selectedIconPath":"/static/images/tabbar/bookCityActive.png"},{"pagePath":"pages/classification/classification/index","text":"分类","iconPath":"/static/images/tabbar/classification.png","selectedIconPath":"/static/images/tabbar/classificationActive.png"},{"pagePath":"pages/myInfo/myInfo/index","text":"我的","iconPath":"/static/images/tabbar/myInfo.png","selectedIconPath":"/static/images/tabbar/myInfoActive.png"}],"child":["lauchwebview"],"selected":1},"launch_path":"__uniappview.html"}}
|
BIN
unpackage/dist/dev/app-plus/static/app_logo.png
vendored
Normal file
After Width: | Height: | Size: 15 KiB |
BIN
unpackage/dist/dev/app-plus/static/app_logo_222.png
vendored
Normal file
After Width: | Height: | Size: 26 KiB |
BIN
unpackage/dist/dev/app-plus/static/app_logoww.png
vendored
Normal file
After Width: | Height: | Size: 21 KiB |
BIN
unpackage/dist/dev/app-plus/static/images/Rectangle_nav_bar.png
vendored
Normal file
After Width: | Height: | Size: 197 KiB |
BIN
unpackage/dist/dev/app-plus/static/images/_coin.png
vendored
Normal file
After Width: | Height: | Size: 10 KiB |
BIN
unpackage/dist/dev/app-plus/static/images/bookCity/complete.png
vendored
Normal file
After Width: | Height: | Size: 2.5 KiB |
BIN
unpackage/dist/dev/app-plus/static/images/bookCity/new_book.png
vendored
Normal file
After Width: | Height: | Size: 3.3 KiB |
BIN
unpackage/dist/dev/app-plus/static/images/bookCity/ranking_four.png
vendored
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
unpackage/dist/dev/app-plus/static/images/bookCity/ranking_one.png
vendored
Normal file
After Width: | Height: | Size: 1.6 KiB |
BIN
unpackage/dist/dev/app-plus/static/images/bookCity/ranking_three.png
vendored
Normal file
After Width: | Height: | Size: 2.1 KiB |
BIN
unpackage/dist/dev/app-plus/static/images/bookCity/ranking_two.png
vendored
Normal file
After Width: | Height: | Size: 2.0 KiB |
BIN
unpackage/dist/dev/app-plus/static/images/bookCity/recharge.png
vendored
Normal file
After Width: | Height: | Size: 2.7 KiB |
BIN
unpackage/dist/dev/app-plus/static/images/bookCity/the_charts.png
vendored
Normal file
After Width: | Height: | Size: 1.5 KiB |
BIN
unpackage/dist/dev/app-plus/static/images/book_111.png
vendored
Normal file
After Width: | Height: | Size: 83 KiB |
BIN
unpackage/dist/dev/app-plus/static/images/bookshelf/_sign_in.png
vendored
Normal file
After Width: | Height: | Size: 55 KiB |
BIN
unpackage/dist/dev/app-plus/static/images/bookshelf/_swipe_add.png
vendored
Normal file
After Width: | Height: | Size: 438 B |
BIN
unpackage/dist/dev/app-plus/static/images/bookshelf/del_all.png
vendored
Normal file
After Width: | Height: | Size: 2.4 KiB |
BIN
unpackage/dist/dev/app-plus/static/images/bookshelf/del_all_checked.png
vendored
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
unpackage/dist/dev/app-plus/static/images/chapter_lock.png
vendored
Normal file
After Width: | Height: | Size: 436 B |
BIN
unpackage/dist/dev/app-plus/static/images/delete_icon.png
vendored
Normal file
After Width: | Height: | Size: 646 B |
BIN
unpackage/dist/dev/app-plus/static/images/login_bg.png
vendored
Normal file
After Width: | Height: | Size: 508 KiB |
BIN
unpackage/dist/dev/app-plus/static/images/member_vip_bg.png
vendored
Normal file
After Width: | Height: | Size: 85 KiB |
BIN
unpackage/dist/dev/app-plus/static/images/myInfo/VIP.png
vendored
Normal file
After Width: | Height: | Size: 565 B |
BIN
unpackage/dist/dev/app-plus/static/images/myInfo/VIP_active.png
vendored
Normal file
After Width: | Height: | Size: 1.3 KiB |
BIN
unpackage/dist/dev/app-plus/static/images/myInfo/VIP_whether.png
vendored
Normal file
After Width: | Height: | Size: 577 B |
BIN
unpackage/dist/dev/app-plus/static/images/myInfo/balance_bg.png
vendored
Normal file
After Width: | Height: | Size: 88 KiB |
BIN
unpackage/dist/dev/app-plus/static/images/myInfo/bookCoinHistoryImage.png
vendored
Normal file
After Width: | Height: | Size: 5.5 KiB |
BIN
unpackage/dist/dev/app-plus/static/images/myInfo/browsingHistoryImage.png
vendored
Normal file
After Width: | Height: | Size: 5.7 KiB |
BIN
unpackage/dist/dev/app-plus/static/images/myInfo/default_sculpture.png
vendored
Normal file
After Width: | Height: | Size: 8.7 KiB |
BIN
unpackage/dist/dev/app-plus/static/images/myInfo/rechargeHistoryImage.png
vendored
Normal file
After Width: | Height: | Size: 5.5 KiB |
BIN
unpackage/dist/dev/app-plus/static/images/myInfo/vip_tips_bg.png
vendored
Normal file
After Width: | Height: | Size: 92 KiB |
BIN
unpackage/dist/dev/app-plus/static/images/nav-bar_Vector.png
vendored
Normal file
After Width: | Height: | Size: 464 B |
BIN
unpackage/dist/dev/app-plus/static/images/nav_bar_search.png
vendored
Normal file
After Width: | Height: | Size: 416 B |
BIN
unpackage/dist/dev/app-plus/static/images/novelReading/bookshelf_333.png
vendored
Normal file
After Width: | Height: | Size: 709 B |
BIN
unpackage/dist/dev/app-plus/static/images/novelReading/bookshelf_ccd9e2.png
vendored
Normal file
After Width: | Height: | Size: 860 B |
BIN
unpackage/dist/dev/app-plus/static/images/novelReading/bookshelf_f3efe.png
vendored
Normal file
After Width: | Height: | Size: 853 B |
BIN
unpackage/dist/dev/app-plus/static/images/novelReading/directory_333.png
vendored
Normal file
After Width: | Height: | Size: 500 B |
BIN
unpackage/dist/dev/app-plus/static/images/novelReading/directory_ccd9e2.png
vendored
Normal file
After Width: | Height: | Size: 596 B |
BIN
unpackage/dist/dev/app-plus/static/images/novelReading/directory_f3efe9.png
vendored
Normal file
After Width: | Height: | Size: 589 B |
BIN
unpackage/dist/dev/app-plus/static/images/novelReading/set_up_mode_black.png
vendored
Normal file
After Width: | Height: | Size: 1.9 KiB |
BIN
unpackage/dist/dev/app-plus/static/images/novelReading/step_up_333.png
vendored
Normal file
After Width: | Height: | Size: 1.2 KiB |
BIN
unpackage/dist/dev/app-plus/static/images/novelReading/step_up_ccd9e2.png
vendored
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
unpackage/dist/dev/app-plus/static/images/novelReading/step_up_f3efe.png
vendored
Normal file
After Width: | Height: | Size: 1.4 KiB |
BIN
unpackage/dist/dev/app-plus/static/images/readingRecords_add.png
vendored
Normal file
After Width: | Height: | Size: 197 B |
BIN
unpackage/dist/dev/app-plus/static/images/readingRecords_add_sdelete.png
vendored
Normal file
After Width: | Height: | Size: 750 B |
BIN
unpackage/dist/dev/app-plus/static/images/readingRecords_book.png
vendored
Normal file
After Width: | Height: | Size: 16 KiB |
BIN
unpackage/dist/dev/app-plus/static/images/readingRecords_have.png
vendored
Normal file
After Width: | Height: | Size: 339 B |
BIN
unpackage/dist/dev/app-plus/static/images/recharge_empty.png
vendored
Normal file
After Width: | Height: | Size: 65 KiB |
BIN
unpackage/dist/dev/app-plus/static/images/search_empty.png
vendored
Normal file
After Width: | Height: | Size: 60 KiB |
BIN
unpackage/dist/dev/app-plus/static/images/signInBookCurrency_header_bag.png
vendored
Normal file
After Width: | Height: | Size: 248 KiB |
BIN
unpackage/dist/dev/app-plus/static/images/sign_in_calendar.png
vendored
Normal file
After Width: | Height: | Size: 2.7 KiB |