Commit 528d4299 authored by jyx's avatar jyx

代码优化

parent 6d82d603
......@@ -25,6 +25,7 @@ export default class Book {
createTime,
tagList,
isUnlock,
shortis,
free,
charge,
bookLegumes,
......@@ -56,6 +57,7 @@ export default class Book {
this.categoryId = categoryId;
this.createTime = createTime;
this.isUnlock = isUnlock;
this.shortis = shortis;
this.charge = charge;
this.free = free;
this.bookLegumes = bookLegumes;
......
......@@ -17,9 +17,31 @@ function gotoBookSearchPage(searchType = ENUM_SEARCH_TYPE.WAREHOUSE, keyword) {
}
// 文章详情
function gotoBookContentPage(bookId) {
function gotoBookContentPage(bookId, shortis) {
if (shortis && shortis == 0) {
gotoShortBookContentPage(bookId)
} else {
gotoLongBookContentPage(bookId)
}
}
// 短篇文章详情
function gotoShortBookContentPage(bookId) {
// 短篇小说
uni.navigateTo({
url: `/page-subs/sub_A/book-content/book-content?bookId=` + bookId,
success: (res) => {
res.eventChannel.emit("openBookContentPage", {
bookId
})
}
})
}
// 长篇文章详情
function gotoLongBookContentPage(bookId) {
uni.navigateTo({
url: `/page-subs/sub_A/book-content/book-content`,
url: `/page-subs/sub_A/book-long-content/book-long-content?bookId=` + bookId,
success: (res) => {
res.eventChannel.emit("openBookContentPage", {
bookId
......
......@@ -65,7 +65,7 @@
})
},
tapItem(e, index) {
gotoBookContentPage(e.detail.data.id)
gotoBookContentPage(e.detail.data.id,e.detail.data.shortis)
},
tapClose(e, index) {
let item = e.detail.data;
......
<template>
<view>
<c-list ref='list' flag='warehouse' method="POST" :height="height" url='/book/articleList/' :param="requestParam"
@change='changeData'>
<c-list ref='list' flag='warehouse' method="POST" :height="height" url='/book/articleList/'
:param="requestParam" @change='changeData'>
<book-list-item v-for='(item, index) in dataList' :key='index' :item='item' :showClose='false'
@tapItem='tapItem($event, index)' @close='tapClose($event, index)'>
</book-list-item>
......@@ -64,7 +64,7 @@
})
},
tapItem(e, index) {
gotoBookContentPage(e.detail.data.id)
gotoBookContentPage(e.detail.data.id, e.detail.data.shortis)
},
tapClose(e, index) {
// TODO 仓库点击关闭按钮,弹窗选择关闭原因
......
......@@ -120,7 +120,7 @@
setTimeout(() => {
// 匹配用户直接跳转小说
if (data.articleMsg != null) {
gotoBookContentPage(data.articleMsg.id);
gotoBookContentPage(data.articleMsg.id,data.articleMsg.shortis);
}
}, 800);
}
......
......@@ -128,7 +128,7 @@
uni.navigateBack({
delta: 2
});
gotoBookContentPage(item.id);
gotoBookContentPage(item.id,item.shortis);
}
}
}
......
<template>
<view :style="[bgStyle]">
<c-empty v-if='showEmpty'></c-empty>
<template v-else>
<detail-content-flip :detail='bookData' :userInfo='userInfo' @tabVip2="tapVipPop"></detail-content-flip>
<detail-bottom :detail='bookData' :userInfo='userInfo' @tapBottomItem='tapBottomItem'></detail-bottom>
<setting-pop :show='showSetting' @close='closePop'></setting-pop>
<catalogue-pop :detail='bookData' :show='showCatalogue' @close='closeCataPop'
@tabVip='tapVipPop'></catalogue-pop>
<!-- <vip-pop v-if="bookData.isUnlock==0 && !isVip()" :show='showVip' @close='closeVipPop'></vip-pop>
<bean-pop v-if="bookData.isUnlock==0 && userInfo.bookLegumes<bookData.bookLegumes" :show='showBean'
@close='closeBeanPop'></bean-pop> -->
<recommend-pop :show='showRecommend' @close='closeRecommendPop' :bookId="bookId"></recommend-pop>
</template>
<!-- <c-login :isShareLink="true"></c-login> -->
<popup :show="showMoibleLogin" @close="showMoibleLogin=false">
<view>
<button open-type="getPhoneNumber" @getphonenumber="MygetPhonenumber">
<view>请先绑定手机号</view>
<view style="color:green;">去绑定</view>
</button>
</view>
</popup>
</view>
</template>
<script>
import {
isEmpty
} from "../../../common/utils/util.js";
import BookDetail from "./models/BookDetail.js";
import {
getBookDetailData,
watchContentFormatChange,
removeContentFormatChangeWatch,
addReadRecord
} from "./services/index.js"
import {
collectionBook
} from "../../../common/services/index.js"
import DetailWarn from "./components/detail-warn.vue";
import DetailThumb from "./components/detail-thumb.vue";
import DetailContent from "./components/detail-content.vue";
import DetailContentFlip from "./components/detail-content-flip.vue"
import DetailBottom from "./components/detail-bottom.vue";
import DetailNewBuy from "./components/detail-new-buy.vue";
import config from "../../../config/index.js";
import SettingPop from "./components/setting-pop.vue";
import CataloguePop from "./components/catalogue-pop.vue";
import VipPop from "./components/vip-pop.vue";
import BeanPop from "./components/bean-pop.vue";
import RecommendPop from "./components/recommend-pop.vue";
import SystemInfoMixin from "../../../common/mixins/system-info-mixin.js";
import {
gotoBookCoverPage
} from "../../../common/services/page-route.js"
import {
watchUserInfoChange,
removeUserInfoChangeWatch,
refreshUserInfo,
postPhone
} from "../../../common/services/userServices.js"
import {
noticeCollectionListChange,
startCountReadTime,
endCountReadTime,
getOpens,
getChapterinfoData
} from "../../../common/services/index.js"
import {
saveStorage,
readStorage
} from "../../../common/utils/storageUtil.js";
export default {
mixins: [SystemInfoMixin],
components: {
DetailWarn,
DetailThumb,
DetailContent,
DetailContentFlip,
DetailNewBuy,
DetailBottom,
SettingPop,
VipPop,
BeanPop,
RecommendPop,
CataloguePop
},
data() {
return {
bookId: null,
bookData: null,
backgroundColor: "#fff",
showSetting: false,
showCatalogue: false,
showVip: false,
showBean: false,
showRecommend: false,
userInfo: null,
showVipOpen: 0,
showBeanOpen: 0,
showMoibleLogin: false,
mobileLoginLock: false,
};
},
onLoad(options) {
if (options.bookId) { // 非navigate进入,读取path参数
this.bookId = options.bookId;
} else { // navigate 进入,接受channel传参
const eventChannel = this.getOpenerEventChannel();
eventChannel.on("openBookContentPage", (info) => {
this.bookId = info.bookId;
})
}
this.mobileLoginLock = readStorage("KEY_NEED_PHONE")
},
onReady() {
// 监听样式变动
watchContentFormatChange((data) => {
let result = data.getFormatValue();
if (this.backgroundColor != result.backgroundColor) {
this.backgroundColor = result.backgroundColor
}
}, this)
// 监听用户变动
watchUserInfoChange((info) => {
this.userInfo = info.userInfo;
// 用户已登录需要记录阅读记录
if (info.userInfo && this.bookId) {
addReadRecord(this.bookId)
}
// // 用户变动,需要刷新数据
// this.$nextTick(() => {
// uni.startPullDownRefresh({})
// })
}, this)
// 用户变动,需要刷新数据
this.$nextTick(() => {
uni.startPullDownRefresh({})
})
// 绑定分享参数
// #ifdef MP-WEIXIN
wx.onCopyUrl(() => {
return {
query: `bookId=${this.bookData.id}`
}
})
// #endif
},
onShow() {
refreshUserInfo();
// 开始记录阅读时长
startCountReadTime();
},
onHide() {
// 停止记录阅读时长
endCountReadTime();
},
onUnload() {
// 停止记录阅读时长,部分情况下页面无法响应onHide事件
endCountReadTime();
// 移除监听
removeContentFormatChangeWatch(this);
removeUserInfoChangeWatch(this);
// 取消绑定分享参数
// #ifdef MP-WEIXIN
wx.offCopyUrl()
// #endif
},
onPullDownRefresh() {
this.refreshBookData(this.bookId)
},
// 文章分享
onShareAppMessage() {
let result = {
title: this.bookData.title,
imageUrl: this.bookData.cover,
type: config.env == 'Prod' ? 0 : config.env == 'Dev' ? 1 : 2,
path: `/page-subs/sub_A/book-long-content/book-long-content?bookId=${this.bookData.id}`
}
return result;
},
computed: {
showEmpty: function() {
return isEmpty(this.bookData)
},
bgStyle: function() {
let height = 300;
if (this.windowHeight) {
height = this.windowHeight;
}
return {
minHeight: `${height}px`,
position: "relative",
background: this.backgroundColor
}
}
},
watch: {
bookData: {
handler: function(n) {
if (n) {
uni.setNavigationBarTitle({
title: `阅读:${n.title}`
})
}
}
}
},
methods: {
async MygetPhonenumber(e) {
console.log(e.detail.code) // 动态令牌
console.log(e.detail.errMsg) // 回调信息(成功失败都会返回)
console.log(e.detail.errno) // 错误码(失败时返回)
this.showMoibleLogin = false
if (e.detail.code == undefined || e.detail.code == '') {
uni.showModal({
title: "提示",
content: e.detail.errMsg
})
return
}
var params = {
userId: this.userInfo.userid,
code: e.detail.code,
}
postPhone(params, (success, result) => {
if (success) {
saveStorage("KEY_NEED_PHONE", false);
this.mobileLoginLock = false
this.tapPayPop()
} else {
uni.showModal({
title: "提示",
content: "网络错误!"
})
}
})
},
// 解锁回调
unlockBook() {
this.$set(this.bookData, "isUnlock", true);
},
// 文章数据刷新
refreshBookData(bookId) {
// getOpens((success, data) => {
// this.showBeanOpen = data.openBeans
// this.showVipOpen = data.openVips
// })
getBookDetailData(bookId, (success, data) => {
setTimeout(() => {
uni.stopPullDownRefresh();
if (success) {
this.bookData = new BookDetail(data);
console.log('bookData=' + JSON.stringify(this.bookData));
}
}, 1000)
})
},
// 点击底部按钮
tapBottomItem(e) {
let flag = e.detail.flag;
switch (flag) {
case 'like': // 点赞
break;
case 'collection': // 收藏
let target = !this.bookData.isCollect;
collectionBook(target, this.bookId, (success, data) => {
if (success) {
this.$set(this.bookData, 'isCollect', target);
noticeCollectionListChange(this.bookId, target);
}
})
break;
case 'setting': // 设置
this.showSetting = true;
break;
case 'catalogue': // 目录
this.showCatalogue = true;
break;
default:
break;
}
},
isVip() {
if (this.userInfo != null && this.userInfo.memberFlag) {
let cDate = new Date();
cDate = cDate.getTime();
return this.userInfo.memberExpirationDate >= cDate;
}
return false;
},
tapPayPop() {
if (this.showVipOpen != 1 && this.showBeanOpen == 1) {
this.tapBeanPop()
return
}
this.tapVipPop()
},
// 展示充值VIP弹框
tapVipPop() {
if (this.mobileLoginLock) {
this.showMoibleLogin = true
return
}
this.showVip = true;
},
// 展示充值书豆弹框
tapBeanPop() {
if (this.mobileLoginLock) {
this.showMoibleLogin = true
return
}
this.showBean = true;
},
// 关闭设置弹窗
closePop(e) {
this.showSetting = false;
},
// 关闭目录弹窗
closeCataPop(e) {
this.showCatalogue = false;
},
// 关闭会员弹窗
closeVipPop(e) {
this.showVip = false;
// setTimeout(() => {
// this.showRecommend = true;
// }, 300);
},
// 关闭书豆弹窗
closeBeanPop(e) {
this.showBean = false;
// setTimeout(() => {
// this.showRecommend = true;
// }, 300);
},
// 关闭推荐弹窗
closeRecommendPop(e) {
this.showRecommend = false;
},
}
}
</script>
<style lang="scss">
</style>
\ No newline at end of file
<template>
<uni-popup ref='beanpop' type="bottom" :is-mask-click='false' :safe-area='false' @maskClick='tapMask'>
<view class="setting-box">
<view class="section">
<view class="title">
充值书豆,订阅全本
</view>
<view class="pack-box">
<view class="pack-item" :class="[{active: index==selectedIndex}]" v-for='(item, index) in packData'
:key='index' @click="choosePack(item, index)">
<view class="name row">
{{item.title}}
</view>
<view class="price row">
{{item.price}}
</view>
<view class="cut-down" v-if='item.giveNumber'>
赠送 {{item.giveNumber}} 书豆
</view>
</view>
</view>
</view>
<view class="section">
<button class="c-button_clear c-button-size_lg c-button-width_full apply-button" :disabled="loading"
:loading="loading" @click="tapPay">立即购买</button>
</view>
<view class="safe-placeholder" :style="[safePlacehoderStyle]">
</view>
</view>
</uni-popup>
</template>
<script>
import SystemInfoMixin from "../../../../common/mixins/system-info-mixin.js";
import BookBeanPack from "../../../../common/models/BookBeanPack.js"
import {
getBookBeanPackData,
getOpenId,
getPayInfo,
ENUM_PAY_TYPE
} from "../../../../common/services/index.js";
import PayInfo from "../../../../common/models/PayInfo.js"
import {
watchUserInfoChange,
removeUserInfoChangeWatch,
showLoginView,
refreshUserInfo
} from "../../../../common/services/userServices.js"
import {
toastMessage
} from "../../../../common/utils/toastUtil.js";
import {
px2rpx
} from "../../../../common/utils/util.js";
export default {
mixins: [SystemInfoMixin],
props: {
show: {
type: Boolean,
default: false
}
},
data: function() {
return {
showPop: false,
userInfo: null,
packData: [],
selectedIndex: 0,
loading: false
}
},
computed: {
safePlacehoderStyle: function() {
let height = 0;
if (this.bottomSafeHeight) {
height = height + px2rpx(this.bottomSafeHeight)
}
return {
height: `${height}rpx`
}
},
beanCount: function() {
if (this.userInfo) return this.userInfo.bookLegumes;
return 0;
}
},
watch: {
show: function(n) {
this.showPop = n;
},
showPop: function(n, o) {
if (n == o) return;
if (n) {
this.open();
} else {
this.close();
}
}
},
onReady() {
watchUserInfoChange((info) => {
this.userInfo = info.userInfo;
}, this)
},
onUnload() {
removeUserInfoChangeWatch(this);
},
mounted() {
this.requestPackData();
},
methods: {
open() {
this.$refs.beanpop.open();
},
close() {
this.$emit('close')
this.$refs.beanpop.close();
},
tapMask() {
this.showPop = false;
},
requestPackData() {
getBookBeanPackData((success, data) => {
uni.stopPullDownRefresh()
if (success) {
this.packData = data.map(item => {
return new BookBeanPack(item)
})
}
})
},
choosePack(item, index) {
this.selectedIndex = index;
},
tapPay() {
let isIOS = uni.getSystemInfoSync().platform == "ios" && false
if (isIOS) {
uni.showModal({
title: "提示",
content: "由于相关规范,iOS功能暂不可用"
})
} else {
if (!this.userInfo) {
uni.showModal({
title: "登录",
content: "购买前请前往登录系统",
success: (res) => {
if (res.confirm) {
showLoginView()
}
}
})
return;
}
if (this.loading) return;
let pack = this.packData[this.selectedIndex];
this.loading = true;
let sysLoginFn = (successCB) => {
uni.login({
provider: "weixin",
onlyAuthorize: true,
success: (res) => {
if (res) {
if (typeof successCB == 'function') successCB(res);
} else {
this.loading = false;
}
},
fail: (error) => {
this.loading = false;
}
})
}
let getOpenIdFn = (code, successCB) => {
getOpenId(code, (success, data) => {
if (success) {
if (typeof successCB == 'function') successCB(data);
} else {
this.loading = false;
}
})
}
let getPayInfoFn = (openId, successCB) => {
getPayInfo(pack.id, pack.price, openId, (success, data) => {
if (success) {
if (typeof successCB == 'function') successCB(new PayInfo(data));
} else {
this.loading = false;
}
}, ENUM_PAY_TYPE.BEAN.value)
}
let payOrderFn = (payInfo, successCB) => {
uni.requestPayment({
timeStamp: payInfo.timeStamp,
nonceStr: payInfo.nonceStr,
package: payInfo.packageStr,
signType: payInfo.signType,
paySign: payInfo.paySign,
success: (res) => {
if (typeof successCB == 'function') successCB(res);
},
fail: (error) => {
this.loading = false;
}
})
}
sysLoginFn((code) => {
getOpenIdFn(code.code, (openId) => {
getPayInfoFn(openId, (payInfo) => {
payOrderFn(payInfo, (data) => {
this.loading = false;
toastMessage('购买成功')
refreshUserInfo();
})
})
})
})
}
}
}
}
</script>
<style lang="scss" scoped>
.setting-box {
display: flex;
flex-direction: column;
background: #fff;
padding-top: 20rpx;
border-top-left-radius: 15rpx;
border-top-right-radius: 15rpx;
.section {
padding: 0 30rpx;
display: flex;
flex-direction: column;
background: #fff;
.title {
margin: 10rpx auto;
font-size: 32rpx;
font-weight: 700;
color: #333;
}
.pack-box {
margin-top: 25rpx;
margin-left: 40rpx;
display: flex;
flex-direction: row;
align-items: center;
flex-wrap: wrap;
.active {
border: 6rpx solid #fd5350 !important;
}
.pack-item {
margin-bottom: 25rpx;
margin-right: 40rpx;
width: calc(31% - 40rpx);
height: 200rpx;
display: flex;
flex-direction: column;
justify-content: space-around;
background: #f5f5f5;
border: 6rpx solid #f5f5f5;
border-radius: 10rpx;
position: relative;
.row {
margin: 8rpx 15rpx;
marign-bottom: 0;
}
.row:last-child {
margin-bottom: 8rpx;
}
.name {
font-size: 26rpx;
font-weight: 700;
color: #333;
}
.price {
font-size: 30rpx;
color: #fd5350;
font-weight: 700;
}
.origin {
font-size: 22rpx;
color: #333;
text-decoration: line-through;
}
.cut-down {
position: absolute;
top: 0;
right: 0;
color: #fff;
background: #ff502f;
font-size: 22rpx;
border-radius: 15rpx;
height: 30rpx;
line-height: 30rpx;
padding: 0 10rpx;
transform: translate(0, -50%);
}
}
}
.apply-button {
border-radius: 50rpx;
background: #e8c8ae;
color: #8d5a29;
margin: 30rpx auto;
}
}
}
</style>
\ No newline at end of file
<template>
<uni-popup ref='pop' type="left" :is-mask-click='false' :safe-area='false' @maskClick='tapMask'>
<scroll-view scroll-y style="background-color: white;">
<view class="setting-box">
<block v-for='(item, index) in catalogueList' :key='index' :item='item'>
<view @click="changeCatalogue(item.chapterNum)">
<view
style="width: 400rpx;max-width: 400rpx;display: flex;flex-direction: row;justify-content: space-between;align-items: center;">
<text class="text"
:style="{'width: 350rpx;color': item.chapterNum==currentCatalogue?'#f00':'#000'}">
{{item.chapterNum}}{{item.chapterName}}</text>
<image v-if="(index+1)>detail.freeNum" style="width: 30rpx;height: 30rpx;"
src="https://mints-pkg.oss-cn-beijing.aliyuncs.com/pkg/img/lock.png"></image>
</view>
<view style="background-color: gainsboro;height: 1rpx;width: 400rpx;margin: 20rpx 0;"></view>
</view>
</block>
</view>
</scroll-view>
</uni-popup>
</template>
<script>
import SystemInfoMixin from "../../../../common/mixins/system-info-mixin.js";
import {
px2rpx
} from "../../../../common/utils/util.js";
import CatalogueList from "../models/CatalogueList.js";
import {
saveContentFormat,
readContentFormat
} from "../services/index.js"
export default {
mixins: [SystemInfoMixin],
props: {
show: {
type: Boolean,
default: false
},
detail: {
type: Object,
default: 0
}
},
data: function() {
return {
showPop: false,
catalogueList: [],
currentContentFormat: {},
currentCatalogue: 1
}
},
computed: {},
watch: {
show: function(n) {
this.showPop = n;
},
showPop: function(n, o) {
if (n == o) return;
if (n) {
this.open();
} else {
this.close();
}
}
},
mounted() {
this.initColorList();
this.currentContentFormat = readContentFormat()
for (let i in this.currentContentFormat.catalogueList) {
if (this.currentContentFormat.catalogueList[i].id == this.detail.id) {
this.currentCatalogue = this.currentContentFormat.catalogueList[i].catalogue
}
}
},
methods: {
open() {
this.$refs.pop.open();
},
close() {
this.$emit('close')
this.$refs.pop.close();
},
tapMask() {
this.showPop = false;
},
initColorList() {
this.catalogueList = this.detail.articleChapterList
},
changeCatalogue(e) {
if (e > this.detail.freeNum) {
this.tabVip()
this.close()
return
}
var hasCatalogue = false
for (let i in this.currentContentFormat.catalogueList) {
if (this.currentContentFormat.catalogueList[i].id == this.detail.id) {
this.currentContentFormat.catalogueList[i].catalogue = e
hasCatalogue = true
}
}
if (!hasCatalogue) {
this.currentContentFormat.catalogueList.push({
id: this.detail.id,
catalogue: e
})
}
saveContentFormat(this.currentContentFormat);
this.close()
},
tabVip() {
setTimeout(() => {
this.$emit('tabVip')
}, 1000)
}
}
}
</script>
<style lang="scss" scoped>
.setting-box {
display: flex;
flex-direction: column;
padding: 40rpx;
background: #fff;
height: 100vh;
.title {
font-size: 34rpx;
font-weight: 700;
}
}
</style>
\ No newline at end of file
<template>
<view class="detail-bottom" :style="[bottomStyle]">
<view class="fixed-bottom" :style="[fixedBottomStyle,{backgroundColor:backgroundColor}]">
<!-- <view class="bottom-item" @click="tapItem('like')">
<uni-icons :type='likeIcon.type' customPrefix="readiconfont" :color='likeIcon.color'
size='20'></uni-icons>
<view class="title" :style="{color: likeIcon.color}">
</view>
</view> -->
<!-- <view class="bottom-item" @click="tapItem('diss')">
<uni-icons :type='dissIcon.type' customPrefix="readiconfont" :color='dissIcon.color'
size='20'></uni-icons>
<view class="title" :style="{color: dissIcon.color}">
</view>
</view> -->
<view class="bottom-item" @click="tapItem('catalogue')">
<uni-icons type='icon-uncollection' customPrefix="readiconfont" color='#333' size='20'></uni-icons>
<view class="title" :style="{color: '#333'}">
目录
</view>
</view>
<!-- <view class="bottom-item" @click="tapItem('collection')">
<uni-icons :type='collectionIcon.type' customPrefix="readiconfont" :color='collectionIcon.color'
size='20'></uni-icons>
<view class="title" :style="{color: collectionIcon.color}">
{{collectionIcon.title}}
</view>
</view> -->
<view class="bottom-item" @click="tapItem('setting')">
<uni-icons type='icon-setting' customPrefix="readiconfont" color='#333' size='20'></uni-icons>
<view class="title" :style="{color: '#333'}">
设置
</view>
</view>
<view class="bottom-item">
<uni-icons type='icon-share' customPrefix="readiconfont" color='#333' size='20'></uni-icons>
<view class="title" :style="{color: '#333'}">
分享
</view>
<button class="c-button_clear cover-button" open-type="share"></button>
</view>
</view>
<view class="safe-placeholder" :style="[safePlaceholderStyle]"></view>
</view>
</template>
<script>
import {
watchContentFormatChange,
removeContentFormatChangeWatch
} from "../services/index.js"
import SystemInfoMixin from "../../../../common/mixins/system-info-mixin.js"
import {
px2rpx
} from "../../../../common/utils/util.js";
import {
showLoginView
} from "../../../../common/services/userServices.js"
export default {
mixins: [SystemInfoMixin],
props: {
detail: {
type: Object,
default: function() {
return {}
}
},
userInfo: {
type: Object,
default: function() {
return null
}
}
},
data: function() {
return {
height: `100`,
backgroundColor: `#fff`
}
},
mounted() {
// 监听样式变动
watchContentFormatChange((data) => {
let result = data.getFormatValue();
if (this.backgroundColor != result.backgroundColor) {
this.backgroundColor = result.backgroundColor
}
}, this)
},
destroyed() {
removeContentFormatChangeWatch(this);
},
computed: {
fixedBottomStyle: function() {
let height = 0;
if (this.bottomSafeHeight) {
height = height + px2rpx(this.bottomSafeHeight)
}
return {
bottom: `${height}rpx`
}
},
safePlaceholderStyle: function() {
let height = 0;
if (this.bottomSafeHeight) {
height = height + px2rpx(this.bottomSafeHeight)
}
return {
height: `${height}rpx`
}
},
bottomStyle: function() {
let height = 100;
if (this.bottomSafeHeight) {
height = height + px2rpx(this.bottomSafeHeight)
}
return {
height: `${height}rpx`
}
},
likeIcon: function() {
return {
type: this.detail.isLike ? 'icon-like' : 'icon-unlike',
color: this.detail.isLike ? '#FECF02' : "#333"
}
},
dissIcon: function() {
return {
type: this.detail.isDiss ? 'icon-diss' : 'icon-undiss',
color: this.detail.isDiss ? '#FECF02' : "#333"
}
},
collectionIcon: function() {
return {
type: this.detail.isCollect ? 'icon-collection' : 'icon-uncollection',
color: this.detail.isCollect ? '#FECF02' : "#333",
title: this.detail.isCollect ? "已收藏" : "收藏"
}
}
},
methods: {
tapItem(flag) {
if (flag == 'collection' && !this.userInfo) {
showLoginView();
return;
}
this.$emit("tapBottomItem", {
detail: {
flag,
item: this.detail
}
})
}
}
}
</script>
<style lang="scss" scoped>
.detail-bottom {
position: absolute;
height: 100rpx;
bottom: 0;
.fixed-bottom {
position: fixed;
background: transparent;
bottom: 0;
left: 0;
right: 0;
height: 100rpx;
display: flex;
flex-direction: row;
align-items: center;
.bottom-item {
position: relative;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 100%;
// width: 33.3333333333%;
flex: 1;
.title {
font-size: 24rpx;
margin-top: 10rpx;
}
.cover-button {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: transparent;
z-index: 2;
}
}
}
.safe-placeholder {
position: fixed;
left: 0;
right: 0;
bottom: 0;
background: #fff;
}
}
</style>
\ No newline at end of file
<template>
<view class="detail-content">
<template>
<yingbing-ReadPage ref="page" :fontSize="fontSize" :bgColor="backgroundColor" style="height: 100vh;"
@loadmore="onLoadmore">
</yingbing-ReadPage>
</template>
</view>
</template>
<script>
import {
watchContentFormatChange,
removeContentFormatChangeWatch,
getChapterinfoData
} from "../services/index.js"
import cataloguePopVue from "./catalogue-pop.vue";
export default {
props: {
userInfo: {
type: Object,
default: function() {
return null
}
},
detail: {
type: Object,
default: function() {
return {}
}
}
},
data: function() {
return {
fontSize: '18',
backgroundColor: "#fff",
catalogue: 1,
}
},
mounted() {
// 监听样式变动
watchContentFormatChange((data) => {
let result = data.getFormatValue();
if (this.fontSize != result.fontSize) {
this.fontSize = result.fontSize.substring(0, 2)
}
if (this.backgroundColor != result.backgroundColor) {
this.backgroundColor = result.backgroundColor
}
let resultCatalogue = 1
for (let i in result.catalogueList) {
if (result.catalogueList[i].id == this.detail.id) {
resultCatalogue = result.catalogueList[i].catalogue
}
}
if (this.catalogue != resultCatalogue) {
this.catalogue = resultCatalogue
this.refreshChapterinfoData(this.catalogue)
}
}, this)
},
destroyed() {
removeContentFormatChangeWatch(this);
},
onReady() {
this.refreshChapterinfoData(this.catalogue)
},
methods: {
onLoadmore(chapter, callback) {
if (chapter >= this.detail.freeNum) {
this.tabVip()
return
}
getChapterinfoData(this.detail.id, chapter, (success, data) => {
if (success) {
this.catalogue = chapter
let mycontent = data.content.replace(/<(\/)?p>/g, '').replace(/<(\/)?sub>/g, '').replace(
/<(\/)?pre>/g, '')
.replace(/<(\/)?code>/g, '')
let content = {
chapter: chapter,
content: mycontent,
title: '第' + chapter + '章',
isStart: chapter == 1,
isEnd: chapter == this.detail.articleChapterList.length
}
callback('success', content)
}
})
},
// 文章数据刷新
refreshChapterinfoData(chapterId, callback) {
this.catalogue = chapterId
let id = this.detail.articleChapterList[chapterId].id
getChapterinfoData(this.detail.id, id, (success, data) => {
if (success) {
this.changeChapterinfo(chapterId, data.content)
}
})
},
changeChapterinfo(chapterId, content) {
this.catalogue = chapterId
let mycontent = content.replace(/<(\/)?p>/g, '').replace(/<(\/)?sub>/g, '').replace(/<(\/)?pre>/g, '')
.replace(/<(\/)?code>/g, '')
let contents = [{
chapter: chapterId, //章节序号
content: mycontent,
title: '第' + chapterId + '章', //章节标题
isStart: chapterId == 1, //是否是第一章节
isEnd: false, //是否是最后一个章节
}]
const {
page
} = this.$refs;
page.change({
contents: contents,
currentChapter: chapterId, //从第二章节开始阅读 和章节chapter对应
start: 0 //从章节的第100个字开始阅读
})
},
tabVip() {
setTimeout(() => {
this.$emit('tabVip2')
}, 1000)
}
}
}
</script>
<style lang="scss" scoped>
.detail-content {
display: flex;
flex-direction: column;
.content-paragraph {
text-indent: 1em;
word-wrap: break-word;
word-break: break-all;
white-space: normal;
line-height: 1.5em;
margin-bottom: 1em;
color: #333;
}
.content-paragraph:last-child {
margin-bottom: 0;
}
.limit-button-box {
text-indent: 0;
height: 120rpx;
line-height: 120rpx;
text-align: center;
color: goldenrod;
font-size: 28rpx;
}
}
</style>
\ No newline at end of file
<template>
<view class="detail-content">
<template v-if='showVIPContent'>
<view class="content-paragraph" :style="[contentStyle]" v-for='(item, index) in contentSources'
:key='index'>
{{item}}
</view>
</template>
<template v-else>
<view class="content-paragraph" :style="[contentStyle]" v-for='(item, index) in preContentSources'
:key="index">
{{item}}
</view>
<view class="limit-button-box" @click="tapVip">
{{limitButtonTitle}}
</view>
</template>
</view>
</template>
<script>
import {
watchContentFormatChange,
removeContentFormatChangeWatch
} from "../services/index.js"
import {
showLoginView
} from "../../../../common/services/userServices.js"
import {
gotoVIPApplyPage
} from "../../../../common/services/page-route.js"
import User from "../../../../common/models/User.js";
export default {
props: {
userInfo: {
type: Object,
default: function() {
return null
}
},
detail: {
type: Object,
default: function() {
return {}
}
}
},
data: function() {
return {
user: null,
fontSize: `18px`,
PRECENT_VALUE:40
}
},
mounted() {
watchContentFormatChange((data) => {
let result = data.getFormatValue();
if (this.fontSize != result.fontSize) {
this.fontSize = result.fontSize
}
}, this)
},
destroyed() {
removeContentFormatChangeWatch(this);
},
watch: {
userInfo: {
handler: function(n) {
if (n) {
this.user = new User(n)
} else {
this.user = null
}
},
deep: true,
immediate: true
}
},
computed: {
contentSources: function() {
return this.detail && this.detail.content ? this.detail.content : [];
},
preContentSources: function() {
let result = this.detail && this.detail.content ? [...this.detail.content] : [];
let length = result.length;
if (length >= 2) {
if(this.detail.lockRate!=null&&this.detail.lockRate>0){
this.PRECENT_VALUE=this.detail.lockRate;
}
console.log('this.PRECENT_VALUE='+this.PRECENT_VALUE);
let precent = this.PRECENT_VALUE / 100;
let preLength = Math.floor(length * precent);
return result.splice(0, preLength);
} else {
return result;
}
},
limitButtonTitle: function() {
let result = `~剩余${100-this.PRECENT_VALUE}%内容`;
if (!this.user) {
result = `${result},点击登录后继续阅读`;
} else if (!this.user.isVip()) {
result = `${result}为会员章节内容,请前往购买会员`
}
result = `${result}~`;
return result;
},
contentStyle: function() {
return {
fontSize: this.fontSize
}
},
showVIPContent: function() {
return (this.user && this.user.isVip()) || this.detail.isUnlock
}
},
methods: {
tapLimitButton() {
if (!this.user) {
showLoginView()
} else if (!this.user.isVip()) {
gotoVIPApplyPage()
}
},
tapVip() {
let isIOS = wx.getSystemInfoSync().platform;
if (isIOS === 'ios') {
uni.showToast({
title: '暂不支持IOS系统',
icon: 'none'
})
return
}
// 展示充值VIP弹框
this.$emit('tapVip')
}
}
}
</script>
<style lang="scss" scoped>
.detail-content {
paading: 20rpx 30rpx;
margin: 20rpx 30rpx;
display: flex;
flex-direction: column;
.content-paragraph {
text-indent: 1em;
word-wrap: break-word;
word-break: break-all;
white-space: normal;
line-height: 1.5em;
margin-bottom: 1em;
color: #333;
}
.content-paragraph:last-child {
margin-bottom: 0;
}
.limit-button-box {
text-indent: 0;
height: 120rpx;
line-height: 120rpx;
text-align: center;
color: goldenrod;
font-size: 28rpx;
}
}
</style>
\ No newline at end of file
<template>
<view class="detail-buy">
<view class="book-card">
<view class="line-box">
<view class="line"></view>
<view class="text">全本订阅超优惠</view>
<view class="line"></view>
</view>
<view v-if="showBeanOpen==1" class="text-box">
<view class="text1">全本特价:</view>
<view class="text2">{{bookBeanCount}}</view>
<view class="text1">书豆</view>
</view>
<view v-if="showBeanOpen==1" class="text-box" style="margin-top: 20rpx;">
<view class="text1">账户余额:</view>
<view class="text2">{{bookAllBeanCount}}</view>
<view class="text1">书豆</view>
</view>
<view v-if="showBeanOpen==1" class="section" style="margin-top: 30rpx;">
<button class="btn1" :disabled="loading" :loading="loading" @click="tapBeanBtn">
{{bookBeanText}}
</button>
</view>
<view v-if="!isVip() && (showVipOpen==1)" class="section" style="margin-top: 30rpx;">
<button class="btn2" :disabled="loading" :loading="loading" @click="tapVip">开通会员,免费阅读本书</button>
</view>
</view>
<view class="warn-box">
<view class="warn-p">
1.目前充值会员暂不支持退款,一经购买不可退换
</view>
<view class="warn-p">
2.未满18岁的未成年人需要在监护人主导,同意下进行相关付费操作
</view>
<view class="warn-p">
3.充值一般在5分钟内到账,如未到账请提供支付截图在"我的"页面联系客服
</view>
<view class="warn-p">
4.之前充值账户请登录后继续阅读
</view>
</view>
</view>
</template>
<script>
import Pack from "../../../../common/models/Pack.js";
import BookBeanPack from "../../../../common/models/BookBeanPack.js"
import {
showLoginView,
} from "../../../../common/services/userServices.js"
import {
buyBookWithBookBean
} from "../../../../common/services/index.js";
import {
toastHide,
toastLoading,
toastMessage
} from "../../../../common/utils/toastUtil.js";
export default {
props: {
detail: {
type: Object,
default: function() {
return {}
}
},
userInfo: {
type: Object,
default: function() {
return null
}
},
showVipOpen: {
type: Number,
default: 1
},
showBeanOpen: {
type: Number,
default: 1
}
},
data: function() {
return {
selectedIndex: 0,
loading: false,
imageError: true
}
},
computed: {
bookBeanText: function() {
if (this.userInfo && this.detail) {
if (this.userInfo.bookLegumes < this.detail.bookLegumes) {
return '余额不足,立即充值'
}
}
return '全本订阅';
},
bookAllBeanCount: function() {
if (this.userInfo) return this.userInfo.bookLegumes
return 0;
},
bookBeanCount: function() {
return this.detail && this.detail.bookLegumes ? `${this.detail.bookLegumes}` : "免费"
},
},
mounted() {
},
methods: {
isVip() {
if (this.userInfo != null && this.userInfo.memberFlag) {
let cDate = new Date();
cDate = cDate.getTime();
return this.userInfo.memberExpirationDate >= cDate;
}
return false;
},
tapBeanBtn() {
let isIOS = wx.getSystemInfoSync().platform;
if (isIOS === 'ios') {
uni.showToast({
title: '暂不支持IOS系统',
icon: 'none'
})
return
}
if (this.userInfo && this.detail) {
if (this.userInfo.bookLegumes < this.detail.bookLegumes) {
this.tapBean()
return
}
}
this.tapBuy()
},
tapVip() {
let isIOS = wx.getSystemInfoSync().platform;
if (isIOS === 'ios') {
uni.showToast({
title: '暂不支持IOS系统',
icon: 'none'
})
return
}
// 展示充值VIP弹框
this.$emit('tapVip')
},
tapBean() {
// 展示充值书豆弹框
this.$emit('tapBean')
},
tapBuy() {
if (!this.userInfo) {
uni.showModal({
title: "登录",
content: "购买前请前往登录系统",
success: (res) => {
if (res.confirm) {
showLoginView()
}
}
})
return;
}
if (this.userInfo.bookLegumes < this.detail.bookLegumes) {
uni.showModal({
title: "余额不足",
content: `当前余额:${this.userInfo.bookLegumes}, 请前往充值`,
showCancel: false,
confirmText: "知道了"
})
return;
}
toastLoading("购买中");
buyBookWithBookBean(this.detail.id, (success, data) => {
toastHide();
if (success) {
this.$emit("unlockBook")
}
})
},
}
}
</script>
<style lang="scss" scoped>
.detail-buy {
display: flex;
flex-direction: column;
.book-card {
margin: 20rpx;
padding: 20rpx;
// background: #fdf6f0;
// border-radius: 20rpx;
display: flex;
flex-direction: column;
.section {
padding: 0 30rpx;
display: flex;
flex-direction: column;
.btn1 {
color: white;
background-image: linear-gradient(90deg, pink, #DCBD3B);
width: 100%;
height: 80rpx;
text-align: center;
border-radius: 80rpx;
line-height: 80rpx;
}
.btn2 {
color: #DCBD3B;
border: #DCBD3B solid 1rpx;
width: 100%;
text-align: center;
border-radius: 80rpx;
height: 80rpx;
line-height: 80rpx;
}
}
.line-box {
display: flex;
flex-direction: row;
padding: 30rpx;
flex-wrap: wrap;
align-items: center;
.line {
width: 30%;
background-color: lightgray;
height: 1rpx;
}
.text {
text-align: center;
width: 36%;
color: lightgray;
font-size: 26rpx;
}
}
.text-box {
display: flex;
flex-direction: row;
.text1 {
font-size: 24rpx;
color: black;
}
.text2 {
padding: 0 10rpx;
font-size: 24rpx;
color: #DCBD3B;
}
}
}
.warn-box {
color: #956244;
font-size: 26rpx;
display: flex;
flex-direction: column;
padding: 30rpx;
.warn-p {
margin-bottom: 15rpx;
}
.warn-p:last-child {
margin-bottom: 0;
}
}
}
</style>
\ No newline at end of file
<template>
<view class="detail-thumb" @click="tapThumb">
<view class="content">
<view class="cover-box item">
<image v-show="imageError" class="cover" src="/static/images/image_error.png" mode="aspectFill"></image>
<image V-show="!imageError" class="cover" :src="detail.avatar" mode="aspectFill" @load="loadImage"
@error="errorImage"></image>
</view>
<view class="info-box item">
<view class="row">
<view class="title">
{{detail.title}}
</view>
</view>
<view class="row">
<view class="desc">
{{detail.summary}}
</view>
</view>
<view class="row">
<view class="author-box">
<uni-icons type='icon-author' custom-prefix="readiconfont" size='20'
color='#378eff'></uni-icons>
<view class="name">
{{detail.author}}
</view>
</view>
</view>
</view>
</view>
</view>
</template>
<script>
export default {
props: {
detail: {
type: Object,
default: function() {
return {}
}
}
},
data: function() {
return {
imageError: true
}
},
methods: {
tapThumb() {
this.$emit('tapThumb', {
detail: {
data: this.detail
}
})
},
loadImage() {
this.imageError = false
},
errorImage() {
this.imageError = true
}
}
}
</script>
<style lang="scss" scoped>
page {
background: #fff;
}
.detail-thumb {
padding: 15rpx 30rpx;
display: flex;
flex-direction: column;
.content {
display: flex;
flex-direction: row;
align-items: center;
border-radius: 18rpx;
background: #f5f5f5;
padding: 20rpx;
.row {
margin-bottom: 20rpx;
}
.row:last-child {
margin-bottom: 0;
}
.item {
margin-right: 20rpx;
}
.item:last-child {
margin-right: 0;
}
.cover-box {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
.cover {
width: 140rpx;
height: 180rpx;
border-radius: 15rpx;
}
}
.info-box {
flex: 1;
display: flex;
flex-direction: column;
.title {
font-size: 32rpx;
font-weight: 700;
color: #333;
}
.desc {
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
font-size: 24rpx;
color: #999
}
.author-box {
display: flex;
flex-direction: row;
align-items: center;
height: 50rpx;
line-height: 50rpx;
.name {
font-size: 26rpx;
color: #378eff;
margin-left: 15rpx;
}
}
}
}
}
</style>
\ No newline at end of file
<template>
<view v-if="false" class="detail-warn">
<view class="content">
<uni-icons type='chatboxes' size="20" color="#aa8062"></uni-icons>
<view class="value">
"本故事纯属虚构,仅供娱乐,无不良引导"
</view>
</view>
</view>
</template>
<script>
</script>
<style lang="scss" scoped>
.detail-warn {
padding: 15rpx 30rpx;
display: flex;
flex-direction: column;
.content {
height: 100rpx;
line-height: 100rpx;
display: flex;
flex-direction: row;
align-items: center;
border-radius: 18rpx;
background: #f4e9eb;
color: #aa8062;
padding: 0 30rpx;
.value {
font-size: 28rpx;
margin-left: 15rpx;
}
}
}
</style>
\ No newline at end of file
<template>
<uni-popup ref='recommendpop' type="bottom" :is-mask-click='false' :safe-area='false' @maskClick='tapMask'>
<view class="setting-box">
<view class="section">
<view class="title">
99%读过这本书的人还在读
</view>
<view class="book-list-item" v-for='(item, index) in dataList' :key='index' :item='item' @click="tapItem(item)">
<view class="cover-box item">
<image v-if="item.avatar===''" class="cover" src="/static/images/logo.png" mode="aspectFill"></image>
<image v-else class="cover" :src="item.avatar" mode="aspectFill" ></image>
</view>
<view class="c-flex_column">
<view class="c-flex_row c-justify_between row">
<view class="c-flex_column c-justify_center item">
<view class="title2">
{{item.title}}
</view>
</view>
</view>
<view class="c-flex_row row">
<view class="c-flex_column c-justify_between content c-flex_1 item">
<view class="desc row">
{{item.summary}}
</view>
<view class="c-flex_row c-align_center label-box row">
<slot name="footer">
<view class="label label-color-1" v-if="item.categoryName!=null&&item.categoryName!=''">
{{item.categoryName}}
</view>
<view class="label label-color-2" v-for='(label, labelIndex) in item.tagList'
:key='labelIndex'>
{{label.name}}
</view>
</slot>
</view>
</view>
</view>
</view>
<view style="display: flex;margin: auto;width: 200rpx;">
<button class="btn2" >去阅读</button>
</view>
</view>
</view>
</view>
</uni-popup>
</template>
<script>
import SystemInfoMixin from "../../../../common/mixins/system-info-mixin.js";
import {
gotoBookContentPage
} from '../../../../common/services/page-route'
import {
getBookRecommendData
} from "../services/index.js";
import {
px2rpx
} from "../../../../common/utils/util.js";
export default {
mixins: [SystemInfoMixin],
props: {
show: {
type: Boolean,
default: false
},
bookId: {
type: [Number, String],
default: 0
}
},
data: function() {
return {
showPop: false,
dataList: []
}
},
computed: {
safePlacehoderStyle: function() {
let height = 0;
if (this.bottomSafeHeight) {
height = height + px2rpx(this.bottomSafeHeight)
}
return {
height: `${height}rpx`
}
}
},
watch: {
show: function(n) {
this.showPop = n;
},
showPop: function(n, o) {
if (n == o) return;
if (n) {
this.open();
} else {
this.close();
}
}
},
onUnload() {
},
mounted() {
this.requestPackData();
},
methods: {
open() {
this.$refs.recommendpop.open();
},
close() {
this.$emit('close')
this.$refs.recommendpop.close();
},
tapMask() {
this.showPop = false;
},
requestPackData() {
getBookRecommendData(this.bookId,(success, data) => {
if (success) {
this.dataList = data.records;
}
})
},
tapItem(item) {
// 在C页面内 navigateBack,将返回A页面
uni.navigateBack({
delta: 2
});
gotoBookContentPage(item.id, item.shortis);
}
}
}
</script>
<style lang="scss" scoped>
.setting-box {
display: flex;
flex-direction: column;
background: #fff;
padding-top: 20rpx;
border-top-left-radius: 15rpx;
border-top-right-radius: 15rpx;
.section {
padding: 0 30rpx;
display: flex;
flex-direction: column;
background: #fff;
.title {
margin: 10rpx auto;
font-size: 32rpx;
font-weight: 700;
color: #DCBD3B;
}
.pack-box {
margin-top: 25rpx;
margin-left: 40rpx;
display: flex;
flex-direction: row;
align-items: center;
flex-wrap: wrap;
.active {
border: 6rpx solid #fd5350 !important;
}
.pack-item {
margin-bottom: 25rpx;
margin-right: 40rpx;
width: calc(31% - 40rpx);
height: 200rpx;
display: flex;
flex-direction: column;
justify-content: space-around;
background: #f5f5f5;
border: 6rpx solid #f5f5f5;
border-radius: 10rpx;
position: relative;
.row {
margin: 8rpx 15rpx;
marign-bottom: 0;
}
.row:last-child {
margin-bottom: 8rpx;
}
.name {
font-size: 26rpx;
font-weight: 700;
color: #333;
}
.price {
font-size: 30rpx;
color: #fd5350;
font-weight: 700;
}
.origin {
font-size: 22rpx;
color: #333;
text-decoration: line-through;
}
.cut-down {
position: absolute;
top: 0;
right: 0;
color: #fff;
background: #ff502f;
font-size: 22rpx;
border-radius: 15rpx;
height: 30rpx;
line-height: 30rpx;
padding: 0 10rpx;
transform: translate(0, -50%);
}
}
}
.apply-button {
border-radius: 50rpx;
background: #e8c8ae;
color: #8d5a29;
margin: 30rpx auto;
}
}
}
.book-list-item {
display: flex;
flex-direction: row;
padding-top: 14rpx;
padding-bottom: 14rpx;
.row {
margin-bottom: 20rpx;
}
.row:last-child {
margin-bottom: 0;
}
.item {
margin-right: 20rpx;
}
.item:last-child {
margin-right: 0;
}
.title {
font-size: 32rpx;
font-weight: 700;
color: #333;
}
.close-button {
border-radius: 10rpx;
border: 2rpx solid #999;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
width: 40rpx;
height: 30rpx;
position: relative;
.cover {
position: absolute;
top: 50%;
left: 50%;
width: 70rpx;
height: 70rpx;
transform: translate(-50%, -50%);
z-index: 2;
background: transparent;
}
}
.desc {
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-line-clamp: 2;
-webkit-box-orient: vertical;
font-size: 26rpx;
color: #999
}
.label-box {
flex-wrap: wrap;
.label {
padding: 5rpx 10rpx;
font-size: 20rpx;
margin-right: 20rpx;
border-radius: 10rpx;
}
.label-color-1 {
background: #faefe6;
color: #cc6008;
}
.label-color-2 {
color: #3d99fd;
background: #d8ebff;
}
.label-color-3 {
background: #ff8787;
color: #ff3737;
}
}
}
.cover-box {
.cover {
width: 150rpx;
height: 200rpx;
border-radius: 15rpx;
}
}
.title2 {
font-size: 32rpx;
font-weight: 700;
color: #333;
}
.btn2 {
color: #d98c5c;
border: #DCBD3B solid 1rpx;
width: 140rpx;
font-size: 24rpx;
text-align: center;
border-radius: 80rpx;
height: 40rpx;
line-height: 40rpx;
}
</style>
\ No newline at end of file
<template>
<uni-popup ref='pop' type="bottom" :is-mask-click='false' :safe-area='false' @change='change'>
<view class="setting-box">
<view class="row">
<view class="title">
字体大小
</view>
<view class="zone">
<view class="c-flex_1">
<slider min="14" max="24" step='1' :value='currentContentFormat.fontSize' :show-value="true"
@change='changeFontSize'>
</slider>
</view>
</view>
</view>
<view class="row">
<view class="title">
背景颜色
</view>
<view class="zone">
<view class="color-item"
:class="{'selected-color': item.value==currentContentFormat.backgroundColor}"
:style="[item.style]" v-for='(item, index) in colorList' :key="index"
@click="chooseColor(item.value)"></view>
</view>
</view>
<view class="safe-placeholder" :style="[safePlacehoderStyle]">
</view>
</view>
</uni-popup>
</template>
<script>
import SystemInfoMixin from "../../../../common/mixins/system-info-mixin.js";
import {
px2rpx
} from "../../../../common/utils/util.js";
import {
saveContentFormat,
readContentFormat
} from "../services/index.js"
export default {
mixins: [SystemInfoMixin],
props: {
show: {
type: Boolean,
default: false
}
},
data: function() {
return {
showPop: false,
colorList: [],
currentContentFormat: {},
}
},
computed: {
safePlacehoderStyle: function() {
let height = 0;
if (this.bottomSafeHeight) {
height = height + px2rpx(this.bottomSafeHeight)
}
return {
height: `${height}rpx`
}
},
},
watch: {
show: function(n) {
this.showPop = n;
},
showPop: function(n, o) {
if (n == o) return;
if (n) {
this.open();
} else {
this.close();
}
}
},
mounted() {
this.initColorList();
this.currentContentFormat = readContentFormat()
},
methods: {
open() {
this.$refs.pop.open();
},
close() {
this.$emit('close')
this.$refs.pop.close();
},
change(e) {
if (this.showPop == e.show) return;
this.showPop = e.show
},
initColorList() {
this.colorList = ["#fff", "#e7e7e7", "#d4e0c6", "#ccd8e4", "#f9d4d4", "#b1b1b1"].map(item => {
return {
style: {
background: item
},
value: item
}
})
},
changeFontSize(e) {
let fontSize = e.detail.value;
this.currentContentFormat.fontSize = fontSize;
saveContentFormat(this.currentContentFormat);
},
chooseColor(color) {
this.currentContentFormat.backgroundColor = color;
saveContentFormat(this.currentContentFormat);
}
}
}
</script>
<style lang="scss" scoped>
.setting-box {
display: flex;
flex-direction: column;
padding: 20rpx;
background: #fff;
border-top-left-radius: 15rpx;
border-top-right-radius: 15rpx;
.row {
display: flex;
flex-direction: column;
justify-content: center;
padding: 20rpx;
.title {
font-size: 30rpx;
font-weight: 700;
margin-bottom: 20rpx;
}
.zone {
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-around;
.color-item {
width: 80rpx;
height: 80rpx;
border-radius: 40rpx;
border: 2rpx solid #999;
}
.selected-color {
width: 100rpx;
height: 100rpx;
border-radius: 50rpx;
border: 5rpx solid #ff2525;
}
}
}
}
</style>
\ No newline at end of file
<template>
<uni-popup ref='vippop' type="bottom" :is-mask-click='false' :safe-area='false' @maskClick='tapMask'>
<view class="setting-box">
<view class="section">
<view class="title">
开通会员,继续阅读
</view>
<view class="pack-box">
<view class="pack-item" :class="[{active: index==selectedIndex}]" v-for='(item, index) in packList'
:key='index' @click="choosePack(item, index)">
<view class="name row">
{{item.title}}
</view>
<view class="price row">
{{item.price}}
</view>
<view class="origin row" v-if='item.originalPrice'>
原价:{{item.originalPrice}}
</view>
<view class="cut-down" v-if='item.cutDown'>
立省 {{item.cutDown}}
</view>
</view>
</view>
</view>
<view class="section">
<button class="c-button_clear c-button-size_lg c-button-width_full apply-button" :disabled="loading"
:loading="loading" @click="tapPay">立即开通</button>
</view>
<view class="safe-placeholder" :style="[safePlacehoderStyle]">
</view>
</view>
</uni-popup>
</template>
<script>
import SystemInfoMixin from "../../../../common/mixins/system-info-mixin.js";
import Pack from "../../../../common/models/Pack.js";
import {
getPackData,
getOpenId,
getPayInfo
} from "../../../../common/services/index.js";
import PayInfo from "../../../../common/models/PayInfo.js"
import {
watchUserInfoChange,
removeUserInfoChangeWatch,
showLoginView,
refreshUserInfo
} from "../../../../common/services/userServices.js"
import {
toastMessage
} from "../../../../common/utils/toastUtil.js";
import {
px2rpx
} from "../../../../common/utils/util.js";
export default {
mixins: [SystemInfoMixin],
props: {
show: {
type: Boolean,
default: false
}
},
data: function() {
return {
showPop: false,
packList: [],
selectedIndex: 0,
loading: false,
userInfo: null
}
},
computed: {
safePlacehoderStyle: function() {
let height = 0;
if (this.bottomSafeHeight) {
height = height + px2rpx(this.bottomSafeHeight)
}
return {
height: `${height}rpx`
}
},
},
watch: {
show: function(n) {
this.showPop = n;
},
showPop: function(n, o) {
if (n == o) return;
if (n) {
this.open();
} else {
this.close();
}
}
},
onReady() {
watchUserInfoChange((info) => {
this.userInfo = info.userInfo;
}, this)
},
onUnload() {
removeUserInfoChangeWatch(this);
},
mounted() {
this.refrehsPackData();
},
methods: {
open() {
this.$refs.vippop.open();
},
close() {
this.$emit('close')
this.$refs.vippop.close();
},
tapMask() {
this.showPop = false;
},
refrehsPackData() {
getPackData((success, data) => {
this.packList = data.map(item => {
return new Pack(item);
})
})
},
choosePack(item, index) {
this.selectedIndex = index;
},
tapPay() {
let isIOS = uni.getSystemInfoSync().platform == "ios" && false
if (isIOS) {
uni.showModal({
title: "提示",
content: "由于相关规范,iOS功能暂不可用"
})
} else {
if (!this.userInfo) {
uni.showModal({
title: "登录",
content: "购买前请前往登录系统",
success: (res) => {
if (res.confirm) {
showLoginView()
}
}
})
return;
}
if (this.loading) return;
let pack = this.packList[this.selectedIndex];
this.loading = true;
let sysLoginFn = (successCB) => {
uni.login({
provider: "weixin",
onlyAuthorize: true,
success: (res) => {
if (res) {
if (typeof successCB == 'function') successCB(res);
} else {
this.loading = false;
}
},
fail: (error) => {
this.loading = false;
}
})
}
let getOpenIdFn = (code, successCB) => {
getOpenId(code, (success, data) => {
if (success) {
if (typeof successCB == 'function') successCB(data);
} else {
this.loading = false;
}
})
}
let getPayInfoFn = (openId, successCB) => {
getPayInfo(pack.id, pack.price, openId, (success, data) => {
if (success) {
if (typeof successCB == 'function') successCB(new PayInfo(data));
} else {
this.loading = false;
}
})
}
let payOrderFn = (payInfo, successCB) => {
uni.requestPayment({
timeStamp: payInfo.timeStamp,
nonceStr: payInfo.nonceStr,
package: payInfo.packageStr,
signType: payInfo.signType,
paySign: payInfo.paySign,
success: (res) => {
if (typeof successCB == 'function') successCB(res);
},
fail: (error) => {
this.loading = false;
}
})
}
sysLoginFn((code) => {
getOpenIdFn(code.code, (openId) => {
getPayInfoFn(openId, (payInfo) => {
payOrderFn(payInfo, (data) => {
this.loading = false;
toastMessage('会员购买成功')
refreshUserInfo();
})
})
})
})
}
}
}
}
</script>
<style lang="scss" scoped>
.setting-box {
display: flex;
flex-direction: column;
background: #fff;
padding-top: 20rpx;
border-top-left-radius: 15rpx;
border-top-right-radius: 15rpx;
.section {
padding: 0 30rpx;
display: flex;
flex-direction: column;
background: #fff;
.title {
margin: 10rpx auto;
font-size: 32rpx;
font-weight: 700;
color: #333;
}
.pack-box {
margin-top: 25rpx;
margin-left: 40rpx;
display: flex;
flex-direction: row;
align-items: center;
flex-wrap: wrap;
.active {
border: 6rpx solid #fd5350 !important;
}
.pack-item {
margin-bottom: 25rpx;
margin-right: 40rpx;
width: calc(31% - 40rpx);
height: 200rpx;
display: flex;
flex-direction: column;
justify-content: space-around;
background: #f5f5f5;
border: 6rpx solid #f5f5f5;
border-radius: 10rpx;
position: relative;
.row {
margin: 8rpx 15rpx;
marign-bottom: 0;
}
.row:last-child {
margin-bottom: 8rpx;
}
.name {
font-size: 26rpx;
font-weight: 700;
color: #333;
}
.price {
font-size: 30rpx;
color: #fd5350;
font-weight: 700;
}
.origin {
font-size: 22rpx;
color: #333;
text-decoration: line-through;
}
.cut-down {
position: absolute;
top: 0;
right: 0;
color: #fff;
background: #ff502f;
font-size: 22rpx;
border-radius: 15rpx;
height: 30rpx;
line-height: 30rpx;
padding: 0 10rpx;
transform: translate(0, -50%);
}
}
}
.apply-button {
border-radius: 50rpx;
background: #e8c8ae;
color: #8d5a29;
margin: 30rpx auto;
}
}
}
</style>
\ No newline at end of file
import Book from "../../../../common/models/Book";
export default class BookDetail extends Book {
constructor(param) {
super(param);
const {
contentMd
} = param || {}
this.contentMd = contentMd;
let result = contentMd ? contentMd.replace(/<[^>]+>/g, "") : "";
result = result.split("\n");
this.content = result.filter(item => {
return item.split(/[\t\r\f\n\s]*/g).join("");
});
}
}
\ No newline at end of file
export default class CatalogueList {
constructor(param) {
const {
id,
catalogue,
} = param || {}
this.id = id;
this.catalogue = catalogue;
}
getFormatValue() {
return {
id: this.id,
catalogue: this.catalogue
}
}
}
\ No newline at end of file
export default class ContentFormat {
constructor(param) {
const {
fontSize,
backgroundColor,
catalogueList,
} = param || {}
this.fontSize = fontSize;
this.catalogueList = catalogueList;
this.backgroundColor = backgroundColor;
}
getFormatValue() {
return {
fontSize: `${this.fontSize}px`,
backgroundColor: this.backgroundColor,
catalogueList: this.catalogueList
}
}
}
\ No newline at end of file
import {
addNormalNotificationObserver,
postNotification,
removeNotificationObserver
} from "../../../../common/utils/notificationCenter";
import {
readStorage,
saveStorage
} from "../../../../common/utils/storageUtil";
import ContentFormat from "../models/ContentFormat";
import {
apiGET,
apiPOST
} from "../../../../common/utils/apiRequest.js"
import CatalogueList from "../models/CatalogueList";
/** 获取文章详情
* @param {Object} bookId
* @param {Object} callback
*/
function getBookDetailData(bookId, callback) {
apiPOST({
url: `/book/info`,
data: {
id: bookId
},
callback
})
}
/** 获取章节数据
* @param {Object} bookId
* @param {Object} chapterId
* @param {Object} callback
*/
function getChapterinfoData(bookId, chapterId, callback) {
apiPOST({
url: `/book/chapterinfo`,
data: {
articleId: bookId,
chapterId: chapterId,
},
callback
})
}
/** 获取推荐文章
* @param {Object} bookId
* @param {Object} callback
*/
function getBookRecommendData(bookId, callback) {
apiPOST({
url: `/book/recommend`,
data: {
id: bookId
},
callback
})
}
/** 添加阅读记录
* @param {Object} bookId
* @param {Object} callback
*/
function addReadRecord(bookId, callback) {
apiPOST({
url: `/book/addReadRecord`,
data: {
articleId: bookId
},
callback
})
}
// 样式设置
const KEY_STORAGE_LONG_CONTENT_FORMAT = "KEY_STORAGE_LONG_CONTENT_FORMAT";
const KEY_NOTIFICATION_LONG_CONTENT_FORMAT_CHANGE = "KEY_NOTIFICATION_LONG_CONTENT_FORMAT_CHANGE";
function saveContentFormat(format) {
let result = readContentFormat();
Object.keys(format).forEach(key => {
let value = format[key];
result[key] = value || result[key];
})
saveStorage(KEY_STORAGE_LONG_CONTENT_FORMAT, result);
notifyContentFormatChange(new ContentFormat(result));
}
function readContentFormat(id) {
let result = readStorage(KEY_STORAGE_LONG_CONTENT_FORMAT);
if (result) {
result = new ContentFormat(result);
} else {
result = new ContentFormat({
fontSize: 17,
backgroundColor: "#fff",
catalogueList: [] // 章节列表
})
}
return result;
}
function notifyContentFormatChange(format) {
postNotification(KEY_NOTIFICATION_LONG_CONTENT_FORMAT_CHANGE, format);
}
function watchContentFormatChange(fn, observer) {
if (typeof fn == 'function') fn.call(observer, readContentFormat());
addNormalNotificationObserver(KEY_NOTIFICATION_LONG_CONTENT_FORMAT_CHANGE, (data) => {
if (typeof fn == 'function') fn.call(observer, data);
}, observer)
}
function removeContentFormatChangeWatch(observer) {
removeNotificationObserver(KEY_NOTIFICATION_LONG_CONTENT_FORMAT_CHANGE, observer);
}
module.exports = {
getBookDetailData,
getBookRecommendData,
addReadRecord,
getChapterinfoData,
/**
* content format
*/
saveContentFormat,
readContentFormat,
notifyContentFormatChange,
watchContentFormatChange,
removeContentFormatChangeWatch
}
\ No newline at end of file
......@@ -45,7 +45,7 @@
},
methods: {
tapItem() {
gotoBookContentPage(this.item.id);
gotoBookContentPage(this.item.id,this.item.shortis);
},
loadImage() {
this.imageError = false
......
<template>
<c-list ref="list" flag='search' method="POST" :needLogin="needLogin" :height="height" :url='requestUrl' :param='requestParam'
@change='changeData'>
<c-list ref="list" flag='search' method="POST" :needLogin="needLogin" :height="height" :url='requestUrl'
:param='requestParam' @change='changeData'>
<book-list-item v-for='(item, index) in dataList' :key='index' :item='item' :showClose="false"
@tapItem='tapItem($event, index)'>
</book-list-item>
......@@ -79,7 +79,7 @@
})
},
tapItem(e, index) {
gotoBookContentPage(e.detail.data.id)
gotoBookContentPage(e.detail.data.id, e.detail.data.id)
},
}
}
......
......@@ -69,6 +69,13 @@
"enablePullDownRefresh": true
}
}, {
"path": "book-long-content/book-long-content",
"style": {
"navigationBarTitleText": "阅读",
"enablePullDownRefresh": true
}
}, {
"path": "book-cover/book-cover",
"style": {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment