Commit 2187139f authored by jyx's avatar jyx

删除无用代码,代码优化

parent 7f2ac0a7
......@@ -53,7 +53,8 @@ let startReadTime = null; // 开始阅读时间
* 开始阅读计时
*/
function startCountReadTime() {
startReadTime = new Date();
startReadTime = new Date();
console.log('AAAAAA---startCountReadTime------->')
}
/**
* 停止阅读计时
......@@ -78,7 +79,9 @@ function endCountReadTime() {
let dayStart = stringToDate(end.join("-"))
count = endReadTime.getTime() - dayStart.getTime();
}
startReadTime = null;
startReadTime = null;
console.log('AAAAAA---endCountReadTime------->' + count)
setReadTimeCount(count);
}
......
<template>
<uni-popup ref="pop" type="center" :is-mask-click='false' :safe-area='false' @maskClick='tapMask'>
<view class="login-content">
<view class="close-button" @click.stop="tapMask">
<view class="icon-box">
<uni-icons type='closeempty' size="24" color="#cc6008"></uni-icons>
</view>
</view>
<view class="welcome-msg">
快速登录
</view>
<view class="login-button">
<image class="logo" src="/static/images/logo.png" mode="scaleToFill"></image>
<view class="info">
授权登录
</view>
<button class="c-button_clear cover-button" @click="getPhoneNumber"></button>
</view>
</view>
</uni-popup>
</template>
<script>
import SystemInfoMixin from "../../common/mixins/system-info-mixin.js";
import {
addNormalNotificationObserver,
postNotification,
removeNotificationObserver
} from "../../common/utils/notificationCenter.js";
import {
px2rpx
} from "../../common/utils/util.js";
import {
KEY_NOTIFICATION_LOGIN_SHOW,
KEY_NOTIFICATION_LOGIN_SUCCESS
} from "../../static/keys/notification-keys.js"
import {
login,
logout
} from "../../common/services/userServices.js"
export default {
mixins: [SystemInfoMixin],
name: "c-login",
props: {
show: {
type: Boolean,
default: false
},
isShareLink: {
type: Boolean,
default: false
}
},
data() {
return {
showPop: false,
};
},
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() {
addNormalNotificationObserver(KEY_NOTIFICATION_LOGIN_SHOW, (info) => {
if (this.showPop != info.show) {
this.showPop = info.show
}
}, this);
addNormalNotificationObserver(KEY_NOTIFICATION_LOGIN_SUCCESS, () => {
this.showPop = false;
}, this)
},
destroyed() {
removeNotificationObserver(KEY_NOTIFICATION_LOGIN_SUCCESS, this);
removeNotificationObserver(KEY_NOTIFICATION_LOGIN_SHOW, this);
},
methods: {
open() {
this.$refs.pop.open();
},
close() {
this.$refs.pop.close();
postNotification(KEY_NOTIFICATION_LOGIN_SHOW, {
show: false
})
},
tapMask() {
logout(() => {}, true);
this.showPop = false;
postNotification(KEY_NOTIFICATION_LOGIN_SHOW, {
show: false
})
},
getPhoneNumber() {
this.ttLoging();
},
ttLoging() {
var that = this;
var obj = wx.getLaunchOptionsSync()
var bookId = '';
var thirdParam = '';
var tips2 = '';
// 抖音feed页参数
thirdParam = JSON.stringify(obj.query);
bookId = obj.query.book_id ?? ''
tips2 = obj.query.tips2 ?? ''
if (this.tips1 == 'mints_book' || tips2 == 'mints_book') {
this.slotParam = JSON.stringify(obj.query);
this.$refs.select.open('center');
}
wx.login({
force: true,
success(res) {
login({
vedioId: bookId,
code: res.code,
thirdParam: thirdParam
})
},
fail(res) {
},
});
}
}
}
</script>
<style lang="scss">
.login-content {
position: relative;
width: 500rpx;
height: 300rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
background-color: #fff;
border-radius: 20rpx;
overflow: hidden;
.close-button {
position: absolute;
width: 220rpx;
height: 220rpx;
top: 0;
right: 0;
transform: translate(50%, -50%);
background: #faefe6;
z-index: 2;
border-radius: 50%;
.icon-box {
position: absolute;
left: 30%;
bottom: 30%;
transform: translate(-50%, 50%);
}
}
.welcome-msg {
font-size: 36rpx;
color: #333;
font-weight: 700;
}
.login-button {
position: relative;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
height: 100rpx;
line-height: 100rpx;
margin-top: 30rpx;
width: 400rpx;
background: black;
color: #fff;
border-radius: 20rpx;
.logo {
width: 60rpx;
height: 60rpx;
border-radius: 30rpx;
border: 2rpx solid #fff;
}
.info {
height: 100rpx;
color: #fff;
line-height: 100rpx;
font-size: 30rpx;
margin-left: 20rpx;
}
.cover-button {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 2;
background: transparent;
}
}
}
</style>
\ No newline at end of file
<template>
<view :class="'popup-mask ' + (showClone ? 'popup-mask-active' : '')" @click.stop.prevent="">
<view class="popup-content">
<view class="popup-body"><slot></slot></view>
<icons v-if="close" icon="close" size="64" color="white" @click="handleClose" />
</view>
</view>
</template>
<script>
export default {
name: 'popup',
props: {
show: {
type: Boolean,
default: false
},
close: {
type: Boolean,
default: true
}
},
data() {
return {
showClone: false
};
},
methods: {
handleClose() {
this.showClone = false;
this.$emit('close');
}
},
watch: {
show: {
handler: function(newVal, oldVal) {
this.showClone = newVal;
},
immediate: true
}
}
};
</script>
<style lang="scss">
.popup {
position: fixed;
z-index: 1002;
left: 75rpx;
right: 75rpx;
top: 50%;
padding: 0;
display: none;
transform: translate(0, -50%);
backface-visibility: hidden;
&-content {
position: relative;
top: 50%;
transform: translateY(-50%);
text-align: center;
}
&-body {
border-radius: 32rpx;
// overflow: hidden;
// background-color: #323231;
margin-bottom: 40rpx;
position: relative;
z-index: -1;
}
&-mask {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
z-index: 999;
background-color: rgba(0, 0, 0, 0.8);
opacity: 0;
transform: scale3d(1, 1, 0);
transition: all 0.3s;
padding: 77rpx;
&-active {
opacity: 1;
transform: scale3d(1, 1, 1);
}
}
}
</style>
......@@ -64,7 +64,7 @@
refresh() {
this.count = new ReadCount({
count: getReadTimeCount()
})
})
this.$nextTick(() => {
this.$emit("resize");
})
......
<template>
<z-paging :style="[bgStyle]">
<c-empty v-if='showEmpty'></c-empty>
<template v-else>
<detail-warn></detail-warn>
<detail-thumb :detail='bookData' @tapThumb='tapThumb'></detail-thumb>
<detail-content @tapVip='tapPayPop' :detail='bookData' :userInfo='userInfo'></detail-content>
<detail-new-buy v-if="bookData.isUnlock==0 && !isVip()" :showVipOpen="1" :showBeanOpen="0"
:detail='bookData' :userInfo='userInfo' @unlockBook='unlockBook' @tapVip='tapVipPop'
@tapBean='tapBeanPop'></detail-new-buy>
<detail-bottom :detail='bookData' :userInfo='userInfo' @tapBottomItem='tapBottomItem'></detail-bottom>
<setting-pop :show='showSetting' @close='closePop'></setting-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>
<coin-popup v-if="bookData.isUnlock==0 && !isVip()" :show="showVip" @close="closeVipPop"
:vedioId="bookData.id" @paySuccess="paySuccess">
</coin-popup>
</template>
<c-login :isShareLink="true"></c-login>
<popup :show="showMoibleLogin" @close="showMoibleLogin=false">
<view>
<button open-type="getPhoneNumber" @getphonenumber="MygetPhonenumber">
<view>请先绑定手机号</view>
<view style="color:green;">去绑定</view>
</button>
</view>
</popup>
</z-paging>
</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 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 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
} from "../../../common/services/index.js"
import {
saveStorage,
readStorage
} from "../../../common/utils/storageUtil.js";
export default {
mixins: [SystemInfoMixin],
components: {
DetailWarn,
DetailThumb,
DetailContent,
DetailNewBuy,
DetailBottom,
SettingPop,
VipPop,
BeanPop,
RecommendPop,
},
data() {
return {
bookId: null,
bookData: null,
backgroundColor: "#fff",
showSetting: 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)
}
if (this.isVip() && this.bookData) {
this.unlockBook()
}
// 用户变动,需要刷新数据
this.$nextTick(() => {
this.refreshBookData(this.bookId)
})
}, this)
// 绑定分享参数
// #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() {},
// 文章分享
onShareAppMessage() {
let result = {
title: this.bookData.title,
imageUrl: this.bookData.cover,
path: '/pages/loading?shareId='
}
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) {
getBookDetailData(bookId, (success, data) => {
// uni.stopPullDownRefresh();
if (success) {
data.isUnlock = 1
this.bookData = new BookDetail(data);
// console.log('bookData=' + JSON.stringify(this.bookData));
}
})
},
paySuccess() {
// this.$set(this.bookData, "isUnlock", true);
},
// 点击底部按钮
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;
default:
break;
}
},
isVip() {
if (this.userInfo != null && this.userInfo.memberExpirationDate > 0) {
return true;
}
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;
},
// 关闭会员弹窗
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;
},
// 点击封面,暂无
tapThumb(e) {
gotoBookCoverPage(this.bookData.id);
}
}
}
</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;
// }
// })
// 头条支付
if (!tt.canIUse('requestOrder')) {
toastMessage('请升级抖音APP版本!')
return
}
if (!tt.canIUse('getOrderPayment')) {
toastMessage('请升级抖音APP版本~')
return
}
tt.requestOrder({
data: payInfo.params.data,
byteAuthorization: payInfo.params.byteAuthorization,
success(res) {
tt.getOrderPayment({
orderId: res.oid,
success(res2) {
if (typeof successCB == 'function') successCB(res2);
},
fail(res2) {
console.log('android=ttPay res.errMsg', res2)
},
});
},
fail(res) {
console.log('requestOrder res.errMsg', res)
},
});
}
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>
<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('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: relative;
height: 100rpx;
.fixed-bottom {
position: fixed;
background: #fff;
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 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.free , ...this.detail.charge];
},
preContentSources: function() {
return this.detail && this.detail.free ? [...this.detail.free] : [];
},
limitButtonTitle: function() {
let result = `后续内容更精彩`;
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 class="cover" :src="detail.avatar" mode="aspectFill"></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.wechatRecord.wxId, item.id);
}
}
}
</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,
freeMd,
chargeMd,
} = param || {}
this.contentMd = contentMd;
this.freeMd = freeMd;
this.chargeMd = chargeMd;
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("");
});
let result2 = freeMd ? freeMd.replace(/<[^>]+>/g, "") : "";
result2 = result2.split("\n");
this.free = result2.filter(item => {
return item.split(/[\t\r\f\n\s]*/g).join("");
});
let result3 = chargeMd ? chargeMd.replace(/<[^>]+>/g, "") : "";
result3 = result3.split("\n");
this.charge = result3.filter(item => {
return item.split(/[\t\r\f\n\s]*/g).join("");
});
console.log('param------',param)
}
}
\ No newline at end of file
export default class ContentFormat {
constructor(param) {
const {
fontSize,
backgroundColor
} = param || {}
this.fontSize = fontSize;
this.backgroundColor = backgroundColor;
}
getFormatValue() {
return {
fontSize: `${this.fontSize}px`,
backgroundColor: this.backgroundColor
}
}
}
\ 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"
/** 获取文章详情
* @param {Object} bookId
* @param {Object} callback
*/
function getBookDetailData(bookId, callback) {
apiPOST({
url: `/book/info`,
data: {
id: bookId
},
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_CONTENT_FORMAT = "KEY_SOTRAGE_CONTENT_FORMAT";
const KEY_NOTIFICATION_CONTENT_FORMAT_CHANGE = "KEY_NOTIFICATION_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_CONTENT_FORMAT, result);
notifyContentFormatChange(new ContentFormat(result));
}
function readContentFormat() {
let result = readStorage(KEY_STORAGE_CONTENT_FORMAT);
if (result) {
result = new ContentFormat(result);
} else {
result = new ContentFormat({
fontSize: 17,
backgroundColor: "#fff"
})
}
return result;
}
function notifyContentFormatChange(format) {
postNotification(KEY_NOTIFICATION_CONTENT_FORMAT_CHANGE, format);
}
function watchContentFormatChange(fn, observer) {
if (typeof fn == 'function') fn.call(observer, readContentFormat());
addNormalNotificationObserver(KEY_NOTIFICATION_CONTENT_FORMAT_CHANGE, (data) => {
if (typeof fn == 'function') fn.call(observer, data);
}, observer)
}
function removeContentFormatChangeWatch(observer) {
removeNotificationObserver(KEY_NOTIFICATION_CONTENT_FORMAT_CHANGE, observer);
}
module.exports = {
getBookDetailData,
getBookRecommendData,
addReadRecord,
/**
* content format
*/
saveContentFormat,
readContentFormat,
notifyContentFormatChange,
watchContentFormatChange,
removeContentFormatChangeWatch
}
\ No newline at end of file
<template>
<view>
<c-login></c-login>
</view>
</template>
<script>
export default {
data() {
return {
bookId: null,
bookCoverData: null
};
},
onReady() {
const eventChannel = this.getOpenerEventChannel();
eventChannel.on("openBookCoverPage", (info) => {
this.bookId = info.bookId
})
},
watch: {
bookId: function(n) {
if (n) {
this.$nextTick(() => {
uni.startPullDownRefresh({
})
})
}
}
},
onPullDownRefresh() {
this.refreshBookCoverData(this.bookId)
},
methods: {
refreshBookCoverData(bookId) {
}
}
}
</script>
<style lang="scss">
</style>
\ No newline at end of file
<template>
</template>
<script>
</script>
<style>
</style>
\ No newline at end of file
<template>
<view :style="[bgStyle]">
<c-empty v-if='showEmpty'></c-empty>
<template v-else>
<detail-content ref="content" :detail='bookData' @tabVip2="tapVipPop"
@changeCurrent="changeCurrent"></detail-content>
<detail-bottom :detail='bookData' :userInfo='userInfo' @tapBottomItem='tapBottomItem'></detail-bottom>
<setting-pop :show='showSetting' @close='closePop'></setting-pop>
<catalogue-pop :detail='bookData' :show='showCatalogue' @close='closeCataPop' :current="current"
@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>
<coin-popup v-if="bookData.isUnlock==0 && !isVip()" :show="showVip" @close="closeVipPop"
:vedioId="bookData.id">
</coin-popup>
</template>
</view>
</template>
<script>
import {
isEmpty
} from "../../../common/utils/util.js";
import BookDetail from "./models/BookDetail.js";
import {
getBookDetailData,
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 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
} from "../../../common/services/index.js"
import {
saveStorage,
readStorage
} from "../../../common/utils/storageUtil.js";
export default {
mixins: [SystemInfoMixin],
components: {
DetailWarn,
DetailThumb,
DetailContent,
DetailNewBuy,
DetailBottom,
SettingPop,
VipPop,
BeanPop,
RecommendPop,
CataloguePop
},
data() {
return {
bookId: null,
bookData: null,
showSetting: false,
showCatalogue: false,
showVip: false,
showBean: false,
showRecommend: false,
userInfo: null,
showVipOpen: 0,
showBeanOpen: 0,
current: 1
};
},
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;
})
}
},
onReady() {
// 监听用户变动
watchUserInfoChange((info) => {
this.userInfo = info.userInfo;
// 用户已登录需要记录阅读记录
if (info.userInfo && this.bookId) {
addReadRecord(this.bookId)
}
if (this.isVip() && this.bookData) {
this.paySuccess()
}
// // 用户变动,需要刷新数据
// 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();
// 移除监听
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",
}
}
},
watch: {
bookData: {
handler: function(n) {
if (n) {
uni.setNavigationBarTitle({
title: `阅读:${n.title}`
})
}
}
}
},
methods: {
// 解锁回调
paySuccess() {
this.$set(this.bookData, "isUnlock", 1);
this.$set(this.bookData, "freeNum", this.bookData.articleChapterList.length);
this.$refs.content.reloadChapterinfoData()
},
// 文章数据刷新
refreshBookData(bookId) {
// getOpens((success, data) => {
// this.showBeanOpen = data.openBeans
// this.showVipOpen = data.openVips
// })
getBookDetailData(bookId, (success, data) => {
uni.stopPullDownRefresh();
if (success) {
if (data.isUnlock) {
data.freeNum = data.articleChapterList.length
}
this.bookData = new BookDetail(data);
console.log('bookData=' + JSON.stringify(this.bookData));
}
})
},
// 点击底部按钮
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.memberExpirationDate > 0) {
return true;
}
return false;
},
tapPayPop() {
if (this.showVipOpen != 1 && this.showBeanOpen == 1) {
this.tapBeanPop()
return
}
this.tapVipPop()
},
// 展示充值VIP弹框
tapVipPop() {
this.showVip = true;
},
// 展示充值书豆弹框
tapBeanPop() {
this.showBean = true;
},
// 关闭设置弹窗
closePop(e) {
this.showSetting = false;
},
// 关闭目录弹窗
closeCataPop(e) {
this.showCatalogue = false;
},
// 关闭会员弹窗
closeVipPop(e) {
this.showVip = false;
if (this.bookData.isUnlock == 0) {
setTimeout(() => {
this.showRecommend = true;
}, 300);
}
},
// 关闭书豆弹窗
closeBeanPop(e) {
this.showBean = false;
// setTimeout(() => {
// this.showRecommend = true;
// }, 300);
},
// 关闭推荐弹窗
closeRecommendPop(e) {
this.showRecommend = false;
},
changeCurrent(current) {
this.current = current
}
}
}
</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'>
<view @click="tapMask">
<scroll-view scroll-y style="background-color: white;width: 400rpx;max-width: 400rpx;" @click.stop="">
<view class="setting-box">
<block v-for='(item, index) in catalogueList' :key='index' :item='item'>
<view @click.stop="changeCatalogue(item.chapterNum)">
<view
style="display: flex;flex-direction: row;justify-content: space-between;align-items: center;">
<text class="text"
:style="{'width: 350rpx;color': item.chapterNum==currentCatalogue?'#f00':'#000'}">
{{item.chapterNum}}{{item.chapterName}}</text>
<image v-if="(index+1)>detail.freeNum" style="width: 30rpx;height: 30rpx;"
src="https://mints-pkg.oss-cn-beijing.aliyuncs.com/pkg/img/lock.png"></image>
</view>
<view style="background-color: gainsboro;height: 1rpx;width: 400rpx;margin: 20rpx 0;">
</view>
</view>
</block>
</view>
</scroll-view>
</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
},
current: {
type: Number,
default: 0
}
},
data: function() {
return {
showPop: false,
catalogueList: [],
currentContentFormat: {},
currentCatalogue: 1
}
},
computed: {},
watch: {
current: function(n) {
if (n != this.currentCatalogue) {
this.changeCatalogue(n)
}
},
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) {
// 处理特殊情况,下标错误
let current_e = this.currentContentFormat.catalogueList[i].catalogue
if (!this.detail.isUnlock && current_e > this.detail.freeNum) {
this.currentCatalogue = this.detail.freeNum
return
}
this.currentCatalogue = current_e
}
}
},
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
})
}
this.currentCatalogue = 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 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
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
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment