Răsfoiți Sursa

!16 新增文章访问验证码功能
Merge pull request !16 from liuyiwuqing/v2.0-beta

小莫唐尼 1 an în urmă
părinte
comite
f9fce7d0ed

+ 30 - 1
api/v2/all.api.js

@@ -20,7 +20,11 @@ export default {
 	 * @param {String} name 分类名称
 	 */
 	getPostByName: (name) => {
-		return HttpHandler.Get(`/apis/api.content.halo.run/v1alpha1/posts/${name}`, {})
+		return HttpHandler.Get(`/apis/api.content.halo.run/v1alpha1/posts/${name}`, {}, {
+			header: {
+				'Wechat-Session-Id': uni.getStorageSync('openid'),
+			}
+		})
 	},
 
 	/**
@@ -140,4 +144,29 @@ export default {
 	getFriendLinkList: (params) => {
 		return HttpHandler.Get(`/apis/api.plugin.halo.run/v1alpha1/plugins/PluginLinks/links`, params)
 	},
+
+
+	/**
+	 * 校验文章访问密码
+	 */
+	checkPostVerifyCode: (verifyCode, postId) => {
+		return HttpHandler.Get(`/tools/verificationCode/check?code=${verifyCode}`, null, {
+			header: {
+				'Authorization': 'Tools工具箱插件设置的认证token',
+				'Wechat-Session-Id': uni.getStorageSync('openid'),
+				'Post-Id': postId
+			}
+		})
+	},
+
+	/**
+	 * 获取文章验证码
+	 */
+	getPostVerifyCode: () => {
+		return HttpHandler.Get(`/tools/verificationCode/create`, null, {
+			header: {
+				'Authorization': 'Tools工具箱插件设置的认证token'
+			}
+		})
+	},
 }

+ 2 - 1
config/ad.config.template.js

@@ -9,5 +9,6 @@ export default {
 		title: 'uni-halo 正式开源啦,欢迎来使用和体验!',
 		content: '基于 uni-app + halo1.x API 实现一款现代化的开源博客 / CMS 系统API开发的多端应用。功能包括:前台博客系统 和 后台管理系统,同时满足浏览和管理两端合一的需求,真正实现一个应用实现博客浏览和后台管理。',
 		url: 'https://uni-halo.925i.cn'
-	}
+	},
+	rewardedVideoAd: '微信小程序激励广告id' // 奖励视频广告
 }

+ 7 - 0
pages.json

@@ -266,6 +266,13 @@
 				{
 					"navigationBarTitleText" : ""
 				}
+			},
+			{
+				"path": "advertise/advertise",
+				"style": {
+					"navigationBarTitleText": "广告页面",
+					"enablePullDownRefresh": false
+				}
 			}]
 		},
 		{

+ 142 - 0
pagesA/advertise/advertise.vue

@@ -0,0 +1,142 @@
+<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: ''
+				}
+			}
+		},
+		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: haloAdConfig.rewardedVideoAd //你的广告key
+					})
+					videoAd.onError(err => {})
+					videoAd.onClose((status) => {
+						if (status && status.isEnded || status === undefined) {
+							//这里写广告播放完成后的事件
+							this.getRegistrationCode();
+						} else {
+							// 广告播放未完成
+						}
+					})
+				}
+			},
+			openVideoAd: function() {
+				if (videoAd && haloAdConfig.rewardedVideoAd != '') {
+					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>
+
+<style>
+
+</style>

+ 158 - 77
pagesA/article-detail/article-detail.vue

@@ -76,13 +76,16 @@
 			<view class="content ml-24 mr-24">
 				<!-- markdown渲染 -->
 				<view class="markdown-wrap">
-					<tm-more :disabled="true" :maxHeight="1500" :isRemovBar="true" :open="showValidVisitMore"
-						@click="fnOnShowValidVisitMore">
-						<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="result.content.raw" :markdown="true" :showLineNumber="true"
-							:showLanguageName="true" :copyByLongPress="true" />
+          <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="result.content.raw" :markdown="true" :showLineNumber="true"
+                   :showLanguageName="true" :copyByLongPress="true" />
+					<tm-more v-if="showValidVisitMore" :disabled="true" :maxHeight="1" :isRemovBar="true"
+						@click="showValidVisitMorePop()">
+            <view class="text-size-n pa-24">
+              以下内容已隐藏,请验证后查看完整内容……
+            </view>
 						<template v-slot:more="{ data }">
 							<view class="">
 								<text class="text-blue text-size-m text-weight-b">文章部分内容已加密点击解锁</text>
@@ -204,11 +207,13 @@
 
 		<!-- 密码访问解密弹窗 -->
 		<tm-dialog :disabled="true" :input-val.sync="validVisitModal.value" title="验证提示" confirmText="立即验证"
-			:showCancel="validVisitModal.useCancel" model="confirm" v-model="validVisitModal.show"
-			content="博主设置了需要密码才能查看该文章内容,请输入文章密码进行验证" theme="split" confirmColor="blue shadow-blue-0"
-			@cancel="fnOnValidVisitCancel" @confirm="fnOnValidVisitConfirm"></tm-dialog>
+               :showCancel="validVisitModal.useCancel" model="confirm" v-model="validVisitModal.show"
+               content="博主设置了需要密码才能查看该文章内容,请输入文章密码进行验证" theme="split" confirmColor="blue shadow-blue-0"
+               @cancel="closeValidVisitPop" @confirm="fnValidVisitPwd"></tm-dialog>
+    <!-- 是否前往获取验证码 -->
+    <tm-dialog v-model="showGetPassword" title="免费获取验证码" content="是否前往获取验证码?" @confirm="toAdvertise" @cancel="closeAllPop"></tm-dialog>
 
-	</view>
+  </view>
 </template>
 
 <script>
@@ -294,7 +299,10 @@
 					useCancel: false,
 					value: undefined
 				},
+        visitType: 0, // 0 未加密 1 后端部分隐藏 2 前端部分隐藏 3 全部隐藏
+        visitPwd: undefined,
 				showValidVisitMore: true,
+        showGetPassword: false
 			};
 		},
 		computed: {
@@ -317,46 +325,15 @@
 			},
 			// 原文链接:个人资质=可以打开公众号文章;非个人:任意链接地址(需在小程序后台配置)
 			originalURL() {
-				const target = this.metas.find(x => x.key == 'unihalo_originalURL');
-				if (!target) return '';
-				return target.value || '';
+        if ('unihalo_originalURL' in this.result?.metadata?.annotations) {
+          return this.result?.metadata?.annotations?.unihalo_originalURL;
+        } else {
+          return '';
+        }
 			},
-			// 是否使用文章密码功能,如果值不为空,则优先于 useVisitMore:unihalo_useVisitPwd
-			useVisitPwd() {
-				const target = this.metas.find(x => x.key == 'unihalo_useVisitPwd');
-				if (!target) return false;
-				return target.value || false;
-			},
-			// 是否启用查看完整文章的功能的密码:unihalo_useVisitMorePwd
-			useVisitMorePwd() {
-				const target = this.metas.find(x => x.key == 'unihalo_useVisitMorePwd');
-				if (!target) return false;
-				return target.value || false;
-			},
-			// 当前是使用哪一种类型的密码验证 0=不使用任何 1=默认弹窗密码 2=查看部分内容 
-			useVisitMode() {
-				if (this.useVisitPwd != false) {
-					return 1
-				}
-				if (this.useVisitMorePwd != false) {
-					return 2
-				}
-				return 0
-			}
 		},
 		watch: {
-			useVisitPwd(val) {
-				if (val) {
-					this.validVisitModal.show = true
-				}
-			},
-			useVisitMode(val) {
-				if (val != 2) {
-					this.showValidVisitMore = true
-				} else {
-					this.showValidVisitMore = false
-				}
-			}
+
 		},
 		onLoad(e) {
 			this.fnSetPageTitle('文章加载中...');
@@ -374,9 +351,36 @@
 					.getPostByName(this.queryParams.name)
 					.then(res => {
 						console.log('详情', res);
-
 						this.result = res;
-						this.metas = [];
+
+            const openid = uni.getStorageSync('openid');
+            if (openid == '' || openid == null) {
+              this.fnGetOpenid();
+            }
+            const visitFlag = uni.getStorageSync('visit_' + this.result?.metadata?.name);
+
+            if (!visitFlag) {
+              const annotationsMap = res?.metadata?.annotations;
+              if (('restrictReadEnable' in annotationsMap) && annotationsMap.restrictReadEnable === 'true') {
+                this.visitType = 1;
+                this.showValidVisitMorePop();
+              } else if ('unihalo_useVisitMorePwd' in annotationsMap) {
+                this.visitType = 2;
+                this.visitPwd = annotationsMap.unihalo_useVisitMorePwd;
+                this.showValidVisitMorePop();
+              } else if ('unihalo_useVisitPwd' in annotationsMap) {
+                this.visitType = 3;
+                this.visitPwd = annotationsMap.unihalo_useVisitPwd;
+                this.showValidVisitPop();
+              } else {
+                this.visitType = 0;
+                this.showValidVisitMore = false;
+              }
+              this.fnHideContent();
+            } else {
+              this.showValidVisitMore = false;
+            }
+
 						this.fnSetPageTitle('文章详情');
 						this.loading = 'success';
 						this.fnSetWxShareConfig({
@@ -787,33 +791,110 @@
 					url: originalURL
 				});
 			},
-			// 查看密码验证确认
-			fnOnValidVisitConfirm() {
-				if (this.useVisitMode == 1) {
-					if (this.validVisitModal.value === this.useVisitPwd) {
-						this.validVisitModal.show = false;
-						return;
-					}
-				} else if (this.useVisitMode == 2) {
-					if (this.validVisitModal.value === this.useVisitMorePwd) {
-						this.showValidVisitMore = true;
-						this.validVisitModal.show = false;
-						return;
-					}
-				}
-				uni.$tm.toast("验证密码不正确!")
-			},
-			fnOnValidVisitCancel() {
-				this.validVisitModal.show = false;
-				this.validVisitModal.useCancel = true;
-				this.validVisitModal.value = undefined;
-			},
-			// 点击解锁弹出密码输入框
-			fnOnShowValidVisitMore() {
-				this.validVisitModal.useCancel = true;
-				this.validVisitModal.value = undefined;
-				this.validVisitModal.show = true;
-			}
+      showValidVisitPop() {
+        this.showValidVisitMore = true;
+        this.validVisitModal.value = undefined;
+        this.validVisitModal.show = true;
+        this.validVisitModal.useCancel = false;
+      },
+      showValidVisitMorePop() {
+        this.showValidVisitMore = true;
+        this.validVisitModal.value = undefined;
+        this.validVisitModal.show = true;
+        this.validVisitModal.useCancel = true;
+      },
+      closeValidVisitPop() {
+        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'
+        });
+      },
+    //   获取openid
+      fnGetOpenid() {
+        uni.login({
+          provider: 'weixin',
+          success: function (loginRes) {
+            try {
+              // todo 因为没有获取openid,所以先使用code代替
+              uni.setStorageSync('openid', loginRes.code)
+            } catch (error) {
+              console.log(error)
+            }
+          }
+        })
+      },
+    //   隐藏内容
+      fnHideContent() {
+        switch (this.visitType) {
+          case 1:
+            const restrictReadRaw = this.result?.content?.raw.split('<!-- restrictRead start -->')[0];
+            this.result.content.raw = restrictReadRaw;
+            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;
+          default:
+            return;
+        }
+      },
+    //   校验密码
+      fnValidVisitPwd() {
+        switch (this.visitType) {
+          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({
+                  title: '密码错误',
+                  icon: 'none'
+                });
+              }
+            }).catch(err => {
+              console.log(err);
+            });
+            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;
+          default:
+            return;
+        }
+      },
 		}
 	};
 </script>