@@ -12,15 +12,14 @@ import { onMounted, watch, nextTick } from 'vue';
1212
1313import ElementPlus from 'element-plus'
1414import 'element-plus/dist/index.css'
15+ import VipBtn from './components/VipBtn.vue'
1516
1617/** @type {import('vitepress').Theme } */
1718export default {
1819 extends : DefaultTheme ,
19- enhanceApp ( { app, router, siteData} ) {
20- // 注册自定义全局组件
21- app . component ( 'WebLink' , WebLink ) . use ( ElementPlus ) ;
22- } ,
23- async enhanceApp ( ) {
20+ async enhanceApp ( { app } ) {
21+ app . use ( ElementPlus ) ;
22+ app . component ( 'VipBtn' , VipBtn ) ;
2423 if ( ! import . meta. env . SSR ) {
2524 const { loadOml2d } = await import ( 'oh-my-live2d' ) ;
2625 loadOml2d ( {
@@ -35,7 +34,7 @@ export default {
3534 // 添加 giscus 评论系统的 script 标签
3635 setup ( ) {
3736 // Get frontmatter and route
38- const { frontmatter } = useData ( ) ;
37+ const { frontmatter, theme } = useData ( ) ;
3938 const route = useRoute ( ) ;
4039 // giscus配置
4140 giscusTalk ( {
@@ -69,9 +68,98 @@ export default {
6968 ( ) => route . path ,
7069 ( ) => nextTick ( ( ) => initZoom ( ) )
7170 ) ;
72-
73-
74-
75-
71+ const gateId = 'member-gate-overlay' ;
72+ const removeGate = ( ) => {
73+ const ov = document . getElementById ( gateId ) ;
74+ if ( ov ) ov . remove ( ) ;
75+ const docEl = document . querySelector ( '.VPDoc' ) || document . querySelector ( '.main .content' ) || document . querySelector ( '.main' ) ;
76+ if ( docEl ) docEl . classList . remove ( 'member-lock-blur' ) ;
77+ } ;
78+ const sha256 = async ( s ) => {
79+ const buf = await crypto . subtle . digest ( 'SHA-256' , new TextEncoder ( ) . encode ( s ) ) ;
80+ return Array . from ( new Uint8Array ( buf ) ) . map ( ( b ) => b . toString ( 16 ) . padStart ( 2 , '0' ) ) . join ( '' ) ;
81+ } ;
82+ const applyGate = async ( ) => {
83+ const fm = frontmatter . value || { } ;
84+ const need = fm . memberOnly === true || fm . memberOnly === 'true' ;
85+ if ( ! need ) {
86+ removeGate ( ) ;
87+ return ;
88+ }
89+ const themeCfg = theme . value || { } ;
90+ const globalPass = themeCfg . memberPassword ?? ( typeof __VIP_PASSWORD__ !== 'undefined' ? __VIP_PASSWORD__ : undefined ) ;
91+ const globalHash = themeCfg . memberHash ?? ( typeof __VIP_HASH__ !== 'undefined' ? __VIP_HASH__ : undefined ) ;
92+ const globalActs = themeCfg . vipActivationHashes ?? ( typeof __VIP_ACTIVATION_HASHES__ !== 'undefined' ? __VIP_ACTIVATION_HASHES__ : undefined ) ;
93+ const actList = Array . isArray ( globalActs ) ? globalActs : ( typeof globalActs === 'string' ? [ globalActs ] : [ ] ) ;
94+ const key = 'vp_member_unlock:*' ;
95+ const unlocked = localStorage . getItem ( key ) === '1' ;
96+ if ( unlocked ) {
97+ removeGate ( ) ;
98+ return ;
99+ }
100+ const docEl = document . querySelector ( '.VPDoc' ) || document . querySelector ( '.main .content' ) || document . querySelector ( '.main' ) ;
101+ if ( docEl ) docEl . classList . add ( 'member-lock-blur' ) ;
102+ let overlay = document . getElementById ( gateId ) ;
103+ if ( ! overlay ) {
104+ overlay = document . createElement ( 'div' ) ;
105+ overlay . id = gateId ;
106+ overlay . className = 'member-gate-overlay' ;
107+ const title = fm . memberTitle || '会员内容已锁定' ;
108+ overlay . innerHTML = `
109+ <div class="member-gate-dialog">
110+ <h3>${ title } </h3>
111+ <div class="row">
112+ <input type="password" placeholder="输入密码" />
113+ <div class="member-gate-error"></div>
114+ <button type="button">确认</button>
115+ </div>
116+ </div>
117+ ` ;
118+ const input = overlay . querySelector ( 'input' ) ;
119+ const btn = overlay . querySelector ( 'button' ) ;
120+ const err = overlay . querySelector ( '.member-gate-error' ) ;
121+ btn . addEventListener ( 'click' , async ( ) => {
122+ const v = ( input ?. value || '' ) . trim ( ) ;
123+ let ok = false ;
124+ if ( globalPass !== undefined && globalPass !== null ) {
125+ const sp = String ( globalPass ) ;
126+ const isHex64 = / ^ [ 0 - 9 a - f A - F ] { 64 } $ / . test ( sp ) ;
127+ if ( isHex64 ) {
128+ const h = await sha256 ( v ) ;
129+ ok = h . toLowerCase ( ) === sp . toLowerCase ( ) ;
130+ } else {
131+ ok = v === sp ;
132+ }
133+ } else if ( globalHash ) {
134+ const h = await sha256 ( v ) ;
135+ ok = h . toLowerCase ( ) === String ( globalHash ) . toLowerCase ( ) ;
136+ } else if ( actList . length > 0 ) {
137+ const items = actList . map ( s => String ( s ) ) ;
138+ const allHex = items . every ( x => / ^ [ 0 - 9 a - f A - F ] { 64 } $ / . test ( x ) ) ;
139+ if ( allHex ) {
140+ const h = await sha256 ( v ) ;
141+ ok = items . map ( x => x . toLowerCase ( ) ) . includes ( h . toLowerCase ( ) ) ;
142+ } else {
143+ ok = items . includes ( v ) ;
144+ }
145+ }
146+ if ( ok ) {
147+ localStorage . setItem ( key , '1' ) ;
148+ removeGate ( ) ;
149+ } else if ( err ) {
150+ err . textContent = '密码错误' ;
151+ input ?. focus ( ) ;
152+ }
153+ } ) ;
154+ document . body . appendChild ( overlay ) ;
155+ }
156+ } ;
157+ onMounted ( ( ) => {
158+ applyGate ( ) ;
159+ } ) ;
160+ watch (
161+ ( ) => route . path ,
162+ ( ) => nextTick ( ( ) => applyGate ( ) )
163+ ) ;
76164 }
77- }
165+ }
0 commit comments