mirror of
https://github.com/ialley-workshop-open/uni-halo.git
synced 2026-06-12 13:19:31 +08:00
!24 feat(restrict-read): 实现文章限制阅读功能
* feat(restrict-read): 实现文章限制阅读功能 * Merge remote-tracking branch 'origin/v2.0-beta' into v2.0-beta * feat(restrict-read): 实现文章限制阅读功能
This commit is contained in:
+21
-21
@@ -165,30 +165,24 @@ export default {
|
|||||||
return HttpHandler.Get(`/apis/api.plugin.halo.run/v1alpha1/plugins/PluginLinks/links`, params)
|
return HttpHandler.Get(`/apis/api.plugin.halo.run/v1alpha1/plugins/PluginLinks/links`, params)
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 校验文章访问密码
|
* 限制阅读校验
|
||||||
|
* @param restrictType
|
||||||
|
* @param code
|
||||||
|
* @param keyId
|
||||||
|
* @returns {HttpPromise<any>}
|
||||||
*/
|
*/
|
||||||
checkPostVerifyCode: (verifyCode, postId) => {
|
requestRestrictReadCheck: (restrictType, code, keyId) => {
|
||||||
return HttpHandler.Get(`/apis/tools.muyin.site/v1alpha1/verificationCode/check?code=${verifyCode}`, null, {
|
const params = {
|
||||||
header: {
|
code: code,
|
||||||
'Authorization': getAppConfigs().pluginConfig.toolsPlugin?.Authorization,
|
templateType: 'post',
|
||||||
'Wechat-Session-Id': uni.getStorageSync('openid'),
|
restrictType: restrictType,
|
||||||
'Post-Id': postId
|
keyId: keyId
|
||||||
}
|
}
|
||||||
})
|
return HttpHandler.Post(`/apis/tools.muyin.site/v1alpha1/restrict-read/check`, params, {
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 校验文章访问密码
|
|
||||||
*/
|
|
||||||
checkPostPasswordAccess: (password, postId) => {
|
|
||||||
return HttpHandler.Get(`/apis/tools.muyin.site/v1alpha1/visitPassword/checkPost?password=${password}`,
|
|
||||||
null, {
|
|
||||||
header: {
|
header: {
|
||||||
'Authorization': getAppConfigs().pluginConfig.toolsPlugin?.Authorization,
|
'Authorization': getAppConfigs().pluginConfig.toolsPlugin?.Authorization,
|
||||||
'Wechat-Session-Id': uni.getStorageSync('openid'),
|
'Wechat-Session-Id': uni.getStorageSync('openid'),
|
||||||
'Post-Id': postId
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
@@ -196,18 +190,24 @@ export default {
|
|||||||
/**
|
/**
|
||||||
* 获取文章验证码
|
* 获取文章验证码
|
||||||
*/
|
*/
|
||||||
getPostVerifyCode: () => {
|
createVerificationCode: () => {
|
||||||
return HttpHandler.Get(`/apis/tools.muyin.site/v1alpha1/verificationCode/create`, null, {
|
return HttpHandler.Get(`/apis/tools.muyin.site/v1alpha1/restrict-read/create`, null, {
|
||||||
header: {
|
header: {
|
||||||
'Authorization': getAppConfigs().pluginConfig.toolsPlugin?.Authorization,
|
'Authorization': getAppConfigs().pluginConfig.toolsPlugin?.Authorization,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 提交友情链接
|
* 提交友情链接
|
||||||
*/
|
*/
|
||||||
submitLink(form) {
|
submitLink(form) {
|
||||||
return HttpHandler.Post(`/apis/linksSubmit.muyin.site/v1alpha1/submit`, form, null)
|
return HttpHandler.Post(`/apis/linksSubmit.muyin.site/v1alpha1/submit`, form, {
|
||||||
|
header: {
|
||||||
|
'Authorization': getAppConfigs().pluginConfig.linksSubmitPlugin?.Authorization,
|
||||||
|
'Wechat-Session-Id': uni.getStorageSync('openid'),
|
||||||
|
}
|
||||||
|
})
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* 获取二维码信息
|
* 获取二维码信息
|
||||||
|
|||||||
@@ -0,0 +1,243 @@
|
|||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: 'restrict-read-skeleton',
|
||||||
|
props: {
|
||||||
|
loading: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
hover: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
buttonText: {
|
||||||
|
type: String,
|
||||||
|
default: '刷新'
|
||||||
|
},
|
||||||
|
buttonColor: {
|
||||||
|
type: String,
|
||||||
|
default: '#07c160'
|
||||||
|
},
|
||||||
|
buttonSize: {
|
||||||
|
type: String,
|
||||||
|
default: 'normal', // 'small', 'normal', 'large'
|
||||||
|
validator: value => ['small', 'normal', 'large'].includes(value)
|
||||||
|
},
|
||||||
|
lines: {
|
||||||
|
type: Number,
|
||||||
|
default: 4,
|
||||||
|
validator: value => value >= 1 && value <= 6
|
||||||
|
},
|
||||||
|
skeletonColor: {
|
||||||
|
type: String,
|
||||||
|
default: '#f5f5f5'
|
||||||
|
},
|
||||||
|
skeletonHighlight: {
|
||||||
|
type: String,
|
||||||
|
default: '#e8e8e8'
|
||||||
|
},
|
||||||
|
animationDuration: {
|
||||||
|
type: Number,
|
||||||
|
default: 1.5
|
||||||
|
},
|
||||||
|
showButton: {
|
||||||
|
type: Boolean,
|
||||||
|
default: true
|
||||||
|
},
|
||||||
|
tipText: {
|
||||||
|
type: String,
|
||||||
|
default: '' // 默认不显示提示文字
|
||||||
|
},
|
||||||
|
tipColor: {
|
||||||
|
type: String,
|
||||||
|
default: '#666666' // 提示文字颜色
|
||||||
|
},
|
||||||
|
tipSize: {
|
||||||
|
type: Number,
|
||||||
|
default: 24 // 提示文字大小,单位rpx
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
handleRefresh() {
|
||||||
|
this.$emit('refresh');
|
||||||
|
},
|
||||||
|
onTouchStart() {
|
||||||
|
this.$emit('touchstart');
|
||||||
|
},
|
||||||
|
onTouchEnd() {
|
||||||
|
this.$emit('touchend');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<!-- 骨架屏容器 -->
|
||||||
|
<view class="container">
|
||||||
|
<!-- 骨架屏内容 -->
|
||||||
|
<view class="skeleton" v-if="loading">
|
||||||
|
<!-- 内容区域 -->
|
||||||
|
<view class="skeleton-body">
|
||||||
|
<view
|
||||||
|
v-for="(item, index) in Array(lines).fill(0)"
|
||||||
|
:key="index"
|
||||||
|
class="skeleton-line"
|
||||||
|
:class="{
|
||||||
|
'short': index === lines - 2,
|
||||||
|
'shorter': index === lines - 1
|
||||||
|
}"
|
||||||
|
:style="{
|
||||||
|
background: `linear-gradient(90deg, ${skeletonColor} 25%, ${skeletonHighlight} 50%, ${skeletonColor} 75%)`,
|
||||||
|
animationDuration: `${animationDuration}s`
|
||||||
|
}">
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 实际内容 -->
|
||||||
|
<view v-else>
|
||||||
|
<slot></slot>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 提示文字和按钮容器 -->
|
||||||
|
<view v-if="showButton" class="button-container">
|
||||||
|
<!-- 提示文字 -->
|
||||||
|
<text
|
||||||
|
v-if="tipText"
|
||||||
|
class="tip-text"
|
||||||
|
:style="{
|
||||||
|
color: tipColor,
|
||||||
|
fontSize: tipSize + 'rpx'
|
||||||
|
}"
|
||||||
|
>{{ tipText }}</text>
|
||||||
|
|
||||||
|
<!-- 按钮 -->
|
||||||
|
<button
|
||||||
|
class="overlay-button"
|
||||||
|
:class="[buttonSize, { 'button-hover': hover }]"
|
||||||
|
hover-class="none"
|
||||||
|
@touchstart="onTouchStart"
|
||||||
|
@touchend="onTouchEnd"
|
||||||
|
@click="handleRefresh"
|
||||||
|
:style="{ backgroundColor: buttonColor }"
|
||||||
|
>
|
||||||
|
{{ buttonText }}
|
||||||
|
</button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
/* 容器样式 */
|
||||||
|
.container {
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 200rpx;
|
||||||
|
background-color: #ffffff;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
box-shadow: 0 8rpx 24rpx rgba(0, 0, 0, 0.08);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 骨架屏样式 */
|
||||||
|
.skeleton {
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: 30rpx;
|
||||||
|
|
||||||
|
&-body {
|
||||||
|
margin: 20rpx 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
&-line {
|
||||||
|
height: 32rpx;
|
||||||
|
background-size: 400% 100%;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
animation: skeleton-loading 1.5s ease infinite;
|
||||||
|
|
||||||
|
&.short {
|
||||||
|
width: 70%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.shorter {
|
||||||
|
width: 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 按钮容器样式 */
|
||||||
|
.button-container {
|
||||||
|
position: absolute;
|
||||||
|
top: 50%;
|
||||||
|
left: 50%;
|
||||||
|
transform: translate(-50%, -50%);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
z-index: 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 提示文字样式 */
|
||||||
|
.tip-text {
|
||||||
|
margin-bottom: 20rpx;
|
||||||
|
text-align: center;
|
||||||
|
line-height: 1.4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 按钮样式 */
|
||||||
|
.overlay-button {
|
||||||
|
position: relative; // 改为相对定位
|
||||||
|
transform: none; // 移除transform
|
||||||
|
color: white;
|
||||||
|
border-radius: 50rpx;
|
||||||
|
padding: 0 40rpx;
|
||||||
|
height: 80rpx;
|
||||||
|
line-height: 80rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.1);
|
||||||
|
transition: all 0.3s ease;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
cursor: pointer;
|
||||||
|
|
||||||
|
&:active {
|
||||||
|
transform: scale(0.95); // 简化active状态的transform
|
||||||
|
}
|
||||||
|
|
||||||
|
&.small {
|
||||||
|
height: 60rpx;
|
||||||
|
line-height: 60rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
padding: 0 30rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.large {
|
||||||
|
height: 100rpx;
|
||||||
|
line-height: 100rpx;
|
||||||
|
font-size: 32rpx;
|
||||||
|
padding: 0 50rpx;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.button-hover {
|
||||||
|
transform: translateY(-4rpx); // 简化hover状态的transform
|
||||||
|
box-shadow: 0 8rpx 16rpx rgba(0, 0, 0, 0.15);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 骨架屏动画 */
|
||||||
|
@keyframes skeleton-loading {
|
||||||
|
0% {
|
||||||
|
background-position: 100% 50%;
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
background-position: 0 50%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@@ -1,3 +1,5 @@
|
|||||||
|
import {getAppConfigs} from "@/config/index";
|
||||||
|
|
||||||
/** 配置后台管理员token */
|
/** 配置后台管理员token */
|
||||||
const HaloTokenConfig = Object.freeze({
|
const HaloTokenConfig = Object.freeze({
|
||||||
|
|
||||||
@@ -7,7 +9,7 @@ const HaloTokenConfig = Object.freeze({
|
|||||||
|
|
||||||
|
|
||||||
/** 管理员token */
|
/** 管理员token */
|
||||||
systemToken: ``,
|
systemToken: getAppConfigs()?.basicConfig?.personalToken,
|
||||||
/** 匿名用户token */
|
/** 匿名用户token */
|
||||||
anonymousToken: ``
|
anonymousToken: ``
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -260,13 +260,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
|
||||||
"path": "advertise/advertise",
|
|
||||||
"style": {
|
|
||||||
"navigationBarTitleText": "广告页面",
|
|
||||||
"enablePullDownRefresh": false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"path": "submit-link/submit-link",
|
"path": "submit-link/submit-link",
|
||||||
"style": {
|
"style": {
|
||||||
|
|||||||
@@ -1,145 +0,0 @@
|
|||||||
<template>
|
|
||||||
<tm-fullView>
|
|
||||||
<tm-sheet :shadow="24">
|
|
||||||
<tm-alerts label="观看视频即可获取注册码" close></tm-alerts>
|
|
||||||
<tm-divider color="red" model="dashed" :text="codeDataShow?'请复制下方注册码':'请点击“获取注册码”'"></tm-divider>
|
|
||||||
<view class="ma-20" v-show="!codeDataShow">
|
|
||||||
<tm-button theme="bg-gradient-orange-accent" :round="24" block @click="openVideoAd">获取注册码</tm-button>
|
|
||||||
</view>
|
|
||||||
<view class="ma-20" v-show="codeDataShow">
|
|
||||||
<tm-coupon :hdata="codeData" color="orange" @click="fnCopyCode"></tm-coupon>
|
|
||||||
</view>
|
|
||||||
</tm-sheet>
|
|
||||||
<!-- <tm-sheet :shadow="24">
|
|
||||||
<tm-images @load="loadimg" src="https://picsum.photos/300?id=7"></tm-images>
|
|
||||||
</tm-sheet> -->
|
|
||||||
</tm-fullView>
|
|
||||||
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
let videoAd = null;
|
|
||||||
import tmFullView from "@/tm-vuetify/components/tm-fullView/tm-fullView.vue"
|
|
||||||
import tmMenubars from "@/tm-vuetify/components/tm-menubars/tm-menubars.vue"
|
|
||||||
import tmSheet from "@/tm-vuetify/components/tm-sheet/tm-sheet.vue"
|
|
||||||
import tmAlerts from "@/tm-vuetify/components/tm-alerts/tm-alerts.vue"
|
|
||||||
import tmDivider from "@/tm-vuetify/components/tm-divider/tm-divider.vue"
|
|
||||||
import tmCoupon from '@/tm-vuetify/components/tm-coupon/tm-coupon.vue'
|
|
||||||
import tmButton from "@/tm-vuetify/components/tm-button/tm-button.vue"
|
|
||||||
import tmImages from "@/tm-vuetify/components/tm-images/tm-images.vue"
|
|
||||||
|
|
||||||
export default {
|
|
||||||
components: {
|
|
||||||
tmFullView,
|
|
||||||
tmMenubars,
|
|
||||||
tmSheet,
|
|
||||||
tmAlerts,
|
|
||||||
tmDivider,
|
|
||||||
tmCoupon,
|
|
||||||
tmButton,
|
|
||||||
tmImages
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
adUnitId: '',
|
|
||||||
codeDataShow: false,
|
|
||||||
codeData: {
|
|
||||||
// img: 'https://lywq.muyin.site/logo.png',
|
|
||||||
title: "请获取",
|
|
||||||
btnText: '复制',
|
|
||||||
time: '有效期5分钟',
|
|
||||||
sale: '',
|
|
||||||
saleLable: '注册码',
|
|
||||||
saleSplit: ''
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
computed: {
|
|
||||||
toolsPluginConfigs() {
|
|
||||||
return this.$tm.vx.getters().getConfigs?.pluginConfig?.toolsPlugin || {};
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onLoad(options) {
|
|
||||||
// #ifdef MP-WEIXIN
|
|
||||||
wx.hideShareMenu();
|
|
||||||
this.adLoad();
|
|
||||||
// #endif
|
|
||||||
uni.onCopyUrl((result) => {
|
|
||||||
setTimeout(() => {
|
|
||||||
uni.setClipboardData({
|
|
||||||
data: "禁止复制哦",
|
|
||||||
})
|
|
||||||
}, 1000);
|
|
||||||
})
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
adLoad() {
|
|
||||||
if (wx.createRewardedVideoAd) {
|
|
||||||
videoAd = wx.createRewardedVideoAd({
|
|
||||||
adUnitId: this.toolsPluginConfigs.rewardedVideoAdId //你的广告key
|
|
||||||
})
|
|
||||||
videoAd.onError(err => {
|
|
||||||
})
|
|
||||||
videoAd.onClose((status) => {
|
|
||||||
if (status && status.isEnded || status === undefined) {
|
|
||||||
//这里写广告播放完成后的事件
|
|
||||||
this.getRegistrationCode();
|
|
||||||
} else {
|
|
||||||
// 广告播放未完成
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
},
|
|
||||||
openVideoAd: function () {
|
|
||||||
if (videoAd && this.toolsPluginConfigs.rewardedVideoAdId !== '') {
|
|
||||||
videoAd.show().catch(err => {
|
|
||||||
// 失败重试
|
|
||||||
console.log("广告拉取失败")
|
|
||||||
videoAd.load().then(() => videoAd.show())
|
|
||||||
})
|
|
||||||
} else {
|
|
||||||
this.getRegistrationCode();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
getRegistrationCode() {
|
|
||||||
uni.showLoading({
|
|
||||||
title: '正在获取...'
|
|
||||||
});
|
|
||||||
this.$httpApi.v2.getPostVerifyCode()
|
|
||||||
.then(res => {
|
|
||||||
if (res.code === 200) {
|
|
||||||
uni.$tm.toast('获取成功!');
|
|
||||||
this.codeData.title = res.data;
|
|
||||||
this.codeDataShow = true;
|
|
||||||
} else {
|
|
||||||
uni.$tm.toast('操作失败,请重试!');
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.catch(err => {
|
|
||||||
uni.$tm.toast(err.message);
|
|
||||||
});
|
|
||||||
},
|
|
||||||
fnCopyCode() {
|
|
||||||
uni.setClipboardData({
|
|
||||||
data: this.codeData.title,
|
|
||||||
showToast: false,
|
|
||||||
success: () => {
|
|
||||||
uni.showToast({
|
|
||||||
icon: 'none',
|
|
||||||
title: '复制成功!'
|
|
||||||
});
|
|
||||||
setTimeout(() => {
|
|
||||||
uni.navigateBack()
|
|
||||||
}, 500);
|
|
||||||
},
|
|
||||||
fail: () => {
|
|
||||||
uni.showToast({
|
|
||||||
icon: 'none',
|
|
||||||
title: '复制失败!'
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
@@ -77,23 +77,46 @@
|
|||||||
<view class="content ml-24 mr-24">
|
<view class="content ml-24 mr-24">
|
||||||
<!-- markdown渲染 -->
|
<!-- markdown渲染 -->
|
||||||
<view class="markdown-wrap">
|
<view class="markdown-wrap">
|
||||||
|
<view v-if="checkPostRestrictRead(result)">
|
||||||
|
<view v-if="showContentArr.length == 0">
|
||||||
|
<restrict-read-skeleton
|
||||||
|
:loading="true"
|
||||||
|
:lines="3"
|
||||||
|
:tip-text="`此处内容已隐藏,「${getRestrictReadTypeName(result)}可见」`"
|
||||||
|
button-text="查看更多"
|
||||||
|
button-color="#1890ff"
|
||||||
|
skeleton-color="#f0f0f0"
|
||||||
|
skeleton-highlight="#e0e0e0"
|
||||||
|
animation-duration="2"
|
||||||
|
@refresh="readMore"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
<view v-else v-for="showContent in showContentArr">
|
||||||
|
<mp-html class="evan-markdown" lazy-load :domain="markdownConfig.domain"
|
||||||
|
:loading-img="markdownConfig.loadingGif" :scroll-table="true" :selectable="true"
|
||||||
|
:tag-style="markdownConfig.tagStyle" :container-style="markdownConfig.containStyle"
|
||||||
|
:content="showContent" :markdown="true" :showLineNumber="true" :showLanguageName="true"
|
||||||
|
:copyByLongPress="true"/>
|
||||||
|
<restrict-read-skeleton
|
||||||
|
:loading="true"
|
||||||
|
:lines="3"
|
||||||
|
:tip-text="`此处内容已隐藏,「${getRestrictReadTypeName(result)}可见」`"
|
||||||
|
button-text="查看更多"
|
||||||
|
button-color="#1890ff"
|
||||||
|
skeleton-color="#f0f0f0"
|
||||||
|
skeleton-highlight="#e0e0e0"
|
||||||
|
animation-duration="2"
|
||||||
|
@refresh="readMore"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
<view v-else>
|
||||||
<mp-html class="evan-markdown" lazy-load :domain="markdownConfig.domain"
|
<mp-html class="evan-markdown" lazy-load :domain="markdownConfig.domain"
|
||||||
:loading-img="markdownConfig.loadingGif" :scroll-table="true" :selectable="true"
|
:loading-img="markdownConfig.loadingGif" :scroll-table="true" :selectable="true"
|
||||||
:tag-style="markdownConfig.tagStyle" :container-style="markdownConfig.containStyle"
|
:tag-style="markdownConfig.tagStyle" :container-style="markdownConfig.containStyle"
|
||||||
:content="result.content.raw" :markdown="true" :showLineNumber="true" :showLanguageName="true"
|
:content="result.content.raw" :markdown="true" :showLineNumber="true" :showLanguageName="true"
|
||||||
:copyByLongPress="true"/>
|
:copyByLongPress="true"/>
|
||||||
<tm-more v-if="showValidVisitMore" :disabled="true" :maxHeight="1" :isRemovBar="true"
|
|
||||||
@click="showValidVisitMorePop()">
|
|
||||||
<view class="text-size-n pa-24">
|
|
||||||
以下内容已隐藏,请验证后查看完整内容……
|
|
||||||
</view>
|
</view>
|
||||||
<template v-slot:more="{ data }">
|
|
||||||
<view class="">
|
|
||||||
<text class="text-blue text-size-m text-weight-b">文章部分内容已加密点击解锁</text>
|
|
||||||
<text class="text-blue iconfont icon-lock-fill text-size-s ml-5"></text>
|
|
||||||
</view>
|
|
||||||
</template>
|
|
||||||
</tm-more>
|
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 版权声明 -->
|
<!-- 版权声明 -->
|
||||||
@@ -223,14 +246,41 @@
|
|||||||
</view>
|
</view>
|
||||||
</tm-poup>
|
</tm-poup>
|
||||||
|
|
||||||
<!-- 密码访问解密弹窗 -->
|
<!-- 验证码弹窗 -->
|
||||||
<tm-dialog :disabled="true" :input-val.sync="validVisitModal.value" title="验证提示" confirmText="立即验证"
|
<tm-dialog v-model="verificationCodeModal.show" :disabled="true" title="验证提示"
|
||||||
:showCancel="validVisitModal.useCancel" model="confirm" v-model="validVisitModal.show"
|
:confirmText="verificationCodeModal.confirmText"
|
||||||
content="博主设置了需要密码才能查看该文章内容,请输入文章密码进行验证" theme="split" confirmColor="blue shadow-blue-0"
|
:showCancel="true" model="verificationCodeModal.model" theme="split" confirmColor="blue shadow-blue-0"
|
||||||
@cancel="closeValidVisitPop" @confirm="fnValidVisitPwd"></tm-dialog>
|
@cancel="verificationCodeModal.show = false"
|
||||||
<!-- 是否前往获取验证码 -->
|
@confirm="restrictReadCheckOrViewVideo">
|
||||||
<tm-dialog v-model="showGetPassword" title="免费获取验证码" content="是否前往获取验证码?" @confirm="toAdvertise"
|
<template #default>
|
||||||
@cancel="closeAllPop"></tm-dialog>
|
<view class="pa-20">
|
||||||
|
<!-- 扫码验证模式 -->
|
||||||
|
<view v-if="verificationCodeModal.type === 'scan'" class="flex flex-col flex-center">
|
||||||
|
<text class="mb-20">请扫描下方二维码获取验证码</text>
|
||||||
|
<tm-images
|
||||||
|
:width="180"
|
||||||
|
:height="180"
|
||||||
|
:src="verificationCodeModal.imgUrl"
|
||||||
|
preview
|
||||||
|
round="5"
|
||||||
|
></tm-images>
|
||||||
|
<tm-input bg-color="grey-lighten-5" required v-model="restrictReadInputCode" placeholder="请输入验证码"
|
||||||
|
:border-bottom="false" :flat="true"></tm-input>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 观看视频模式 -->
|
||||||
|
<view v-else class="flex flex-col flex-center">
|
||||||
|
<text class="mb-20">点击观看视频之后,可访问</text>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</template>
|
||||||
|
</tm-dialog>
|
||||||
|
<!-- 密码弹窗 -->
|
||||||
|
<tm-dialog v-model="passwordModal.show" :disabled="true" title="验证提示" confirmText="确定" content="请输入密码"
|
||||||
|
:showCancel="true" model="confirm" theme="split" confirmColor="blue shadow-blue-0"
|
||||||
|
:input-val.sync="restrictReadInputCode"
|
||||||
|
@cancel="passwordModal.show = false"
|
||||||
|
@confirm="restrictReadCheck"></tm-dialog>
|
||||||
|
|
||||||
<!-- 评论弹窗 -->
|
<!-- 评论弹窗 -->
|
||||||
<block v-if="calcIsShowComment">
|
<block v-if="calcIsShowComment">
|
||||||
@@ -260,9 +310,23 @@ import rCanvas from '@/components/r-canvas/r-canvas.vue';
|
|||||||
import barrage from '@/components/barrage/barrage.vue';
|
import barrage from '@/components/barrage/barrage.vue';
|
||||||
import {getAppConfigs} from '@/config/index.js'
|
import {getAppConfigs} from '@/config/index.js'
|
||||||
import {upvote} from '@/utils/upvote.js'
|
import {upvote} from '@/utils/upvote.js'
|
||||||
|
import {
|
||||||
|
checkPostRestrictRead,
|
||||||
|
copyToClipboard,
|
||||||
|
getRestrictReadTypeName,
|
||||||
|
getShowableContent
|
||||||
|
} from "@/utils/restrictRead";
|
||||||
|
import HaloTokenConfig from "@/config/token.config";
|
||||||
|
import RestrictReadSkeleton from "@/components/restrict-read-skeleton/restrict-read-skeleton.vue";
|
||||||
|
import TmImages from "@/tm-vuetify/components/tm-images/tm-images.vue";
|
||||||
|
import TmInput from "@/tm-vuetify/components/tm-input/tm-input.vue";
|
||||||
|
|
||||||
|
let videoAd = null;
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
|
TmInput,
|
||||||
|
TmImages,
|
||||||
|
RestrictReadSkeleton,
|
||||||
tmSkeleton,
|
tmSkeleton,
|
||||||
tmPoup,
|
tmPoup,
|
||||||
tmFlotbutton,
|
tmFlotbutton,
|
||||||
@@ -305,16 +369,19 @@ export default {
|
|||||||
},
|
},
|
||||||
|
|
||||||
metas: [], // 自定义元数据
|
metas: [], // 自定义元数据
|
||||||
// 文章加密(弹窗输入密码解密)
|
showContentArr: [],
|
||||||
validVisitModal: {
|
restrictReadInputCode: '',
|
||||||
|
verificationCodeModal: {
|
||||||
show: false,
|
show: false,
|
||||||
useCancel: false,
|
model: 'confirm',
|
||||||
value: undefined
|
confirmText: '确定',
|
||||||
|
type: '',
|
||||||
|
imgUrl: '',
|
||||||
|
adId: ''
|
||||||
|
},
|
||||||
|
passwordModal: {
|
||||||
|
show: false
|
||||||
},
|
},
|
||||||
visitType: 0, // 0 未加密 1 后端部分隐藏 2 前端部分隐藏 3 全部隐藏
|
|
||||||
visitPwd: undefined,
|
|
||||||
showValidVisitMore: true,
|
|
||||||
showGetPassword: false,
|
|
||||||
commentModal: {
|
commentModal: {
|
||||||
show: false,
|
show: false,
|
||||||
isComment: false,
|
isComment: false,
|
||||||
@@ -397,6 +464,8 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
getRestrictReadTypeName,
|
||||||
|
checkPostRestrictRead,
|
||||||
fnGetData() {
|
fnGetData() {
|
||||||
this.loading = 'loading';
|
this.loading = 'loading';
|
||||||
this.$httpApi.v2
|
this.$httpApi.v2
|
||||||
@@ -409,35 +478,31 @@ export default {
|
|||||||
if (openid === '' || openid === null) {
|
if (openid === '' || openid === null) {
|
||||||
this.fnGetOpenid();
|
this.fnGetOpenid();
|
||||||
}
|
}
|
||||||
const visitFlag = uni.getStorageSync('visit_' + this.result?.metadata?.name);
|
|
||||||
|
|
||||||
const toolsPluginEnabled = getAppConfigs().pluginConfig.toolsPlugin?.enabled;
|
const toolsPluginEnabled = getAppConfigs().pluginConfig.toolsPlugin?.enabled;
|
||||||
|
const restrictRead = checkPostRestrictRead(this.result);
|
||||||
|
|
||||||
if (toolsPluginEnabled && !visitFlag) {
|
if (restrictRead && toolsPluginEnabled) {
|
||||||
const annotationsMap = res?.metadata?.annotations;
|
const verifyCodeType = getAppConfigs().pluginConfig.toolsPlugin?.verifyCodeType;
|
||||||
if (('restrictReadEnable' in annotationsMap) && annotationsMap.restrictReadEnable ===
|
if (verifyCodeType === 'scan') {
|
||||||
'true') {
|
const scanCodeUrl = getAppConfigs().pluginConfig.toolsPlugin?.scanCodeUrl;
|
||||||
this.visitType = 1;
|
this.verificationCodeModal.type = 'scan';
|
||||||
this.showValidVisitMorePop();
|
this.verificationCodeModal.imgUrl = this.$utils.checkImageUrl(scanCodeUrl);
|
||||||
} else if ('unihalo_useVisitMorePwd' in annotationsMap) {
|
this.verificationCodeModal.model = 'confirm';
|
||||||
this.visitType = 2;
|
this.verificationCodeModal.confirmText = '立即验证';
|
||||||
this.visitPwd = annotationsMap.unihalo_useVisitMorePwd;
|
} else if (verifyCodeType === 'advert') {
|
||||||
this.showValidVisitMorePop();
|
const rewardedVideoAdId = getAppConfigs().pluginConfig.toolsPlugin?.rewardedVideoAdId;
|
||||||
} else if ('unihalo_useVisitPwd' in annotationsMap) {
|
this.verificationCodeModal.type = 'advert';
|
||||||
this.visitType = 3;
|
this.verificationCodeModal.adId = rewardedVideoAdId;
|
||||||
this.visitPwd = annotationsMap.unihalo_useVisitPwd;
|
this.verificationCodeModal.model = 'dialog';
|
||||||
this.showValidVisitPop();
|
this.verificationCodeModal.confirmText = '观看视频';
|
||||||
} else if (('restrictReadEnable' in annotationsMap) && annotationsMap
|
// #ifdef MP-WEIXIN
|
||||||
.restrictReadEnable === 'password') {
|
this.adLoad();
|
||||||
this.visitType = 4;
|
// #endif
|
||||||
this.showValidVisitPop();
|
|
||||||
} else {
|
|
||||||
this.visitType = 0;
|
|
||||||
this.showValidVisitMore = false;
|
|
||||||
}
|
}
|
||||||
this.fnHideContent();
|
|
||||||
} else {
|
const showableContentArr = getShowableContent(this.result);
|
||||||
this.showValidVisitMore = false;
|
this.showContentArr = showableContentArr;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.fnSetPageTitle('文章详情');
|
this.fnSetPageTitle('文章详情');
|
||||||
@@ -1008,39 +1073,35 @@ export default {
|
|||||||
url: originalURL
|
url: originalURL
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
showValidVisitPop() {
|
readMore() {
|
||||||
this.showValidVisitMore = true;
|
const annotations = this.result?.metadata?.annotations;
|
||||||
this.validVisitModal.value = undefined;
|
const restrictReadEnable = annotations?.restrictReadEnable;
|
||||||
this.validVisitModal.show = true;
|
if (restrictReadEnable === 'password') {
|
||||||
this.validVisitModal.useCancel = false;
|
this.passwordModal.show = true;
|
||||||
},
|
return;
|
||||||
showValidVisitMorePop() {
|
} else if (restrictReadEnable === 'code') {
|
||||||
this.showValidVisitMore = true;
|
this.verificationCodeModal.show = true;
|
||||||
this.validVisitModal.value = undefined;
|
return;
|
||||||
this.validVisitModal.show = true;
|
} else if (restrictReadEnable === 'comment') {
|
||||||
this.validVisitModal.useCancel = true;
|
uni.showToast({
|
||||||
},
|
title: '前往web端评论后访问',
|
||||||
closeValidVisitPop() {
|
icon: 'none'
|
||||||
this.validVisitModal.show = false;
|
|
||||||
this.validVisitModal.useCancel = true;
|
|
||||||
this.validVisitModal.value = undefined;
|
|
||||||
if (this.visitType === 1) {
|
|
||||||
// 显示是否前往获取验证弹窗
|
|
||||||
this.validVisitModal.show = true;
|
|
||||||
this.showGetPassword = true;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
closeAllPop() {
|
|
||||||
this.validVisitModal.show = false;
|
|
||||||
this.validVisitModal.useCancel = true;
|
|
||||||
this.validVisitModal.value = undefined;
|
|
||||||
this.showGetPassword = false;
|
|
||||||
},
|
|
||||||
toAdvertise() {
|
|
||||||
this.showGetPassword = false;
|
|
||||||
uni.navigateTo({
|
|
||||||
url: '/pagesA/advertise/advertise'
|
|
||||||
});
|
});
|
||||||
|
} else if (restrictReadEnable === 'login') {
|
||||||
|
uni.showToast({
|
||||||
|
title: '前往web端登录后访问',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
} else if (restrictReadEnable === 'pay') {
|
||||||
|
uni.showToast({
|
||||||
|
title: '前往web端支付后访问',
|
||||||
|
icon: 'none'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
// 两秒后执行
|
||||||
|
setTimeout(() => {
|
||||||
|
copyToClipboard(`${HaloTokenConfig.BASE_API + this.result.status.permalink}`);
|
||||||
|
}, 2000);
|
||||||
},
|
},
|
||||||
// 获取openid
|
// 获取openid
|
||||||
fnGetOpenid() {
|
fnGetOpenid() {
|
||||||
@@ -1058,68 +1119,28 @@ export default {
|
|||||||
})
|
})
|
||||||
// #endif
|
// #endif
|
||||||
},
|
},
|
||||||
// 隐藏内容
|
restrictReadCheckOrViewVideo() {
|
||||||
fnHideContent() {
|
console.log('restrictReadCheckOrViewVideo', this.verificationCodeModal.type)
|
||||||
switch (this.visitType) {
|
if (this.verificationCodeModal.type === 'advert') {
|
||||||
case 1:
|
this.openVideoAd();
|
||||||
const restrictReadRaw = this.result?.content?.raw.split('<!-- restrictRead start -->')[0];
|
} else {
|
||||||
this.result.content.raw = restrictReadRaw;
|
this.restrictReadCheck();
|
||||||
return;
|
|
||||||
case 2:
|
|
||||||
case 3:
|
|
||||||
const length = this.result?.content?.raw?.length;
|
|
||||||
const first30PercentLength = Math.floor(length * 0.3);
|
|
||||||
const first30PercentRaw = this.result?.content?.raw?.substring(0, first30PercentLength);
|
|
||||||
this.result.content.raw = first30PercentRaw;
|
|
||||||
return;
|
|
||||||
case 4:
|
|
||||||
this.result.content.raw = "";
|
|
||||||
return;
|
|
||||||
default:
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
// 校验密码
|
// 校验密码
|
||||||
fnValidVisitPwd() {
|
restrictReadCheck() {
|
||||||
switch (this.visitType) {
|
if (!this.restrictReadInputCode) {
|
||||||
case 0:
|
|
||||||
return;
|
|
||||||
case 1:
|
|
||||||
this.$httpApi.v2.checkPostVerifyCode(this.validVisitModal.value, this.result?.metadata?.name).then(
|
|
||||||
res => {
|
|
||||||
if (res.code === 200) {
|
|
||||||
uni.setStorageSync('visit_' + this.result?.metadata?.name, true)
|
|
||||||
this.closeAllPop();
|
|
||||||
this.fnGetData();
|
|
||||||
} else {
|
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
title: '密码错误',
|
title: '请输入内容',
|
||||||
icon: 'none'
|
icon: 'none'
|
||||||
});
|
});
|
||||||
}
|
|
||||||
}).catch(err => {
|
|
||||||
console.log(err);
|
|
||||||
});
|
|
||||||
return;
|
return;
|
||||||
case 2:
|
|
||||||
case 3:
|
|
||||||
if (this.visitPwd === this.validVisitModal.value) {
|
|
||||||
uni.setStorageSync('visit_' + this.result?.metadata?.name, true)
|
|
||||||
this.closeValidVisitPop();
|
|
||||||
this.fnGetData();
|
|
||||||
} else {
|
|
||||||
uni.showToast({
|
|
||||||
title: '密码错误',
|
|
||||||
icon: 'none'
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
return;
|
this.$httpApi.v2.requestRestrictReadCheck(this.result?.metadata?.annotations?.restrictReadEnable, this.restrictReadInputCode, this.result?.metadata?.name)
|
||||||
case 4:
|
|
||||||
this.$httpApi.v2.checkPostPasswordAccess(this.validVisitModal.value, this.result?.metadata?.name)
|
|
||||||
.then(res => {
|
.then(res => {
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
uni.setStorageSync('visit_' + this.result?.metadata?.name, true)
|
this.passwordModal.show = false;
|
||||||
this.closeAllPop();
|
this.verificationCodeModal.show = false;
|
||||||
this.fnGetData();
|
this.fnGetData();
|
||||||
} else {
|
} else {
|
||||||
uni.showToast({
|
uni.showToast({
|
||||||
@@ -1127,13 +1148,56 @@ export default {
|
|||||||
icon: 'none'
|
icon: 'none'
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}).catch(err => {
|
})
|
||||||
console.log(err);
|
.catch(err => {
|
||||||
|
console.error(err);
|
||||||
});
|
});
|
||||||
return;
|
},
|
||||||
default:
|
adLoad() {
|
||||||
return;
|
if (wx.createRewardedVideoAd) {
|
||||||
|
videoAd = wx.createRewardedVideoAd({
|
||||||
|
adUnitId: this.verificationCodeModal.adId
|
||||||
|
})
|
||||||
|
videoAd.onError(err => {
|
||||||
|
})
|
||||||
|
videoAd.onClose((status) => {
|
||||||
|
if (status && status.isEnded || status === undefined) {
|
||||||
|
//这里写广告播放完成后的事件
|
||||||
|
this.getVerificationCode();
|
||||||
|
} else {
|
||||||
|
// 广告播放未完成
|
||||||
}
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
},
|
||||||
|
openVideoAd: function () {
|
||||||
|
if (videoAd && this.verificationCodeModal.adId !== '') {
|
||||||
|
videoAd.show().catch(err => {
|
||||||
|
// 失败重试
|
||||||
|
console.log("广告拉取失败")
|
||||||
|
videoAd.load().then(() => videoAd.show())
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
this.getVerificationCode();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
getVerificationCode() {
|
||||||
|
uni.showLoading({
|
||||||
|
title: '正在获取...'
|
||||||
|
});
|
||||||
|
this.$httpApi.v2.createVerificationCode()
|
||||||
|
.then(res => {
|
||||||
|
if (res.code === 200) {
|
||||||
|
this.verificationCodeModal.show = false;
|
||||||
|
this.restrictReadInputCode = res.data;
|
||||||
|
this.restrictReadCheck();
|
||||||
|
} else {
|
||||||
|
uni.$tm.toast('操作失败,请重试!');
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
uni.$tm.toast(err.message);
|
||||||
|
});
|
||||||
},
|
},
|
||||||
async qrCodeImageUrl() {
|
async qrCodeImageUrl() {
|
||||||
const useDynamicQRCode = this.haloConfigs?.appConfig?.appInfo?.useDynamicQRCode;
|
const useDynamicQRCode = this.haloConfigs?.appConfig?.appInfo?.useDynamicQRCode;
|
||||||
|
|||||||
@@ -192,7 +192,7 @@ export default {
|
|||||||
this.$httpApi.v2.submitLink(this.form)
|
this.$httpApi.v2.submitLink(this.form)
|
||||||
.then(res => {
|
.then(res => {
|
||||||
if (res.code === 200) {
|
if (res.code === 200) {
|
||||||
uni.$tm.toast('友链提交成功!');
|
uni.$tm.toast(res.msg);
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
uni.navigateTo({
|
uni.navigateTo({
|
||||||
url: '/pagesA/friend-links/friend-links',
|
url: '/pagesA/friend-links/friend-links',
|
||||||
@@ -204,7 +204,7 @@ export default {
|
|||||||
});
|
});
|
||||||
}, 1000);
|
}, 1000);
|
||||||
} else {
|
} else {
|
||||||
uni.$tm.toast('操作失败,请重试!');
|
uni.$tm.toast(res.msg);
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.catch(err => {
|
.catch(err => {
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ class vuex {
|
|||||||
let t = this;
|
let t = this;
|
||||||
const g = this.store.getters
|
const g = this.store.getters
|
||||||
let keys = Object.keys(g);
|
let keys = Object.keys(g);
|
||||||
console.log(keys)
|
|
||||||
let k = keys.map((el,index)=>{
|
let k = keys.map((el,index)=>{
|
||||||
let f = el.split('/');
|
let f = el.split('/');
|
||||||
let tst = {}
|
let tst = {}
|
||||||
|
|||||||
@@ -0,0 +1,140 @@
|
|||||||
|
/**
|
||||||
|
* 检查文章是否受限
|
||||||
|
* @param post
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
export function checkPostRestrictRead(post) {
|
||||||
|
const annotations = post?.metadata?.annotations;
|
||||||
|
const restrictReadEnable = annotations?.restrictReadEnable;
|
||||||
|
|
||||||
|
if (restrictReadEnable === 'false') return false;
|
||||||
|
|
||||||
|
const restrictType = restrictReadEnable;
|
||||||
|
const raw = post.content.raw;
|
||||||
|
|
||||||
|
const startTag = `<!-- ${restrictType}:restrict-read-html-tpl start -->`;
|
||||||
|
const endTag = `<!-- ${restrictType}:restrict-read-html-tpl end -->`;
|
||||||
|
|
||||||
|
// 使用正则模糊匹配(允许前后有空白字符)
|
||||||
|
const startRegex = new RegExp(`\\s*${escapeRegExp(startTag)}\\s*`);
|
||||||
|
const endRegex = new RegExp(`\\s*${escapeRegExp(endTag)}\\s*`);
|
||||||
|
|
||||||
|
return startRegex.test(raw) && endRegex.test(raw);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 替换受限内容
|
||||||
|
* @param post
|
||||||
|
* @param replacement
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
export function replaceRestrictedContent(post, replacement = '') {
|
||||||
|
const annotations = post?.metadata?.annotations;
|
||||||
|
const restrictReadEnable = annotations?.restrictReadEnable;
|
||||||
|
|
||||||
|
if (restrictReadEnable === 'false') return post.content.raw;
|
||||||
|
|
||||||
|
const restrictType = restrictReadEnable;
|
||||||
|
const raw = post.content.raw;
|
||||||
|
|
||||||
|
const startTag = `<!-- ${restrictType}:restrict-read-html-tpl start -->`;
|
||||||
|
const endTag = `<!-- ${restrictType}:restrict-read-html-tpl end -->`;
|
||||||
|
|
||||||
|
const startRegex = new RegExp(`\\s*${escapeRegExp(startTag)}\\s*`, 'g');
|
||||||
|
const endRegex = new RegExp(`\\s*${escapeRegExp(endTag)}\\s*`, 'g');
|
||||||
|
|
||||||
|
// 构造完整匹配的正则
|
||||||
|
const pattern = `${startRegex.source}(.*?)${endRegex.source}`;
|
||||||
|
const regex = new RegExp(pattern, 'gs');
|
||||||
|
|
||||||
|
return raw.replace(regex, replacement);
|
||||||
|
}
|
||||||
|
|
||||||
|
// 常量定义(可抽离到 constants.js)
|
||||||
|
const PLACEHOLDER = 'restrict-read-placeholder';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取可展示的HTML内容块
|
||||||
|
* @param {Object} post - 文章对象
|
||||||
|
* @returns {string[]} - 分割后的HTML片段数组
|
||||||
|
*/
|
||||||
|
export function getShowableContent(post) {
|
||||||
|
const restrictEnabled = checkPostRestrictRead(post);
|
||||||
|
const rawContent = post?.content?.raw || '';
|
||||||
|
|
||||||
|
// 替换受限内容为占位符
|
||||||
|
const processedContent = restrictEnabled
|
||||||
|
? replaceRestrictedContent(post, PLACEHOLDER)
|
||||||
|
: rawContent;
|
||||||
|
|
||||||
|
// 按占位符分割内容
|
||||||
|
const contentFragments = processedContent
|
||||||
|
.split(PLACEHOLDER)
|
||||||
|
.map(fragment => fragment.trim())
|
||||||
|
.filter(fragment => fragment);
|
||||||
|
|
||||||
|
// 移除最后一个元素如果它只包含HTML标签而无实际文本
|
||||||
|
if (contentFragments.length > 0 && isHtmlEmpty(contentFragments[contentFragments.length - 1])) {
|
||||||
|
contentFragments.pop();
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('contentFragments:', contentFragments);
|
||||||
|
|
||||||
|
return contentFragments;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取受限阅读类型名称
|
||||||
|
* @param post
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
export function getRestrictReadTypeName(post) {
|
||||||
|
const annotations = post?.metadata?.annotations;
|
||||||
|
const restrictReadEnable = annotations?.restrictReadEnable;
|
||||||
|
|
||||||
|
if (restrictReadEnable === 'false') return '';
|
||||||
|
if (restrictReadEnable === 'password') return '密码';
|
||||||
|
if (restrictReadEnable === 'code') return '验证码';
|
||||||
|
if (restrictReadEnable === 'login') return '登录';
|
||||||
|
if (restrictReadEnable === 'pay') return '付费';
|
||||||
|
if (restrictReadEnable === 'comment') return '评论';
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 复制文本到剪贴板
|
||||||
|
* @param text
|
||||||
|
* @returns {Promise<void>}
|
||||||
|
*/
|
||||||
|
export async function copyToClipboard(text) {
|
||||||
|
try {
|
||||||
|
await uni.setClipboardData({
|
||||||
|
data: text,
|
||||||
|
success: () => {
|
||||||
|
uni.showToast({
|
||||||
|
title: '复制成功',
|
||||||
|
icon: 'success'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
console.error('复制出错:', error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 转义字符串用于正则表达式
|
||||||
|
* @param string
|
||||||
|
* @returns {*}
|
||||||
|
*/
|
||||||
|
function escapeRegExp(string) {
|
||||||
|
return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断字符串去除HTML标签后是否为空
|
||||||
|
* @param {string} html
|
||||||
|
* @returns {boolean}
|
||||||
|
*/
|
||||||
|
function isHtmlEmpty(html) {
|
||||||
|
return !html || !html.replace(/<[^>]+>/g, '').trim();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user