o{luWsDqtqb@ou6DnF^}>aU(1EU@SB|gRP%(d@2VtQ8e5rZh%*TJe)KU>?PeCLV
z{%Xb7fUJ8OJXH<8c@2AmE{~_GuC5*x^LjkNytq!8r;p7>Rs353{_>LaoOI19x!n6V
zUb9IC!!^6)PTjxhS^|X~Qbu;KIqRB}Zd_7w+WyVg5-FUdM!4xFS<1}mHD_N>!OaND
zluk;U%k4qChuaKQadT4QnsamMy6OC#cdV+3`XO~a$2WowF-nx23+dLr9Y6Mbr
z)!o%tN2m-mC0@=~?Wqa`6yMIqfY&clMjE@?TU+b#oHQs|^nyfaD;0WIg+w0%eseeO
z@a9d@H|By-3#$FRq~vNTVpxOdldh3%05Pml5@ik92$nDgtlMqOBqm}K*-U~aBE;+*
zV`275kgWj+*vBawi7JE6RgE>iz*Z)xMi-tVxPS2FUk^0x-yFj-L`lptg$Rt#GO~f#
zqyjSQq*0;Lf>y-~Vy;80q~o^SsAS?tw2+`o(L_0vHd|Mh7>b#LSU=_eOOrGz*)7Yi
z5$M>;Kiqg+Q6%7t+8Y(MkH`o#@nEt8;$w$VHtjIU<{f6)QekN@R2sHu(m>>6SSf9x
zip5>BRTJ>c%M{y~4DdgXyih1wY@oR<^zt!*Jo>7N9D4gd47~EQ#zacl`YRQAwfjSn
z?8TFR?rtp%o%?9uXYYpI`bA?JC+lV;%iz$(Ux(UW<8B!^^8VnlKWX_%d(E-OJ03r=qR+jw$G$Y$9a`g>Q7F^RV+f2-
zHjUK`
zg~kkpx_xRYRfrf%@Xw8`4bu}
zMk?JBCzbADxkIwZE|c_0t~EsD6x1zXZG?@=DR~p;fUbpOC(&t$rz|vzz9Xl#h7Hqq;Q%5N&-n41|>z?dbBxrH%>jJcIDw=t$j-paV8j9W%Hyiu{djj_uad%KD)
zk?&{h3dY`{VoMpfl5wjD7ns4AI~lW@F=dQd!y~aI6U_zd3}}?D3>
z&XP9*&X(^E%)#h5S2jU_R0flDdJI(QMjF5;(zEyF><$DP{7Wk;NF{p8>wDT)H}8oj
z=2h44i5*81PzW*6hypOaJQU+7ZV8vY2mzfDk4$HxCF_YAMZWd%
zz-z~^etLQEjT3`!{!|}{Nm1y%=b*F=y>>3t`g6>V96|T$dpB*lck|lK+dQkbY}v3T
zm==TBx^}g^c3Uuc)&1Mn-z#r;XsxU!bFEsvdhOP&o^AKuy>|1XqQO?I#BA9X@a^%|
zHwHGBJA%e~f6xkb4-;qQcBD~O5FkwrQ$2mnFl}L;^mw0c@F|#{U@~YSne=qfy1Lc}
zMh{x6`4_6u0xBf7IAtYpe(L`+x`ghCi4^A7m`Db*#xSsmVCH=uxu7@}N`)0y3O4r?
zZ2q|TT=_@kJw5CneITEH@cjqR{G@O0I*ORqUsO!L)30Xc
z9aV35%!N3b}NiaHJEth
zfsMy|Rte!F>>bUg)j^2H@$>3L4BD0iD4D6gOWJZ{<19Q=@Q|K}|9t6Hu-CvZjtpIX
zY2e7QP;*D9`P{(I&jh7%v$7Fcf@b0kJc}
zuH-AueEjt|^WQD~W%=9X@60}9zA}GZ@BDS27q9D2%es;_qc?3vk81|h=H&D%?h^d<
zxJx22SJGzpq|H8&+T)t7rYP!l7hQ4B>~YWRlzZ$mqvW7UNNr4#M#wxg8Cmj}Z>XTj
zx=D=N7H0CGL*sH^okA1GsVTOK-kPQsJj$9ZD-G8qf?d^N{ypm#_oB9uu?ZO>#JdwW
zP0|C&b@&z64HK!6=1tlq8|Kg?UA^>UDw`!ttY{6iyb^l(#L%hFV5QXeo^K9XaWk5O
z$U&jLWNH9{h1w4Y@gRVoULhjLlKoyNIy%)QSP&=B;uCOt6{3tL>FfMrNS!|w&G==`
z+d1##p4j(A(QVzUzbLw++5AWQ^s(JbrVo}n)KF7)FO1ft)Zss7nk$nEHx_H5boeJl
z(6lHfM}-AsVpR<%MoIrODDLt=a&7l=xC*37U
zj-;0AgNvGXADDj44UA~VhysG*ZTNRe5~L>^ojSbdH(=NP3o>U<<
zm}Enx={68bO|mKjBhnOQs#hga5d*>!q<^X!tI1c{$j162wVZKe<`%nFV)VC2sdaR
ziDc^zNwZ=gvKeJ56+7JJ1%h_ehf(DTLJsYq
z{2l^7BtX(6Xnp*tDyY3d6PfzW(9s&mAmwkY4fv_cSnrwxjabvY6z>y_UO$X>aiYqj
zMlc`#1vxR0rAn!phw~5Sx4Apb?N9b3l{K3%8o5$mUeTYC)x56XnbVp1xpQX!jPffp
zmh{e8(!8#1MX#&)YiCO9@|NYVtZ2Xgb7wKew8P5|E<1e3&+h14)SI^Q#FIT~E4woh
zYFYEOh`W+j@`tpN&egqX_noo!q}|v3Ai(d-aNE2lY&9p$?a#2sEJKJmHpraz@lYhQ9~X~)t|UtjM0GiB#yot@SFgWo*cxA1|!)F1S?
ze(<$BqdD;}-=>Jcw%1+qtz=A?`*nJLd*IkJ9nbWn&H00CPS^t6U;UsU>38Pj)!EYD
zWtXg8Vfwq<4S?U@Zp|uSxq;2~Q_I0u@N~Tr=pgOS(dFi!=&*oPd_k1au;3c8Lol&8
z!*Bd0!&I#?QiI_UlFEFIFQjA`w6)X|q_9kx!a8LNthP<@6f8PSks=`~fMmxlsu;z9
zX(u(RweRILp6&S|r8|wlUm=&qPomGBcwGYdG3W-K#
zD*+NY3dsuvLx7~D5Fm2F2+4^ij5jF?r6r-v0^p=kaP$*E#2D6-Pyr7=8k3}5^}+l&
zHjRyDVu9R6&wrhgL97Zkow=zmWplIjPwwgcso95T9h}wvKkjg>$_{AqDe`GK;w`K5j5|tudkh&V-6#&pZc3M2a6^BngnpN
zBuqbNOZiSzR6kx5u`uH-L|`(;!FX$^Fv#XxRJNh9g><4uT0XdX`P9H0=Lb(UUA_1U
zrtypCE?77}^x&OEAC3~lq0XF>
zMP^SV_ZWpUJ^{DQ3={?evAbFmTN2xL^-N#c@5*Y=37d?Y436eR5`|7ESjp*QyOVSn
z?#^`xOr~{edL{kp4UBQxq(Pk#L$ju0g{nvonYx=hk;-2Lz}*a)$P%$*O;=i1&B;Z_
zeVx<4$eTZvl>8m?Pu|(G8J%s>I=jIjTOkA0u1Ds`C0pl3lponPK^mJbjeUYNc3ql;
z3DP7))3CkOp_6X|M-GHu?h19B2pu~XYC9lYFqr!Onp*MH(^VBJP1Mxysq)nYtyR_J
zXTefP`3YVpn5d5YY6ACD{#b;ov4K1-)aaxd=sD4Q@G46-(rOfvL^0CLLE=iym@|$f
z6H%R%tpOlLiY@DRrMG!Or0Wfiw)_#vnCfh{hV4>fUVG)ShdLfQ;qFfD%U|B_g3LR%
zuVY`&tYzJDUjb-)TKhT@SDimOqx9u1?RVw1?>hEa$74O^w{EPw%Qep7TZa?68wiE==F9B_bpQ5!Fw-
zBI5ezz`&ym8cl_v!BT0^j7v30ohLnzA=Ys#jdA!HGOyuQn&R*`%j{ZHX^z9M6>c_|
zTxlblRulZnn4nZzUssMph{J$(T#G&**U%Hk-e4X7`saXe;P-
z6@BHL)?apWPfBsGtK=(ZR)0xF+w@*n;aAR_{uzsUQi@2$j}9gayK9;gKew;`(w#PT
z{2*RpewvTKRQ#mpjEL}#($v9QlQ
zug5+w+MF7jm{An>_!0!ZOOLa}H0sQf3Mnr_V40#cpxl_O`aSYSEt6IV7YX33`RvwD
z62cTbU&9#j0(!-e3@>;A*9g79RJwu;2~+`>Je?#Et5e+PY-b$P=L(51(Zj72;@8yi
zy{i4O>P8jQ)=g50hhsZ07~j8RlW;?j)Q`~sZ|45(cjdNk4_i!k3N_FKHPAk;8mRJ!
zG_o88P1dQxwoJnm4MuUwpU&d!gd^gURn>bvPr@vsj!En>L!=|dBqF4#8U7DQtwQ@{0H(iuk~09`WKQc?+-2(8+G#(!;)_h^
zMHFL|vJ1ORhkhKk(VbnpOQ1W4l${%PQb;I42#K~IR|(pO2a*y5|3f1Qpa!@w!=h_|
zy*gg;DVsp?St1ml2fW`)(fKD-z#*tyPxtyIczvfVEflH5TfDTem|TjTFCrlYtgL_{Jwgr;l=c3|%ZO3>|zK&XMFVaH-WHxbt1Sv+_#<
z#|gX#5KP2m2yQGI7n2^RGb%+H9tq+YUMKKvLeb;$BSniRwb7GK0{CqJLfKQby)=|L
zr4Se=4O46-N`wAaEG15BUqCaadoTS7iA%cCjBs1r2K
z#3td!6{DqQact}x*q$wv119f
z9{rDe@7aO!A+k?R690l!(P@FnGKZ*OPDf4G;-imoFj_aHAG=sU&@+D
zH@43?VeE6y?y=8~HqPHb$>-OZg_^)5$P+yxh#OS$&8SaQ$#-Dz#9B#uSK#ED9L5R-Wyh#cBXe`mh>+A3+a9XxjB1CViDy3#i0AHEJXVO<3_FHnG{IxpTPVU6q?z
zADAA`%{AUy_})dj^Aakdd`dvqr|JYC5*O=v;ub|jJ9m1V@*fw^Z9_~JGYP~bKdtS4
znhEa0=#QB`b@9ZLKnL=(v|f+EWSYO21d1+Zj}w{<#;gjk4zIUW2Au)#o`zc5>IJu9
zS0hwFni~3HsSYOaU5(dY9kfFY^w3&-%$$)Hi-bk<#wcYPQOYW0VoJ#&u>tT+Mt0aj
zH&!WYTG&P*yOf?4PN0xOO3Uq7bZBnaNq0g;M@SITxGK8Pu;4NR%TXn4M{wFO!CpDm
z&M54X|et*t|s&v2a9_HcP^ISCb9DD&jKfEBO758k`l{gFfY
zJL-%7NZ{`Y{3im;1x_|}K_lMd3+M^mDwN;x^0guJ7$c`s6yah2i)nFWuZ!bOUXBtp
zH5alBYOYxMvYAQO9d$^q^!|dGZPR*Pc@cHCeb1QyDYMJ}M#`-61}ZhR%5pOiX~dFg
zvdktuWp+nthyQ3f2cx4h%Hzun2nKbH{F|#Rr6^flO+WXMp6;Ny40nApeDrO-x|+yc
z%{B_`K@BD}&`wmEn@^y&*ds~fol>+2Zj2ICB5Js3?5yugSrXM&W_K!m?pZzdSB8aj76^x02UEvcC3#$Vy9tMNW9V6np?^WIO@%Rf?H=&jENTVBHL(B-#6&A%3#
z%N@!})Pen__PHY@`2>4zVWs_th=kWVr-?XbDZ434Ek&7yVB#MCAz>Q;{6tHz!)Yrn
zM4+O?;CSnsB8t0x12RGsrAui=ouyZb7xflnDP?i9^(&{lKfkDLNz3N1-D#~qYWY#?
zvn|hdmi}^X*WAv6Gwz;(mA}tyezwoOrP5_}ka#0=D>j^7?0xf6GJy)`?lsvl>!7R~NPgrw8@c_1e<
z@YJldiCAe3k}@kzBVT!dZ!k))=}JGk@r$&XVGCg~nDa)Su#N7bHaEI+NRGs7PJ~2r
zQrBu%WPb1_FiqtBX&%E@>8ht^yv40l<+u2y#>MK=Vw)3-nnN$X5IS`}jw40+p#z7YYbbw2)s!m)`U$WakRsN*8h1hs@bETTVf9`@DP9K9
zmObM#w^2M>!l`S>5T@~<%dabJ%%oQz$uzSC*;yq>PNhZ9^!~!~w%lG>2GS<^7~Tmjt)8Ho%i*@Bmdvl8DHU+AYC0yz)K<|&ZlMwA2CG4>ZQ5IoHWrq
z*m2gWbBxiC0+D&o32v5&tzPk7Pz^@C8)2n!ym15@xFQFfOpwB=c>+%mZ`0ys`zHuDh)%F8$7mZz(Nv6?&pQ#?FF~&VBJ^^>yLnuC~*MQP)?{{UiJ%w5KM%{wU
zw^)45?N86`Ps=&H^x)F=-LTH2&5qplrOoWm%0ImC;J(f|eOVRXPU7Q2{SRG28Yu*r
zOdX1>!NYm=#)z)Z>OJzIxXaSte<)7%Fg#>&>sP8wpy)he`p6bS>IUMMtS>&)h
zub-NUXG*d;>RmoT3X4W{6U0ZT7E`k*RVIinCQOwBN1Cw1D26tx@%(+c5!$4=u3se=
zJ&DHBq4GbFOZkex*94vdi1Dz>B9hXVrIB7)+|+1-blhXxP->I{Vb5L7hQL3$r}d{#
zJG}AW#`e7@iu%&$M5yiGk@ldeJ%j2^EdZa5p*<6(>6kF3cZr{|v`78M8a0X98=*Z|
zdDvXlND90-UPJURooRErHT3Su;f}Ti10Qse^Cx$x5@|G2|7)h&=1?R9MC@t{PC)KyikA`!g4a4B-hW}7G?7+_
zA^V#@w=WsDH)xQf|G)Y2QXBP7#8zXm%p-S`d0pyyBZ6alD8dTlJ208}`QBb%G4
z#wOu*BV{UP@FCx`p3K?tp1%?2vw0_q``mMT>~o{7iuUJA73$w(_d(E&)86)auuH2N
zTd!bv;3=x|8H$}+3?8Qb*XiDne^_-oxmv72t@)d+L9ImXa0J4O!|lHwIQ5B;jr!?d
z+$&*bxBpD&!g~X!-?;kOD}@W@E|@=GU2jSjdI_39Wk==zplqG+L?bL9454EenfM_Z
z6UQIPp^xNNDntIbMW*@W!u>3|`}Qd|Be;;4`MCyx$rM-J0tC!eWXm8s1^)S@lS!PB
z$h8RGe1Mh;Uee*UDK;SmEl>Gsu6Ch})uEkD02c7e#g?)a{?!8rn5!MZ>aAV^s
z9BDAZUP$#?E{{5+qUx8)V(r+z1KYAt|I?5oP>iVA#YMS-HT1m!-lC=c4%4
zi*>5^^ti^z@yr*PU=okUT`(&X-VViC8fKL~W_qT*71hN_53ZBO6qCOY2FC*y;Kh@WP-vX?BeD
zA8aMf>@S+#me=c=u3BHOxC?sS1!QUcam$aJ>)Sm&uDSi`#hq)el-}H1dUH?OlKzy8
zuuV!X9Fcet2@X@q=@Xh|X+t23tbGVfEwV^NjWN5bB8=Be$)i#eWRrPSk_a=&HWp;;
zkgP7U`!TwCX#+n7xM<%-o1g7Ys^nthY*c)G2g&tc(4GFBcw?4aGa+{*fN!va-$FOo
z%b#`7e_&78MtATyP75bc2+}Jj?4%F`R-7D5Bs7lc%n7?GluU9g1tB5FY^Ge--IvR_pCB_Owuei8A<
z8oK%rkNT8xy}skpFgN}PTxap
zS4c*cO{I~AsM(Z~Q9aLul{rbj;AH2;Wjg!7!J&XJQ0oo4FcLzQ-%~|r(FskS8enRj
zYXn{8r-LwAa%Eb|=-9ZjZ&EYp$ha~{E;xzcxVSP%t*l%+Dy~djMM6l3BE5R!KtT|j
zt`TA4o!q)kUPV{~f}4nL6$s~aj3;Jj1Da;#WX0zkp5z#O;}{(o8#%7~*oVWfe}+TH
zVwDAker#JHsRp@&vBQ)#?nwO}~{H%j~%%wtw<$$Ji~7c?shI(B*=_3|2@u5?fhYz0``ZdmYx~!-9Xxm^EywrpP
z1CbafffYW8YtyDR;vhh+)u3=+e0|{PvG_ECPG?u}QS^;Fz2t=bl(z~e)?+unzlzSg
ztigtpy-)i5KAh)`;uP{hQgZc_tNOs8+juq|O~CX@vV&GO1-WLBO3OuhxJ(Zh8Z3=7
zR*svByO4+r#G9<1;?nO*?RVYQpHXl>{QsW6YIuo&r8D#XsNMC|N>Q3XHe7xB+;7c+7a3KZ6*sU@%8
zpOYvM6lQJo;^``dVdKd_^SQw{E@I2c--@m;#M&!&=j%3M{)Dm+dogL@0cxVDH
z2Jd3L+thMvE>Vom3^$VuZ!_sEI{chuS8(;*JS2=2>z2o4Ia
zc%l&>O(5~4%#6q$3nPVxh3Z?(kt}zplOkd#k%$Jx;JXGWCg!^fIFD*xOf0uJEt-Q^
zl2kE6W8`WcBVp3pqFceHgAB#UII|O5LdtsVs>*~)j{S%RG45nEgV8Y7rFe|59-qj=
z6Iz|G-VH7$-bSQE3wP8vQL&A&!?@W>`9IvA$j!Vqz>(x&bMuRZy%V}WK+zs
z!Kf{rdI?FTFAX)IYuxi^NCpqTL?2wyQxz5<;r=GpBnOT()0*Ubby4z3WZ0J;A(v__
zkSU^<#py-7R0cLvkWRS+0AU`cs3l{JzD`}5AgV)cUGMs$?v|}6WH6}#nE>NJ&CXMSQ)ib8*!p%`+A?A
z;A~$JaTnE@PAx;7v2^WdalGbYE`sCNMxezptVDgnh!{CgxZJ!MSGmDgi1sQsQjm4u
za?|F`qC+p8AAJ8XT;Z@cRJ;UY@M)ppQ+Shgo7a_FHY>NQLBqQ8c@uvfVK+VfUIMof
zSV`bv0?ZjtQ>cjm9lxa*VLE`|K0m1P7s}
zklgmagv9&_hcC^=U%O{Dtsgbx#PjQPSUQXa)}&D*jv!XQur*Tkh#b^@t;qacE(RA$8zfRlr
zN0YGY`8w^N9=+2*yEN$#*}L!@?A4>-oKXjs)p${DG&`Cd`!KH429D7saFC~Tvqv59
zrM^xM)uUx_@4QZ~p`*7(s{@~;FgYfTro%&oa-@$YMM8L*igRG)mMS}MG%51jJgl8u
zr*#;-Gt7_I$*?&p8!-K+HY*!ljJXj{kVdCNO=Tr@v>56K>mb}X^s4FClZ?o0H{i)u
zgC!#@0WcSE&s3vlA{SX;=^E(=sFJD-(eGK|J60xq&kBP{zzmm(WwL1}HYdpzK&xy6
z#3wHR9kLV9B_{$V$!@@8IR!9PP6JGrGXOK?EWm6z2XLC43z#S815TF<01M?Jz+$-s
zuvDHQm&r5bo8)qNmONXYBhQuR$rbW^d4ar8UL-G;Zrm6rx^W>x?e(Pf4}0%Y=XWe;A@cQ>R%BiX*7Y7D>8F?9Mgvo^MJ4_-Vr
zcz%EAa))mf_>3I@^+)f*910_Y?@o$KLw+ng;wuCsvaWU?82Ip=!LC!jO%zEMd7i9A
z@!JXS0G?Je)Pg>ZJfxvPoC+UJKdTezQ;>e}(uL5OgIrpu^*Ic?zBv>{Jr;=)nbqi&
zcM(mag}&1CAVMr?`RXAt8s{RTy4UBO
zrh!jhxq1;h6T9$G*_#^
zz(;6|n9BO{v^c$+Ug*+uq3&+q`pC0eK0~G8_9C`W`Li0%uIX|wC%wsJ76z_
z>O=(3Q+tFnDNIgNtS}Zgi9sg^h+u|xo^8yRI%-KFh^X2o%RRhZ{~kjJc5c$$$wlPk
zbdS1aAL>)&B00h8)H4XsTtR<|_ydJ?^tyaKV%9=iO_kCQq0Y)Upc$*qw2g+0RdLrA
z(of>92U0bID-MQ^eBA;XC7)^&G(5b|xV>;Ql$e!xtg;FqXz@3~B+80J(79HY@0C5P
z?_INYtMDaOO+eA)(D?YnZ}==6)fa{!wed(338v^8PkbeT8;%r|zd)5r&we$bKWqA{
zu0PomTWr1d?Dhri+uGN47I$vDGUMjn88=^@sIYqh2CY1
zR+&9b%N-MIM(gP1s6J5zBl2}X@v%CPz+IFmLINvQy@TXC3we_9eo|8YnLb(fr8zBZ
z&P%iQ+a1lD!zP5jw&NRNC1DGKm@+2dvtgxS8{OFXrXZX^HxAr1KNEIR*u@bg;Y7N@
z{$*q-4!h|lnQzL%DRh&nCQGB6biOGKXV6Wi8k0pg*?hAioI^L$)R1I0L
zEDRUWO`#f7L^s8JQywm%n^L|h?5CGn$go5Q1%8e$b}om@8P2(O3yNsEdvrN5z$ml9
zNR%14HDj0>ElV6Bjg$hAx*z`mO4;xTV#F13?}m#l>4VUxiFsL9ERM3RLcB}N?5Dwr
zTv3b$Ea(Cp*}*?z4ve_SrB=f{Lgh6{5KZc(%``CJ8`z)o>c!UvUppOo`}t67`|wMx
zq2{+j7cYR)L#KWL7bWFxq9l@zLN_7_h;Itvdu!MyOeP9irNlJpNrks|P#wk0;;J*J
z&zaj}78|AL(~WfYrN_f#FBPwSHb#76uY8(PL=Ra{poYLU0`~v}t8wjtpk%*9`t-9Oy~Rx&zAjW$CL&h$0pK-o7s
z!+e|05y^&SVF^HuKxnk=KD!~~nndv1-N~hfdkoEczm@3se{V5>GEk)1)x7U>Q_eSz
z{60rP)9NqH2|bSKe>4~1lXm85e+h3$lk92V;-hd0Uz+Vy<%GZbNu~NA@lKcYJD2Uw
LOw;c&41oU+sqm2s
literal 0
HcmV?d00001
diff --git a/开发文档/小程序管理/scripts/apps_config.json b/开发文档/小程序管理/scripts/apps_config.json
new file mode 100644
index 00000000..eeb0a7d5
--- /dev/null
+++ b/开发文档/小程序管理/scripts/apps_config.json
@@ -0,0 +1,40 @@
+{
+ "_comment": "小程序配置文件 - 支持管理多个小程序",
+ "apps": [
+ {
+ "id": "soul-party",
+ "name": "Soul派对",
+ "appid": "wxb8bbb2b10dec74aa",
+ "project_path": "/Users/karuo/Documents/开发/3、自营项目/一场soul的创业实验/miniprogram",
+ "private_key_path": "",
+ "api_domain": "https://soul.quwanzhi.com",
+ "description": "一场SOUL的创业实验场",
+ "certification": {
+ "status": "pending",
+ "enterprise_name": "泉州市卡若网络技术有限公司",
+ "license_number": "",
+ "legal_persona_name": "",
+ "legal_persona_wechat": "",
+ "component_phone": "15880802661"
+ }
+ }
+ ],
+ "certification_materials": {
+ "_comment": "企业认证通用材料(所有小程序共用)",
+ "enterprise_name": "泉州市卡若网络技术有限公司",
+ "license_number": "",
+ "license_media_id": "",
+ "legal_persona_name": "",
+ "legal_persona_wechat": "",
+ "legal_persona_idcard": "",
+ "component_phone": "15880802661",
+ "contact_email": "zhiqun@qq.com"
+ },
+ "third_party_platform": {
+ "_comment": "第三方平台配置(用于代认证)",
+ "component_appid": "",
+ "component_appsecret": "",
+ "component_verify_ticket": "",
+ "authorized": false
+ }
+}
diff --git a/开发文档/小程序管理/scripts/env_template.txt b/开发文档/小程序管理/scripts/env_template.txt
new file mode 100644
index 00000000..9f7686d6
--- /dev/null
+++ b/开发文档/小程序管理/scripts/env_template.txt
@@ -0,0 +1,30 @@
+# 微信小程序管理 - 环境变量配置
+# 复制此文件为 .env 并填入实际值
+
+# ==================== 方式一:直接使用 access_token ====================
+# 如果你已经有 access_token,直接填入即可(适合快速测试)
+ACCESS_TOKEN=你的access_token
+
+# ==================== 方式二:使用第三方平台凭证 ====================
+# 如果你有第三方平台资质,填入以下信息可自动刷新token
+
+# 第三方平台 AppID
+COMPONENT_APPID=你的第三方平台AppID
+
+# 第三方平台密钥
+COMPONENT_APPSECRET=你的第三方平台密钥
+
+# 授权小程序 AppID(要管理的小程序)
+AUTHORIZER_APPID=wxb8bbb2b10dec74aa
+
+# 授权刷新令牌(从授权回调中获取)
+AUTHORIZER_REFRESH_TOKEN=授权时获取的refresh_token
+
+# ==================== 小程序项目路径 ====================
+# 用于 CLI 工具操作
+MINIPROGRAM_PATH=/Users/karuo/Documents/开发/3、自营项目/一场soul的创业实验/miniprogram
+
+# ==================== 可选配置 ====================
+# 隐私协议联系方式(用于快速配置)
+CONTACT_EMAIL=zhiqun@qq.com
+CONTACT_PHONE=15880802661
diff --git a/开发文档/小程序管理/scripts/mp_api.py b/开发文档/小程序管理/scripts/mp_api.py
new file mode 100644
index 00000000..61fae3ae
--- /dev/null
+++ b/开发文档/小程序管理/scripts/mp_api.py
@@ -0,0 +1,635 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+微信小程序管理API封装
+支持:注册、配置、代码管理、审核、发布、数据分析
+"""
+
+import os
+import json
+import time
+import httpx
+from typing import Optional, Dict, Any, List
+from dataclasses import dataclass
+from pathlib import Path
+
+# 尝试加载dotenv(可选依赖)
+try:
+ from dotenv import load_dotenv
+ load_dotenv()
+except ImportError:
+ pass # dotenv不是必需的
+
+
+@dataclass
+class MiniProgramInfo:
+ """小程序基础信息"""
+ appid: str
+ nickname: str
+ head_image_url: str
+ signature: str
+ principal_name: str
+ realname_status: int # 1=已认证
+
+
+@dataclass
+class AuditStatus:
+ """审核状态"""
+ auditid: int
+ status: int # 0=成功,1=被拒,2=审核中,3=已撤回,4=延后
+ reason: Optional[str] = None
+ screenshot: Optional[str] = None
+
+ @property
+ def status_text(self) -> str:
+ status_map = {
+ 0: "✅ 审核成功",
+ 1: "❌ 审核被拒",
+ 2: "⏳ 审核中",
+ 3: "↩️ 已撤回",
+ 4: "⏸️ 审核延后"
+ }
+ return status_map.get(self.status, "未知状态")
+
+
+class MiniProgramAPI:
+ """微信小程序管理API"""
+
+ BASE_URL = "https://api.weixin.qq.com"
+
+ def __init__(
+ self,
+ component_appid: Optional[str] = None,
+ component_appsecret: Optional[str] = None,
+ authorizer_appid: Optional[str] = None,
+ access_token: Optional[str] = None
+ ):
+ """
+ 初始化API
+
+ Args:
+ component_appid: 第三方平台AppID
+ component_appsecret: 第三方平台密钥
+ authorizer_appid: 授权小程序AppID
+ access_token: 直接使用的access_token(如已获取)
+ """
+ self.component_appid = component_appid or os.getenv("COMPONENT_APPID")
+ self.component_appsecret = component_appsecret or os.getenv("COMPONENT_APPSECRET")
+ self.authorizer_appid = authorizer_appid or os.getenv("AUTHORIZER_APPID")
+ self._access_token = access_token or os.getenv("ACCESS_TOKEN")
+ self._token_expires_at = 0
+
+ self.client = httpx.Client(timeout=30.0)
+
+ @property
+ def access_token(self) -> str:
+ """获取access_token,如果过期则刷新"""
+ if self._access_token and time.time() < self._token_expires_at:
+ return self._access_token
+
+ # 如果没有配置刷新token的信息,直接返回现有token
+ if not self.component_appid:
+ return self._access_token or ""
+
+ # TODO: 实现token刷新逻辑
+ return self._access_token or ""
+
+ def set_access_token(self, token: str, expires_in: int = 7200):
+ """手动设置access_token"""
+ self._access_token = token
+ self._token_expires_at = time.time() + expires_in - 300 # 提前5分钟过期
+
+ def _request(
+ self,
+ method: str,
+ path: str,
+ params: Optional[Dict] = None,
+ json_data: Optional[Dict] = None,
+ **kwargs
+ ) -> Dict[str, Any]:
+ """发起API请求"""
+ url = f"{self.BASE_URL}{path}"
+
+ # 添加access_token
+ if params is None:
+ params = {}
+ if "access_token" not in params:
+ params["access_token"] = self.access_token
+
+ if method.upper() == "GET":
+ resp = self.client.get(url, params=params, **kwargs)
+ else:
+ resp = self.client.post(url, params=params, json=json_data, **kwargs)
+
+ # 解析响应
+ try:
+ result = resp.json()
+ except json.JSONDecodeError:
+ # 可能是二进制数据(如图片)
+ return {"_binary": resp.content}
+
+ # 检查错误
+ if result.get("errcode", 0) != 0:
+ raise APIError(result.get("errcode"), result.get("errmsg", "Unknown error"))
+
+ return result
+
+ # ==================== 基础信息 ====================
+
+ def get_basic_info(self) -> MiniProgramInfo:
+ """获取小程序基础信息"""
+ result = self._request("POST", "/cgi-bin/account/getaccountbasicinfo")
+ return MiniProgramInfo(
+ appid=result.get("appid", ""),
+ nickname=result.get("nickname", ""),
+ head_image_url=result.get("head_image_url", ""),
+ signature=result.get("signature", ""),
+ principal_name=result.get("principal_name", ""),
+ realname_status=result.get("realname_status", 0)
+ )
+
+ def modify_signature(self, signature: str) -> bool:
+ """修改简介(4-120字)"""
+ self._request("POST", "/cgi-bin/account/modifysignature", json_data={
+ "signature": signature
+ })
+ return True
+
+ # ==================== 域名配置 ====================
+
+ def get_domain(self) -> Dict[str, List[str]]:
+ """获取服务器域名配置"""
+ result = self._request("POST", "/wxa/modify_domain", json_data={
+ "action": "get"
+ })
+ return {
+ "requestdomain": result.get("requestdomain", []),
+ "wsrequestdomain": result.get("wsrequestdomain", []),
+ "uploaddomain": result.get("uploaddomain", []),
+ "downloaddomain": result.get("downloaddomain", [])
+ }
+
+ def set_domain(
+ self,
+ requestdomain: Optional[List[str]] = None,
+ wsrequestdomain: Optional[List[str]] = None,
+ uploaddomain: Optional[List[str]] = None,
+ downloaddomain: Optional[List[str]] = None
+ ) -> bool:
+ """设置服务器域名"""
+ data = {"action": "set"}
+ if requestdomain:
+ data["requestdomain"] = requestdomain
+ if wsrequestdomain:
+ data["wsrequestdomain"] = wsrequestdomain
+ if uploaddomain:
+ data["uploaddomain"] = uploaddomain
+ if downloaddomain:
+ data["downloaddomain"] = downloaddomain
+
+ self._request("POST", "/wxa/modify_domain", json_data=data)
+ return True
+
+ def get_webview_domain(self) -> List[str]:
+ """获取业务域名"""
+ result = self._request("POST", "/wxa/setwebviewdomain", json_data={
+ "action": "get"
+ })
+ return result.get("webviewdomain", [])
+
+ def set_webview_domain(self, webviewdomain: List[str]) -> bool:
+ """设置业务域名"""
+ self._request("POST", "/wxa/setwebviewdomain", json_data={
+ "action": "set",
+ "webviewdomain": webviewdomain
+ })
+ return True
+
+ # ==================== 隐私协议 ====================
+
+ def get_privacy_setting(self, privacy_ver: int = 2) -> Dict[str, Any]:
+ """获取隐私协议设置"""
+ result = self._request("POST", "/cgi-bin/component/getprivacysetting", json_data={
+ "privacy_ver": privacy_ver
+ })
+ return result
+
+ def set_privacy_setting(
+ self,
+ setting_list: List[Dict[str, str]],
+ contact_email: Optional[str] = None,
+ contact_phone: Optional[str] = None,
+ notice_method: str = "弹窗提示"
+ ) -> bool:
+ """
+ 设置隐私协议
+
+ Args:
+ setting_list: 隐私配置列表,如 [{"privacy_key": "UserInfo", "privacy_text": "用于展示头像"}]
+ contact_email: 联系邮箱
+ contact_phone: 联系电话
+ notice_method: 告知方式
+ """
+ data = {
+ "privacy_ver": 2,
+ "setting_list": setting_list
+ }
+
+ owner_setting = {"notice_method": notice_method}
+ if contact_email:
+ owner_setting["contact_email"] = contact_email
+ if contact_phone:
+ owner_setting["contact_phone"] = contact_phone
+ data["owner_setting"] = owner_setting
+
+ self._request("POST", "/cgi-bin/component/setprivacysetting", json_data=data)
+ return True
+
+ # ==================== 类目管理 ====================
+
+ def get_all_categories(self) -> List[Dict]:
+ """获取可选类目列表"""
+ result = self._request("GET", "/cgi-bin/wxopen/getallcategories")
+ return result.get("categories_list", {}).get("categories", [])
+
+ def get_category(self) -> List[Dict]:
+ """获取已设置的类目"""
+ result = self._request("GET", "/cgi-bin/wxopen/getcategory")
+ return result.get("categories", [])
+
+ def add_category(self, categories: List[Dict]) -> bool:
+ """
+ 添加类目
+
+ Args:
+ categories: 类目列表,如 [{"first": 1, "second": 2}]
+ """
+ self._request("POST", "/cgi-bin/wxopen/addcategory", json_data={
+ "categories": categories
+ })
+ return True
+
+ def delete_category(self, first: int, second: int) -> bool:
+ """删除类目"""
+ self._request("POST", "/cgi-bin/wxopen/deletecategory", json_data={
+ "first": first,
+ "second": second
+ })
+ return True
+
+ # ==================== 代码管理 ====================
+
+ def commit_code(
+ self,
+ template_id: int,
+ user_version: str,
+ user_desc: str,
+ ext_json: Optional[str] = None
+ ) -> bool:
+ """
+ 上传代码
+
+ Args:
+ template_id: 代码模板ID
+ user_version: 版本号
+ user_desc: 版本描述
+ ext_json: 扩展配置JSON字符串
+ """
+ data = {
+ "template_id": template_id,
+ "user_version": user_version,
+ "user_desc": user_desc
+ }
+ if ext_json:
+ data["ext_json"] = ext_json
+
+ self._request("POST", "/wxa/commit", json_data=data)
+ return True
+
+ def get_page(self) -> List[str]:
+ """获取已上传代码的页面列表"""
+ result = self._request("GET", "/wxa/get_page")
+ return result.get("page_list", [])
+
+ def get_qrcode(self, path: Optional[str] = None) -> bytes:
+ """
+ 获取体验版二维码
+
+ Args:
+ path: 页面路径,如 "pages/index/index"
+
+ Returns:
+ 二维码图片二进制数据
+ """
+ params = {"access_token": self.access_token}
+ if path:
+ params["path"] = path
+
+ resp = self.client.get(f"{self.BASE_URL}/wxa/get_qrcode", params=params)
+ return resp.content
+
+ # ==================== 审核管理 ====================
+
+ def submit_audit(
+ self,
+ item_list: Optional[List[Dict]] = None,
+ version_desc: Optional[str] = None,
+ feedback_info: Optional[str] = None
+ ) -> int:
+ """
+ 提交审核
+
+ Args:
+ item_list: 页面审核信息列表
+ version_desc: 版本说明
+ feedback_info: 反馈内容
+
+ Returns:
+ 审核单ID
+ """
+ data = {}
+ if item_list:
+ data["item_list"] = item_list
+ if version_desc:
+ data["version_desc"] = version_desc
+ if feedback_info:
+ data["feedback_info"] = feedback_info
+
+ result = self._request("POST", "/wxa/submit_audit", json_data=data)
+ return result.get("auditid", 0)
+
+ def get_audit_status(self, auditid: int) -> AuditStatus:
+ """查询审核状态"""
+ result = self._request("POST", "/wxa/get_auditstatus", json_data={
+ "auditid": auditid
+ })
+ return AuditStatus(
+ auditid=auditid,
+ status=result.get("status", -1),
+ reason=result.get("reason"),
+ screenshot=result.get("screenshot")
+ )
+
+ def get_latest_audit_status(self) -> AuditStatus:
+ """查询最新审核状态"""
+ result = self._request("GET", "/wxa/get_latest_auditstatus")
+ return AuditStatus(
+ auditid=result.get("auditid", 0),
+ status=result.get("status", -1),
+ reason=result.get("reason"),
+ screenshot=result.get("screenshot")
+ )
+
+ def undo_code_audit(self) -> bool:
+ """撤回审核(每天限1次)"""
+ self._request("GET", "/wxa/undocodeaudit")
+ return True
+
+ # ==================== 发布管理 ====================
+
+ def release(self) -> bool:
+ """发布已审核通过的版本"""
+ self._request("POST", "/wxa/release", json_data={})
+ return True
+
+ def revert_code_release(self) -> bool:
+ """版本回退(只能回退到上一版本)"""
+ self._request("GET", "/wxa/revertcoderelease")
+ return True
+
+ def get_revert_history(self) -> List[Dict]:
+ """获取可回退版本历史"""
+ result = self._request("GET", "/wxa/revertcoderelease", params={
+ "action": "get_history_version"
+ })
+ return result.get("version_list", [])
+
+ def gray_release(self, gray_percentage: int) -> bool:
+ """
+ 分阶段发布
+
+ Args:
+ gray_percentage: 灰度比例 1-100
+ """
+ self._request("POST", "/wxa/grayrelease", json_data={
+ "gray_percentage": gray_percentage
+ })
+ return True
+
+ # ==================== 小程序码 ====================
+
+ def get_wxacode(
+ self,
+ path: str,
+ width: int = 430,
+ auto_color: bool = False,
+ line_color: Optional[Dict[str, int]] = None,
+ is_hyaline: bool = False
+ ) -> bytes:
+ """
+ 获取小程序码(有限制,每个path最多10万个)
+
+ Args:
+ path: 页面路径,如 "pages/index/index?id=123"
+ width: 宽度 280-1280
+ auto_color: 自动配置线条颜色
+ line_color: 线条颜色 {"r": 0, "g": 0, "b": 0}
+ is_hyaline: 是否透明背景
+
+ Returns:
+ 二维码图片二进制数据
+ """
+ data = {
+ "path": path,
+ "width": width,
+ "auto_color": auto_color,
+ "is_hyaline": is_hyaline
+ }
+ if line_color:
+ data["line_color"] = line_color
+
+ resp = self.client.post(
+ f"{self.BASE_URL}/wxa/getwxacode",
+ params={"access_token": self.access_token},
+ json=data
+ )
+ return resp.content
+
+ def get_wxacode_unlimit(
+ self,
+ scene: str,
+ page: Optional[str] = None,
+ width: int = 430,
+ auto_color: bool = False,
+ line_color: Optional[Dict[str, int]] = None,
+ is_hyaline: bool = False
+ ) -> bytes:
+ """
+ 获取无限小程序码(推荐)
+
+ Args:
+ scene: 场景值,最长32字符,如 "user_id=123&from=share"
+ page: 页面路径,必须是已发布的页面
+ width: 宽度 280-1280
+ auto_color: 自动配置线条颜色
+ line_color: 线条颜色 {"r": 0, "g": 0, "b": 0}
+ is_hyaline: 是否透明背景
+
+ Returns:
+ 二维码图片二进制数据
+ """
+ data = {
+ "scene": scene,
+ "width": width,
+ "auto_color": auto_color,
+ "is_hyaline": is_hyaline
+ }
+ if page:
+ data["page"] = page
+ if line_color:
+ data["line_color"] = line_color
+
+ resp = self.client.post(
+ f"{self.BASE_URL}/wxa/getwxacodeunlimit",
+ params={"access_token": self.access_token},
+ json=data
+ )
+ return resp.content
+
+ def gen_short_link(
+ self,
+ page_url: str,
+ page_title: str,
+ is_permanent: bool = False
+ ) -> str:
+ """
+ 生成小程序短链接
+
+ Args:
+ page_url: 页面路径,如 "pages/index/index?id=123"
+ page_title: 页面标题
+ is_permanent: 是否永久有效
+
+ Returns:
+ 短链接
+ """
+ result = self._request("POST", "/wxa/genwxashortlink", json_data={
+ "page_url": page_url,
+ "page_title": page_title,
+ "is_permanent": is_permanent
+ })
+ return result.get("link", "")
+
+ # ==================== 数据分析 ====================
+
+ def get_daily_visit_trend(self, begin_date: str, end_date: str) -> List[Dict]:
+ """
+ 获取每日访问趋势
+
+ Args:
+ begin_date: 开始日期 YYYYMMDD
+ end_date: 结束日期 YYYYMMDD
+ """
+ result = self._request(
+ "POST",
+ "/datacube/getweanalysisappiddailyvisittrend",
+ json_data={"begin_date": begin_date, "end_date": end_date}
+ )
+ return result.get("list", [])
+
+ def get_user_portrait(self, begin_date: str, end_date: str) -> Dict:
+ """
+ 获取用户画像
+
+ Args:
+ begin_date: 开始日期 YYYYMMDD
+ end_date: 结束日期 YYYYMMDD
+ """
+ result = self._request(
+ "POST",
+ "/datacube/getweanalysisappiduserportrait",
+ json_data={"begin_date": begin_date, "end_date": end_date}
+ )
+ return result
+
+ # ==================== API配额 ====================
+
+ def get_api_quota(self, cgi_path: str) -> Dict:
+ """
+ 查询接口调用额度
+
+ Args:
+ cgi_path: 接口路径,如 "/wxa/getwxacode"
+ """
+ result = self._request("POST", "/cgi-bin/openapi/quota/get", json_data={
+ "cgi_path": cgi_path
+ })
+ return result.get("quota", {})
+
+ def clear_quota(self, appid: Optional[str] = None) -> bool:
+ """重置接口调用次数(每月限10次)"""
+ self._request("POST", "/cgi-bin/clear_quota", json_data={
+ "appid": appid or self.authorizer_appid
+ })
+ return True
+
+ def close(self):
+ """关闭连接"""
+ self.client.close()
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, exc_type, exc_val, exc_tb):
+ self.close()
+
+
+class APIError(Exception):
+ """API错误"""
+
+ ERROR_CODES = {
+ -1: "系统繁忙",
+ 40001: "access_token无效",
+ 40002: "grant_type不正确",
+ 40013: "appid不正确",
+ 40029: "code无效",
+ 40125: "appsecret不正确",
+ 41002: "缺少appid参数",
+ 41004: "缺少appsecret参数",
+ 42001: "access_token过期",
+ 42007: "refresh_token过期",
+ 45009: "调用超过限制",
+ 61039: "代码检测任务未完成,请稍后再试",
+ 85006: "标签格式错误",
+ 85007: "页面路径错误",
+ 85009: "已有审核版本,请先撤回",
+ 85010: "版本输入错误",
+ 85011: "当前版本不能回退",
+ 85012: "无效的版本",
+ 85015: "该账号已有发布中的版本",
+ 85019: "没有审核版本",
+ 85020: "审核状态异常",
+ 85064: "找不到模板",
+ 85085: "该小程序不能被操作",
+ 85086: "小程序没有绑定任何类目",
+ 87013: "每天只能撤回1次审核",
+ 89020: "该小程序尚未认证",
+ 89248: "隐私协议内容不完整",
+ }
+
+ def __init__(self, code: int, message: str):
+ self.code = code
+ self.message = message
+ super().__init__(f"[{code}] {self.ERROR_CODES.get(code, message)}")
+
+
+# 便捷函数
+def create_api_from_env() -> MiniProgramAPI:
+ """从环境变量创建API实例"""
+ return MiniProgramAPI()
+
+
+if __name__ == "__main__":
+ # 测试
+ api = create_api_from_env()
+ print("API初始化成功")
diff --git a/开发文档/小程序管理/scripts/mp_deploy.py b/开发文档/小程序管理/scripts/mp_deploy.py
new file mode 100644
index 00000000..22f859f4
--- /dev/null
+++ b/开发文档/小程序管理/scripts/mp_deploy.py
@@ -0,0 +1,725 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+小程序一键部署工具 v2.0
+
+功能:
+- 多小程序管理
+- 一键部署上线
+- 自动认证提交
+- 认证状态检查
+- 材料有效性验证
+
+使用方法:
+ python mp_deploy.py list # 列出所有小程序
+ python mp_deploy.py add # 添加新小程序
+ python mp_deploy.py deploy # 一键部署
+ python mp_deploy.py cert # 提交认证
+ python mp_deploy.py cert-status # 查询认证状态
+ python mp_deploy.py upload # 上传代码
+ python mp_deploy.py release # 发布上线
+"""
+
+import os
+import sys
+import json
+import subprocess
+import argparse
+from datetime import datetime
+from pathlib import Path
+from typing import Optional, Dict, List, Any
+from dataclasses import dataclass, asdict
+
+# 配置文件路径
+CONFIG_FILE = Path(__file__).parent / "apps_config.json"
+
+
+@dataclass
+class AppConfig:
+ """小程序配置"""
+ id: str
+ name: str
+ appid: str
+ project_path: str
+ private_key_path: str = ""
+ api_domain: str = ""
+ description: str = ""
+ certification: Dict = None
+
+ def __post_init__(self):
+ if self.certification is None:
+ self.certification = {
+ "status": "unknown",
+ "enterprise_name": "",
+ "license_number": "",
+ "legal_persona_name": "",
+ "legal_persona_wechat": "",
+ "component_phone": ""
+ }
+
+
+class ConfigManager:
+ """配置管理器"""
+
+ def __init__(self, config_file: Path = CONFIG_FILE):
+ self.config_file = config_file
+ self.config = self._load_config()
+
+ def _load_config(self) -> Dict:
+ """加载配置"""
+ if self.config_file.exists():
+ with open(self.config_file, 'r', encoding='utf-8') as f:
+ return json.load(f)
+ return {"apps": [], "certification_materials": {}, "third_party_platform": {}}
+
+ def _save_config(self):
+ """保存配置"""
+ with open(self.config_file, 'w', encoding='utf-8') as f:
+ json.dump(self.config, f, ensure_ascii=False, indent=2)
+
+ def get_apps(self) -> List[AppConfig]:
+ """获取所有小程序"""
+ return [AppConfig(**app) for app in self.config.get("apps", [])]
+
+ def get_app(self, app_id: str) -> Optional[AppConfig]:
+ """获取指定小程序"""
+ for app in self.config.get("apps", []):
+ if app["id"] == app_id or app["appid"] == app_id:
+ return AppConfig(**app)
+ return None
+
+ def add_app(self, app: AppConfig):
+ """添加小程序"""
+ apps = self.config.get("apps", [])
+ # 检查是否已存在
+ for i, existing in enumerate(apps):
+ if existing["id"] == app.id:
+ apps[i] = asdict(app)
+ self.config["apps"] = apps
+ self._save_config()
+ return
+ apps.append(asdict(app))
+ self.config["apps"] = apps
+ self._save_config()
+
+ def update_app(self, app_id: str, updates: Dict):
+ """更新小程序配置"""
+ apps = self.config.get("apps", [])
+ for i, app in enumerate(apps):
+ if app["id"] == app_id:
+ apps[i].update(updates)
+ self.config["apps"] = apps
+ self._save_config()
+ return True
+ return False
+
+ def get_cert_materials(self) -> Dict:
+ """获取通用认证材料"""
+ return self.config.get("certification_materials", {})
+
+ def update_cert_materials(self, materials: Dict):
+ """更新认证材料"""
+ self.config["certification_materials"] = materials
+ self._save_config()
+
+
+class MiniProgramDeployer:
+ """小程序部署器"""
+
+ # 微信开发者工具CLI路径
+ WX_CLI = "/Applications/wechatwebdevtools.app/Contents/MacOS/cli"
+
+ def __init__(self):
+ self.config = ConfigManager()
+
+ def _check_wx_cli(self) -> bool:
+ """检查微信开发者工具是否安装"""
+ return os.path.exists(self.WX_CLI)
+
+ def _run_cli(self, *args, project_path: str = None) -> tuple:
+ """运行CLI命令"""
+ cmd = [self.WX_CLI] + list(args)
+ if project_path:
+ cmd.extend(["--project", project_path])
+
+ try:
+ result = subprocess.run(cmd, capture_output=True, text=True, timeout=120)
+ return result.returncode == 0, result.stdout + result.stderr
+ except subprocess.TimeoutExpired:
+ return False, "命令执行超时"
+ except Exception as e:
+ return False, str(e)
+
+ def list_apps(self):
+ """列出所有小程序"""
+ apps = self.config.get_apps()
+
+ if not apps:
+ print("\n📭 暂无配置的小程序")
+ print(" 运行 'python mp_deploy.py add' 添加小程序")
+ return
+
+ print("\n" + "=" * 60)
+ print(" 📱 小程序列表")
+ print("=" * 60)
+
+ for i, app in enumerate(apps, 1):
+ cert_status = app.certification.get("status", "unknown")
+ status_icon = {
+ "verified": "✅",
+ "pending": "⏳",
+ "rejected": "❌",
+ "expired": "⚠️",
+ "unknown": "❓"
+ }.get(cert_status, "❓")
+
+ print(f"\n [{i}] {app.name}")
+ print(f" ID: {app.id}")
+ print(f" AppID: {app.appid}")
+ print(f" 认证: {status_icon} {cert_status}")
+ print(f" 路径: {app.project_path}")
+
+ print("\n" + "-" * 60)
+ print(" 使用方法:")
+ print(" python mp_deploy.py deploy 一键部署")
+ print(" python mp_deploy.py cert 提交认证")
+ print("=" * 60 + "\n")
+
+ def add_app(self):
+ """交互式添加小程序"""
+ print("\n" + "=" * 50)
+ print(" ➕ 添加新小程序")
+ print("=" * 50 + "\n")
+
+ # 收集信息
+ app_id = input("小程序ID(用于标识,如 my-app): ").strip()
+ if not app_id:
+ print("❌ ID不能为空")
+ return
+
+ name = input("小程序名称: ").strip()
+ appid = input("AppID(如 wx1234567890): ").strip()
+ project_path = input("项目路径: ").strip()
+
+ if not os.path.exists(project_path):
+ print(f"⚠️ 警告:路径不存在 {project_path}")
+
+ api_domain = input("API域名(可选): ").strip()
+ description = input("描述(可选): ").strip()
+
+ # 认证信息
+ print("\n📋 认证信息(可稍后配置):")
+ enterprise_name = input("企业名称: ").strip()
+
+ app = AppConfig(
+ id=app_id,
+ name=name,
+ appid=appid,
+ project_path=project_path,
+ api_domain=api_domain,
+ description=description,
+ certification={
+ "status": "unknown",
+ "enterprise_name": enterprise_name,
+ "license_number": "",
+ "legal_persona_name": "",
+ "legal_persona_wechat": "",
+ "component_phone": "15880802661"
+ }
+ )
+
+ self.config.add_app(app)
+ print(f"\n✅ 小程序 [{name}] 添加成功!")
+
+ def deploy(self, app_id: str, skip_cert_check: bool = False):
+ """一键部署流程"""
+ app = self.config.get_app(app_id)
+ if not app:
+ print(f"❌ 未找到小程序: {app_id}")
+ print(" 运行 'python mp_deploy.py list' 查看所有小程序")
+ return False
+
+ print("\n" + "=" * 60)
+ print(f" 🚀 一键部署: {app.name}")
+ print("=" * 60)
+
+ steps = [
+ ("检查环境", self._step_check_env),
+ ("检查认证状态", lambda a: self._step_check_cert(a, skip_cert_check)),
+ ("编译项目", self._step_build),
+ ("上传代码", self._step_upload),
+ ("提交审核", self._step_submit_audit),
+ ]
+
+ for step_name, step_func in steps:
+ print(f"\n📍 步骤: {step_name}")
+ print("-" * 40)
+
+ success = step_func(app)
+ if not success:
+ print(f"\n❌ 部署中断于: {step_name}")
+ return False
+
+ print("\n" + "=" * 60)
+ print(" 🎉 部署完成!")
+ print("=" * 60)
+ print(f"\n 下一步操作:")
+ print(f" 1. 等待审核(通常1-3个工作日)")
+ print(f" 2. 审核通过后运行: python mp_deploy.py release {app_id}")
+ print(f" 3. 查看状态: python mp_deploy.py status {app_id}")
+
+ return True
+
+ def _step_check_env(self, app: AppConfig) -> bool:
+ """检查环境"""
+ # 检查微信开发者工具
+ if not self._check_wx_cli():
+ print("❌ 未找到微信开发者工具")
+ print(" 请安装: https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html")
+ return False
+ print("✅ 微信开发者工具已安装")
+
+ # 检查项目路径
+ if not os.path.exists(app.project_path):
+ print(f"❌ 项目路径不存在: {app.project_path}")
+ return False
+ print(f"✅ 项目路径存在")
+
+ # 检查project.config.json
+ config_file = os.path.join(app.project_path, "project.config.json")
+ if os.path.exists(config_file):
+ with open(config_file, 'r') as f:
+ config = json.load(f)
+ if config.get("appid") != app.appid:
+ print(f"⚠️ 警告: project.config.json中的AppID与配置不一致")
+ print(f" 配置: {app.appid}")
+ print(f" 文件: {config.get('appid')}")
+
+ print("✅ 环境检查通过")
+ return True
+
+ def _step_check_cert(self, app: AppConfig, skip: bool = False) -> bool:
+ """检查认证状态"""
+ if skip:
+ print("⏭️ 跳过认证检查")
+ return True
+
+ cert_status = app.certification.get("status", "unknown")
+
+ if cert_status == "verified":
+ print("✅ 已完成微信认证")
+ return True
+
+ if cert_status == "pending":
+ print("⏳ 认证审核中")
+ print(" 可选择:")
+ print(" 1. 继续部署(未认证可上传,但无法发布)")
+ print(" 2. 等待认证完成")
+
+ choice = input("\n是否继续? (y/n): ").strip().lower()
+ return choice == 'y'
+
+ if cert_status == "expired":
+ print("⚠️ 认证已过期,需要重新认证")
+ print(" 运行: python mp_deploy.py cert " + app.id)
+
+ choice = input("\n是否继续部署? (y/n): ").strip().lower()
+ return choice == 'y'
+
+ # 未认证或未知状态
+ print("⚠️ 未完成微信认证")
+ print(" 未认证的小程序可以上传代码,但无法发布上线")
+ print(" 运行: python mp_deploy.py cert " + app.id + " 提交认证")
+
+ choice = input("\n是否继续? (y/n): ").strip().lower()
+ return choice == 'y'
+
+ def _step_build(self, app: AppConfig) -> bool:
+ """编译项目"""
+ print("📦 编译项目...")
+
+ # 使用CLI编译
+ success, output = self._run_cli("build-npm", project_path=app.project_path)
+
+ if not success:
+ # build-npm可能失败(如果没有npm依赖),不算错误
+ print("ℹ️ 编译完成(无npm依赖或编译失败,继续...)")
+ else:
+ print("✅ 编译成功")
+
+ return True
+
+ def _step_upload(self, app: AppConfig) -> bool:
+ """上传代码"""
+ # 获取版本号
+ version = datetime.now().strftime("%Y.%m.%d.%H%M")
+ desc = f"自动部署 - {datetime.now().strftime('%Y-%m-%d %H:%M')}"
+
+ print(f"📤 上传代码...")
+ print(f" 版本: {version}")
+ print(f" 描述: {desc}")
+
+ success, output = self._run_cli(
+ "upload",
+ "--version", version,
+ "--desc", desc,
+ project_path=app.project_path
+ )
+
+ if not success:
+ print(f"❌ 上传失败")
+ print(f" {output}")
+
+ # 常见错误处理
+ if "login" in output.lower():
+ print("\n💡 提示: 请在微信开发者工具中登录后重试")
+ return False
+
+ print("✅ 上传成功")
+ return True
+
+ def _step_submit_audit(self, app: AppConfig) -> bool:
+ """提交审核"""
+ print("📝 提交审核...")
+
+ # 使用CLI提交审核
+ success, output = self._run_cli(
+ "submit-audit",
+ project_path=app.project_path
+ )
+
+ if not success:
+ if "未认证" in output or "认证" in output:
+ print("⚠️ 提交审核失败:未完成微信认证")
+ print(" 代码已上传,但需要完成认证后才能提交审核")
+ print(f" 运行: python mp_deploy.py cert {app.id}")
+ return True # 不算失败,只是需要认证
+
+ print(f"❌ 提交审核失败")
+ print(f" {output}")
+ return False
+
+ print("✅ 审核已提交")
+ return True
+
+ def submit_certification(self, app_id: str):
+ """提交企业认证"""
+ app = self.config.get_app(app_id)
+ if not app:
+ print(f"❌ 未找到小程序: {app_id}")
+ return
+
+ print("\n" + "=" * 60)
+ print(f" 📋 提交认证: {app.name}")
+ print("=" * 60)
+
+ # 获取通用认证材料
+ materials = self.config.get_cert_materials()
+ cert = app.certification
+
+ # 合并材料(小程序配置优先)
+ enterprise_name = cert.get("enterprise_name") or materials.get("enterprise_name", "")
+
+ print(f"\n📌 认证信息:")
+ print(f" 小程序: {app.name} ({app.appid})")
+ print(f" 企业名称: {enterprise_name}")
+
+ # 检查必要材料
+ missing = []
+ if not enterprise_name:
+ missing.append("企业名称")
+ if not materials.get("license_number"):
+ missing.append("营业执照号")
+ if not materials.get("legal_persona_name"):
+ missing.append("法人姓名")
+
+ if missing:
+ print(f"\n⚠️ 缺少认证材料:")
+ for m in missing:
+ print(f" - {m}")
+
+ print(f"\n请先完善认证材料:")
+ print(f" 编辑: {self.config.config_file}")
+ print(f" 或运行: python mp_deploy.py cert-config")
+ return
+
+ print("\n" + "-" * 40)
+ print("📋 认证方式说明:")
+ print("-" * 40)
+ print("""
+ 【方式一】微信后台手动认证(推荐)
+
+ 1. 登录小程序后台: https://mp.weixin.qq.com/
+ 2. 设置 → 基本设置 → 微信认证
+ 3. 选择"企业"类型
+ 4. 填写企业信息、上传营业执照
+ 5. 法人微信扫码验证
+ 6. 支付认证费用(300元/年)
+ 7. 等待审核(1-5个工作日)
+
+ 【方式二】通过第三方平台代认证(需开发)
+
+ 如果你有第三方平台资质,可以通过API代认证:
+ 1. 配置第三方平台凭证
+ 2. 获取授权
+ 3. 调用认证API
+
+ API接口: POST /wxa/sec/wxaauth
+ """)
+
+ print("\n" + "-" * 40)
+ print("📝 认证材料清单:")
+ print("-" * 40)
+ print("""
+ 必需材料:
+ ☐ 企业营业执照(扫描件或照片)
+ ☐ 法人身份证(正反面)
+ ☐ 法人微信号(用于扫码验证)
+ ☐ 联系人手机号
+ ☐ 认证费用 300元
+
+ 认证有效期: 1年
+ 到期后需重新认证(年审)
+ """)
+
+ # 更新状态为待认证
+ self.config.update_app(app_id, {
+ "certification": {
+ **cert,
+ "status": "pending",
+ "submit_time": datetime.now().isoformat()
+ }
+ })
+
+ print("\n✅ 已标记为待认证状态")
+ print(" 完成认证后运行: python mp_deploy.py cert-done " + app_id)
+
+ def check_cert_status(self, app_id: str):
+ """检查认证状态"""
+ app = self.config.get_app(app_id)
+ if not app:
+ print(f"❌ 未找到小程序: {app_id}")
+ return
+
+ print("\n" + "=" * 60)
+ print(f" 🔍 认证状态: {app.name}")
+ print("=" * 60)
+
+ cert = app.certification
+ status = cert.get("status", "unknown")
+
+ status_info = {
+ "verified": ("✅ 已认证", "认证有效"),
+ "pending": ("⏳ 审核中", "请等待审核结果"),
+ "rejected": ("❌ 被拒绝", "请查看拒绝原因并重新提交"),
+ "expired": ("⚠️ 已过期", "需要重新认证(年审)"),
+ "unknown": ("❓ 未知", "请在微信后台确认状态")
+ }
+
+ icon, desc = status_info.get(status, ("❓", "未知状态"))
+
+ print(f"\n📌 当前状态: {icon}")
+ print(f" 说明: {desc}")
+ print(f" 企业: {cert.get('enterprise_name', '未填写')}")
+
+ if cert.get("submit_time"):
+ print(f" 提交时间: {cert.get('submit_time')}")
+
+ if cert.get("verify_time"):
+ print(f" 认证时间: {cert.get('verify_time')}")
+
+ if cert.get("expire_time"):
+ print(f" 到期时间: {cert.get('expire_time')}")
+
+ # 提示下一步操作
+ print("\n" + "-" * 40)
+ if status == "unknown" or status == "rejected":
+ print("👉 下一步: python mp_deploy.py cert " + app_id)
+ elif status == "pending":
+ print("👉 等待审核,通常1-5个工作日")
+ print(" 审核通过后运行: python mp_deploy.py cert-done " + app_id)
+ elif status == "verified":
+ print("👉 可以发布小程序: python mp_deploy.py deploy " + app_id)
+ elif status == "expired":
+ print("👉 需要重新认证: python mp_deploy.py cert " + app_id)
+
+ def mark_cert_done(self, app_id: str):
+ """标记认证完成"""
+ app = self.config.get_app(app_id)
+ if not app:
+ print(f"❌ 未找到小程序: {app_id}")
+ return
+
+ cert = app.certification
+ self.config.update_app(app_id, {
+ "certification": {
+ **cert,
+ "status": "verified",
+ "verify_time": datetime.now().isoformat(),
+ "expire_time": datetime.now().replace(year=datetime.now().year + 1).isoformat()
+ }
+ })
+
+ print(f"✅ 已标记 [{app.name}] 认证完成")
+ print(f" 有效期至: {datetime.now().year + 1}年")
+
+ def release(self, app_id: str):
+ """发布上线"""
+ app = self.config.get_app(app_id)
+ if not app:
+ print(f"❌ 未找到小程序: {app_id}")
+ return
+
+ print("\n" + "=" * 60)
+ print(f" 🎉 发布上线: {app.name}")
+ print("=" * 60)
+
+ # 检查认证状态
+ if app.certification.get("status") != "verified":
+ print("\n⚠️ 警告: 小程序未完成认证")
+ print(" 未认证的小程序无法发布上线")
+
+ choice = input("\n是否继续尝试? (y/n): ").strip().lower()
+ if choice != 'y':
+ return
+
+ print("\n📦 正在发布...")
+
+ # 尝试使用CLI发布
+ success, output = self._run_cli("release", project_path=app.project_path)
+
+ if success:
+ print("\n🎉 发布成功!小程序已上线")
+ else:
+ print(f"\n发布结果: {output}")
+
+ if "认证" in output:
+ print("\n💡 提示: 请先完成微信认证")
+ print(f" 运行: python mp_deploy.py cert {app_id}")
+ else:
+ print("\n💡 提示: 请在微信后台手动发布")
+ print(" 1. 登录 https://mp.weixin.qq.com/")
+ print(" 2. 版本管理 → 审核版本 → 发布")
+
+ def quick_upload(self, app_id: str, version: str = None, desc: str = None):
+ """快速上传代码"""
+ app = self.config.get_app(app_id)
+ if not app:
+ print(f"❌ 未找到小程序: {app_id}")
+ return
+
+ if not version:
+ version = datetime.now().strftime("%Y.%m.%d.%H%M")
+ if not desc:
+ desc = f"快速上传 - {datetime.now().strftime('%Y-%m-%d %H:%M')}"
+
+ print(f"\n📤 上传代码: {app.name}")
+ print(f" 版本: {version}")
+ print(f" 描述: {desc}")
+
+ success, output = self._run_cli(
+ "upload",
+ "--version", version,
+ "--desc", desc,
+ project_path=app.project_path
+ )
+
+ if success:
+ print("✅ 上传成功")
+ else:
+ print(f"❌ 上传失败: {output}")
+
+
+def print_header(title: str):
+ print("\n" + "=" * 50)
+ print(f" {title}")
+ print("=" * 50)
+
+
+def main():
+ parser = argparse.ArgumentParser(
+ description="小程序一键部署工具 v2.0",
+ formatter_class=argparse.RawDescriptionHelpFormatter,
+ epilog="""
+示例:
+ python mp_deploy.py list 列出所有小程序
+ python mp_deploy.py add 添加新小程序
+ python mp_deploy.py deploy soul-party 一键部署
+ python mp_deploy.py cert soul-party 提交认证
+ python mp_deploy.py cert-status soul-party 查询认证状态
+ python mp_deploy.py cert-done soul-party 标记认证完成
+ python mp_deploy.py upload soul-party 仅上传代码
+ python mp_deploy.py release soul-party 发布上线
+
+部署流程:
+ 1. add 添加小程序配置
+ 2. cert 提交企业认证(首次)
+ 3. cert-done 认证通过后标记
+ 4. deploy 一键部署(编译+上传+提审)
+ 5. release 审核通过后发布
+"""
+ )
+
+ subparsers = parser.add_subparsers(dest="command", help="子命令")
+
+ # list
+ subparsers.add_parser("list", help="列出所有小程序")
+
+ # add
+ subparsers.add_parser("add", help="添加新小程序")
+
+ # deploy
+ deploy_parser = subparsers.add_parser("deploy", help="一键部署")
+ deploy_parser.add_argument("app_id", help="小程序ID")
+ deploy_parser.add_argument("--skip-cert", action="store_true", help="跳过认证检查")
+
+ # cert
+ cert_parser = subparsers.add_parser("cert", help="提交认证")
+ cert_parser.add_argument("app_id", help="小程序ID")
+
+ # cert-status
+ cert_status_parser = subparsers.add_parser("cert-status", help="查询认证状态")
+ cert_status_parser.add_argument("app_id", help="小程序ID")
+
+ # cert-done
+ cert_done_parser = subparsers.add_parser("cert-done", help="标记认证完成")
+ cert_done_parser.add_argument("app_id", help="小程序ID")
+
+ # upload
+ upload_parser = subparsers.add_parser("upload", help="上传代码")
+ upload_parser.add_argument("app_id", help="小程序ID")
+ upload_parser.add_argument("-v", "--version", help="版本号")
+ upload_parser.add_argument("-d", "--desc", help="版本描述")
+
+ # release
+ release_parser = subparsers.add_parser("release", help="发布上线")
+ release_parser.add_argument("app_id", help="小程序ID")
+
+ args = parser.parse_args()
+
+ if not args.command:
+ parser.print_help()
+ return
+
+ deployer = MiniProgramDeployer()
+
+ commands = {
+ "list": lambda: deployer.list_apps(),
+ "add": lambda: deployer.add_app(),
+ "deploy": lambda: deployer.deploy(args.app_id, args.skip_cert if hasattr(args, 'skip_cert') else False),
+ "cert": lambda: deployer.submit_certification(args.app_id),
+ "cert-status": lambda: deployer.check_cert_status(args.app_id),
+ "cert-done": lambda: deployer.mark_cert_done(args.app_id),
+ "upload": lambda: deployer.quick_upload(args.app_id, getattr(args, 'version', None), getattr(args, 'desc', None)),
+ "release": lambda: deployer.release(args.app_id),
+ }
+
+ cmd_func = commands.get(args.command)
+ if cmd_func:
+ cmd_func()
+ else:
+ parser.print_help()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/开发文档/小程序管理/scripts/mp_full.py b/开发文档/小程序管理/scripts/mp_full.py
new file mode 100644
index 00000000..2a754a2a
--- /dev/null
+++ b/开发文档/小程序管理/scripts/mp_full.py
@@ -0,0 +1,555 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+小程序全能管理工具 v3.0
+
+整合能力:
+- 微信开发者工具CLI
+- miniprogram-ci (npm官方工具)
+- 微信开放平台API
+- 多小程序管理
+- 自动化部署+提审
+- 汇总报告生成
+
+使用方法:
+ python mp_full.py report # 生成汇总报告
+ python mp_full.py check # 检查项目问题
+ python mp_full.py auto # 全自动部署(上传+提审)
+ python mp_full.py batch-report # 批量生成所有小程序报告
+"""
+
+import os
+import sys
+import json
+import subprocess
+import argparse
+from datetime import datetime
+from pathlib import Path
+from typing import Optional, Dict, List, Any
+from dataclasses import dataclass, asdict, field
+
+# 配置文件路径
+SCRIPT_DIR = Path(__file__).parent
+CONFIG_FILE = SCRIPT_DIR / "apps_config.json"
+REPORT_DIR = SCRIPT_DIR / "reports"
+
+
+@dataclass
+class CheckResult:
+ """检查结果"""
+ name: str
+ status: str # ok, warning, error
+ message: str
+ fix_hint: str = ""
+
+
+@dataclass
+class AppReport:
+ """小程序报告"""
+ app_id: str
+ app_name: str
+ appid: str
+ check_time: str
+ checks: List[CheckResult] = field(default_factory=list)
+ summary: Dict = field(default_factory=dict)
+
+ @property
+ def has_errors(self) -> bool:
+ return any(c.status == "error" for c in self.checks)
+
+ @property
+ def has_warnings(self) -> bool:
+ return any(c.status == "warning" for c in self.checks)
+
+
+class MiniProgramManager:
+ """小程序全能管理器"""
+
+ # 工具路径
+ WX_CLI = "/Applications/wechatwebdevtools.app/Contents/MacOS/cli"
+
+ def __init__(self):
+ self.config = self._load_config()
+ REPORT_DIR.mkdir(exist_ok=True)
+
+ def _load_config(self) -> Dict:
+ if CONFIG_FILE.exists():
+ with open(CONFIG_FILE, 'r', encoding='utf-8') as f:
+ return json.load(f)
+ return {"apps": []}
+
+ def _save_config(self):
+ with open(CONFIG_FILE, 'w', encoding='utf-8') as f:
+ json.dump(self.config, f, ensure_ascii=False, indent=2)
+
+ def get_app(self, app_id: str) -> Optional[Dict]:
+ for app in self.config.get("apps", []):
+ if app["id"] == app_id or app["appid"] == app_id:
+ return app
+ return None
+
+ def _run_cmd(self, cmd: List[str], timeout: int = 120) -> tuple:
+ """运行命令"""
+ try:
+ result = subprocess.run(cmd, capture_output=True, text=True, timeout=timeout)
+ return result.returncode == 0, result.stdout + result.stderr
+ except subprocess.TimeoutExpired:
+ return False, "命令执行超时"
+ except Exception as e:
+ return False, str(e)
+
+ def _check_tool(self, tool: str) -> bool:
+ """检查工具是否可用"""
+ success, _ = self._run_cmd(["which", tool], timeout=5)
+ return success
+
+ # ==================== 检查功能 ====================
+
+ def check_project(self, app_id: str) -> AppReport:
+ """检查项目问题"""
+ app = self.get_app(app_id)
+ if not app:
+ print(f"❌ 未找到小程序: {app_id}")
+ return None
+
+ report = AppReport(
+ app_id=app["id"],
+ app_name=app["name"],
+ appid=app["appid"],
+ check_time=datetime.now().isoformat()
+ )
+
+ project_path = app["project_path"]
+
+ # 1. 检查项目路径
+ if os.path.exists(project_path):
+ report.checks.append(CheckResult("项目路径", "ok", f"路径存在: {project_path}"))
+ else:
+ report.checks.append(CheckResult("项目路径", "error", f"路径不存在: {project_path}", "请检查项目路径配置"))
+ return report # 路径不存在,无法继续检查
+
+ # 2. 检查project.config.json
+ config_file = os.path.join(project_path, "project.config.json")
+ if os.path.exists(config_file):
+ with open(config_file, 'r') as f:
+ config = json.load(f)
+
+ if config.get("appid") == app["appid"]:
+ report.checks.append(CheckResult("AppID配置", "ok", f"AppID正确: {app['appid']}"))
+ else:
+ report.checks.append(CheckResult("AppID配置", "error",
+ f"AppID不匹配: 配置={app['appid']}, 文件={config.get('appid')}",
+ "请修改project.config.json中的appid"))
+ else:
+ report.checks.append(CheckResult("项目配置", "error", "project.config.json不存在", "请确认这是有效的小程序项目"))
+
+ # 3. 检查app.js
+ app_js = os.path.join(project_path, "app.js")
+ if os.path.exists(app_js):
+ with open(app_js, 'r') as f:
+ content = f.read()
+
+ # 检查API域名
+ if "baseUrl" in content or "apiBase" in content:
+ if "https://" in content:
+ report.checks.append(CheckResult("API域名", "ok", "已配置HTTPS域名"))
+ elif "http://localhost" in content:
+ report.checks.append(CheckResult("API域名", "warning", "使用本地开发地址", "发布前请更换为HTTPS域名"))
+ else:
+ report.checks.append(CheckResult("API域名", "warning", "未检测到HTTPS域名"))
+
+ report.checks.append(CheckResult("入口文件", "ok", "app.js存在"))
+ else:
+ report.checks.append(CheckResult("入口文件", "error", "app.js不存在"))
+
+ # 4. 检查app.json
+ app_json = os.path.join(project_path, "app.json")
+ if os.path.exists(app_json):
+ with open(app_json, 'r') as f:
+ app_config = json.load(f)
+
+ pages = app_config.get("pages", [])
+ if pages:
+ report.checks.append(CheckResult("页面配置", "ok", f"共{len(pages)}个页面"))
+ else:
+ report.checks.append(CheckResult("页面配置", "error", "没有配置页面"))
+
+ # 检查隐私配置
+ if app_config.get("__usePrivacyCheck__"):
+ report.checks.append(CheckResult("隐私配置", "ok", "已启用隐私检查"))
+ else:
+ report.checks.append(CheckResult("隐私配置", "warning", "未启用隐私检查", "建议添加 __usePrivacyCheck__: true"))
+ else:
+ report.checks.append(CheckResult("应用配置", "error", "app.json不存在"))
+
+ # 5. 检查认证状态
+ cert_status = app.get("certification", {}).get("status", "unknown")
+ if cert_status == "verified":
+ report.checks.append(CheckResult("企业认证", "ok", "已完成认证"))
+ elif cert_status == "pending":
+ report.checks.append(CheckResult("企业认证", "warning", "认证审核中", "等待审核结果"))
+ elif cert_status == "expired":
+ report.checks.append(CheckResult("企业认证", "error", "认证已过期", "请尽快完成年审"))
+ else:
+ report.checks.append(CheckResult("企业认证", "warning", "未认证", "无法发布上线,请先完成认证"))
+
+ # 6. 检查开发工具
+ if os.path.exists(self.WX_CLI):
+ report.checks.append(CheckResult("开发者工具", "ok", "微信开发者工具已安装"))
+ else:
+ report.checks.append(CheckResult("开发者工具", "error", "微信开发者工具未安装"))
+
+ # 7. 检查miniprogram-ci
+ if self._check_tool("miniprogram-ci"):
+ report.checks.append(CheckResult("miniprogram-ci", "ok", "npm工具已安装"))
+ else:
+ report.checks.append(CheckResult("miniprogram-ci", "warning", "miniprogram-ci未安装", "运行: npm install -g miniprogram-ci"))
+
+ # 8. 检查私钥
+ if app.get("private_key_path") and os.path.exists(app["private_key_path"]):
+ report.checks.append(CheckResult("上传密钥", "ok", "私钥文件存在"))
+ else:
+ report.checks.append(CheckResult("上传密钥", "warning", "未配置私钥", "在小程序后台下载代码上传密钥"))
+
+ # 生成汇总
+ ok_count = sum(1 for c in report.checks if c.status == "ok")
+ warn_count = sum(1 for c in report.checks if c.status == "warning")
+ error_count = sum(1 for c in report.checks if c.status == "error")
+
+ report.summary = {
+ "total": len(report.checks),
+ "ok": ok_count,
+ "warning": warn_count,
+ "error": error_count,
+ "can_deploy": error_count == 0,
+ "can_release": cert_status == "verified" and error_count == 0
+ }
+
+ return report
+
+ def print_report(self, report: AppReport):
+ """打印报告"""
+ print("\n" + "=" * 70)
+ print(f" 📊 项目检查报告: {report.app_name}")
+ print("=" * 70)
+ print(f" AppID: {report.appid}")
+ print(f" 检查时间: {report.check_time}")
+ print("-" * 70)
+
+ status_icons = {"ok": "✅", "warning": "⚠️", "error": "❌"}
+
+ for check in report.checks:
+ icon = status_icons.get(check.status, "❓")
+ print(f" {icon} {check.name}: {check.message}")
+ if check.fix_hint:
+ print(f" 💡 {check.fix_hint}")
+
+ print("-" * 70)
+ s = report.summary
+ print(f" 📈 汇总: 通过 {s['ok']} / 警告 {s['warning']} / 错误 {s['error']}")
+
+ if s['can_release']:
+ print(" 🎉 状态: 可以发布上线")
+ elif s['can_deploy']:
+ print(" 📦 状态: 可以上传代码,但无法发布(需完成认证)")
+ else:
+ print(" 🚫 状态: 存在错误,请先修复")
+
+ print("=" * 70 + "\n")
+
+ def save_report(self, report: AppReport):
+ """保存报告到文件"""
+ filename = f"report_{report.app_id}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
+ filepath = REPORT_DIR / filename
+
+ with open(filepath, 'w', encoding='utf-8') as f:
+ json.dump(asdict(report), f, ensure_ascii=False, indent=2)
+
+ return filepath
+
+ # ==================== 自动化部署 ====================
+
+ def auto_deploy(self, app_id: str, version: str = None, desc: str = None, submit_audit: bool = True) -> bool:
+ """全自动部署:编译 → 上传 → 提审"""
+ app = self.get_app(app_id)
+ if not app:
+ print(f"❌ 未找到小程序: {app_id}")
+ return False
+
+ print("\n" + "=" * 70)
+ print(f" 🚀 全自动部署: {app['name']}")
+ print("=" * 70)
+
+ # 1. 先检查项目
+ print("\n📋 步骤1: 检查项目...")
+ report = self.check_project(app_id)
+ if report.has_errors:
+ print("❌ 项目存在错误,无法部署")
+ self.print_report(report)
+ return False
+ print("✅ 项目检查通过")
+
+ # 2. 准备版本信息
+ if not version:
+ version = datetime.now().strftime("%Y.%m.%d.%H%M")
+ if not desc:
+ desc = f"自动部署 - {datetime.now().strftime('%Y-%m-%d %H:%M')}"
+
+ print(f"\n📦 步骤2: 上传代码...")
+ print(f" 版本: {version}")
+ print(f" 描述: {desc}")
+
+ # 3. 上传代码
+ success = self._upload_code(app, version, desc)
+ if not success:
+ print("❌ 代码上传失败")
+ return False
+ print("✅ 代码上传成功")
+
+ # 4. 提交审核
+ if submit_audit:
+ print(f"\n📝 步骤3: 提交审核...")
+ cert_status = app.get("certification", {}).get("status", "unknown")
+
+ if cert_status != "verified":
+ print(f"⚠️ 认证状态: {cert_status}")
+ print(" 未认证的小程序无法提交审核")
+ print(" 代码已上传到开发版,请在微信后台手动提交")
+ print("\n" + "-" * 40)
+ print("👉 下一步操作:")
+ print(" 1. 完成企业认证")
+ print(" 2. 在微信后台提交审核")
+ print(" 3. 审核通过后发布上线")
+ else:
+ # 尝试通过API提交审核
+ audit_success = self._submit_audit_via_api(app)
+ if audit_success:
+ print("✅ 审核已提交")
+ else:
+ print("⚠️ 自动提审失败,请在微信后台手动提交")
+ print(" 登录: https://mp.weixin.qq.com/")
+ print(" 版本管理 → 开发版本 → 提交审核")
+
+ # 5. 生成报告
+ print(f"\n📊 步骤4: 生成报告...")
+ report_file = self.save_report(report)
+ print(f"✅ 报告已保存: {report_file}")
+
+ print("\n" + "=" * 70)
+ print(" 🎉 部署完成!")
+ print("=" * 70)
+
+ return True
+
+ def _upload_code(self, app: Dict, version: str, desc: str) -> bool:
+ """上传代码(优先使用CLI)"""
+ project_path = app["project_path"]
+
+ # 方法1:使用微信开发者工具CLI
+ if os.path.exists(self.WX_CLI):
+ cmd = [
+ self.WX_CLI, "upload",
+ "--project", project_path,
+ "--version", version,
+ "--desc", desc
+ ]
+ success, output = self._run_cmd(cmd, timeout=120)
+ if success:
+ return True
+ print(f" CLI上传失败: {output[:200]}")
+
+ # 方法2:使用miniprogram-ci
+ if self._check_tool("miniprogram-ci") and app.get("private_key_path"):
+ cmd = [
+ "miniprogram-ci", "upload",
+ "--pp", project_path,
+ "--pkp", app["private_key_path"],
+ "--appid", app["appid"],
+ "--uv", version,
+ "-r", "1",
+ "--desc", desc
+ ]
+ success, output = self._run_cmd(cmd, timeout=120)
+ if success:
+ return True
+ print(f" miniprogram-ci上传失败: {output[:200]}")
+
+ return False
+
+ def _submit_audit_via_api(self, app: Dict) -> bool:
+ """通过API提交审核(需要access_token)"""
+ # 这里需要access_token才能调用API
+ # 目前返回False,提示用户手动提交
+ return False
+
+ # ==================== 汇总报告 ====================
+
+ def generate_summary_report(self):
+ """生成所有小程序的汇总报告"""
+ apps = self.config.get("apps", [])
+
+ if not apps:
+ print("📭 暂无配置的小程序")
+ return
+
+ print("\n" + "=" * 80)
+ print(" 📊 小程序管理汇总报告")
+ print(f" 生成时间: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
+ print("=" * 80)
+
+ all_reports = []
+
+ for app in apps:
+ report = self.check_project(app["id"])
+ if report:
+ all_reports.append(report)
+
+ # 打印汇总表格
+ print("\n┌" + "─" * 78 + "┐")
+ print(f"│ {'小程序名称':<20} │ {'AppID':<25} │ {'状态':<10} │ {'可发布':<8} │")
+ print("├" + "─" * 78 + "┤")
+
+ for report in all_reports:
+ status = "✅ 正常" if not report.has_errors else "❌ 错误"
+ can_release = "✅" if report.summary.get("can_release") else "❌"
+ print(f"│ {report.app_name:<20} │ {report.appid:<25} │ {status:<10} │ {can_release:<8} │")
+
+ print("└" + "─" * 78 + "┘")
+
+ # 统计
+ total = len(all_reports)
+ ok_count = sum(1 for r in all_reports if not r.has_errors and not r.has_warnings)
+ warn_count = sum(1 for r in all_reports if r.has_warnings and not r.has_errors)
+ error_count = sum(1 for r in all_reports if r.has_errors)
+ can_release = sum(1 for r in all_reports if r.summary.get("can_release"))
+
+ print(f"\n📈 统计:")
+ print(f" 总计: {total} 个小程序")
+ print(f" 正常: {ok_count} | 警告: {warn_count} | 错误: {error_count}")
+ print(f" 可发布: {can_release} 个")
+
+ # 问题清单
+ issues = []
+ for report in all_reports:
+ for check in report.checks:
+ if check.status == "error":
+ issues.append((report.app_name, check.name, check.message, check.fix_hint))
+
+ if issues:
+ print(f"\n⚠️ 问题清单 ({len(issues)} 个):")
+ print("-" * 60)
+ for app_name, check_name, message, hint in issues:
+ print(f" [{app_name}] {check_name}: {message}")
+ if hint:
+ print(f" 💡 {hint}")
+ else:
+ print(f"\n✅ 所有小程序状态正常")
+
+ # 待办事项
+ print(f"\n📋 待办事项:")
+ for report in all_reports:
+ cert_status = "unknown"
+ for check in report.checks:
+ if check.name == "企业认证":
+ if "审核中" in check.message:
+ cert_status = "pending"
+ elif "已完成" in check.message:
+ cert_status = "verified"
+ elif "未认证" in check.message:
+ cert_status = "unknown"
+ break
+
+ if cert_status == "pending":
+ print(f" ⏳ {report.app_name}: 等待认证审核结果")
+ elif cert_status == "unknown":
+ print(f" 📝 {report.app_name}: 需要完成企业认证")
+
+ print("\n" + "=" * 80)
+
+ # 保存汇总报告
+ summary_file = REPORT_DIR / f"summary_{datetime.now().strftime('%Y%m%d_%H%M%S')}.json"
+ summary_data = {
+ "generated_at": datetime.now().isoformat(),
+ "total_apps": total,
+ "summary": {
+ "ok": ok_count,
+ "warning": warn_count,
+ "error": error_count,
+ "can_release": can_release
+ },
+ "apps": [asdict(r) for r in all_reports]
+ }
+ with open(summary_file, 'w', encoding='utf-8') as f:
+ json.dump(summary_data, f, ensure_ascii=False, indent=2)
+
+ print(f"📁 报告已保存: {summary_file}\n")
+
+
+def main():
+ parser = argparse.ArgumentParser(
+ description="小程序全能管理工具 v3.0",
+ formatter_class=argparse.RawDescriptionHelpFormatter,
+ epilog="""
+示例:
+ python mp_full.py report 生成汇总报告
+ python mp_full.py check soul-party 检查项目问题
+ python mp_full.py auto soul-party 全自动部署(上传+提审)
+ python mp_full.py auto soul-party -v 1.0.13 -d "修复问题"
+
+流程说明:
+ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐ ┌─────────┐
+ │ 检查 │ → │ 编译 │ → │ 上传 │ → │ 提审 │ → │ 发布 │
+ │ check │ │ build │ │ upload │ │ audit │ │ release │
+ └─────────┘ └─────────┘ └─────────┘ └─────────┘ └─────────┘
+
+工具整合:
+ • 微信开发者工具CLI - 本地编译上传
+ • miniprogram-ci - npm官方CI工具
+ • 开放平台API - 审核/发布/认证
+"""
+ )
+
+ subparsers = parser.add_subparsers(dest="command", help="子命令")
+
+ # report
+ subparsers.add_parser("report", help="生成汇总报告")
+
+ # check
+ check_parser = subparsers.add_parser("check", help="检查项目问题")
+ check_parser.add_argument("app_id", help="小程序ID")
+
+ # auto
+ auto_parser = subparsers.add_parser("auto", help="全自动部署")
+ auto_parser.add_argument("app_id", help="小程序ID")
+ auto_parser.add_argument("-v", "--version", help="版本号")
+ auto_parser.add_argument("-d", "--desc", help="版本描述")
+ auto_parser.add_argument("--no-audit", action="store_true", help="不提交审核")
+
+ args = parser.parse_args()
+
+ if not args.command:
+ parser.print_help()
+ return
+
+ manager = MiniProgramManager()
+
+ if args.command == "report":
+ manager.generate_summary_report()
+
+ elif args.command == "check":
+ report = manager.check_project(args.app_id)
+ if report:
+ manager.print_report(report)
+ manager.save_report(report)
+
+ elif args.command == "auto":
+ manager.auto_deploy(
+ args.app_id,
+ version=args.version,
+ desc=args.desc,
+ submit_audit=not args.no_audit
+ )
+
+
+if __name__ == "__main__":
+ main()
diff --git a/开发文档/小程序管理/scripts/mp_manager.py b/开发文档/小程序管理/scripts/mp_manager.py
new file mode 100644
index 00000000..197f780d
--- /dev/null
+++ b/开发文档/小程序管理/scripts/mp_manager.py
@@ -0,0 +1,558 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+微信小程序管理命令行工具
+
+使用方法:
+ python mp_manager.py status # 查看小程序状态
+ python mp_manager.py audit # 查看审核状态
+ python mp_manager.py release # 发布上线
+ python mp_manager.py qrcode # 生成小程序码
+ python mp_manager.py domain # 查看/配置域名
+ python mp_manager.py privacy # 配置隐私协议
+ python mp_manager.py data # 查看数据分析
+"""
+
+import os
+import sys
+import argparse
+from datetime import datetime, timedelta
+from pathlib import Path
+
+# 添加当前目录到路径
+sys.path.insert(0, str(Path(__file__).parent))
+
+from mp_api import MiniProgramAPI, APIError, create_api_from_env
+
+
+def print_header(title: str):
+ """打印标题"""
+ print("\n" + "=" * 50)
+ print(f" {title}")
+ print("=" * 50)
+
+
+def print_success(message: str):
+ """打印成功信息"""
+ print(f"✅ {message}")
+
+
+def print_error(message: str):
+ """打印错误信息"""
+ print(f"❌ {message}")
+
+
+def print_info(message: str):
+ """打印信息"""
+ print(f"ℹ️ {message}")
+
+
+def cmd_status(api: MiniProgramAPI, args):
+ """查看小程序状态"""
+ print_header("小程序基础信息")
+
+ try:
+ info = api.get_basic_info()
+ print(f"\n📱 AppID: {info.appid}")
+ print(f"📝 名称: {info.nickname}")
+ print(f"📄 简介: {info.signature}")
+ print(f"🏢 主体: {info.principal_name}")
+ print(f"✓ 认证状态: {'已认证' if info.realname_status == 1 else '未认证'}")
+
+ if info.head_image_url:
+ print(f"🖼️ 头像: {info.head_image_url}")
+
+ # 获取类目
+ print("\n📂 已设置类目:")
+ categories = api.get_category()
+ if categories:
+ for cat in categories:
+ print(f" - {cat.get('first_class', '')} > {cat.get('second_class', '')}")
+ else:
+ print(" (未设置类目)")
+
+ except APIError as e:
+ print_error(f"获取信息失败: {e}")
+
+
+def cmd_audit(api: MiniProgramAPI, args):
+ """查看审核状态"""
+ print_header("审核状态")
+
+ try:
+ status = api.get_latest_audit_status()
+ print(f"\n🔢 审核单ID: {status.auditid}")
+ print(f"📊 状态: {status.status_text}")
+
+ if status.reason:
+ print(f"\n❗ 拒绝原因:")
+ print(f" {status.reason}")
+
+ if status.screenshot:
+ print(f"\n📸 问题截图: {status.screenshot}")
+
+ if status.status == 0:
+ print("\n👉 下一步: 运行 'python mp_manager.py release' 发布上线")
+ elif status.status == 1:
+ print("\n👉 请根据拒绝原因修改后重新提交审核")
+ elif status.status == 2:
+ print("\n👉 审核中,请耐心等待(通常1-3个工作日)")
+ print(" 可运行 'python mp_manager.py audit' 再次查询")
+
+ except APIError as e:
+ print_error(f"获取审核状态失败: {e}")
+
+
+def cmd_submit(api: MiniProgramAPI, args):
+ """提交审核"""
+ print_header("提交审核")
+
+ version_desc = args.desc or input("请输入版本说明: ").strip()
+ if not version_desc:
+ print_error("版本说明不能为空")
+ return
+
+ try:
+ # 获取页面列表
+ pages = api.get_page()
+ if not pages:
+ print_error("未找到页面,请先上传代码")
+ return
+
+ print(f"\n📄 检测到 {len(pages)} 个页面:")
+ for p in pages[:5]:
+ print(f" - {p}")
+ if len(pages) > 5:
+ print(f" ... 还有 {len(pages) - 5} 个")
+
+ # 确认提交
+ confirm = input("\n确认提交审核? (y/n): ").strip().lower()
+ if confirm != 'y':
+ print_info("已取消")
+ return
+
+ auditid = api.submit_audit(version_desc=version_desc)
+ print_success(f"审核已提交,审核单ID: {auditid}")
+ print("\n👉 运行 'python mp_manager.py audit' 查询审核状态")
+
+ except APIError as e:
+ print_error(f"提交审核失败: {e}")
+
+
+def cmd_release(api: MiniProgramAPI, args):
+ """发布上线"""
+ print_header("发布上线")
+
+ try:
+ # 先检查审核状态
+ status = api.get_latest_audit_status()
+ if status.status != 0:
+ print_error(f"当前审核状态: {status.status_text}")
+ print_info("只有审核通过的版本才能发布")
+ return
+
+ print(f"📊 审核状态: {status.status_text}")
+
+ # 确认发布
+ confirm = input("\n确认发布上线? (y/n): ").strip().lower()
+ if confirm != 'y':
+ print_info("已取消")
+ return
+
+ api.release()
+ print_success("🎉 发布成功!小程序已上线")
+
+ except APIError as e:
+ print_error(f"发布失败: {e}")
+
+
+def cmd_revert(api: MiniProgramAPI, args):
+ """版本回退"""
+ print_header("版本回退")
+
+ try:
+ # 获取可回退版本
+ history = api.get_revert_history()
+ if not history:
+ print_info("没有可回退的版本")
+ return
+
+ print("\n📜 可回退版本:")
+ for v in history:
+ print(f" - {v.get('user_version', '?')}: {v.get('user_desc', '')}")
+
+ # 确认回退
+ confirm = input("\n确认回退到上一版本? (y/n): ").strip().lower()
+ if confirm != 'y':
+ print_info("已取消")
+ return
+
+ api.revert_code_release()
+ print_success("版本回退成功")
+
+ except APIError as e:
+ print_error(f"版本回退失败: {e}")
+
+
+def cmd_qrcode(api: MiniProgramAPI, args):
+ """生成小程序码"""
+ print_header("生成小程序码")
+
+ # 场景选择
+ print("\n选择类型:")
+ print(" 1. 体验版二维码")
+ print(" 2. 小程序码(有限制,每个path最多10万个)")
+ print(" 3. 无限小程序码(推荐)")
+
+ choice = args.type or input("\n请选择 (1/2/3): ").strip()
+
+ output_file = args.output or f"qrcode_{datetime.now().strftime('%Y%m%d_%H%M%S')}.png"
+
+ try:
+ if choice == "1":
+ # 体验版二维码
+ path = args.path or input("页面路径 (默认首页): ").strip() or None
+ data = api.get_qrcode(path)
+
+ elif choice == "2":
+ # 小程序码
+ path = args.path or input("页面路径: ").strip()
+ if not path:
+ print_error("页面路径不能为空")
+ return
+ data = api.get_wxacode(path)
+
+ elif choice == "3":
+ # 无限小程序码
+ scene = args.scene or input("场景值 (最长32字符): ").strip()
+ if not scene:
+ print_error("场景值不能为空")
+ return
+ page = args.path or input("页面路径 (需已发布): ").strip() or None
+ data = api.get_wxacode_unlimit(scene, page)
+
+ else:
+ print_error("无效选择")
+ return
+
+ # 保存文件
+ with open(output_file, "wb") as f:
+ f.write(data)
+
+ print_success(f"小程序码已保存: {output_file}")
+
+ # 尝试打开
+ if sys.platform == "darwin":
+ os.system(f'open "{output_file}"')
+
+ except APIError as e:
+ print_error(f"生成小程序码失败: {e}")
+
+
+def cmd_domain(api: MiniProgramAPI, args):
+ """查看/配置域名"""
+ print_header("域名配置")
+
+ try:
+ # 获取当前配置
+ domains = api.get_domain()
+ webview_domains = api.get_webview_domain()
+
+ print("\n🌐 服务器域名:")
+ print(f" request: {', '.join(domains.get('requestdomain', [])) or '(无)'}")
+ print(f" wsrequest: {', '.join(domains.get('wsrequestdomain', [])) or '(无)'}")
+ print(f" upload: {', '.join(domains.get('uploaddomain', [])) or '(无)'}")
+ print(f" download: {', '.join(domains.get('downloaddomain', [])) or '(无)'}")
+
+ print(f"\n🔗 业务域名:")
+ print(f" webview: {', '.join(webview_domains) or '(无)'}")
+
+ # 是否要配置
+ if args.set_request:
+ print(f"\n配置 request 域名: {args.set_request}")
+ api.set_domain(requestdomain=[args.set_request])
+ print_success("域名配置成功")
+
+ except APIError as e:
+ print_error(f"域名配置失败: {e}")
+
+
+def cmd_privacy(api: MiniProgramAPI, args):
+ """配置隐私协议"""
+ print_header("隐私协议配置")
+
+ try:
+ # 获取当前配置
+ settings = api.get_privacy_setting()
+
+ print("\n📋 当前隐私设置:")
+ setting_list = settings.get("setting_list", [])
+ if setting_list:
+ for s in setting_list:
+ print(f" - {s.get('privacy_key', '?')}: {s.get('privacy_text', '')}")
+ else:
+ print(" (未配置)")
+
+ owner = settings.get("owner_setting", {})
+ if owner:
+ print(f"\n📧 联系方式:")
+ if owner.get("contact_email"):
+ print(f" 邮箱: {owner['contact_email']}")
+ if owner.get("contact_phone"):
+ print(f" 电话: {owner['contact_phone']}")
+
+ # 快速配置
+ if args.quick:
+ print("\n⚡ 快速配置常用隐私项...")
+
+ default_settings = [
+ {"privacy_key": "UserInfo", "privacy_text": "用于展示您的头像和昵称"},
+ {"privacy_key": "Location", "privacy_text": "用于获取您的位置信息以推荐附近服务"},
+ {"privacy_key": "PhoneNumber", "privacy_text": "用于登录验证和订单通知"},
+ ]
+
+ api.set_privacy_setting(
+ setting_list=default_settings,
+ contact_email=args.email or "contact@example.com",
+ contact_phone=args.phone or "15880802661"
+ )
+ print_success("隐私协议配置成功")
+
+ except APIError as e:
+ print_error(f"隐私协议配置失败: {e}")
+
+
+def cmd_data(api: MiniProgramAPI, args):
+ """查看数据分析"""
+ print_header("数据分析")
+
+ # 默认查询最近7天
+ end_date = datetime.now().strftime("%Y%m%d")
+ begin_date = (datetime.now() - timedelta(days=7)).strftime("%Y%m%d")
+
+ if args.begin:
+ begin_date = args.begin
+ if args.end:
+ end_date = args.end
+
+ try:
+ print(f"\n📊 访问趋势 ({begin_date} ~ {end_date}):")
+
+ data = api.get_daily_visit_trend(begin_date, end_date)
+ if not data:
+ print(" (暂无数据)")
+ return
+
+ # 统计汇总
+ total_pv = sum(d.get("visit_pv", 0) for d in data)
+ total_uv = sum(d.get("visit_uv", 0) for d in data)
+ total_new = sum(d.get("visit_uv_new", 0) for d in data)
+
+ print(f"\n📈 汇总数据:")
+ print(f" 总访问次数: {total_pv:,}")
+ print(f" 总访问人数: {total_uv:,}")
+ print(f" 新用户数: {total_new:,}")
+
+ print(f"\n📅 每日明细:")
+ for d in data[-7:]: # 只显示最近7天
+ date = d.get("ref_date", "?")
+ pv = d.get("visit_pv", 0)
+ uv = d.get("visit_uv", 0)
+ stay = d.get("stay_time_uv", 0)
+ print(f" {date}: PV={pv}, UV={uv}, 人均停留={stay:.1f}秒")
+
+ except APIError as e:
+ print_error(f"获取数据失败: {e}")
+
+
+def cmd_quota(api: MiniProgramAPI, args):
+ """查看API配额"""
+ print_header("API配额")
+
+ common_apis = [
+ "/wxa/getwxacode",
+ "/wxa/getwxacodeunlimit",
+ "/wxa/genwxashortlink",
+ "/wxa/submit_audit",
+ "/cgi-bin/message/subscribe/send"
+ ]
+
+ try:
+ for cgi_path in common_apis:
+ try:
+ quota = api.get_api_quota(cgi_path)
+ daily_limit = quota.get("daily_limit", 0)
+ used = quota.get("used", 0)
+ remain = quota.get("remain", 0)
+
+ print(f"\n📌 {cgi_path}")
+ print(f" 每日限额: {daily_limit:,}")
+ print(f" 已使用: {used:,}")
+ print(f" 剩余: {remain:,}")
+ except APIError:
+ pass
+
+ except APIError as e:
+ print_error(f"获取配额失败: {e}")
+
+
+def cmd_cli(api: MiniProgramAPI, args):
+ """使用微信开发者工具CLI"""
+ print_header("微信开发者工具CLI")
+
+ cli_path = "/Applications/wechatwebdevtools.app/Contents/MacOS/cli"
+ project_path = args.project or os.getenv("MINIPROGRAM_PATH", "")
+
+ if not project_path:
+ project_path = input("请输入小程序项目路径: ").strip()
+
+ if not os.path.exists(project_path):
+ print_error(f"项目路径不存在: {project_path}")
+ return
+
+ if not os.path.exists(cli_path):
+ print_error("未找到微信开发者工具,请先安装")
+ return
+
+ print(f"\n📂 项目路径: {project_path}")
+ print("\n选择操作:")
+ print(" 1. 打开项目")
+ print(" 2. 预览(生成二维码)")
+ print(" 3. 上传代码")
+ print(" 4. 编译")
+
+ choice = input("\n请选择: ").strip()
+
+ if choice == "1":
+ os.system(f'"{cli_path}" -o "{project_path}"')
+ print_success("项目已打开")
+
+ elif choice == "2":
+ output = f"{project_path}/preview.png"
+ os.system(f'"{cli_path}" preview --project "{project_path}" --qr-format image --qr-output "{output}"')
+ if os.path.exists(output):
+ print_success(f"预览二维码已生成: {output}")
+ os.system(f'open "{output}"')
+ else:
+ print_error("生成失败,请检查开发者工具是否已登录")
+
+ elif choice == "3":
+ version = input("版本号 (如 1.0.0): ").strip()
+ desc = input("版本说明: ").strip()
+ os.system(f'"{cli_path}" upload --project "{project_path}" --version "{version}" --desc "{desc}"')
+ print_success("代码上传完成")
+
+ elif choice == "4":
+ os.system(f'"{cli_path}" build-npm --project "{project_path}"')
+ print_success("编译完成")
+
+ else:
+ print_error("无效选择")
+
+
+def main():
+ parser = argparse.ArgumentParser(
+ description="微信小程序管理工具",
+ formatter_class=argparse.RawDescriptionHelpFormatter,
+ epilog="""
+示例:
+ python mp_manager.py status 查看小程序状态
+ python mp_manager.py audit 查看审核状态
+ python mp_manager.py submit -d "修复xxx问题" 提交审核
+ python mp_manager.py release 发布上线
+ python mp_manager.py qrcode -t 3 -s "id=123" 生成无限小程序码
+ python mp_manager.py domain 查看域名配置
+ python mp_manager.py privacy --quick 快速配置隐私协议
+ python mp_manager.py data 查看数据分析
+ python mp_manager.py cli 使用开发者工具CLI
+"""
+ )
+
+ subparsers = parser.add_subparsers(dest="command", help="子命令")
+
+ # status
+ subparsers.add_parser("status", help="查看小程序状态")
+
+ # audit
+ subparsers.add_parser("audit", help="查看审核状态")
+
+ # submit
+ submit_parser = subparsers.add_parser("submit", help="提交审核")
+ submit_parser.add_argument("-d", "--desc", help="版本说明")
+
+ # release
+ subparsers.add_parser("release", help="发布上线")
+
+ # revert
+ subparsers.add_parser("revert", help="版本回退")
+
+ # qrcode
+ qr_parser = subparsers.add_parser("qrcode", help="生成小程序码")
+ qr_parser.add_argument("-t", "--type", choices=["1", "2", "3"], help="类型:1=体验版,2=小程序码,3=无限小程序码")
+ qr_parser.add_argument("-p", "--path", help="页面路径")
+ qr_parser.add_argument("-s", "--scene", help="场景值(类型3时使用)")
+ qr_parser.add_argument("-o", "--output", help="输出文件名")
+
+ # domain
+ domain_parser = subparsers.add_parser("domain", help="查看/配置域名")
+ domain_parser.add_argument("--set-request", help="设置request域名")
+
+ # privacy
+ privacy_parser = subparsers.add_parser("privacy", help="配置隐私协议")
+ privacy_parser.add_argument("--quick", action="store_true", help="快速配置常用隐私项")
+ privacy_parser.add_argument("--email", help="联系邮箱")
+ privacy_parser.add_argument("--phone", help="联系电话")
+
+ # data
+ data_parser = subparsers.add_parser("data", help="查看数据分析")
+ data_parser.add_argument("--begin", help="开始日期 YYYYMMDD")
+ data_parser.add_argument("--end", help="结束日期 YYYYMMDD")
+
+ # quota
+ subparsers.add_parser("quota", help="查看API配额")
+
+ # cli
+ cli_parser = subparsers.add_parser("cli", help="使用微信开发者工具CLI")
+ cli_parser.add_argument("-p", "--project", help="小程序项目路径")
+
+ args = parser.parse_args()
+
+ if not args.command:
+ parser.print_help()
+ return
+
+ # 创建API实例
+ try:
+ api = create_api_from_env()
+ except Exception as e:
+ print_error(f"初始化API失败: {e}")
+ print_info("请检查 .env 文件中的配置")
+ return
+
+ # 执行命令
+ commands = {
+ "status": cmd_status,
+ "audit": cmd_audit,
+ "submit": cmd_submit,
+ "release": cmd_release,
+ "revert": cmd_revert,
+ "qrcode": cmd_qrcode,
+ "domain": cmd_domain,
+ "privacy": cmd_privacy,
+ "data": cmd_data,
+ "quota": cmd_quota,
+ "cli": cmd_cli,
+ }
+
+ cmd_func = commands.get(args.command)
+ if cmd_func:
+ try:
+ cmd_func(api, args)
+ finally:
+ api.close()
+ else:
+ parser.print_help()
+
+
+if __name__ == "__main__":
+ main()
diff --git a/开发文档/小程序管理/scripts/reports/report_soul-party_20260125_113301.json b/开发文档/小程序管理/scripts/reports/report_soul-party_20260125_113301.json
new file mode 100644
index 00000000..628d5a38
--- /dev/null
+++ b/开发文档/小程序管理/scripts/reports/report_soul-party_20260125_113301.json
@@ -0,0 +1,76 @@
+{
+ "app_id": "soul-party",
+ "app_name": "Soul派对",
+ "appid": "wxb8bbb2b10dec74aa",
+ "check_time": "2026-01-25T11:33:01.054516",
+ "checks": [
+ {
+ "name": "项目路径",
+ "status": "ok",
+ "message": "路径存在: /Users/karuo/Documents/开发/3、自营项目/一场soul的创业实验/miniprogram",
+ "fix_hint": ""
+ },
+ {
+ "name": "AppID配置",
+ "status": "ok",
+ "message": "AppID正确: wxb8bbb2b10dec74aa",
+ "fix_hint": ""
+ },
+ {
+ "name": "API域名",
+ "status": "ok",
+ "message": "已配置HTTPS域名",
+ "fix_hint": ""
+ },
+ {
+ "name": "入口文件",
+ "status": "ok",
+ "message": "app.js存在",
+ "fix_hint": ""
+ },
+ {
+ "name": "页面配置",
+ "status": "ok",
+ "message": "共9个页面",
+ "fix_hint": ""
+ },
+ {
+ "name": "隐私配置",
+ "status": "warning",
+ "message": "未启用隐私检查",
+ "fix_hint": "建议添加 __usePrivacyCheck__: true"
+ },
+ {
+ "name": "企业认证",
+ "status": "warning",
+ "message": "认证审核中",
+ "fix_hint": "等待审核结果"
+ },
+ {
+ "name": "开发者工具",
+ "status": "ok",
+ "message": "微信开发者工具已安装",
+ "fix_hint": ""
+ },
+ {
+ "name": "miniprogram-ci",
+ "status": "ok",
+ "message": "npm工具已安装",
+ "fix_hint": ""
+ },
+ {
+ "name": "上传密钥",
+ "status": "warning",
+ "message": "未配置私钥",
+ "fix_hint": "在小程序后台下载代码上传密钥"
+ }
+ ],
+ "summary": {
+ "total": 10,
+ "ok": 7,
+ "warning": 3,
+ "error": 0,
+ "can_deploy": true,
+ "can_release": false
+ }
+}
\ No newline at end of file
diff --git a/开发文档/小程序管理/scripts/reports/report_soul-party_20260125_113423.json b/开发文档/小程序管理/scripts/reports/report_soul-party_20260125_113423.json
new file mode 100644
index 00000000..60ca8c23
--- /dev/null
+++ b/开发文档/小程序管理/scripts/reports/report_soul-party_20260125_113423.json
@@ -0,0 +1,76 @@
+{
+ "app_id": "soul-party",
+ "app_name": "Soul派对",
+ "appid": "wxb8bbb2b10dec74aa",
+ "check_time": "2026-01-25T11:34:23.760802",
+ "checks": [
+ {
+ "name": "项目路径",
+ "status": "ok",
+ "message": "路径存在: /Users/karuo/Documents/开发/3、自营项目/一场soul的创业实验/miniprogram",
+ "fix_hint": ""
+ },
+ {
+ "name": "AppID配置",
+ "status": "ok",
+ "message": "AppID正确: wxb8bbb2b10dec74aa",
+ "fix_hint": ""
+ },
+ {
+ "name": "API域名",
+ "status": "ok",
+ "message": "已配置HTTPS域名",
+ "fix_hint": ""
+ },
+ {
+ "name": "入口文件",
+ "status": "ok",
+ "message": "app.js存在",
+ "fix_hint": ""
+ },
+ {
+ "name": "页面配置",
+ "status": "ok",
+ "message": "共9个页面",
+ "fix_hint": ""
+ },
+ {
+ "name": "隐私配置",
+ "status": "ok",
+ "message": "已启用隐私检查",
+ "fix_hint": ""
+ },
+ {
+ "name": "企业认证",
+ "status": "warning",
+ "message": "认证审核中",
+ "fix_hint": "等待审核结果"
+ },
+ {
+ "name": "开发者工具",
+ "status": "ok",
+ "message": "微信开发者工具已安装",
+ "fix_hint": ""
+ },
+ {
+ "name": "miniprogram-ci",
+ "status": "ok",
+ "message": "npm工具已安装",
+ "fix_hint": ""
+ },
+ {
+ "name": "上传密钥",
+ "status": "warning",
+ "message": "未配置私钥",
+ "fix_hint": "在小程序后台下载代码上传密钥"
+ }
+ ],
+ "summary": {
+ "total": 10,
+ "ok": 8,
+ "warning": 2,
+ "error": 0,
+ "can_deploy": true,
+ "can_release": false
+ }
+}
\ No newline at end of file
diff --git a/开发文档/小程序管理/scripts/reports/report_soul-party_20260125_113434.json b/开发文档/小程序管理/scripts/reports/report_soul-party_20260125_113434.json
new file mode 100644
index 00000000..83e19c8e
--- /dev/null
+++ b/开发文档/小程序管理/scripts/reports/report_soul-party_20260125_113434.json
@@ -0,0 +1,76 @@
+{
+ "app_id": "soul-party",
+ "app_name": "Soul派对",
+ "appid": "wxb8bbb2b10dec74aa",
+ "check_time": "2026-01-25T11:34:28.854418",
+ "checks": [
+ {
+ "name": "项目路径",
+ "status": "ok",
+ "message": "路径存在: /Users/karuo/Documents/开发/3、自营项目/一场soul的创业实验/miniprogram",
+ "fix_hint": ""
+ },
+ {
+ "name": "AppID配置",
+ "status": "ok",
+ "message": "AppID正确: wxb8bbb2b10dec74aa",
+ "fix_hint": ""
+ },
+ {
+ "name": "API域名",
+ "status": "ok",
+ "message": "已配置HTTPS域名",
+ "fix_hint": ""
+ },
+ {
+ "name": "入口文件",
+ "status": "ok",
+ "message": "app.js存在",
+ "fix_hint": ""
+ },
+ {
+ "name": "页面配置",
+ "status": "ok",
+ "message": "共9个页面",
+ "fix_hint": ""
+ },
+ {
+ "name": "隐私配置",
+ "status": "ok",
+ "message": "已启用隐私检查",
+ "fix_hint": ""
+ },
+ {
+ "name": "企业认证",
+ "status": "warning",
+ "message": "认证审核中",
+ "fix_hint": "等待审核结果"
+ },
+ {
+ "name": "开发者工具",
+ "status": "ok",
+ "message": "微信开发者工具已安装",
+ "fix_hint": ""
+ },
+ {
+ "name": "miniprogram-ci",
+ "status": "ok",
+ "message": "npm工具已安装",
+ "fix_hint": ""
+ },
+ {
+ "name": "上传密钥",
+ "status": "warning",
+ "message": "未配置私钥",
+ "fix_hint": "在小程序后台下载代码上传密钥"
+ }
+ ],
+ "summary": {
+ "total": 10,
+ "ok": 8,
+ "warning": 2,
+ "error": 0,
+ "can_deploy": true,
+ "can_release": false
+ }
+}
\ No newline at end of file
diff --git a/开发文档/小程序管理/scripts/reports/summary_20260125_113255.json b/开发文档/小程序管理/scripts/reports/summary_20260125_113255.json
new file mode 100644
index 00000000..88ed994a
--- /dev/null
+++ b/开发文档/小程序管理/scripts/reports/summary_20260125_113255.json
@@ -0,0 +1,88 @@
+{
+ "generated_at": "2026-01-25T11:32:55.447833",
+ "total_apps": 1,
+ "summary": {
+ "ok": 0,
+ "warning": 1,
+ "error": 0,
+ "can_release": 0
+ },
+ "apps": [
+ {
+ "app_id": "soul-party",
+ "app_name": "Soul派对",
+ "appid": "wxb8bbb2b10dec74aa",
+ "check_time": "2026-01-25T11:32:55.428736",
+ "checks": [
+ {
+ "name": "项目路径",
+ "status": "ok",
+ "message": "路径存在: /Users/karuo/Documents/开发/3、自营项目/一场soul的创业实验/miniprogram",
+ "fix_hint": ""
+ },
+ {
+ "name": "AppID配置",
+ "status": "ok",
+ "message": "AppID正确: wxb8bbb2b10dec74aa",
+ "fix_hint": ""
+ },
+ {
+ "name": "API域名",
+ "status": "ok",
+ "message": "已配置HTTPS域名",
+ "fix_hint": ""
+ },
+ {
+ "name": "入口文件",
+ "status": "ok",
+ "message": "app.js存在",
+ "fix_hint": ""
+ },
+ {
+ "name": "页面配置",
+ "status": "ok",
+ "message": "共9个页面",
+ "fix_hint": ""
+ },
+ {
+ "name": "隐私配置",
+ "status": "warning",
+ "message": "未启用隐私检查",
+ "fix_hint": "建议添加 __usePrivacyCheck__: true"
+ },
+ {
+ "name": "企业认证",
+ "status": "warning",
+ "message": "认证审核中",
+ "fix_hint": "等待审核结果"
+ },
+ {
+ "name": "开发者工具",
+ "status": "ok",
+ "message": "微信开发者工具已安装",
+ "fix_hint": ""
+ },
+ {
+ "name": "miniprogram-ci",
+ "status": "ok",
+ "message": "npm工具已安装",
+ "fix_hint": ""
+ },
+ {
+ "name": "上传密钥",
+ "status": "warning",
+ "message": "未配置私钥",
+ "fix_hint": "在小程序后台下载代码上传密钥"
+ }
+ ],
+ "summary": {
+ "total": 10,
+ "ok": 7,
+ "warning": 3,
+ "error": 0,
+ "can_deploy": true,
+ "can_release": false
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/开发文档/小程序管理/scripts/requirements.txt b/开发文档/小程序管理/scripts/requirements.txt
new file mode 100644
index 00000000..29b7c9d3
--- /dev/null
+++ b/开发文档/小程序管理/scripts/requirements.txt
@@ -0,0 +1,7 @@
+# 微信小程序管理工具依赖
+
+# HTTP客户端
+httpx>=0.25.0
+
+# 环境变量管理
+python-dotenv>=1.0.0
diff --git a/开发文档/服务器管理/SKILL.md b/开发文档/服务器管理/SKILL.md
new file mode 100644
index 00000000..8bce80cd
--- /dev/null
+++ b/开发文档/服务器管理/SKILL.md
@@ -0,0 +1,314 @@
+---
+name: 服务器管理
+description: 宝塔服务器统一管理与自动化部署。触发词:服务器、宝塔、部署、上线、发布、Node项目、SSL证书、HTTPS、DNS解析、域名配置、端口、PM2、Nginx、MySQL数据库、服务器状态。涵盖多服务器资产管理、Node.js项目一键部署、SSL证书管理、DNS配置、系统诊断等运维能力。
+---
+
+# 服务器管理
+
+让 AI 写完代码后,无需人工介入,自动把项目「变成一个在线网站」。
+
+---
+
+## 快速入口(复制即用)
+
+### 服务器资产
+
+| 服务器 | IP | 配置 | 用途 | 宝塔面板 |
+|--------|-----|------|------|----------|
+| **小型宝塔** | 42.194.232.22 | 2核4G 5M | 主力部署(Node项目) | https://42.194.232.22:9988/ckbpanel |
+| **存客宝** | 42.194.245.239 | 2核16G 50M | 私域银行业务 | https://42.194.245.239:9988 |
+| **kr宝塔** | 43.139.27.93 | 2核4G 5M | 辅助服务器 | https://43.139.27.93:9988 |
+
+### 凭证速查
+
+```bash
+# SSH连接(小型宝塔为例)
+ssh root@42.194.232.22
+密码: Zhiqun1984
+
+# 宝塔面板登录(小型宝塔)
+地址: https://42.194.232.22:9988/ckbpanel
+账号: ckb
+密码: zhiqun1984
+
+# 宝塔API密钥
+小型宝塔: hsAWqFSi0GOCrunhmYdkxy92tBXfqYjd
+存客宝: TNKjqDv5N1QLOU20gcmGVgr82Z4mXzRi
+kr宝塔: qcWubCdlfFjS2b2DMT1lzPFaDfmv1cBT
+```
+
+---
+
+## 一键操作
+
+### 1. 检查服务器状态
+
+```bash
+# 运行快速检查脚本
+python3 /Users/karuo/Documents/个人/卡若AI/01_系统管理/服务器管理/scripts/快速检查服务器.py
+```
+
+### 2. 部署 Node 项目(标准流程)
+
+```bash
+# 1. 压缩项目(排除无用目录)
+cd /项目路径
+tar --exclude='node_modules' --exclude='.next' --exclude='.git' \
+ -czf /tmp/项目名_update.tar.gz .
+
+# 2. 上传到服务器
+sshpass -p 'Zhiqun1984' scp /tmp/项目名_update.tar.gz root@42.194.232.22:/tmp/
+
+# 3. SSH部署
+ssh root@42.194.232.22
+cd /www/wwwroot/项目名
+rm -rf app components lib public styles *.json *.js *.ts *.mjs *.md .next
+tar -xzf /tmp/项目名_update.tar.gz
+pnpm install
+pnpm run build
+rm /tmp/项目名_update.tar.gz
+
+# 4. 宝塔面板重启项目
+# 【网站】→【Node项目】→ 找到项目 → 点击【重启】
+```
+
+### 3. SSL证书检查/修复
+
+```bash
+# 检查所有服务器SSL证书状态
+python3 /Users/karuo/Documents/个人/卡若AI/01_系统管理/服务器管理/scripts/ssl证书检查.py
+
+# 自动修复过期证书
+python3 /Users/karuo/Documents/个人/卡若AI/01_系统管理/服务器管理/scripts/ssl证书检查.py --fix
+```
+
+### 4. 常用诊断命令
+
+```bash
+# 检查端口占用
+ssh root@42.194.232.22 "ss -tlnp | grep :3006"
+
+# 检查PM2进程
+ssh root@42.194.232.22 "/www/server/nodejs/v22.14.0/bin/pm2 list"
+
+# 测试HTTP响应
+ssh root@42.194.232.22 "curl -I http://localhost:3006"
+
+# 检查Nginx配置
+ssh root@42.194.232.22 "nginx -t"
+
+# 重载Nginx
+ssh root@42.194.232.22 "nginx -s reload"
+
+# DNS解析检查
+dig soul.quwanzhi.com +short @8.8.8.8
+```
+
+---
+
+## 端口配置表(小型宝塔 42.194.232.22)
+
+| 端口 | 项目名 | 类型 | 域名 | 状态 |
+|------|--------|------|------|------|
+| 3000 | cunkebao | Next.js | mckb.quwanzhi.com | ✅ |
+| 3001 | ai_hair | NestJS | ai-hair.quwanzhi.com | ✅ |
+| 3002 | kr_wb | Next.js | kr_wb.quwanzhi.com | ✅ |
+| 3003 | hx | Vue | krjzk.quwanzhi.com | ⚠️ |
+| 3004 | dlmdashboard | Next.js | dlm.quwanzhi.com | ✅ |
+| 3005 | document | Next.js | docc.quwanzhi.com | ✅ |
+| 3006 | soul | Next.js | soul.quwanzhi.com | ✅ |
+| 3015 | 神射手 | Next.js | kr-users.quwanzhi.com | ⚠️ |
+| 3018 | zhaoping | Next.js | zp.quwanzhi.com | ✅ |
+| 3021 | is_phone | Next.js | is-phone.quwanzhi.com | ✅ |
+| 3031 | word | Next.js | word.quwanzhi.com | ✅ |
+| 3036 | ymao | Next.js | ymao.quwanzhi.com | ✅ |
+| 3043 | tongzhi | Next.js | touzhi.lkdie.com | ✅ |
+| 3045 | 玩值大屏 | Next.js | wz-screen.quwanzhi.com | ✅ |
+| 3050 | zhiji | Next.js | zhiji.quwanzhi.com | ✅ |
+| 3051 | zhiji1 | Next.js | zhiji1.quwanzhi.com | ✅ |
+| 3055 | wzdj | Next.js | wzdj.quwanzhi.com | ✅ |
+| 3305 | AITOUFA | Next.js | ai-tf.quwanzhi.com | ✅ |
+| 9528 | mbti | Vue | mbtiadmin.quwanzhi.com | ✅ |
+
+### 端口分配原则
+
+- **3000-3099**: Next.js / React 项目
+- **3100-3199**: Vue 项目
+- **3200-3299**: NestJS / Express 后端
+- **3300-3399**: AI相关项目
+- **9000-9999**: 管理面板 / 特殊用途
+
+---
+
+## 核心工作流程
+
+```
+┌─────────────────────────────────────────────────────────────────────────────┐
+│ Node项目一键部署流程 │
+├─────────────────────────────────────────────────────────────────────────────┤
+│ │
+│ START │
+│ │ │
+│ ▼ │
+│ ┌──────────────────┐ │
+│ │ 1. 压缩本地代码 │ 排除 node_modules, .next, .git │
+│ └────────┬─────────┘ │
+│ │ │
+│ ▼ │
+│ ┌──────────────────┐ │
+│ │ 2. 上传到服务器 │ scp 到 /tmp/ │
+│ └────────┬─────────┘ │
+│ │ │
+│ ▼ │
+│ ┌──────────────────┐ │
+│ │ 3. 清理旧文件 │ 保留 .env 等配置文件 │
+│ └────────┬─────────┘ │
+│ │ │
+│ ▼ │
+│ ┌──────────────────┐ │
+│ │ 4. 解压新代码 │ tar -xzf │
+│ └────────┬─────────┘ │
+│ │ │
+│ ▼ │
+│ ┌──────────────────┐ │
+│ │ 5. 安装依赖 │ pnpm install │
+│ └────────┬─────────┘ │
+│ │ │
+│ ▼ │
+│ ┌──────────────────┐ │
+│ │ 6. 构建项目 │ pnpm run build │
+│ └────────┬─────────┘ │
+│ │ │
+│ ▼ │
+│ ┌──────────────────┐ │
+│ │ 7. 宝塔面板重启 │ Node项目 → 重启 │
+│ └────────┬─────────┘ │
+│ │ │
+│ ▼ │
+│ ┌──────────────────┐ │
+│ │ 8. 验证访问 │ curl https://域名 │
+│ └────────┬─────────┘ │
+│ │ │
+│ ▼ │
+│ SUCCESS │
+│ │
+└─────────────────────────────────────────────────────────────────────────────┘
+```
+
+---
+
+## 操作优先级矩阵
+
+| 操作类型 | 优先方式 | 备选方式 | 说明 |
+|---------|---------|---------|------|
+| 查询信息 | ✅ 宝塔API | SSH | API稳定 |
+| 文件操作 | ✅ 宝塔API | SSH | API支持 |
+| 配置Nginx | ✅ 宝塔API | SSH | API可读写 |
+| 重载服务 | ⚠️ SSH | - | API无接口 |
+| 上传代码 | ⚠️ SSH/scp | - | 大文件 |
+| 添加项目 | ❌ 宝塔界面 | - | API不稳定 |
+
+---
+
+## 常见问题速查
+
+### Q1: 外网无法访问(ERR_EMPTY_RESPONSE)
+
+**原因**: 腾讯云安全组只开放443端口
+
+**解决**:
+1. 必须配置SSL证书
+2. Nginx配置添加443监听
+
+### Q2: Node项目启动失败(Could not find production build)
+
+**原因**: 使用 `npm run start` 但未执行 `npm run build`
+
+**解决**: 先 `pnpm run build` 再重启
+
+### Q3: 端口冲突(EADDRINUSE)
+
+**解决**:
+```bash
+# 检查端口占用
+ss -tlnp | grep :端口号
+
+# 修改package.json中的端口
+"start": "next start -p 新端口"
+```
+
+### Q4: DNS被代理劫持
+
+**现象**: 本地DNS解析到198.18.x.x
+
+**解决**:
+- 关闭代理软件
+- 或用手机4G网络测试
+
+### Q5: 宝塔与PM2冲突
+
+**原因**: 同时使用root用户PM2和宝塔PM2
+
+**解决**:
+- 停止所有独立PM2: `pm2 kill`
+- 只使用宝塔界面管理
+
+---
+
+## 安全约束
+
+### 绝对禁止
+
+- ❌ 输出完整密码/密钥到聊天
+- ❌ 执行危险命令(rm -rf /, reboot等)
+- ❌ 跳过验证步骤
+- ❌ 使用独立PM2(避免与宝塔冲突)
+
+### 必须遵守
+
+- ✅ 操作前检查服务器状态
+- ✅ 操作后验证结果
+- ✅ 生成操作报告
+
+---
+
+## 相关脚本
+
+| 脚本 | 功能 | 位置 |
+|------|------|------|
+| `快速检查服务器.py` | 一键检查所有服务器状态 | `./scripts/` |
+| `一键部署.py` | 根据配置文件部署项目 | `./scripts/` |
+| `ssl证书检查.py` | 检查/修复SSL证书 | `./scripts/` |
+
+---
+
+## 相关文档
+
+| 文档 | 内容 | 位置 |
+|------|------|------|
+| `宝塔API接口文档.md` | 宝塔API完整接口说明 | `./references/` |
+| `端口配置表.md` | 完整端口分配表 | `./references/` |
+| `常见问题手册.md` | 问题解决方案大全 | `./references/` |
+| `部署配置模板.md` | JSON配置文件模板 | `./references/` |
+| `系统架构说明.md` | 完整架构图和流程图 | `./references/` |
+
+---
+
+## 历史对话整理
+
+### kr_wb白板项目部署(2026-01-23)
+
+- 项目类型: Next.js
+- 部署位置: /www/wwwroot/kr_wb
+- 域名: kr_wb.quwanzhi.com
+- 端口: 3002
+- 遇到问题: AI功能401错误(API密钥未配置)
+- 解决方案: 修改 lib/ai-client.ts,改用 SiliconFlow 作为默认服务
+
+### soul项目部署(2026-01-23)
+
+- 项目类型: Next.js
+- 部署位置: /www/wwwroot/soul
+- 域名: soul.quwanzhi.com
+- 端口: 3006
+- 部署流程: 压缩→上传→解压→安装依赖→构建→PM2启动→配置Nginx→配置SSL
diff --git a/开发文档/服务器管理/scripts/ssl证书检查.py b/开发文档/服务器管理/scripts/ssl证书检查.py
new file mode 100644
index 00000000..4ac56952
--- /dev/null
+++ b/开发文档/服务器管理/scripts/ssl证书检查.py
@@ -0,0 +1,168 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+SSL证书检查脚本
+===============
+用途:检查所有服务器的SSL证书状态
+
+使用方法:
+python3 ssl证书检查.py
+python3 ssl证书检查.py --fix # 自动修复过期证书
+"""
+
+import sys
+import time
+import hashlib
+import requests
+import urllib3
+from datetime import datetime
+
+# 禁用SSL警告
+urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
+
+# 服务器配置
+服务器列表 = {
+ "小型宝塔": {
+ "面板地址": "https://42.194.232.22:9988",
+ "密钥": "hsAWqFSi0GOCrunhmYdkxy92tBXfqYjd"
+ },
+ "存客宝": {
+ "面板地址": "https://42.194.245.239:9988",
+ "密钥": "TNKjqDv5N1QLOU20gcmGVgr82Z4mXzRi"
+ },
+ "kr宝塔": {
+ "面板地址": "https://43.139.27.93:9988",
+ "密钥": "qcWubCdlfFjS2b2DMT1lzPFaDfmv1cBT"
+ }
+}
+
+def 生成签名(api_key: str) -> tuple:
+ """生成宝塔API签名"""
+ now_time = int(time.time())
+ sign_str = str(now_time) + hashlib.md5(api_key.encode('utf-8')).hexdigest()
+ request_token = hashlib.md5(sign_str.encode('utf-8')).hexdigest()
+ return now_time, request_token
+
+def 获取证书列表(面板地址: str, 密钥: str) -> dict:
+ """获取SSL证书列表"""
+ now_time, request_token = 生成签名(密钥)
+
+ url = f"{面板地址}/ssl?action=GetCertList"
+ data = {
+ "request_time": now_time,
+ "request_token": request_token
+ }
+
+ try:
+ response = requests.post(url, data=data, timeout=10, verify=False)
+ return response.json()
+ except Exception as e:
+ return {"error": str(e)}
+
+def 获取网站列表(面板地址: str, 密钥: str) -> dict:
+ """获取网站列表"""
+ now_time, request_token = 生成签名(密钥)
+
+ url = f"{面板地址}/data?action=getData&table=sites"
+ data = {
+ "request_time": now_time,
+ "request_token": request_token,
+ "limit": 100,
+ "p": 1
+ }
+
+ try:
+ response = requests.post(url, data=data, timeout=10, verify=False)
+ return response.json()
+ except Exception as e:
+ return {"error": str(e)}
+
+def 检查服务器证书(名称: str, 配置: dict) -> dict:
+ """检查单台服务器的证书状态"""
+ print(f"\n检查服务器: {名称}")
+ print("-" * 40)
+
+ try:
+ # 获取网站列表
+ 网站数据 = 获取网站列表(配置["面板地址"], 配置["密钥"])
+
+ if "error" in 网站数据:
+ print(f" ❌ API错误: {网站数据['error']}")
+ return {"error": 网站数据['error']}
+
+ 网站列表 = 网站数据.get("data", [])
+
+ if not 网站列表:
+ print(" ⚠️ 没有找到网站")
+ return {"网站数": 0}
+
+ print(f" 📊 共 {len(网站列表)} 个网站")
+
+ # 统计
+ 已配置SSL = 0
+ 未配置SSL = 0
+
+ for 网站 in 网站列表:
+ 网站名 = 网站.get("name", "未知")
+ ssl状态 = 网站.get("ssl", 0)
+
+ if ssl状态:
+ 已配置SSL += 1
+ 状态标识 = "🔒"
+ else:
+ 未配置SSL += 1
+ 状态标识 = "🔓"
+
+ print(f" {状态标识} {网站名}")
+
+ print(f"\n 统计: 已配置SSL {已配置SSL} 个, 未配置 {未配置SSL} 个")
+
+ return {
+ "网站数": len(网站列表),
+ "已配置SSL": 已配置SSL,
+ "未配置SSL": 未配置SSL
+ }
+
+ except Exception as e:
+ print(f" ❌ 检查失败: {e}")
+ return {"error": str(e)}
+
+def main():
+ 自动修复 = "--fix" in sys.argv
+
+ print("=" * 60)
+ print(" SSL证书状态检查报告")
+ print(f" {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
+ print("=" * 60)
+
+ 总统计 = {
+ "服务器数": 0,
+ "网站总数": 0,
+ "已配置SSL": 0,
+ "未配置SSL": 0
+ }
+
+ for 服务器名称, 配置 in 服务器列表.items():
+ 结果 = 检查服务器证书(服务器名称, 配置)
+
+ if "error" not in 结果:
+ 总统计["服务器数"] += 1
+ 总统计["网站总数"] += 结果.get("网站数", 0)
+ 总统计["已配置SSL"] += 结果.get("已配置SSL", 0)
+ 总统计["未配置SSL"] += 结果.get("未配置SSL", 0)
+
+ print("\n" + "=" * 60)
+ print(" 汇总统计")
+ print("=" * 60)
+ print(f" 服务器数量: {总统计['服务器数']}")
+ print(f" 网站总数: {总统计['网站总数']}")
+ print(f" 已配置SSL: {总统计['已配置SSL']} 🔒")
+ print(f" 未配置SSL: {总统计['未配置SSL']} 🔓")
+ print("=" * 60)
+
+ if 自动修复 and 总统计['未配置SSL'] > 0:
+ print("\n⚠️ --fix 模式需要手动在宝塔面板配置SSL证书")
+ print(" 建议使用通配符证书 *.quwanzhi.com")
+
+if __name__ == "__main__":
+ main()
diff --git a/开发文档/服务器管理/scripts/一键部署.py b/开发文档/服务器管理/scripts/一键部署.py
new file mode 100644
index 00000000..a5660e18
--- /dev/null
+++ b/开发文档/服务器管理/scripts/一键部署.py
@@ -0,0 +1,138 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+一键部署脚本
+============
+用途:根据配置文件一键部署Node项目到宝塔服务器
+
+使用方法:
+python3 一键部署.py 项目名称 本地项目路径
+
+示例:
+python3 一键部署.py soul /Users/karuo/Documents/开发/3、自营项目/一场soul的创业实验
+"""
+
+import sys
+import os
+import subprocess
+import time
+
+# 默认服务器配置
+默认配置 = {
+ "服务器IP": "42.194.232.22",
+ "SSH用户": "root",
+ "SSH密码": "Zhiqun1984",
+ "服务器根目录": "/www/wwwroot"
+}
+
+def 执行命令(命令: str, 显示输出: bool = True) -> tuple:
+ """执行shell命令"""
+ result = subprocess.run(命令, shell=True, capture_output=True, text=True)
+ if 显示输出 and result.stdout:
+ print(result.stdout)
+ if result.stderr and "Warning" not in result.stderr:
+ print(f"错误: {result.stderr}")
+ return result.returncode, result.stdout
+
+def 部署项目(项目名称: str, 本地路径: str):
+ """执行部署流程"""
+ 服务器路径 = f"{默认配置['服务器根目录']}/{项目名称}"
+ 压缩文件 = f"/tmp/{项目名称}_update.tar.gz"
+
+ print(f"\n{'='*60}")
+ print(f"开始部署: {项目名称}")
+ print(f"本地路径: {本地路径}")
+ print(f"服务器路径: {服务器路径}")
+ print(f"{'='*60}\n")
+
+ # 步骤1: 压缩项目
+ print("📦 步骤1: 压缩项目文件...")
+ 排除项 = "--exclude='node_modules' --exclude='.next' --exclude='.git' --exclude='android' --exclude='out'"
+ 压缩命令 = f"cd '{本地路径}' && tar {排除项} -czf {压缩文件} ."
+ code, _ = 执行命令(压缩命令, False)
+ if code != 0:
+ print("❌ 压缩失败")
+ return False
+
+ # 获取文件大小
+ 大小 = os.path.getsize(压缩文件) / 1024 / 1024
+ print(f" ✅ 压缩完成,大小: {大小:.2f} MB")
+
+ # 步骤2: 上传到服务器
+ print("\n📤 步骤2: 上传到服务器...")
+ 上传命令 = f"sshpass -p '{默认配置['SSH密码']}' scp -o StrictHostKeyChecking=no {压缩文件} {默认配置['SSH用户']}@{默认配置['服务器IP']}:/tmp/"
+ code, _ = 执行命令(上传命令, False)
+ if code != 0:
+ print("❌ 上传失败")
+ return False
+ print(" ✅ 上传完成")
+
+ # 步骤3-6: SSH远程执行
+ print("\n🔧 步骤3-6: 服务器端操作...")
+
+ SSH前缀 = f"sshpass -p '{默认配置['SSH密码']}' ssh -o StrictHostKeyChecking=no {默认配置['SSH用户']}@{默认配置['服务器IP']}"
+
+ # 清理旧文件
+ 清理命令 = f"{SSH前缀} 'cd {服务器路径} && rm -rf app components lib public styles *.json *.js *.ts *.mjs *.md .next 2>/dev/null || true'"
+ 执行命令(清理命令, False)
+ print(" ✅ 清理旧文件")
+
+ # 解压
+ 解压命令 = f"{SSH前缀} 'cd {服务器路径} && tar -xzf /tmp/{项目名称}_update.tar.gz'"
+ 执行命令(解压命令, False)
+ print(" ✅ 解压新代码")
+
+ # 安装依赖
+ print("\n📚 安装依赖 (这可能需要几分钟)...")
+ 安装命令 = f"{SSH前缀} 'export PATH=/www/server/nodejs/v22.14.0/bin:$PATH && cd {服务器路径} && npm install --legacy-peer-deps 2>&1'"
+ 执行命令(安装命令, True)
+
+ # 构建
+ print("\n🏗️ 构建项目...")
+ 构建命令 = f"{SSH前缀} 'export PATH=/www/server/nodejs/v22.14.0/bin:$PATH && cd {服务器路径} && npm run build 2>&1'"
+ 执行命令(构建命令, True)
+
+ # 重启PM2
+ print("\n🔄 重启服务...")
+ 重启命令 = f"{SSH前缀} 'export PATH=/www/server/nodejs/v22.14.0/bin:$PATH && pm2 restart {项目名称} 2>&1'"
+ 执行命令(重启命令, True)
+
+ # 清理临时文件
+ 清理临时命令 = f"{SSH前缀} 'rm -f /tmp/{项目名称}_update.tar.gz'"
+ 执行命令(清理临时命令, False)
+ os.remove(压缩文件)
+
+ print(f"\n{'='*60}")
+ print("✅ 部署完成!")
+ print(f"{'='*60}")
+ print("\n⚠️ 请在宝塔面板手动重启项目:")
+ print(f" 1. 登录 https://42.194.232.22:9988/ckbpanel")
+ print(f" 2. 进入【网站】→【Node项目】")
+ print(f" 3. 找到 {项目名称},点击【重启】")
+
+ return True
+
+def main():
+ if len(sys.argv) < 3:
+ print("用法: python3 一键部署.py <项目名称> <本地项目路径>")
+ print("\n示例:")
+ print(" python3 一键部署.py soul /Users/karuo/Documents/开发/3、自营项目/一场soul的创业实验")
+ print(" python3 一键部署.py kr_wb /Users/karuo/Documents/开发/4、小工具/whiteboard")
+ sys.exit(1)
+
+ 项目名称 = sys.argv[1]
+ 本地路径 = sys.argv[2]
+
+ if not os.path.exists(本地路径):
+ print(f"❌ 本地路径不存在: {本地路径}")
+ sys.exit(1)
+
+ 确认 = input(f"\n确认部署 {项目名称} 到服务器? (y/n): ")
+ if 确认.lower() != 'y':
+ print("已取消部署")
+ sys.exit(0)
+
+ 部署项目(项目名称, 本地路径)
+
+if __name__ == "__main__":
+ main()
diff --git a/开发文档/服务器管理/scripts/快速检查服务器.py b/开发文档/服务器管理/scripts/快速检查服务器.py
new file mode 100644
index 00000000..5667788c
--- /dev/null
+++ b/开发文档/服务器管理/scripts/快速检查服务器.py
@@ -0,0 +1,104 @@
+#!/usr/bin/env python3
+# -*- coding: utf-8 -*-
+"""
+快速检查服务器状态
+==================
+用途:一键检查所有服务器的基本状态
+
+使用方法:
+python3 快速检查服务器.py
+"""
+
+import time
+import hashlib
+import requests
+import urllib3
+
+# 禁用SSL警告
+urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
+
+# 服务器配置
+服务器列表 = {
+ "小型宝塔": {
+ "面板地址": "https://42.194.232.22:9988",
+ "密钥": "hsAWqFSi0GOCrunhmYdkxy92tBXfqYjd"
+ },
+ "存客宝": {
+ "面板地址": "https://42.194.245.239:9988",
+ "密钥": "TNKjqDv5N1QLOU20gcmGVgr82Z4mXzRi"
+ },
+ "kr宝塔": {
+ "面板地址": "https://43.139.27.93:9988",
+ "密钥": "qcWubCdlfFjS2b2DMT1lzPFaDfmv1cBT"
+ }
+}
+
+def 生成签名(api_key: str) -> tuple:
+ """生成宝塔API签名"""
+ now_time = int(time.time())
+ sign_str = str(now_time) + hashlib.md5(api_key.encode('utf-8')).hexdigest()
+ request_token = hashlib.md5(sign_str.encode('utf-8')).hexdigest()
+ return now_time, request_token
+
+def 获取系统信息(面板地址: str, 密钥: str) -> dict:
+ """获取系统基础统计信息"""
+ now_time, request_token = 生成签名(密钥)
+
+ url = f"{面板地址}/system?action=GetSystemTotal"
+ data = {
+ "request_time": now_time,
+ "request_token": request_token
+ }
+
+ try:
+ response = requests.post(url, data=data, timeout=10, verify=False)
+ return response.json()
+ except Exception as e:
+ return {"error": str(e)}
+
+def 检查单台服务器(名称: str, 配置: dict) -> dict:
+ """检查单台服务器状态"""
+ try:
+ 系统信息 = 获取系统信息(配置["面板地址"], 配置["密钥"])
+
+ if isinstance(系统信息, dict) and "error" not in 系统信息 and 系统信息.get("status") != False:
+ return {
+ "名称": 名称,
+ "状态": "✅ 正常",
+ "CPU": f"{系统信息.get('cpuRealUsed', 'N/A')}%",
+ "内存": f"{系统信息.get('memRealUsed', 'N/A')}%",
+ "磁盘": f"{系统信息.get('diskPer', 'N/A')}%"
+ }
+ else:
+ return {
+ "名称": 名称,
+ "状态": "❌ API错误",
+ "错误": str(系统信息)
+ }
+ except Exception as e:
+ return {
+ "名称": 名称,
+ "状态": "❌ 连接失败",
+ "错误": str(e)
+ }
+
+def main():
+ print("=" * 60)
+ print(" 服务器状态检查报告")
+ print("=" * 60)
+ print()
+
+ for 名称, 配置 in 服务器列表.items():
+ 结果 = 检查单台服务器(名称, 配置)
+ print(f"📦 {结果['名称']}")
+ print(f" 状态: {结果['状态']}")
+ if "CPU" in 结果:
+ print(f" CPU: {结果['CPU']} | 内存: {结果['内存']} | 磁盘: {结果['磁盘']}")
+ if "错误" in 结果:
+ print(f" 错误: {结果['错误'][:50]}...")
+ print()
+
+ print("=" * 60)
+
+if __name__ == "__main__":
+ main()
diff --git a/归档/miniprogram/.gitignore b/归档/miniprogram/.gitignore
deleted file mode 100644
index 14ea590c..00000000
--- a/归档/miniprogram/.gitignore
+++ /dev/null
@@ -1,14 +0,0 @@
-# Windows
-[Dd]esktop.ini
-Thumbs.db
-$RECYCLE.BIN/
-
-# macOS
-.DS_Store
-.fseventsd
-.Spotlight-V100
-.TemporaryItems
-.Trashes
-
-# Node.js
-node_modules/
diff --git a/归档/miniprogram/README.md b/归档/miniprogram/README.md
deleted file mode 100644
index 2cc11dbf..00000000
--- a/归档/miniprogram/README.md
+++ /dev/null
@@ -1,138 +0,0 @@
-# Soul创业实验 - 微信小程序
-
-> 一场SOUL的创业实验场 - 来自Soul派对房的真实商业故事
-
-## 📱 项目简介
-
-本项目是《一场SOUL的创业实验场》的微信小程序版本,完整还原了Web端的所有UI界面和功能。
-
-## 🎨 设计特点
-
-- **主题色**: Soul青色 (#00CED1)
-- **设计风格**: 深色主题 + 毛玻璃效果
-- **1:1还原**: 完全复刻Web端的UI设计
-
-## 📂 项目结构
-
-```
-miniprogram/
-├── app.js # 应用入口
-├── app.json # 应用配置
-├── app.wxss # 全局样式
-├── custom-tab-bar/ # 自定义TabBar组件
-│ ├── index.js
-│ ├── index.json
-│ ├── index.wxml
-│ └── index.wxss
-├── pages/
-│ ├── index/ # 首页
-│ ├── chapters/ # 目录页
-│ ├── match/ # 找伙伴页
-│ ├── my/ # 我的页面
-│ ├── read/ # 阅读页
-│ ├── about/ # 关于作者
-│ ├── referral/ # 推广中心
-│ ├── purchases/ # 订单页
-│ └── settings/ # 设置页
-├── utils/
-│ ├── util.js # 工具函数
-│ └── payment.js # 支付工具
-├── assets/
-│ └── icons/ # 图标资源
-├── project.config.json # 项目配置
-└── sitemap.json # 站点地图
-```
-
-## 🚀 功能列表
-
-### 核心功能
-- ✅ 首页 - 书籍展示、推荐章节、阅读进度
-- ✅ 目录 - 完整章节列表、篇章折叠展开
-- ✅ 找伙伴 - 匹配动画、匹配类型选择
-- ✅ 我的 - 个人信息、订单、推广中心
-- ✅ 阅读 - 付费墙、章节导航、分享功能
-
-### 特色功能
-- ✅ 自定义TabBar(中间突出的找伙伴按钮)
-- ✅ 阅读进度条
-- ✅ 匹配动画效果
-- ✅ 付费墙与购买流程
-- ✅ 分享海报功能
-- ✅ 推广佣金系统
-
-## 🛠 开发指南
-
-### 环境要求
-- 微信开发者工具 >= 1.06.2308310
-- 基础库版本 >= 3.3.4
-
-### 快速开始
-
-1. **下载微信开发者工具**
- - 前往 [微信开发者工具下载页面](https://developers.weixin.qq.com/miniprogram/dev/devtools/download.html)
-
-2. **导入项目**
- - 打开微信开发者工具
- - 选择"导入项目"
- - 项目目录选择 `miniprogram` 文件夹
- - AppID 使用: `wx432c93e275548671`
-
-3. **编译运行**
- - 点击"编译"按钮
- - 在模拟器中预览效果
-
-### 真机调试
-
-1. 点击工具栏的"预览"按钮
-2. 使用微信扫描二维码
-3. 在真机上测试所有功能
-
-## 📝 配置说明
-
-### API配置
-在 `app.js` 中修改 `globalData.baseUrl`:
-
-```javascript
-globalData: {
- baseUrl: 'https://soul.ckb.fit', // 你的API地址
- // ...
-}
-```
-
-### AppID配置
-在 `project.config.json` 中修改:
-
-```json
-{
- "appid": "你的小程序AppID"
-}
-```
-
-## 🎯 上线发布
-
-1. **准备工作**
- - 确保所有功能测试通过
- - 检查API接口是否正常
- - 确认支付功能已配置
-
-2. **上传代码**
- - 在开发者工具中点击"上传"
- - 填写版本号和项目备注
-
-3. **提交审核**
- - 登录[微信公众平台](https://mp.weixin.qq.com)
- - 进入"版本管理"
- - 提交审核
-
-4. **发布上线**
- - 审核通过后点击"发布"
-
-## 🔗 相关链接
-
-- **Web版本**: https://soul.ckb.fit
-- **作者微信**: 28533368
-- **技术支持**: 存客宝
-
-## 📄 版权信息
-
-© 2024 卡若. All rights reserved.
diff --git a/归档/miniprogram/app.js b/归档/miniprogram/app.js
deleted file mode 100644
index 8b507f28..00000000
--- a/归档/miniprogram/app.js
+++ /dev/null
@@ -1,540 +0,0 @@
-/**
- * Soul创业派对 - 小程序入口
- * 开发: 卡若
- */
-
-App({
- globalData: {
- // API基础地址 - 连接真实后端
- baseUrl: 'https://soul.quwanzhi.com',
-
- // 小程序配置 - 真实AppID
- appId: 'wxb8bbb2b10dec74aa',
-
- // 订阅消息:用户点击「申请提现」→「立即提现」时会先弹出订阅授权窗
- withdrawSubscribeTmplId: 'u3MbZGPRkrZIk-I7QdpwzFxnO_CeQPaCWF2FkiIablE',
-
- // 微信支付配置
- mchId: '1318592501', // 商户号
-
- // 用户信息
- userInfo: null,
- openId: null, // 微信openId,支付必需
- isLoggedIn: false,
-
- // 书籍数据
- bookData: null,
- totalSections: 62,
-
- // 购买记录
- purchasedSections: [],
- hasFullBook: false,
-
- // 已读章节(仅统计有权限打开过的章节,用于首页「已读/待读」)
- readSectionIds: [],
-
- // 推荐绑定
- pendingReferralCode: null, // 待绑定的推荐码
-
- // 主题配置
- theme: {
- brandColor: '#00CED1',
- brandSecondary: '#20B2AA',
- goldColor: '#FFD700',
- bgColor: '#000000',
- cardBg: '#1c1c1e'
- },
-
- // 系统信息
- systemInfo: null,
- statusBarHeight: 44,
- navBarHeight: 88,
-
- // TabBar相关
- currentTab: 0
- },
-
- onLaunch(options) {
- this.globalData.readSectionIds = wx.getStorageSync('readSectionIds') || []
- // 获取系统信息
- this.getSystemInfo()
-
- // 检查登录状态
- this.checkLoginStatus()
-
- // 加载书籍数据
- this.loadBookData()
-
- // 检查更新
- this.checkUpdate()
-
- // 处理分享参数(推荐码绑定)
- this.handleReferralCode(options)
- },
-
- // 小程序显示时也检查分享参数
- onShow(options) {
- this.handleReferralCode(options)
- },
-
- // 处理推荐码绑定
- handleReferralCode(options) {
- const query = options?.query || {}
- const refCode = query.ref || query.referralCode
-
- if (refCode) {
- console.log('[App] 检测到推荐码:', refCode)
-
- // 立即记录访问(不需要登录,用于统计"通过链接进的人数")
- this.recordReferralVisit(refCode)
-
- // 保存待绑定的推荐码(不再在前端做"只能绑定一次"的限制,让后端根据30天规则判断续期/抢夺)
- this.globalData.pendingReferralCode = refCode
- wx.setStorageSync('pendingReferralCode', refCode)
- // 同步写入 referral_code,供章节/找伙伴支付时传给后端,订单会记录 referrer_id 与 referral_code
- wx.setStorageSync('referral_code', refCode)
-
- // 如果已登录,立即尝试绑定,由 /api/miniprogram/referral/bind 按 30 天规则决定 new / renew / takeover
- if (this.globalData.isLoggedIn && this.globalData.userInfo) {
- this.bindReferralCode(refCode)
- }
- }
- },
-
- // 记录推荐访问(不需要登录,用于统计)
- async recordReferralVisit(refCode) {
- try {
- // 获取openId(如果有)
- const openId = this.globalData.openId || wx.getStorageSync('openId') || ''
- const userId = this.globalData.userInfo?.id || ''
-
- await this.request('/api/miniprogram/referral/visit', {
- method: 'POST',
- data: {
- referralCode: refCode,
- visitorOpenId: openId,
- visitorId: userId,
- source: 'miniprogram',
- page: getCurrentPages()[getCurrentPages().length - 1]?.route || ''
- },
- silent: true
- })
- console.log('[App] 记录推荐访问成功')
- } catch (e) {
- console.log('[App] 记录推荐访问失败:', e.message)
- // 忽略错误,不影响用户体验
- }
- },
-
- // 绑定推荐码到用户
- async bindReferralCode(refCode) {
- try {
- const userId = this.globalData.userInfo?.id
- if (!userId || !refCode) return
-
- console.log('[App] 绑定推荐码:', refCode, '到用户:', userId)
-
- // 调用API绑定推荐关系
- const res = await this.request('/api/miniprogram/referral/bind', {
- method: 'POST',
- data: {
- userId,
- referralCode: refCode
- },
- silent: true
- })
-
- if (res.success) {
- console.log('[App] 推荐码绑定成功')
- // 仅记录当前已绑定的推荐码,用于展示/调试;是否允许更换由后端根据30天规则判断
- wx.setStorageSync('boundReferralCode', refCode)
- this.globalData.pendingReferralCode = null
- wx.removeStorageSync('pendingReferralCode')
- }
- } catch (e) {
- console.error('[App] 绑定推荐码失败:', e)
- }
- },
-
- // 获取系统信息
- getSystemInfo() {
- try {
- const systemInfo = wx.getSystemInfoSync()
- this.globalData.systemInfo = systemInfo
- this.globalData.statusBarHeight = systemInfo.statusBarHeight || 44
-
- // 计算导航栏高度
- const menuButton = wx.getMenuButtonBoundingClientRect()
- if (menuButton) {
- this.globalData.navBarHeight = (menuButton.top - systemInfo.statusBarHeight) * 2 + menuButton.height + systemInfo.statusBarHeight
- }
- } catch (e) {
- console.error('获取系统信息失败:', e)
- }
- },
-
- // 检查登录状态
- checkLoginStatus() {
- try {
- const userInfo = wx.getStorageSync('userInfo')
- const token = wx.getStorageSync('token')
-
- if (userInfo && token) {
- this.globalData.userInfo = userInfo
- this.globalData.isLoggedIn = true
- this.globalData.purchasedSections = userInfo.purchasedSections || []
- this.globalData.hasFullBook = userInfo.hasFullBook || false
- }
- } catch (e) {
- console.error('检查登录状态失败:', e)
- }
- },
-
- // 加载书籍数据
- async loadBookData() {
- try {
- // 先从缓存加载
- const cachedData = wx.getStorageSync('bookData')
- if (cachedData) {
- this.globalData.bookData = cachedData
- }
-
- // 从服务器获取最新数据
- const res = await this.request('/api/book/all-chapters')
- if (res && (res.data || res.chapters)) {
- const chapters = res.data || res.chapters || []
- this.globalData.bookData = chapters
- wx.setStorageSync('bookData', chapters)
- }
- } catch (e) {
- console.error('加载书籍数据失败:', e)
- }
- },
-
- // 检查更新
- checkUpdate() {
- if (wx.canIUse('getUpdateManager')) {
- const updateManager = wx.getUpdateManager()
-
- updateManager.onCheckForUpdate((res) => {
- if (res.hasUpdate) {
- console.log('发现新版本')
- }
- })
-
- updateManager.onUpdateReady(() => {
- wx.showModal({
- title: '更新提示',
- content: '新版本已准备好,是否重启应用?',
- success: (res) => {
- if (res.confirm) {
- updateManager.applyUpdate()
- }
- }
- })
- })
-
- updateManager.onUpdateFailed(() => {
- wx.showToast({
- title: '更新失败,请稍后重试',
- icon: 'none'
- })
- })
- }
- },
-
- /**
- * 从 soul-api 返回体中取错误提示文案(兼容 message / error 字段)
- */
- _getApiErrorMsg(data, defaultMsg = '请求失败') {
- if (!data || typeof data !== 'object') return defaultMsg
- const msg = data.message || data.error
- return (msg && String(msg).trim()) ? String(msg).trim() : defaultMsg
- },
-
- /**
- * 统一请求方法。接口失败时会弹窗提示(与 soul-api 返回的 message/error 一致)。
- * @param {string|object} urlOrOptions - 接口路径,或 { url, method, data, header, silent }
- * @param {object} options - { method, data, header, silent }
- * @param {boolean} options.silent - 为 true 时不弹窗,仅 reject(用于静默请求如访问统计)
- */
- request(urlOrOptions, options = {}) {
- let url
- if (typeof urlOrOptions === 'string') {
- url = urlOrOptions
- } else if (urlOrOptions && typeof urlOrOptions === 'object' && urlOrOptions.url) {
- url = urlOrOptions.url
- options = { ...urlOrOptions, url: undefined }
- } else {
- url = ''
- }
- const silent = !!options.silent
- const showError = (msg) => {
- if (!silent && msg) {
- wx.showToast({ title: msg, icon: 'none', duration: 2500 })
- }
- }
-
- return new Promise((resolve, reject) => {
- const token = wx.getStorageSync('token')
-
- wx.request({
- url: this.globalData.baseUrl + url,
- method: options.method || 'GET',
- data: options.data || {},
- header: {
- 'Content-Type': 'application/json',
- 'Authorization': token ? `Bearer ${token}` : '',
- ...options.header
- },
- success: (res) => {
- const data = res.data
- if (res.statusCode === 200) {
- // 业务失败:success === false,soul-api 用 message 或 error 返回原因
- if (data && data.success === false) {
- const msg = this._getApiErrorMsg(data, '操作失败')
- showError(msg)
- reject(new Error(msg))
- return
- }
- resolve(data)
- return
- }
- if (res.statusCode === 401) {
- this.logout()
- showError('未授权,请重新登录')
- reject(new Error('未授权'))
- return
- }
- // 4xx/5xx:优先用返回体的 message/error
- const msg = this._getApiErrorMsg(data, res.statusCode >= 500 ? '服务器异常,请稍后重试' : '请求失败')
- showError(msg)
- reject(new Error(msg))
- },
- fail: (err) => {
- const msg = (err && err.errMsg) ? (err.errMsg.indexOf('timeout') !== -1 ? '请求超时,请重试' : '网络异常,请重试') : '网络异常,请重试'
- showError(msg)
- reject(new Error(msg))
- }
- })
- })
- },
-
- // 登录方法 - 获取openId用于支付(加固错误处理,避免审核报“登录报错”)
- async login() {
- try {
- const loginRes = await new Promise((resolve, reject) => {
- wx.login({ success: resolve, fail: reject })
- })
- if (!loginRes || !loginRes.code) {
- console.warn('[App] wx.login 未返回 code')
- wx.showToast({ title: '获取登录态失败,请重试', icon: 'none' })
- return null
- }
- try {
- const res = await this.request('/api/miniprogram/login', {
- method: 'POST',
- data: { code: loginRes.code }
- })
-
- if (res.success && res.data) {
- // 保存openId
- if (res.data.openId) {
- this.globalData.openId = res.data.openId
- wx.setStorageSync('openId', res.data.openId)
- console.log('[App] 获取openId成功')
- }
-
- // 保存用户信息
- if (res.data.user) {
- this.globalData.userInfo = res.data.user
- this.globalData.isLoggedIn = true
- this.globalData.purchasedSections = res.data.user.purchasedSections || []
- this.globalData.hasFullBook = res.data.user.hasFullBook || false
-
- wx.setStorageSync('userInfo', res.data.user)
- wx.setStorageSync('token', res.data.token || '')
-
- // 登录成功后,检查待绑定的推荐码并执行绑定
- const pendingRef = wx.getStorageSync('pendingReferralCode') || this.globalData.pendingReferralCode
- if (pendingRef) {
- console.log('[App] 登录后自动绑定推荐码:', pendingRef)
- this.bindReferralCode(pendingRef)
- }
- }
-
- return res.data
- }
- } catch (apiError) {
- console.log('[App] API登录失败:', apiError.message)
- // 不使用模拟登录,提示用户网络问题
- wx.showToast({ title: '网络异常,请重试', icon: 'none' })
- return null
- }
-
- return null
- } catch (e) {
- console.error('[App] 登录失败:', e)
- wx.showToast({ title: '登录失败,请重试', icon: 'none' })
- return null
- }
- },
-
- // 获取openId (支付必需)
- async getOpenId() {
- // 先检查缓存
- const cachedOpenId = wx.getStorageSync('openId')
- if (cachedOpenId) {
- this.globalData.openId = cachedOpenId
- return cachedOpenId
- }
-
- // 没有缓存则登录获取
- try {
- const loginRes = await new Promise((resolve, reject) => {
- wx.login({ success: resolve, fail: reject })
- })
-
- const res = await this.request('/api/miniprogram/login', {
- method: 'POST',
- data: { code: loginRes.code }
- })
-
- if (res.success && res.data?.openId) {
- this.globalData.openId = res.data.openId
- wx.setStorageSync('openId', res.data.openId)
- // 接口同时返回 user 时视为登录,补全登录态并从登录开始绑定推荐码
- if (res.data.user) {
- this.globalData.userInfo = res.data.user
- this.globalData.isLoggedIn = true
- this.globalData.purchasedSections = res.data.user.purchasedSections || []
- this.globalData.hasFullBook = res.data.user.hasFullBook || false
- wx.setStorageSync('userInfo', res.data.user)
- wx.setStorageSync('token', res.data.token || '')
- const pendingRef = wx.getStorageSync('pendingReferralCode') || this.globalData.pendingReferralCode
- if (pendingRef) {
- console.log('[App] getOpenId 登录后自动绑定推荐码:', pendingRef)
- this.bindReferralCode(pendingRef)
- }
- }
- return res.data.openId
- }
- } catch (e) {
- console.error('[App] 获取openId失败:', e)
- }
-
- return null
- },
-
- // 模拟登录已废弃 - 不再使用
- // 现在必须使用真实的微信登录获取openId作为唯一标识
- mockLogin() {
- console.warn('[App] mockLogin已废弃,请使用真实登录')
- return null
- },
-
- // 手机号登录:需同时传 wx.login 的 code 与 getPhoneNumber 的 phoneCode
- async loginWithPhone(phoneCode) {
- try {
- const loginRes = await new Promise((resolve, reject) => {
- wx.login({ success: resolve, fail: reject })
- })
- if (!loginRes.code) {
- wx.showToast({ title: '获取登录态失败', icon: 'none' })
- return null
- }
- const res = await this.request('/api/miniprogram/phone-login', {
- method: 'POST',
- data: { code: loginRes.code, phoneCode }
- })
-
- if (res.success && res.data) {
- this.globalData.userInfo = res.data.user
- this.globalData.isLoggedIn = true
- this.globalData.purchasedSections = res.data.user.purchasedSections || []
- this.globalData.hasFullBook = res.data.user.hasFullBook || false
-
- wx.setStorageSync('userInfo', res.data.user)
- wx.setStorageSync('token', res.data.token)
-
- // 登录成功后绑定推荐码
- const pendingRef = wx.getStorageSync('pendingReferralCode') || this.globalData.pendingReferralCode
- if (pendingRef) {
- console.log('[App] 手机号登录后自动绑定推荐码:', pendingRef)
- this.bindReferralCode(pendingRef)
- }
-
- return res.data
- }
- } catch (e) {
- console.log('[App] 手机号登录失败:', e)
- wx.showToast({ title: '登录失败,请重试', icon: 'none' })
- }
-
- return null
- },
-
- // 退出登录
- logout() {
- this.globalData.userInfo = null
- this.globalData.isLoggedIn = false
- this.globalData.purchasedSections = []
- this.globalData.hasFullBook = false
-
- wx.removeStorageSync('userInfo')
- wx.removeStorageSync('token')
- },
-
- // 检查是否已购买章节
- hasPurchased(sectionId) {
- if (this.globalData.hasFullBook) return true
- return this.globalData.purchasedSections.includes(sectionId)
- },
-
- // 标记章节为已读(仅在有权限打开时由阅读页调用,用于首页已读/待读统计)
- markSectionAsRead(sectionId) {
- if (!sectionId) return
- const list = this.globalData.readSectionIds || []
- if (list.includes(sectionId)) return
- list.push(sectionId)
- this.globalData.readSectionIds = list
- wx.setStorageSync('readSectionIds', list)
- },
-
- // 已读章节数(用于首页展示)
- getReadCount() {
- return (this.globalData.readSectionIds || []).length
- },
-
- // 获取章节总数
- getTotalSections() {
- return this.globalData.totalSections
- },
-
- // 切换TabBar
- switchTab(index) {
- this.globalData.currentTab = index
- },
-
- // 显示Toast
- showToast(title, icon = 'none') {
- wx.showToast({
- title,
- icon,
- duration: 2000
- })
- },
-
- // 显示Loading
- showLoading(title = '加载中...') {
- wx.showLoading({
- title,
- mask: true
- })
- },
-
- // 隐藏Loading
- hideLoading() {
- wx.hideLoading()
- }
-})
diff --git a/归档/miniprogram/app.json b/归档/miniprogram/app.json
deleted file mode 100644
index 4bbe3721..00000000
--- a/归档/miniprogram/app.json
+++ /dev/null
@@ -1,67 +0,0 @@
-{
- "pages": [
- "pages/index/index",
- "pages/chapters/chapters",
- "pages/match/match",
- "pages/my/my",
- "pages/read/read",
- "pages/about/about",
- "pages/agreement/agreement",
- "pages/privacy/privacy",
- "pages/referral/referral",
- "pages/purchases/purchases",
- "pages/settings/settings",
- "pages/search/search",
- "pages/addresses/addresses",
- "pages/addresses/edit",
- "pages/withdraw-records/withdraw-records",
- "pages/vip/vip",
- "pages/member-detail/member-detail"
- ],
- "window": {
- "backgroundTextStyle": "light",
- "navigationBarBackgroundColor": "#000000",
- "navigationBarTitleText": "Soul创业派对",
- "navigationBarTextStyle": "white",
- "backgroundColor": "#000000",
- "navigationStyle": "custom"
- },
- "tabBar": {
- "custom": true,
- "color": "#8e8e93",
- "selectedColor": "#00CED1",
- "backgroundColor": "#1c1c1e",
- "borderStyle": "black",
- "list": [
- {
- "pagePath": "pages/index/index",
- "text": "首页"
- },
- {
- "pagePath": "pages/chapters/chapters",
- "text": "目录"
- },
- {
- "pagePath": "pages/match/match",
- "text": "找伙伴"
- },
- {
- "pagePath": "pages/my/my",
- "text": "我的"
- }
- ]
- },
- "usingComponents": {},
- "__usePrivacyCheck__": true,
- "permission": {
- "scope.userLocation": {
- "desc": "用于匹配附近的书友"
- }
- },
- "requiredPrivateInfos": [
- "getLocation"
- ],
- "lazyCodeLoading": "requiredComponents",
- "style": "v2",
- "sitemapLocation": "sitemap.json"
-}
\ No newline at end of file
diff --git a/归档/miniprogram/app.wxss b/归档/miniprogram/app.wxss
deleted file mode 100644
index 9ce22a06..00000000
--- a/归档/miniprogram/app.wxss
+++ /dev/null
@@ -1,606 +0,0 @@
-/**
- * Soul创业实验 - 全局样式
- * 主题色: #00CED1 (Soul青色)
- * 开发: 卡若
- */
-
-/* ===== CSS 变量系统 ===== */
-page {
- /* 品牌色 */
- --app-brand: #00CED1;
- --app-brand-light: rgba(0, 206, 209, 0.1);
- --app-brand-dark: #20B2AA;
-
- /* 背景色 */
- --app-bg-primary: #000000;
- --app-bg-secondary: #1c1c1e;
- --app-bg-tertiary: #2c2c2e;
-
- /* 文字色 */
- --app-text-primary: #ffffff;
- --app-text-secondary: rgba(255, 255, 255, 0.7);
- --app-text-tertiary: rgba(255, 255, 255, 0.4);
-
- /* 分隔线 */
- --app-separator: rgba(255, 255, 255, 0.05);
-
- /* iOS 系统色 */
- --ios-indigo: #5856D6;
- --ios-green: #30d158;
- --ios-red: #FF3B30;
- --ios-orange: #FF9500;
- --ios-yellow: #FFD700;
-
- /* 金色 */
- --gold: #FFD700;
- --gold-light: #FFA500;
-
- /* 粉色 */
- --pink: #E91E63;
-
- /* 紫色 */
- --purple: #7B61FF;
-}
-
-/* ===== 页面基础样式 ===== */
-page {
- background-color: var(--app-bg-primary);
- color: var(--app-text-primary);
- font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', 'SF Pro Text', 'Helvetica Neue', 'PingFang SC', 'Microsoft YaHei', sans-serif;
- font-size: 28rpx;
- line-height: 1.5;
- -webkit-font-smoothing: antialiased;
-}
-
-/* ===== 全局容器 ===== */
-.container {
- min-height: 100vh;
- padding: 0;
- background: #000000;
- padding-bottom: env(safe-area-inset-bottom);
-}
-
-/* ===== 品牌色系 ===== */
-.brand-color {
- color: #00CED1;
-}
-
-.brand-bg {
- background-color: #00CED1;
-}
-
-.brand-gradient {
- background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%);
-}
-
-.gold-color {
- color: #FFD700;
-}
-
-.gold-bg {
- background: linear-gradient(135deg, #FFD700 0%, #FFA500 100%);
-}
-
-/* ===== 文字渐变 ===== */
-.gradient-text {
- background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%);
- -webkit-background-clip: text;
- -webkit-text-fill-color: transparent;
- background-clip: text;
-}
-
-.gold-gradient-text {
- background: linear-gradient(135deg, #FFD700 0%, #FFA500 100%);
- -webkit-background-clip: text;
- -webkit-text-fill-color: transparent;
- background-clip: text;
-}
-
-/* ===== 按钮样式 ===== */
-.btn-primary {
- background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%);
- color: #ffffff;
- border: none;
- border-radius: 48rpx;
- padding: 28rpx 48rpx;
- font-size: 32rpx;
- font-weight: 600;
- box-shadow: 0 8rpx 24rpx rgba(0, 206, 209, 0.3);
- display: flex;
- align-items: center;
- justify-content: center;
-}
-
-.btn-primary::after {
- border: none;
-}
-
-.btn-primary:active {
- opacity: 0.85;
- transform: scale(0.98);
-}
-
-.btn-secondary {
- background: rgba(0, 206, 209, 0.1);
- color: #00CED1;
- border: 2rpx solid rgba(0, 206, 209, 0.3);
- border-radius: 48rpx;
- padding: 28rpx 48rpx;
- font-size: 32rpx;
- font-weight: 500;
-}
-
-.btn-secondary::after {
- border: none;
-}
-
-.btn-secondary:active {
- background: rgba(0, 206, 209, 0.2);
-}
-
-.btn-ghost {
- background: rgba(255, 255, 255, 0.05);
- color: #ffffff;
- border: 2rpx solid rgba(255, 255, 255, 0.1);
- border-radius: 48rpx;
- padding: 28rpx 48rpx;
- font-size: 32rpx;
-}
-
-.btn-ghost::after {
- border: none;
-}
-
-.btn-ghost:active {
- background: rgba(255, 255, 255, 0.1);
-}
-
-.btn-gold {
- background: linear-gradient(135deg, #FFD700 0%, #FFA500 100%);
- color: #000000;
- border: none;
- border-radius: 48rpx;
- padding: 28rpx 48rpx;
- font-size: 32rpx;
- font-weight: 600;
- box-shadow: 0 8rpx 24rpx rgba(255, 215, 0, 0.3);
-}
-
-.btn-gold::after {
- border: none;
-}
-
-/* ===== 卡片样式 ===== */
-.card {
- background: rgba(28, 28, 30, 0.9);
- border-radius: 32rpx;
- padding: 32rpx;
- margin: 24rpx 32rpx;
- border: 2rpx solid rgba(255, 255, 255, 0.05);
-}
-
-.card-light {
- background: rgba(44, 44, 46, 0.8);
- border-radius: 24rpx;
- padding: 24rpx;
- border: 2rpx solid rgba(255, 255, 255, 0.08);
-}
-
-.card-gradient {
- background: linear-gradient(135deg, rgba(28, 28, 30, 1) 0%, rgba(44, 44, 46, 1) 100%);
- border-radius: 32rpx;
- padding: 32rpx;
- border: 2rpx solid rgba(0, 206, 209, 0.2);
-}
-
-.card-brand {
- background: linear-gradient(135deg, rgba(0, 206, 209, 0.1) 0%, rgba(32, 178, 170, 0.05) 100%);
- border-radius: 32rpx;
- padding: 32rpx;
- border: 2rpx solid rgba(0, 206, 209, 0.2);
-}
-
-/* ===== 输入框样式 ===== */
-.input-ios {
- background: rgba(0, 0, 0, 0.3);
- border: 2rpx solid rgba(255, 255, 255, 0.1);
- border-radius: 24rpx;
- padding: 28rpx 32rpx;
- font-size: 32rpx;
- color: #ffffff;
-}
-
-.input-ios:focus {
- border-color: rgba(0, 206, 209, 0.5);
-}
-
-.input-ios-placeholder {
- color: rgba(255, 255, 255, 0.3);
-}
-
-/* ===== 列表项样式 ===== */
-.list-item {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 28rpx 32rpx;
- background: rgba(28, 28, 30, 0.9);
- border-bottom: 1rpx solid rgba(255, 255, 255, 0.05);
-}
-
-.list-item:first-child {
- border-radius: 24rpx 24rpx 0 0;
-}
-
-.list-item:last-child {
- border-radius: 0 0 24rpx 24rpx;
- border-bottom: none;
-}
-
-.list-item:only-child {
- border-radius: 24rpx;
-}
-
-.list-item:active {
- background: rgba(44, 44, 46, 1);
-}
-
-/* ===== 标签样式 ===== */
-.tag {
- display: inline-flex;
- align-items: center;
- justify-content: center;
- padding: 8rpx 20rpx;
- min-width: 80rpx;
- border-radius: 8rpx;
- font-size: 22rpx;
- font-weight: 500;
- box-sizing: border-box;
- text-align: center;
-}
-
-.tag-brand {
- background: rgba(0, 206, 209, 0.1);
- color: #00CED1;
-}
-
-.tag-gold {
- background: rgba(255, 215, 0, 0.1);
- color: #FFD700;
-}
-
-.tag-pink {
- background: rgba(233, 30, 99, 0.1);
- color: #E91E63;
-}
-
-.tag-purple {
- background: rgba(123, 97, 255, 0.1);
- color: #7B61FF;
-}
-
-.tag-free {
- background: rgba(0, 206, 209, 0.1);
- color: #00CED1;
-}
-
-/* ===== 分隔线 ===== */
-.divider {
- height: 1rpx;
- background: rgba(255, 255, 255, 0.05);
- margin: 24rpx 0;
-}
-
-.divider-vertical {
- width: 2rpx;
- height: 48rpx;
- background: rgba(255, 255, 255, 0.1);
-}
-
-/* ===== 骨架屏动画 ===== */
-.skeleton {
- background: linear-gradient(90deg,
- rgba(28, 28, 30, 1) 25%,
- rgba(44, 44, 46, 1) 50%,
- rgba(28, 28, 30, 1) 75%
- );
- background-size: 200% 100%;
- animation: skeleton-loading 1.5s ease-in-out infinite;
- border-radius: 8rpx;
-}
-
-@keyframes skeleton-loading {
- 0% {
- background-position: 200% 0;
- }
- 100% {
- background-position: -200% 0;
- }
-}
-
-/* ===== 页面过渡动画 ===== */
-.page-transition {
- animation: fadeIn 0.3s ease-out;
-}
-
-@keyframes fadeIn {
- from {
- opacity: 0;
- transform: translateY(20rpx);
- }
- to {
- opacity: 1;
- transform: translateY(0);
- }
-}
-
-/* ===== 弹窗动画 ===== */
-.modal-overlay {
- animation: modalOverlayIn 0.25s ease-out;
-}
-
-.modal-content {
- animation: modalContentIn 0.3s cubic-bezier(0.32, 0.72, 0, 1);
-}
-
-@keyframes modalOverlayIn {
- from { opacity: 0; }
- to { opacity: 1; }
-}
-
-@keyframes modalContentIn {
- from {
- opacity: 0;
- transform: scale(0.95) translateY(20rpx);
- }
- to {
- opacity: 1;
- transform: scale(1) translateY(0);
- }
-}
-
-/* ===== 脉动动画 ===== */
-.pulse {
- animation: pulse 2s ease-in-out infinite;
-}
-
-@keyframes pulse {
- 0%, 100% {
- transform: scale(1);
- opacity: 1;
- }
- 50% {
- transform: scale(1.05);
- opacity: 0.8;
- }
-}
-
-/* ===== 发光效果 ===== */
-.glow {
- box-shadow: 0 0 40rpx rgba(0, 206, 209, 0.3);
-}
-
-.glow-gold {
- box-shadow: 0 0 40rpx rgba(255, 215, 0, 0.3);
-}
-
-/* ===== 文字样式 ===== */
-.text-xs {
- font-size: 22rpx;
-}
-
-.text-sm {
- font-size: 26rpx;
-}
-
-.text-base {
- font-size: 28rpx;
-}
-
-.text-lg {
- font-size: 32rpx;
-}
-
-.text-xl {
- font-size: 36rpx;
-}
-
-.text-2xl {
- font-size: 44rpx;
-}
-
-.text-3xl {
- font-size: 56rpx;
-}
-
-.text-white {
- color: #ffffff;
-}
-
-.text-gray {
- color: rgba(255, 255, 255, 0.6);
-}
-
-.text-muted {
- color: rgba(255, 255, 255, 0.4);
-}
-
-.text-center {
- text-align: center;
-}
-
-.font-medium {
- font-weight: 500;
-}
-
-.font-semibold {
- font-weight: 600;
-}
-
-.font-bold {
- font-weight: 700;
-}
-
-/* ===== Flex布局 ===== */
-.flex {
- display: flex;
-}
-
-.flex-col {
- flex-direction: column;
-}
-
-.items-center {
- align-items: center;
-}
-
-.justify-center {
- justify-content: center;
-}
-
-.justify-between {
- justify-content: space-between;
-}
-
-.justify-around {
- justify-content: space-around;
-}
-
-.flex-1 {
- flex: 1;
-}
-
-.gap-1 {
- gap: 8rpx;
-}
-
-.gap-2 {
- gap: 16rpx;
-}
-
-.gap-3 {
- gap: 24rpx;
-}
-
-.gap-4 {
- gap: 32rpx;
-}
-
-/* ===== 间距 ===== */
-.p-2 { padding: 16rpx; }
-.p-3 { padding: 24rpx; }
-.p-4 { padding: 32rpx; }
-.p-5 { padding: 40rpx; }
-
-.px-4 { padding-left: 32rpx; padding-right: 32rpx; }
-.py-2 { padding-top: 16rpx; padding-bottom: 16rpx; }
-.py-3 { padding-top: 24rpx; padding-bottom: 24rpx; }
-
-.m-4 { margin: 32rpx; }
-.mx-4 { margin-left: 32rpx; margin-right: 32rpx; }
-.my-3 { margin-top: 24rpx; margin-bottom: 24rpx; }
-.mb-2 { margin-bottom: 16rpx; }
-.mb-3 { margin-bottom: 24rpx; }
-.mb-4 { margin-bottom: 32rpx; }
-.mt-4 { margin-top: 32rpx; }
-
-/* ===== 圆角 ===== */
-.rounded { border-radius: 8rpx; }
-.rounded-lg { border-radius: 16rpx; }
-.rounded-xl { border-radius: 24rpx; }
-.rounded-2xl { border-radius: 32rpx; }
-.rounded-full { border-radius: 50%; }
-
-/* ===== 安全区域 ===== */
-.safe-bottom {
- padding-bottom: calc(env(safe-area-inset-bottom) + 20rpx);
-}
-
-.pb-tabbar {
- padding-bottom: 200rpx;
-}
-
-/* ===== 头部导航占位 ===== */
-.nav-placeholder {
- height: calc(88rpx + env(safe-area-inset-top, 44rpx));
-}
-
-/* ===== 隐藏滚动条 ===== */
-::-webkit-scrollbar {
- display: none;
- width: 0;
- height: 0;
-}
-
-/* ===== 触摸反馈 ===== */
-.touch-feedback {
- transition: all 0.15s ease;
-}
-
-.touch-feedback:active {
- opacity: 0.7;
- transform: scale(0.98);
-}
-
-/* ===== 进度条 ===== */
-.progress-bar {
- height: 8rpx;
- background: rgba(44, 44, 46, 1);
- border-radius: 4rpx;
- overflow: hidden;
-}
-
-.progress-fill {
- height: 100%;
- background: linear-gradient(90deg, #00CED1 0%, #20B2AA 100%);
- border-radius: 4rpx;
- transition: width 0.3s ease;
-}
-
-/* ===== 头像样式 ===== */
-.avatar {
- width: 80rpx;
- height: 80rpx;
- border-radius: 50%;
- background: linear-gradient(135deg, rgba(0, 206, 209, 0.2) 0%, rgba(32, 178, 170, 0.1) 100%);
- display: flex;
- align-items: center;
- justify-content: center;
- color: #00CED1;
- font-weight: 700;
- font-size: 32rpx;
- border: 4rpx solid rgba(0, 206, 209, 0.3);
-}
-
-.avatar-lg {
- width: 120rpx;
- height: 120rpx;
- font-size: 48rpx;
-}
-
-/* ===== 图标容器 ===== */
-.icon-box {
- width: 64rpx;
- height: 64rpx;
- border-radius: 16rpx;
- display: flex;
- align-items: center;
- justify-content: center;
-}
-
-.icon-box-brand {
- background: linear-gradient(135deg, rgba(0, 206, 209, 0.2) 0%, rgba(32, 178, 170, 0.1) 100%);
-}
-
-.icon-box-gold {
- background: linear-gradient(135deg, rgba(255, 215, 0, 0.2) 0%, rgba(255, 165, 0, 0.1) 100%);
-}
-
-/* ===== 渐变背景 ===== */
-.bg-gradient-dark {
- background: linear-gradient(180deg, #000000 0%, #1a1a1a 100%);
-}
-
-.bg-gradient-brand {
- background: linear-gradient(135deg, rgba(0, 206, 209, 0.1) 0%, transparent 100%);
-}
diff --git a/归档/miniprogram/assets/icons/alert-circle.svg b/归档/miniprogram/assets/icons/alert-circle.svg
deleted file mode 100644
index f5a441f3..00000000
--- a/归档/miniprogram/assets/icons/alert-circle.svg
+++ /dev/null
@@ -1,5 +0,0 @@
-
diff --git a/归档/miniprogram/assets/icons/arrow-right.svg b/归档/miniprogram/assets/icons/arrow-right.svg
deleted file mode 100644
index 1dc64d3f..00000000
--- a/归档/miniprogram/assets/icons/arrow-right.svg
+++ /dev/null
@@ -1,4 +0,0 @@
-
diff --git a/归档/miniprogram/assets/icons/bell.svg b/归档/miniprogram/assets/icons/bell.svg
deleted file mode 100644
index 0e7e405b..00000000
--- a/归档/miniprogram/assets/icons/bell.svg
+++ /dev/null
@@ -1,4 +0,0 @@
-
diff --git a/归档/miniprogram/assets/icons/book-open.svg b/归档/miniprogram/assets/icons/book-open.svg
deleted file mode 100644
index d833e86b..00000000
--- a/归档/miniprogram/assets/icons/book-open.svg
+++ /dev/null
@@ -1,4 +0,0 @@
-
diff --git a/归档/miniprogram/assets/icons/book.svg b/归档/miniprogram/assets/icons/book.svg
deleted file mode 100644
index 93579576..00000000
--- a/归档/miniprogram/assets/icons/book.svg
+++ /dev/null
@@ -1,4 +0,0 @@
-
diff --git a/归档/miniprogram/assets/icons/chevron-left.svg b/归档/miniprogram/assets/icons/chevron-left.svg
deleted file mode 100644
index e406b2b9..00000000
--- a/归档/miniprogram/assets/icons/chevron-left.svg
+++ /dev/null
@@ -1,3 +0,0 @@
-
diff --git a/归档/miniprogram/assets/icons/gift.svg b/归档/miniprogram/assets/icons/gift.svg
deleted file mode 100644
index 66ac806c..00000000
--- a/归档/miniprogram/assets/icons/gift.svg
+++ /dev/null
@@ -1,6 +0,0 @@
-
diff --git a/归档/miniprogram/assets/icons/home-active.png b/归档/miniprogram/assets/icons/home-active.png
deleted file mode 100644
index b6090d87610396c4e046dd531906e3f4aeaeee09..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 699
zcmeAS@N?(olHy`uVBq!ia0vp^fgsGm3?%0U?qUH_3dtTpz6=aiY77hwEes65fIUwRG5VK4FYb!C6W$j8E@-(9CP(<^;b
zR+8R{-Kzt1UUod~dg>GEyyj}|lD4KBJx$xLPV3HJcKPkh5AU=)(hO?WUYv2w#c|H1
zIcM)iojCeb>rS@0`M!udpYC0}*L{@vz4F`15jLOqUD(%kRQi3xw^*IJy7SCRE*%1b
zP8^C~CfhQGev1{DBX_YA=wj6p*NBpo#FA92CCE6dF8*(}7if-ZKuxwC}J8ub)+W
zZO+pTZ&M~mym`$VxJYQ~iX(fN>%zHe7ib!}w`$JY@%YR=_h}iYayA_La(qFuRL-qy
z|NVG>IM0sMTWQ%Ckkq(r<`VRhu<)JU8pk8;M?1KeKUI$3q=~tmdjjRGW&*f
z`py3jCiHtx*Zr0wec()i&()jJDc8T>nDD=Q^|pU<9N)TBCbu8SQu%-6_LV1=8>LR!e>61p+?Q9m|3s^I
zji+yw-13xJGbB~D48^=cl60n6D0C`8z^C&3Ki#JC_pKuq0Yh1}#5JNMC9x#cD!C{X
zNHG{07@6rB80s3Dgcz7u85mg^8)+LDSQ!|oyG-v#(U6;;l9^VCTSJ(nzA;dP2Hb{{
e%-q!ClEmBs+
-
-
-
diff --git a/归档/miniprogram/assets/icons/image.svg b/归档/miniprogram/assets/icons/image.svg
deleted file mode 100644
index 50ed9e6d..00000000
--- a/归档/miniprogram/assets/icons/image.svg
+++ /dev/null
@@ -1,5 +0,0 @@
-
diff --git a/归档/miniprogram/assets/icons/list.svg b/归档/miniprogram/assets/icons/list.svg
deleted file mode 100644
index 688326aa..00000000
--- a/归档/miniprogram/assets/icons/list.svg
+++ /dev/null
@@ -1,8 +0,0 @@
-
diff --git a/归档/miniprogram/assets/icons/match-active.png b/归档/miniprogram/assets/icons/match-active.png
deleted file mode 100644
index da62b4367dbc809c2e64a00821cce980ce3d1c30..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 907
zcmeAS@N?(olHy`uVBq!ia0vp^fgsGm3?%0U?qUH_3dtTpz6=aiY77hwEes65fIl8_s;5>q48lEBhUuMByV?@@LH~$
z@<0xIiKnkC`zuC17A7UP*Y34IpH79fui}4y
z^g1w!^=MUw^E|V|(UX6BltdS)i>2$mdl%_{%1->}Ed9Y};Is
zc6OR=`NyWs8Pj*4iGRAxnANyGHbCF?TGGm#)kTj#Y|FYFt^8un(cQbA?0w|yvdJ;4
zNG$ruwnc6}lcai%_9ab>cr7H~8F(zof1|&N==_fJJx9flG8V45q1q?Wy}ob%--gXU
zp2gMt|NE%Dy0FJpC#kScToCB=FVlaqurMBfv2Et&1He#FEpd$~Nl7e8wMs5Z1yT$~
z21aJO28OzZCLsnURt82^#wOYZ237_JE>jk5MbVI(pOTqYiCcr|NhwaC1`W6kC7HRY
b#U+Wk1-SJj-Lbv~)WhKE>gTe~DWM4f?pT9{
diff --git a/归档/miniprogram/assets/icons/match.png b/归档/miniprogram/assets/icons/match.png
deleted file mode 100644
index b15582e3720ae81bfd63a759fb0aaf1ac555fa51..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 725
zcmeAS@N?(olHy`uVBq!ia0vp^fgsGm0wfvQn)U)og=CK)Uj~LMH3o);76yi2K%s^g
z3=E|P3=FRl7#OT(FffQ0%-I!a1C(G&@^*J&_}|`tWSuh@
z85LUuoH!tai*Hc)U$J}IR+YzCt5x2dxNdsEo88}IZ6z2iRX#*6FrShYz!
zO#K(xcE0EKv;AMs_I&J{R#Sb}Bzc?kLM7dr1?w{+ySAJ0$LU|*BDytw&&H=KMB+b8
zeXc6L?8UOVk#bXV&cEFBR-tQSLVk$Do*o68C(83raX1N2KFHQ;e^e&&+Kq)%=S|YD
zcAg}^<@2qXlb^1X4YS*BcjV{a^v=ZJd=E-DI&5C=`{~|3#)`jZF3zZzTRdBTDnIA{
zg^C9g`~-9&zx*_lJi*zdYxJ~4*6~=#VvDSoO(yTN)!!^pI6C)Tp-I>NoTDeJ{dT1s
zJ83^RtjgNNe&$rGzY~*KG7PLTGAnxOV>Feiid7pjRkL$~A-t{l$NqsxsxqRQ%
zn-_{#1)RBaYnk?)r*WeBdAX}Cw(bt@;nDQk7&c*%NH(h`7c4B5qW&=*o0Bx}L0Kax
zmQ_n!BT7;dOH!?pi&B9UgOP!enXZAMuAxbYfr*uYk(IHDwt<0_fq~1EMO#rc
-
-
diff --git a/归档/miniprogram/assets/icons/my-active.png b/归档/miniprogram/assets/icons/my-active.png
deleted file mode 100644
index da62b4367dbc809c2e64a00821cce980ce3d1c30..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 907
zcmeAS@N?(olHy`uVBq!ia0vp^fgsGm3?%0U?qUH_3dtTpz6=aiY77hwEes65fIl8_s;5>q48lEBhUuMByV?@@LH~$
z@<0xIiKnkC`zuC17A7UP*Y34IpH79fui}4y
z^g1w!^=MUw^E|V|(UX6BltdS)i>2$mdl%_{%1->}Ed9Y};Is
zc6OR=`NyWs8Pj*4iGRAxnANyGHbCF?TGGm#)kTj#Y|FYFt^8un(cQbA?0w|yvdJ;4
zNG$ruwnc6}lcai%_9ab>cr7H~8F(zof1|&N==_fJJx9flG8V45q1q?Wy}ob%--gXU
zp2gMt|NE%Dy0FJpC#kScToCB=FVlaqurMBfv2Et&1He#FEpd$~Nl7e8wMs5Z1yT$~
z21aJO28OzZCLsnURt82^#wOYZ237_JE>jk5MbVI(pOTqYiCcr|NhwaC1`W6kC7HRY
b#U+Wk1-SJj-Lbv~)WhKE>gTe~DWM4f?pT9{
diff --git a/归档/miniprogram/assets/icons/my.png b/归档/miniprogram/assets/icons/my.png
deleted file mode 100644
index b15582e3720ae81bfd63a759fb0aaf1ac555fa51..0000000000000000000000000000000000000000
GIT binary patch
literal 0
HcmV?d00001
literal 725
zcmeAS@N?(olHy`uVBq!ia0vp^fgsGm0wfvQn)U)og=CK)Uj~LMH3o);76yi2K%s^g
z3=E|P3=FRl7#OT(FffQ0%-I!a1C(G&@^*J&_}|`tWSuh@
z85LUuoH!tai*Hc)U$J}IR+YzCt5x2dxNdsEo88}IZ6z2iRX#*6FrShYz!
zO#K(xcE0EKv;AMs_I&J{R#Sb}Bzc?kLM7dr1?w{+ySAJ0$LU|*BDytw&&H=KMB+b8
zeXc6L?8UOVk#bXV&cEFBR-tQSLVk$Do*o68C(83raX1N2KFHQ;e^e&&+Kq)%=S|YD
zcAg}^<@2qXlb^1X4YS*BcjV{a^v=ZJd=E-DI&5C=`{~|3#)`jZF3zZzTRdBTDnIA{
zg^C9g`~-9&zx*_lJi*zdYxJ~4*6~=#VvDSoO(yTN)!!^pI6C)Tp-I>NoTDeJ{dT1s
zJ83^RtjgNNe&$rGzY~*KG7PLTGAnxOV>Feiid7pjRkL$~A-t{l$NqsxsxqRQ%
zn-_{#1)RBaYnk?)r*WeBdAX}Cw(bt@;nDQk7&c*%NH(h`7c4B5qW&=*o0Bx}L0Kax
zmQ_n!BT7;dOH!?pi&B9UgOP!enXZAMuAxbYfr*uYk(IHDwt<0_fq~1EMO#rc
-
-
-
-
-
-
-
-
-
-
-
diff --git a/归档/miniprogram/assets/icons/settings.svg b/归档/miniprogram/assets/icons/settings.svg
deleted file mode 100644
index c7006ea8..00000000
--- a/归档/miniprogram/assets/icons/settings.svg
+++ /dev/null
@@ -1,4 +0,0 @@
-
diff --git a/归档/miniprogram/assets/icons/share.svg b/归档/miniprogram/assets/icons/share.svg
deleted file mode 100644
index 93179fc2..00000000
--- a/归档/miniprogram/assets/icons/share.svg
+++ /dev/null
@@ -1,7 +0,0 @@
-
diff --git a/归档/miniprogram/assets/icons/sparkles.svg b/归档/miniprogram/assets/icons/sparkles.svg
deleted file mode 100644
index e2a4461f..00000000
--- a/归档/miniprogram/assets/icons/sparkles.svg
+++ /dev/null
@@ -1,6 +0,0 @@
-
diff --git a/归档/miniprogram/assets/icons/user.svg b/归档/miniprogram/assets/icons/user.svg
deleted file mode 100644
index 8b190427..00000000
--- a/归档/miniprogram/assets/icons/user.svg
+++ /dev/null
@@ -1,4 +0,0 @@
-
diff --git a/归档/miniprogram/assets/icons/users.svg b/归档/miniprogram/assets/icons/users.svg
deleted file mode 100644
index 4816094b..00000000
--- a/归档/miniprogram/assets/icons/users.svg
+++ /dev/null
@@ -1,6 +0,0 @@
-
diff --git a/归档/miniprogram/assets/icons/wallet.svg b/归档/miniprogram/assets/icons/wallet.svg
deleted file mode 100644
index 6d431e54..00000000
--- a/归档/miniprogram/assets/icons/wallet.svg
+++ /dev/null
@@ -1,4 +0,0 @@
-
diff --git a/归档/miniprogram/components/icon/README.md b/归档/miniprogram/components/icon/README.md
deleted file mode 100644
index 34e394c8..00000000
--- a/归档/miniprogram/components/icon/README.md
+++ /dev/null
@@ -1,175 +0,0 @@
-# Icon 图标组件
-
-SVG 图标组件,参考 lucide-react 实现,用于在小程序中使用矢量图标。
-
-**技术实现**: 使用 Base64 编码的 SVG + image 组件(小程序不支持直接使用 SVG 标签)
-
----
-
-## 使用方法
-
-### 1. 在页面 JSON 中引入组件
-
-```json
-{
- "usingComponents": {
- "icon": "/components/icon/icon"
- }
-}
-```
-
-### 2. 在 WXML 中使用
-
-```xml
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-```
-
----
-
-## 属性说明
-
-| 属性 | 类型 | 默认值 | 说明 |
-|-----|------|--------|-----|
-| name | String | 'share' | 图标名称 |
-| size | Number | 48 | 图标大小(rpx) |
-| color | String | 'currentColor' | 图标颜色 |
-| customClass | String | '' | 自定义类名 |
-| customStyle | String | '' | 自定义样式 |
-
----
-
-## 可用图标
-
-| 图标名称 | 说明 | 对应 lucide-react |
-|---------|------|-------------------|
-| `share` | 分享 | `` |
-| `arrow-up-right` | 右上箭头 | `` |
-| `chevron-left` | 左箭头 | `` |
-| `search` | 搜索 | `` |
-| `heart` | 心形 | `` |
-
----
-
-## 添加新图标
-
-在 `icon.js` 的 `getSvgPath` 方法中添加新图标:
-
-```javascript
-getSvgPath(name) {
- const svgMap = {
- 'new-icon': '',
- // ... 其他图标
- }
- return svgMap[name] || ''
-}
-```
-
-**获取 SVG 代码**: 访问 [lucide.dev](https://lucide.dev) 搜索图标,复制 SVG 内容。
-**注意**: 颜色使用 `COLOR` 占位符,组件会自动替换。
-
----
-
-## 样式定制
-
-### 1. 使用 customClass
-
-```xml
-
-```
-
-```css
-.my-icon-class {
- opacity: 0.8;
-}
-```
-
-### 2. 使用 customStyle
-
-```xml
-
-```
-
----
-
-## 技术说明
-
-### 为什么使用 Base64 + image?
-
-1. **矢量图标**:任意缩放不失真
-2. **灵活着色**:通过 `COLOR` 占位符动态改变颜色
-3. **轻量级**:无需加载字体文件或外部图片
-4. **兼容性**:小程序不支持直接使用 SVG 标签,image 组件支持 Base64 SVG
-
-### 为什么不用字体图标?
-
-小程序对字体文件有限制,Base64 编码字体文件会增加包体积,SVG 图标更轻量。
-
-### 与 lucide-react 的对应关系
-
-- **lucide-react**: React 组件库,使用 SVG
-- **本组件**: 小程序自定义组件,也使用 SVG
-- **SVG path 数据**: 完全相同,从 lucide 官网复制
-
----
-
-## 示例
-
-### 悬浮分享按钮
-
-```xml
-
-```
-
-```css
-.fab-share {
- position: fixed;
- right: 32rpx;
- bottom: calc(120rpx + env(safe-area-inset-bottom));
- width: 96rpx;
- height: 96rpx;
- border-radius: 50%;
- background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%);
- display: flex;
- align-items: center;
- justify-content: center;
-}
-```
-
----
-
-## 扩展图标库
-
-可以继续添加更多 lucide-react 图标:
-
-- `star` - 星星
-- `wallet` - 钱包
-- `gift` - 礼物
-- `info` - 信息
-- `settings` - 设置
-- `user` - 用户
-- `book-open` - 打开的书
-- `eye` - 眼睛
-- `clock` - 时钟
-- `users` - 用户组
-
----
-
-**图标组件创建完成!** 🎉
diff --git a/归档/miniprogram/components/icon/icon.js b/归档/miniprogram/components/icon/icon.js
deleted file mode 100644
index b2dec23f..00000000
--- a/归档/miniprogram/components/icon/icon.js
+++ /dev/null
@@ -1,83 +0,0 @@
-// components/icon/icon.js
-Component({
- properties: {
- // 图标名称
- name: {
- type: String,
- value: 'share',
- observer: 'updateIcon'
- },
- // 图标大小(rpx)
- size: {
- type: Number,
- value: 48
- },
- // 图标颜色
- color: {
- type: String,
- value: '#ffffff',
- observer: 'updateIcon'
- },
- // 自定义类名
- customClass: {
- type: String,
- value: ''
- },
- // 自定义样式
- customStyle: {
- type: String,
- value: ''
- }
- },
-
- data: {
- svgData: ''
- },
-
- lifetimes: {
- attached() {
- this.updateIcon()
- }
- },
-
- methods: {
- // SVG 图标数据映射
- getSvgPath(name) {
- const svgMap = {
- 'share': '',
-
- 'arrow-up-right': '',
-
- 'chevron-left': '',
-
- 'search': '',
-
- 'heart': ''
- }
-
- return svgMap[name] || ''
- },
-
- // 更新图标
- updateIcon() {
- const { name, color } = this.data
- let svgString = this.getSvgPath(name)
-
- if (svgString) {
- // 替换颜色占位符
- svgString = svgString.replace(/COLOR/g, color)
-
- // 转换为 Base64 Data URL
- const svgData = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgString)}`
-
- this.setData({
- svgData: svgData
- })
- } else {
- this.setData({
- svgData: ''
- })
- }
- }
- }
-})
diff --git a/归档/miniprogram/components/icon/icon.json b/归档/miniprogram/components/icon/icon.json
deleted file mode 100644
index a89ef4db..00000000
--- a/归档/miniprogram/components/icon/icon.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "component": true,
- "usingComponents": {}
-}
diff --git a/归档/miniprogram/components/icon/icon.wxml b/归档/miniprogram/components/icon/icon.wxml
deleted file mode 100644
index b1c29a25..00000000
--- a/归档/miniprogram/components/icon/icon.wxml
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
- {{name}}
-
diff --git a/归档/miniprogram/components/icon/icon.wxss b/归档/miniprogram/components/icon/icon.wxss
deleted file mode 100644
index d12d2a0a..00000000
--- a/归档/miniprogram/components/icon/icon.wxss
+++ /dev/null
@@ -1,18 +0,0 @@
-/* components/icon/icon.wxss */
-.icon {
- display: inline-flex;
- align-items: center;
- justify-content: center;
- flex-shrink: 0;
-}
-
-.icon-image {
- display: block;
- width: 100%;
- height: 100%;
-}
-
-.icon-text {
- font-size: 24rpx;
- color: currentColor;
-}
diff --git a/归档/miniprogram/custom-tab-bar/index.js b/归档/miniprogram/custom-tab-bar/index.js
deleted file mode 100644
index 4acd9546..00000000
--- a/归档/miniprogram/custom-tab-bar/index.js
+++ /dev/null
@@ -1,153 +0,0 @@
-/**
- * Soul创业实验 - 自定义TabBar组件
- * 根据后台配置动态显示/隐藏"找伙伴"按钮
- */
-
-console.log('[TabBar] ===== 组件文件开始加载 =====')
-
-const app = getApp()
-console.log('[TabBar] App 对象:', app)
-
-Component({
- data: {
- selected: 0,
- color: '#8e8e93',
- selectedColor: '#00CED1',
- matchEnabled: false, // 找伙伴功能开关,默认关闭
- list: [
- {
- pagePath: '/pages/index/index',
- text: '首页',
- iconType: 'home'
- },
- {
- pagePath: '/pages/chapters/chapters',
- text: '目录',
- iconType: 'list'
- },
- {
- pagePath: '/pages/match/match',
- text: '找伙伴',
- iconType: 'match',
- isSpecial: true
- },
- {
- pagePath: '/pages/my/my',
- text: '我的',
- iconType: 'user'
- }
- ]
- },
-
- lifetimes: {
- attached() {
- console.log('[TabBar] Component attached 生命周期触发')
- this.loadFeatureConfig()
- },
- ready() {
- console.log('[TabBar] Component ready 生命周期触发')
- // 如果 attached 中没有成功加载,在 ready 中再次尝试
- if (this.data.matchEnabled === undefined || this.data.matchEnabled === null) {
- console.log('[TabBar] 在 ready 中重新加载配置')
- this.loadFeatureConfig()
- }
- }
- },
-
- // 页面加载时也调用(兼容性更好)
- attached() {
- console.log('[TabBar] attached() 方法触发')
- this.loadFeatureConfig()
- },
-
- methods: {
- // 加载功能配置
- async loadFeatureConfig() {
- try {
- console.log('[TabBar] 开始加载功能配置...')
- console.log('[TabBar] API地址:', app.globalData.baseUrl + '/api/miniprogram/config')
-
- // app.request 的第一个参数是 url 字符串,第二个参数是 options 对象
- const res = await app.request('/api/miniprogram/config', {
- method: 'GET'
- })
-
-
- // 兼容两种返回格式
- let matchEnabled = false
-
- if (res && res.success && res.features) {
- console.log('[TabBar] features配置:', JSON.stringify(res.features))
- matchEnabled = res.features.matchEnabled === true
- console.log('[TabBar] matchEnabled值:', matchEnabled)
- } else if (res && res.configs && res.configs.feature_config) {
- // 备用格式:从 configs.feature_config 读取
- console.log('[TabBar] 使用备用格式,从configs读取')
- matchEnabled = res.configs.feature_config.matchEnabled === true
- console.log('[TabBar] matchEnabled值:', matchEnabled)
- } else {
- console.log('[TabBar] ⚠️ 未找到features配置,使用默认值false')
- console.log('[TabBar] res对象keys:', Object.keys(res || {}))
- }
-
- this.setData({ matchEnabled }, () => {
- console.log('[TabBar] ✅ matchEnabled已设置为:', this.data.matchEnabled)
- // 配置加载完成后,根据当前路由设置选中状态
- this.updateSelected()
- })
-
- // 如果当前在找伙伴页面,但功能已关闭,跳转到首页
- if (!matchEnabled) {
- const pages = getCurrentPages()
- const currentPage = pages[pages.length - 1]
- if (currentPage && currentPage.route === 'pages/match/match') {
- console.log('[TabBar] 找伙伴功能已关闭,从match页面跳转到首页')
- wx.switchTab({ url: '/pages/index/index' })
- }
- }
- } catch (error) {
- console.log('[TabBar] ❌ 加载功能配置失败:', error)
- console.log('[TabBar] 错误详情:', error.message || error)
- // 默认关闭找伙伴功能
- this.setData({ matchEnabled: false }, () => {
- this.updateSelected()
- })
- }
- },
-
- // 根据当前路由更新选中状态
- updateSelected() {
- const pages = getCurrentPages()
- if (pages.length === 0) return
-
- const currentPage = pages[pages.length - 1]
- const route = currentPage.route
-
- let selected = 0
- const { matchEnabled } = this.data
-
- // 根据路由匹配对应的索引
- if (route === 'pages/index/index') {
- selected = 0
- } else if (route === 'pages/chapters/chapters') {
- selected = 1
- } else if (route === 'pages/match/match') {
- selected = 2
- } else if (route === 'pages/my/my') {
- selected = matchEnabled ? 3 : 2
- }
-
- this.setData({ selected })
- },
-
- switchTab(e) {
- const data = e.currentTarget.dataset
- const url = data.path
- const index = data.index
-
- if (this.data.selected === index) return
-
- wx.switchTab({ url })
- }
- }
-})
diff --git a/归档/miniprogram/custom-tab-bar/index.json b/归档/miniprogram/custom-tab-bar/index.json
deleted file mode 100644
index 467ce294..00000000
--- a/归档/miniprogram/custom-tab-bar/index.json
+++ /dev/null
@@ -1,3 +0,0 @@
-{
- "component": true
-}
diff --git a/归档/miniprogram/custom-tab-bar/index.wxml b/归档/miniprogram/custom-tab-bar/index.wxml
deleted file mode 100644
index 73369b2a..00000000
--- a/归档/miniprogram/custom-tab-bar/index.wxml
+++ /dev/null
@@ -1,47 +0,0 @@
-
-
-
-
-
-
-
-
-
- {{list[0].text}}
-
-
-
-
-
-
-
- {{list[1].text}}
-
-
-
-
-
-
-
- {{list[2].text}}
-
-
-
-
-
-
-
- {{list[3].text}}
-
-
diff --git a/归档/miniprogram/custom-tab-bar/index.wxss b/归档/miniprogram/custom-tab-bar/index.wxss
deleted file mode 100644
index 98036655..00000000
--- a/归档/miniprogram/custom-tab-bar/index.wxss
+++ /dev/null
@@ -1,121 +0,0 @@
-/**
- * Soul创业实验 - 自定义TabBar样式
- * 实现中间突出的"找伙伴"按钮
- */
-
-.tab-bar {
- position: fixed;
- bottom: 0;
- left: 0;
- right: 0;
- height: 100rpx;
- background: rgba(28, 28, 30, 0.95);
- backdrop-filter: blur(40rpx);
- -webkit-backdrop-filter: blur(40rpx);
- display: flex;
- align-items: flex-end;
- padding-bottom: env(safe-area-inset-bottom);
- z-index: 999;
-}
-
-/* 三个tab布局(找伙伴功能关闭时) */
-.tab-bar-three .tab-bar-item {
- flex: 1;
-}
-
-/* 四个tab布局(找伙伴功能开启时) */
-.tab-bar-four .tab-bar-item {
- flex: 1;
-}
-
-.tab-bar-border {
- position: absolute;
- top: 0;
- left: 0;
- right: 0;
- height: 1rpx;
- background: rgba(255, 255, 255, 0.05);
-}
-
-.tab-bar-item {
- flex: 1;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- padding: 10rpx 0 16rpx;
-}
-
-.icon-wrapper {
- width: 48rpx;
- height: 48rpx;
- display: flex;
- align-items: center;
- justify-content: center;
- margin-bottom: 4rpx;
-}
-
-.icon {
- width: 44rpx;
- height: 44rpx;
- display: flex;
- align-items: center;
- justify-content: center;
-}
-
-.tab-bar-text {
- font-size: 22rpx;
- line-height: 1;
-}
-
-/* ===== SVG 图标样式 ===== */
-.tab-icon {
- width: 48rpx;
- height: 48rpx;
- display: block;
- filter: brightness(0) saturate(100%) invert(60%) sepia(0%) saturate(0%) hue-rotate(0deg) brightness(95%) contrast(85%);
-}
-
-.tab-icon.icon-active {
- filter: brightness(0) saturate(100%) invert(72%) sepia(54%) saturate(2933%) hue-rotate(134deg) brightness(101%) contrast(101%);
-}
-
-
-/* ===== 找伙伴 - 中间特殊按钮 ===== */
-.special-item {
- position: relative;
- margin-top: -32rpx;
-}
-
-.special-button {
- width: 112rpx;
- height: 112rpx;
- border-radius: 50%;
- background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%);
- display: flex;
- align-items: center;
- justify-content: center;
- box-shadow: 0 8rpx 32rpx rgba(0, 206, 209, 0.4);
- margin-bottom: 4rpx;
- transition: all 0.2s ease;
-}
-
-.special-button:active {
- transform: scale(0.95);
-}
-
-.special-active {
- box-shadow: 0 8rpx 40rpx rgba(0, 206, 209, 0.6);
-}
-
-.special-text {
- margin-top: 4rpx;
-}
-
-/* ===== 找伙伴特殊按钮图标 ===== */
-.special-icon {
- width: 80rpx;
- height: 80rpx;
- display: block;
- filter: brightness(0) saturate(100%) invert(100%) sepia(0%) saturate(0%) hue-rotate(0deg) brightness(100%) contrast(100%);
-}
diff --git a/归档/miniprogram/pages/about/about.js b/归档/miniprogram/pages/about/about.js
deleted file mode 100644
index 8f19cc60..00000000
--- a/归档/miniprogram/pages/about/about.js
+++ /dev/null
@@ -1,81 +0,0 @@
-/**
- * Soul创业派对 - 关于作者页
- * 开发: 卡若
- */
-const app = getApp()
-
-Page({
- data: {
- statusBarHeight: 44,
- author: {
- name: '卡若',
- avatar: 'K',
- title: 'Soul派对房主理人 · 私域运营专家',
- bio: '每天早上6点到9点,在Soul派对房分享真实的创业故事。专注私域运营与项目变现,用"云阿米巴"模式帮助创业者构建可持续的商业体系。本书记录了62个真实商业案例,涵盖电商、内容、传统行业等多个领域。',
- stats: [
- { label: '商业案例', value: '62' },
- { label: '连续直播', value: '365天' },
- { label: '派对分享', value: '1000+' }
- ],
- // 联系方式已移至后台配置
- contact: null,
- highlights: [
- '5年私域运营经验',
- '帮助100+品牌从0到1增长',
- '连续创业者,擅长商业模式设计'
- ]
- },
- bookInfo: {
- title: '一场Soul的创业实验',
- totalChapters: 62,
- parts: [
- { name: '真实的人', chapters: 10 },
- { name: '真实的行业', chapters: 15 },
- { name: '真实的错误', chapters: 9 },
- { name: '真实的赚钱', chapters: 20 },
- { name: '真实的社会', chapters: 9 }
- ],
- price: 9.9
- }
- },
-
- onLoad() {
- this.setData({
- statusBarHeight: app.globalData.statusBarHeight
- })
- this.loadBookStats()
- },
-
- // 加载书籍统计
- async loadBookStats() {
- try {
- const res = await app.request('/api/miniprogram/book/stats')
- if (res && res.success) {
- this.setData({
- 'bookInfo.totalChapters': res.data?.totalChapters || 62,
- 'author.stats': [
- { label: '商业案例', value: String(res.data?.totalChapters || 62) },
- { label: '连续直播', value: '365天' },
- { label: '派对分享', value: '1000+' }
- ]
- })
- }
- } catch (e) {
- console.log('[About] 加载书籍统计失败,使用默认值')
- }
- },
-
- // 联系方式功能已禁用
- copyWechat() {
- wx.showToast({ title: '请在派对房联系作者', icon: 'none' })
- },
-
- callPhone() {
- wx.showToast({ title: '请在派对房联系作者', icon: 'none' })
- },
-
- // 返回
- goBack() {
- wx.navigateBack()
- }
-})
diff --git a/归档/miniprogram/pages/about/about.json b/归档/miniprogram/pages/about/about.json
deleted file mode 100644
index e90e9960..00000000
--- a/归档/miniprogram/pages/about/about.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "usingComponents": {},
- "navigationStyle": "custom"
-}
diff --git a/归档/miniprogram/pages/about/about.wxml b/归档/miniprogram/pages/about/about.wxml
deleted file mode 100644
index 598e9464..00000000
--- a/归档/miniprogram/pages/about/about.wxml
+++ /dev/null
@@ -1,75 +0,0 @@
-
-
-
- ←
- 关于作者
-
-
-
-
-
-
-
- {{author.avatar}}
- {{author.name}}
- {{author.title}}
- {{author.bio}}
-
-
-
-
- {{item.value}}
- {{item.label}}
-
-
-
-
-
-
- ✓
- {{item}}
-
-
-
-
-
-
- 📚 {{bookInfo.title}}
-
-
- {{bookInfo.totalChapters}}
- 篇章节
-
-
- 5
- 大篇章
-
-
- ¥{{bookInfo.price}}
- 全书价格
-
-
-
-
- {{item.name}}
- {{item.chapters}}节
-
-
-
-
-
-
- 联系作者
-
- 🎉
-
- Soul派对房
- 每天早上6-9点开播
-
-
-
- 在Soul App搜索"创业实验"或"卡若",加入派对房直接交流
-
-
-
-
diff --git a/归档/miniprogram/pages/about/about.wxss b/归档/miniprogram/pages/about/about.wxss
deleted file mode 100644
index 337aa041..00000000
--- a/归档/miniprogram/pages/about/about.wxss
+++ /dev/null
@@ -1,40 +0,0 @@
-.page { min-height: 100vh; background: #000; }
-.nav-bar { position: fixed; top: 0; left: 0; right: 0; z-index: 100; background: rgba(0,0,0,0.9); backdrop-filter: blur(40rpx); display: flex; align-items: center; justify-content: space-between; padding: 0 32rpx; height: 88rpx; }
-.nav-back { width: 72rpx; height: 72rpx; background: #1c1c1e; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 32rpx; color: #fff; }
-.nav-title { font-size: 36rpx; font-weight: 600; color: #00CED1; }
-.nav-placeholder { width: 72rpx; }
-.content { padding: 32rpx; }
-.author-card { background: linear-gradient(135deg, #1c1c1e 0%, #2c2c2e 100%); border-radius: 32rpx; padding: 48rpx; text-align: center; margin-bottom: 24rpx; border: 2rpx solid rgba(0,206,209,0.2); }
-.author-avatar { width: 160rpx; height: 160rpx; border-radius: 50%; background: linear-gradient(135deg, #00CED1, #20B2AA); display: flex; align-items: center; justify-content: center; margin: 0 auto 24rpx; font-size: 64rpx; color: #fff; font-weight: 700; border: 4rpx solid rgba(0,206,209,0.3); }
-.author-name { font-size: 40rpx; font-weight: 700; color: #fff; display: block; margin-bottom: 8rpx; }
-.author-title { font-size: 26rpx; color: #00CED1; display: block; margin-bottom: 24rpx; }
-.author-bio { font-size: 26rpx; color: rgba(255,255,255,0.7); line-height: 1.8; display: block; margin-bottom: 32rpx; }
-.stats-row { display: flex; justify-content: space-around; padding-top: 32rpx; border-top: 2rpx solid rgba(255,255,255,0.1); }
-.stat-item { text-align: center; }
-.stat-value { font-size: 36rpx; font-weight: 700; color: #00CED1; display: block; }
-.stat-label { font-size: 22rpx; color: rgba(255,255,255,0.5); }
-.contact-card { background: #1c1c1e; border-radius: 32rpx; padding: 32rpx; }
-.card-title { font-size: 28rpx; font-weight: 600; color: #fff; display: block; margin-bottom: 24rpx; }
-.contact-item { display: flex; align-items: center; gap: 24rpx; padding: 24rpx; background: rgba(255,255,255,0.05); border-radius: 16rpx; margin-bottom: 16rpx; }
-.contact-item:last-child { margin-bottom: 0; }
-.contact-icon { font-size: 40rpx; }
-.contact-info { flex: 1; }
-.contact-label { font-size: 22rpx; color: rgba(255,255,255,0.5); display: block; }
-.contact-value { font-size: 28rpx; color: #fff; }
-.contact-btn { padding: 12rpx 24rpx; background: rgba(0,206,209,0.2); color: #00CED1; font-size: 24rpx; border-radius: 16rpx; }
-
-/* 亮点标签 */
-.highlights { display: flex; flex-wrap: wrap; gap: 16rpx; margin-top: 32rpx; padding-top: 24rpx; border-top: 2rpx solid rgba(255,255,255,0.1); justify-content: center; }
-.highlight-tag { display: flex; align-items: center; gap: 8rpx; padding: 12rpx 24rpx; background: rgba(0,206,209,0.15); border-radius: 24rpx; font-size: 24rpx; color: rgba(255,255,255,0.8); }
-.tag-icon { color: #00CED1; font-size: 22rpx; }
-
-/* 书籍信息卡片 */
-.book-info-card { background: #1c1c1e; border-radius: 32rpx; padding: 32rpx; margin-bottom: 24rpx; }
-.book-stats { display: flex; justify-content: space-around; padding: 24rpx 0; margin: 16rpx 0; background: rgba(0,0,0,0.3); border-radius: 16rpx; }
-.book-stat { text-align: center; }
-.book-stat-value { font-size: 36rpx; font-weight: 700; color: #FFD700; display: block; }
-.book-stat-label { font-size: 22rpx; color: rgba(255,255,255,0.5); }
-.parts-list { display: flex; flex-wrap: wrap; gap: 12rpx; margin-top: 16rpx; }
-.part-item { display: flex; align-items: center; gap: 8rpx; padding: 12rpx 20rpx; background: rgba(255,255,255,0.05); border-radius: 12rpx; }
-.part-name { font-size: 24rpx; color: rgba(255,255,255,0.8); }
-.part-chapters { font-size: 22rpx; color: #00CED1; }
diff --git a/归档/miniprogram/pages/addresses/addresses.js b/归档/miniprogram/pages/addresses/addresses.js
deleted file mode 100644
index 685528cf..00000000
--- a/归档/miniprogram/pages/addresses/addresses.js
+++ /dev/null
@@ -1,123 +0,0 @@
-/**
- * 收货地址列表页
- * 参考 Next.js: app/view/my/addresses/page.tsx
- */
-
-const app = getApp()
-
-Page({
- data: {
- statusBarHeight: 44,
- isLoggedIn: false,
- addressList: [],
- loading: true
- },
-
- onLoad() {
- this.setData({
- statusBarHeight: app.globalData.statusBarHeight || 44
- })
- this.checkLogin()
- },
-
- onShow() {
- if (this.data.isLoggedIn) {
- this.loadAddresses()
- }
- },
-
- // 检查登录状态
- checkLogin() {
- const isLoggedIn = app.globalData.isLoggedIn
- const userId = app.globalData.userInfo?.id
-
- if (!isLoggedIn || !userId) {
- wx.showModal({
- title: '需要登录',
- content: '请先登录后再管理收货地址',
- confirmText: '去登录',
- success: (res) => {
- if (res.confirm) {
- wx.switchTab({ url: '/pages/my/my' })
- } else {
- wx.navigateBack()
- }
- }
- })
- return
- }
-
- this.setData({ isLoggedIn: true })
- this.loadAddresses()
- },
-
- // 加载地址列表
- async loadAddresses() {
- const userId = app.globalData.userInfo?.id
- if (!userId) return
-
- this.setData({ loading: true })
-
- try {
- const res = await app.request(`/api/miniprogram/user/addresses?userId=${userId}`)
- if (res.success && res.list) {
- this.setData({
- addressList: res.list,
- loading: false
- })
- } else {
- this.setData({ addressList: [], loading: false })
- }
- } catch (e) {
- console.error('加载地址列表失败:', e)
- this.setData({ loading: false })
- wx.showToast({ title: '加载失败', icon: 'none' })
- }
- },
-
- // 编辑地址
- editAddress(e) {
- const id = e.currentTarget.dataset.id
- wx.navigateTo({ url: `/pages/addresses/edit?id=${id}` })
- },
-
- // 删除地址
- deleteAddress(e) {
- const id = e.currentTarget.dataset.id
-
- wx.showModal({
- title: '确认删除',
- content: '确定要删除该收货地址吗?',
- confirmColor: '#FF3B30',
- success: async (res) => {
- if (res.confirm) {
- try {
- const result = await app.request(`/api/miniprogram/user/addresses/${id}`, {
- method: 'DELETE'
- })
-
- if (result.success) {
- wx.showToast({ title: '删除成功', icon: 'success' })
- this.loadAddresses()
- } else {
- wx.showToast({ title: result.message || '删除失败', icon: 'none' })
- }
- } catch (e) {
- console.error('删除地址失败:', e)
- wx.showToast({ title: '删除失败', icon: 'none' })
- }
- }
- }
- })
- },
-
- // 新增地址
- addAddress() {
- wx.navigateTo({ url: '/pages/addresses/edit' })
- },
-
- // 返回
- goBack() {
- wx.navigateBack()
- }
-})
diff --git a/归档/miniprogram/pages/addresses/addresses.json b/归档/miniprogram/pages/addresses/addresses.json
deleted file mode 100644
index 2e45b65e..00000000
--- a/归档/miniprogram/pages/addresses/addresses.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "usingComponents": {},
- "navigationStyle": "custom",
- "enablePullDownRefresh": false
-}
diff --git a/归档/miniprogram/pages/addresses/addresses.wxml b/归档/miniprogram/pages/addresses/addresses.wxml
deleted file mode 100644
index cec2ef6e..00000000
--- a/归档/miniprogram/pages/addresses/addresses.wxml
+++ /dev/null
@@ -1,66 +0,0 @@
-
-
-
-
-
- ‹
-
- 收货地址
-
-
-
-
-
-
-
- 加载中...
-
-
-
-
- 📍
- 暂无收货地址
- 点击下方按钮添加
-
-
-
-
-
-
- {{item.fullAddress}}
-
-
- ✏️
- 编辑
-
-
- 🗑️
- 删除
-
-
-
-
-
-
-
- ➕
- 新增收货地址
-
-
-
diff --git a/归档/miniprogram/pages/addresses/addresses.wxss b/归档/miniprogram/pages/addresses/addresses.wxss
deleted file mode 100644
index 9ff21637..00000000
--- a/归档/miniprogram/pages/addresses/addresses.wxss
+++ /dev/null
@@ -1,217 +0,0 @@
-/**
- * 收货地址列表页样式
- */
-
-.page {
- min-height: 100vh;
- background: #000000;
- padding-bottom: 200rpx;
-}
-
-/* ===== 导航栏 ===== */
-.nav-bar {
- position: fixed;
- top: 0;
- left: 0;
- right: 0;
- z-index: 100;
- background: rgba(0, 0, 0, 0.9);
- backdrop-filter: blur(40rpx);
- border-bottom: 1rpx solid rgba(255, 255, 255, 0.05);
-}
-
-.nav-bar {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 0 32rpx;
- height: 88rpx;
-}
-
-.nav-back {
- width: 64rpx;
- height: 64rpx;
- border-radius: 50%;
- background: rgba(255, 255, 255, 0.1);
- display: flex;
- align-items: center;
- justify-content: center;
-}
-
-.nav-back:active {
- background: rgba(255, 255, 255, 0.15);
-}
-
-.back-icon {
- font-size: 48rpx;
- color: #ffffff;
- line-height: 1;
-}
-
-.nav-title {
- flex: 1;
- text-align: center;
- font-size: 36rpx;
- font-weight: 600;
- color: #ffffff;
-}
-
-.nav-placeholder {
- width: 64rpx;
-}
-
-/* ===== 内容区 ===== */
-.content {
- padding: 32rpx;
-}
-
-/* ===== 加载状态 ===== */
-.loading-state {
- padding: 240rpx 0;
- text-align: center;
-}
-
-.loading-text {
- font-size: 28rpx;
- color: rgba(255, 255, 255, 0.4);
-}
-
-/* ===== 空状态 ===== */
-.empty-state {
- padding: 240rpx 0;
- text-align: center;
- display: flex;
- flex-direction: column;
- align-items: center;
-}
-
-.empty-icon {
- font-size: 96rpx;
- margin-bottom: 24rpx;
- opacity: 0.3;
-}
-
-.empty-text {
- font-size: 28rpx;
- color: rgba(255, 255, 255, 0.6);
- margin-bottom: 16rpx;
-}
-
-.empty-tip {
- font-size: 24rpx;
- color: rgba(255, 255, 255, 0.4);
-}
-
-/* ===== 地址列表 ===== */
-.address-list {
- margin-bottom: 24rpx;
-}
-
-.address-card {
- background: #1c1c1e;
- border-radius: 24rpx;
- border: 2rpx solid rgba(255, 255, 255, 0.05);
- padding: 32rpx;
- margin-bottom: 24rpx;
-}
-
-/* 地址头部 */
-.address-header {
- display: flex;
- align-items: center;
- gap: 16rpx;
- margin-bottom: 16rpx;
-}
-
-.receiver-name {
- font-size: 32rpx;
- font-weight: 600;
- color: #ffffff;
-}
-
-.receiver-phone {
- font-size: 28rpx;
- color: rgba(255, 255, 255, 0.5);
-}
-
-.default-tag {
- font-size: 22rpx;
- color: #00CED1;
- background: rgba(0, 206, 209, 0.2);
- padding: 6rpx 16rpx;
- border-radius: 8rpx;
- margin-left: auto;
-}
-
-/* 地址文本 */
-.address-text {
- font-size: 28rpx;
- color: rgba(255, 255, 255, 0.6);
- line-height: 1.6;
- display: block;
- margin-bottom: 24rpx;
- padding-bottom: 24rpx;
- border-bottom: 2rpx solid rgba(255, 255, 255, 0.05);
-}
-
-/* 操作按钮 */
-.address-actions {
- display: flex;
- justify-content: flex-end;
- gap: 32rpx;
-}
-
-.action-btn {
- display: flex;
- align-items: center;
- gap: 8rpx;
- padding: 8rpx 0;
-}
-
-.action-btn:active {
- opacity: 0.6;
-}
-
-.edit-btn {
- color: #00CED1;
-}
-
-.delete-btn {
- color: #FF3B30;
-}
-
-.action-icon {
- font-size: 28rpx;
-}
-
-.action-text {
- font-size: 28rpx;
-}
-
-/* ===== 新增按钮 ===== */
-.add-btn {
- display: flex;
- align-items: center;
- justify-content: center;
- gap: 16rpx;
- padding: 32rpx;
- background: #00CED1;
- border-radius: 24rpx;
- font-weight: 600;
- margin-top: 48rpx;
-}
-
-.add-btn:active {
- opacity: 0.8;
- transform: scale(0.98);
-}
-
-.add-icon {
- font-size: 36rpx;
- color: #000000;
-}
-
-.add-text {
- font-size: 32rpx;
- color: #000000;
-}
diff --git a/归档/miniprogram/pages/addresses/edit.js b/归档/miniprogram/pages/addresses/edit.js
deleted file mode 100644
index 9542c1dc..00000000
--- a/归档/miniprogram/pages/addresses/edit.js
+++ /dev/null
@@ -1,201 +0,0 @@
-/**
- * 地址编辑页(新增/编辑)
- * 参考 Next.js: app/view/my/addresses/[id]/page.tsx
- */
-
-const app = getApp()
-
-Page({
- data: {
- statusBarHeight: 44,
- isEdit: false, // 是否为编辑模式
- addressId: null,
-
- // 表单数据
- name: '',
- phone: '',
- province: '',
- city: '',
- district: '',
- detail: '',
- isDefault: false,
-
- // 地区选择器
- region: [],
-
- saving: false
- },
-
- onLoad(options) {
- this.setData({
- statusBarHeight: app.globalData.statusBarHeight || 44
- })
-
- // 如果有 id 参数,则为编辑模式
- if (options.id) {
- this.setData({
- isEdit: true,
- addressId: options.id
- })
- this.loadAddress(options.id)
- }
- },
-
- // 加载地址详情(编辑模式)
- async loadAddress(id) {
- wx.showLoading({ title: '加载中...', mask: true })
-
- try {
- const res = await app.request(`/api/miniprogram/user/addresses/${id}`)
- if (res.success && res.data) {
- const addr = res.data
- this.setData({
- name: addr.name || '',
- phone: addr.phone || '',
- province: addr.province || '',
- city: addr.city || '',
- district: addr.district || '',
- detail: addr.detail || '',
- isDefault: addr.isDefault || false,
- region: [addr.province, addr.city, addr.district]
- })
- } else {
- wx.showToast({ title: '加载失败', icon: 'none' })
- }
- } catch (e) {
- console.error('加载地址详情失败:', e)
- wx.showToast({ title: '加载失败', icon: 'none' })
- } finally {
- wx.hideLoading()
- }
- },
-
- // 表单输入
- onNameInput(e) {
- this.setData({ name: e.detail.value })
- },
-
- onPhoneInput(e) {
- this.setData({ phone: e.detail.value.replace(/\D/g, '').slice(0, 11) })
- },
-
- onDetailInput(e) {
- this.setData({ detail: e.detail.value })
- },
-
- // 地区选择
- onRegionChange(e) {
- const region = e.detail.value
- this.setData({
- region,
- province: region[0],
- city: region[1],
- district: region[2]
- })
- },
-
- // 切换默认地址
- onDefaultChange(e) {
- this.setData({ isDefault: e.detail.value })
- },
-
- // 表单验证
- validateForm() {
- const { name, phone, province, city, district, detail } = this.data
-
- if (!name || name.trim().length === 0) {
- wx.showToast({ title: '请输入收货人姓名', icon: 'none' })
- return false
- }
-
- if (!phone || phone.length !== 11) {
- wx.showToast({ title: '请输入正确的手机号', icon: 'none' })
- return false
- }
-
- if (!province || !city || !district) {
- wx.showToast({ title: '请选择省市区', icon: 'none' })
- return false
- }
-
- if (!detail || detail.trim().length === 0) {
- wx.showToast({ title: '请输入详细地址', icon: 'none' })
- return false
- }
-
- return true
- },
-
- // 保存地址
- async saveAddress() {
- if (!this.validateForm()) return
- if (this.data.saving) return
-
- this.setData({ saving: true })
- wx.showLoading({ title: '保存中...', mask: true })
-
- const { isEdit, addressId, name, phone, province, city, district, detail, isDefault } = this.data
- const userId = app.globalData.userInfo?.id
-
- if (!userId) {
- wx.hideLoading()
- wx.showToast({ title: '请先登录', icon: 'none' })
- this.setData({ saving: false })
- return
- }
-
- const addressData = {
- userId,
- name,
- phone,
- province,
- city,
- district,
- detail,
- fullAddress: `${province}${city}${district}${detail}`,
- isDefault
- }
-
- try {
- let res
- if (isEdit) {
- // 编辑模式 - PUT 请求
- res = await app.request(`/api/miniprogram/user/addresses/${addressId}`, {
- method: 'PUT',
- data: addressData
- })
- } else {
- // 新增模式 - POST 请求
- res = await app.request('/api/miniprogram/user/addresses', {
- method: 'POST',
- data: addressData
- })
- }
-
- if (res.success) {
- wx.hideLoading()
- wx.showToast({
- title: isEdit ? '保存成功' : '添加成功',
- icon: 'success'
- })
- setTimeout(() => {
- wx.navigateBack()
- }, 1500)
- } else {
- wx.hideLoading()
- wx.showToast({ title: res.message || '保存失败', icon: 'none' })
- this.setData({ saving: false })
- }
- } catch (e) {
- console.error('保存地址失败:', e)
- wx.hideLoading()
- wx.showToast({ title: '保存失败', icon: 'none' })
- this.setData({ saving: false })
- }
- },
-
- // 返回
- goBack() {
- wx.navigateBack()
- }
-})
diff --git a/归档/miniprogram/pages/addresses/edit.json b/归档/miniprogram/pages/addresses/edit.json
deleted file mode 100644
index 2e45b65e..00000000
--- a/归档/miniprogram/pages/addresses/edit.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "usingComponents": {},
- "navigationStyle": "custom",
- "enablePullDownRefresh": false
-}
diff --git a/归档/miniprogram/pages/addresses/edit.wxml b/归档/miniprogram/pages/addresses/edit.wxml
deleted file mode 100644
index c5429207..00000000
--- a/归档/miniprogram/pages/addresses/edit.wxml
+++ /dev/null
@@ -1,101 +0,0 @@
-
-
-
-
-
- ‹
-
- {{isEdit ? '编辑地址' : '新增地址'}}
-
-
-
-
-
-
-
-
-
- 👤
- 收货人
-
-
-
-
-
-
-
- 📱
- 手机号
-
-
-
-
-
-
-
- 📍
- 所在地区
-
-
-
- {{province || city || district ? province + ' ' + city + ' ' + district : '请选择省市区'}}
-
-
-
-
-
-
-
- 🏠
- 详细地址
-
-
-
-
-
-
-
- ⭐
- 设为默认地址
-
-
-
-
-
-
-
- {{saving ? '保存中...' : '保存'}}
-
-
-
diff --git a/归档/miniprogram/pages/addresses/edit.wxss b/归档/miniprogram/pages/addresses/edit.wxss
deleted file mode 100644
index 1045a287..00000000
--- a/归档/miniprogram/pages/addresses/edit.wxss
+++ /dev/null
@@ -1,186 +0,0 @@
-/**
- * 地址编辑页样式
- */
-
-.page {
- min-height: 100vh;
- background: #000000;
- padding-bottom: 200rpx;
-}
-
-/* ===== 导航栏 ===== */
-.nav-bar {
- position: fixed;
- top: 0;
- left: 0;
- right: 0;
- z-index: 100;
- background: rgba(0, 0, 0, 0.9);
- backdrop-filter: blur(40rpx);
- border-bottom: 1rpx solid rgba(255, 255, 255, 0.05);
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 0 32rpx;
- height: 88rpx;
-}
-
-.nav-back {
- width: 64rpx;
- height: 64rpx;
- border-radius: 50%;
- background: rgba(255, 255, 255, 0.1);
- display: flex;
- align-items: center;
- justify-content: center;
-}
-
-.nav-back:active {
- background: rgba(255, 255, 255, 0.15);
-}
-
-.back-icon {
- font-size: 48rpx;
- color: #ffffff;
- line-height: 1;
-}
-
-.nav-title {
- flex: 1;
- text-align: center;
- font-size: 36rpx;
- font-weight: 600;
- color: #ffffff;
-}
-
-.nav-placeholder {
- width: 64rpx;
-}
-
-/* ===== 内容区 ===== */
-.content {
- padding: 32rpx;
-}
-
-/* ===== 表单卡片 ===== */
-.form-card {
- background: #1c1c1e;
- border-radius: 32rpx;
- border: 2rpx solid rgba(255, 255, 255, 0.05);
- padding: 32rpx;
- margin-bottom: 32rpx;
-}
-
-/* 表单项 */
-.form-item {
- margin-bottom: 32rpx;
-}
-
-.form-item:last-child {
- margin-bottom: 0;
-}
-
-.form-label {
- display: flex;
- align-items: center;
- gap: 12rpx;
- margin-bottom: 16rpx;
-}
-
-.label-icon {
- font-size: 28rpx;
-}
-
-.label-text {
- font-size: 28rpx;
- color: rgba(255, 255, 255, 0.7);
-}
-
-/* 输入框 */
-.form-input {
- width: 100%;
- padding: 24rpx 32rpx;
- background: rgba(0, 0, 0, 0.3);
- border: 2rpx solid rgba(255, 255, 255, 0.1);
- border-radius: 24rpx;
- color: #ffffff;
- font-size: 28rpx;
-}
-
-.form-input:focus {
- border-color: rgba(0, 206, 209, 0.5);
-}
-
-.input-placeholder {
- color: rgba(255, 255, 255, 0.3);
-}
-
-/* 地区选择器 */
-.region-picker {
- width: 100%;
- padding: 24rpx 32rpx;
- background: rgba(0, 0, 0, 0.3);
- border: 2rpx solid rgba(255, 255, 255, 0.1);
- border-radius: 24rpx;
-}
-
-.picker-value {
- color: #ffffff;
- font-size: 28rpx;
-}
-
-.picker-value:empty::before {
- content: '请选择省市区';
- color: rgba(255, 255, 255, 0.3);
-}
-
-/* 多行文本框 */
-.form-textarea {
- width: 100%;
- padding: 24rpx 32rpx;
- background: rgba(0, 0, 0, 0.3);
- border: 2rpx solid rgba(255, 255, 255, 0.1);
- border-radius: 24rpx;
- color: #ffffff;
- font-size: 28rpx;
- min-height: 160rpx;
- line-height: 1.6;
-}
-
-.form-textarea:focus {
- border-color: rgba(0, 206, 209, 0.5);
-}
-
-/* 开关项 */
-.form-switch {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 16rpx 0;
-}
-
-.form-switch .form-label {
- margin-bottom: 0;
-}
-
-/* ===== 保存按钮 ===== */
-.save-btn {
- padding: 32rpx;
- background: #00CED1;
- border-radius: 24rpx;
- text-align: center;
- font-size: 32rpx;
- font-weight: 600;
- color: #000000;
- margin-top: 48rpx;
-}
-
-.save-btn:active {
- opacity: 0.8;
- transform: scale(0.98);
-}
-
-.btn-disabled {
- opacity: 0.5;
- pointer-events: none;
-}
diff --git a/归档/miniprogram/pages/agreement/agreement.js b/归档/miniprogram/pages/agreement/agreement.js
deleted file mode 100644
index aedd4c68..00000000
--- a/归档/miniprogram/pages/agreement/agreement.js
+++ /dev/null
@@ -1,21 +0,0 @@
-/**
- * Soul创业派对 - 用户协议
- * 审核要求:登录前可点击《用户协议》查看完整内容
- */
-const app = getApp()
-
-Page({
- data: {
- statusBarHeight: 44
- },
-
- onLoad() {
- this.setData({
- statusBarHeight: app.globalData.statusBarHeight || 44
- })
- },
-
- goBack() {
- wx.navigateBack()
- }
-})
diff --git a/归档/miniprogram/pages/agreement/agreement.json b/归档/miniprogram/pages/agreement/agreement.json
deleted file mode 100644
index 9fff5b87..00000000
--- a/归档/miniprogram/pages/agreement/agreement.json
+++ /dev/null
@@ -1 +0,0 @@
-{"usingComponents":{},"navigationStyle":"custom","navigationBarTitleText":"用户协议"}
diff --git a/归档/miniprogram/pages/agreement/agreement.wxml b/归档/miniprogram/pages/agreement/agreement.wxml
deleted file mode 100644
index 4060b50c..00000000
--- a/归档/miniprogram/pages/agreement/agreement.wxml
+++ /dev/null
@@ -1,37 +0,0 @@
-
-
-
- ←
- 用户协议
-
-
-
-
-
-
- Soul创业实验 用户服务协议
- 更新日期:以小程序内展示为准
-
- 一、接受条款
- 欢迎使用 Soul创业实验 小程序。使用本服务即表示您已阅读、理解并同意受本协议约束。若不同意,请勿使用本服务。
-
- 二、服务说明
- 本小程序提供《一场Soul的创业实验》等数字内容阅读、推广与相关服务。我们保留变更、中断或终止部分或全部服务的权利。
-
- 三、用户行为规范
- 您应合法、合规使用本服务,不得利用本服务从事违法违规活动,不得侵犯他人权益。违规行为可能导致账号限制或追究责任。
-
- 四、知识产权
- 本小程序内全部内容(包括但不限于文字、图片、音频、视频)的知识产权归本小程序或权利人所有,未经授权不得复制、传播或用于商业用途。
-
- 五、免责与限制
- 在法律允许范围内,因网络、设备或不可抗力导致的服务中断或数据丢失,我们尽力减少损失但不承担超出法律规定的责任。
-
- 六、协议变更
- 我们可能适时修订本协议,修订后将在小程序内公示。若您继续使用服务,即视为接受修订后的协议。
-
- 七、联系我们
- 如有疑问,请通过小程序内「关于作者」或 Soul 派对房与我们联系。
-
-
-
diff --git a/归档/miniprogram/pages/agreement/agreement.wxss b/归档/miniprogram/pages/agreement/agreement.wxss
deleted file mode 100644
index 08fadc43..00000000
--- a/归档/miniprogram/pages/agreement/agreement.wxss
+++ /dev/null
@@ -1,11 +0,0 @@
-.page { min-height: 100vh; background: #000; }
-.nav-bar { position: fixed; top: 0; left: 0; right: 0; z-index: 100; background: rgba(0,0,0,0.95); display: flex; align-items: center; justify-content: space-between; padding: 0 32rpx; height: 88rpx; }
-.nav-back { width: 72rpx; height: 72rpx; background: #1c1c1e; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 32rpx; color: #fff; }
-.nav-title { font-size: 36rpx; font-weight: 600; color: #00CED1; }
-.nav-placeholder { width: 72rpx; }
-.content { height: calc(100vh - 132rpx); padding: 32rpx; box-sizing: border-box; }
-.doc-card { background: #1c1c1e; border-radius: 24rpx; padding: 40rpx; border: 2rpx solid rgba(0,206,209,0.2); }
-.doc-title { font-size: 34rpx; font-weight: 700; color: #fff; display: block; margin-bottom: 16rpx; }
-.doc-update { font-size: 24rpx; color: rgba(255,255,255,0.5); display: block; margin-bottom: 32rpx; }
-.doc-section { font-size: 28rpx; font-weight: 600; color: #00CED1; display: block; margin: 24rpx 0 12rpx; }
-.doc-p { font-size: 26rpx; color: rgba(255,255,255,0.85); line-height: 1.75; display: block; margin-bottom: 16rpx; }
diff --git a/归档/miniprogram/pages/chapters/chapters.js b/归档/miniprogram/pages/chapters/chapters.js
deleted file mode 100644
index d002ed2a..00000000
--- a/归档/miniprogram/pages/chapters/chapters.js
+++ /dev/null
@@ -1,298 +0,0 @@
-/**
- * Soul创业派对 - 目录页
- * 开发: 卡若
- * 技术支持: 存客宝
- * 数据: 完整真实文章标题
- */
-
-const app = getApp()
-
-Page({
- data: {
- // 系统信息
- statusBarHeight: 44,
- navBarHeight: 88,
-
- // 用户状态
- isLoggedIn: false,
- hasFullBook: false,
- purchasedSections: [],
-
- // 书籍数据 - 完整真实标题
- totalSections: 62,
- bookData: [
- {
- id: 'part-1',
- number: '一',
- title: '真实的人',
- subtitle: '人与人之间的底层逻辑',
- chapters: [
- {
- id: 'chapter-1',
- title: '第1章|人与人之间的底层逻辑',
- sections: [
- { id: '1.1', title: '荷包:电动车出租的被动收入模式', isFree: true, price: 1 },
- { id: '1.2', title: '老墨:资源整合高手的社交方法', isFree: false, price: 1 },
- { id: '1.3', title: '笑声背后的MBTI:为什么ENTJ适合做资源,INTP适合做系统', isFree: false, price: 1 },
- { id: '1.4', title: '人性的三角结构:利益、情感、价值观', isFree: false, price: 1 },
- { id: '1.5', title: '沟通差的问题:为什么你说的别人听不懂', isFree: false, price: 1 }
- ]
- },
- {
- id: 'chapter-2',
- title: '第2章|人性困境案例',
- sections: [
- { id: '2.1', title: '相亲故事:你以为找的是人,实际是在找模式', isFree: false, price: 1 },
- { id: '2.2', title: '找工作迷茫者:为什么简历解决不了人生', isFree: false, price: 1 },
- { id: '2.3', title: '撸运费险:小钱困住大脑的真实心理', isFree: false, price: 1 },
- { id: '2.4', title: '游戏上瘾的年轻人:不是游戏吸引他,是生活没吸引力', isFree: false, price: 1 },
- { id: '2.5', title: '健康焦虑(我的糖尿病经历):疾病是人生的第一次清醒', isFree: false, price: 1 }
- ]
- }
- ]
- },
- {
- id: 'part-2',
- number: '二',
- title: '真实的行业',
- subtitle: '电商、内容、传统行业解析',
- chapters: [
- {
- id: 'chapter-3',
- title: '第3章|电商篇',
- sections: [
- { id: '3.1', title: '3000万流水如何跑出来(退税模式解析)', isFree: false, price: 1 },
- { id: '3.2', title: '供应链之王 vs 打工人:利润不在前端', isFree: false, price: 1 },
- { id: '3.3', title: '社区团购的底层逻辑', isFree: false, price: 1 },
- { id: '3.4', title: '跨境电商与退税套利', isFree: false, price: 1 }
- ]
- },
- {
- id: 'chapter-4',
- title: '第4章|内容商业篇',
- sections: [
- { id: '4.1', title: '旅游号:30天10万粉的真实逻辑', isFree: false, price: 1 },
- { id: '4.2', title: '做号工厂:如何让一个号变成一个机器', isFree: false, price: 1 },
- { id: '4.3', title: '情绪内容为什么比专业内容更赚钱', isFree: false, price: 1 },
- { id: '4.4', title: '猫与宠物号:为什么宠物赛道永不过时', isFree: false, price: 1 },
- { id: '4.5', title: '直播间里的三种人:演员、技术工、系统流', isFree: false, price: 1 }
- ]
- },
- {
- id: 'chapter-5',
- title: '第5章|传统行业篇',
- sections: [
- { id: '5.1', title: '拍卖行抱朴:一天240万的摇号生意', isFree: false, price: 1 },
- { id: '5.2', title: '土地拍卖:招拍挂背后的游戏规则', isFree: false, price: 1 },
- { id: '5.3', title: '地摊经济数字化:一个月900块的餐车生意', isFree: false, price: 1 },
- { id: '5.4', title: '不良资产拍卖:我错过的一个亿佣金', isFree: false, price: 1 },
- { id: '5.5', title: '桶装水李总:跟物业合作的轻资产模式', isFree: false, price: 1 }
- ]
- }
- ]
- },
- {
- id: 'part-3',
- number: '三',
- title: '真实的错误',
- subtitle: '我和别人犯过的错',
- chapters: [
- {
- id: 'chapter-6',
- title: '第6章|我人生错过的4件大钱',
- sections: [
- { id: '6.1', title: '电商财税窗口:2016年的千万级机会', isFree: false, price: 1 },
- { id: '6.2', title: '供应链金融:我不懂的杠杆游戏', isFree: false, price: 1 },
- { id: '6.3', title: '内容红利:2019年我为什么没做抖音', isFree: false, price: 1 },
- { id: '6.4', title: '数据资产化:我还在观望的未来机会', isFree: false, price: 1 }
- ]
- },
- {
- id: 'chapter-7',
- title: '第7章|别人犯的错误',
- sections: [
- { id: '7.1', title: '投资房年轻人的迷茫:资金 vs 能力', isFree: false, price: 1 },
- { id: '7.2', title: '信息差骗局:永远有人靠卖学习赚钱', isFree: false, price: 1 },
- { id: '7.3', title: '在Soul找恋爱但想赚钱的人', isFree: false, price: 1 },
- { id: '7.4', title: '创业者的三种死法:冲动、轻信、没结构', isFree: false, price: 1 },
- { id: '7.5', title: '人情生意的终点:关系越多亏得越多', isFree: false, price: 1 }
- ]
- }
- ]
- },
- {
- id: 'part-4',
- number: '四',
- title: '真实的赚钱',
- subtitle: '底层结构与真实案例',
- chapters: [
- {
- id: 'chapter-8',
- title: '第8章|底层结构',
- sections: [
- { id: '8.1', title: '流量杠杆:抖音、Soul、飞书', isFree: false, price: 1 },
- { id: '8.2', title: '价格杠杆:供应链与信息差', isFree: false, price: 1 },
- { id: '8.3', title: '时间杠杆:自动化 + AI', isFree: false, price: 1 },
- { id: '8.4', title: '情绪杠杆:咨询、婚恋、生意场', isFree: false, price: 1 },
- { id: '8.5', title: '社交杠杆:认识谁比你会什么更重要', isFree: false, price: 1 },
- { id: '8.6', title: '云阿米巴:分不属于自己的钱', isFree: false, price: 1 }
- ]
- },
- {
- id: 'chapter-9',
- title: '第9章|我在Soul上亲访的赚钱案例',
- sections: [
- { id: '9.1', title: '游戏账号私域:账号即资产', isFree: false, price: 1 },
- { id: '9.2', title: '健康包模式:高复购、高毛利', isFree: false, price: 1 },
- { id: '9.3', title: '药物私域:长期关系赛道', isFree: false, price: 1 },
- { id: '9.4', title: '残疾机构合作:退税 × AI × 人力成本', isFree: false, price: 1 },
- { id: '9.5', title: '私域银行:粉丝即小股东', isFree: false, price: 1 },
- { id: '9.6', title: 'Soul派对房:陌生人成交的最快场景', isFree: false, price: 1 },
- { id: '9.7', title: '飞书中台:从聊天到成交的流程化体系', isFree: false, price: 1 },
- { id: '9.8', title: '餐饮女孩:6万营收、1万利润的死撑生意', isFree: false, price: 1 },
- { id: '9.9', title: '电竞生态:从陪玩到签约到酒店的完整链条', isFree: false, price: 1 },
- { id: '9.10', title: '淘客大佬:损耗30%的白色通道', isFree: false, price: 1 },
- { id: '9.11', title: '蔬菜供应链:农户才是最赚钱的人', isFree: false, price: 1 },
- { id: '9.12', title: '美业整合:一个人的公司如何月入十万', isFree: false, price: 1 },
- { id: '9.13', title: 'AI工具推广:一个隐藏的高利润赛道', isFree: false, price: 1 },
- { id: '9.14', title: '大健康私域:一个月150万的70后', isFree: false, price: 1 }
- ]
- }
- ]
- },
- {
- id: 'part-5',
- number: '五',
- title: '真实的社会',
- subtitle: '未来职业与商业生态',
- chapters: [
- {
- id: 'chapter-10',
- title: '第10章|未来职业的变化趋势',
- sections: [
- { id: '10.1', title: 'AI时代:哪些工作会消失,哪些会崛起', isFree: false, price: 1 },
- { id: '10.2', title: '一人公司:为什么越来越多人选择单干', isFree: false, price: 1 },
- { id: '10.3', title: '为什么链接能力会成为第一价值', isFree: false, price: 1 },
- { id: '10.4', title: '新型公司:Soul-飞书-线下的三位一体', isFree: false, price: 1 }
- ]
- },
- {
- id: 'chapter-11',
- title: '第11章|中国社会商业生态的未来',
- sections: [
- { id: '11.1', title: '私域经济:为什么流量越来越贵', isFree: false, price: 1 },
- { id: '11.2', title: '银发经济与孤独经济:两个被忽视的万亿市场', isFree: false, price: 1 },
- { id: '11.3', title: '流量红利的终局', isFree: false, price: 1 },
- { id: '11.4', title: '大模型 + 供应链的组合拳', isFree: false, price: 1 },
- { id: '11.5', title: '社会分层的最终逻辑', isFree: false, price: 1 }
- ]
- }
- ]
- }
- ],
-
- // 展开状态
- expandedPart: 'part-1',
-
- // 附录
- appendixList: [
- { id: 'appendix-1', title: '附录1|Soul派对房精选对话' },
- { id: 'appendix-2', title: '附录2|创业者自检清单' },
- { id: 'appendix-3', title: '附录3|本书提到的工具和资源' }
- ],
-
- // 每日新增章节
- dailyChapters: []
- },
-
- onLoad() {
- this.setData({
- statusBarHeight: app.globalData.statusBarHeight,
- navBarHeight: app.globalData.navBarHeight
- })
- this.updateUserStatus()
- this.loadDailyChapters()
- this.loadTotalFromServer()
- },
-
- async loadTotalFromServer() {
- try {
- const res = await app.request({ url: '/api/book/all-chapters', silent: true })
- if (res && res.total) {
- this.setData({ totalSections: res.total })
- }
- } catch (e) {}
- },
-
- onShow() {
- // 设置TabBar选中状态
- if (typeof this.getTabBar === 'function' && this.getTabBar()) {
- const tabBar = this.getTabBar()
- if (tabBar.updateSelected) {
- tabBar.updateSelected()
- } else {
- tabBar.setData({ selected: 1 })
- }
- }
- this.updateUserStatus()
- },
-
- // 更新用户状态
- updateUserStatus() {
- const { isLoggedIn, hasFullBook, purchasedSections } = app.globalData
- this.setData({ isLoggedIn, hasFullBook, purchasedSections })
- },
-
- // 切换展开状态
- togglePart(e) {
- const partId = e.currentTarget.dataset.id
- this.setData({
- expandedPart: this.data.expandedPart === partId ? null : partId
- })
- },
-
- // 跳转到阅读页
- goToRead(e) {
- const id = e.currentTarget.dataset.id
- wx.navigateTo({ url: `/pages/read/read?id=${id}` })
- },
-
- // 检查是否已购买
- hasPurchased(sectionId) {
- if (this.data.hasFullBook) return true
- return this.data.purchasedSections.includes(sectionId)
- },
-
- // 返回首页
- goBack() {
- wx.switchTab({ url: '/pages/index/index' })
- },
-
- async loadDailyChapters() {
- try {
- const res = await app.request({ url: '/api/book/all-chapters', silent: true })
- const chapters = (res && res.data) || (res && res.chapters) || []
- const daily = chapters
- .filter(c => (c.sectionOrder || c.sort_order || 0) > 62)
- .sort((a, b) => new Date(b.updatedAt || b.updated_at || 0) - new Date(a.updatedAt || a.updated_at || 0))
- .slice(0, 20)
- .map(c => {
- const d = new Date(c.updatedAt || c.updated_at || Date.now())
- return {
- id: c.id,
- title: c.section_title || c.title || c.sectionTitle,
- price: c.price || 1,
- dateStr: `${d.getMonth()+1}/${d.getDate()}`
- }
- })
- if (daily.length > 0) {
- this.setData({ dailyChapters: daily, totalSections: 62 + daily.length })
- }
- } catch (e) { console.log('[Chapters] 加载最新新增失败:', e) }
- },
-
- // 跳转到搜索页
- goToSearch() {
- wx.navigateTo({ url: '/pages/search/search' })
- }
-})
diff --git a/归档/miniprogram/pages/chapters/chapters.json b/归档/miniprogram/pages/chapters/chapters.json
deleted file mode 100644
index e7696321..00000000
--- a/归档/miniprogram/pages/chapters/chapters.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "usingComponents": {},
- "enablePullDownRefresh": false,
- "backgroundTextStyle": "light",
- "backgroundColor": "#000000"
-}
diff --git a/归档/miniprogram/pages/chapters/chapters.wxml b/归档/miniprogram/pages/chapters/chapters.wxml
deleted file mode 100644
index f769c6ee..00000000
--- a/归档/miniprogram/pages/chapters/chapters.wxml
+++ /dev/null
@@ -1,126 +0,0 @@
-
-
-
-
-
-
-
-
- 🔍
-
-
- 目录
-
-
-
-
-
-
-
-
-
-
- 📚
-
-
- 一场SOUL的创业实验场
- 来自Soul派对房的真实商业故事
-
-
- {{totalSections}}
- 章节
-
-
-
-
-
-
-
-
- 📖
- 序言|为什么我每天早上6点在Soul开播?
-
-
- 免费
- →
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{section.isFree || hasFullBook || purchasedSections.indexOf(section.id) > -1 ? '○' : '●'}}
- {{section.id}} {{section.title}}
-
-
- 免费
- 已购
- ¥{{section.price}}
- ›
-
-
-
-
-
-
-
-
-
-
-
-
-
- 📖
- 尾声|这本书的真实目的
-
-
- 免费
- →
-
-
-
-
-
- 附录
-
-
- {{item.title}}
- →
-
-
-
-
-
-
-
-
diff --git a/归档/miniprogram/pages/chapters/chapters.wxss b/归档/miniprogram/pages/chapters/chapters.wxss
deleted file mode 100644
index 7ff3d64c..00000000
--- a/归档/miniprogram/pages/chapters/chapters.wxss
+++ /dev/null
@@ -1,497 +0,0 @@
-/**
- * Soul创业实验 - 目录页样式
- * 1:1还原Web版本UI
- */
-
-.page {
- min-height: 100vh;
- background: #000000;
- padding-bottom: 200rpx;
-}
-
-/* ===== 自定义导航栏 ===== */
-.nav-bar {
- position: fixed;
- top: 0;
- left: 0;
- right: 0;
- z-index: 100;
- background: rgba(0, 0, 0, 0.9);
- backdrop-filter: blur(40rpx);
- -webkit-backdrop-filter: blur(40rpx);
- border-bottom: 1rpx solid rgba(255, 255, 255, 0.05);
-}
-
-.nav-content {
- height: 88rpx;
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 0 32rpx;
-}
-
-.nav-left,
-.nav-right {
- width: 64rpx;
- display: flex;
- align-items: center;
- justify-content: center;
-}
-
-.nav-title {
- font-size: 36rpx;
- font-weight: 600;
- flex: 1;
- text-align: center;
-}
-
-/* 搜索按钮 */
-.search-btn {
- width: 64rpx;
- height: 64rpx;
- border-radius: 50%;
- background: #2c2c2e;
- display: flex;
- align-items: center;
- justify-content: center;
- transition: all 0.3s ease;
-}
-
-.search-btn:active {
- background: #3c3c3e;
- transform: scale(0.95);
-}
-
-.search-icon {
- font-size: 32rpx;
- color: rgba(255, 255, 255, 0.6);
-}
-
-.brand-color {
- color: #00CED1;
-}
-
-.nav-placeholder {
- width: 100%;
-}
-
-/* ===== 书籍信息卡 ===== */
-.book-info-card {
- display: flex;
- align-items: center;
- gap: 24rpx;
- margin: 32rpx 32rpx 24rpx 32rpx;
- padding: 32rpx;
-}
-
-.card-gradient {
- background: linear-gradient(135deg, #1c1c1e 0%, #2c2c2e 100%);
- border-radius: 32rpx;
- border: 2rpx solid rgba(0, 206, 209, 0.2);
- box-shadow: 0 8rpx 16rpx rgba(0, 0, 0, 0.2);
-}
-
-.book-icon {
- width: 96rpx;
- height: 96rpx;
- border-radius: 24rpx;
- background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%);
- display: flex;
- align-items: center;
- justify-content: center;
- flex-shrink: 0;
-}
-
-.book-icon-inner {
- font-size: 48rpx;
-}
-
-.book-info {
- flex: 1;
- min-width: 0;
-}
-
-.book-title {
- font-size: 32rpx;
- font-weight: 600;
- color: #ffffff;
- display: block;
- margin-bottom: 4rpx;
-}
-
-.book-subtitle {
- font-size: 22rpx;
- color: rgba(255, 255, 255, 0.4);
-}
-
-.book-count {
- text-align: right;
-}
-
-.count-value {
- font-size: 40rpx;
- font-weight: 700;
- display: block;
-}
-
-.count-label {
- font-size: 20rpx;
- color: rgba(255, 255, 255, 0.4);
-}
-
-/* ===== 目录内容 ===== */
-.chapters-content {
- padding: 0 32rpx;
- width: 100%;
- box-sizing: border-box;
-}
-
-/* ===== 章节项 ===== */
-.chapter-item {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 24rpx;
- background: #1c1c1e;
- border-radius: 24rpx;
- border: 2rpx solid rgba(255, 255, 255, 0.05);
- margin-bottom: 24rpx;
-}
-
-.chapter-item:active {
- background: #2c2c2e;
-}
-
-.item-left {
- display: flex;
- align-items: center;
- gap: 24rpx;
- flex: 1;
- min-width: 0;
-}
-
-.item-icon {
- width: 64rpx;
- height: 64rpx;
- border-radius: 16rpx;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 32rpx;
- flex-shrink: 0;
-}
-
-.icon-brand {
- background: rgba(0, 206, 209, 0.2);
-}
-
-.item-title {
- font-size: 28rpx;
- font-weight: 500;
- color: #ffffff;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
-}
-
-.item-right {
- display: flex;
- align-items: center;
- gap: 16rpx;
- flex-shrink: 0;
-}
-
-.item-arrow {
- font-size: 32rpx;
- color: rgba(255, 255, 255, 0.4);
-}
-
-/* ===== 标签 ===== */
-.tag {
- display: inline-flex;
- align-items: center;
- justify-content: center;
- font-size: 22rpx;
- padding: 6rpx 16rpx;
- min-width: 80rpx;
- border-radius: 8rpx;
- box-sizing: border-box;
- text-align: center;
-}
-
-.tag-free {
- background: rgba(0, 206, 209, 0.1);
- color: #00CED1;
-}
-
-.text-brand {
- color: #00CED1;
-}
-
-.text-muted {
- color: rgba(255, 255, 255, 0.4);
-}
-
-.text-xs {
- font-size: 22rpx;
-}
-
-/* ===== 篇章列表 ===== */
-.part-list {
- margin-bottom: 24rpx;
-}
-
-.part-item {
- margin-bottom: 24rpx;
-}
-
-.part-header {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 24rpx;
- background: #1c1c1e;
- border-radius: 24rpx;
- border: 2rpx solid rgba(255, 255, 255, 0.05);
-}
-
-.part-header:active {
- background: #2c2c2e;
-}
-
-.part-left {
- display: flex;
- align-items: center;
- gap: 24rpx;
-}
-
-.part-icon {
- width: 64rpx;
- height: 64rpx;
- border-radius: 16rpx;
- background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%);
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 28rpx;
- font-weight: 700;
- color: #ffffff;
- flex-shrink: 0;
-}
-
-.part-info {
- display: flex;
- flex-direction: column;
-}
-
-.part-title {
- font-size: 28rpx;
- font-weight: 600;
- color: #ffffff;
-}
-
-.part-subtitle {
- font-size: 20rpx;
- color: rgba(255, 255, 255, 0.4);
- margin-top: 4rpx;
-}
-
-.part-right {
- display: flex;
- align-items: center;
- gap: 16rpx;
-}
-
-.part-count {
- font-size: 22rpx;
- color: rgba(255, 255, 255, 0.4);
-}
-
-.part-arrow {
- font-size: 32rpx;
- color: rgba(255, 255, 255, 0.4);
- transition: transform 0.3s ease;
-}
-
-.arrow-down {
- transform: rotate(90deg);
-}
-
-/* ===== 章节组 ===== */
-.chapters-list {
- margin-top: 16rpx;
- margin-left: 16rpx;
-}
-
-.chapter-group {
- background: rgba(28, 28, 30, 0.5);
- border-radius: 16rpx;
- border: 2rpx solid rgba(255, 255, 255, 0.05);
- overflow: hidden;
- margin-bottom: 8rpx;
-}
-
-.chapter-header {
- padding: 16rpx 24rpx;
- font-size: 24rpx;
- font-weight: 500;
- color: rgba(255, 255, 255, 0.6);
- border-bottom: 1rpx solid rgba(255, 255, 255, 0.05);
-}
-
-.section-list {
- /* 小节列表 */
-}
-
-.section-item {
- display: flex;
- flex-direction: row;
- align-items: center;
- justify-content: space-between;
- padding: 20rpx 24rpx;
- border-bottom: 1rpx solid rgba(255, 255, 255, 0.05);
-}
-
-.section-item:last-child {
- border-bottom: none;
-}
-
-.section-item:active {
- background: rgba(255, 255, 255, 0.05);
-}
-
-.section-left {
- display: flex;
- flex-direction: row;
- align-items: center;
- gap: 16rpx;
- flex: 1;
- min-width: 0;
-}
-
-/* 小节锁图标 */
-.section-lock {
- width: 32rpx;
- min-width: 32rpx;
- font-size: 24rpx;
- text-align: center;
- flex-shrink: 0;
-}
-
-.lock-open {
- color: #00CED1;
-}
-
-.lock-closed {
- color: rgba(255, 255, 255, 0.3);
-}
-
-/* 小节标题 */
-.section-title {
- font-size: 26rpx;
- color: #ffffff;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- flex: 1;
-}
-
-/* 小节价格 */
-.section-price {
- font-size: 24rpx;
- color: rgba(255, 255, 255, 0.5);
-}
-
-/* 已购标签 */
-.tag-purchased {
- background: rgba(0, 206, 209, 0.15);
- color: #00CED1;
-}
-
-.section-right {
- display: flex;
- align-items: center;
- gap: 16rpx;
- flex-shrink: 0;
- margin-left: 16rpx;
-}
-
-.section-arrow {
- font-size: 28rpx;
- color: rgba(255, 255, 255, 0.3);
-}
-
-/* ===== 附录 ===== */
-.card {
- background: #1c1c1e;
- border-radius: 24rpx;
- border: 2rpx solid rgba(255, 255, 255, 0.05);
- margin: 0 0 24rpx 0;
- width: 100%;
- box-sizing: border-box;
-}
-
-.appendix-card {
- padding: 24rpx;
- width: 100%;
- box-sizing: border-box;
- margin: 0 0 24rpx 0;
-}
-
-.appendix-title {
- font-size: 24rpx;
- font-weight: 500;
- color: rgba(255, 255, 255, 0.6);
- display: block;
- margin-bottom: 16rpx;
-}
-
-.appendix-list {
- /* 附录列表 */
-}
-
-.appendix-item {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 16rpx 0;
- border-bottom: 1rpx solid rgba(255, 255, 255, 0.05);
-}
-
-.appendix-item:last-child {
- border-bottom: none;
-}
-
-.appendix-item:active {
- opacity: 0.7;
-}
-
-.appendix-text {
- font-size: 24rpx;
- color: rgba(255, 255, 255, 0.8);
-}
-
-.appendix-arrow {
- font-size: 28rpx;
- color: rgba(255, 255, 255, 0.3);
-}
-
-/* ===== 每日新增章节 ===== */
-.daily-section { margin: 20rpx 0; padding: 24rpx; background: rgba(255,215,0,0.04); border: 1rpx solid rgba(255,215,0,0.12); border-radius: 16rpx; }
-.daily-header { display: flex; align-items: center; gap: 12rpx; margin-bottom: 16rpx; }
-.daily-title { font-size: 30rpx; font-weight: 600; color: #FFD700; }
-.daily-badge { font-size: 22rpx; background: #FFD700; color: #000; padding: 2rpx 12rpx; border-radius: 20rpx; font-weight: bold; }
-.daily-list { display: flex; flex-direction: column; gap: 12rpx; }
-.daily-item { display: flex; justify-content: space-between; align-items: center; padding: 16rpx; background: rgba(255,255,255,0.03); border-radius: 12rpx; }
-.daily-left { display: flex; align-items: center; gap: 10rpx; flex: 1; min-width: 0; }
-.daily-new-tag { font-size: 18rpx; background: #FF4444; color: #fff; padding: 2rpx 8rpx; border-radius: 6rpx; font-weight: bold; flex-shrink: 0; }
-.daily-item-title { font-size: 26rpx; color: rgba(255,255,255,0.85); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
-.daily-right { display: flex; align-items: center; gap: 12rpx; flex-shrink: 0; }
-.daily-price { font-size: 26rpx; color: #FFD700; font-weight: 600; }
-.daily-date { font-size: 20rpx; color: rgba(255,255,255,0.35); }
-.daily-note { display: block; font-size: 22rpx; color: rgba(255,215,0,0.5); margin-top: 12rpx; text-align: center; }
-
-/* ===== 底部留白 ===== */
-.bottom-space {
- height: 40rpx;
-}
diff --git a/归档/miniprogram/pages/index/index.js b/归档/miniprogram/pages/index/index.js
deleted file mode 100644
index 1cf16fc9..00000000
--- a/归档/miniprogram/pages/index/index.js
+++ /dev/null
@@ -1,292 +0,0 @@
-/**
- * Soul创业派对 - 首页
- * 开发: 卡若
- * 技术支持: 存客宝
- */
-
-console.log('[Index] ===== 首页文件开始加载 =====')
-
-const app = getApp()
-
-Page({
- data: {
- // 系统信息
- statusBarHeight: 44,
- navBarHeight: 88,
-
- // 用户信息
- isLoggedIn: false,
- hasFullBook: false,
- readCount: 0,
-
- // 书籍数据
- totalSections: 62,
- bookData: [],
-
- // 推荐章节
- featuredSections: [
- { id: '1.1', title: '荷包:电动车出租的被动收入模式', tag: '免费', tagClass: 'tag-free', part: '真实的人' },
- { id: '3.1', title: '3000万流水如何跑出来', tag: '热门', tagClass: 'tag-pink', part: '真实的行业' },
- { id: '8.1', title: '流量杠杆:抖音、Soul、飞书', tag: '推荐', tagClass: 'tag-purple', part: '真实的赚钱' }
- ],
-
- // 最新章节(动态计算)
- latestSection: null,
- latestLabel: '最新更新',
-
- // 内容概览
- partsList: [
- { id: 'part-1', number: '一', title: '真实的人', subtitle: '人与人之间的底层逻辑' },
- { id: 'part-2', number: '二', title: '真实的行业', subtitle: '电商、内容、传统行业解析' },
- { id: 'part-3', number: '三', title: '真实的错误', subtitle: '我和别人犯过的错' },
- { id: 'part-4', number: '四', title: '真实的赚钱', subtitle: '底层结构与真实案例' },
- { id: 'part-5', number: '五', title: '真实的社会', subtitle: '未来职业与商业生态' }
- ],
-
- // 超级个体(VIP会员)
- superMembers: [],
-
- // 最新新增章节
- latestChapters: [],
-
- // 加载状态
- loading: true
- },
-
- onLoad(options) {
- console.log('[Index] ===== onLoad 触发 =====')
-
- // 获取系统信息
- this.setData({
- statusBarHeight: app.globalData.statusBarHeight,
- navBarHeight: app.globalData.navBarHeight
- })
-
- // 处理分享参数(推荐码绑定)
- if (options && options.ref) {
- console.log('[Index] 检测到推荐码:', options.ref)
- app.handleReferralCode({ query: options })
- }
-
- // 初始化数据
- this.initData()
- },
-
- onShow() {
- console.log('[Index] onShow 触发')
-
- // 设置TabBar选中状态
- if (typeof this.getTabBar === 'function' && this.getTabBar()) {
- const tabBar = this.getTabBar()
- console.log('[Index] TabBar 组件:', tabBar ? '已找到' : '未找到')
-
- // 主动触发配置加载
- if (tabBar && tabBar.loadFeatureConfig) {
- console.log('[Index] 主动调用 TabBar.loadFeatureConfig()')
- tabBar.loadFeatureConfig()
- }
-
- // 更新选中状态
- if (tabBar && tabBar.updateSelected) {
- tabBar.updateSelected()
- } else if (tabBar) {
- tabBar.setData({ selected: 0 })
- }
- } else {
- console.log('[Index] TabBar 组件未找到或 getTabBar 方法不存在')
- }
-
- // 更新用户状态
- this.updateUserStatus()
- },
-
- // 初始化数据
- async initData() {
- this.setData({ loading: true })
- try {
- await this.loadBookData()
- await this.loadFeaturedFromServer()
- this.loadSuperMembers()
- this.loadLatestChapters()
- } catch (e) {
- console.error('初始化失败:', e)
- } finally {
- this.setData({ loading: false })
- }
- },
-
- async loadSuperMembers() {
- try {
- // 优先加载VIP会员
- let members = []
- try {
- const res = await app.request({ url: '/api/vip/members', silent: true })
- if (res && res.success && res.data) {
- members = res.data.filter(u => u.avatar || u.vip_avatar).slice(0, 4).map(u => ({
- id: u.id, name: u.vip_name || u.nickname || '会员',
- avatar: u.vip_avatar || u.avatar, isVip: true
- }))
- }
- } catch (e) {}
- // 不足4个则用有头像的普通用户补充
- if (members.length < 4) {
- try {
- const dbRes = await app.request({ url: '/api/miniprogram/users?limit=20', silent: true })
- if (dbRes && dbRes.success && dbRes.data) {
- const existIds = new Set(members.map(m => m.id))
- const extra = dbRes.data
- .filter(u => u.avatar && u.nickname && !existIds.has(u.id))
- .slice(0, 4 - members.length)
- .map(u => ({ id: u.id, name: u.nickname, avatar: u.avatar, isVip: u.is_vip === 1 }))
- members = members.concat(extra)
- }
- } catch (e) {}
- }
- this.setData({ superMembers: members })
- } catch (e) { console.log('[Index] 加载超级个体失败:', e) }
- },
-
- // 从服务端获取精选推荐(加权算法:阅读量50% + 时效30% + 付款率20%)和最新更新
- async loadFeaturedFromServer() {
- try {
- const res = await app.request({ url: '/api/book/all-chapters', silent: true })
- const chapters = (res && res.data) ? res.data : (res && res.chapters) ? res.chapters : []
- let featured = (res && res.featuredSections) ? res.featuredSections : []
- // 服务端未返回精选时,从前端按更新时间取前3条有效章节作为回退
- if (featured.length === 0 && chapters.length > 0) {
- const valid = chapters.filter(c => {
- const id = (c.id || '').toLowerCase()
- const pt = (c.part_title || c.partTitle || '').toLowerCase()
- return !id.includes('preface') && !id.includes('epilogue') && !id.includes('appendix')
- && !pt.includes('序言') && !pt.includes('尾声') && !pt.includes('附录')
- })
- featured = valid
- .sort((a, b) => new Date(b.updated_at || b.updatedAt || 0) - new Date(a.updated_at || a.updatedAt || 0))
- .slice(0, 5)
- }
- if (featured.length > 0) {
- this.setData({
- featuredSections: featured.slice(0, 3).map(s => ({
- id: s.id || s.section_id,
- title: s.section_title || s.title,
- part: (s.cleanPartTitle || s.part_title || '').replace(/[_||]/g, ' ').trim()
- }))
- })
- }
-
- // 最新更新 = 按 updated_at 排序第1篇(排除序言/尾声/附录)
- const validChapters = chapters.filter(c => {
- const id = (c.id || '').toLowerCase()
- const pt = (c.part_title || c.partTitle || '').toLowerCase()
- return !id.includes('preface') && !id.includes('epilogue') && !id.includes('appendix')
- && !pt.includes('序言') && !pt.includes('尾声') && !pt.includes('附录')
- })
- if (validChapters.length > 0) {
- validChapters.sort((a, b) => new Date(b.updated_at || b.updatedAt || 0) - new Date(a.updated_at || a.updatedAt || 0))
- const latest = validChapters[0]
- this.setData({
- latestSection: {
- id: latest.id || latest.section_id,
- title: latest.section_title || latest.title,
- part: latest.cleanPartTitle || latest.part_title || ''
- }
- })
- }
- } catch (e) {
- console.log('[Index] 从服务端加载推荐失败,使用默认:', e)
- }
- },
-
- async loadBookData() {
- try {
- const res = await app.request({ url: '/api/book/all-chapters', silent: true })
- if (res && (res.data || res.chapters)) {
- const chapters = res.data || res.chapters || []
- this.setData({
- bookData: chapters,
- totalSections: res.total || chapters.length || 62
- })
- }
- } catch (e) {
- console.error('加载书籍数据失败:', e)
- }
- },
-
- // 更新用户状态(已读数 = 用户实际打开过的章节数,仅统计有权限阅读的)
- updateUserStatus() {
- const { isLoggedIn, hasFullBook, purchasedSections } = app.globalData
- const readCount = Math.min(app.getReadCount(), this.data.totalSections || 62)
- this.setData({
- isLoggedIn,
- hasFullBook,
- readCount
- })
- },
-
- // 跳转到目录
- goToChapters() {
- wx.switchTab({ url: '/pages/chapters/chapters' })
- },
-
- // 跳转到搜索页
- goToSearch() {
- wx.navigateTo({ url: '/pages/search/search' })
- },
-
- // 跳转到阅读页
- goToRead(e) {
- const id = e.currentTarget.dataset.id
- wx.navigateTo({ url: `/pages/read/read?id=${id}` })
- },
-
- // 跳转到匹配页
- goToMatch() {
- wx.switchTab({ url: '/pages/match/match' })
- },
-
- goToVip() {
- wx.navigateTo({ url: '/pages/vip/vip' })
- },
-
- goToSuperList() {
- wx.switchTab({ url: '/pages/match/match' })
- },
-
- async loadLatestChapters() {
- try {
- const res = await app.request({ url: '/api/book/all-chapters', silent: true })
- const chapters = (res && res.data) || (res && res.chapters) || []
- const latest = chapters
- .filter(c => (c.sectionOrder || c.sort_order || 0) > 62)
- .sort((a, b) => new Date(b.updatedAt || b.updated_at || 0) - new Date(a.updatedAt || a.updated_at || 0))
- .slice(0, 10)
- .map(c => {
- const d = new Date(c.updatedAt || c.updated_at || Date.now())
- return {
- id: c.id,
- title: c.section_title || c.title || c.sectionTitle,
- price: c.price || 1,
- dateStr: `${d.getMonth() + 1}/${d.getDate()}`
- }
- })
- this.setData({ latestChapters: latest })
- } catch (e) { console.log('[Index] 加载最新新增失败:', e) }
- },
-
- goToMemberDetail(e) {
- const id = e.currentTarget.dataset.id
- wx.navigateTo({ url: `/pages/member-detail/member-detail?id=${id}` })
- },
-
- // 跳转到我的页面
- goToMy() {
- wx.switchTab({ url: '/pages/my/my' })
- },
-
- // 下拉刷新
- async onPullDownRefresh() {
- await this.initData()
- this.updateUserStatus()
- wx.stopPullDownRefresh()
- }
-})
diff --git a/归档/miniprogram/pages/index/index.json b/归档/miniprogram/pages/index/index.json
deleted file mode 100644
index 1246275b..00000000
--- a/归档/miniprogram/pages/index/index.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "usingComponents": {},
- "enablePullDownRefresh": true,
- "backgroundTextStyle": "light",
- "backgroundColor": "#000000"
-}
diff --git a/归档/miniprogram/pages/index/index.wxml b/归档/miniprogram/pages/index/index.wxml
deleted file mode 100644
index 470c18b5..00000000
--- a/归档/miniprogram/pages/index/index.wxml
+++ /dev/null
@@ -1,166 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 最新更新
- {{latestSection.title}}
- {{latestSection.part}}
-
- 开始阅读
- →
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{readCount}}
- 已读
-
-
- {{totalSections - readCount}}
- 待读
-
-
- 5
- 篇章
-
-
- 11
- 章节
-
-
-
-
-
-
-
-
-
-
-
- {{item.name[0] || '会'}}
-
- {{item.name}}
-
-
-
- 成为会员,展示你的项目
- 加入创业派对 →
-
-
-
-
-
-
-
-
-
-
- {{item.id}}
-
- {{item.title}}
- {{item.part}}
-
- →
-
-
-
-
-
-
-
-
-
-
- NEW
- {{item.title}}
-
-
- ¥{{item.price}}
- {{item.dateStr}}
-
-
-
-
-
-
-
-
-
diff --git a/归档/miniprogram/pages/index/index.wxss b/归档/miniprogram/pages/index/index.wxss
deleted file mode 100644
index b3d68715..00000000
--- a/归档/miniprogram/pages/index/index.wxss
+++ /dev/null
@@ -1,635 +0,0 @@
-/**
- * Soul创业实验 - 首页样式
- * 1:1还原Web版本UI
- */
-
-.page {
- min-height: 100vh;
- background: #000000;
- padding-bottom: 200rpx;
-}
-
-/* ===== 导航栏占位 ===== */
-.nav-placeholder {
- width: 100%;
-}
-
-/* ===== 顶部区域 ===== */
-.header {
- padding: 0 32rpx 32rpx;
-}
-
-.header-content {
- display: flex;
- align-items: center;
- justify-content: space-between;
- margin-bottom: 32rpx;
- padding-top: 24rpx;
-}
-
-.logo-section {
- display: flex;
- align-items: center;
- gap: 16rpx;
-}
-
-.logo-icon {
- width: 80rpx;
- height: 80rpx;
- border-radius: 20rpx;
- background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%);
- display: flex;
- align-items: center;
- justify-content: center;
- box-shadow: 0 8rpx 24rpx rgba(0, 206, 209, 0.3);
-}
-
-.logo-text {
- color: #ffffff;
- font-size: 36rpx;
- font-weight: 700;
-}
-
-.logo-info {
- display: flex;
- flex-direction: column;
-}
-
-.logo-title {
- font-size: 36rpx;
- font-weight: 700;
-}
-
-.text-white {
- color: #ffffff;
-}
-
-.brand-color {
- color: #00CED1;
-}
-
-.logo-subtitle {
- font-size: 22rpx;
- color: rgba(255, 255, 255, 0.4);
- margin-top: 4rpx;
-}
-
-.header-right {
- display: flex;
- align-items: center;
- gap: 16rpx;
-}
-
-.chapter-badge {
- font-size: 22rpx;
- color: #00CED1;
- background: rgba(0, 206, 209, 0.1);
- padding: 8rpx 16rpx;
- border-radius: 32rpx;
-}
-
-/* ===== 搜索栏 ===== */
-.search-bar {
- display: flex;
- align-items: center;
- gap: 24rpx;
- padding: 24rpx 32rpx;
- background: #1c1c1e;
- border-radius: 24rpx;
- border: 2rpx solid rgba(255, 255, 255, 0.05);
-}
-
-.search-icon {
- position: relative;
- width: 32rpx;
- height: 32rpx;
-}
-
-.search-circle {
- width: 20rpx;
- height: 20rpx;
- border: 4rpx solid rgba(255, 255, 255, 0.4);
- border-radius: 50%;
-}
-
-.search-handle {
- position: absolute;
- bottom: 0;
- right: 0;
- width: 12rpx;
- height: 4rpx;
- background: rgba(255, 255, 255, 0.4);
- transform: rotate(45deg);
- border-radius: 2rpx;
-}
-
-.search-placeholder {
- font-size: 28rpx;
- color: rgba(255, 255, 255, 0.4);
-}
-
-/* ===== 主内容区 ===== */
-.main-content {
- padding: 0 32rpx;
- width: 100%;
- box-sizing: border-box;
-}
-
-/* ===== Banner卡片 ===== */
-.banner-card {
- position: relative;
- padding: 40rpx;
- border-radius: 32rpx;
- overflow: hidden;
- background: linear-gradient(135deg, #0d3331 0%, #1a1a2e 50%, #16213e 100%);
- margin-bottom: 24rpx;
-}
-
-.banner-glow {
- position: absolute;
- top: 0;
- right: 0;
- width: 256rpx;
- height: 256rpx;
- background: #00CED1;
- border-radius: 50%;
- filter: blur(120rpx);
- opacity: 0.2;
-}
-
-.banner-tag {
- display: inline-block;
- padding: 8rpx 16rpx;
- background: #00CED1;
- color: #000000;
- font-size: 22rpx;
- font-weight: 500;
- border-radius: 8rpx;
- margin-bottom: 24rpx;
-}
-
-.banner-title {
- font-size: 36rpx;
- font-weight: 700;
- color: #ffffff;
- margin-bottom: 16rpx;
- padding-right: 64rpx;
-}
-
-.banner-part {
- font-size: 28rpx;
- color: rgba(255, 255, 255, 0.6);
- margin-bottom: 24rpx;
-}
-
-.banner-action {
- display: flex;
- align-items: center;
- gap: 8rpx;
-}
-
-.banner-action-text {
- font-size: 28rpx;
- color: #00CED1;
- font-weight: 500;
-}
-
-.banner-arrow {
- color: #00CED1;
- font-size: 28rpx;
-}
-
-/* ===== 通用卡片 ===== */
-.card {
- background: #1c1c1e;
- border-radius: 32rpx;
- padding: 32rpx;
- border: 2rpx solid rgba(255, 255, 255, 0.05);
- margin: 0 0 24rpx 0;
- width: 100%;
- box-sizing: border-box;
-}
-
-/* ===== 阅读进度卡 ===== */
-.progress-card {
- width: 100%;
- background: #1c1c1e;
- border-radius: 24rpx;
- padding: 28rpx;
- border: 2rpx solid rgba(255, 255, 255, 0.05);
- margin: 0 0 24rpx 0;
- box-sizing: border-box;
-}
-
-.progress-header {
- display: flex;
- align-items: center;
- justify-content: space-between;
- margin-bottom: 24rpx;
-}
-
-.progress-title {
- font-size: 28rpx;
- color: #ffffff;
- font-weight: 500;
-}
-
-.progress-count {
- font-size: 22rpx;
- color: rgba(255, 255, 255, 0.4);
-}
-
-.progress-bar-wrapper {
- margin-bottom: 24rpx;
-}
-
-.progress-bar-bg {
- width: 100%;
- height: 16rpx;
- background: #2c2c2e;
- border-radius: 8rpx;
- overflow: hidden;
-}
-
-.progress-bar-fill {
- height: 100%;
- background: linear-gradient(90deg, #00CED1 0%, #20B2AA 100%);
- border-radius: 8rpx;
- transition: width 0.3s ease;
-}
-
-.progress-stats {
- display: grid;
- grid-template-columns: repeat(4, 1fr);
- gap: 24rpx;
-}
-
-.stat-item {
- text-align: center;
-}
-
-.stat-value {
- font-size: 36rpx;
- font-weight: 700;
- color: #ffffff;
- display: block;
-}
-
-.stat-label {
- font-size: 22rpx;
- color: rgba(255, 255, 255, 0.4);
-}
-
-/* ===== 区块标题 ===== */
-.section {
- margin-bottom: 24rpx;
-}
-
-.section-header {
- display: flex;
- align-items: center;
- justify-content: space-between;
- margin-bottom: 24rpx;
-}
-
-.section-title {
- font-size: 32rpx;
- font-weight: 600;
- color: #ffffff;
-}
-
-.section-more {
- display: flex;
- align-items: center;
- gap: 8rpx;
-}
-
-.more-text {
- font-size: 24rpx;
- color: #00CED1;
-}
-
-.more-arrow {
- font-size: 24rpx;
- color: #00CED1;
-}
-
-/* ===== 精选推荐列表 ===== */
-.featured-list {
- display: flex;
- flex-direction: column;
- gap: 24rpx;
-}
-
-.featured-item {
- display: flex;
- align-items: flex-start;
- justify-content: space-between;
- padding: 32rpx;
- background: #1c1c1e;
- border-radius: 24rpx;
- border: 2rpx solid rgba(255, 255, 255, 0.05);
-}
-
-.featured-item:active {
- transform: scale(0.98);
- background: #2c2c2e;
-}
-
-.featured-content {
- flex: 1;
-}
-
-.featured-meta {
- display: flex;
- align-items: center;
- gap: 16rpx;
- margin-bottom: 16rpx;
-}
-
-.featured-id {
- font-size: 24rpx;
- font-weight: 500;
-}
-
-.tag {
- display: inline-flex;
- align-items: center;
- justify-content: center;
- font-size: 22rpx;
- padding: 6rpx 16rpx;
- min-width: 80rpx;
- border-radius: 8rpx;
- box-sizing: border-box;
- text-align: center;
-}
-
-.tag-free {
- background: rgba(0, 206, 209, 0.1);
- color: #00CED1;
-}
-
-.tag-pink {
- background: rgba(233, 30, 99, 0.1);
- color: #E91E63;
-}
-
-.tag-purple {
- background: rgba(123, 97, 255, 0.1);
- color: #7B61FF;
-}
-
-.featured-title {
- font-size: 28rpx;
- color: #ffffff;
- font-weight: 500;
- display: block;
- margin-bottom: 8rpx;
-}
-
-.featured-part {
- font-size: 22rpx;
- color: rgba(255, 255, 255, 0.4);
-}
-
-.featured-arrow {
- font-size: 32rpx;
- color: rgba(255, 255, 255, 0.3);
- margin-top: 8rpx;
-}
-
-/* ===== 内容概览列表 ===== */
-.parts-list {
- display: flex;
- flex-direction: column;
- gap: 24rpx;
-}
-
-.part-item {
- display: flex;
- align-items: center;
- gap: 24rpx;
- padding: 32rpx;
- background: #1c1c1e;
- border-radius: 24rpx;
- border: 2rpx solid rgba(255, 255, 255, 0.05);
-}
-
-.part-item:active {
- transform: scale(0.98);
- background: #2c2c2e;
-}
-
-.part-icon {
- width: 80rpx;
- height: 80rpx;
- border-radius: 16rpx;
- background: linear-gradient(135deg, rgba(0, 206, 209, 0.2) 0%, rgba(32, 178, 170, 0.1) 100%);
- display: flex;
- align-items: center;
- justify-content: center;
- flex-shrink: 0;
-}
-
-.part-number {
- font-size: 28rpx;
- font-weight: 700;
- color: #00CED1;
-}
-
-.part-info {
- flex: 1;
- min-width: 0;
-}
-
-.part-title {
- font-size: 28rpx;
- color: #ffffff;
- font-weight: 500;
- display: block;
- margin-bottom: 4rpx;
-}
-
-.part-subtitle {
- font-size: 22rpx;
- color: rgba(255, 255, 255, 0.4);
- display: block;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
-}
-
-.part-arrow {
- font-size: 32rpx;
- color: rgba(255, 255, 255, 0.3);
- flex-shrink: 0;
-}
-
-/* ===== 序言入口 ===== */
-.preface-card {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 32rpx;
- border-radius: 24rpx;
- background: linear-gradient(90deg, rgba(0, 206, 209, 0.1) 0%, transparent 100%);
- border: 2rpx solid rgba(0, 206, 209, 0.2);
- margin-bottom: 24rpx;
-}
-
-.preface-card:active {
- opacity: 0.8;
-}
-
-.preface-content {
- flex: 1;
-}
-
-.preface-title {
- font-size: 28rpx;
- color: #ffffff;
- font-weight: 500;
- display: block;
- margin-bottom: 8rpx;
-}
-
-.preface-desc {
- font-size: 24rpx;
- color: rgba(255, 255, 255, 0.6);
-}
-
-/* ===== 超级个体 ===== */
-.super-grid {
- display: grid;
- grid-template-columns: repeat(4, 1fr);
- gap: 24rpx 16rpx;
-}
-.super-item {
- display: flex;
- flex-direction: column;
- align-items: center;
- gap: 10rpx;
-}
-.super-avatar {
- width: 108rpx;
- height: 108rpx;
- border-radius: 50%;
- overflow: hidden;
- background: rgba(0,206,209,0.1);
- display: flex;
- align-items: center;
- justify-content: center;
- border: 3rpx solid rgba(255,255,255,0.1);
-}
-.super-avatar-vip {
- border: 3rpx solid #FFD700;
- box-shadow: 0 0 12rpx rgba(255,215,0,0.3);
-}
-.super-avatar-img {
- width: 100%;
- height: 100%;
- object-fit: cover;
-}
-.super-avatar-text {
- font-size: 40rpx;
- font-weight: 600;
- color: #00CED1;
-}
-.super-name {
- font-size: 22rpx;
- color: rgba(255,255,255,0.7);
- text-align: center;
- width: 100%;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
-}
-.super-empty {
- padding: 32rpx;
- text-align: center;
- background: rgba(255,255,255,0.03);
- border-radius: 16rpx;
-}
-.super-empty-text {
- font-size: 24rpx;
- color: rgba(255,255,255,0.4);
- display: block;
- margin-bottom: 16rpx;
-}
-.super-empty-btn {
- font-size: 26rpx;
- color: #00CED1;
-}
-
-/* ===== 最新新增 ===== */
-.daily-badge-wrap {
- display: inline-flex;
- align-items: center;
-}
-.daily-badge {
- background: #FF4500;
- color: #fff;
- font-size: 20rpx;
- font-weight: 600;
- padding: 4rpx 12rpx;
- border-radius: 16rpx;
- margin-left: 8rpx;
-}
-.latest-list {
- display: flex;
- flex-direction: column;
- gap: 12rpx;
-}
-.latest-item {
- display: flex;
- justify-content: space-between;
- align-items: center;
- padding: 20rpx 24rpx;
- background: rgba(255,255,255,0.04);
- border-radius: 12rpx;
- border-left: 4rpx solid #FF4500;
-}
-.latest-left {
- flex: 1;
- display: flex;
- align-items: center;
- gap: 12rpx;
- min-width: 0;
-}
-.latest-new-tag {
- font-size: 18rpx;
- font-weight: 700;
- color: #FF4500;
- background: rgba(255,69,0,0.15);
- padding: 2rpx 10rpx;
- border-radius: 6rpx;
- flex-shrink: 0;
-}
-.latest-title {
- font-size: 26rpx;
- color: rgba(255,255,255,0.9);
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
-}
-.latest-right {
- display: flex;
- align-items: center;
- gap: 12rpx;
- flex-shrink: 0;
- margin-left: 12rpx;
-}
-.latest-price {
- font-size: 26rpx;
- font-weight: 600;
- color: #FFD700;
-}
-.latest-date {
- font-size: 22rpx;
- color: rgba(255,255,255,0.4);
-}
-
-/* ===== 底部留白 ===== */
-.bottom-space {
- height: 40rpx;
-}
diff --git a/归档/miniprogram/pages/match/match.js b/归档/miniprogram/pages/match/match.js
deleted file mode 100644
index 6d3b9d87..00000000
--- a/归档/miniprogram/pages/match/match.js
+++ /dev/null
@@ -1,663 +0,0 @@
-/**
- * Soul创业派对 - 找伙伴页
- * 按H5网页端完全重构
- * 开发: 卡若
- */
-
-const app = getApp()
-
-// 默认匹配类型配置
-// 找伙伴:真正的匹配功能,匹配数据库中的真实用户
-// 资源对接:需要登录+购买章节才能使用,填写2项信息(我能帮到你什么、我需要什么帮助)
-// 导师顾问:跳转到存客宝添加微信
-// 团队招募:跳转到存客宝添加微信
-let MATCH_TYPES = [
- { id: 'partner', label: '找伙伴', matchLabel: '找伙伴', icon: '⭐', matchFromDB: true, showJoinAfterMatch: false },
- { id: 'investor', label: '资源对接', matchLabel: '资源对接', icon: '👥', matchFromDB: true, showJoinAfterMatch: true, requirePurchase: true },
- { id: 'mentor', label: '导师顾问', matchLabel: '立即咨询', icon: '❤️', matchFromDB: true, showJoinAfterMatch: true },
- { id: 'team', label: '团队招募', matchLabel: '团队招募', icon: '🎮', matchFromDB: true, showJoinAfterMatch: true }
-]
-
-let FREE_MATCH_LIMIT = 3 // 每日免费匹配次数
-
-Page({
- data: {
- statusBarHeight: 44,
-
- // 匹配类型
- matchTypes: MATCH_TYPES,
- selectedType: 'partner',
- currentTypeLabel: '找伙伴',
-
- // 用户状态
- isLoggedIn: false,
- hasPurchased: false,
- hasFullBook: false,
-
- // 匹配次数
- todayMatchCount: 0,
- totalMatchesAllowed: FREE_MATCH_LIMIT,
- matchesRemaining: FREE_MATCH_LIMIT,
- needPayToMatch: false,
-
- // 匹配状态
- isMatching: false,
- matchAttempts: 0,
- currentMatch: null,
-
- // 加入弹窗
- showJoinModal: false,
- joinType: null,
- joinTypeLabel: '',
- contactType: 'phone',
- phoneNumber: '',
- wechatId: '',
- userPhone: '',
- isJoining: false,
- joinSuccess: false,
- joinError: '',
- needBindFirst: false,
-
- // 资源对接表单
- canHelp: '',
- needHelp: '',
- goodAt: '',
-
- // 解锁弹窗
- showUnlockModal: false,
-
- // 匹配价格(可配置)
- matchPrice: 1,
- extraMatches: 0
- },
-
- onLoad() {
- this.setData({
- statusBarHeight: app.globalData.statusBarHeight || 44
- })
- this.loadMatchConfig()
- this.loadStoredContact()
- this.loadTodayMatchCount()
- this.initUserStatus()
- },
-
- onShow() {
- if (typeof this.getTabBar === 'function' && this.getTabBar()) {
- const tabBar = this.getTabBar()
- if (tabBar.updateSelected) {
- tabBar.updateSelected()
- } else {
- tabBar.setData({ selected: 2 })
- }
- }
- this.initUserStatus()
- },
-
- // 加载匹配配置
- async loadMatchConfig() {
- try {
- const res = await app.request({ url: '/api/match/config', silent: true, method: 'GET',
- method: 'GET'
- })
-
- if (res.success && res.data) {
- // 更新全局配置
- MATCH_TYPES = res.data.matchTypes || MATCH_TYPES
- FREE_MATCH_LIMIT = res.data.freeMatchLimit || FREE_MATCH_LIMIT
- const matchPrice = res.data.matchPrice || 1
-
- this.setData({
- matchTypes: MATCH_TYPES,
- totalMatchesAllowed: FREE_MATCH_LIMIT,
- matchPrice: matchPrice
- })
-
- console.log('[Match] 加载匹配配置成功:', {
- types: MATCH_TYPES.length,
- freeLimit: FREE_MATCH_LIMIT,
- price: matchPrice
- })
- }
- } catch (e) {
- console.log('[Match] 加载匹配配置失败,使用默认配置:', e)
- }
- },
-
- // 加载本地存储的联系方式
- loadStoredContact() {
- const phone = wx.getStorageSync('user_phone') || ''
- const wechat = wx.getStorageSync('user_wechat') || ''
- this.setData({
- phoneNumber: phone,
- wechatId: wechat,
- userPhone: phone
- })
- },
-
- // 加载今日匹配次数
- loadTodayMatchCount() {
- try {
- const today = new Date().toISOString().split('T')[0]
- const stored = wx.getStorageSync('match_count_data')
- if (stored) {
- const data = typeof stored === 'string' ? JSON.parse(stored) : stored
- if (data.date === today) {
- this.setData({ todayMatchCount: data.count })
- }
- }
- } catch (e) {
- console.error('加载匹配次数失败:', e)
- }
- },
-
- // 保存今日匹配次数
- saveTodayMatchCount(count) {
- const today = new Date().toISOString().split('T')[0]
- wx.setStorageSync('match_count_data', { date: today, count })
- },
-
- // 初始化用户状态
- initUserStatus() {
- const { isLoggedIn, hasFullBook, purchasedSections } = app.globalData
-
- // 获取额外购买的匹配次数
- const extraMatches = wx.getStorageSync('extra_match_count') || 0
-
- // 总匹配次数 = 每日免费(3) + 额外购买次数
- // 全书用户无限制
- const totalMatchesAllowed = hasFullBook ? 999999 : FREE_MATCH_LIMIT + extraMatches
- const matchesRemaining = hasFullBook ? 999999 : Math.max(0, totalMatchesAllowed - this.data.todayMatchCount)
- const needPayToMatch = !hasFullBook && matchesRemaining <= 0
-
- this.setData({
- isLoggedIn,
- hasFullBook,
- hasPurchased: true, // 所有用户都可以使用匹配功能
- totalMatchesAllowed,
- matchesRemaining,
- needPayToMatch,
- extraMatches
- })
- },
-
- // 选择匹配类型
- selectType(e) {
- const typeId = e.currentTarget.dataset.type
- const type = MATCH_TYPES.find(t => t.id === typeId)
- this.setData({
- selectedType: typeId,
- currentTypeLabel: type?.matchLabel || type?.label || '创业伙伴'
- })
- },
-
- // 点击匹配按钮
- handleMatchClick() {
- const currentType = MATCH_TYPES.find(t => t.id === this.data.selectedType)
-
- // 资源对接类型需要登录+购买章节才能使用
- if (currentType && currentType.id === 'investor') {
- // 检查是否登录
- if (!this.data.isLoggedIn) {
- wx.showModal({
- title: '需要登录',
- content: '请先登录后再使用资源对接功能',
- confirmText: '去登录',
- success: (res) => {
- if (res.confirm) {
- wx.switchTab({ url: '/pages/my/my' })
- }
- }
- })
- return
- }
-
- // 检查是否购买过章节
- const hasPurchased = app.globalData.purchasedSections?.length > 0 || app.globalData.hasFullBook
- if (!hasPurchased) {
- wx.showModal({
- title: '需要购买章节',
- content: '购买任意章节后即可使用资源对接功能',
- confirmText: '去购买',
- success: (res) => {
- if (res.confirm) {
- wx.switchTab({ url: '/pages/catalog/catalog' })
- }
- }
- })
- return
- }
- }
-
- // 如果是需要填写联系方式的类型(资源对接、导师顾问、团队招募)
- if (currentType && currentType.showJoinAfterMatch) {
- // 先检查是否已绑定联系方式
- const hasPhone = !!this.data.phoneNumber
- const hasWechat = !!this.data.wechatId
-
- if (!hasPhone && !hasWechat) {
- // 没有绑定联系方式,先显示绑定提示
- this.setData({
- showJoinModal: true,
- joinType: currentType.id,
- joinTypeLabel: currentType.matchLabel || currentType.label,
- joinSuccess: false,
- joinError: '',
- needBindFirst: true
- })
- return
- }
-
- // 已绑定联系方式,先显示匹配动画1-3秒,再弹出确认
- this.startMatchingAnimation(currentType)
- return
- }
-
- // 创业合伙类型 - 真正的匹配功能
- if (this.data.needPayToMatch) {
- this.setData({ showUnlockModal: true })
- return
- }
-
- this.startMatch()
- },
-
- // 匹配动画后弹出加入确认
- startMatchingAnimation(currentType) {
- // 显示匹配中状态
- this.setData({
- isMatching: true,
- matchAttempts: 0,
- currentMatch: null
- })
-
- // 动画计时
- const timer = setInterval(() => {
- this.setData({ matchAttempts: this.data.matchAttempts + 1 })
- }, 500)
-
- // 1-3秒随机延迟后显示弹窗
- const delay = Math.random() * 2000 + 1000
- setTimeout(() => {
- clearInterval(timer)
- this.setData({
- isMatching: false,
- showJoinModal: true,
- joinType: currentType.id,
- joinTypeLabel: currentType.matchLabel || currentType.label,
- joinSuccess: false,
- joinError: '',
- needBindFirst: false
- })
- }, delay)
- },
-
- // 显示购买提示
- showPurchaseTip() {
- wx.showModal({
- title: '需要购买书籍',
- content: '购买《Soul创业派对》后即可使用匹配功能,仅需9.9元',
- confirmText: '去购买',
- success: (res) => {
- if (res.confirm) {
- this.goToChapters()
- }
- }
- })
- },
-
- // 开始匹配 - 只匹配数据库中的真实用户
- async startMatch() {
- this.setData({
- isMatching: true,
- matchAttempts: 0,
- currentMatch: null
- })
-
- // 匹配动画计时器
- const timer = setInterval(() => {
- this.setData({ matchAttempts: this.data.matchAttempts + 1 })
- }, 1000)
-
- // 从数据库获取真实用户匹配
- let matchedUser = null
- try {
- const res = await app.request({ url: '/api/match/users', silent: true,
- method: 'POST',
- data: {
- matchType: this.data.selectedType,
- userId: app.globalData.userInfo?.id || ''
- }
- })
-
- if (res.success && res.data) {
- matchedUser = res.data
- console.log('[Match] 从数据库匹配到用户:', matchedUser.nickname)
- }
- } catch (e) {
- console.log('[Match] 数据库匹配失败:', e)
- }
-
- // 延迟显示结果(模拟匹配过程)
- const delay = Math.random() * 2000 + 2000
- setTimeout(() => {
- clearInterval(timer)
-
- // 如果没有匹配到用户,提示用户
- if (!matchedUser) {
- this.setData({ isMatching: false })
- wx.showModal({
- title: '暂无匹配',
- content: '当前暂无合适的匹配用户,请稍后再试',
- showCancel: false,
- confirmText: '知道了'
- })
- return
- }
-
- // 增加今日匹配次数
- const newCount = this.data.todayMatchCount + 1
- const matchesRemaining = this.data.hasFullBook ? 999999 : Math.max(0, this.data.totalMatchesAllowed - newCount)
-
- this.setData({
- isMatching: false,
- currentMatch: matchedUser,
- todayMatchCount: newCount,
- matchesRemaining,
- needPayToMatch: !this.data.hasFullBook && matchesRemaining <= 0
- })
- this.saveTodayMatchCount(newCount)
-
- // 上报匹配行为到存客宝
- this.reportMatch(matchedUser)
-
- }, delay)
- },
-
- // 生成模拟匹配数据
- generateMockMatch() {
- const nicknames = ['创业先锋', '资源整合者', '私域专家', '商业导师', '连续创业者']
- const concepts = [
- '专注私域流量运营5年,帮助100+品牌实现从0到1的增长。',
- '连续创业者,擅长商业模式设计和资源整合。',
- '在Soul分享真实创业故事,希望找到志同道合的合作伙伴。'
- ]
- const wechats = ['soul_partner_1', 'soul_business_2024', 'soul_startup_fan']
-
- const index = Math.floor(Math.random() * nicknames.length)
- const currentType = MATCH_TYPES.find(t => t.id === this.data.selectedType)
-
- return {
- id: `user_${Date.now()}`,
- nickname: nicknames[index],
- avatar: `https://picsum.photos/200/200?random=${Date.now()}`,
- tags: ['创业者', '私域运营', currentType?.label || '创业合伙'],
- matchScore: Math.floor(Math.random() * 20) + 80,
- concept: concepts[index % concepts.length],
- wechat: wechats[index % wechats.length],
- commonInterests: [
- { icon: '📚', text: '都在读《创业派对》' },
- { icon: '💼', text: '对私域运营感兴趣' },
- { icon: '🎯', text: '相似的创业方向' }
- ]
- }
- },
-
- // 上报匹配行为
- async reportMatch(matchedUser) {
- try {
- await app.request({ url: '/api/ckb/match', silent: true,
- method: 'POST',
- data: {
- matchType: this.data.selectedType,
- phone: this.data.phoneNumber,
- wechat: this.data.wechatId,
- userId: app.globalData.userInfo?.id || '',
- nickname: app.globalData.userInfo?.nickname || '',
- matchedUser: {
- id: matchedUser.id,
- nickname: matchedUser.nickname,
- matchScore: matchedUser.matchScore
- }
- }
- })
- } catch (e) {
- console.log('上报匹配失败:', e)
- }
- },
-
- // 取消匹配
- cancelMatch() {
- this.setData({ isMatching: false, matchAttempts: 0 })
- },
-
- // 重置匹配(返回)
- resetMatch() {
- this.setData({ currentMatch: null })
- },
-
- // 添加微信好友
- handleAddWechat() {
- if (!this.data.currentMatch) return
-
- wx.setClipboardData({
- data: this.data.currentMatch.wechat,
- success: () => {
- wx.showModal({
- title: '微信号已复制',
- content: `微信号:${this.data.currentMatch.wechat}\n\n请打开微信添加好友,备注"创业合作"即可`,
- showCancel: false,
- confirmText: '知道了'
- })
- }
- })
- },
-
- // 切换联系方式类型
- switchContactType(e) {
- const type = e.currentTarget.dataset.type
- this.setData({ contactType: type, joinError: '' })
- },
-
- // 手机号输入
- onPhoneInput(e) {
- this.setData({
- phoneNumber: e.detail.value.replace(/\D/g, '').slice(0, 11),
- joinError: ''
- })
- },
-
- // 资源对接表单输入
- onCanHelpInput(e) {
- this.setData({ canHelp: e.detail.value })
- },
- onNeedHelpInput(e) {
- this.setData({ needHelp: e.detail.value })
- },
- onGoodAtInput(e) {
- this.setData({ goodAt: e.detail.value })
- },
-
- // 微信号输入
- onWechatInput(e) {
- this.setData({
- wechatId: e.detail.value,
- joinError: ''
- })
- },
-
- // 提交加入
- async handleJoinSubmit() {
- const { contactType, phoneNumber, wechatId, joinType, isJoining, canHelp, needHelp } = this.data
-
- if (isJoining) return
-
- // 验证联系方式
- if (contactType === 'phone') {
- if (!phoneNumber || phoneNumber.length !== 11) {
- this.setData({ joinError: '请输入正确的11位手机号' })
- return
- }
- } else {
- if (!wechatId || wechatId.length < 6) {
- this.setData({ joinError: '请输入正确的微信号(至少6位)' })
- return
- }
- }
-
- // 资源对接需要填写两项信息
- if (joinType === 'investor') {
- if (!canHelp || canHelp.trim().length < 2) {
- this.setData({ joinError: '请填写"我能帮到你什么"' })
- return
- }
- if (!needHelp || needHelp.trim().length < 2) {
- this.setData({ joinError: '请填写"我需要什么帮助"' })
- return
- }
- }
-
- this.setData({ isJoining: true, joinError: '' })
-
- try {
- const res = await app.request('/api/miniprogram/ckb/join', {
- method: 'POST',
- data: {
- type: joinType,
- phone: contactType === 'phone' ? phoneNumber : '',
- wechat: contactType === 'wechat' ? wechatId : '',
- userId: app.globalData.userInfo?.id || '',
- // 资源对接专属字段
- canHelp: joinType === 'investor' ? canHelp : '',
- needHelp: joinType === 'investor' ? needHelp : ''
- }
- })
-
- // 保存联系方式到本地
- if (phoneNumber) wx.setStorageSync('user_phone', phoneNumber)
- if (wechatId) wx.setStorageSync('user_wechat', wechatId)
-
- if (res.success) {
- this.setData({ joinSuccess: true })
- setTimeout(() => {
- this.setData({ showJoinModal: false, joinSuccess: false })
- }, 2000)
- } else {
- // 即使API返回失败,也模拟成功(因为已保存本地)
- this.setData({ joinSuccess: true })
- setTimeout(() => {
- this.setData({ showJoinModal: false, joinSuccess: false })
- }, 2000)
- }
- } catch (e) {
- // 网络错误时也模拟成功
- this.setData({ joinSuccess: true })
- setTimeout(() => {
- this.setData({ showJoinModal: false, joinSuccess: false })
- }, 2000)
- } finally {
- this.setData({ isJoining: false })
- }
- },
-
- // 关闭加入弹窗
- closeJoinModal() {
- if (this.data.isJoining) return
- this.setData({ showJoinModal: false, joinError: '' })
- },
-
- // 显示解锁弹窗
- showUnlockModal() {
- this.setData({ showUnlockModal: true })
- },
-
- // 关闭解锁弹窗
- closeUnlockModal() {
- this.setData({ showUnlockModal: false })
- },
-
- // 购买匹配次数
- async buyMatchCount() {
- this.setData({ showUnlockModal: false })
-
- try {
- // 获取openId
- let openId = app.globalData.openId || wx.getStorageSync('openId')
- if (!openId) {
- openId = await app.getOpenId()
- }
-
- if (!openId) {
- wx.showToast({ title: '请先登录', icon: 'none' })
- return
- }
-
- // 邀请码:与章节支付一致,写入订单便于分销归属与对账
- const referralCode = wx.getStorageSync('referral_code') || ''
- // 调用支付接口购买匹配次数
- const res = await app.request('/api/miniprogram/pay', {
- method: 'POST',
- data: {
- openId,
- productType: 'match',
- productId: 'match_1',
- amount: 1,
- description: '匹配次数x1',
- userId: app.globalData.userInfo?.id || '',
- referralCode: referralCode || undefined
- }
- })
-
- if (res.success && res.data?.payParams) {
- // 调用微信支付
- await new Promise((resolve, reject) => {
- wx.requestPayment({
- ...res.data.payParams,
- success: resolve,
- fail: reject
- })
- })
-
- // 支付成功,增加匹配次数
- const extraMatches = (wx.getStorageSync('extra_match_count') || 0) + 1
- wx.setStorageSync('extra_match_count', extraMatches)
-
- wx.showToast({ title: '购买成功', icon: 'success' })
- this.initUserStatus()
- } else {
- throw new Error(res.error || '创建订单失败')
- }
- } catch (e) {
- if (e.errMsg && e.errMsg.includes('cancel')) {
- wx.showToast({ title: '已取消', icon: 'none' })
- } else {
- // 测试模式
- wx.showModal({
- title: '支付服务暂不可用',
- content: '是否使用测试模式购买?',
- success: (res) => {
- if (res.confirm) {
- const extraMatches = (wx.getStorageSync('extra_match_count') || 0) + 1
- wx.setStorageSync('extra_match_count', extraMatches)
- wx.showToast({ title: '测试购买成功', icon: 'success' })
- this.initUserStatus()
- }
- }
- })
- }
- }
- },
-
- // 跳转到目录页购买
- goToChapters() {
- this.setData({ showUnlockModal: false })
- wx.switchTab({ url: '/pages/chapters/chapters' })
- },
-
- // 打开设置
- openSettings() {
- wx.navigateTo({ url: '/pages/settings/settings' })
- },
-
- // 阻止事件冒泡
- preventBubble() {}
-})
diff --git a/归档/miniprogram/pages/match/match.json b/归档/miniprogram/pages/match/match.json
deleted file mode 100644
index e7696321..00000000
--- a/归档/miniprogram/pages/match/match.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "usingComponents": {},
- "enablePullDownRefresh": false,
- "backgroundTextStyle": "light",
- "backgroundColor": "#000000"
-}
diff --git a/归档/miniprogram/pages/match/match.wxml b/归档/miniprogram/pages/match/match.wxml
deleted file mode 100644
index 3585aee0..00000000
--- a/归档/miniprogram/pages/match/match.wxml
+++ /dev/null
@@ -1,295 +0,0 @@
-
-
-
-
-
-
- 找伙伴
-
- ⚙️
-
-
-
-
-
-
-
-
-
-
- ⚡
- 今日免费次数已用完
- 购买次数
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ⚡
- 购买次数
- ¥1 = 1次匹配
-
-
- 👥
- 开始匹配
- 匹配{{currentTypeLabel}}
-
-
-
-
-
-
-
- 当前模式: {{currentTypeLabel}}
-
-
-
-
-
-
-
- 选择匹配类型
-
-
- {{item.icon}}
- {{item.label}}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 🔍
-
-
-
- ✨
- 💫
- ⭐
- 🌟
-
-
-
-
-
- 正在匹配{{currentTypeLabel}}...
- 正在从 {{matchAttempts * 127 + 89}} 位创业者中为你寻找
-
- ✓ 分析兴趣标签
- ✓ 匹配创业方向
- ✓ 筛选优质伙伴
-
- 取消
-
-
-
-
-
-
-
-
- ✨
-
-
-
-
-
-
-
-
- 共同兴趣
-
-
- {{item.icon}}
- {{item.text}}
-
-
-
-
-
-
- 核心理念
- {{currentMatch.concept}}
-
-
-
-
-
- 一键加好友
- 返回
-
-
-
-
-
-
-
-
-
-
-
- ✅
- 提交成功
- 工作人员将在24小时内与您联系
-
-
-
-
-
-
-
-
-
-
-
- 📱
- 手机号
-
-
- 💬
- 微信号
-
-
-
-
-
-
-
- 我能帮到你什么 *
-
-
-
- 我需要什么帮助 *
-
-
-
-
-
-
-
-
- {{contactType === 'phone' ? '+86' : '@'}}
-
-
-
- {{joinError}}
-
-
-
-
- {{isJoining ? '提交中...' : '确认提交'}}
-
-
- 提交后我们会尽快与您联系
-
-
-
-
-
-
-
- ⚡
- 购买匹配次数
- 今日3次免费匹配已用完,可付费购买额外次数
-
-
-
- 单价
- ¥{{matchPrice || 1}} / 次
-
-
- 已购买
- {{extraMatches || 0}} 次
-
-
-
-
- 立即购买 ¥{{matchPrice || 1}}
- 明天再来
-
-
-
-
-
-
-
diff --git a/归档/miniprogram/pages/match/match.wxss b/归档/miniprogram/pages/match/match.wxss
deleted file mode 100644
index 378e1c99..00000000
--- a/归档/miniprogram/pages/match/match.wxss
+++ /dev/null
@@ -1,1202 +0,0 @@
-/**
- * Soul创业实验 - 找伙伴页样式
- * 按H5网页端完全重构
- */
-
-.page {
- min-height: 100vh;
- background: #000000;
- padding-bottom: 200rpx;
-}
-
-/* ===== 导航栏 ===== */
-.nav-bar {
- position: fixed;
- top: 0;
- left: 0;
- right: 0;
- z-index: 100;
- background: rgba(0, 0, 0, 0.9);
- backdrop-filter: blur(40rpx);
-}
-
-.nav-content {
- height: 88rpx;
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 0 32rpx;
-}
-
-.nav-title {
- font-size: 36rpx;
- font-weight: 700;
- color: #ffffff;
-}
-
-.nav-settings {
- width: 80rpx;
- height: 80rpx;
- border-radius: 50%;
- background: #1c1c1e;
- display: flex;
- align-items: center;
- justify-content: center;
-}
-
-.settings-icon {
- font-size: 36rpx;
-}
-
-.nav-placeholder {
- width: 100%;
-}
-
-/* ===== 匹配提示条 - 简化版 ===== */
-.match-tip-bar {
- display: flex;
- align-items: center;
- justify-content: center;
- gap: 16rpx;
- margin: 24rpx 32rpx;
- padding: 20rpx 32rpx;
- background: rgba(255, 215, 0, 0.1);
- border-radius: 16rpx;
- border: 1rpx solid rgba(255, 215, 0, 0.2);
-}
-
-.tip-icon {
- font-size: 28rpx;
-}
-
-.tip-text {
- font-size: 26rpx;
- color: rgba(255, 255, 255, 0.7);
-}
-
-.tip-btn {
- padding: 10rpx 24rpx;
- background: linear-gradient(135deg, #FFD700 0%, #FFA500 100%);
- color: #000;
- font-size: 24rpx;
- font-weight: 500;
- border-radius: 20rpx;
-}
-
-.text-brand {
- color: #00CED1;
-}
-
-.text-red {
- color: #ff4444;
-}
-
-.text-gray {
- color: #666666;
-}
-
-.text-muted {
- color: rgba(255, 255, 255, 0.4);
-}
-
-.gold-text {
- color: #FFD700;
-}
-
-/* ===== 主内容区 ===== */
-.main-content {
- padding: 0 32rpx;
-}
-
-/* ===== 匹配圆环 ===== */
-.match-circle-wrapper {
- position: relative;
- width: 480rpx;
- height: 480rpx;
- margin: 48rpx auto;
-}
-
-.outer-glow {
- position: absolute;
- inset: -60rpx;
- border-radius: 50%;
- animation: pulseGlow 2s ease-in-out infinite;
-}
-
-.outer-glow.glow-active {
- background: radial-gradient(circle, transparent 50%, rgba(0, 229, 255, 0.1) 70%, transparent 100%);
-}
-
-.outer-glow.glow-inactive {
- background: radial-gradient(circle, transparent 50%, rgba(100, 100, 100, 0.1) 70%, transparent 100%);
-}
-
-@keyframes pulseGlow {
- 0%, 100% { transform: scale(1); opacity: 0.5; }
- 50% { transform: scale(1.1); opacity: 0.8; }
-}
-
-.middle-ring {
- position: absolute;
- inset: -30rpx;
- border-radius: 50%;
- border: 4rpx solid;
- animation: pulseRing 1.5s ease-in-out infinite;
-}
-
-.middle-ring.ring-active {
- border-color: rgba(0, 229, 255, 0.3);
-}
-
-.middle-ring.ring-inactive {
- border-color: rgba(100, 100, 100, 0.3);
-}
-
-@keyframes pulseRing {
- 0%, 100% { transform: scale(1); opacity: 0.3; }
- 50% { transform: scale(1.05); opacity: 0.6; }
-}
-
-.inner-sphere {
- position: absolute;
- inset: 0;
- border-radius: 50%;
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- animation: floatSphere 3s ease-in-out infinite;
- overflow: hidden;
-}
-
-.inner-sphere.sphere-active {
- background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
- box-shadow: 0 0 120rpx rgba(0, 229, 255, 0.3), inset 0 0 120rpx rgba(123, 97, 255, 0.2);
-}
-
-.inner-sphere.sphere-inactive {
- background: linear-gradient(135deg, #1a1a1a 0%, #2a2a2a 50%, #1a1a1a 100%);
- box-shadow: 0 0 60rpx rgba(100, 100, 100, 0.2);
-}
-
-@keyframes floatSphere {
- 0%, 100% { transform: translateY(0); }
- 50% { transform: translateY(-10rpx); }
-}
-
-.sphere-gradient {
- position: absolute;
- inset: 0;
- border-radius: 50%;
- background: radial-gradient(circle at 30% 30%, rgba(123, 97, 255, 0.4) 0%, transparent 50%),
- radial-gradient(circle at 70% 70%, rgba(233, 30, 99, 0.3) 0%, transparent 50%);
-}
-
-.sphere-content {
- position: relative;
- z-index: 1;
- display: flex;
- flex-direction: column;
- align-items: center;
- text-align: center;
-}
-
-.sphere-icon {
- font-size: 96rpx;
- margin-bottom: 16rpx;
-}
-
-.sphere-title {
- font-size: 36rpx;
- font-weight: 700;
- color: #ffffff;
- margin-bottom: 8rpx;
-}
-
-.sphere-desc {
- font-size: 26rpx;
- color: rgba(255, 255, 255, 0.6);
-}
-
-/* ===== 当前模式 ===== */
-.current-mode {
- text-align: center;
- font-size: 26rpx;
- color: rgba(255, 255, 255, 0.5);
- margin-bottom: 16rpx;
-}
-
-/* ===== 免费次数提示 ===== */
-.free-tip {
- text-align: center;
- font-size: 24rpx;
- color: rgba(255, 255, 255, 0.4);
- margin-bottom: 32rpx;
-}
-
-/* ===== 购买提示卡片 ===== */
-.purchase-tip-card {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 32rpx;
- background: linear-gradient(90deg, rgba(0, 229, 255, 0.1) 0%, transparent 100%);
- border: 2rpx solid rgba(0, 229, 255, 0.2);
- border-radius: 24rpx;
- margin-bottom: 32rpx;
-}
-
-.tip-left {
- flex: 1;
-}
-
-.tip-title {
- display: block;
- font-size: 28rpx;
- font-weight: 500;
- color: #ffffff;
- margin-bottom: 8rpx;
-}
-
-.tip-desc {
- font-size: 24rpx;
- color: rgba(255, 255, 255, 0.6);
-}
-
-.tip-btn {
- padding: 16rpx 32rpx;
- background: #00CED1;
- color: #000000;
- font-size: 26rpx;
- font-weight: 500;
- border-radius: 16rpx;
-}
-
-/* ===== 分隔线 ===== */
-.divider {
- height: 2rpx;
- background: rgba(255, 255, 255, 0.1);
- margin: 32rpx 0;
-}
-
-/* ===== 匹配类型选择 ===== */
-.type-section {
- margin-bottom: 32rpx;
-}
-
-.type-section-title {
- display: block;
- font-size: 26rpx;
- color: rgba(255, 255, 255, 0.4);
- text-align: center;
- margin-bottom: 24rpx;
-}
-
-.type-grid {
- display: grid;
- grid-template-columns: repeat(4, 1fr);
- gap: 20rpx;
-}
-
-.type-item {
- display: flex;
- flex-direction: column;
- align-items: center;
- gap: 16rpx;
- padding: 32rpx 16rpx;
- background: #1c1c1e;
- border-radius: 24rpx;
- border: 2rpx solid transparent;
- transition: all 0.2s;
-}
-
-.type-item.type-active {
- background: rgba(0, 229, 255, 0.1);
- border-color: rgba(0, 229, 255, 0.5);
-}
-
-.type-icon {
- font-size: 48rpx;
-}
-
-.type-label {
- font-size: 22rpx;
- color: rgba(255, 255, 255, 0.6);
- text-align: center;
-}
-
-/* ===== 匹配中状态 ===== */
-.matching-state {
- display: flex;
- flex-direction: column;
- align-items: center;
- padding: 48rpx 0;
-}
-
-.matching-animation {
- position: relative;
- width: 400rpx;
- height: 400rpx;
- margin-bottom: 48rpx;
-}
-
-.matching-ring {
- position: absolute;
- inset: 0;
- border-radius: 50%;
- background: linear-gradient(135deg, #00CED1, #7B61FF, #E91E63);
- animation: rotateRing 3s linear infinite;
-}
-
-@keyframes rotateRing {
- to { transform: rotate(360deg); }
-}
-
-.matching-center {
- position: absolute;
- inset: 16rpx;
- border-radius: 50%;
- background: #000000;
- display: flex;
- align-items: center;
- justify-content: center;
-}
-
-.matching-icon {
- font-size: 96rpx;
- animation: pulseIcon 1s ease-in-out infinite;
-}
-
-@keyframes pulseIcon {
- 0%, 100% { transform: scale(1); }
- 50% { transform: scale(1.2); }
-}
-
-.ripple {
- position: absolute;
- inset: 0;
- border-radius: 50%;
- border: 4rpx solid rgba(0, 229, 255, 0.3);
- animation: rippleExpand 2s ease-out infinite;
-}
-
-.ripple-1 { animation-delay: 0s; }
-.ripple-2 { animation-delay: 0.5s; }
-.ripple-3 { animation-delay: 1s; }
-
-@keyframes rippleExpand {
- 0% { transform: scale(1); opacity: 0.6; }
- 100% { transform: scale(2); opacity: 0; }
-}
-
-.matching-title {
- font-size: 36rpx;
- font-weight: 600;
- color: #ffffff;
- margin-bottom: 16rpx;
-}
-
-.matching-count {
- font-size: 28rpx;
- color: rgba(255, 255, 255, 0.5);
- margin-bottom: 48rpx;
-}
-
-.cancel-btn {
- padding: 24rpx 64rpx;
- background: #1c1c1e;
- color: #ffffff;
- font-size: 28rpx;
- border-radius: 48rpx;
- border: 2rpx solid rgba(255, 255, 255, 0.1);
-}
-
-/* ===== 匹配成功状态 ===== */
-.matched-state {
- padding: 32rpx 0;
-}
-
-.success-icon-wrapper {
- text-align: center;
- margin-bottom: 32rpx;
-}
-
-.success-icon {
- font-size: 120rpx;
-}
-
-.match-card {
- background: #1c1c1e;
- border-radius: 32rpx;
- padding: 40rpx;
- border: 2rpx solid rgba(255, 255, 255, 0.05);
- margin-bottom: 32rpx;
-}
-
-.card-header {
- display: flex;
- align-items: center;
- gap: 24rpx;
- margin-bottom: 32rpx;
-}
-
-.match-avatar {
- width: 128rpx;
- height: 128rpx;
- border-radius: 50%;
- border: 4rpx solid #00CED1;
- flex-shrink: 0;
-}
-
-.match-info {
- flex: 1;
- min-width: 0;
-}
-
-.match-name {
- display: block;
- font-size: 32rpx;
- font-weight: 600;
- color: #ffffff;
- margin-bottom: 12rpx;
-}
-
-.match-tags {
- display: flex;
- flex-wrap: wrap;
- gap: 8rpx;
-}
-
-.match-tag {
- padding: 8rpx 16rpx;
- background: rgba(0, 229, 255, 0.2);
- color: #00CED1;
- font-size: 20rpx;
- border-radius: 8rpx;
-}
-
-.match-score-box {
- text-align: center;
- flex-shrink: 0;
-}
-
-.score-value {
- display: block;
- font-size: 48rpx;
- font-weight: 700;
- color: #00CED1;
-}
-
-.score-label {
- font-size: 22rpx;
- color: rgba(255, 255, 255, 0.5);
-}
-
-.card-section {
- padding-top: 24rpx;
- border-top: 2rpx solid rgba(255, 255, 255, 0.1);
- margin-top: 24rpx;
-}
-
-.section-title {
- display: block;
- font-size: 24rpx;
- color: rgba(255, 255, 255, 0.6);
- margin-bottom: 16rpx;
-}
-
-.interest-list {
- display: flex;
- flex-direction: column;
- gap: 12rpx;
-}
-
-.interest-item {
- display: flex;
- align-items: center;
- gap: 16rpx;
-}
-
-.interest-icon {
- font-size: 28rpx;
-}
-
-.interest-text {
- font-size: 26rpx;
- color: rgba(255, 255, 255, 0.8);
-}
-
-.concept-text {
- font-size: 26rpx;
- color: rgba(255, 255, 255, 0.7);
- line-height: 1.6;
-}
-
-/* ===== 操作按钮 ===== */
-.action-buttons {
- display: flex;
- flex-direction: column;
- gap: 20rpx;
-}
-
-.btn-primary {
- padding: 32rpx;
- background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%);
- color: #ffffff;
- font-size: 32rpx;
- font-weight: 600;
- text-align: center;
- border-radius: 24rpx;
-}
-
-.btn-secondary {
- padding: 32rpx;
- background: #1c1c1e;
- color: #ffffff;
- font-size: 32rpx;
- text-align: center;
- border-radius: 24rpx;
- border: 2rpx solid rgba(255, 255, 255, 0.1);
-}
-
-.btn-disabled {
- opacity: 0.5;
-}
-
-/* ===== 弹窗 ===== */
-.modal-overlay {
- position: fixed;
- inset: 0;
- background: rgba(0, 0, 0, 0.6);
- backdrop-filter: blur(20rpx);
- display: flex;
- align-items: center;
- justify-content: center;
- z-index: 1000;
- padding: 48rpx;
-}
-
-.modal-content {
- width: 100%;
- max-width: 640rpx;
- background: #1c1c1e;
- border-radius: 32rpx;
- overflow: hidden;
-}
-
-/* ===== 加入弹窗 ===== */
-.join-modal {
- padding: 40rpx;
-}
-
-.modal-header {
- display: flex;
- align-items: center;
- justify-content: space-between;
- margin-bottom: 24rpx;
-}
-
-.modal-title {
- font-size: 36rpx;
- font-weight: 600;
- color: #ffffff;
-}
-
-.close-btn {
- width: 64rpx;
- height: 64rpx;
- border-radius: 50%;
- background: rgba(255, 255, 255, 0.1);
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 32rpx;
- color: rgba(255, 255, 255, 0.6);
-}
-
-.form-tip {
- display: block;
- font-size: 26rpx;
- color: rgba(255, 255, 255, 0.6);
- margin-bottom: 24rpx;
-}
-
-.contact-tabs {
- display: flex;
- gap: 16rpx;
- margin-bottom: 24rpx;
-}
-
-.contact-tab {
- flex: 1;
- padding: 20rpx;
- text-align: center;
- font-size: 28rpx;
- font-weight: 500;
- color: rgba(255, 255, 255, 0.6);
- background: rgba(255, 255, 255, 0.05);
- border: 2rpx solid rgba(255, 255, 255, 0.1);
- border-radius: 16rpx;
-}
-
-.contact-tab.tab-active-phone {
- background: rgba(0, 229, 255, 0.2);
- color: #00CED1;
- border-color: rgba(0, 229, 255, 0.3);
-}
-
-.contact-tab.tab-active-wechat {
- background: rgba(7, 193, 96, 0.2);
- color: #07C160;
- border-color: rgba(7, 193, 96, 0.3);
-}
-
-.input-group {
- margin-bottom: 24rpx;
-}
-
-.input-label {
- display: block;
- font-size: 24rpx;
- color: rgba(255, 255, 255, 0.4);
- margin-bottom: 12rpx;
-}
-
-.form-input {
- width: 100%;
- padding: 28rpx;
- background: rgba(0, 0, 0, 0.3);
- border: 2rpx solid rgba(255, 255, 255, 0.1);
- border-radius: 20rpx;
- font-size: 32rpx;
- color: #ffffff;
- box-sizing: border-box;
-}
-
-.input-placeholder {
- color: rgba(255, 255, 255, 0.3);
-}
-
-.error-text {
- display: block;
- font-size: 24rpx;
- color: #ff4444;
- margin-bottom: 16rpx;
-}
-
-.submit-btn {
- margin-top: 16rpx;
-}
-
-.form-notice {
- display: block;
- font-size: 22rpx;
- color: rgba(255, 255, 255, 0.3);
- text-align: center;
- margin-top: 24rpx;
-}
-
-/* ===== 新版加入弹窗 ===== */
-.join-modal-new {
- padding: 0;
- border-radius: 32rpx;
- overflow: hidden;
-}
-
-.join-header {
- position: relative;
- padding: 48rpx 40rpx 32rpx;
- background: linear-gradient(135deg, rgba(0, 206, 209, 0.15) 0%, rgba(123, 97, 255, 0.1) 100%);
- text-align: center;
-}
-
-.join-icon-wrap {
- width: 100rpx;
- height: 100rpx;
- margin: 0 auto 20rpx;
- background: rgba(0, 0, 0, 0.3);
- border-radius: 50%;
- display: flex;
- align-items: center;
- justify-content: center;
-}
-
-.join-icon {
- font-size: 48rpx;
-}
-
-.join-title {
- display: block;
- font-size: 36rpx;
- font-weight: 600;
- color: #ffffff;
- margin-bottom: 8rpx;
-}
-
-.join-subtitle {
- display: block;
- font-size: 26rpx;
- color: rgba(255, 255, 255, 0.6);
-}
-
-.close-btn-new {
- position: absolute;
- top: 24rpx;
- right: 24rpx;
- width: 56rpx;
- height: 56rpx;
- border-radius: 50%;
- background: rgba(255, 255, 255, 0.1);
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 28rpx;
- color: rgba(255, 255, 255, 0.6);
-}
-
-.contact-switch {
- display: flex;
- gap: 16rpx;
- padding: 24rpx 40rpx;
-}
-
-.switch-item {
- flex: 1;
- display: flex;
- align-items: center;
- justify-content: center;
- gap: 12rpx;
- padding: 24rpx;
- background: rgba(255, 255, 255, 0.05);
- border-radius: 16rpx;
- font-size: 28rpx;
- color: rgba(255, 255, 255, 0.6);
- border: 2rpx solid transparent;
-}
-
-.switch-item.switch-active {
- background: rgba(0, 206, 209, 0.15);
- color: #00CED1;
- border-color: rgba(0, 206, 209, 0.3);
-}
-
-.switch-icon {
- font-size: 32rpx;
-}
-
-.input-area {
- padding: 0 40rpx 24rpx;
-}
-
-.input-wrapper {
- display: flex;
- align-items: center;
- background: rgba(0, 0, 0, 0.3);
- border: 2rpx solid rgba(255, 255, 255, 0.1);
- border-radius: 20rpx;
- overflow: hidden;
-}
-
-.input-prefix {
- padding: 0 24rpx;
- font-size: 28rpx;
- color: rgba(255, 255, 255, 0.5);
- border-right: 1rpx solid rgba(255, 255, 255, 0.1);
-}
-
-.input-field {
- flex: 1;
- padding: 28rpx 24rpx;
- font-size: 32rpx;
- color: #ffffff;
-}
-
-.input-placeholder-new {
- color: rgba(255, 255, 255, 0.3);
-}
-
-.error-msg {
- display: block;
- font-size: 24rpx;
- color: #ff4444;
- margin-top: 12rpx;
- padding-left: 8rpx;
-}
-
-.submit-btn-new {
- margin: 8rpx 40rpx 24rpx;
- padding: 28rpx;
- background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%);
- color: #ffffff;
- font-size: 32rpx;
- font-weight: 600;
- text-align: center;
- border-radius: 20rpx;
-}
-
-.btn-disabled-new {
- opacity: 0.5;
-}
-
-.form-notice-new {
- display: block;
- text-align: center;
- font-size: 22rpx;
- color: rgba(255, 255, 255, 0.3);
- padding-bottom: 32rpx;
-}
-
-/* ===== 新版加入成功 ===== */
-.join-success-new {
- padding: 64rpx 40rpx;
- text-align: center;
-}
-
-.success-icon-big {
- font-size: 96rpx;
- display: block;
- margin-bottom: 24rpx;
-}
-
-.success-title-new {
- display: block;
- font-size: 36rpx;
- font-weight: 600;
- color: #ffffff;
- margin-bottom: 12rpx;
-}
-
-.success-desc-new {
- font-size: 26rpx;
- color: rgba(255, 255, 255, 0.6);
-}
-
-/* ===== 旧版加入成功 (保留兼容) ===== */
-.join-success {
- padding: 48rpx;
- text-align: center;
-}
-
-.success-check {
- font-size: 128rpx;
- display: block;
- margin-bottom: 24rpx;
-}
-
-.success-title {
- display: block;
- font-size: 36rpx;
- font-weight: 600;
- color: #ffffff;
- margin-bottom: 12rpx;
-}
-
-.success-desc {
- font-size: 26rpx;
- color: rgba(255, 255, 255, 0.6);
-}
-
-/* ===== 解锁弹窗 ===== */
-.unlock-modal {
- padding: 48rpx;
- text-align: center;
-}
-
-.unlock-icon {
- width: 128rpx;
- height: 128rpx;
- margin: 0 auto 24rpx;
- background: rgba(255, 215, 0, 0.2);
- border-radius: 50%;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 64rpx;
-}
-
-.unlock-title {
- display: block;
- font-size: 36rpx;
- font-weight: 700;
- color: #ffffff;
- margin-bottom: 12rpx;
-}
-
-.unlock-desc {
- display: block;
- font-size: 26rpx;
- color: rgba(255, 255, 255, 0.6);
- margin-bottom: 32rpx;
-}
-
-.unlock-info {
- background: rgba(0, 0, 0, 0.3);
- border-radius: 20rpx;
- padding: 24rpx;
- margin-bottom: 32rpx;
-}
-
-.info-row {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 12rpx 0;
-}
-
-.info-label {
- font-size: 26rpx;
- color: rgba(255, 255, 255, 0.6);
-}
-
-.info-value {
- font-size: 26rpx;
- font-weight: 500;
- color: #ffffff;
-}
-
-.unlock-buttons {
- display: flex;
- flex-direction: column;
- gap: 16rpx;
-}
-
-.btn-gold {
- padding: 28rpx;
- background: linear-gradient(135deg, #FFD700 0%, #FFA500 100%);
- color: #000000;
- font-size: 30rpx;
- font-weight: 600;
- text-align: center;
- border-radius: 24rpx;
-}
-
-.btn-ghost {
- padding: 28rpx;
- background: rgba(255, 255, 255, 0.05);
- color: rgba(255, 255, 255, 0.6);
- font-size: 28rpx;
- text-align: center;
- border-radius: 24rpx;
-}
-
-/* ===== 底部留白 ===== */
-.bottom-space {
- height: 40rpx;
-}
-
-/* ===== 新版匹配动画 V2 ===== */
-.matching-animation-v2 {
- position: relative;
- width: 440rpx;
- height: 440rpx;
- margin: 0 auto 48rpx;
-}
-
-/* 外层旋转光环 */
-.matching-outer-ring {
- position: absolute;
- inset: -20rpx;
- border-radius: 50%;
- background: conic-gradient(
- from 0deg,
- transparent 0deg,
- #00CED1 60deg,
- #7B61FF 120deg,
- #E91E63 180deg,
- #FFD700 240deg,
- #00CED1 300deg,
- transparent 360deg
- );
- animation: rotateRingV2 2s linear infinite;
- opacity: 0.8;
-}
-
-.matching-outer-ring::before {
- content: '';
- position: absolute;
- inset: 8rpx;
- border-radius: 50%;
- background: #000;
-}
-
-@keyframes rotateRingV2 {
- to { transform: rotate(360deg); }
-}
-
-/* 中层脉冲环 */
-.matching-pulse-ring {
- position: absolute;
- inset: 20rpx;
- border-radius: 50%;
- border: 4rpx solid rgba(0, 206, 209, 0.5);
- animation: pulseRingV2 1.5s ease-in-out infinite;
-}
-
-@keyframes pulseRingV2 {
- 0%, 100% { transform: scale(1); opacity: 0.5; }
- 50% { transform: scale(1.1); opacity: 1; }
-}
-
-/* 内层核心球体 */
-.matching-core {
- position: absolute;
- inset: 60rpx;
- border-radius: 50%;
- background: linear-gradient(135deg, #1a2a4a 0%, #0a1628 50%, #16213e 100%);
- box-shadow:
- 0 0 60rpx rgba(0, 206, 209, 0.4),
- 0 0 120rpx rgba(123, 97, 255, 0.2),
- inset 0 0 80rpx rgba(0, 206, 209, 0.1);
- display: flex;
- align-items: center;
- justify-content: center;
- animation: floatCoreV2 2s ease-in-out infinite;
-}
-
-.matching-core-inner {
- width: 160rpx;
- height: 160rpx;
- border-radius: 50%;
- background: radial-gradient(circle, rgba(0, 206, 209, 0.3) 0%, transparent 70%);
- display: flex;
- align-items: center;
- justify-content: center;
-}
-
-@keyframes floatCoreV2 {
- 0%, 100% { transform: translateY(0) scale(1); }
- 50% { transform: translateY(-10rpx) scale(1.02); }
-}
-
-.matching-icon-v2 {
- font-size: 80rpx;
- animation: searchIconV2 1s ease-in-out infinite;
-}
-
-@keyframes searchIconV2 {
- 0%, 100% { transform: rotate(-15deg); }
- 50% { transform: rotate(15deg); }
-}
-
-/* 粒子效果 */
-.particle {
- position: absolute;
- font-size: 32rpx;
- animation: floatParticle 3s ease-in-out infinite;
- opacity: 0.8;
-}
-
-.particle-1 { top: 10%; left: 15%; animation-delay: 0s; }
-.particle-2 { top: 20%; right: 10%; animation-delay: 0.5s; }
-.particle-3 { bottom: 20%; left: 10%; animation-delay: 1s; }
-.particle-4 { bottom: 15%; right: 15%; animation-delay: 1.5s; }
-
-@keyframes floatParticle {
- 0%, 100% { transform: translateY(0) rotate(0deg); opacity: 0.4; }
- 50% { transform: translateY(-20rpx) rotate(180deg); opacity: 1; }
-}
-
-/* 扩散波纹 V2 */
-.ripple-v2 {
- position: absolute;
- inset: 40rpx;
- border-radius: 50%;
- border: 3rpx solid;
- border-color: rgba(0, 206, 209, 0.6);
- animation: rippleExpandV2 2.5s ease-out infinite;
-}
-
-.ripple-v2-1 { animation-delay: 0s; }
-.ripple-v2-2 { animation-delay: 0.8s; }
-.ripple-v2-3 { animation-delay: 1.6s; }
-
-@keyframes rippleExpandV2 {
- 0% { transform: scale(1); opacity: 0.8; }
- 100% { transform: scale(1.8); opacity: 0; }
-}
-
-/* 新版匹配文字 */
-.matching-title-v2 {
- display: block;
- font-size: 38rpx;
- font-weight: 700;
- color: #ffffff;
- text-align: center;
- margin-bottom: 12rpx;
- background: linear-gradient(90deg, #00CED1, #7B61FF, #00CED1);
- background-size: 200% auto;
- -webkit-background-clip: text;
- -webkit-text-fill-color: transparent;
- animation: shineText 2s linear infinite;
-}
-
-@keyframes shineText {
- to { background-position: 200% center; }
-}
-
-.matching-subtitle-v2 {
- display: block;
- font-size: 26rpx;
- color: rgba(255, 255, 255, 0.5);
- text-align: center;
- margin-bottom: 32rpx;
-}
-
-.matching-tips {
- display: flex;
- flex-direction: column;
- align-items: center;
- gap: 16rpx;
- margin-bottom: 40rpx;
-}
-
-.tip-item {
- font-size: 26rpx;
- color: #00CED1;
- animation: fadeInUp 0.5s ease-out forwards;
- opacity: 0;
-}
-
-.tip-item:nth-child(1) { animation-delay: 0.5s; }
-.tip-item:nth-child(2) { animation-delay: 1.5s; }
-.tip-item:nth-child(3) { animation-delay: 2.5s; }
-
-@keyframes fadeInUp {
- from { opacity: 0; transform: translateY(20rpx); }
- to { opacity: 1; transform: translateY(0); }
-}
-
-.cancel-btn-v2 {
- display: inline-block;
- padding: 20rpx 60rpx;
- background: rgba(255, 255, 255, 0.1);
- color: rgba(255, 255, 255, 0.6);
- font-size: 28rpx;
- border-radius: 40rpx;
- border: 1rpx solid rgba(255, 255, 255, 0.2);
-}
-
-/* 资源对接表单 */
-.resource-form {
- display: flex;
- flex-direction: column;
- gap: 20rpx;
- margin-bottom: 24rpx;
-}
-.resource-form .form-item {
- display: flex;
- flex-direction: column;
- gap: 8rpx;
-}
-.resource-form .form-label {
- font-size: 26rpx;
- color: rgba(255,255,255,0.6);
-}
-.resource-form .form-input-new {
- background: #1c1c1e;
- border: 2rpx solid rgba(0,206,209,0.3);
- border-radius: 16rpx;
- padding: 20rpx;
- font-size: 28rpx;
- color: #fff;
-}
diff --git a/归档/miniprogram/pages/member-detail/member-detail.js b/归档/miniprogram/pages/member-detail/member-detail.js
deleted file mode 100644
index 04533440..00000000
--- a/归档/miniprogram/pages/member-detail/member-detail.js
+++ /dev/null
@@ -1,91 +0,0 @@
-const app = getApp()
-
-const MOCK_ENRICHMENT = [
- { mbti: 'ENTJ', region: '深圳', skills: '电商运营、供应链管理、团队搭建', contactRaw: '13800138001', bestMonth: '做跨境电商独立站,单月GMV突破200万,净利润35万', achievement: '从0到1搭建了30人的电商团队,年营收破3000万', turningPoint: '2019年从传统外贸转型跨境电商,放弃稳定薪资All in创业', canHelp: '电商选品、供应链对接、团队管理SOP', needHelp: '寻找品牌合作方和内容营销人才', project: '跨境电商独立站+亚马逊多店铺运营,主营家居类目' },
- { mbti: 'INFP', region: '杭州', skills: '短视频制作、IP打造、私域运营', contactRaw: '13900139002', bestMonth: '旅游账号30天涨粉10万,带货佣金收入12万', achievement: '帮助3个素人打造个人IP,每个月稳定变现5万+', turningPoint: '辞去互联网大厂工作开始做自媒体,第三个月就超过原薪资', canHelp: '短视频脚本、账号冷启动、私域转化设计', needHelp: '寻找供应链资源和线下活动合作', project: '旅游+生活方式自媒体矩阵,全网粉丝50万+' },
- { mbti: 'INTP', region: '厦门', skills: 'AI开发、小程序开发、系统架构', contactRaw: '13700137003', bestMonth: 'AI客服系统外包项目,单月收入18万', achievement: '独立开发的SaaS产品获得天使轮200万融资', turningPoint: '从程序员转型技术创业者,学会用技术解决商业问题', canHelp: '技术方案设计、AI应用落地、小程序开发', needHelp: '需要商业化运营和市场推广合伙人', project: 'AI+私域运营工具SaaS平台' },
- { mbti: 'ESTP', region: '成都', skills: '资源对接、商务BD、活动策划', contactRaw: '13600136004', bestMonth: '撮合景区合作,居间费收入25万', achievement: '组建覆盖全国50+城市创业者社群,活跃成员3000+', turningPoint: '在Soul派对房认识第一个合伙人,打开了社交创业的大门', canHelp: '各行业资源对接、活动策划、社群引荐', needHelp: '寻找技术合伙人和内容创作者', project: '创业者资源对接平台+线下创业者沙龙' }
-]
-
-Page({
- data: { statusBarHeight: 44, member: null, loading: true },
-
- onLoad(options) {
- this.setData({ statusBarHeight: app.globalData.statusBarHeight })
- if (options.id) this.loadMember(options.id)
- },
-
- async loadMember(id) {
- try {
- const res = await app.request({ url: `/api/vip/members?id=${id}`, silent: true })
- if (res?.success && res.data) {
- const d = Array.isArray(res.data) ? res.data[0] : res.data
- if (d) { this.setData({ member: this.enrichAndFormat(d), loading: false }); return }
- }
- } catch (e) {}
-
- try {
- const dbRes = await app.request({ url: `/api/miniprogram/users?id=${id}`, silent: true })
- if (dbRes?.success && dbRes.data) {
- const u = Array.isArray(dbRes.data) ? dbRes.data[0] : dbRes.data
- if (u) {
- this.setData({ member: this.enrichAndFormat({
- id: u.id, name: u.vip_name || u.nickname || '创业者',
- avatar: u.vip_avatar || u.avatar || '', isVip: u.is_vip === 1,
- contactRaw: u.vip_contact || u.phone || '', project: u.vip_project || '',
- bio: u.vip_bio || '', mbti: '', region: '', skills: '',
- }), loading: false })
- return
- }
- }
- } catch (e) {}
- this.setData({ loading: false })
- },
-
- enrichAndFormat(raw) {
- const hash = (raw.id || '').split('').reduce((a, c) => a + c.charCodeAt(0), 0)
- const mock = MOCK_ENRICHMENT[hash % MOCK_ENRICHMENT.length]
-
- const merged = {
- id: raw.id,
- name: raw.name || raw.vip_name || raw.nickname || '创业者',
- avatar: raw.avatar || raw.vip_avatar || '',
- isVip: raw.isVip || raw.is_vip === 1,
- mbti: raw.mbti || mock.mbti,
- region: raw.region || mock.region,
- skills: raw.skills || mock.skills,
- contactRaw: raw.contactRaw || raw.vip_contact || mock.contactRaw,
- bestMonth: raw.bestMonth || mock.bestMonth,
- achievement: raw.achievement || mock.achievement,
- turningPoint: raw.turningPoint || mock.turningPoint,
- canHelp: raw.canHelp || mock.canHelp,
- needHelp: raw.needHelp || mock.needHelp,
- project: raw.project || raw.vip_project || mock.project
- }
-
- const contact = merged.contactRaw || ''
- const isMatched = (app.globalData.matchedUsers || []).includes(merged.id)
- merged.contactDisplay = contact ? (contact.slice(0, 3) + '****' + (contact.length > 7 ? contact.slice(-2) : '')) : ''
- merged.contactUnlocked = isMatched
- merged.contactFull = contact
- return merged
- },
-
- unlockContact() {
- wx.showModal({
- title: '解锁完整联系方式', content: '成为VIP会员并完成匹配后,即可查看完整联系方式',
- confirmText: '去匹配', cancelText: '知道了',
- success: (res) => { if (res.confirm) wx.switchTab({ url: '/pages/match/match' }) }
- })
- },
-
- copyContact() {
- const c = this.data.member?.contactFull
- if (!c) return
- wx.setClipboardData({ data: c, success: () => wx.showToast({ title: '已复制', icon: 'success' }) })
- },
-
- goToMatch() { wx.switchTab({ url: '/pages/match/match' }) },
- goToVip() { wx.navigateTo({ url: '/pages/vip/vip' }) },
- goBack() { wx.navigateBack() }
-})
diff --git a/归档/miniprogram/pages/member-detail/member-detail.json b/归档/miniprogram/pages/member-detail/member-detail.json
deleted file mode 100644
index 52bdd937..00000000
--- a/归档/miniprogram/pages/member-detail/member-detail.json
+++ /dev/null
@@ -1 +0,0 @@
-{ "usingComponents": {}, "navigationStyle": "custom" }
diff --git a/归档/miniprogram/pages/member-detail/member-detail.wxml b/归档/miniprogram/pages/member-detail/member-detail.wxml
deleted file mode 100644
index ca8b155f..00000000
--- a/归档/miniprogram/pages/member-detail/member-detail.wxml
+++ /dev/null
@@ -1,101 +0,0 @@
-
-
- ‹
- 超级个体
-
-
-
-
-
-
-
-
-
-
-
-
- {{member.name[0] || '创'}}
- VIP
-
- {{member.name}}
-
- {{member.mbti}}
- 📍{{member.region}}
-
-
-
-
-
-
- 👤基本信息
-
- 我擅长
- {{member.skills}}
-
-
- 联系方式
-
- {{member.contactDisplay}}
-
- 🔒匹配解锁
-
- 复制
-
-
-
-
-
-
- 💡个人故事
-
- 🏆 最赚钱的一个月做的是什么
- {{member.bestMonth}}
-
-
-
- ⭐ 最有成就感的一件事
- {{member.achievement}}
-
-
-
- 🔄 人生的转折点
- {{member.turningPoint}}
-
-
-
-
-
- 🤝互助需求
-
- 我能帮到你
- {{member.canHelp}}
-
-
- 我需要帮助
- {{member.needHelp}}
-
-
-
-
-
- 🚀项目介绍
- {{member.project}}
-
-
-
-
- 开始匹配 · 解锁联系方式
- 成为超级个体 →
-
-
-
-
-
-
-
- 加载中...
-
-
- 👤暂无该超级个体信息
-
-
diff --git a/归档/miniprogram/pages/member-detail/member-detail.wxss b/归档/miniprogram/pages/member-detail/member-detail.wxss
deleted file mode 100644
index 7afe12e4..00000000
--- a/归档/miniprogram/pages/member-detail/member-detail.wxss
+++ /dev/null
@@ -1,75 +0,0 @@
-.page { background: #050a10; min-height: 100vh; color: #fff; }
-
-/* 导航 */
-.nav-bar { position: fixed; top: 0; left: 0; right: 0; z-index: 999; display: flex; align-items: center; justify-content: space-between; padding: 0 24rpx; height: 44px; background: rgba(5,10,16,.92); backdrop-filter: blur(24px); -webkit-backdrop-filter: blur(24px); border-bottom: 1rpx solid rgba(56,189,172,.08); }
-.back-arrow { font-size: 48rpx; color: #38bdac; font-weight: 300; }
-.nav-title { font-size: 32rpx; font-weight: 600; letter-spacing: 2rpx; }
-.nav-ph { width: 48rpx; }
-.nav-back { width: 48rpx; height: 48rpx; display: flex; align-items: center; justify-content: center; }
-
-.scroll-wrap { height: calc(100vh - 88px); }
-
-/* ===== 顶部名片 ===== */
-.card-hero { position: relative; margin: 24rpx 24rpx 0; padding: 56rpx 0 40rpx; border-radius: 28rpx; overflow: hidden; background: linear-gradient(160deg, #0d1f2d 0%, #0a1620 40%, #101828 100%); border: 1rpx solid rgba(56,189,172,.15); }
-.hero-deco { position: absolute; top: -60rpx; right: -60rpx; width: 240rpx; height: 240rpx; border-radius: 50%; background: radial-gradient(circle, rgba(56,189,172,.12) 0%, transparent 70%); }
-.hero-deco2 { position: absolute; bottom: -40rpx; left: -40rpx; width: 180rpx; height: 180rpx; border-radius: 50%; background: radial-gradient(circle, rgba(245,166,35,.06) 0%, transparent 70%); }
-.hero-body { position: relative; display: flex; flex-direction: column; align-items: center; z-index: 1; }
-
-.avatar-ring { position: relative; width: 168rpx; height: 168rpx; border-radius: 50%; padding: 6rpx; background: linear-gradient(135deg, #1a3a3a, #0d1f2d); margin-bottom: 20rpx; }
-.avatar-ring.vip-ring { background: linear-gradient(135deg, #f5a623, #38bdac, #f5a623); background-size: 300% 300%; animation: vipGlow 4s ease infinite; }
-@keyframes vipGlow { 0%,100%{background-position:0% 50%} 50%{background-position:100% 50%} }
-.avatar-img { width: 100%; height: 100%; border-radius: 50%; border: 4rpx solid #0a1620; }
-.avatar-ph { width: 100%; height: 100%; border-radius: 50%; border: 4rpx solid #0a1620; background: #152030; display: flex; align-items: center; justify-content: center; font-size: 52rpx; color: #38bdac; font-weight: 700; }
-.vip-tag { position: absolute; bottom: 4rpx; right: 4rpx; background: linear-gradient(135deg, #f5a623, #e8920d); color: #000; font-size: 18rpx; font-weight: 800; padding: 4rpx 14rpx; border-radius: 16rpx; letter-spacing: 1rpx; box-shadow: 0 4rpx 12rpx rgba(245,166,35,.4); }
-
-.hero-name { font-size: 38rpx; font-weight: 700; letter-spacing: 2rpx; margin-bottom: 12rpx; text-shadow: 0 2rpx 8rpx rgba(0,0,0,.5); }
-.hero-tags { display: flex; gap: 12rpx; flex-wrap: wrap; justify-content: center; }
-.tag-item { font-size: 22rpx; padding: 6rpx 18rpx; border-radius: 24rpx; }
-.tag-mbti { color: #38bdac; background: rgba(56,189,172,.12); border: 1rpx solid rgba(56,189,172,.2); }
-.tag-region { color: #ccc; background: rgba(255,255,255,.06); border: 1rpx solid rgba(255,255,255,.08); }
-
-/* ===== 通用卡片 ===== */
-.card { margin: 20rpx 24rpx; padding: 28rpx 28rpx; border-radius: 24rpx; background: rgba(15,25,40,.8); border: 1rpx solid rgba(255,255,255,.06); backdrop-filter: blur(10px); }
-.card-head { display: flex; align-items: center; gap: 10rpx; margin-bottom: 24rpx; }
-.card-icon { font-size: 28rpx; }
-.card-label { font-size: 28rpx; font-weight: 600; color: #fff; letter-spacing: 1rpx; }
-
-.field { margin-bottom: 20rpx; }
-.field:last-child { margin-bottom: 0; }
-.f-key { display: block; font-size: 22rpx; color: #6b7b8e; margin-bottom: 8rpx; text-transform: uppercase; letter-spacing: 2rpx; }
-.f-val { font-size: 28rpx; color: #e0e8f0; line-height: 1.7; }
-.f-contact { display: flex; align-items: center; gap: 16rpx; }
-.masked { letter-spacing: 3rpx; font-family: 'Courier New', monospace; }
-.lock-chip { display: flex; align-items: center; gap: 6rpx; font-size: 20rpx; color: #f5a623; background: rgba(245,166,35,.1); border: 1rpx solid rgba(245,166,35,.2); padding: 6rpx 16rpx; border-radius: 20rpx; white-space: nowrap; }
-.lock-icon { font-size: 18rpx; }
-.copy-chip { font-size: 20rpx; color: #38bdac; background: rgba(56,189,172,.1); border: 1rpx solid rgba(56,189,172,.2); padding: 6rpx 16rpx; border-radius: 20rpx; white-space: nowrap; }
-
-/* ===== 故事 ===== */
-.story { padding: 4rpx 0; }
-.story-q { display: block; font-size: 24rpx; color: #7a8fa3; margin-bottom: 10rpx; }
-.story-a { display: block; font-size: 28rpx; color: #e0e8f0; line-height: 1.8; }
-.divider { height: 1rpx; background: rgba(255,255,255,.04); margin: 20rpx 0; }
-
-/* ===== 互助 ===== */
-.help-box { padding: 20rpx; border-radius: 16rpx; margin-bottom: 16rpx; }
-.help-box:last-child { margin-bottom: 0; }
-.help-give { background: rgba(56,189,172,.06); border: 1rpx solid rgba(56,189,172,.1); }
-.help-need { background: rgba(245,166,35,.06); border: 1rpx solid rgba(245,166,35,.1); }
-.help-tag { display: inline-block; font-size: 22rpx; font-weight: 600; color: #38bdac; margin-bottom: 10rpx; padding: 4rpx 14rpx; border-radius: 12rpx; background: rgba(56,189,172,.12); }
-.help-tag.need { color: #f5a623; background: rgba(245,166,35,.12); }
-.help-txt { display: block; font-size: 26rpx; color: #ccd4de; line-height: 1.7; }
-
-/* ===== 项目 ===== */
-.proj-txt { font-size: 26rpx; color: #ccd4de; line-height: 1.8; }
-
-/* ===== 底部按钮 ===== */
-.bottom-actions { padding: 32rpx 24rpx 0; display: flex; flex-direction: column; gap: 16rpx; }
-.btn-match { text-align: center; padding: 26rpx 0; border-radius: 48rpx; font-size: 30rpx; font-weight: 700; letter-spacing: 2rpx; background: linear-gradient(135deg, #38bdac 0%, #2ca898 50%, #249e8c 100%); color: #fff; box-shadow: 0 8rpx 24rpx rgba(56,189,172,.3); }
-.btn-vip { text-align: center; padding: 22rpx 0; border-radius: 48rpx; font-size: 26rpx; color: #f5a623; background: transparent; border: 1rpx solid rgba(245,166,35,.3); }
-
-/* ===== 状态 ===== */
-.state-wrap { display: flex; flex-direction: column; align-items: center; justify-content: center; min-height: 60vh; gap: 16rpx; }
-.state-txt { font-size: 28rpx; color: #4a5a6e; }
-.state-emoji { font-size: 80rpx; }
-.loading-dot { width: 48rpx; height: 48rpx; border-radius: 50%; border: 4rpx solid rgba(56,189,172,.2); border-top-color: #38bdac; animation: spin 1s linear infinite; }
-@keyframes spin { to { transform: rotate(360deg); } }
diff --git a/归档/miniprogram/pages/my/my.js b/归档/miniprogram/pages/my/my.js
deleted file mode 100644
index becc5a0f..00000000
--- a/归档/miniprogram/pages/my/my.js
+++ /dev/null
@@ -1,715 +0,0 @@
-/**
- * Soul创业派对 - 我的页面
- * 开发: 卡若
- * 技术支持: 存客宝
- */
-
-const app = getApp()
-
-Page({
- data: {
- // 系统信息
- statusBarHeight: 44,
- navBarHeight: 88,
-
- // 用户状态
- isLoggedIn: false,
- userInfo: null,
-
- // 统计数据
- totalSections: 62,
- readCount: 0,
- referralCount: 0,
- earnings: '-',
- pendingEarnings: '-',
- earningsLoading: true,
- earningsRefreshing: false,
-
- // 阅读统计
- totalReadTime: 0,
- matchHistory: 0,
-
- // 最近阅读
- recentChapters: [],
-
- // 功能配置
- matchEnabled: false,
-
- // VIP状态
- isVip: false,
- vipExpireDate: '',
-
- // 待确认收款
- pendingConfirmList: [],
- withdrawMchId: '',
- withdrawAppId: '',
-
- // 未登录假资料(展示用)
- guestNickname: '游客',
- guestAvatar: '',
-
- // 登录弹窗
- showLoginModal: false,
- isLoggingIn: false,
- // 用户须主动勾选同意协议(审核要求:不得默认同意)
- agreeProtocol: false,
-
- // 修改昵称弹窗
- showNicknameModal: false,
- editingNickname: ''
- },
-
- onLoad() {
- this.setData({
- statusBarHeight: app.globalData.statusBarHeight,
- navBarHeight: app.globalData.navBarHeight
- })
- this.loadFeatureConfig()
- this.initUserStatus()
- },
-
- onShow() {
- // 设置TabBar选中状态(根据 matchEnabled 动态设置)
- if (typeof this.getTabBar === 'function' && this.getTabBar()) {
- const tabBar = this.getTabBar()
- if (tabBar.updateSelected) {
- tabBar.updateSelected()
- } else {
- const selected = tabBar.data.matchEnabled ? 3 : 2
- tabBar.setData({ selected })
- }
- }
- this.initUserStatus()
- },
-
- async loadFeatureConfig() {
- try {
- const res = await app.request('/api/miniprogram/config')
- const features = (res && res.features) || (res && res.data && res.data.features) || {}
- this.setData({ matchEnabled: features.matchEnabled === true })
- } catch (error) {
- console.log('加载功能配置失败:', error)
- this.setData({ matchEnabled: false })
- }
- },
-
- // 初始化用户状态
- initUserStatus() {
- const { isLoggedIn, userInfo } = app.globalData
-
- if (isLoggedIn && userInfo) {
- const readIds = app.globalData.readSectionIds || []
- const recentList = readIds.slice(-5).reverse().map(id => ({
- id: id,
- title: `章节 ${id}`
- }))
-
- const userId = userInfo.id || ''
- const userIdShort = userId.length > 20 ? userId.slice(0, 10) + '...' + userId.slice(-6) : userId
- const userWechat = wx.getStorageSync('user_wechat') || userInfo.wechat || ''
-
- // 先设基础信息;收益由 loadMyEarnings 专用接口拉取,加载前用 - 占位
- this.setData({
- isLoggedIn: true,
- userInfo,
- userIdShort,
- userWechat,
- readCount: Math.min(app.getReadCount(), this.data.totalSections || 62),
- referralCount: userInfo.referralCount || 0,
- earnings: '-',
- pendingEarnings: '-',
- earningsLoading: true,
- recentChapters: recentList,
- totalReadTime: Math.floor(Math.random() * 200) + 50
- })
- this.loadMyEarnings()
- this.loadPendingConfirm()
- this.loadVipStatus()
- } else {
- this.setData({
- isLoggedIn: false,
- userInfo: null,
- userIdShort: '',
- readCount: app.getReadCount(),
- referralCount: 0,
- earnings: '-',
- pendingEarnings: '-',
- earningsLoading: false,
- recentChapters: []
- })
- }
- },
-
- // 拉取待确认收款列表(用于「确认收款」按钮)
- async loadPendingConfirm() {
- const userInfo = app.globalData.userInfo
- if (!app.globalData.isLoggedIn || !userInfo || !userInfo.id) return
- try {
- const res = await app.request({ url: '/api/miniprogram/withdraw/pending-confirm?userId=' + userInfo.id, silent: true })
- if (res && res.success && res.data) {
- const list = (res.data.list || []).map(item => ({
- id: item.id,
- amount: (item.amount || 0).toFixed(2),
- package: item.package,
- createdAt: (item.createdAt ?? item.created_at) ? this.formatDateMy(item.createdAt ?? item.created_at) : '--'
- }))
- this.setData({
- pendingConfirmList: list,
- withdrawMchId: res.data.mchId ?? res.data.mch_id ?? '',
- withdrawAppId: res.data.appId ?? res.data.app_id ?? ''
- })
- } else {
- this.setData({ pendingConfirmList: [], withdrawMchId: '', withdrawAppId: '' })
- }
- } catch (e) {
- this.setData({ pendingConfirmList: [] })
- }
- },
-
- formatDateMy(dateStr) {
- if (!dateStr) return '--'
- const d = new Date(dateStr)
- const m = (d.getMonth() + 1).toString().padStart(2, '0')
- const day = d.getDate().toString().padStart(2, '0')
- return `${m}-${day}`
- },
-
- // 确认收款:有 package 时调起微信收款页,成功后记录;无 package 时仅调用后端记录「已确认收款」
- async confirmReceive(e) {
- const index = e.currentTarget.dataset.index
- const id = e.currentTarget.dataset.id
- const list = this.data.pendingConfirmList || []
- let item = (typeof index === 'number' || (index !== undefined && index !== '')) ? list[index] : null
- if (!item && id) item = list.find(x => x.id === id) || null
- if (!item) {
- wx.showToast({ title: '请稍后刷新再试', icon: 'none' })
- return
- }
- const mchId = this.data.withdrawMchId
- const appId = this.data.withdrawAppId
- const hasPackage = item.package && mchId && appId && wx.canIUse('requestMerchantTransfer')
-
- const recordConfirmReceived = async () => {
- const userInfo = app.globalData.userInfo
- if (userInfo && userInfo.id) {
- try {
- await app.request({
- url: '/api/miniprogram/withdraw/confirm-received',
- method: 'POST',
- data: { withdrawalId: item.id, userId: userInfo.id }
- })
- } catch (e) { /* 仅记录,不影响前端展示 */ }
- }
- const newList = list.filter(x => x.id !== item.id)
- this.setData({ pendingConfirmList: newList })
- this.loadPendingConfirm()
- }
-
- if (hasPackage) {
- wx.showLoading({ title: '调起收款...', mask: true })
- wx.requestMerchantTransfer({
- mchId,
- appId,
- package: item.package,
- success: async () => {
- wx.hideLoading()
- wx.showToast({ title: '收款成功', icon: 'success' })
- await recordConfirmReceived()
- },
- fail: (err) => {
- wx.hideLoading()
- const msg = (err.errMsg || '').includes('cancel') ? '已取消' : (err.errMsg || '收款失败')
- wx.showToast({ title: msg, icon: 'none' })
- },
- complete: () => { wx.hideLoading() }
- })
- return
- }
-
- // 无 package 时仅记录「确认已收款」(当前直接打款无 package,用户点按钮即记录)
- wx.showLoading({ title: '提交中...', mask: true })
- try {
- await recordConfirmReceived()
- wx.hideLoading()
- wx.showToast({ title: '已记录确认收款', icon: 'success' })
- } catch (e) {
- wx.hideLoading()
- wx.showToast({ title: (e && e.message) || '操作失败', icon: 'none' })
- }
- },
-
- // 专用接口:拉取「我的收益」卡片数据(累计、可提现、推荐人数)
- async loadMyEarnings() {
- const userInfo = app.globalData.userInfo
- if (!app.globalData.isLoggedIn || !userInfo || !userInfo.id) {
- this.setData({ earningsLoading: false })
- return
- }
- const formatMoney = (num) => (typeof num === 'number' ? num.toFixed(2) : '0.00')
- try {
- const res = await app.request({ url: '/api/miniprogram/earnings?userId=' + userInfo.id, silent: true })
- if (!res || !res.success || !res.data) {
- this.setData({ earningsLoading: false, earnings: '0.00', pendingEarnings: '0.00' })
- return
- }
- const d = res.data
- this.setData({
- earnings: formatMoney(d.totalCommission),
- pendingEarnings: formatMoney(d.availableEarnings),
- referralCount: d.referralCount ?? this.data.referralCount,
- earningsLoading: false,
- earningsRefreshing: false
- })
- } catch (e) {
- console.log('[My] 拉取我的收益失败:', e && e.message)
- this.setData({
- earningsLoading: false,
- earningsRefreshing: false,
- earnings: '0.00',
- pendingEarnings: '0.00'
- })
- }
- },
-
- // 点击刷新图标:刷新我的收益
- async refreshEarnings() {
- if (!this.data.isLoggedIn) return
- if (this.data.earningsRefreshing) return
- this.setData({ earningsRefreshing: true })
- wx.showToast({ title: '刷新中...', icon: 'loading', duration: 2000 })
- await this.loadMyEarnings()
- wx.showToast({ title: '已刷新', icon: 'success' })
- },
-
- // 微信原生获取头像(button open-type="chooseAvatar" 回调)
- async onChooseAvatar(e) {
- const tempAvatarUrl = e.detail.avatarUrl
- if (!tempAvatarUrl) return
-
- wx.showLoading({ title: '上传中...', mask: true })
-
- try {
- // 1. 先上传图片到服务器
- console.log('[My] 开始上传头像:', tempAvatarUrl)
-
- const uploadRes = await new Promise((resolve, reject) => {
- wx.uploadFile({
- url: app.globalData.baseUrl + '/api/miniprogram/upload',
- filePath: tempAvatarUrl,
- name: 'file',
- formData: {
- folder: 'avatars'
- },
- success: (res) => {
- try {
- const data = JSON.parse(res.data)
- if (data.success) {
- resolve(data)
- } else {
- reject(new Error(data.error || '上传失败'))
- }
- } catch (err) {
- reject(new Error('解析响应失败'))
- }
- },
- fail: (err) => {
- reject(err)
- }
- })
- })
-
- // 2. 获取上传后的完整URL
- const avatarUrl = app.globalData.baseUrl + uploadRes.data.url
- console.log('[My] 头像上传成功:', avatarUrl)
-
- // 3. 更新本地头像
- const userInfo = this.data.userInfo
- userInfo.avatar = avatarUrl
- this.setData({ userInfo })
- app.globalData.userInfo = userInfo
- wx.setStorageSync('userInfo', userInfo)
-
- // 4. 同步到服务器数据库
- await app.request('/api/miniprogram/user/update', {
- method: 'POST',
- data: { userId: userInfo.id, avatar: avatarUrl }
- })
-
- wx.hideLoading()
- wx.showToast({ title: '头像更新成功', icon: 'success' })
-
- } catch (e) {
- wx.hideLoading()
- console.error('[My] 上传头像失败:', e)
- wx.showToast({
- title: e.message || '上传失败,请重试',
- icon: 'none'
- })
- }
- },
-
- // 微信原生获取昵称回调(针对 input type="nickname" 的 bindblur 或 bindchange)
- async handleNicknameChange(nickname) {
- if (!nickname || nickname === this.data.userInfo?.nickname) return
-
- try {
- const userInfo = this.data.userInfo
- userInfo.nickname = nickname
- this.setData({ userInfo })
- app.globalData.userInfo = userInfo
- wx.setStorageSync('userInfo', userInfo)
-
- // 同步到服务器
- await app.request('/api/miniprogram/user/update', {
- method: 'POST',
- data: { userId: userInfo.id, nickname }
- })
-
- wx.showToast({ title: '昵称已更新', icon: 'success' })
- } catch (e) {
- console.error('[My] 同步昵称失败:', e)
- }
- },
-
- // 打开昵称修改弹窗
- editNickname() {
- this.setData({
- showNicknameModal: true,
- editingNickname: this.data.userInfo?.nickname || ''
- })
- },
-
- // 关闭昵称弹窗
- closeNicknameModal() {
- this.setData({
- showNicknameModal: false,
- editingNickname: ''
- })
- },
-
- // 阻止事件冒泡
- stopPropagation() {},
-
- // 昵称输入实时更新
- onNicknameInput(e) {
- this.setData({
- editingNickname: e.detail.value
- })
- },
-
- // 昵称变化(微信自动填充时触发)
- onNicknameChange(e) {
- const nickname = e.detail.value
- console.log('[My] 昵称已自动填充:', nickname)
- this.setData({
- editingNickname: nickname
- })
- // 自动填充时也尝试直接同步
- this.handleNicknameChange(nickname)
- },
-
- // 确认修改昵称
- async confirmNickname() {
- const newNickname = this.data.editingNickname.trim()
-
- if (!newNickname) {
- wx.showToast({ title: '昵称不能为空', icon: 'none' })
- return
- }
-
- if (newNickname.length < 1 || newNickname.length > 20) {
- wx.showToast({ title: '昵称1-20个字符', icon: 'none' })
- return
- }
-
- // 关闭弹窗
- this.closeNicknameModal()
-
- // 显示加载
- wx.showLoading({ title: '更新中...', mask: true })
-
- try {
- // 1. 同步到服务器
- const res = await app.request('/api/miniprogram/user/update', {
- method: 'POST',
- data: {
- userId: this.data.userInfo.id,
- nickname: newNickname
- }
- })
-
- if (res && res.success) {
- // 2. 更新本地状态
- const userInfo = this.data.userInfo
- userInfo.nickname = newNickname
- this.setData({ userInfo })
-
- // 3. 更新全局和缓存
- app.globalData.userInfo = userInfo
- wx.setStorageSync('userInfo', userInfo)
-
- wx.hideLoading()
- wx.showToast({ title: '昵称已修改', icon: 'success' })
- } else {
- throw new Error(res?.message || '更新失败')
- }
- } catch (e) {
- wx.hideLoading()
- console.error('[My] 修改昵称失败:', e)
- wx.showToast({ title: '修改失败,请重试', icon: 'none' })
- }
- },
-
- // 复制用户ID
- copyUserId() {
- const userId = this.data.userInfo?.id || ''
- if (!userId) {
- wx.showToast({ title: '暂无ID', icon: 'none' })
- return
- }
- wx.setClipboardData({
- data: userId,
- success: () => {
- wx.showToast({ title: 'ID已复制', icon: 'success' })
- }
- })
- },
-
- // 切换Tab
- switchTab(e) {
- const tab = e.currentTarget.dataset.tab
- this.setData({ activeTab: tab })
- },
-
- // 显示登录弹窗(每次打开时协议未勾选,符合审核要求)
- showLogin() {
- try {
- this.setData({ showLoginModal: true, agreeProtocol: false })
- } catch (e) {
- console.error('[My] showLogin error:', e)
- this.setData({ showLoginModal: true })
- }
- },
-
- // 切换协议勾选(用户主动勾选,非默认同意)
- toggleAgree() {
- this.setData({ agreeProtocol: !this.data.agreeProtocol })
- },
-
- // 打开用户协议页(审核要求:点击《用户协议》需有响应)
- openUserProtocol() {
- wx.navigateTo({ url: '/pages/agreement/agreement' })
- },
-
- // 打开隐私政策页(审核要求:点击《隐私政策》需有响应)
- openPrivacy() {
- wx.navigateTo({ url: '/pages/privacy/privacy' })
- },
-
- // 关闭登录弹窗
- closeLoginModal() {
- if (this.data.isLoggingIn) return
- this.setData({ showLoginModal: false })
- },
-
- // 微信登录(须已勾选同意协议,且做好错误处理避免审核报错)
- async handleWechatLogin() {
- if (!this.data.agreeProtocol) {
- wx.showToast({ title: '请先阅读并同意用户协议和隐私政策', icon: 'none' })
- return
- }
- this.setData({ isLoggingIn: true })
- try {
- const result = await app.login()
- if (result) {
- this.initUserStatus()
- this.setData({ showLoginModal: false, agreeProtocol: false })
- wx.showToast({ title: '登录成功', icon: 'success' })
- } else {
- wx.showToast({ title: '登录失败,请重试', icon: 'none' })
- }
- } catch (e) {
- console.error('[My] 微信登录错误:', e)
- wx.showToast({ title: '登录失败,请重试', icon: 'none' })
- } finally {
- this.setData({ isLoggingIn: false })
- }
- },
-
- // 手机号登录(需要用户授权)
- async handlePhoneLogin(e) {
- // 检查是否有授权code
- if (!e.detail.code) {
- // 用户拒绝授权或获取失败,尝试使用微信登录
- console.log('手机号授权失败,尝试微信登录')
- return this.handleWechatLogin()
- }
-
- this.setData({ isLoggingIn: true })
-
- try {
- const result = await app.loginWithPhone(e.detail.code)
- if (result) {
- this.initUserStatus()
- this.setData({ showLoginModal: false })
- wx.showToast({ title: '登录成功', icon: 'success' })
- } else {
- wx.showToast({ title: '登录失败,请重试', icon: 'none' })
- }
- } catch (e) {
- console.error('手机号登录错误:', e)
- wx.showToast({ title: '登录失败,请重试', icon: 'none' })
- } finally {
- this.setData({ isLoggingIn: false })
- }
- },
-
- // 点击菜单
- handleMenuTap(e) {
- const id = e.currentTarget.dataset.id
-
- if (!this.data.isLoggedIn && id !== 'about') {
- this.showLogin()
- return
- }
-
- const routes = {
- orders: '/pages/purchases/purchases',
- referral: '/pages/referral/referral',
- withdrawRecords: '/pages/withdraw-records/withdraw-records',
- about: '/pages/about/about',
- settings: '/pages/settings/settings'
- }
-
- if (routes[id]) {
- wx.navigateTo({ url: routes[id] })
- }
- },
-
- // 跳转到阅读页
- goToRead(e) {
- const id = e.currentTarget.dataset.id
- wx.navigateTo({ url: `/pages/read/read?id=${id}` })
- },
-
- // 跳转到目录
- goToChapters() {
- wx.switchTab({ url: '/pages/chapters/chapters' })
- },
-
- // 跳转到关于页
- goToAbout() {
- wx.navigateTo({ url: '/pages/about/about' })
- },
-
- // 跳转到匹配
- goToMatch() {
- wx.switchTab({ url: '/pages/match/match' })
- },
-
- // 跳转到推广中心
- goToReferral() {
- if (!this.data.isLoggedIn) {
- this.showLogin()
- return
- }
- wx.navigateTo({ url: '/pages/referral/referral' })
- },
-
- // 跳转到找伙伴页面
- goToMatch() {
- wx.switchTab({ url: '/pages/match/match' })
- },
-
- // 退出登录
- handleLogout() {
- wx.showModal({
- title: '退出登录',
- content: '确定要退出登录吗?',
- success: (res) => {
- if (res.confirm) {
- app.logout()
- this.initUserStatus()
- wx.showToast({ title: '已退出登录', icon: 'success' })
- }
- }
- })
- },
-
- // VIP状态查询
- async loadVipStatus() {
- const userId = app.globalData.userInfo?.id
- if (!userId) return
- try {
- const res = await app.request({ url: `/api/vip/status?userId=${userId}`, silent: true })
- if (res?.success) {
- this.setData({ isVip: res.data?.isVip, vipExpireDate: res.data?.expireDate || '' })
- }
- } catch (e) { console.log('[My] VIP查询失败', e) }
- },
-
- // 头像点击:已登录弹出选项(改头像/进VIP)
- onAvatarTap() {
- if (!this.data.isLoggedIn) { this.showLogin(); return }
- wx.showActionSheet({
- itemList: ['获取微信头像', '开通/管理VIP'],
- success: (res) => {
- if (res.tapIndex === 0) this.chooseAvatarFallback()
- if (res.tapIndex === 1) this.goToVip()
- }
- })
- },
-
- chooseAvatarFallback() {
- wx.chooseMedia({
- count: 1, mediaType: ['image'], sourceType: ['album', 'camera'],
- success: async (res) => {
- const tempPath = res.tempFiles[0].tempFilePath
- const userInfo = this.data.userInfo
- userInfo.avatar = tempPath
- this.setData({ userInfo })
- app.globalData.userInfo = userInfo
- wx.setStorageSync('userInfo', userInfo)
- try {
- await app.request('/api/user/update', { method: 'POST', data: { userId: userInfo.id, avatar: tempPath } })
- } catch (e) { console.log('头像同步失败', e) }
- wx.showToast({ title: '头像已更新', icon: 'success' })
- }
- })
- },
-
- goToVip() {
- if (!this.data.isLoggedIn) { this.showLogin(); return }
- wx.navigateTo({ url: '/pages/vip/vip' })
- },
-
- async handleWithdraw() {
- if (!this.data.isLoggedIn) { this.showLogin(); return }
- const amount = parseFloat(this.data.pendingEarnings)
- if (isNaN(amount) || amount <= 0) {
- wx.showToast({ title: '暂无可提现金额', icon: 'none' })
- return
- }
- wx.showModal({
- title: '申请提现',
- content: `确认提现 ¥${amount.toFixed(2)} ?`,
- success: async (res) => {
- if (!res.confirm) return
- wx.showLoading({ title: '提交中...', mask: true })
- try {
- const userId = app.globalData.userInfo?.id
- await app.request({ url: '/api/withdraw', method: 'POST', data: { userId, amount } })
- wx.hideLoading()
- wx.showToast({ title: '提现申请已提交', icon: 'success' })
- this.loadMyEarnings()
- } catch (e) {
- wx.hideLoading()
- wx.showToast({ title: e.message || '提现失败', icon: 'none' })
- }
- }
- })
- },
-
- // 阻止冒泡
- stopPropagation() {}
-})
diff --git a/归档/miniprogram/pages/my/my.json b/归档/miniprogram/pages/my/my.json
deleted file mode 100644
index e7696321..00000000
--- a/归档/miniprogram/pages/my/my.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "usingComponents": {},
- "enablePullDownRefresh": false,
- "backgroundTextStyle": "light",
- "backgroundColor": "#000000"
-}
diff --git a/归档/miniprogram/pages/my/my.wxml b/归档/miniprogram/pages/my/my.wxml
deleted file mode 100644
index ac0d2042..00000000
--- a/归档/miniprogram/pages/my/my.wxml
+++ /dev/null
@@ -1,246 +0,0 @@
-
-
-
-
-
-
- 我的
-
-
-
-
-
-
-
-
-
-
-
- --
- 已读章节
-
-
- --
- 推荐好友
-
-
- --
- 待领收益
-
-
-
-
-
-
-
-
-
-
- {{readCount}}
- 已读章节
-
-
- {{referralCount}}
- 推荐好友
-
-
- {{pendingEarnings > 0 ? '¥' + pendingEarnings : '--'}}
- 我的收益
-
-
-
-
-
-
-
-
-
-
-
-
- 👁️
- 阅读统计
-
-
-
- 📖
- {{readCount}}
- 已读章节
-
-
- ⏱️
- {{totalReadTime}}
- 阅读分钟
-
-
- 👥
- {{matchHistory}}
- 匹配伙伴
-
-
-
-
-
-
-
- 📖
- 最近阅读
-
-
-
-
- {{index + 1}}
- {{item.title}}
-
- 继续阅读
-
-
-
- 📖
- 暂无阅读记录
- 去阅读 →
-
-
-
-
-
-
-
-
-
-
- ✕
- 🔐
- 登录 Soul创业实验
- 登录后可购买章节、解锁更多内容
-
-
- 取消
-
-
- {{agreeProtocol ? '✓' : ''}}
- 我已阅读并同意
- 《用户协议》
- 和
- 《隐私政策》
-
-
-
-
-
-
-
- ✕
-
-
-
-
- 微信用户可点击自动填充昵称
-
-
-
- 取消
- 确定
-
-
-
-
-
-
-
diff --git a/归档/miniprogram/pages/my/my.wxss b/归档/miniprogram/pages/my/my.wxss
deleted file mode 100644
index bbdb848a..00000000
--- a/归档/miniprogram/pages/my/my.wxss
+++ /dev/null
@@ -1,1373 +0,0 @@
-/**
- * Soul创业实验 - 我的页面样式
- * 1:1还原Web版本UI
- */
-
-.page {
- min-height: 100vh;
- background: #000000;
- padding-bottom: 200rpx;
-}
-
-/* ===== 导航栏 ===== */
-.nav-bar {
- position: fixed;
- top: 0;
- left: 0;
- right: 0;
- z-index: 100;
- background: rgba(0, 0, 0, 0.9);
- backdrop-filter: blur(40rpx);
- border-bottom: 1rpx solid rgba(255, 255, 255, 0.05);
-}
-
-.nav-content {
- height: 88rpx;
- display: flex;
- align-items: center;
- justify-content: flex-start;
- padding: 0 32rpx;
-}
-
-.nav-title {
- font-size: 36rpx;
- font-weight: 600;
-}
-
-.nav-title-left {
- font-size: 36rpx;
- font-weight: 600;
-}
-
-.brand-color {
- color: #00CED1;
-}
-
-.gold-color {
- color: #FFD700;
-}
-
-.pink-color {
- color: #E91E63;
-}
-
-.nav-placeholder {
- width: 100%;
-}
-
-/* ===== 用户卡片 ===== */
-.user-card {
- margin: 32rpx;
- padding: 32rpx;
-}
-.margin-partner-badge{
-}
-/* 创业伙伴按钮 - inline 布局 */
-.partner-badge {
- display: flex;
- align-items: center;
- gap: 6rpx;
- padding: 10rpx 18rpx;
- background: rgba(0, 206, 209, 0.15);
- border: 1rpx solid rgba(0, 206, 209, 0.3);
- border-radius: 20rpx;
- flex-shrink: 0;
- transition: all 0.2s;
- margin-top:-20px;
-}
-
-.partner-badge:active {
- background: rgba(0, 206, 209, 0.3);
- transform: scale(0.95);
-}
-
-.partner-icon {
- font-size: 18rpx;
- color: #00CED1;
- line-height: 1;
-}
-
-.partner-text {
- font-size: 20rpx;
- font-weight: 500;
- color: #00CED1;
- line-height: 1;
- white-space: nowrap;
-}
-
-.card-gradient {
- background: linear-gradient(135deg, #1c1c1e 0%, #2c2c2e 100%);
- border-radius: 32rpx;
- border: 2rpx solid rgba(0, 206, 209, 0.2);
-}
-
-/* ===== 新版用户头部布局 ===== */
-.user-header-row {
- display: flex;
- align-items: center;
- gap: 20rpx;
- margin-bottom: 32rpx;
- width: 100%;
-}
-
-/* 头像容器 */
-.avatar-wrapper {
- position: relative;
- flex-shrink: 0;
- width: 120rpx;
- height: 120rpx;
-}
-
-/* 头像按钮样式 - 简化版 */
-.avatar-btn-simple {
- flex-shrink: 0;
- width: 60rpx!important;
- height: 120rpx;
- min-width: 120rpx;
- min-height: 120rpx;
- padding: 0;
- margin: 0;
- background: transparent !important;
- border: none;
- line-height: normal;
- border-radius: 50%;
- overflow: visible;
- display: flex;
- align-items: center;
- justify-content: center;
-}
-.avatar-btn-simple::after { border: none; }
-
-.edit-icon-small {
- font-size: 24rpx;
- color: rgba(255,255,255,0.5);
- margin-left: 12rpx;
-}
-
-.avatar {
- width: 120rpx;
- height: 120rpx;
- border-radius: 50%;
- border: 3rpx solid #00CED1;
- display: flex;
- align-items: center;
- justify-content: center;
- background: linear-gradient(135deg, rgba(0, 206, 209, 0.2) 0%, transparent 100%);
- overflow: hidden;
- box-sizing: border-box;
- flex-shrink: 0;
-}
-
-.avatar-img {
- width: 100%;
- height: 100%;
- object-fit: cover;
-}
-
-.avatar-edit-hint {
- position: absolute;
- bottom: 0;
- right: 0;
- width: 36rpx;
- height: 36rpx;
- background: #00CED1;
- border-radius: 50%;
- display: flex;
- align-items: center;
- justify-content: center;
- border: 3rpx solid #000;
-}
-
-.edit-icon {
- font-size: 20rpx;
- color: #000;
-}
-
-.avatar-empty {
- border-style: dashed;
- border-color: rgba(0, 206, 209, 0.5);
-}
-
-.avatar-icon {
- font-size: 64rpx;
- opacity: 0.3;
-}
-
-.avatar-text {
- font-size: 48rpx;
- font-weight: 700;
- color: #00CED1;
- line-height: 1;
-}
-
-.user-info-block {
- flex: 1;
- display: flex;
- flex-direction: column;
- justify-content: center;
- gap: 12rpx;
- min-width: 0;
-}
-
-.user-name-row {
- display: flex;
- align-items: center;
- gap: 8rpx;
- line-height: 1.3;
-}
-
-.user-name {
- font-size: 34rpx;
- font-weight: 600;
- color: #ffffff;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- flex: 1;
- min-width: 0;
-}
-
-.edit-name-icon {
- font-size: 24rpx;
- color: rgba(255,255,255,0.4);
- flex-shrink: 0;
-}
-
-.user-id-row {
- display: flex;
- align-items: center;
- gap: 8rpx;
- line-height: 1.3;
-}
-
-.user-id {
- font-size: 24rpx;
- color: rgba(255, 255, 255, 0.4);
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- flex: 1;
- min-width: 0;
-}
-
-.copy-icon {
- font-size: 22rpx;
- opacity: 0.5;
- flex-shrink: 0;
-}
-
-.user-wechat {
- font-size: 24rpx;
- color: #00CED1;
-}
-
-.user-badge-small {
- display: inline-flex;
- align-items: center;
- gap: 6rpx;
- padding: 6rpx 14rpx;
- background: rgba(0, 206, 209, 0.15);
- border: 1rpx solid rgba(0, 206, 209, 0.3);
- border-radius: 20rpx;
-}
-
-.badge-star {
- font-size: 18rpx;
-}
-
-.badge-label {
- font-size: 20rpx;
- color: #00CED1;
-}
-
-/* 兼容旧样式 */
-.user-header {
- display: flex;
- align-items: center;
- gap: 24rpx;
- margin-bottom: 32rpx;
-}
-
-.user-info {
- flex: 1;
-}
-
-.login-btn {
- font-size: 36rpx;
- font-weight: 600;
- color: #00CED1;
-}
-
-.user-subtitle {
- font-size: 26rpx;
- color: rgba(255, 255, 255, 0.3);
- display: block;
- margin-top: 4rpx;
-}
-
-/* ===== 未登录假资料样式 ===== */
-.avatar-placeholder {
- cursor: default;
-}
-
-.user-id-guest {
- color: rgba(255, 255, 255, 0.35) !important;
-}
-
-.btn-login-inline {
- flex-shrink: 0;
- margin-left: 16rpx;
- padding: 10rpx 24rpx;
- background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%);
- color: #000;
- font-size: 24rpx;
- font-weight: 600;
- border-radius: 24rpx;
-}
-
-.user-name {
- font-size: 36rpx;
- font-weight: 600;
- color: #ffffff;
- display: block;
-}
-
-.user-id {
- font-size: 26rpx;
- color: rgba(255, 255, 255, 0.3);
- display: block;
- margin-top: 4rpx;
-}
-
-.user-badge {
- padding: 8rpx 20rpx;
- background: rgba(0, 206, 209, 0.2);
- border: 2rpx solid rgba(0, 206, 209, 0.3);
- border-radius: 32rpx;
- display: flex;
- align-items: center;
- gap: 8rpx;
-}
-
-.badge-icon {
- font-size: 22rpx;
-}
-
-.badge-text {
- font-size: 22rpx;
- color: #00CED1;
-}
-
-.stats-grid {
- display: grid;
- grid-template-columns: repeat(3, 1fr);
- gap: 16rpx;
- padding-top: 32rpx;
- border-top: 2rpx solid rgba(255, 255, 255, 0.05);
-}
-
-.stat-item {
- text-align: center;
- padding: 16rpx 8rpx;
-}
-
-.stat-value {
- font-size: 40rpx;
- font-weight: 700;
- display: block;
-}
-
-.stat-label {
- font-size: 22rpx;
- color: rgba(255, 255, 255, 0.4);
-}
-
-/* ===== 收益卡片 - 艺术化设计 ===== */
-.earnings-card {
- margin: 32rpx;
- padding: 32rpx;
- background: linear-gradient(135deg, #1a1a2e 0%, #16213e 50%, #0f3460 100%);
- border-radius: 32rpx;
- border: 2rpx solid rgba(0, 206, 209, 0.2);
- position: relative;
- overflow: hidden;
- box-shadow: 0 16rpx 32rpx rgba(0, 0, 0, 0.3);
-}
-
-/* 背景装饰圆 */
-.bg-decoration {
- position: absolute;
- border-radius: 50%;
- z-index: 0;
-}
-
-.bg-decoration-gold {
- top: 0;
- right: 0;
- width: 256rpx;
- height: 256rpx;
- background: linear-gradient(135deg, rgba(255, 215, 0, 0.1) 0%, transparent 100%);
- transform: translate(50%, -50%);
-}
-
-.bg-decoration-brand {
- bottom: 0;
- left: 0;
- width: 192rpx;
- height: 192rpx;
- background: linear-gradient(45deg, rgba(0, 206, 209, 0.1) 0%, transparent 100%);
- transform: translate(-50%, 50%);
-}
-
-.earnings-content {
- position: relative;
- z-index: 1;
-}
-
-/* 标题行 */
-.earnings-header {
- display: flex;
- align-items: center;
- justify-content: space-between;
- margin-bottom: 40rpx;
-}
-
-.earnings-title-wrap {
- display: flex;
- align-items: center;
- gap: 12rpx;
-}
-
-.earnings-icon {
- font-size: 32rpx;
-}
-
-.earnings-title {
- font-size: 30rpx;
- font-weight: 600;
- color: #ffffff;
-}
-
-.earnings-link {
- display: flex;
- align-items: center;
- gap: 6rpx;
- padding: 8rpx 16rpx;
- background: rgba(0, 206, 209, 0.1);
- border-radius: 16rpx;
-}
-
-.earnings-link:active {
- background: rgba(0, 206, 209, 0.2);
-}
-
-.link-text {
- font-size: 24rpx;
- font-weight: 500;
-}
-
-.link-arrow {
- font-size: 20rpx;
- font-weight: 600;
-}
-
-/* 我的收益 - 刷新图标 */
-.earnings-refresh-wrap {
- display: flex;
- align-items: center;
- justify-content: center;
- width: 64rpx;
- height: 64rpx;
- border-radius: 50%;
- background: rgba(0, 206, 209, 0.15);
-}
-
-.earnings-refresh-wrap:active {
- background: rgba(0, 206, 209, 0.3);
-}
-
-.earnings-refresh-icon {
- font-size: 36rpx;
- font-weight: 600;
- color: #00CED1;
-}
-
-.earnings-refresh-spin {
- animation: earnings-spin 0.8s linear infinite;
-}
-
-@keyframes earnings-spin {
- from { transform: rotate(0deg); }
- to { transform: rotate(360deg); }
-}
-
-/* 收益数据 */
-.earnings-data {
- display: flex;
- align-items: flex-end;
- gap: 48rpx;
- margin-bottom: 32rpx;
-}
-
-.earnings-main,
-.earnings-secondary {
- display: flex;
- flex-direction: column;
- gap: 8rpx;
-}
-
-.earnings-main {
- flex: 1;
-}
-
-.earnings-secondary {
- flex: 1;
-}
-
-.earnings-label {
- font-size: 24rpx;
- font-weight: 500;
- color: rgba(255, 255, 255, 0.6);
- letter-spacing: 0.5rpx;
-}
-
-.earnings-amount-large {
- font-size: 64rpx;
- font-weight: 700;
- line-height: 1;
- display: block;
-}
-
-.earnings-amount-medium {
- font-size: 48rpx;
- font-weight: 700;
- line-height: 1;
- color: #ffffff;
-}
-
-/* 渐变文字效果 */
-.gold-gradient {
- background: linear-gradient(90deg, #FFD700 0%, #FFA500 100%);
- -webkit-background-clip: text;
- background-clip: text;
- -webkit-text-fill-color: transparent;
- color: transparent;
-}
-
-/* 操作按钮 */
-.earnings-action {
- display: flex;
- align-items: center;
- justify-content: center;
- gap: 16rpx;
- padding: 24rpx;
- background: linear-gradient(90deg, rgba(255, 215, 0, 0.9) 0%, rgba(255, 165, 0, 0.9) 100%);
- border-radius: 24rpx;
- font-weight: 700;
- box-shadow: 0 4rpx 12rpx rgba(255, 215, 0, 0.3);
- margin-top: 8rpx;
-}
-
-.earnings-action:active {
- opacity: 0.85;
- transform: scale(0.98);
-}
-
-.action-icon {
- font-size: 32rpx;
-}
-
-.action-text {
- font-size: 30rpx;
- font-weight: 700;
- color: #000000;
- letter-spacing: 1rpx;
-}
-
-/* ===== 推广入口 ===== */
-.referral-card {
- display: flex;
- align-items: center;
- justify-content: space-between;
- margin: 32rpx;
- padding: 32rpx;
- background: linear-gradient(90deg, rgba(255, 215, 0, 0.1) 0%, #1c1c1e 100%);
- border: 2rpx solid rgba(255, 215, 0, 0.2);
- border-radius: 32rpx;
-}
-
-.referral-left {
- display: flex;
- align-items: center;
- gap: 24rpx;
-}
-
-.referral-icon {
- width: 80rpx;
- height: 80rpx;
- border-radius: 50%;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 40rpx;
-}
-
-.gold-bg {
- background: linear-gradient(135deg, #FFD700 0%, #FFA500 100%);
-}
-
-.referral-info {
- display: flex;
- flex-direction: column;
-}
-
-.referral-title {
- font-size: 28rpx;
- font-weight: 500;
- color: #ffffff;
-}
-
-.referral-desc {
- font-size: 24rpx;
- color: rgba(255, 255, 255, 0.4);
- margin-top: 4rpx;
-}
-
-.referral-btn {
- padding: 16rpx 32rpx;
- background: rgba(255, 215, 0, 0.2);
- color: #FFD700;
- font-size: 26rpx;
- font-weight: 500;
- border-radius: 16rpx;
-}
-
-/* ===== Tab切换 ===== */
-.tab-bar-custom {
- display: flex;
- gap: 16rpx;
- margin: 32rpx;
-}
-
-.tab-item {
- flex: 1;
- padding: 20rpx;
- text-align: center;
- font-size: 28rpx;
- font-weight: 500;
- color: rgba(255, 255, 255, 0.6);
- background: #1c1c1e;
- border-radius: 24rpx;
- display: flex;
- align-items: center;
- justify-content: center;
- gap: 8rpx;
-}
-
-.tab-active {
- background: rgba(0, 206, 209, 0.2);
- color: #00CED1;
- border: 2rpx solid rgba(0, 206, 209, 0.3);
-}
-
-.tab-icon {
- font-size: 28rpx;
-}
-
-/* ===== Tab内容 ===== */
-.tab-content {
- padding: 0 32rpx;
- width: 100%;
- box-sizing: border-box;
-}
-
-/* ===== 菜单卡片 ===== */
-.card {
- background: #1c1c1e;
- border-radius: 24rpx;
- border: 2rpx solid rgba(255, 255, 255, 0.05);
- overflow: hidden;
- margin: 0 0 24rpx 0;
- width: 100%;
- box-sizing: border-box;
-}
-
-.menu-card {
- padding: 0;
- width: 100%;
- box-sizing: border-box;
-}
-
-.menu-item {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 28rpx 32rpx;
- border-bottom: 1rpx solid rgba(255, 255, 255, 0.05);
-}
-
-.menu-item:last-child {
- border-bottom: none;
-}
-
-.menu-item:active {
- background: rgba(255, 255, 255, 0.05);
-}
-
-.menu-left {
- display: flex;
- align-items: center;
- gap: 24rpx;
-}
-
-.menu-icon {
- width: 52rpx;
- height: 52rpx;
- border-radius: 50%;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 32rpx;
- flex-shrink: 0;
-}
-
-/* 有背景的图标样式 */
-.icon-brand,
-.icon-gold,
-.icon-gray {
- width: 52rpx;
- height: 52rpx;
- font-size: 20rpx;
-}
-
-.icon-brand {
- background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%);
-}
-
-.icon-gold {
- background: linear-gradient(135deg, #FFD700 0%, #FFA500 100%);
-}
-
-.icon-gray {
- background: rgba(128, 128, 128, 0.2);
-}
-
-.menu-title {
- font-size: 28rpx;
- color: #ffffff;
-}
-
-.menu-right {
- display: flex;
- align-items: center;
- gap: 16rpx;
-}
-
-.menu-count {
- font-size: 26rpx;
- color: rgba(255, 255, 255, 0.4);
-}
-
-.menu-badge {
- font-size: 26rpx;
- font-weight: 500;
-}
-
-.menu-arrow {
- font-size: 28rpx;
- color: rgba(255, 255, 255, 0.3);
-}
-
-/* ===== 统计卡片 ===== */
-.stats-card {
- padding: 32rpx;
-}
-
-.card-title {
- display: flex;
- align-items: center;
- gap: 16rpx;
- font-size: 28rpx;
- font-weight: 500;
- color: #ffffff;
- margin-bottom: 24rpx;
-}
-
-.stats-row {
- display: grid;
- grid-template-columns: repeat(3, 1fr);
- gap: 24rpx;
-}
-
-/* 两列布局(当找伙伴功能关闭时) */
-.stats-row-two-cols {
- grid-template-columns: repeat(2, 1fr) !important;
-}
-
-.stat-box {
- display: flex;
- flex-direction: column;
- align-items: center;
- padding: 24rpx;
- background: rgba(255, 255, 255, 0.05);
- border-radius: 24rpx;
-}
-
-.stat-icon {
- font-size: 36rpx;
- margin-bottom: 8rpx;
-}
-
-.stat-num {
- font-size: 32rpx;
- font-weight: 700;
- color: #ffffff;
-}
-
-.stat-text {
- font-size: 22rpx;
- color: rgba(255, 255, 255, 0.4);
- margin-top: 4rpx;
-}
-
-/* ===== 最近阅读 ===== */
-.recent-card {
- padding: 32rpx;
-}
-
-.recent-list {
- display: flex;
- flex-direction: column;
- gap: 16rpx;
-}
-
-.recent-item {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 24rpx;
- background: rgba(255, 255, 255, 0.05);
- border-radius: 24rpx;
-}
-
-.recent-left {
- display: flex;
- align-items: center;
- gap: 24rpx;
-}
-
-.recent-index {
- font-size: 26rpx;
- color: rgba(255, 255, 255, 0.3);
-}
-
-.recent-title {
- font-size: 26rpx;
- color: #ffffff;
-}
-
-.recent-btn {
- font-size: 24rpx;
- color: #00CED1;
-}
-
-/* ===== 空状态 ===== */
-.empty-state {
- display: flex;
- flex-direction: column;
- align-items: center;
- padding: 48rpx;
-}
-
-.empty-icon {
- font-size: 64rpx;
- opacity: 0.5;
- margin-bottom: 16rpx;
-}
-
-.empty-text {
- font-size: 26rpx;
- color: rgba(255, 255, 255, 0.4);
-}
-
-.empty-btn {
- margin-top: 16rpx;
- font-size: 26rpx;
- color: #00CED1;
-}
-
-/* ===== 匹配记录 ===== */
-.match-card {
- padding: 32rpx;
-}
-
-/* ===== 登录弹窗 ===== */
-.modal-overlay {
- position: fixed;
- inset: 0;
- background: rgba(0, 0, 0, 0.6);
- backdrop-filter: blur(20rpx);
- display: flex;
- align-items: center;
- justify-content: center;
- z-index: 1000;
- padding: 48rpx;
-}
-
-.modal-content {
- width: 100%;
- max-width: 640rpx;
- background: #1c1c1e;
- border-radius: 32rpx;
- padding: 48rpx;
- position: relative;
-}
-
-.modal-close {
- position: absolute;
- top: 24rpx;
- right: 24rpx;
- width: 64rpx;
- height: 64rpx;
- border-radius: 50%;
- background: rgba(255, 255, 255, 0.1);
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 28rpx;
- color: rgba(255, 255, 255, 0.6);
-}
-
-.login-icon {
- font-size: 96rpx;
- text-align: center;
- display: block;
- margin-bottom: 24rpx;
-}
-
-.login-title {
- font-size: 36rpx;
- font-weight: 700;
- color: #ffffff;
- text-align: center;
- display: block;
- margin-bottom: 16rpx;
-}
-
-.login-desc {
- font-size: 26rpx;
- color: rgba(255, 255, 255, 0.6);
- text-align: center;
- display: block;
- margin-bottom: 48rpx;
-}
-
-.btn-wechat {
- width: 100%;
- display: flex;
- align-items: center;
- justify-content: center;
- gap: 16rpx;
- padding: 28rpx;
- background: #07C160;
- color: #ffffff;
- font-size: 28rpx;
- font-weight: 500;
- border-radius: 24rpx;
- margin-bottom: 24rpx;
- border: none;
-}
-
-.btn-wechat::after {
- border: none;
-}
-
-.btn-wechat-icon {
- width: 40rpx;
- height: 40rpx;
- background: rgba(255, 255, 255, 0.2);
- border-radius: 8rpx;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 24rpx;
-}
-
-.btn-phone {
- width: 100%;
- display: flex;
- align-items: center;
- justify-content: center;
- gap: 16rpx;
- padding: 28rpx;
- background: rgba(255, 255, 255, 0.1);
- color: #ffffff;
- font-size: 28rpx;
- border-radius: 24rpx;
- border: 2rpx solid rgba(255, 255, 255, 0.1);
-}
-
-.btn-phone::after {
- border: none;
-}
-
-.btn-phone-icon {
- font-size: 32rpx;
-}
-
-/* 登录弹窗内取消按钮 */
-.login-modal-cancel {
- margin-top: 24rpx;
- padding: 24rpx;
- font-size: 28rpx;
- color: rgba(255, 255, 255, 0.5);
- text-align: center;
-}
-
-/* 协议勾选行(审核:用户须主动勾选,协议可点击查看) */
-.login-agree-row {
- display: flex;
- flex-wrap: wrap;
- align-items: center;
- justify-content: center;
- margin-top: 32rpx;
- font-size: 22rpx;
- color: rgba(255, 255, 255, 0.5);
-}
-.agree-checkbox {
- width: 32rpx;
- height: 32rpx;
- border: 2rpx solid rgba(255, 255, 255, 0.5);
- border-radius: 6rpx;
- margin-right: 12rpx;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 22rpx;
- color: #fff;
- flex-shrink: 0;
-}
-.agree-checked {
- background: #00CED1;
- border-color: #00CED1;
-}
-.agree-text {
- color: rgba(255, 255, 255, 0.6);
-}
-.agree-link {
- color: #00CED1;
- text-decoration: underline;
- padding: 0 4rpx;
-}
-.btn-wechat-disabled {
- opacity: 0.6;
-}
-
-/* ===== 底部留白 ===== */
-.bottom-space {
- height: 40rpx;
-}
-
-/* ===== 推广入口卡片 ===== */
-.promo-entry-card {
- display: flex;
- align-items: center;
- justify-content: space-between;
- margin: 24rpx 24rpx 0;
- padding: 32rpx;
- background: linear-gradient(135deg, rgba(255, 215, 0, 0.15) 0%, rgba(255, 165, 0, 0.1) 100%);
- border: 2rpx solid rgba(255, 215, 0, 0.3);
- border-radius: 24rpx;
-}
-
-.promo-entry-left {
- display: flex;
- align-items: center;
- gap: 20rpx;
-}
-
-.promo-entry-icon {
- width: 80rpx;
- height: 80rpx;
- background: rgba(255, 215, 0, 0.2);
- border-radius: 50%;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 36rpx;
-}
-
-.promo-entry-info {
- display: flex;
- flex-direction: column;
- gap: 4rpx;
-}
-
-.promo-entry-title {
- font-size: 30rpx;
- color: #ffffff;
- font-weight: 600;
-}
-
-.promo-entry-desc {
- font-size: 24rpx;
- color: rgba(255, 255, 255, 0.5);
-}
-
-.promo-entry-right {
- display: flex;
- align-items: center;
- gap: 16rpx;
-}
-
-.promo-entry-earnings {
- font-size: 32rpx;
- color: #FFD700;
- font-weight: 600;
-}
-
-.promo-entry-arrow {
- font-size: 28rpx;
- color: #FFD700;
-}
-
-/* ===== 修改昵称弹窗 ===== */
-.nickname-modal {
- width: 600rpx;
- max-width: 90%;
-}
-
-.modal-header {
- display: flex;
- flex-direction: column;
- align-items: center;
- margin-bottom: 40rpx;
-}
-
-.modal-icon {
- font-size: 60rpx;
- margin-bottom: 16rpx;
-}
-
-.modal-title {
- font-size: 32rpx;
- color: #ffffff;
- font-weight: 600;
-}
-
-.nickname-input-wrap {
- margin-bottom: 40rpx;
-}
-
-.nickname-input {
- width: 100%;
- height: 88rpx;
- padding: 0 24rpx;
- background: rgba(255, 255, 255, 0.05);
- border: 2rpx solid rgba(56, 189, 172, 0.3);
- border-radius: 12rpx;
- font-size: 28rpx;
- color: #ffffff;
- box-sizing: border-box;
-}
-
-.nickname-placeholder {
- color: rgba(255, 255, 255, 0.3);
-}
-
-.input-tip {
- display: block;
- margin-top: 12rpx;
- font-size: 22rpx;
- color: rgba(56, 189, 172, 0.6);
- text-align: center;
-}
-
-.modal-actions {
- display: flex;
- gap: 20rpx;
-}
-
-.modal-btn {
- flex: 1;
- height: 80rpx;
- display: flex;
- align-items: center;
- justify-content: center;
- border-radius: 12rpx;
- font-size: 28rpx;
- font-weight: 500;
- transition: all 0.3s;
-}
-
-.modal-btn-cancel {
- background: rgba(255, 255, 255, 0.05);
- color: rgba(255, 255, 255, 0.5);
- border: 2rpx solid rgba(255, 255, 255, 0.1);
-}
-
-.modal-btn-confirm {
- background: linear-gradient(135deg, #38bdac 0%, #2da396 100%);
- color: #ffffff;
- box-shadow: 0 8rpx 24rpx rgba(56, 189, 172, 0.3);
-}
-
-/* 待确认收款 */
-.pending-confirm-card {
- margin: 32rpx;
- padding: 28rpx 32rpx;
- background: rgba(76, 175, 80, 0.08);
- border: 2rpx solid rgba(76, 175, 80, 0.25);
- border-radius: 24rpx;
-}
-.pending-confirm-header { margin-bottom: 20rpx; }
-.pending-confirm-title { font-size: 28rpx; font-weight: 600; color: #fff; display: block; }
-.pending-confirm-desc { font-size: 24rpx; color: rgba(255,255,255,0.6); margin-top: 8rpx; display: block; }
-.pending-confirm-list { display: flex; flex-direction: column; gap: 16rpx; }
-.pending-confirm-item {
- display: flex; align-items: center; justify-content: space-between;
- padding: 20rpx 24rpx; background: rgba(28,28,30,0.6); border-radius: 16rpx;
-}
-.pending-confirm-info { display: flex; flex-direction: column; gap: 4rpx; }
-.pending-confirm-amount { font-size: 32rpx; font-weight: 600; color: #4CAF50; }
-.pending-confirm-time { font-size: 22rpx; color: rgba(255,255,255,0.5); }
-.pending-confirm-btn {
- padding: 16rpx 32rpx;
- background: linear-gradient(135deg, #4CAF50 0%, #388E3C 100%);
- color: #fff; font-size: 26rpx; font-weight: 500; border-radius: 20rpx;
-}
-
-/* ===== 收益面板(内嵌) ===== */
-.earnings-inline {
- margin: 0 32rpx 24rpx;
- padding: 24rpx 28rpx;
- background: linear-gradient(135deg, #1a1a2e 0%, #16213e 100%);
- border-radius: 24rpx;
- border: 2rpx solid rgba(0, 206, 209, 0.15);
-}
-.earnings-inline-row {
- display: flex;
- align-items: center;
- gap: 16rpx;
-}
-.earnings-inline-item {
- display: flex;
- flex-direction: column;
- gap: 4rpx;
- flex: 1;
-}
-.earnings-inline-label {
- font-size: 22rpx;
- color: rgba(255,255,255,0.5);
-}
-.earnings-inline-val {
- font-size: 36rpx;
- font-weight: 700;
- color: #fff;
-}
-.earnings-inline-divider {
- width: 2rpx;
- height: 48rpx;
- background: rgba(255,255,255,0.1);
- flex-shrink: 0;
-}
-.earnings-inline-btn {
- padding: 12rpx 28rpx;
- background: linear-gradient(90deg, #FFD700 0%, #FFA500 100%);
- border-radius: 20rpx;
- font-size: 26rpx;
- font-weight: 600;
- color: #000;
- flex-shrink: 0;
-}
-.earnings-inline-refresh {
- width: 48rpx;
- height: 48rpx;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 28rpx;
- color: #00CED1;
- flex-shrink: 0;
-}
-.pending-inline {
- margin-top: 16rpx;
- padding-top: 16rpx;
- border-top: 1rpx solid rgba(255,255,255,0.08);
-}
-.pending-inline-item {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 8rpx 0;
-}
-.pending-inline-text {
- font-size: 24rpx;
- color: #4CAF50;
-}
-.pending-inline-btn {
- padding: 8rpx 20rpx;
- background: #4CAF50;
- color: #fff;
- font-size: 22rpx;
- border-radius: 12rpx;
-}
-
-/* VIP头像标识 */
-.avatar-wrap { position: relative; }
-.avatar-vip { border: 4rpx solid #FFD700 !important; box-shadow: 0 0 20rpx rgba(255,215,0,0.4); }
-.vip-badge { position: absolute; bottom: -4rpx; right: -4rpx; background: linear-gradient(135deg, #FFD700, #FFA500); color: #000; font-size: 16rpx; font-weight: bold; padding: 2rpx 8rpx; border-radius: 10rpx; line-height: 1.4; }
-.vip-badge-gray { background: rgba(255,255,255,0.2); color: rgba(255,255,255,0.5); }
-
-/* 会员权益小标签 */
-.vip-tags-row {
- display: flex;
- gap: 6rpx;
- margin-left: 8rpx;
- flex-shrink: 0;
-}
-.vip-tag-mini {
- padding: 2rpx 10rpx;
- font-size: 18rpx;
- border-radius: 8rpx;
- background: rgba(255,255,255,0.08);
- color: rgba(255,255,255,0.3);
- border: 1rpx solid rgba(255,255,255,0.1);
-}
-.vip-tag-active {
- background: rgba(255,215,0,0.15);
- color: #FFD700;
- border-color: rgba(255,215,0,0.3);
-}
-
-/* 阅读统计 */
-.stats-card { padding: 24rpx 28rpx; }
-.stats-row { display: flex; gap: 16rpx; margin-top: 16rpx; }
-.stat-box {
- flex: 1;
- display: flex;
- flex-direction: column;
- align-items: center;
- gap: 6rpx;
- padding: 20rpx 12rpx;
- border-radius: 16rpx;
- background: rgba(139,92,246,0.06);
-}
-.stat-icon { font-size: 32rpx; }
-.stat-num { font-size: 36rpx; font-weight: bold; color: #fff; }
-.stat-text { font-size: 22rpx; color: rgba(255,255,255,0.5); }
-.pink-color { color: #ec4899; }
-
-/* 成为会员小按钮 */
-.become-vip-chip {
- display: inline-flex;
- align-items: center;
- gap: 4rpx;
- padding: 4rpx 14rpx;
- border-radius: 20rpx;
- background: linear-gradient(135deg, rgba(245,166,35,.15), rgba(245,166,35,.08));
- border: 1rpx solid rgba(245,166,35,.3);
- margin-left: 8rpx;
-}
-.chip-star { font-size: 18rpx; }
-.chip-text { font-size: 20rpx; color: #f5a623; font-weight: 600; white-space: nowrap; }
diff --git a/归档/miniprogram/pages/privacy/privacy.js b/归档/miniprogram/pages/privacy/privacy.js
deleted file mode 100644
index 0cd665db..00000000
--- a/归档/miniprogram/pages/privacy/privacy.js
+++ /dev/null
@@ -1,21 +0,0 @@
-/**
- * Soul创业派对 - 隐私政策
- * 审核要求:登录前可点击《隐私政策》查看完整内容
- */
-const app = getApp()
-
-Page({
- data: {
- statusBarHeight: 44
- },
-
- onLoad() {
- this.setData({
- statusBarHeight: app.globalData.statusBarHeight || 44
- })
- },
-
- goBack() {
- wx.navigateBack()
- }
-})
diff --git a/归档/miniprogram/pages/privacy/privacy.json b/归档/miniprogram/pages/privacy/privacy.json
deleted file mode 100644
index f567904d..00000000
--- a/归档/miniprogram/pages/privacy/privacy.json
+++ /dev/null
@@ -1 +0,0 @@
-{"usingComponents":{},"navigationStyle":"custom","navigationBarTitleText":"隐私政策"}
diff --git a/归档/miniprogram/pages/privacy/privacy.wxml b/归档/miniprogram/pages/privacy/privacy.wxml
deleted file mode 100644
index cf414ad7..00000000
--- a/归档/miniprogram/pages/privacy/privacy.wxml
+++ /dev/null
@@ -1,40 +0,0 @@
-
-
-
- ←
- 隐私政策
-
-
-
-
-
-
- Soul创业实验 隐私政策
- 更新日期:以小程序内展示为准
-
- 一、信息收集
- 为向您提供阅读、购买、推广与提现等服务,我们可能收集:微信昵称、头像、openId、手机号(在您授权时)、订单与收益相关数据。我们仅在法律允许及您同意的范围内收集必要信息。
-
- 二、信息使用
- 所收集信息用于账号识别、订单与收益结算、客服与纠纷处理、产品优化及法律义务履行,不会用于与上述目的无关的营销或向第三方出售。
-
- 三、信息存储与安全
- 数据存储在中华人民共和国境内,我们采取合理技术和管理措施保障数据安全,防止未经授权的访问、泄露或篡改。
-
- 四、信息共享
- 未经您同意,我们不会将您的个人信息共享给第三方,法律法规要求或为完成支付、提现等必要合作除外(如微信支付、微信商家转账)。
-
- 五、您的权利
- 您有权查询、更正、删除您的个人信息,或撤回授权。部分权限撤回可能影响相关功能使用。您可通过小程序设置或联系我们就隐私问题提出请求。
-
- 六、未成年人
- 如您为未成年人,请在监护人同意下使用本服务。我们不会主动收集未成年人个人信息。
-
- 七、政策更新
- 我们可能适时更新本政策,更新后将通过小程序内公示等方式通知您。继续使用即视为接受更新后的政策。
-
- 八、联系我们
- 如有隐私相关疑问或投诉,请通过小程序内「关于作者」或 Soul 派对房与我们联系。
-
-
-
diff --git a/归档/miniprogram/pages/privacy/privacy.wxss b/归档/miniprogram/pages/privacy/privacy.wxss
deleted file mode 100644
index 08fadc43..00000000
--- a/归档/miniprogram/pages/privacy/privacy.wxss
+++ /dev/null
@@ -1,11 +0,0 @@
-.page { min-height: 100vh; background: #000; }
-.nav-bar { position: fixed; top: 0; left: 0; right: 0; z-index: 100; background: rgba(0,0,0,0.95); display: flex; align-items: center; justify-content: space-between; padding: 0 32rpx; height: 88rpx; }
-.nav-back { width: 72rpx; height: 72rpx; background: #1c1c1e; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 32rpx; color: #fff; }
-.nav-title { font-size: 36rpx; font-weight: 600; color: #00CED1; }
-.nav-placeholder { width: 72rpx; }
-.content { height: calc(100vh - 132rpx); padding: 32rpx; box-sizing: border-box; }
-.doc-card { background: #1c1c1e; border-radius: 24rpx; padding: 40rpx; border: 2rpx solid rgba(0,206,209,0.2); }
-.doc-title { font-size: 34rpx; font-weight: 700; color: #fff; display: block; margin-bottom: 16rpx; }
-.doc-update { font-size: 24rpx; color: rgba(255,255,255,0.5); display: block; margin-bottom: 32rpx; }
-.doc-section { font-size: 28rpx; font-weight: 600; color: #00CED1; display: block; margin: 24rpx 0 12rpx; }
-.doc-p { font-size: 26rpx; color: rgba(255,255,255,0.85); line-height: 1.75; display: block; margin-bottom: 16rpx; }
diff --git a/归档/miniprogram/pages/purchases/purchases.js b/归档/miniprogram/pages/purchases/purchases.js
deleted file mode 100644
index c458ad74..00000000
--- a/归档/miniprogram/pages/purchases/purchases.js
+++ /dev/null
@@ -1,67 +0,0 @@
-/**
- * Soul创业实验 - 订单页
- */
-const app = getApp()
-
-Page({
- data: {
- statusBarHeight: 44,
- orders: [],
- loading: true
- },
-
- onLoad() {
- this.setData({ statusBarHeight: app.globalData.statusBarHeight })
- this.loadOrders()
- },
-
- async loadOrders() {
- this.setData({ loading: true })
- try {
- const userId = app.globalData.userInfo?.id
- if (userId) {
- const res = await app.request(`/api/orders?userId=${userId}`)
- if (res && res.success && res.data) {
- const orders = (res.data || []).map(item => ({
- id: item.id || item.order_sn,
- sectionId: item.product_id || item.section_id,
- title: item.product_name || `章节 ${item.product_id || ''}`,
- amount: item.amount || 0,
- status: item.status || 'completed',
- createTime: item.created_at ? new Date(item.created_at).toLocaleDateString() : '--'
- }))
- this.setData({ orders })
- return
- }
- }
- const purchasedSections = app.globalData.purchasedSections || []
- const orders = purchasedSections.map((id, index) => ({
- id: `order_${index}`,
- sectionId: id,
- title: `章节 ${id}`,
- amount: 1,
- status: 'completed',
- createTime: new Date(Date.now() - index * 86400000).toLocaleDateString()
- }))
- this.setData({ orders })
- } catch (e) {
- console.error('加载订单失败:', e)
- const purchasedSections = app.globalData.purchasedSections || []
- this.setData({
- orders: purchasedSections.map((id, i) => ({
- id: `order_${i}`, sectionId: id, title: `章节 ${id}`, amount: 1, status: 'completed',
- createTime: new Date(Date.now() - i * 86400000).toLocaleDateString()
- }))
- })
- } finally {
- this.setData({ loading: false })
- }
- },
-
- goToRead(e) {
- const id = e.currentTarget.dataset.id
- wx.navigateTo({ url: `/pages/read/read?id=${id}` })
- },
-
- goBack() { wx.navigateBack() }
-})
diff --git a/归档/miniprogram/pages/purchases/purchases.json b/归档/miniprogram/pages/purchases/purchases.json
deleted file mode 100644
index e90e9960..00000000
--- a/归档/miniprogram/pages/purchases/purchases.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "usingComponents": {},
- "navigationStyle": "custom"
-}
diff --git a/归档/miniprogram/pages/purchases/purchases.wxml b/归档/miniprogram/pages/purchases/purchases.wxml
deleted file mode 100644
index 0c5942cd..00000000
--- a/归档/miniprogram/pages/purchases/purchases.wxml
+++ /dev/null
@@ -1,35 +0,0 @@
-
-
-
- ←
- 我的订单
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{item.title}}
- {{item.createTime}}
-
-
- ¥{{item.amount}}
- 已完成
-
-
-
-
-
- 📦
- 暂无订单
-
-
-
diff --git a/归档/miniprogram/pages/purchases/purchases.wxss b/归档/miniprogram/pages/purchases/purchases.wxss
deleted file mode 100644
index c2f6cf3e..00000000
--- a/归档/miniprogram/pages/purchases/purchases.wxss
+++ /dev/null
@@ -1,21 +0,0 @@
-.page { min-height: 100vh; background: #000; }
-.nav-bar { position: fixed; top: 0; left: 0; right: 0; z-index: 100; background: rgba(0,0,0,0.9); backdrop-filter: blur(40rpx); display: flex; align-items: center; justify-content: space-between; padding: 0 32rpx; height: 88rpx; }
-.nav-back { width: 72rpx; height: 72rpx; background: #1c1c1e; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 32rpx; color: #fff; }
-.nav-title { font-size: 36rpx; font-weight: 600; color: #00CED1; }
-.nav-placeholder { width: 72rpx; }
-.content { padding: 32rpx; }
-.loading { display: flex; flex-direction: column; gap: 24rpx; }
-.skeleton { height: 120rpx; background: linear-gradient(90deg, #1c1c1e 25%, #2c2c2e 50%, #1c1c1e 75%); background-size: 200% 100%; animation: skeleton 1.5s ease-in-out infinite; border-radius: 24rpx; }
-@keyframes skeleton { 0% { background-position: 200% 0; } 100% { background-position: -200% 0; } }
-.orders-list { display: flex; flex-direction: column; gap: 16rpx; }
-.order-item { display: flex; align-items: center; justify-content: space-between; padding: 24rpx; background: #1c1c1e; border-radius: 24rpx; }
-.order-item:active { background: #2c2c2e; }
-.order-info { flex: 1; }
-.order-title { font-size: 28rpx; color: #fff; display: block; margin-bottom: 8rpx; }
-.order-time { font-size: 22rpx; color: rgba(255,255,255,0.4); }
-.order-right { text-align: right; }
-.order-amount { font-size: 28rpx; font-weight: 600; color: #00CED1; display: block; margin-bottom: 4rpx; }
-.order-status { font-size: 22rpx; color: rgba(255,255,255,0.4); }
-.empty { display: flex; flex-direction: column; align-items: center; padding: 96rpx; }
-.empty-icon { font-size: 96rpx; margin-bottom: 24rpx; opacity: 0.5; }
-.empty-text { font-size: 28rpx; color: rgba(255,255,255,0.4); }
diff --git a/归档/miniprogram/pages/read/read.js b/归档/miniprogram/pages/read/read.js
deleted file mode 100644
index 6e9fface..00000000
--- a/归档/miniprogram/pages/read/read.js
+++ /dev/null
@@ -1,1196 +0,0 @@
-/**
- * Soul创业派对 - 阅读页(标准流程版)
- * 开发: 卡若
- * 技术支持: 存客宝
- *
- * 更新: 2026-02-04
- * - 引入权限管理器(chapterAccessManager)统一权限判断
- * - 引入阅读追踪器(readingTracker)记录阅读进度、时长、是否读完
- * - 使用状态机(accessState)规范权限流转
- * - 异常统一保守处理,避免误解锁
- */
-
-import accessManager from '../../utils/chapterAccessManager'
-import readingTracker from '../../utils/readingTracker'
-
-const app = getApp()
-
-Page({
- data: {
- // 系统信息
- statusBarHeight: 44,
- navBarHeight: 88,
-
- // 章节信息
- sectionId: '',
- section: null,
- partTitle: '',
- chapterTitle: '',
-
- // 内容
- content: '',
- previewContent: '',
- contentParagraphs: [],
- previewParagraphs: [],
- loading: true,
-
- // 【新增】权限状态机(替代 canAccess)
- // unknown: 加载中 | free: 免费 | locked_not_login: 未登录 | locked_not_purchased: 未购买 | unlocked_purchased: 已购买 | error: 错误
- accessState: 'unknown',
-
- // 用户状态
- isLoggedIn: false,
- hasFullBook: false,
- canAccess: false, // 保留兼容性,从 accessState 派生
- purchasedCount: 0,
-
- // 阅读进度
- readingProgress: 0,
- showPaywall: false,
-
- // 上一篇/下一篇
- prevSection: null,
- nextSection: null,
-
- // 价格
- sectionPrice: 1,
- fullBookPrice: 9.9,
- totalSections: 62,
-
- // 弹窗
- showShareModal: false,
- showLoginModal: false,
- agreeProtocol: false,
- showPosterModal: false,
- isPaying: false,
- isGeneratingPoster: false,
-
- // 免费章节
- freeIds: ['preface', 'epilogue', '1.1', 'appendix-1', 'appendix-2', 'appendix-3']
- },
-
- async onLoad(options) {
- const { id, ref } = options
-
- this.setData({
- statusBarHeight: app.globalData.statusBarHeight,
- navBarHeight: app.globalData.navBarHeight,
- sectionId: id,
- loading: true,
- accessState: 'unknown'
- })
-
- // 处理推荐码绑定(异步不阻塞)
- if (ref) {
- console.log('[Read] 检测到推荐码:', ref)
- wx.setStorageSync('referral_code', ref)
- app.handleReferralCode({ query: { ref } })
- }
-
- try {
- // 【标准流程】1. 拉取最新配置(免费列表、价格)
- const config = await accessManager.fetchLatestConfig()
- this.setData({
- freeIds: config.freeChapters,
- sectionPrice: config.prices?.section ?? 1,
- fullBookPrice: config.prices?.fullbook ?? 9.9
- })
-
- // 【标准流程】2. 确定权限状态
- const accessState = await accessManager.determineAccessState(id, config.freeChapters)
- const canAccess = accessManager.canAccessFullContent(accessState)
-
- this.setData({
- accessState,
- canAccess,
- isLoggedIn: !!app.globalData.userInfo?.id,
- showPaywall: !canAccess
- })
-
- // 【标准流程】3. 加载内容
- await this.loadContent(id, accessState)
-
- // 【标准流程】4. 如果有权限,初始化阅读追踪
- if (canAccess) {
- readingTracker.init(id)
- }
-
- // 5. 加载导航
- this.loadNavigation(id)
-
- } catch (e) {
- console.error('[Read] 初始化失败:', e)
- wx.showToast({ title: '加载失败,请重试', icon: 'none' })
- this.setData({ accessState: 'error', loading: false })
- } finally {
- this.setData({ loading: false })
- }
- },
-
- // 从后端加载免费章节配置
- onPageScroll(e) {
- // 只在有权限时追踪阅读进度
- if (!accessManager.canAccessFullContent(this.data.accessState)) {
- return
- }
-
- // 获取滚动信息并更新追踪器
- const query = wx.createSelectorQuery()
- query.select('.page').boundingClientRect()
- query.selectViewport().scrollOffset()
- query.exec((res) => {
- if (res[0] && res[1]) {
- const scrollInfo = {
- scrollTop: res[1].scrollTop,
- scrollHeight: res[0].height,
- clientHeight: res[1].height
- }
-
- // 计算进度条显示(用于 UI)
- const totalScrollable = scrollInfo.scrollHeight - scrollInfo.clientHeight
- const progress = totalScrollable > 0
- ? Math.min((scrollInfo.scrollTop / totalScrollable) * 100, 100)
- : 0
- this.setData({ readingProgress: progress })
-
- // 更新阅读追踪器(记录最大进度、判断是否读完)
- readingTracker.updateProgress(scrollInfo)
- }
- })
- },
-
- // 【重构】加载章节内容(专注于内容加载,权限判断已在 onLoad 中由 accessManager 完成)
- async loadContent(id, accessState) {
- try {
- const section = this.getSectionInfo(id)
- const sectionPrice = this.data.sectionPrice ?? 1
- if (section.price === undefined || section.price === null) {
- section.price = sectionPrice
- }
- this.setData({ section })
-
- // 从 API 获取内容
- const res = await app.request({ url: `/api/miniprogram/book/chapter/${id}`, silent: true })
-
- if (res && res.content) {
- const lines = res.content.split('\n').filter(line => line.trim())
- const previewCount = Math.ceil(lines.length * 0.2)
-
- this.setData({
- content: res.content,
- contentParagraphs: lines,
- previewParagraphs: lines.slice(0, previewCount),
- partTitle: res.partTitle || '',
- chapterTitle: res.chapterTitle || ''
- })
-
- // 如果有权限,标记为已读
- if (accessManager.canAccessFullContent(accessState)) {
- app.markSectionAsRead(id)
- }
- }
- } catch (e) {
- console.error('[Read] 加载内容失败:', e)
- // 尝试从本地缓存加载
- const cacheKey = `chapter_${id}`
- try {
- const cached = wx.getStorageSync(cacheKey)
- if (cached && cached.content) {
- const lines = cached.content.split('\n').filter(line => line.trim())
- const previewCount = Math.ceil(lines.length * 0.2)
-
- this.setData({
- content: cached.content,
- contentParagraphs: lines,
- previewParagraphs: lines.slice(0, previewCount)
- })
- console.log('[Read] 从本地缓存加载成功')
- }
- } catch (cacheErr) {
- console.warn('[Read] 本地缓存也失败:', cacheErr)
- }
- throw e
- }
- },
-
- // 获取章节信息
- getSectionInfo(id) {
- // 特殊章节
- if (id === 'preface') {
- return { id: 'preface', title: '为什么我每天早上6点在Soul开播?', isFree: true, price: 0 }
- }
- if (id === 'epilogue') {
- return { id: 'epilogue', title: '这本书的真实目的', isFree: true, price: 0 }
- }
- if (id.startsWith('appendix')) {
- const appendixTitles = {
- 'appendix-1': 'Soul派对房精选对话',
- 'appendix-2': '创业者自检清单',
- 'appendix-3': '本书提到的工具和资源'
- }
- return { id, title: appendixTitles[id] || '附录', isFree: true, price: 0 }
- }
-
- // 普通章节
- return {
- id: id,
- title: this.getSectionTitle(id),
- isFree: id === '1.1',
- price: 1
- }
- },
-
- // 获取章节标题
- getSectionTitle(id) {
- const titles = {
- '1.1': '荷包:电动车出租的被动收入模式',
- '1.2': '老墨:资源整合高手的社交方法',
- '1.3': '笑声背后的MBTI',
- '1.4': '人性的三角结构:利益、情感、价值观',
- '1.5': '沟通差的问题:为什么你说的别人听不懂',
- '2.1': '相亲故事:你以为找的是人,实际是在找模式',
- '2.2': '找工作迷茫者:为什么简历解决不了人生',
- '2.3': '撸运费险:小钱困住大脑的真实心理',
- '2.4': '游戏上瘾的年轻人:不是游戏吸引他,是生活没吸引力',
- '2.5': '健康焦虑(我的糖尿病经历):疾病是人生的第一次清醒',
- '3.1': '3000万流水如何跑出来(退税模式解析)',
- '8.1': '流量杠杆:抖音、Soul、飞书',
- '9.14': '大健康私域:一个月150万的70后'
- }
- return titles[id] || `章节 ${id}`
- },
-
- // 加载内容 - 三级降级方案:API → 本地缓存 → 备用API
- async loadContent(id) {
- const cacheKey = `chapter_${id}`
-
- // 1. 优先从API获取
- try {
- const res = await this.fetchChapterWithTimeout(id, 5000)
- if (res && res.content) {
- this.setChapterContent(res)
- // 成功后缓存到本地
- wx.setStorageSync(cacheKey, res)
- console.log('[Read] 从API加载成功:', id)
- return
- }
- } catch (e) {
- console.warn('[Read] API加载失败,尝试本地缓存:', e.message)
- }
-
- // 2. API失败,尝试从本地缓存读取
- try {
- const cached = wx.getStorageSync(cacheKey)
- if (cached && cached.content) {
- this.setChapterContent(cached)
- console.log('[Read] 从本地缓存加载成功:', id)
- // 后台静默刷新
- this.silentRefresh(id)
- return
- }
- } catch (e) {
- console.warn('[Read] 本地缓存读取失败')
- }
-
- // 3. 都失败,显示加载中并持续重试
- this.setData({
- contentParagraphs: ['章节内容加载中...', '正在尝试连接服务器,请稍候...'],
- previewParagraphs: ['章节内容加载中...']
- })
-
- // 延迟重试(最多3次)
- this.retryLoadContent(id, 3)
- },
-
- // 带超时的章节请求
- fetchChapterWithTimeout(id, timeout = 5000) {
- return new Promise((resolve, reject) => {
- const timer = setTimeout(() => {
- reject(new Error('请求超时'))
- }, timeout)
-
- app.request(`/api/miniprogram/book/chapter/${id}`)
- .then(res => {
- clearTimeout(timer)
- resolve(res)
- })
- .catch(err => {
- clearTimeout(timer)
- reject(err)
- })
- })
- },
-
- // 设置章节内容
- setChapterContent(res) {
- const lines = res.content.split('\n').filter(line => line.trim())
- const previewCount = Math.ceil(lines.length * 0.2)
-
- this.setData({
- content: res.content,
- previewContent: lines.slice(0, previewCount).join('\n'),
- contentParagraphs: lines,
- previewParagraphs: lines.slice(0, previewCount),
- partTitle: res.partTitle || '',
- chapterTitle: res.chapterTitle || ''
- })
- },
-
- // 静默刷新(后台更新缓存)
- async silentRefresh(id) {
- try {
- const res = await this.fetchChapterWithTimeout(id, 10000)
- if (res && res.content) {
- wx.setStorageSync(`chapter_${id}`, res)
- console.log('[Read] 后台缓存更新成功:', id)
- }
- } catch (e) {
- // 静默失败不处理
- }
- },
-
- // 重试加载
- retryLoadContent(id, maxRetries, currentRetry = 0) {
- if (currentRetry >= maxRetries) {
- this.setData({
- contentParagraphs: ['内容加载失败', '请检查网络连接后下拉刷新重试'],
- previewParagraphs: ['内容加载失败']
- })
- return
- }
-
- setTimeout(async () => {
- try {
- const res = await this.fetchChapterWithTimeout(id, 8000)
- if (res && res.content) {
- this.setChapterContent(res)
- wx.setStorageSync(`chapter_${id}`, res)
- console.log('[Read] 重试成功:', id, '第', currentRetry + 1, '次')
- return
- }
- } catch (e) {
- console.warn('[Read] 重试失败,继续重试:', currentRetry + 1)
- }
- this.retryLoadContent(id, maxRetries, currentRetry + 1)
- }, 2000 * (currentRetry + 1))
- },
-
-
- // 加载导航
- loadNavigation(id) {
- const sectionOrder = [
- 'preface', '1.1', '1.2', '1.3', '1.4', '1.5',
- '2.1', '2.2', '2.3', '2.4', '2.5',
- '3.1', '3.2', '3.3', '3.4',
- '4.1', '4.2', '4.3', '4.4', '4.5',
- '5.1', '5.2', '5.3', '5.4', '5.5',
- '6.1', '6.2', '6.3', '6.4',
- '7.1', '7.2', '7.3', '7.4', '7.5',
- '8.1', '8.2', '8.3', '8.4', '8.5', '8.6',
- '9.1', '9.2', '9.3', '9.4', '9.5', '9.6', '9.7', '9.8', '9.9', '9.10', '9.11', '9.12', '9.13', '9.14',
- '10.1', '10.2', '10.3', '10.4',
- '11.1', '11.2', '11.3', '11.4', '11.5',
- 'epilogue'
- ]
-
- const currentIndex = sectionOrder.indexOf(id)
- const prevId = currentIndex > 0 ? sectionOrder[currentIndex - 1] : null
- const nextId = currentIndex < sectionOrder.length - 1 ? sectionOrder[currentIndex + 1] : null
-
- this.setData({
- prevSection: prevId ? { id: prevId, title: this.getSectionTitle(prevId) } : null,
- nextSection: nextId ? { id: nextId, title: this.getSectionTitle(nextId) } : null
- })
- },
-
- // 返回
- goBack() {
- wx.navigateBack({
- fail: () => wx.switchTab({ url: '/pages/chapters/chapters' })
- })
- },
-
- // 分享弹窗
- showShare() {
- this.setData({ showShareModal: true })
- },
-
- closeShareModal() {
- this.setData({ showShareModal: false })
- },
-
- // 复制链接
- copyLink() {
- const userInfo = app.globalData.userInfo
- const referralCode = userInfo?.referralCode || ''
- const shareUrl = `https://soul.quwanzhi.com/read/${this.data.sectionId}${referralCode ? '?ref=' + referralCode : ''}`
-
- wx.setClipboardData({
- data: shareUrl,
- success: () => {
- wx.showToast({ title: '链接已复制', icon: 'success' })
- this.setData({ showShareModal: false })
- }
- })
- },
-
- // 复制分享文案(朋友圈风格)
- copyShareText() {
- const { section } = this.data
-
- const shareText = `🔥 刚看完这篇《${section?.title || 'Soul创业派对'}》,太上头了!
-
-62个真实商业案例,每个都是从0到1的实战经验。私域运营、资源整合、商业变现,干货满满。
-
-推荐给正在创业或想创业的朋友,搜"Soul创业派对"小程序就能看!
-
-#创业派对 #私域运营 #商业案例`
-
- wx.setClipboardData({
- data: shareText,
- success: () => {
- wx.showToast({ title: '文案已复制', icon: 'success' })
- }
- })
- },
-
- // 分享到微信 - 自动带分享人ID
- onShareAppMessage() {
- const { section, sectionId } = this.data
- const userInfo = app.globalData.userInfo
- const referralCode = userInfo?.referralCode || wx.getStorageSync('referralCode') || ''
-
- // 分享标题优化
- const shareTitle = section?.title
- ? `📚 ${section.title.length > 20 ? section.title.slice(0, 20) + '...' : section.title}`
- : '📚 Soul创业派对 - 真实商业故事'
-
- return {
- title: shareTitle,
- path: `/pages/read/read?id=${sectionId}${referralCode ? '&ref=' + referralCode : ''}`,
- imageUrl: '/assets/share-cover.png' // 可配置分享封面图
- }
- },
-
- // 分享到朋友圈
- onShareTimeline() {
- const { section, sectionId } = this.data
- const userInfo = app.globalData.userInfo
- const referralCode = userInfo?.referralCode || ''
-
- return {
- title: `${section?.title || 'Soul创业派对'} - 来自派对房的真实故事`,
- query: `id=${sectionId}${referralCode ? '&ref=' + referralCode : ''}`
- }
- },
-
- // 显示登录弹窗(每次打开协议未勾选,符合审核要求)
- showLoginModal() {
- try {
- this.setData({ showLoginModal: true, agreeProtocol: false })
- } catch (e) {
- console.error('[Read] showLoginModal error:', e)
- this.setData({ showLoginModal: true })
- }
- },
-
- closeLoginModal() {
- this.setData({ showLoginModal: false })
- },
-
- toggleAgree() {
- this.setData({ agreeProtocol: !this.data.agreeProtocol })
- },
-
- openUserProtocol() {
- wx.navigateTo({ url: '/pages/agreement/agreement' })
- },
-
- openPrivacy() {
- wx.navigateTo({ url: '/pages/privacy/privacy' })
- },
-
- // 从服务端刷新购买状态,避免登录后误用旧数据导致误解锁
- // 【重构】微信登录(须先勾选同意协议,符合审核要求)
- async handleWechatLogin() {
- if (!this.data.agreeProtocol) {
- wx.showToast({ title: '请先阅读并同意用户协议和隐私政策', icon: 'none' })
- return
- }
- try {
- const result = await app.login()
- if (!result) return
-
- this.setData({ showLoginModal: false, agreeProtocol: false })
- await this.onLoginSuccess()
- wx.showToast({ title: '登录成功', icon: 'success' })
-
- } catch (e) {
- console.error('[Read] 登录失败:', e)
- wx.showToast({ title: '登录失败,请重试', icon: 'none' })
- }
- },
-
- // 【重构】手机号登录(标准流程)
- async handlePhoneLogin(e) {
- if (!e.detail.code) {
- return this.handleWechatLogin()
- }
-
- try {
- const result = await app.loginWithPhone(e.detail.code)
- if (!result) return
-
- this.setData({ showLoginModal: false })
- await this.onLoginSuccess()
- wx.showToast({ title: '登录成功', icon: 'success' })
-
- } catch (e) {
- console.error('[Read] 手机号登录失败:', e)
- wx.showToast({ title: '登录失败', icon: 'none' })
- }
- },
-
- // 【新增】登录成功后的标准处理流程
- async onLoginSuccess() {
- wx.showLoading({ title: '更新状态中...', mask: true })
-
- try {
- // 1. 刷新用户购买状态(从 orders 表拉取最新)
- await accessManager.refreshUserPurchaseStatus()
-
- // 2. 重新拉取免费列表(极端情况:刚登录时当前章节可能改免费了)
- const config = await accessManager.fetchLatestConfig()
- this.setData({ freeIds: config.freeChapters })
-
- // 3. 重新判断当前章节权限
- const newAccessState = await accessManager.determineAccessState(
- this.data.sectionId,
- config.freeChapters
- )
- const canAccess = accessManager.canAccessFullContent(newAccessState)
-
- this.setData({
- accessState: newAccessState,
- canAccess,
- isLoggedIn: true,
- showPaywall: !canAccess
- })
-
- // 4. 如果已解锁,重新加载内容并初始化阅读追踪
- if (canAccess) {
- await this.loadContent(this.data.sectionId, newAccessState)
- readingTracker.init(this.data.sectionId)
- }
-
- wx.hideLoading()
-
- } catch (e) {
- wx.hideLoading()
- console.error('[Read] 登录后更新状态失败:', e)
- wx.showToast({ title: '状态更新失败,请重试', icon: 'none' })
- }
- },
-
- // 购买章节 - 直接调起支付
- async handlePurchaseSection() {
- console.log('[Pay] 点击购买章节按钮')
- wx.showLoading({ title: '处理中...', mask: true })
-
- if (!this.data.isLoggedIn) {
- wx.hideLoading()
- console.log('[Pay] 用户未登录,显示登录弹窗')
- this.setData({ showLoginModal: true })
- return
- }
-
- const price = this.data.section?.price || 1
- console.log('[Pay] 开始支付流程:', { sectionId: this.data.sectionId, price })
- wx.hideLoading()
- await this.processPayment('section', this.data.sectionId, price)
- },
-
- // 购买全书 - 直接调起支付
- async handlePurchaseFullBook() {
- console.log('[Pay] 点击购买全书按钮')
- wx.showLoading({ title: '处理中...', mask: true })
-
- if (!this.data.isLoggedIn) {
- wx.hideLoading()
- console.log('[Pay] 用户未登录,显示登录弹窗')
- this.setData({ showLoginModal: true })
- return
- }
-
- console.log('[Pay] 开始支付流程: 全书', { price: this.data.fullBookPrice })
- wx.hideLoading()
- await this.processPayment('fullbook', null, this.data.fullBookPrice)
- },
-
- // 处理支付 - 调用真实微信支付接口
- async processPayment(type, sectionId, amount) {
- console.log('[Pay] processPayment开始:', { type, sectionId, amount })
-
- // 检查金额是否有效
- if (!amount || amount <= 0) {
- console.error('[Pay] 金额无效:', amount)
- wx.showToast({ title: '价格信息错误', icon: 'none' })
- return
- }
-
- // ✅ 从服务器查询是否已购买(基于 orders 表)
- try {
- wx.showLoading({ title: '检查购买状态...', mask: true })
- const userId = app.globalData.userInfo?.id
-
- if (userId) {
- const checkRes = await app.request(`/api/miniprogram/user/purchase-status?userId=${userId}`)
-
- if (checkRes.success && checkRes.data) {
- // 更新本地购买状态
- app.globalData.hasFullBook = checkRes.data.hasFullBook
- app.globalData.purchasedSections = checkRes.data.purchasedSections || []
-
- // 检查是否已购买
- if (type === 'section' && sectionId) {
- if (checkRes.data.purchasedSections.includes(sectionId)) {
- wx.hideLoading()
- wx.showToast({ title: '已购买过此章节', icon: 'none' })
- return
- }
- }
-
- if (type === 'fullbook' && checkRes.data.hasFullBook) {
- wx.hideLoading()
- wx.showToast({ title: '已购买全书', icon: 'none' })
- return
- }
- }
- }
- } catch (e) {
- console.warn('[Pay] 查询购买状态失败,继续支付流程:', e)
- // 查询失败不影响支付
- }
-
- this.setData({ isPaying: true })
- wx.showLoading({ title: '正在发起支付...', mask: true })
-
- try {
- // 1. 先获取openId (支付必需)
- let openId = app.globalData.openId || wx.getStorageSync('openId')
-
- if (!openId) {
- console.log('[Pay] 需要先获取openId,尝试静默获取')
- wx.showLoading({ title: '获取支付凭证...', mask: true })
- openId = await app.getOpenId()
-
- if (!openId) {
- // openId获取失败,但已登录用户可以使用用户ID替代
- if (app.globalData.isLoggedIn && app.globalData.userInfo?.id) {
- console.log('[Pay] 使用用户ID作为替代')
- openId = app.globalData.userInfo.id
- } else {
- wx.hideLoading()
- wx.showModal({
- title: '提示',
- content: '需要登录后才能支付,请先登录',
- showCancel: false
- })
- this.setData({ showLoginModal: true, isPaying: false })
- return
- }
- }
- }
-
- console.log('[Pay] 开始创建订单:', { type, sectionId, amount, openId: openId.slice(0, 10) + '...' })
- wx.showLoading({ title: '创建订单中...', mask: true })
-
- // 2. 调用后端创建预支付订单
- let paymentData = null
-
- try {
- // 获取章节完整名称用于支付描述
- const sectionTitle = this.data.section?.title || sectionId
- const description = type === 'fullbook'
- ? '《一场Soul的创业实验》全书'
- : `章节${sectionId}-${sectionTitle.length > 20 ? sectionTitle.slice(0, 20) + '...' : sectionTitle}`
-
- // 邀请码:谁邀请了我(从落地页 ref 或 storage 带入),会写入订单 referrer_id / referral_code 便于分销与对账
- const referralCode = wx.getStorageSync('referral_code') || ''
- const res = await app.request('/api/miniprogram/pay', {
- method: 'POST',
- data: {
- openId,
- productType: type,
- productId: sectionId,
- amount,
- description,
- userId: app.globalData.userInfo?.id || '',
- referralCode: referralCode || undefined
- }
- })
-
- console.log('[Pay] 创建订单响应:', res)
-
- if (res.success && res.data?.payParams) {
- paymentData = res.data.payParams
- console.log('[Pay] 获取支付参数成功:', paymentData)
- } else {
- throw new Error(res.error || res.message || '创建订单失败')
- }
- } catch (apiError) {
- console.error('[Pay] API创建订单失败:', apiError)
- wx.hideLoading()
- // 支付接口失败时,显示客服联系方式
- wx.showModal({
- title: '支付通道维护中',
- content: '微信支付正在审核中,请添加客服微信(28533368)手动购买,感谢理解!',
- confirmText: '复制微信号',
- cancelText: '稍后再说',
- success: (res) => {
- if (res.confirm) {
- wx.setClipboardData({
- data: '28533368',
- success: () => {
- wx.showToast({ title: '微信号已复制', icon: 'success' })
- }
- })
- }
- }
- })
- this.setData({ isPaying: false })
- return
- }
-
- // 3. 调用微信支付
- wx.hideLoading()
- console.log('[Pay] 调起微信支付, paymentData:', paymentData)
-
- try {
- await this.callWechatPay(paymentData)
-
- // 4. 【标准流程】支付成功后刷新权限并解锁内容
- console.log('[Pay] 微信支付成功!')
- await this.onPaymentSuccess()
-
- } catch (payErr) {
- console.error('[Pay] 微信支付调起失败:', payErr)
- if (payErr.errMsg && payErr.errMsg.includes('cancel')) {
- wx.showToast({ title: '已取消支付', icon: 'none' })
- } else if (payErr.errMsg && payErr.errMsg.includes('requestPayment:fail')) {
- // 支付失败,可能是参数错误或权限问题
- wx.showModal({
- title: '支付失败',
- content: '微信支付暂不可用,请添加客服微信(28533368)手动购买',
- confirmText: '复制微信号',
- cancelText: '取消',
- success: (res) => {
- if (res.confirm) {
- wx.setClipboardData({
- data: '28533368',
- success: () => wx.showToast({ title: '微信号已复制', icon: 'success' })
- })
- }
- }
- })
- } else {
- wx.showToast({ title: payErr.errMsg || '支付失败', icon: 'none' })
- }
- }
-
- } catch (e) {
- console.error('[Pay] 支付流程异常:', e)
- wx.hideLoading()
- wx.showToast({ title: '支付出错,请重试', icon: 'none' })
- } finally {
- this.setData({ isPaying: false })
- }
- },
-
- // 【新增】支付成功后的标准处理流程
- async onPaymentSuccess() {
- wx.showLoading({ title: '确认购买中...', mask: true })
-
- try {
- // 1. 等待服务端处理支付回调(1-2秒)
- await this.sleep(2000)
-
- // 2. 刷新用户购买状态
- await accessManager.refreshUserPurchaseStatus()
-
- // 3. 重新判断当前章节权限(应为 unlocked_purchased)
- let newAccessState = await accessManager.determineAccessState(
- this.data.sectionId,
- this.data.freeIds
- )
-
- // 如果权限未生效,再重试一次(可能回调延迟)
- if (newAccessState !== 'unlocked_purchased') {
- console.log('[Pay] 权限未生效,1秒后重试...')
- await this.sleep(1000)
- newAccessState = await accessManager.determineAccessState(
- this.data.sectionId,
- this.data.freeIds
- )
- }
-
- const canAccess = accessManager.canAccessFullContent(newAccessState)
-
- this.setData({
- accessState: newAccessState,
- canAccess,
- showPaywall: !canAccess
- })
-
- // 4. 重新加载全文
- await this.loadContent(this.data.sectionId, newAccessState)
-
- // 5. 初始化阅读追踪
- if (canAccess) {
- readingTracker.init(this.data.sectionId)
- }
-
- wx.hideLoading()
- wx.showToast({ title: '购买成功', icon: 'success' })
-
- } catch (e) {
- wx.hideLoading()
- console.error('[Pay] 支付后更新失败:', e)
- wx.showModal({
- title: '提示',
- content: '购买成功,但内容加载失败,请返回重新进入',
- showCancel: false
- })
- }
- },
-
- // ✅ 刷新用户购买状态(从服务器获取最新数据)
- async refreshUserPurchaseStatus() {
- try {
- const userId = app.globalData.userInfo?.id
- if (!userId) {
- console.warn('[Pay] 用户未登录,无法刷新购买状态')
- return
- }
-
- // 调用专门的购买状态查询接口
- const res = await app.request(`/api/miniprogram/user/purchase-status?userId=${userId}`)
-
- if (res.success && res.data) {
- // 更新全局购买状态
- app.globalData.hasFullBook = res.data.hasFullBook
- app.globalData.purchasedSections = res.data.purchasedSections || []
-
- // 更新用户信息中的购买记录
- const userInfo = app.globalData.userInfo || {}
- userInfo.hasFullBook = res.data.hasFullBook
- userInfo.purchasedSections = res.data.purchasedSections || []
- app.globalData.userInfo = userInfo
- wx.setStorageSync('userInfo', userInfo)
-
- console.log('[Pay] ✅ 购买状态已刷新:', {
- hasFullBook: res.data.hasFullBook,
- purchasedCount: res.data.purchasedSections.length
- })
- }
- } catch (e) {
- console.error('[Pay] 刷新购买状态失败:', e)
- // 刷新失败时不影响用户体验,只是记录日志
- }
- },
-
- // 调用微信支付
- callWechatPay(paymentData) {
- return new Promise((resolve, reject) => {
- wx.requestPayment({
- timeStamp: paymentData.timeStamp,
- nonceStr: paymentData.nonceStr,
- package: paymentData.package,
- signType: paymentData.signType || 'MD5',
- paySign: paymentData.paySign,
- success: resolve,
- fail: reject
- })
- })
- },
-
- // 跳转到上一篇
- goToPrev() {
- if (this.data.prevSection) {
- wx.redirectTo({ url: `/pages/read/read?id=${this.data.prevSection.id}` })
- }
- },
-
- // 跳转到下一篇
- goToNext() {
- if (this.data.nextSection) {
- wx.redirectTo({ url: `/pages/read/read?id=${this.data.nextSection.id}` })
- }
- },
-
- // 跳转到推广中心
- goToReferral() {
- wx.navigateTo({ url: '/pages/referral/referral' })
- },
-
- // 生成海报
- async generatePoster() {
- wx.showLoading({ title: '生成中...' })
- this.setData({ showPosterModal: true, isGeneratingPoster: true })
-
- try {
- const ctx = wx.createCanvasContext('posterCanvas', this)
- const { section, contentParagraphs, sectionId } = this.data
- const userInfo = app.globalData.userInfo
- const userId = userInfo?.id || ''
-
- // 获取小程序码(带推荐人参数)
- let qrcodeImage = null
- try {
- const scene = userId ? `id=${sectionId}&ref=${userId.slice(0,10)}` : `id=${sectionId}`
- const qrRes = await app.request('/api/miniprogram/qrcode', {
- method: 'POST',
- data: { scene, page: 'pages/read/read', width: 280 }
- })
- if (qrRes.success && qrRes.image) {
- qrcodeImage = qrRes.image
- }
- } catch (e) {
- console.log('[Poster] 获取小程序码失败,使用占位符')
- }
-
- // 海报尺寸 300x450
- const width = 300
- const height = 450
-
- // 背景渐变
- const grd = ctx.createLinearGradient(0, 0, 0, height)
- grd.addColorStop(0, '#1a1a2e')
- grd.addColorStop(1, '#16213e')
- ctx.setFillStyle(grd)
- ctx.fillRect(0, 0, width, height)
-
- // 顶部装饰条
- ctx.setFillStyle('#00CED1')
- ctx.fillRect(0, 0, width, 4)
-
- // 标题区域
- ctx.setFillStyle('#ffffff')
- ctx.setFontSize(14)
- ctx.fillText('📚 Soul创业派对', 20, 35)
-
- // 章节标题
- ctx.setFontSize(18)
- ctx.setFillStyle('#ffffff')
- const title = section?.title || '精彩内容'
- const titleLines = this.wrapText(ctx, title, width - 40, 18)
- let y = 70
- titleLines.forEach(line => {
- ctx.fillText(line, 20, y)
- y += 26
- })
-
- // 分隔线
- ctx.setStrokeStyle('rgba(255,255,255,0.1)')
- ctx.beginPath()
- ctx.moveTo(20, y + 10)
- ctx.lineTo(width - 20, y + 10)
- ctx.stroke()
-
- // 内容摘要
- ctx.setFontSize(12)
- ctx.setFillStyle('rgba(255,255,255,0.8)')
- y += 30
- const summary = contentParagraphs.slice(0, 3).join(' ').slice(0, 150) + '...'
- const summaryLines = this.wrapText(ctx, summary, width - 40, 12)
- summaryLines.slice(0, 6).forEach(line => {
- ctx.fillText(line, 20, y)
- y += 20
- })
-
- // 底部区域背景
- ctx.setFillStyle('rgba(0,206,209,0.1)')
- ctx.fillRect(0, height - 100, width, 100)
-
- // 左侧提示文字
- ctx.setFillStyle('#ffffff')
- ctx.setFontSize(13)
- ctx.fillText('长按识别小程序码', 20, height - 60)
- ctx.setFillStyle('rgba(255,255,255,0.6)')
- ctx.setFontSize(11)
- ctx.fillText('长按小程序码阅读全文', 20, height - 38)
-
- // 绘制小程序码或占位符
- const drawQRCode = () => {
- return new Promise((resolve) => {
- if (qrcodeImage) {
- // 下载base64图片并绘制
- const fs = wx.getFileSystemManager()
- const filePath = `${wx.env.USER_DATA_PATH}/qrcode_${Date.now()}.png`
- const base64Data = qrcodeImage.replace(/^data:image\/\w+;base64,/, '')
-
- fs.writeFile({
- filePath,
- data: base64Data,
- encoding: 'base64',
- success: () => {
- ctx.drawImage(filePath, width - 85, height - 85, 70, 70)
- resolve()
- },
- fail: () => {
- this.drawQRPlaceholder(ctx, width, height)
- resolve()
- }
- })
- } else {
- this.drawQRPlaceholder(ctx, width, height)
- resolve()
- }
- })
- }
-
- await drawQRCode()
-
- ctx.draw(true, () => {
- wx.hideLoading()
- this.setData({ isGeneratingPoster: false })
- })
- } catch (e) {
- console.error('生成海报失败:', e)
- wx.hideLoading()
- wx.showToast({ title: '生成失败', icon: 'none' })
- this.setData({ showPosterModal: false, isGeneratingPoster: false })
- }
- },
-
- // 绘制小程序码占位符
- drawQRPlaceholder(ctx, width, height) {
- ctx.setFillStyle('#ffffff')
- ctx.beginPath()
- ctx.arc(width - 50, height - 50, 35, 0, Math.PI * 2)
- ctx.fill()
- ctx.setFillStyle('#00CED1')
- ctx.setFontSize(9)
- ctx.fillText('扫码', width - 57, height - 52)
- ctx.fillText('阅读', width - 57, height - 40)
- },
-
- // 文字换行处理
- wrapText(ctx, text, maxWidth, fontSize) {
- const lines = []
- let line = ''
- for (let i = 0; i < text.length; i++) {
- const testLine = line + text[i]
- const metrics = ctx.measureText(testLine)
- if (metrics.width > maxWidth && line) {
- lines.push(line)
- line = text[i]
- } else {
- line = testLine
- }
- }
- if (line) lines.push(line)
- return lines
- },
-
- // 关闭海报弹窗
- closePosterModal() {
- this.setData({ showPosterModal: false })
- },
-
- // 保存海报到相册
- savePoster() {
- wx.canvasToTempFilePath({
- canvasId: 'posterCanvas',
- success: (res) => {
- wx.saveImageToPhotosAlbum({
- filePath: res.tempFilePath,
- success: () => {
- wx.showToast({ title: '已保存到相册', icon: 'success' })
- this.setData({ showPosterModal: false })
- },
- fail: (err) => {
- if (err.errMsg.includes('auth deny')) {
- wx.showModal({
- title: '提示',
- content: '需要相册权限才能保存海报',
- confirmText: '去设置',
- success: (res) => {
- if (res.confirm) {
- wx.openSetting()
- }
- }
- })
- } else {
- wx.showToast({ title: '保存失败', icon: 'none' })
- }
- }
- })
- },
- fail: () => {
- wx.showToast({ title: '生成图片失败', icon: 'none' })
- }
- }, this)
- },
-
- // 阻止冒泡
- stopPropagation() {},
-
- // 【新增】页面隐藏时上报阅读进度
- onHide() {
- readingTracker.onPageHide()
- },
-
- // 【新增】页面卸载时清理追踪器
- onUnload() {
- readingTracker.cleanup()
- },
-
- // 【新增】重试加载(当 accessState 为 error 时)
- async handleRetry() {
- wx.showLoading({ title: '重试中...', mask: true })
-
- try {
- // 重新拉取配置
- const config = await accessManager.fetchLatestConfig()
- this.setData({ freeIds: config.freeChapters })
-
- // 重新判断权限
- const newAccessState = await accessManager.determineAccessState(
- this.data.sectionId,
- config.freeChapters
- )
- const canAccess = accessManager.canAccessFullContent(newAccessState)
-
- this.setData({
- accessState: newAccessState,
- canAccess,
- showPaywall: !canAccess
- })
-
- // 重新加载内容
- await this.loadContent(this.data.sectionId, newAccessState)
-
- // 如果有权限,初始化阅读追踪
- if (canAccess) {
- readingTracker.init(this.data.sectionId)
- }
-
- // 加载导航
- this.loadNavigation(this.data.sectionId)
-
- wx.hideLoading()
- wx.showToast({ title: '加载成功', icon: 'success' })
-
- } catch (e) {
- wx.hideLoading()
- console.error('[Read] 重试失败:', e)
- wx.showToast({ title: '重试失败,请检查网络', icon: 'none' })
- }
- },
-
- // 工具:延迟
- sleep(ms) {
- return new Promise(resolve => setTimeout(resolve, ms))
- }
-})
diff --git a/归档/miniprogram/pages/read/read.js.backup b/归档/miniprogram/pages/read/read.js.backup
deleted file mode 100644
index 65ea2b60..00000000
--- a/归档/miniprogram/pages/read/read.js.backup
+++ /dev/null
@@ -1,1055 +0,0 @@
-/**
- * Soul创业派对 - 阅读页
- * 开发: 卡若
- * 技术支持: 存客宝
- */
-
-const app = getApp()
-
-Page({
- data: {
- // 系统信息
- statusBarHeight: 44,
- navBarHeight: 88,
-
- // 章节信息
- sectionId: '',
- section: null,
- partTitle: '',
- chapterTitle: '',
-
- // 内容
- content: '',
- previewContent: '',
- contentParagraphs: [],
- previewParagraphs: [],
- loading: true,
-
- // 用户状态
- isLoggedIn: false,
- hasFullBook: false,
- canAccess: false,
- purchasedCount: 0,
-
- // 阅读进度
- readingProgress: 0,
- showPaywall: false,
-
- // 上一篇/下一篇
- prevSection: null,
- nextSection: null,
-
- // 价格
- sectionPrice: 1,
- fullBookPrice: 9.9,
- totalSections: 62,
-
- // 弹窗
- showShareModal: false,
- showLoginModal: false,
- showPosterModal: false,
- isPaying: false,
- isGeneratingPoster: false,
-
- // 免费章节
- freeIds: ['preface', 'epilogue', '1.1', 'appendix-1', 'appendix-2', 'appendix-3']
- },
-
- onLoad(options) {
- const { id, ref } = options
-
- this.setData({
- statusBarHeight: app.globalData.statusBarHeight,
- navBarHeight: app.globalData.navBarHeight,
- sectionId: id
- })
-
- // 处理推荐码绑定
- if (ref) {
- console.log('[Read] 检测到推荐码:', ref)
- wx.setStorageSync('referral_code', ref)
- app.handleReferralCode({ query: { ref } })
- }
-
- // 先拉取免费章节配置再初始化,避免首帧误判免费/付费
- const run = async () => {
- await this.loadFreeChaptersConfig()
- this.initSection(id)
- }
- run()
- },
-
- // 从后端加载免费章节配置
- async loadFreeChaptersConfig() {
- try {
- const res = await app.request('/api/db/config')
- if (res.success && res.freeChapters) {
- this.setData({ freeIds: res.freeChapters })
- console.log('[Read] 加载免费章节配置:', res.freeChapters)
- }
- } catch (e) {
- console.log('[Read] 使用默认免费章节配置')
- }
- },
-
- onPageScroll(e) {
- // 计算阅读进度
- const query = wx.createSelectorQuery()
- query.select('.page').boundingClientRect()
- query.exec((res) => {
- if (res[0]) {
- const scrollTop = e.scrollTop
- const pageHeight = res[0].height - this.data.statusBarHeight - 200
- const progress = pageHeight > 0 ? Math.min((scrollTop / pageHeight) * 100, 100) : 0
- this.setData({ readingProgress: progress })
- }
- })
- },
-
- // 初始化章节:免费直接可看;付费则请求接口校验是否已购买
- async initSection(id) {
- this.setData({ loading: true })
-
- try {
- const section = this.getSectionInfo(id)
- const { isLoggedIn, hasFullBook, purchasedSections } = app.globalData
- const isFree = this.data.freeIds.includes(id)
-
- let canAccess = isFree
- let isPurchased = false
-
- if (!isFree) {
- if (!isLoggedIn || !app.globalData.userInfo?.id) {
- canAccess = false
- } else {
- try {
- const userId = app.globalData.userInfo.id
- const res = await app.request(
- `/api/user/check-purchased?userId=${encodeURIComponent(userId)}&type=section&productId=${encodeURIComponent(id)}`
- )
- if (res.success && res.data && res.data.isPurchased) {
- isPurchased = true
- canAccess = true
- if (!purchasedSections.includes(id)) {
- app.globalData.purchasedSections = [...(app.globalData.purchasedSections || []), id]
- }
- if (res.data.reason === 'has_full_book') {
- app.globalData.hasFullBook = true
- }
- }
- } catch (e) {
- console.warn('[Read] 校验购买状态失败,保守处理为未购买:', e)
- isPurchased = false
- canAccess = false
- }
- }
- }
-
- const purchasedCount = (app.globalData.purchasedSections || []).length
-
- this.setData({
- section,
- isLoggedIn,
- hasFullBook: app.globalData.hasFullBook,
- canAccess,
- purchasedCount,
- showPaywall: !canAccess
- })
-
- await this.loadContent(id)
-
- if (canAccess) {
- app.markSectionAsRead(id)
- }
-
- this.loadNavigation(id)
-
- } catch (e) {
- console.error('初始化章节失败:', e)
- wx.showToast({ title: '加载失败', icon: 'none' })
- } finally {
- this.setData({ loading: false })
- }
- },
-
- // 获取章节信息
- getSectionInfo(id) {
- // 特殊章节
- if (id === 'preface') {
- return { id: 'preface', title: '为什么我每天早上6点在Soul开播?', isFree: true, price: 0 }
- }
- if (id === 'epilogue') {
- return { id: 'epilogue', title: '这本书的真实目的', isFree: true, price: 0 }
- }
- if (id.startsWith('appendix')) {
- const appendixTitles = {
- 'appendix-1': 'Soul派对房精选对话',
- 'appendix-2': '创业者自检清单',
- 'appendix-3': '本书提到的工具和资源'
- }
- return { id, title: appendixTitles[id] || '附录', isFree: true, price: 0 }
- }
-
- // 普通章节
- return {
- id: id,
- title: this.getSectionTitle(id),
- isFree: id === '1.1',
- price: 1
- }
- },
-
- // 获取章节标题
- getSectionTitle(id) {
- const titles = {
- '1.1': '荷包:电动车出租的被动收入模式',
- '1.2': '老墨:资源整合高手的社交方法',
- '1.3': '笑声背后的MBTI',
- '1.4': '人性的三角结构:利益、情感、价值观',
- '1.5': '沟通差的问题:为什么你说的别人听不懂',
- '2.1': '相亲故事:你以为找的是人,实际是在找模式',
- '2.2': '找工作迷茫者:为什么简历解决不了人生',
- '2.3': '撸运费险:小钱困住大脑的真实心理',
- '2.4': '游戏上瘾的年轻人:不是游戏吸引他,是生活没吸引力',
- '2.5': '健康焦虑(我的糖尿病经历):疾病是人生的第一次清醒',
- '3.1': '3000万流水如何跑出来(退税模式解析)',
- '8.1': '流量杠杆:抖音、Soul、飞书',
- '9.14': '大健康私域:一个月150万的70后'
- }
- return titles[id] || `章节 ${id}`
- },
-
- // 加载内容 - 三级降级方案:API → 本地缓存 → 备用API
- async loadContent(id) {
- const cacheKey = `chapter_${id}`
-
- // 1. 优先从API获取
- try {
- const res = await this.fetchChapterWithTimeout(id, 5000)
- if (res && res.content) {
- this.setChapterContent(res)
- // 成功后缓存到本地
- wx.setStorageSync(cacheKey, res)
- console.log('[Read] 从API加载成功:', id)
- return
- }
- } catch (e) {
- console.warn('[Read] API加载失败,尝试本地缓存:', e.message)
- }
-
- // 2. API失败,尝试从本地缓存读取
- try {
- const cached = wx.getStorageSync(cacheKey)
- if (cached && cached.content) {
- this.setChapterContent(cached)
- console.log('[Read] 从本地缓存加载成功:', id)
- // 后台静默刷新
- this.silentRefresh(id)
- return
- }
- } catch (e) {
- console.warn('[Read] 本地缓存读取失败')
- }
-
- // 3. 都失败,显示加载中并持续重试
- this.setData({
- contentParagraphs: ['章节内容加载中...', '正在尝试连接服务器,请稍候...'],
- previewParagraphs: ['章节内容加载中...']
- })
-
- // 延迟重试(最多3次)
- this.retryLoadContent(id, 3)
- },
-
- // 带超时的章节请求
- fetchChapterWithTimeout(id, timeout = 5000) {
- return new Promise((resolve, reject) => {
- const timer = setTimeout(() => {
- reject(new Error('请求超时'))
- }, timeout)
-
- app.request(`/api/book/chapter/${id}`)
- .then(res => {
- clearTimeout(timer)
- resolve(res)
- })
- .catch(err => {
- clearTimeout(timer)
- reject(err)
- })
- })
- },
-
- // 设置章节内容
- setChapterContent(res) {
- const lines = res.content.split('\n').filter(line => line.trim())
- const previewCount = Math.ceil(lines.length * 0.2)
-
- this.setData({
- content: res.content,
- previewContent: lines.slice(0, previewCount).join('\n'),
- contentParagraphs: lines,
- previewParagraphs: lines.slice(0, previewCount),
- partTitle: res.partTitle || '',
- chapterTitle: res.chapterTitle || ''
- })
- },
-
- // 静默刷新(后台更新缓存)
- async silentRefresh(id) {
- try {
- const res = await this.fetchChapterWithTimeout(id, 10000)
- if (res && res.content) {
- wx.setStorageSync(`chapter_${id}`, res)
- console.log('[Read] 后台缓存更新成功:', id)
- }
- } catch (e) {
- // 静默失败不处理
- }
- },
-
- // 重试加载
- retryLoadContent(id, maxRetries, currentRetry = 0) {
- if (currentRetry >= maxRetries) {
- this.setData({
- contentParagraphs: ['内容加载失败', '请检查网络连接后下拉刷新重试'],
- previewParagraphs: ['内容加载失败']
- })
- return
- }
-
- setTimeout(async () => {
- try {
- const res = await this.fetchChapterWithTimeout(id, 8000)
- if (res && res.content) {
- this.setChapterContent(res)
- wx.setStorageSync(`chapter_${id}`, res)
- console.log('[Read] 重试成功:', id, '第', currentRetry + 1, '次')
- return
- }
- } catch (e) {
- console.warn('[Read] 重试失败,继续重试:', currentRetry + 1)
- }
- this.retryLoadContent(id, maxRetries, currentRetry + 1)
- }, 2000 * (currentRetry + 1))
- },
-
-
- // 加载导航
- loadNavigation(id) {
- const sectionOrder = [
- 'preface', '1.1', '1.2', '1.3', '1.4', '1.5',
- '2.1', '2.2', '2.3', '2.4', '2.5',
- '3.1', '3.2', '3.3', '3.4',
- '4.1', '4.2', '4.3', '4.4', '4.5',
- '5.1', '5.2', '5.3', '5.4', '5.5',
- '6.1', '6.2', '6.3', '6.4',
- '7.1', '7.2', '7.3', '7.4', '7.5',
- '8.1', '8.2', '8.3', '8.4', '8.5', '8.6',
- '9.1', '9.2', '9.3', '9.4', '9.5', '9.6', '9.7', '9.8', '9.9', '9.10', '9.11', '9.12', '9.13', '9.14',
- '10.1', '10.2', '10.3', '10.4',
- '11.1', '11.2', '11.3', '11.4', '11.5',
- 'epilogue'
- ]
-
- const currentIndex = sectionOrder.indexOf(id)
- const prevId = currentIndex > 0 ? sectionOrder[currentIndex - 1] : null
- const nextId = currentIndex < sectionOrder.length - 1 ? sectionOrder[currentIndex + 1] : null
-
- this.setData({
- prevSection: prevId ? { id: prevId, title: this.getSectionTitle(prevId) } : null,
- nextSection: nextId ? { id: nextId, title: this.getSectionTitle(nextId) } : null
- })
- },
-
- // 返回
- goBack() {
- wx.navigateBack({
- fail: () => wx.switchTab({ url: '/pages/chapters/chapters' })
- })
- },
-
- // 分享弹窗
- showShare() {
- this.setData({ showShareModal: true })
- },
-
- closeShareModal() {
- this.setData({ showShareModal: false })
- },
-
- // 复制链接
- copyLink() {
- const userInfo = app.globalData.userInfo
- const referralCode = userInfo?.referralCode || ''
- const shareUrl = `https://soul.quwanzhi.com/read/${this.data.sectionId}${referralCode ? '?ref=' + referralCode : ''}`
-
- wx.setClipboardData({
- data: shareUrl,
- success: () => {
- wx.showToast({ title: '链接已复制', icon: 'success' })
- this.setData({ showShareModal: false })
- }
- })
- },
-
- // 复制分享文案(朋友圈风格)
- copyShareText() {
- const { section } = this.data
-
- const shareText = `🔥 刚看完这篇《${section?.title || 'Soul创业派对'}》,太上头了!
-
-62个真实商业案例,每个都是从0到1的实战经验。私域运营、资源整合、商业变现,干货满满。
-
-推荐给正在创业或想创业的朋友,搜"Soul创业派对"小程序就能看!
-
-#创业派对 #私域运营 #商业案例`
-
- wx.setClipboardData({
- data: shareText,
- success: () => {
- wx.showToast({ title: '文案已复制', icon: 'success' })
- }
- })
- },
-
- // 分享到微信 - 自动带分享人ID
- onShareAppMessage() {
- const { section, sectionId } = this.data
- const userInfo = app.globalData.userInfo
- const referralCode = userInfo?.referralCode || wx.getStorageSync('referralCode') || ''
-
- // 分享标题优化
- const shareTitle = section?.title
- ? `📚 ${section.title.length > 20 ? section.title.slice(0, 20) + '...' : section.title}`
- : '📚 Soul创业派对 - 真实商业故事'
-
- return {
- title: shareTitle,
- path: `/pages/read/read?id=${sectionId}${referralCode ? '&ref=' + referralCode : ''}`,
- imageUrl: '/assets/share-cover.png' // 可配置分享封面图
- }
- },
-
- // 分享到朋友圈
- onShareTimeline() {
- const { section, sectionId } = this.data
- const userInfo = app.globalData.userInfo
- const referralCode = userInfo?.referralCode || ''
-
- return {
- title: `${section?.title || 'Soul创业派对'} - 来自派对房的真实故事`,
- query: `id=${sectionId}${referralCode ? '&ref=' + referralCode : ''}`
- }
- },
-
- // 显示登录弹窗
- showLoginModal() {
- this.setData({ showLoginModal: true })
- },
-
- closeLoginModal() {
- this.setData({ showLoginModal: false })
- },
-
- // 从服务端刷新购买状态,避免登录后误用旧数据导致误解锁
- async refreshPurchaseFromServer() {
- const userId = app.globalData.userInfo?.id
- if (!userId) return
- try {
- const res = await app.request(`/api/user/purchase-status?userId=${encodeURIComponent(userId)}`)
- if (res.success && res.data) {
- app.globalData.hasFullBook = res.data.hasFullBook || false
- app.globalData.purchasedSections = res.data.purchasedSections || []
- wx.setStorageSync('userInfo', { ...(app.globalData.userInfo || {}), purchasedSections: app.globalData.purchasedSections, hasFullBook: app.globalData.hasFullBook })
- }
- } catch (e) {
- console.warn('[Read] 刷新购买状态失败:', e)
- }
- },
-
- // 微信登录(含:因付款弹窗发起的登录)
- async handleWechatLogin() {
- try {
- const result = await app.login()
- if (result) {
- this.setData({ showLoginModal: false })
- // 登录后必须重新向服务端拉取购买状态,并重新校验当前章节是否已购买,再决定是否解锁(避免误解锁)
- await this.refreshPurchaseFromServer()
- await this.recheckCurrentSectionAndRefresh()
- wx.showToast({ title: '登录成功', icon: 'success' })
- }
- } catch (e) {
- wx.showToast({ title: '登录失败', icon: 'none' })
- }
- },
-
- // 手机号登录(含:因付款弹窗发起的登录)
- async handlePhoneLogin(e) {
- if (!e.detail.code) {
- return this.handleWechatLogin()
- }
-
- try {
- const result = await app.loginWithPhone(e.detail.code)
- if (result) {
- this.setData({ showLoginModal: false })
- // 登录后必须重新向服务端拉取购买状态,并重新校验当前章节是否已购买,再决定是否解锁(避免误解锁)
- await this.refreshPurchaseFromServer()
- await this.recheckCurrentSectionAndRefresh()
- wx.showToast({ title: '登录成功', icon: 'success' })
- }
- } catch (e) {
- wx.showToast({ title: '登录失败', icon: 'none' })
- }
- },
-
- // 登录后专用:重新向服务端校验当前章节是否已付费购买(或是否已改为免费),再刷新页面状态
- async recheckCurrentSectionAndRefresh() {
- const sectionId = this.data.sectionId
- // 极端情况:用户登录后,当前章节可能刚被后台改为免费,先拉取最新免费列表再判断
- await this.loadFreeChaptersConfig()
- const isFree = this.data.freeIds.includes(sectionId)
- if (isFree) {
- this.setData({ isLoggedIn: true, canAccess: true, showPaywall: false })
- await this.initSection(sectionId)
- return
- }
- const userId = app.globalData.userInfo?.id
- if (!userId) {
- this.setData({ canAccess: false, showPaywall: true })
- return
- }
- try {
- const res = await app.request(
- `/api/user/check-purchased?userId=${encodeURIComponent(userId)}&type=section&productId=${encodeURIComponent(sectionId)}`
- )
- const isPurchased = res.success && res.data && res.data.isPurchased
- if (isPurchased) {
- if (!(app.globalData.purchasedSections || []).includes(sectionId)) {
- app.globalData.purchasedSections = [...(app.globalData.purchasedSections || []), sectionId]
- }
- if (res.data.reason === 'has_full_book') {
- app.globalData.hasFullBook = true
- }
- }
- this.setData({
- isLoggedIn: true,
- hasFullBook: app.globalData.hasFullBook,
- canAccess: isPurchased,
- purchasedCount: (app.globalData.purchasedSections || []).length,
- showPaywall: !isPurchased
- })
- if (isPurchased) {
- app.markSectionAsRead(sectionId)
- }
- await this.initSection(sectionId)
- } catch (e) {
- console.warn('[Read] 登录后校验当前章节购买状态失败,保守处理为未购买:', e)
- this.setData({
- isLoggedIn: true,
- canAccess: false,
- showPaywall: true
- })
- await this.initSection(sectionId)
- }
- },
-
- // 购买章节 - 直接调起支付
- async handlePurchaseSection() {
- console.log('[Pay] 点击购买章节按钮')
- wx.showLoading({ title: '处理中...', mask: true })
-
- if (!this.data.isLoggedIn) {
- wx.hideLoading()
- console.log('[Pay] 用户未登录,显示登录弹窗')
- this.setData({ showLoginModal: true })
- return
- }
-
- const price = this.data.section?.price || 1
- console.log('[Pay] 开始支付流程:', { sectionId: this.data.sectionId, price })
- wx.hideLoading()
- await this.processPayment('section', this.data.sectionId, price)
- },
-
- // 购买全书 - 直接调起支付
- async handlePurchaseFullBook() {
- console.log('[Pay] 点击购买全书按钮')
- wx.showLoading({ title: '处理中...', mask: true })
-
- if (!this.data.isLoggedIn) {
- wx.hideLoading()
- console.log('[Pay] 用户未登录,显示登录弹窗')
- this.setData({ showLoginModal: true })
- return
- }
-
- console.log('[Pay] 开始支付流程: 全书', { price: this.data.fullBookPrice })
- wx.hideLoading()
- await this.processPayment('fullbook', null, this.data.fullBookPrice)
- },
-
- // 处理支付 - 调用真实微信支付接口
- async processPayment(type, sectionId, amount) {
- console.log('[Pay] processPayment开始:', { type, sectionId, amount })
-
- // 检查金额是否有效
- if (!amount || amount <= 0) {
- console.error('[Pay] 金额无效:', amount)
- wx.showToast({ title: '价格信息错误', icon: 'none' })
- return
- }
-
- // ✅ 从服务器查询是否已购买(基于 orders 表)
- try {
- wx.showLoading({ title: '检查购买状态...', mask: true })
- const userId = app.globalData.userInfo?.id
-
- if (userId) {
- const checkRes = await app.request(`/api/user/purchase-status?userId=${userId}`)
-
- if (checkRes.success && checkRes.data) {
- // 更新本地购买状态
- app.globalData.hasFullBook = checkRes.data.hasFullBook
- app.globalData.purchasedSections = checkRes.data.purchasedSections || []
-
- // 检查是否已购买
- if (type === 'section' && sectionId) {
- if (checkRes.data.purchasedSections.includes(sectionId)) {
- wx.hideLoading()
- wx.showToast({ title: '已购买过此章节', icon: 'none' })
- return
- }
- }
-
- if (type === 'fullbook' && checkRes.data.hasFullBook) {
- wx.hideLoading()
- wx.showToast({ title: '已购买全书', icon: 'none' })
- return
- }
- }
- }
- } catch (e) {
- console.warn('[Pay] 查询购买状态失败,继续支付流程:', e)
- // 查询失败不影响支付
- }
-
- this.setData({ isPaying: true })
- wx.showLoading({ title: '正在发起支付...', mask: true })
-
- try {
- // 1. 先获取openId (支付必需)
- let openId = app.globalData.openId || wx.getStorageSync('openId')
-
- if (!openId) {
- console.log('[Pay] 需要先获取openId,尝试静默获取')
- wx.showLoading({ title: '获取支付凭证...', mask: true })
- openId = await app.getOpenId()
-
- if (!openId) {
- // openId获取失败,但已登录用户可以使用用户ID替代
- if (app.globalData.isLoggedIn && app.globalData.userInfo?.id) {
- console.log('[Pay] 使用用户ID作为替代')
- openId = app.globalData.userInfo.id
- } else {
- wx.hideLoading()
- wx.showModal({
- title: '提示',
- content: '需要登录后才能支付,请先登录',
- showCancel: false
- })
- this.setData({ showLoginModal: true, isPaying: false })
- return
- }
- }
- }
-
- console.log('[Pay] 开始创建订单:', { type, sectionId, amount, openId: openId.slice(0, 10) + '...' })
- wx.showLoading({ title: '创建订单中...', mask: true })
-
- // 2. 调用后端创建预支付订单
- let paymentData = null
-
- try {
- // 获取章节完整名称用于支付描述
- const sectionTitle = this.data.section?.title || sectionId
- const description = type === 'fullbook'
- ? '《一场Soul的创业实验》全书'
- : `章节${sectionId}-${sectionTitle.length > 20 ? sectionTitle.slice(0, 20) + '...' : sectionTitle}`
-
- // 邀请码:谁邀请了我(从落地页 ref 或 storage 带入),用于订单分销归属
- const referralCode = wx.getStorageSync('referral_code') || ''
- const res = await app.request('/api/miniprogram/pay', {
- method: 'POST',
- data: {
- openId,
- productType: type,
- productId: sectionId,
- amount,
- description,
- userId: app.globalData.userInfo?.id || '',
- referralCode: referralCode || undefined
- }
- })
-
- console.log('[Pay] 创建订单响应:', res)
-
- if (res.success && res.data?.payParams) {
- paymentData = res.data.payParams
- console.log('[Pay] 获取支付参数成功:', paymentData)
- } else {
- throw new Error(res.error || res.message || '创建订单失败')
- }
- } catch (apiError) {
- console.error('[Pay] API创建订单失败:', apiError)
- wx.hideLoading()
- // 支付接口失败时,显示客服联系方式
- wx.showModal({
- title: '支付通道维护中',
- content: '微信支付正在审核中,请添加客服微信(28533368)手动购买,感谢理解!',
- confirmText: '复制微信号',
- cancelText: '稍后再说',
- success: (res) => {
- if (res.confirm) {
- wx.setClipboardData({
- data: '28533368',
- success: () => {
- wx.showToast({ title: '微信号已复制', icon: 'success' })
- }
- })
- }
- }
- })
- this.setData({ isPaying: false })
- return
- }
-
- // 3. 调用微信支付
- wx.hideLoading()
- console.log('[Pay] 调起微信支付, paymentData:', paymentData)
-
- try {
- await this.callWechatPay(paymentData)
-
- // 4. 支付成功,刷新用户购买状态
- console.log('[Pay] 微信支付成功!')
- wx.showLoading({ title: '正在确认购买...', mask: true })
-
- // 等待后端处理支付回调(1-3秒)
- await new Promise(resolve => setTimeout(resolve, 2000))
-
- // 重新获取用户信息(包含最新购买记录)
- await this.refreshUserPurchaseStatus()
-
- wx.hideLoading()
- wx.showToast({ title: '购买成功', icon: 'success' })
-
- // 5. 刷新页面
- this.initSection(this.data.sectionId)
- } catch (payErr) {
- console.error('[Pay] 微信支付调起失败:', payErr)
- if (payErr.errMsg && payErr.errMsg.includes('cancel')) {
- wx.showToast({ title: '已取消支付', icon: 'none' })
- } else if (payErr.errMsg && payErr.errMsg.includes('requestPayment:fail')) {
- // 支付失败,可能是参数错误或权限问题
- wx.showModal({
- title: '支付失败',
- content: '微信支付暂不可用,请添加客服微信(28533368)手动购买',
- confirmText: '复制微信号',
- cancelText: '取消',
- success: (res) => {
- if (res.confirm) {
- wx.setClipboardData({
- data: '28533368',
- success: () => wx.showToast({ title: '微信号已复制', icon: 'success' })
- })
- }
- }
- })
- } else {
- wx.showToast({ title: payErr.errMsg || '支付失败', icon: 'none' })
- }
- }
-
- } catch (e) {
- console.error('[Pay] 支付流程异常:', e)
- wx.hideLoading()
- wx.showToast({ title: '支付出错,请重试', icon: 'none' })
- } finally {
- this.setData({ isPaying: false })
- }
- },
-
- // ✅ 刷新用户购买状态(从服务器获取最新数据)
- async refreshUserPurchaseStatus() {
- try {
- const userId = app.globalData.userInfo?.id
- if (!userId) {
- console.warn('[Pay] 用户未登录,无法刷新购买状态')
- return
- }
-
- // 调用专门的购买状态查询接口
- const res = await app.request(`/api/user/purchase-status?userId=${userId}`)
-
- if (res.success && res.data) {
- // 更新全局购买状态
- app.globalData.hasFullBook = res.data.hasFullBook
- app.globalData.purchasedSections = res.data.purchasedSections || []
-
- // 更新用户信息中的购买记录
- const userInfo = app.globalData.userInfo || {}
- userInfo.hasFullBook = res.data.hasFullBook
- userInfo.purchasedSections = res.data.purchasedSections || []
- app.globalData.userInfo = userInfo
- wx.setStorageSync('userInfo', userInfo)
-
- console.log('[Pay] ✅ 购买状态已刷新:', {
- hasFullBook: res.data.hasFullBook,
- purchasedCount: res.data.purchasedSections.length
- })
- }
- } catch (e) {
- console.error('[Pay] 刷新购买状态失败:', e)
- // 刷新失败时不影响用户体验,只是记录日志
- }
- },
-
- // 调用微信支付
- callWechatPay(paymentData) {
- return new Promise((resolve, reject) => {
- wx.requestPayment({
- timeStamp: paymentData.timeStamp,
- nonceStr: paymentData.nonceStr,
- package: paymentData.package,
- signType: paymentData.signType || 'MD5',
- paySign: paymentData.paySign,
- success: resolve,
- fail: reject
- })
- })
- },
-
- // 跳转到上一篇
- goToPrev() {
- if (this.data.prevSection) {
- wx.redirectTo({ url: `/pages/read/read?id=${this.data.prevSection.id}` })
- }
- },
-
- // 跳转到下一篇
- goToNext() {
- if (this.data.nextSection) {
- wx.redirectTo({ url: `/pages/read/read?id=${this.data.nextSection.id}` })
- }
- },
-
- // 跳转到推广中心
- goToReferral() {
- wx.navigateTo({ url: '/pages/referral/referral' })
- },
-
- // 生成海报
- async generatePoster() {
- wx.showLoading({ title: '生成中...' })
- this.setData({ showPosterModal: true, isGeneratingPoster: true })
-
- try {
- const ctx = wx.createCanvasContext('posterCanvas', this)
- const { section, contentParagraphs, sectionId } = this.data
- const userInfo = app.globalData.userInfo
- const userId = userInfo?.id || ''
-
- // 获取小程序码(带推荐人参数)
- let qrcodeImage = null
- try {
- const scene = userId ? `id=${sectionId}&ref=${userId.slice(0,10)}` : `id=${sectionId}`
- const qrRes = await app.request('/api/miniprogram/qrcode', {
- method: 'POST',
- data: { scene, page: 'pages/read/read', width: 280 }
- })
- if (qrRes.success && qrRes.image) {
- qrcodeImage = qrRes.image
- }
- } catch (e) {
- console.log('[Poster] 获取小程序码失败,使用占位符')
- }
-
- // 海报尺寸 300x450
- const width = 300
- const height = 450
-
- // 背景渐变
- const grd = ctx.createLinearGradient(0, 0, 0, height)
- grd.addColorStop(0, '#1a1a2e')
- grd.addColorStop(1, '#16213e')
- ctx.setFillStyle(grd)
- ctx.fillRect(0, 0, width, height)
-
- // 顶部装饰条
- ctx.setFillStyle('#00CED1')
- ctx.fillRect(0, 0, width, 4)
-
- // 标题区域
- ctx.setFillStyle('#ffffff')
- ctx.setFontSize(14)
- ctx.fillText('📚 Soul创业派对', 20, 35)
-
- // 章节标题
- ctx.setFontSize(18)
- ctx.setFillStyle('#ffffff')
- const title = section?.title || '精彩内容'
- const titleLines = this.wrapText(ctx, title, width - 40, 18)
- let y = 70
- titleLines.forEach(line => {
- ctx.fillText(line, 20, y)
- y += 26
- })
-
- // 分隔线
- ctx.setStrokeStyle('rgba(255,255,255,0.1)')
- ctx.beginPath()
- ctx.moveTo(20, y + 10)
- ctx.lineTo(width - 20, y + 10)
- ctx.stroke()
-
- // 内容摘要
- ctx.setFontSize(12)
- ctx.setFillStyle('rgba(255,255,255,0.8)')
- y += 30
- const summary = contentParagraphs.slice(0, 3).join(' ').slice(0, 150) + '...'
- const summaryLines = this.wrapText(ctx, summary, width - 40, 12)
- summaryLines.slice(0, 6).forEach(line => {
- ctx.fillText(line, 20, y)
- y += 20
- })
-
- // 底部区域背景
- ctx.setFillStyle('rgba(0,206,209,0.1)')
- ctx.fillRect(0, height - 100, width, 100)
-
- // 左侧提示文字
- ctx.setFillStyle('#ffffff')
- ctx.setFontSize(13)
- ctx.fillText('长按识别小程序码', 20, height - 60)
- ctx.setFillStyle('rgba(255,255,255,0.6)')
- ctx.setFontSize(11)
- ctx.fillText('长按小程序码阅读全文', 20, height - 38)
-
- // 绘制小程序码或占位符
- const drawQRCode = () => {
- return new Promise((resolve) => {
- if (qrcodeImage) {
- // 下载base64图片并绘制
- const fs = wx.getFileSystemManager()
- const filePath = `${wx.env.USER_DATA_PATH}/qrcode_${Date.now()}.png`
- const base64Data = qrcodeImage.replace(/^data:image\/\w+;base64,/, '')
-
- fs.writeFile({
- filePath,
- data: base64Data,
- encoding: 'base64',
- success: () => {
- ctx.drawImage(filePath, width - 85, height - 85, 70, 70)
- resolve()
- },
- fail: () => {
- this.drawQRPlaceholder(ctx, width, height)
- resolve()
- }
- })
- } else {
- this.drawQRPlaceholder(ctx, width, height)
- resolve()
- }
- })
- }
-
- await drawQRCode()
-
- ctx.draw(true, () => {
- wx.hideLoading()
- this.setData({ isGeneratingPoster: false })
- })
- } catch (e) {
- console.error('生成海报失败:', e)
- wx.hideLoading()
- wx.showToast({ title: '生成失败', icon: 'none' })
- this.setData({ showPosterModal: false, isGeneratingPoster: false })
- }
- },
-
- // 绘制小程序码占位符
- drawQRPlaceholder(ctx, width, height) {
- ctx.setFillStyle('#ffffff')
- ctx.beginPath()
- ctx.arc(width - 50, height - 50, 35, 0, Math.PI * 2)
- ctx.fill()
- ctx.setFillStyle('#00CED1')
- ctx.setFontSize(9)
- ctx.fillText('扫码', width - 57, height - 52)
- ctx.fillText('阅读', width - 57, height - 40)
- },
-
- // 文字换行处理
- wrapText(ctx, text, maxWidth, fontSize) {
- const lines = []
- let line = ''
- for (let i = 0; i < text.length; i++) {
- const testLine = line + text[i]
- const metrics = ctx.measureText(testLine)
- if (metrics.width > maxWidth && line) {
- lines.push(line)
- line = text[i]
- } else {
- line = testLine
- }
- }
- if (line) lines.push(line)
- return lines
- },
-
- // 关闭海报弹窗
- closePosterModal() {
- this.setData({ showPosterModal: false })
- },
-
- // 保存海报到相册
- savePoster() {
- wx.canvasToTempFilePath({
- canvasId: 'posterCanvas',
- success: (res) => {
- wx.saveImageToPhotosAlbum({
- filePath: res.tempFilePath,
- success: () => {
- wx.showToast({ title: '已保存到相册', icon: 'success' })
- this.setData({ showPosterModal: false })
- },
- fail: (err) => {
- if (err.errMsg.includes('auth deny')) {
- wx.showModal({
- title: '提示',
- content: '需要相册权限才能保存海报',
- confirmText: '去设置',
- success: (res) => {
- if (res.confirm) {
- wx.openSetting()
- }
- }
- })
- } else {
- wx.showToast({ title: '保存失败', icon: 'none' })
- }
- }
- })
- },
- fail: () => {
- wx.showToast({ title: '生成图片失败', icon: 'none' })
- }
- }, this)
- },
-
- // 阻止冒泡
- stopPropagation() {}
-})
diff --git a/归档/miniprogram/pages/read/read.json b/归档/miniprogram/pages/read/read.json
deleted file mode 100644
index d182eac9..00000000
--- a/归档/miniprogram/pages/read/read.json
+++ /dev/null
@@ -1,9 +0,0 @@
-{
- "usingComponents": {
- "icon": "/components/icon/icon"
- },
- "enablePullDownRefresh": false,
- "backgroundTextStyle": "light",
- "backgroundColor": "#000000",
- "navigationStyle": "custom"
-}
diff --git a/归档/miniprogram/pages/read/read.wxml b/归档/miniprogram/pages/read/read.wxml
deleted file mode 100644
index a17cc3b3..00000000
--- a/归档/miniprogram/pages/read/read.wxml
+++ /dev/null
@@ -1,300 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
- ←
-
-
- {{partTitle}}
- {{chapterTitle}}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{item}}
-
-
-
-
-
-
- 上一篇
- {{prevSection.title}}
-
-
-
-
- 下一篇
-
- {{nextSection.title}}
- →
-
-
-
- 已是最后一篇 🎉
-
-
-
-
-
-
-
-
- 🖼️
- 生成海报
-
-
-
-
-
-
-
-
-
-
- {{item}}
-
-
-
-
-
-
-
- 🔒
- 登录后继续阅读
- 已阅读20%,登录后查看完整内容
-
-
- 立即登录
-
-
-
-
-
-
-
- 上一篇
- 章节 {{prevSection.id}}
-
-
-
-
- 下一篇
-
- {{nextSection.title}}
- →
-
-
-
- 已是最后一篇 🎉
-
-
-
-
-
-
-
-
- {{item}}
-
-
-
-
-
-
-
- 🔒
- 解锁完整内容
- 已阅读20%,购买后继续阅读
-
-
-
-
-
- 购买本章
- ¥{{section && section.price != null ? section.price : sectionPrice}}
-
-
-
-
-
- ✨
- 解锁全部 {{totalSections}} 章
-
-
- ¥{{fullBookPrice || 9.9}}
- 省82%
-
-
-
-
- 分享给好友一起学习,还能赚取佣金
-
-
-
-
-
-
- 上一篇
- 章节 {{prevSection.id}}
-
-
-
-
- 下一篇
-
- {{nextSection.title}}
- →
-
-
-
- 已是最后一篇 🎉
-
-
-
-
-
-
-
-
- {{item}}
-
-
-
-
-
-
-
- ⚠️
- 网络异常
- 无法确认权限,请检查网络后重试
-
-
- 重新加载
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 💾
- 保存到相册
-
-
-
- 长按海报可直接分享到微信
-
-
-
-
-
-
- ✕
- 🔐
- 登录 Soul创业派对
- 登录后可购买章节、解锁更多内容
-
-
-
-
- {{agreeProtocol ? '✓' : ''}}
- 我已阅读并同意
- 《用户协议》
- 和
- 《隐私政策》
-
-
-
-
-
-
-
-
- 支付处理中...
-
-
-
-
-
-
diff --git a/归档/miniprogram/pages/read/read.wxss b/归档/miniprogram/pages/read/read.wxss
deleted file mode 100644
index 8b0ecca2..00000000
--- a/归档/miniprogram/pages/read/read.wxss
+++ /dev/null
@@ -1,984 +0,0 @@
-/**
- * Soul创业实验 - 阅读页样式
- * 1:1还原Web版本UI
- */
-
-.page {
- min-height: 100vh;
- background: #000000;
-}
-
-/* ===== 阅读进度条 ===== */
-.progress-bar-fixed {
- position: fixed;
- left: 0;
- right: 0;
- height: 4rpx;
- background: #1c1c1e;
- z-index: 200;
-}
-
-.progress-fill {
- height: 100%;
- background: linear-gradient(90deg, #00CED1 0%, #20B2AA 100%);
- transition: width 0.15s ease;
-}
-
-/* ===== 导航栏 ===== */
-.nav-bar {
- position: fixed;
- top: 0;
- left: 0;
- right: 0;
- z-index: 100;
- background: rgba(0, 0, 0, 0.8);
- backdrop-filter: blur(40rpx);
- border-bottom: 1rpx solid rgba(255, 255, 255, 0.05);
-}
-
-.nav-content {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 0 24rpx;
- height: 88rpx;
-}
-
-.nav-back, .nav-right-placeholder {
- width: 72rpx;
- height: 72rpx;
- flex-shrink: 0;
-}
-
-.nav-back {
- border-radius: 50%;
- background: #1c1c1e;
- display: flex;
- align-items: center;
- justify-content: center;
-}
-
-.nav-right-placeholder {
- /* 占位保持标题居中 */
-}
-
-.back-arrow {
- font-size: 36rpx;
- color: rgba(255, 255, 255, 0.8);
-}
-
-.nav-info {
- flex: 1;
- text-align: center;
- padding: 0 16rpx;
-}
-
-.nav-part {
- font-size: 20rpx;
- color: rgba(255, 255, 255, 0.4);
- display: block;
-}
-
-.nav-chapter {
- font-size: 24rpx;
- color: rgba(255, 255, 255, 0.6);
- display: block;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
-}
-
-.nav-placeholder {
- width: 100%;
-}
-
-/* ===== 阅读内容 ===== */
-.read-content {
- max-width: 750rpx;
- margin: 0 auto;
- padding: 48rpx 40rpx 200rpx;
-}
-
-/* ===== 章节标题 ===== */
-.chapter-header {
- margin-bottom: 48rpx;
-}
-
-.chapter-meta {
- display: flex;
- align-items: center;
- gap: 16rpx;
- margin-bottom: 24rpx;
-}
-
-.chapter-id {
- font-size: 28rpx;
- font-weight: 500;
- color: #00CED1;
- background: rgba(0, 206, 209, 0.1);
- padding: 8rpx 24rpx;
- border-radius: 32rpx;
-}
-
-.tag {
- display: inline-flex;
- align-items: center;
- justify-content: center;
- font-size: 22rpx;
- padding: 6rpx 16rpx;
- min-width: 80rpx;
- border-radius: 8rpx;
- box-sizing: border-box;
- text-align: center;
-}
-
-.tag-free {
- background: rgba(0, 206, 209, 0.1);
- color: #00CED1;
-}
-
-.chapter-title {
- font-size: 44rpx;
- font-weight: 700;
- color: #ffffff;
- line-height: 1.4;
-}
-
-/* ===== 加载状态 ===== */
-.loading-state {
- display: flex;
- flex-direction: column;
- gap: 32rpx;
-}
-
-.skeleton {
- height: 32rpx;
- background: linear-gradient(90deg, #1c1c1e 25%, #2c2c2e 50%, #1c1c1e 75%);
- background-size: 200% 100%;
- animation: skeleton-loading 1.5s ease-in-out infinite;
- border-radius: 8rpx;
-}
-
-.skeleton-1 { width: 75%; }
-.skeleton-2 { width: 90%; }
-.skeleton-3 { width: 65%; }
-.skeleton-4 { width: 85%; }
-.skeleton-5 { width: 70%; }
-
-@keyframes skeleton-loading {
- 0% { background-position: 200% 0; }
- 100% { background-position: -200% 0; }
-}
-
-/* ===== 文章内容 ===== */
-.article {
- color: rgba(255, 255, 255, 0.85);
- font-size: 34rpx;
- line-height: 1.9;
-}
-
-.paragraph {
- margin-bottom: 48rpx;
- text-align: justify;
-}
-
-.preview {
- position: relative;
-}
-
-/* ===== 渐变遮罩 ===== */
-.fade-mask {
- position: absolute;
- bottom: 0;
- left: -40rpx;
- right: -40rpx;
- height: 300rpx;
- background: linear-gradient(to top, #000000 0%, transparent 100%);
- pointer-events: none;
-}
-
-/* ===== 付费墙 ===== */
-.paywall {
- position: relative;
- z-index: 10;
- margin-top: 48rpx;
- padding: 48rpx;
- background: linear-gradient(135deg, #1c1c1e 0%, #2c2c2e 100%);
- border-radius: 32rpx;
- border: 2rpx solid rgba(0, 206, 209, 0.2);
-}
-
-.paywall-icon {
- width: 128rpx;
- height: 128rpx;
- margin: 0 auto 32rpx;
- background: rgba(0, 206, 209, 0.1);
- border-radius: 32rpx;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 64rpx;
-}
-
-.paywall-title {
- font-size: 40rpx;
- font-weight: 600;
- color: #ffffff;
- text-align: center;
- display: block;
- margin-bottom: 16rpx;
-}
-
-.paywall-desc {
- font-size: 28rpx;
- color: rgba(255, 255, 255, 0.6);
- text-align: center;
- display: block;
- margin-bottom: 48rpx;
-}
-
-/* ===== 购买选项 ===== */
-.purchase-options {
- display: flex;
- flex-direction: column;
- gap: 24rpx;
- margin-bottom: 32rpx;
-}
-
-.purchase-btn {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 28rpx 32rpx;
- border-radius: 24rpx;
-}
-
-.purchase-section {
- background: #2c2c2e;
- border: 2rpx solid rgba(255, 255, 255, 0.1);
-}
-
-.purchase-fullbook {
- background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%);
- box-shadow: 0 8rpx 32rpx rgba(0, 206, 209, 0.3);
-}
-
-.purchase-section .btn-label {
- font-size: 28rpx;
- color: #ffffff;
-}
-
-.purchase-section .btn-price {
- font-size: 28rpx;
- font-weight: 600;
-}
-
-.brand-color {
- color: #00CED1;
-}
-
-.purchase-fullbook .btn-left {
- display: flex;
- align-items: center;
- gap: 8rpx;
-}
-
-.purchase-fullbook .btn-sparkle {
- font-size: 28rpx;
-}
-
-.purchase-fullbook .btn-label {
- font-size: 28rpx;
- color: #ffffff;
- font-weight: 500;
-}
-
-.purchase-fullbook .btn-right {
- text-align: right;
-}
-
-.purchase-fullbook .btn-price {
- font-size: 36rpx;
- font-weight: 700;
- color: #ffffff;
-}
-
-.purchase-fullbook .btn-discount {
- font-size: 22rpx;
- color: rgba(255, 255, 255, 0.7);
- margin-left: 8rpx;
-}
-
-.paywall-tip {
- font-size: 24rpx;
- color: rgba(255, 255, 255, 0.4);
- text-align: center;
- display: block;
-}
-
-/* ===== 章节导航 ===== */
-.chapter-nav {
- margin-top: 96rpx;
- padding-top: 64rpx;
- border-top: 2rpx solid rgba(255, 255, 255, 0.1);
-}
-
-.nav-buttons {
- display: flex;
- gap: 24rpx;
- margin-bottom: 48rpx;
-}
-
-.nav-btn {
- flex: 1;
- padding: 24rpx;
- border-radius: 24rpx;
- max-width: 48%;
-}
-
-.nav-btn-placeholder {
- flex: 1;
- max-width: 48%;
-}
-
-.nav-prev {
- background: #1c1c1e;
- border: 2rpx solid rgba(255, 255, 255, 0.05);
-}
-
-.nav-next {
- background: linear-gradient(90deg, rgba(0, 206, 209, 0.1) 0%, rgba(32, 178, 170, 0.1) 100%);
- border: 2rpx solid rgba(0, 206, 209, 0.2);
-}
-
-.nav-end {
- background: #1c1c1e;
- border: 2rpx solid rgba(255, 255, 255, 0.05);
- text-align: center;
-}
-
-.nav-disabled {
- opacity: 0.5;
-}
-
-.btn-label {
- font-size: 20rpx;
- color: rgba(255, 255, 255, 0.5);
- display: block;
- margin-bottom: 4rpx;
-}
-
-.nav-next .btn-label {
- color: #00CED1;
-}
-
-.btn-title {
- font-size: 24rpx;
- color: #ffffff;
- display: block;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
-}
-
-.btn-row {
- display: flex;
- align-items: center;
- justify-content: space-between;
-}
-
-.btn-arrow {
- font-size: 24rpx;
- color: #00CED1;
- flex-shrink: 0;
- margin-left: 8rpx;
-}
-
-.btn-end-text {
- font-size: 24rpx;
- color: rgba(255, 255, 255, 0.6);
-}
-
-/* ===== 分享操作区 ===== */
-.action-section {
- margin-top: 48rpx;
-}
-
-.action-row-inline {
- display: flex;
- gap: 16rpx;
-}
-
-.action-btn-inline {
- flex: 1;
- display: flex;
- align-items: center;
- justify-content: center;
- gap: 8rpx;
- padding: 24rpx 16rpx;
- border-radius: 16rpx;
- border: none;
- background: transparent;
- line-height: normal;
- box-sizing: border-box;
-}
-
-.action-btn-inline::after {
- border: none;
-}
-
-.btn-share-inline {
- background: rgba(7, 193, 96, 0.15);
- border: 2rpx solid rgba(7, 193, 96, 0.3);
-}
-
-.btn-poster-inline {
- background: rgba(255, 215, 0, 0.15);
- border: 2rpx solid rgba(255, 215, 0, 0.3);
-}
-
-
-.action-icon-small {
- font-size: 28rpx;
-}
-
-.action-text-small {
- font-size: 24rpx;
- color: #ffffff;
- font-weight: 500;
-}
-
-/* ===== 推广提示区 ===== */
-.promo-section {
- margin-top: 32rpx;
-}
-
-.promo-card {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 32rpx;
- background: linear-gradient(135deg, rgba(255, 215, 0, 0.1) 0%, rgba(255, 165, 0, 0.05) 100%);
- border: 2rpx solid rgba(255, 215, 0, 0.2);
- border-radius: 24rpx;
-}
-
-.promo-left {
- display: flex;
- align-items: center;
- gap: 20rpx;
-}
-
-.promo-icon {
- width: 80rpx;
- height: 80rpx;
- background: rgba(255, 215, 0, 0.2);
- border-radius: 50%;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 36rpx;
-}
-
-.promo-info {
- flex: 1;
-}
-
-.promo-title {
- font-size: 30rpx;
- color: #ffffff;
- font-weight: 600;
- display: block;
-}
-
-.promo-desc {
- font-size: 24rpx;
- color: rgba(255, 255, 255, 0.5);
- display: block;
- margin-top: 8rpx;
-}
-
-.promo-right {
- padding-left: 20rpx;
-}
-
-.promo-arrow {
- font-size: 32rpx;
- color: #FFD700;
-}
-
-/* ===== 弹窗 ===== */
-.modal-overlay {
- position: fixed;
- inset: 0;
- background: rgba(0, 0, 0, 0.6);
- backdrop-filter: blur(20rpx);
- display: flex;
- align-items: flex-end;
- justify-content: center;
- z-index: 1000;
-}
-
-.modal-content {
- width: 100%;
- max-width: 750rpx;
- background: #1c1c1e;
- border-radius: 48rpx 48rpx 0 0;
- padding: 48rpx;
- padding-bottom: calc(48rpx + env(safe-area-inset-bottom));
- animation: slideUp 0.3s ease;
-}
-
-@keyframes slideUp {
- from { transform: translateY(100%); }
- to { transform: translateY(0); }
-}
-
-.modal-header {
- display: flex;
- align-items: center;
- justify-content: space-between;
- margin-bottom: 32rpx;
-}
-
-.modal-title {
- font-size: 36rpx;
- font-weight: 600;
- color: #ffffff;
-}
-
-.modal-close {
- width: 64rpx;
- height: 64rpx;
- border-radius: 50%;
- background: rgba(255, 255, 255, 0.1);
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 28rpx;
- color: rgba(255, 255, 255, 0.6);
-}
-
-/* ===== 分享弹窗 ===== */
-.share-link-box {
- padding: 32rpx;
- background: rgba(0, 0, 0, 0.3);
- border: 2rpx solid rgba(255, 255, 255, 0.1);
- border-radius: 24rpx;
- margin-bottom: 32rpx;
-}
-
-.link-label {
- font-size: 22rpx;
- color: rgba(255, 255, 255, 0.5);
- display: block;
- margin-bottom: 16rpx;
-}
-
-.link-url {
- font-size: 26rpx;
- color: #00CED1;
- display: block;
- word-break: break-all;
- font-family: monospace;
-}
-
-.link-tip {
- font-size: 22rpx;
- color: rgba(255, 255, 255, 0.4);
- display: block;
- margin-top: 16rpx;
-}
-
-.share-buttons {
- display: grid;
- grid-template-columns: repeat(3, 1fr);
- gap: 24rpx;
- margin-bottom: 32rpx;
-}
-
-.share-btn {
- display: flex;
- flex-direction: column;
- align-items: center;
- gap: 16rpx;
- padding: 24rpx;
- background: rgba(255, 255, 255, 0.05);
- border-radius: 24rpx;
- border: none;
- line-height: normal;
-}
-
-.share-btn::after {
- border: none;
-}
-
-.share-btn-icon {
- width: 96rpx;
- height: 96rpx;
- border-radius: 50%;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 40rpx;
-}
-
-.icon-copy {
- background: rgba(0, 206, 209, 0.2);
-}
-
-.icon-wechat {
- background: rgba(7, 193, 96, 0.2);
- color: #07C160;
- font-size: 32rpx;
-}
-
-.icon-poster {
- background: rgba(255, 215, 0, 0.2);
-}
-
-.share-btn-text {
- font-size: 24rpx;
- color: rgba(255, 255, 255, 0.6);
-}
-
-/* ===== 支付弹窗 ===== */
-.payment-info {
- padding: 24rpx;
- background: rgba(0, 0, 0, 0.3);
- border-radius: 24rpx;
- margin-bottom: 32rpx;
-}
-
-.payment-type {
- font-size: 26rpx;
- color: rgba(255, 255, 255, 0.6);
- display: block;
- margin-bottom: 16rpx;
-}
-
-.payment-amount {
- display: flex;
- align-items: center;
- justify-content: space-between;
-}
-
-.amount-label {
- font-size: 28rpx;
- color: rgba(255, 255, 255, 0.6);
-}
-
-.amount-value {
- font-size: 48rpx;
- font-weight: 700;
- color: #00CED1;
-}
-
-.payment-methods {
- margin-bottom: 32rpx;
-}
-
-.method-item {
- display: flex;
- align-items: center;
- gap: 16rpx;
- padding: 24rpx;
- background: rgba(255, 255, 255, 0.05);
- border-radius: 16rpx;
- border: 2rpx solid transparent;
-}
-
-.method-active {
- border-color: #07C160;
-}
-
-.method-icon {
- width: 48rpx;
- height: 48rpx;
- background: #07C160;
- color: #ffffff;
- font-size: 24rpx;
- border-radius: 8rpx;
- display: flex;
- align-items: center;
- justify-content: center;
-}
-
-.method-name {
- flex: 1;
- font-size: 28rpx;
- color: #ffffff;
-}
-
-.method-check {
- color: #07C160;
- font-size: 28rpx;
-}
-
-.btn-primary {
- width: 100%;
- padding: 28rpx;
- background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%);
- color: #ffffff;
- font-size: 32rpx;
- font-weight: 600;
- border-radius: 24rpx;
- text-align: center;
- margin-bottom: 16rpx;
-}
-
-.payment-notice {
- font-size: 22rpx;
- color: rgba(255, 255, 255, 0.3);
- text-align: center;
- display: block;
-}
-
-/* ===== 登录提示 ===== */
-.login-prompt {
- margin-top: 32rpx;
-}
-
-.login-btn {
- padding: 28rpx;
- background: rgba(255, 255, 255, 0.1);
- border: 2rpx solid rgba(255, 255, 255, 0.2);
- border-radius: 24rpx;
- text-align: center;
-}
-
-.login-btn-text {
- font-size: 28rpx;
- color: rgba(255, 255, 255, 0.6);
-}
-
-/* ===== 登录弹窗 ===== */
-.login-modal {
- padding: 48rpx 32rpx;
- text-align: center;
-}
-
-.login-icon {
- font-size: 80rpx;
- display: block;
- margin-bottom: 24rpx;
-}
-
-.login-title {
- font-size: 36rpx;
- font-weight: 700;
- color: #ffffff;
- display: block;
- margin-bottom: 16rpx;
-}
-
-.login-desc {
- font-size: 26rpx;
- color: rgba(255, 255, 255, 0.5);
- display: block;
- margin-bottom: 48rpx;
-}
-
-.btn-wechat {
- display: flex;
- align-items: center;
- justify-content: center;
- gap: 16rpx;
- padding: 28rpx;
- background: #07C160;
- color: #ffffff;
- font-size: 30rpx;
- font-weight: 600;
- border-radius: 24rpx;
- margin-bottom: 20rpx;
- border: none;
-}
-
-.btn-wechat::after {
- border: none;
-}
-
-.btn-wechat-icon {
- width: 40rpx;
- height: 40rpx;
- background: rgba(255, 255, 255, 0.2);
- border-radius: 8rpx;
- font-size: 24rpx;
- display: flex;
- align-items: center;
- justify-content: center;
-}
-
-.btn-phone {
- display: flex;
- align-items: center;
- justify-content: center;
- gap: 16rpx;
- padding: 28rpx;
- background: rgba(255, 255, 255, 0.05);
- color: #ffffff;
- font-size: 30rpx;
- border-radius: 24rpx;
- border: 2rpx solid rgba(255, 255, 255, 0.1);
-}
-
-.btn-phone::after {
- border: none;
-}
-
-.btn-phone-icon {
- font-size: 32rpx;
-}
-
-.login-agree-row {
- display: flex;
- flex-wrap: wrap;
- align-items: center;
- justify-content: center;
- margin-top: 32rpx;
- font-size: 22rpx;
- color: rgba(255, 255, 255, 0.5);
-}
-.agree-checkbox {
- width: 32rpx;
- height: 32rpx;
- border: 2rpx solid rgba(255, 255, 255, 0.5);
- border-radius: 6rpx;
- margin-right: 12rpx;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 22rpx;
- color: #fff;
- flex-shrink: 0;
-}
-.agree-checked {
- background: #00CED1;
- border-color: #00CED1;
-}
-.agree-text { color: rgba(255, 255, 255, 0.6); }
-.agree-link {
- color: #00CED1;
- text-decoration: underline;
- padding: 0 4rpx;
-}
-.btn-wechat-disabled { opacity: 0.6; }
-
-/* ===== 支付中加载 ===== */
-.loading-box {
- background: rgba(0, 0, 0, 0.8);
- border-radius: 24rpx;
- padding: 48rpx 64rpx;
- display: flex;
- flex-direction: column;
- align-items: center;
- gap: 24rpx;
-}
-
-.loading-spinner {
- width: 64rpx;
- height: 64rpx;
- border: 4rpx solid rgba(255, 255, 255, 0.2);
- border-top-color: #00CED1;
- border-radius: 50%;
- animation: spin 0.8s linear infinite;
-}
-
-@keyframes spin {
- to { transform: rotate(360deg); }
-}
-
-.loading-text {
- font-size: 28rpx;
- color: rgba(255, 255, 255, 0.8);
-}
-
-/* ===== 海报弹窗 ===== */
-.poster-modal {
- padding-bottom: calc(64rpx + env(safe-area-inset-bottom));
-}
-
-.poster-preview {
- display: flex;
- justify-content: center;
- margin: 32rpx 0;
- padding: 24rpx;
- background: rgba(0, 0, 0, 0.3);
- border-radius: 24rpx;
-}
-
-.poster-canvas {
- border-radius: 16rpx;
- box-shadow: 0 16rpx 48rpx rgba(0, 0, 0, 0.5);
-}
-
-.poster-actions {
- display: flex;
- gap: 24rpx;
- margin-bottom: 24rpx;
-}
-
-.poster-btn {
- flex: 1;
- display: flex;
- align-items: center;
- justify-content: center;
- gap: 12rpx;
- padding: 28rpx;
- border-radius: 24rpx;
- font-size: 30rpx;
- font-weight: 500;
-}
-
-.btn-save {
- background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%);
- color: #ffffff;
-}
-
-.btn-icon {
- font-size: 32rpx;
-}
-
-.poster-tip {
- font-size: 24rpx;
- color: rgba(255, 255, 255, 0.4);
- text-align: center;
- display: block;
-}
-
-/* ===== 右下角悬浮分享按钮 ===== */
-.fab-share {
- position: fixed;
- right: 32rpx;
- width:70rpx!important;
- bottom: calc(120rpx + env(safe-area-inset-bottom));
- height: 70rpx;
- border-radius: 60rpx;
- background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%);
- box-shadow: 0 8rpx 32rpx rgba(0, 206, 209, 0.4);
- padding: 0;
- margin: 0;
- border: none;
- z-index: 9999;
- display:flex;
- align-items: center;
- justify-content: center;
- transition: transform 0.2s ease, box-shadow 0.2s ease;
-}
-
-.fab-share::after {
- border: none;
-}
-
-.fab-share:active {
- transform: scale(0.95);
- box-shadow: 0 4rpx 20rpx rgba(0, 206, 209, 0.5);
-}
-
-.fab-icon {
- padding:16rpx;
- width: 50rpx;
- height: 50rpx;
- display: block;
-}
-
diff --git a/归档/miniprogram/pages/referral/earnings-detail-styles.wxss b/归档/miniprogram/pages/referral/earnings-detail-styles.wxss
deleted file mode 100644
index a64f5b59..00000000
--- a/归档/miniprogram/pages/referral/earnings-detail-styles.wxss
+++ /dev/null
@@ -1,182 +0,0 @@
-/* ===================================
- 收益明细卡片样式 - 重构版
- 创建时间:2026-02-04
- 说明:修复布局错乱问题,优化文本显示
- =================================== */
-
-/* 收益明细卡片容器 */
-.earnings-detail-card {
- background: rgba(28, 28, 30, 0.8);
- backdrop-filter: blur(40rpx);
- border: 2rpx solid rgba(255,255,255,0.1);
- border-radius: 32rpx;
- overflow: hidden;
- margin-bottom: 24rpx;
- width: 100%;
- box-sizing: border-box;
-}
-
-/* 卡片头部 */
-.detail-header {
- padding: 40rpx 40rpx 24rpx;
- border-bottom: 2rpx solid rgba(255,255,255,0.05);
-}
-
-.detail-title {
- font-size: 30rpx;
- font-weight: 600;
- color: #fff;
-}
-
-/* 列表容器 */
-.detail-list {
- max-height: 480rpx;
- overflow-y: auto;
- padding: 16rpx 0;
-}
-
-/* ===================================
- 收益明细列表项 - 核心样式
- =================================== */
-
-/* 列表项容器 - 使用flex布局 */
-.earnings-detail-card .detail-item {
- display: flex;
- align-items: center;
- gap: 24rpx;
- padding: 24rpx 40rpx;
- background: transparent;
- border-bottom: 2rpx solid rgba(255,255,255,0.03);
- transition: background 0.3s;
-}
-
-.earnings-detail-card .detail-item:last-child {
- border-bottom: none;
-}
-
-.earnings-detail-card .detail-item:active {
- background: rgba(255, 255, 255, 0.05);
-}
-
-/* 头像容器 - 固定宽度,不收缩 */
-.earnings-detail-card .detail-avatar-wrap {
- width: 88rpx;
- height: 88rpx;
- flex-shrink: 0;
-}
-
-.earnings-detail-card .detail-avatar {
- width: 100%;
- height: 100%;
- border-radius: 50%;
- border: 2rpx solid rgba(56, 189, 172, 0.2);
- display: block;
-}
-
-.earnings-detail-card .detail-avatar-text {
- width: 100%;
- height: 100%;
- border-radius: 50%;
- background: linear-gradient(135deg, #38bdac 0%, #2da396 100%);
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 36rpx;
- font-weight: 700;
- color: #ffffff;
-}
-
-/* 详细信息容器 - 占据剩余空间,允许收缩 */
-.earnings-detail-card .detail-content {
- flex: 1;
- min-width: 0; /* 关键:允许flex子元素收缩到比内容更小 */
- display: flex;
- flex-direction: column;
- gap: 8rpx;
-}
-
-/* 顶部行:昵称 + 金额 */
-.earnings-detail-card .detail-top {
- display: flex;
- align-items: center;
- justify-content: space-between;
- gap: 16rpx;
-}
-
-/* 买家昵称 - 允许收缩,显示省略号 */
-.earnings-detail-card .detail-buyer {
- font-size: 28rpx;
- font-weight: 500;
- color: #ffffff;
- flex: 1;
- min-width: 0; /* 关键:允许收缩 */
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
-}
-
-/* 佣金金额 - 固定宽度,不收缩 */
-.earnings-detail-card .detail-amount {
- font-size: 32rpx;
- font-weight: 700;
- color: #38bdac;
- flex-shrink: 0;
- white-space: nowrap;
-}
-
-/* 商品信息行:书名 + 章节 */
-.earnings-detail-card .detail-product {
- display: flex;
- align-items: baseline;
- gap: 4rpx;
- font-size: 24rpx;
- color: rgba(255, 255, 255, 0.6);
- min-width: 0; /* 关键:允许收缩 */
- overflow: hidden;
-}
-
-/* 书名 - 限制最大宽度,显示省略号 */
-.earnings-detail-card .detail-book {
- color: rgba(255, 255, 255, 0.7);
- font-weight: 500;
- max-width: 50%; /* 限制书名最多占一半宽度 */
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- flex-shrink: 1;
-}
-
-/* 章节 - 占据剩余空间,显示省略号 */
-.earnings-detail-card .detail-chapter {
- color: rgba(255, 255, 255, 0.5);
- flex: 1;
- min-width: 0; /* 关键:允许收缩 */
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
-}
-
-/* 时间信息 */
-.earnings-detail-card .detail-time {
- font-size: 22rpx;
- color: rgba(255, 255, 255, 0.4);
-}
-
-/* ===================================
- 响应式优化
- =================================== */
-
-/* 小屏幕优化 */
-@media (max-width: 375px) {
- .earnings-detail-card .detail-buyer {
- font-size: 26rpx;
- }
-
- .earnings-detail-card .detail-amount {
- font-size: 30rpx;
- }
-
- .earnings-detail-card .detail-book {
- max-width: 45%;
- }
-}
diff --git a/归档/miniprogram/pages/referral/referral.js b/归档/miniprogram/pages/referral/referral.js
deleted file mode 100644
index b56fdc74..00000000
--- a/归档/miniprogram/pages/referral/referral.js
+++ /dev/null
@@ -1,939 +0,0 @@
-/**
- * Soul创业派对 - 分销中心页
- *
- * 可见数据:
- * - 绑定用户数(当前有效绑定)
- * - 通过链接进的人数(总访问量)
- * - 带来的付款人数(已转化购买)
- * - 收益统计(90%归分发者)
- */
-const app = getApp()
-
-Page({
- data: {
- statusBarHeight: 44,
- isLoggedIn: false,
- userInfo: null,
-
- // === 核心可见数据 ===
- bindingCount: 0, // 绑定用户数(当前有效)
- visitCount: 0, // 通过链接进的人数
- paidCount: 0, // 带来的付款人数
- unboughtCount: 0, // 待购买人数(绑定但未付款)
- expiredCount: 0, // 已过期人数
-
- // === 收益数据 ===
- totalCommission: 0, // 累计佣金总额(所有获得的佣金)
- availableEarnings: 0, // 可提现金额(未申请提现的佣金)- 字符串格式用于显示
- availableEarningsNum: 0, // 可提现金额 - 数字格式用于判断
- pendingWithdrawAmount: 0, // 待审核金额(已申请提现但未审核)
- withdrawnEarnings: 0, // 已提现金额
- earnings: 0, // 已结算收益(保留兼容)
- pendingEarnings: 0, // 待结算收益(保留兼容)
- shareRate: 90, // 分成比例(90%),从 referral/data 或 config 获取
- minWithdrawAmount: 10, // 最低提现金额,从 referral/data 获取
- bindingDays: 30, // 绑定期天数,从 referral/data 获取
- userDiscount: 5, // 好友购买优惠%,从 referral/data 获取
- hasWechatId: false, // 是否已绑定微信号(未绑定时需引导去设置)
-
- // === 统计数据 ===
- referralCount: 0, // 总推荐人数
- expiringCount: 0, // 即将过期人数
-
- // 邀请码
- referralCode: '',
-
- // 绑定用户列表
- showBindingList: true,
- activeTab: 'active',
- activeBindings: [],
- convertedBindings: [],
- expiredBindings: [],
- currentBindings: [],
- totalBindings: 0,
-
- // 收益明细
- earningsDetails: [],
-
- // 海报
- showPosterModal: false,
- isGeneratingPoster: false,
- posterQrSrc: '',
- posterReferralLink: '',
- posterNickname: '',
- posterNicknameInitial: '',
- posterCaseCount: 62,
-
- },
-
- onLoad() {
- this.setData({ statusBarHeight: app.globalData.statusBarHeight })
- this.initData()
- },
-
- onShow() {
- // 从设置页返回时同步微信号绑定状态,便于提现按钮立即更新
- const hasWechatId = !!(app.globalData.userInfo?.wechat || app.globalData.userInfo?.wechatId || wx.getStorageSync('user_wechat'))
- this.setData({ hasWechatId })
- this.initData()
- },
-
- // 初始化数据
- async initData() {
- const { isLoggedIn, userInfo } = app.globalData
- if (isLoggedIn && userInfo) {
- // 显示加载提示
- wx.showLoading({
- title: '加载中...',
- mask: true // 防止触摸穿透
- })
-
- // 生成邀请码
- const referralCode = userInfo.referralCode || 'SOUL' + (userInfo.id || Date.now().toString(36)).toUpperCase().slice(-6)
-
- console.log('[Referral] 开始加载分销数据,userId:', userInfo.id)
-
- // 从API获取真实数据
- let realData = null
- try {
- // app.request 第一个参数是 URL 字符串(会自动拼接 baseUrl)
- const res = await app.request('/api/miniprogram/referral/data?userId=' + userInfo.id)
- console.log('[Referral] API返回:', JSON.stringify(res).substring(0, 200))
-
- if (res && res.success && res.data) {
- realData = res.data
- console.log('[Referral] ✅ 获取推广数据成功')
- console.log('[Referral] - bindingCount:', realData.bindingCount)
- console.log('[Referral] - paidCount:', realData.paidCount)
- console.log('[Referral] - earnings:', realData.earnings)
- console.log('[Referral] - expiringCount:', realData.stats?.expiringCount)
- } else {
- console.log('[Referral] ❌ API返回格式错误:', res?.error || 'unknown')
- }
- } catch (e) {
- console.log('[Referral] ❌ API调用失败:', e.message || e)
- console.log('[Referral] 错误详情:', e)
- }
-
- // 使用真实数据或默认值
- let activeBindings = realData?.activeUsers || []
- let convertedBindings = realData?.convertedUsers || []
- let expiredBindings = realData?.expiredUsers || []
-
- console.log('[Referral] activeBindings:', activeBindings.length)
- console.log('[Referral] convertedBindings:', convertedBindings.length)
- console.log('[Referral] expiredBindings:', expiredBindings.length)
-
- // 计算即将过期的数量(7天内)
- const expiringCount = realData?.stats?.expiringCount || activeBindings.filter(b => b.daysRemaining <= 7 && b.daysRemaining > 0).length
-
- console.log('[Referral] expiringCount:', expiringCount)
-
- // 计算各类统计
- const bindingCount = realData?.bindingCount || activeBindings.length
- const paidCount = realData?.paidCount || convertedBindings.length
- const expiredCount = realData?.expiredCount || expiredBindings.length
- const unboughtCount = bindingCount - paidCount // 绑定中但未付款的
-
- // 格式化用户数据
- const formatUser = (user, type) => {
- const formatted = {
- id: user.id,
- nickname: user.nickname || '用户' + (user.id || '').slice(-4),
- avatar: user.avatar,
- status: type,
- daysRemaining: user.daysRemaining || 0,
- bindingDate: user.bindingDate ? this.formatDate(user.bindingDate) : '--',
- expiryDate: user.expiryDate ? this.formatDate(user.expiryDate) : '--',
- commission: (user.commission || 0).toFixed(2),
- orderAmount: (user.orderAmount || 0).toFixed(2),
- purchaseCount: user.purchaseCount || 0,
- conversionDate: user.conversionDate ? this.formatDate(user.conversionDate) : '--'
- }
- console.log('[Referral] 格式化用户:', formatted.nickname, formatted.status, formatted.daysRemaining + '天')
- return formatted
- }
-
- // 格式化金额(保留两位小数)
- const formatMoney = (num) => {
- return typeof num === 'number' ? num.toFixed(2) : '0.00'
- }
-
- // ✅ 可提现金额 = 累计佣金 - 已提现金额 - 待审核金额,且不低于 0(防止数据不同步时出现负数)
- const totalCommissionNum = realData?.totalCommission || 0
- const withdrawnNum = realData?.withdrawnEarnings || 0
- const pendingWithdrawNum = realData?.pendingWithdrawAmount || 0
- const availableEarningsNum = Math.max(0, totalCommissionNum - withdrawnNum - pendingWithdrawNum)
- const minWithdrawAmount = realData?.minWithdrawAmount || 10
-
- console.log('=== [Referral] 收益计算(完整版)===')
- console.log('累计佣金 (totalCommission):', totalCommissionNum)
- console.log('已提现金额 (withdrawnEarnings):', withdrawnNum)
- console.log('待审核金额 (pendingWithdrawAmount):', pendingWithdrawNum)
- console.log('可提现金额 = 累计 - 已提现 - 待审核 =', totalCommissionNum, '-', withdrawnNum, '-', pendingWithdrawNum, '=', availableEarningsNum)
- console.log('最低提现金额 (minWithdrawAmount):', minWithdrawAmount)
- console.log('按钮判断:', availableEarningsNum, '>=', minWithdrawAmount, '=', availableEarningsNum >= minWithdrawAmount)
- console.log('✅ 按钮应该:', availableEarningsNum >= minWithdrawAmount ? '🟢 启用(绿色)' : '⚫ 禁用(灰色)')
-
- const hasWechatId = !!(userInfo?.wechat || userInfo?.wechatId || wx.getStorageSync('user_wechat'))
- this.setData({
- isLoggedIn: true,
- userInfo,
- hasWechatId,
-
- // 核心可见数据
- bindingCount,
- visitCount: realData?.visitCount || 0,
- paidCount,
- unboughtCount: expiringCount, // "即将过期"显示的是 expiringCount
- expiredCount,
-
- // 收益数据 - 格式化为两位小数
- totalCommission: formatMoney(totalCommissionNum),
- availableEarnings: formatMoney(availableEarningsNum), // ✅ 使用计算后的可提现金额
- availableEarningsNum: availableEarningsNum, // ✅ 数字格式用于按钮判断
- pendingWithdrawAmount: formatMoney(pendingWithdrawNum),
- withdrawnEarnings: formatMoney(realData?.withdrawnEarnings || 0),
- earnings: formatMoney(realData?.earnings || 0),
- pendingEarnings: formatMoney(realData?.pendingEarnings || 0),
- shareRate: realData?.shareRate ?? 90,
- minWithdrawAmount: minWithdrawAmount,
- bindingDays: realData?.bindingDays ?? 30,
- userDiscount: realData?.userDiscount ?? 5,
-
- // 统计
- referralCount: realData?.referralCount || realData?.stats?.totalBindings || activeBindings.length + convertedBindings.length,
- expiringCount,
-
- referralCode,
- activeBindings: activeBindings.map(u => formatUser(u, 'active')),
- convertedBindings: convertedBindings.map(u => formatUser(u, 'converted')),
- expiredBindings: expiredBindings.map(u => formatUser(u, 'expired')),
- currentBindings: activeBindings.map(u => formatUser(u, 'active')),
- totalBindings: activeBindings.length + convertedBindings.length + expiredBindings.length,
-
- // 收益明细
- earningsDetails: (realData?.earningsDetails || []).map(item => {
- // 解析商品描述,获取书名和章节
- const productInfo = this.parseProductDescription(item.description, item.productType)
-
- return {
- id: item.id,
- productType: item.productType,
- bookTitle: productInfo.bookTitle,
- chapterTitle: productInfo.chapterTitle,
- commission: (item.commission || 0).toFixed(2),
- payTime: item.payTime ? this.formatDate(item.payTime) : '--',
- buyerNickname: item.buyerNickname || '用户',
- buyerAvatar: item.buyerAvatar
- }
- })
- })
-
-
- console.log('[Referral] ✅ 数据设置完成')
- console.log('[Referral] - 绑定中:', this.data.bindingCount)
- console.log('[Referral] - 即将过期:', this.data.expiringCount)
- console.log('[Referral] - 收益:', this.data.earnings)
-
- console.log('=== [Referral] 按钮状态验证 ===')
- console.log('累计佣金 (totalCommission):', this.data.totalCommission)
- console.log('待审核金额 (pendingWithdrawAmount):', this.data.pendingWithdrawAmount)
- console.log('可提现金额 (availableEarnings 显示):', this.data.availableEarnings)
- console.log('可提现金额 (availableEarningsNum 判断):', this.data.availableEarningsNum, typeof this.data.availableEarningsNum)
- console.log('最低提现金额 (minWithdrawAmount):', this.data.minWithdrawAmount, typeof this.data.minWithdrawAmount)
- console.log('按钮启用条件:', this.data.availableEarningsNum, '>=', this.data.minWithdrawAmount, '=', this.data.availableEarningsNum >= this.data.minWithdrawAmount)
- console.log('✅ 最终结果: 按钮应该', this.data.availableEarningsNum >= this.data.minWithdrawAmount ? '🟢 启用' : '⚫ 禁用')
-
- // 隐藏加载提示
- wx.hideLoading()
- } else {
- // 未登录时也隐藏loading
- this.setData({ isLoading: false })
- }
- },
-
- // 切换Tab
- switchTab(e) {
- const tab = e.currentTarget.dataset.tab
- let currentBindings = []
-
- if (tab === 'active') {
- currentBindings = this.data.activeBindings
- } else if (tab === 'converted') {
- currentBindings = this.data.convertedBindings
- } else {
- currentBindings = this.data.expiredBindings
- }
-
- this.setData({ activeTab: tab, currentBindings })
- },
-
- // 切换绑定列表显示
- toggleBindingList() {
- this.setData({ showBindingList: !this.data.showBindingList })
- },
-
- // 复制邀请链接
- copyLink() {
- const link = `https://soul.quwanzhi.com/?ref=${this.data.referralCode}`
- wx.setClipboardData({
- data: link,
- success: () => wx.showToast({ title: '链接已复制', icon: 'success' })
- })
- },
-
- // 分享到朋友圈 - 1:1 迁移 Next.js 的 handleShareToWechat
- shareToWechat() {
- const { referralCode } = this.data
- const referralLink = `https://soul.quwanzhi.com/?ref=${referralCode}`
-
- // 与 Next.js 完全相同的文案
- const shareText = `📖 推荐一本好书《一场SOUL的创业实验场》
-
-这是卡若每天早上6-9点在Soul派对房分享的真实商业故事,55个真实案例,讲透创业的底层逻辑。
-
-👉 点击阅读: ${referralLink}
-
-#创业 #商业思维 #Soul派对`
-
- wx.setClipboardData({
- data: shareText,
- success: () => {
- wx.showModal({
- title: '朋友圈文案已复制!',
- content: '打开微信 → 发朋友圈 → 粘贴即可',
- showCancel: false,
- confirmText: '知道了'
- })
- }
- })
- },
-
- // 更多分享方式 - 1:1 迁移 Next.js 的 handleShare
- handleMoreShare() {
- const { referralCode } = this.data
- const referralLink = `https://soul.quwanzhi.com/?ref=${referralCode}`
-
- // 与 Next.js 完全相同的文案
- const shareText = `我正在读《一场SOUL的创业实验场》,每天6-9点的真实商业故事,推荐给你!${referralLink}`
-
- wx.setClipboardData({
- data: shareText,
- success: () => {
- wx.showToast({
- title: '分享文案已复制',
- icon: 'success',
- duration: 2000
- })
- }
- })
- },
-
- // 生成推广海报 - 1:1 对齐 Next.js 设计
- async generatePoster() {
- wx.showLoading({ title: '生成中...', mask: true })
- this.setData({ showPosterModal: true, isGeneratingPoster: true })
-
- try {
- const { referralCode, userInfo } = this.data
- const nickname = userInfo?.nickname || '用户'
- const scene = `ref=${referralCode}`
-
- console.log('[Poster] 请求小程序码, scene:', scene)
-
- // 调用后端接口生成「小程序码」(官方 getwxacodeunlimit),不再使用 H5 二维码
- const res = await app.request('/api/miniprogram/qrcode', {
- method: 'POST',
- data: {
- scene, // ref=XXXX
- page: 'pages/index/index',
- width: 280,
- },
- })
-
- if (!res || !res.success || !res.image) {
- console.error('[Poster] 生成小程序码失败:', res)
- throw new Error(res?.error || '生成小程序码失败')
- }
-
- // 后端返回的是 data:image/png;base64,... 需要先写入本地临时文件,再作为 的 src
- const base64Data = String(res.image).replace(/^data:image\/\w+;base64,/, '')
- const fs = wx.getFileSystemManager()
- const filePath = `${wx.env.USER_DATA_PATH}/poster_qrcode_${Date.now()}.png`
-
- await new Promise((resolve, reject) => {
- fs.writeFile({
- filePath,
- data: base64Data,
- encoding: 'base64',
- success: () => resolve(true),
- fail: (err) => {
- console.error('[Poster] 小程序码写入本地失败:', err)
- reject(err)
- },
- })
- })
-
- console.log('[Poster] 小程序码已保存到本地:', filePath)
-
- this.setData({
- posterQrSrc: filePath,
- posterReferralLink: '', // 小程序版本不再使用 H5 链接
- posterNickname: nickname,
- posterNicknameInitial: (nickname || '用').charAt(0),
- isGeneratingPoster: false
- })
- wx.hideLoading()
- } catch (e) {
- console.error('[Poster] 生成二维码失败:', e)
- wx.hideLoading()
- wx.showToast({ title: '生成失败', icon: 'none' })
- this.setData({ showPosterModal: false, isGeneratingPoster: false, posterQrSrc: '', posterReferralLink: '' })
- }
- },
-
- // 绘制数据卡片
- drawDataCard(ctx, x, y, width, height, value, label, color) {
- // 卡片背景
- ctx.setFillStyle('rgba(255,255,255,0.05)')
- this.drawRoundRect(ctx, x, y, width, height, 8)
- ctx.setStrokeStyle('rgba(255,255,255,0.1)')
- ctx.setLineWidth(1)
- ctx.stroke()
-
- // 数值
- ctx.setFillStyle(color)
- ctx.setFontSize(24)
- ctx.setTextAlign('center')
- ctx.fillText(value, x + width / 2, y + 24)
-
- // 标签
- ctx.setFillStyle('rgba(255,255,255,0.5)')
- ctx.setFontSize(10)
- ctx.fillText(label, x + width / 2, y + 40)
- },
-
- // 绘制圆角矩形
- drawRoundRect(ctx, x, y, width, height, radius) {
- ctx.beginPath()
- ctx.moveTo(x + radius, y)
- ctx.lineTo(x + width - radius, y)
- ctx.arc(x + width - radius, y + radius, radius, -Math.PI / 2, 0)
- ctx.lineTo(x + width, y + height - radius)
- ctx.arc(x + width - radius, y + height - radius, radius, 0, Math.PI / 2)
- ctx.lineTo(x + radius, y + height)
- ctx.arc(x + radius, y + height - radius, radius, Math.PI / 2, Math.PI)
- ctx.lineTo(x, y + radius)
- ctx.arc(x + radius, y + radius, radius, Math.PI, Math.PI * 1.5)
- ctx.closePath()
- ctx.fill()
- },
-
- // 光晕(替代 createRadialGradient):用同心圆叠加模拟模糊
- // centerX/centerY: 圆心坐标;radius: 最大半径;rgb: [r,g,b];maxAlpha: 最内层透明度
- drawGlow(ctx, centerX, centerY, radius, rgb, maxAlpha = 0.10) {
- const steps = 14
- for (let i = steps; i >= 1; i--) {
- const r = (radius * i) / steps
- const alpha = (maxAlpha * i) / steps
- ctx.setFillStyle(`rgba(${rgb[0]},${rgb[1]},${rgb[2]},${alpha})`)
- ctx.beginPath()
- ctx.arc(centerX, centerY, r, 0, Math.PI * 2)
- ctx.fill()
- }
- },
-
- // 绘制二维码(支持Base64和URL两种格式)
- async drawQRCode(ctx, qrcodeImage, x, y, size) {
- return new Promise((resolve) => {
- if (!qrcodeImage) {
- console.log('[Poster] 无二维码数据,绘制占位符')
- this.drawQRPlaceholder(ctx, x, y, size)
- resolve()
- return
- }
-
- // 判断是Base64还是URL
- if (qrcodeImage.startsWith('data:image') || !qrcodeImage.startsWith('http')) {
- // Base64格式(小程序码)
- console.log('[Poster] 绘制Base64二维码')
- const fs = wx.getFileSystemManager()
- const filePath = `${wx.env.USER_DATA_PATH}/qrcode_promo_${Date.now()}.png`
- const base64Data = qrcodeImage.replace(/^data:image\/\w+;base64,/, '')
-
- fs.writeFile({
- filePath,
- data: base64Data,
- encoding: 'base64',
- success: () => {
- console.log('[Poster] ✅ Base64写入成功')
- ctx.drawImage(filePath, x, y, size, size)
- resolve()
- },
- fail: (err) => {
- console.error('[Poster] ❌ Base64写入失败:', err)
- this.drawQRPlaceholder(ctx, x, y, size)
- resolve()
- }
- })
- } else {
- // URL格式(第三方二维码)
- console.log('[Poster] 下载在线二维码:', qrcodeImage)
- wx.downloadFile({
- url: qrcodeImage,
- success: (res) => {
- if (res.statusCode === 200) {
- console.log('[Poster] ✅ 二维码下载成功')
- ctx.drawImage(res.tempFilePath, x, y, size, size)
- resolve()
- } else {
- console.error('[Poster] ❌ 二维码下载失败, status:', res.statusCode)
- this.drawQRPlaceholder(ctx, x, y, size)
- resolve()
- }
- },
- fail: (err) => {
- console.error('[Poster] ❌ 二维码下载失败:', err)
- this.drawQRPlaceholder(ctx, x, y, size)
- resolve()
- }
- })
- }
- })
- },
-
- // 绘制小程序码占位符
- drawQRPlaceholder(ctx, x, y, size) {
- // 绘制占位符方框
- ctx.setFillStyle('rgba(200,200,200,0.3)')
- this.drawRoundRect(ctx, x, y, size, size, 8)
-
- ctx.setFillStyle('#00CED1')
- ctx.setFontSize(11)
- ctx.setTextAlign('center')
- ctx.fillText('小程序码', x + size / 2, y + size / 2)
- },
-
- // 关闭海报弹窗
- closePosterModal() {
- this.setData({ showPosterModal: false })
- },
-
- // 保存海报
- savePoster() {
- const { posterQrSrc } = this.data
- if (!posterQrSrc) {
- wx.showToast({ title: '二维码未生成', icon: 'none' })
- return
- }
-
- wx.showLoading({ title: '保存中...', mask: true })
- wx.downloadFile({
- url: posterQrSrc,
- success: (res) => {
- if (res.statusCode !== 200) {
- wx.hideLoading()
- wx.showToast({ title: '下载失败', icon: 'none' })
- return
- }
-
- wx.saveImageToPhotosAlbum({
- filePath: res.tempFilePath,
- success: () => {
- wx.hideLoading()
- wx.showToast({ title: '已保存到相册', icon: 'success' })
- },
- fail: (err) => {
- wx.hideLoading()
- if (String(err.errMsg || '').includes('auth deny')) {
- wx.showModal({
- title: '提示',
- content: '需要相册权限才能保存二维码',
- confirmText: '去设置',
- success: (r) => {
- if (r.confirm) wx.openSetting()
- }
- })
- return
- }
- wx.showToast({ title: '保存失败', icon: 'none' })
- }
- })
- },
- fail: () => {
- wx.hideLoading()
- wx.showToast({ title: '下载失败', icon: 'none' })
- }
- })
- },
-
- // 预览二维码
- previewPosterQr() {
- const { posterQrSrc } = this.data
- if (!posterQrSrc) return
- wx.previewImage({ urls: [posterQrSrc] })
- },
-
- // 阻止冒泡
- stopPropagation() {},
-
- // 分享到朋友圈 - 随机文案
- shareToMoments() {
- // 10条随机文案,基于书的内容
- const shareTexts = [
- `🔥 在派对房里听到的真实故事,比虚构的小说精彩100倍!\n\n电动车出租月入5万、私域一年赚1000万、一个人的公司月入10万...\n\n62个真实案例,搜"Soul创业派对"小程序看全部!\n\n#创业 #私域 #商业`,
-
- `💡 今天终于明白:会赚钱的人,都在用"流量杠杆"\n\n抖音、Soul、飞书...同一套内容,撬动不同平台的流量。\n\n《Soul创业派对》里的实战方法,受用终身!\n\n#流量 #副业 #创业派对`,
-
- `📚 一个70后大健康私域,一个月150万流水是怎么做到的?\n\n答案在《Soul创业派对》第9章,全是干货。\n\n搜小程序"Soul创业派对",我在里面等你\n\n#大健康 #私域运营 #真实案例`,
-
- `🎯 "分钱不是分你的钱,是分不属于对方的钱"\n\n这句话改变了我对商业合作的认知。\n\n推荐《Soul创业派对》,创业者必读!\n\n#云阿米巴 #商业思维 #创业`,
-
- `✨ 资源整合高手的社交方法论,在派对房里学到了\n\n"先让对方赚到钱,自己才能长久赚钱"\n\n这本《Soul创业派对》,每章都是实战经验\n\n#资源整合 #社交 #创业故事`,
-
- `🚀 AI工具推广:一个隐藏的高利润赛道\n\n客单价高、复购率高、需求旺盛...\n\n《Soul创业派对》里的商业机会,你发现了吗?\n\n#AI #副业 #商业机会`,
-
- `💰 美业整合:一个人的公司如何月入十万?\n\n不开店、不囤货、轻资产运营...\n\n《Soul创业派对》告诉你答案!\n\n#美业 #轻创业 #月入十万`,
-
- `🌟 3000万流水是怎么跑出来的?\n\n不是靠运气,是靠系统。\n\n《Soul创业派对》里的电商底层逻辑,值得反复看\n\n#电商 #创业 #商业系统`,
-
- `📖 "人与人之间的关系,归根结底就三个东西:利益、情感、价值观"\n\n在派对房里聊出的金句,都在《Soul创业派对》里\n\n#人性 #商业 #创业派对`,
-
- `🔔 未来职业的三个方向:技术型、资源型、服务型\n\n你属于哪一种?\n\n《Soul创业派对》帮你找到答案!\n\n#职业规划 #创业 #未来`
- ]
-
- // 随机选择一条文案
- const randomIndex = Math.floor(Math.random() * shareTexts.length)
- const shareText = shareTexts[randomIndex]
-
- wx.setClipboardData({
- data: shareText,
- success: () => {
- wx.showModal({
- title: '文案已复制',
- content: '请打开微信朋友圈,粘贴分享文案,配合推广海报一起发布效果更佳!\n\n再次点击可获取新的随机文案',
- showCancel: false,
- confirmText: '去发朋友圈'
- })
- }
- })
- },
-
- // 提现 - 直接到微信零钱
- async handleWithdraw() {
- const availableEarnings = this.data.availableEarningsNum || 0
- const minWithdrawAmount = this.data.minWithdrawAmount || 10
- const hasWechatId = this.data.hasWechatId
-
- if (availableEarnings <= 0) {
- wx.showToast({ title: '暂无可提现收益', icon: 'none' })
- return
- }
- // 任意金额可提现,不再设最低限额
-
- // 未绑定微信号时引导去设置
- if (!hasWechatId) {
- wx.showModal({
- title: '请先绑定微信号',
- content: '提现需先绑定微信号,便于到账核对。请到「设置」中绑定后再提现。',
- confirmText: '去绑定',
- cancelText: '取消',
- success: (res) => {
- if (res.confirm) wx.navigateTo({ url: '/pages/settings/settings' })
- }
- })
- return
- }
-
- wx.showModal({
- title: '确认提现',
- content: `将提现 ¥${availableEarnings.toFixed(2)} 到您的微信零钱`,
- confirmText: '立即提现',
- success: async (res) => {
- if (!res.confirm) return
- const tmplId = app.globalData.withdrawSubscribeTmplId
- if (tmplId && tmplId.length > 10) {
- wx.requestSubscribeMessage({
- tmplIds: [tmplId],
- success: () => { this.doWithdraw(availableEarnings) },
- fail: () => { this.doWithdraw(availableEarnings) }
- })
- } else {
- await this.doWithdraw(availableEarnings)
- }
- }
- })
- },
-
- // 跳转提现记录页
- goToWithdrawRecords() {
- wx.navigateTo({ url: '/pages/withdraw-records/withdraw-records' })
- },
-
- // 执行提现
- async doWithdraw(amount) {
- wx.showLoading({ title: '提现中...' })
-
- try {
- const userId = app.globalData.userInfo?.id
- if (!userId) {
- wx.hideLoading()
- wx.showToast({ title: '请先登录', icon: 'none' })
- return
- }
-
- const res = await app.request('/api/miniprogram/withdraw', {
- method: 'POST',
- data: { userId, amount }
- })
-
- wx.hideLoading()
-
- if (res.success) {
- wx.showModal({
- title: '提现申请已提交 ✅',
- content: res.message || '正在审核中,通过后会自动到账您的微信零钱',
- showCancel: false,
- confirmText: '知道了'
- })
-
- // 刷新数据(此时待审核金额会增加,可提现金额会减少)
- this.initData()
- } else {
- if (res.needBind || res.needBindWechat) {
- wx.showModal({
- title: res.needBindWechat ? '请先绑定微信号' : '需要绑定微信',
- content: res.needBindWechat ? '请到「设置」中绑定微信号后再提现,便于到账核对。' : '请先在设置中绑定微信账号后再提现',
- confirmText: '去绑定',
- success: (modalRes) => {
- if (modalRes.confirm) wx.navigateTo({ url: '/pages/settings/settings' })
- }
- })
- } else {
- wx.showToast({ title: res.message || res.error || '提现失败', icon: 'none', duration: 3000 })
- }
- }
- } catch (e) {
- wx.hideLoading()
- console.error('[Referral] 提现失败:', e)
- wx.showToast({ title: '提现失败,请重试', icon: 'none' })
- }
- },
-
- // 显示通知
- showNotification() {
- wx.showToast({ title: '暂无新消息', icon: 'none' })
- },
-
- // 显示设置
- showSettings() {
- wx.showActionSheet({
- itemList: ['自动提现设置', '收益通知设置'],
- success: (res) => {
- if (res.tapIndex === 0) {
- this.showAutoWithdrawSettings()
- } else {
- this.showNotificationSettings()
- }
- }
- })
- },
-
- // 自动提现设置
- async showAutoWithdrawSettings() {
- const app = getApp()
- const { userInfo } = app.globalData
-
- if (!userInfo) {
- wx.showToast({ title: '请先登录', icon: 'none' })
- return
- }
-
- // 获取当前设置
- let autoWithdrawEnabled = wx.getStorageSync(`autoWithdraw_${userInfo.id}`) || false
- let autoWithdrawThreshold = wx.getStorageSync(`autoWithdrawThreshold_${userInfo.id}`) || this.data.minWithdrawAmount || 10
-
- wx.showModal({
- title: '自动提现设置',
- content: `当前状态:${autoWithdrawEnabled ? '已开启' : '已关闭'}\n自动提现阈值:¥${autoWithdrawThreshold}\n\n开启后,当可提现金额达到阈值时将自动发起提现申请。`,
- confirmText: autoWithdrawEnabled ? '关闭' : '开启',
- cancelText: '修改阈值',
- success: (res) => {
- if (res.confirm) {
- // 切换开关
- this.toggleAutoWithdraw(!autoWithdrawEnabled, autoWithdrawThreshold)
- } else if (res.cancel) {
- // 修改阈值
- this.setAutoWithdrawThreshold(autoWithdrawEnabled, autoWithdrawThreshold)
- }
- }
- })
- },
-
- // 切换自动提现开关
- toggleAutoWithdraw(enabled, threshold) {
- const app = getApp()
- const { userInfo } = app.globalData
-
- wx.setStorageSync(`autoWithdraw_${userInfo.id}`, enabled)
-
- wx.showToast({
- title: enabled ? '自动提现已开启' : '自动提现已关闭',
- icon: 'success'
- })
-
- // 如果开启,检查当前金额是否达到阈值
- if (enabled && this.data.availableEarningsNum >= threshold) {
- wx.showModal({
- title: '提示',
- content: `当前可提现金额¥${this.data.availableEarnings}已达到阈值¥${threshold},是否立即提现?`,
- success: (res) => {
- if (res.confirm) {
- this.handleWithdraw()
- }
- }
- })
- }
- },
-
- // 设置自动提现阈值
- setAutoWithdrawThreshold(currentEnabled, currentThreshold) {
- const minAmount = this.data.minWithdrawAmount || 10
-
- wx.showModal({
- title: '设置提现阈值',
- content: `请输入自动提现金额阈值(最低¥${minAmount})`,
- editable: true,
- placeholderText: currentThreshold.toString(),
- success: (res) => {
- if (res.confirm && res.content) {
- const threshold = parseFloat(res.content)
-
- if (isNaN(threshold) || threshold < minAmount) {
- wx.showToast({
- title: `请输入不小于¥${minAmount}的金额`,
- icon: 'none'
- })
- return
- }
-
- const app = getApp()
- const { userInfo } = app.globalData
-
- wx.setStorageSync(`autoWithdrawThreshold_${userInfo.id}`, threshold)
-
- wx.showToast({
- title: `阈值已设置为¥${threshold}`,
- icon: 'success'
- })
-
- // 重新显示设置界面
- setTimeout(() => {
- this.showAutoWithdrawSettings()
- }, 1500)
- }
- }
- })
- },
-
- // 收益通知设置
- showNotificationSettings() {
- const app = getApp()
- const { userInfo } = app.globalData
-
- if (!userInfo) {
- wx.showToast({ title: '请先登录', icon: 'none' })
- return
- }
-
- // 获取当前设置
- let notifyEnabled = wx.getStorageSync(`earningsNotify_${userInfo.id}`) !== false // 默认开启
-
- wx.showModal({
- title: '收益通知设置',
- content: `当前状态:${notifyEnabled ? '已开启' : '已关闭'}\n\n开启后,将在有新收益时收到小程序通知提醒。`,
- confirmText: notifyEnabled ? '关闭通知' : '开启通知',
- success: (res) => {
- if (res.confirm) {
- const newState = !notifyEnabled
- wx.setStorageSync(`earningsNotify_${userInfo.id}`, newState)
-
- wx.showToast({
- title: newState ? '收益通知已开启' : '收益通知已关闭',
- icon: 'success'
- })
-
- // 如果开启,请求通知权限
- if (newState) {
- wx.requestSubscribeMessage({
- tmplIds: [''] // 需要配置模板ID
- }).catch(() => {
- // 用户拒绝授权,不影响功能
- })
- }
- }
- }
- })
- },
-
- // 分享 - 带推荐码
- onShareAppMessage() {
- console.log('[Referral] 分享给好友,推荐码:', this.data.referralCode)
- return {
- title: 'Soul创业派对 - 来自派对房的真实商业故事',
- path: `/pages/index/index?ref=${this.data.referralCode}`
- // 不设置 imageUrl,使用小程序默认截图
- // 如需自定义图片,请将图片放在 /assets/ 目录并配置路径
- }
- },
-
- // 分享到朋友圈
- onShareTimeline() {
- console.log('[Referral] 分享到朋友圈,推荐码:', this.data.referralCode)
- return {
- title: `Soul创业派对 - 62个真实商业案例`,
- query: `ref=${this.data.referralCode}`
- // 不设置 imageUrl,使用小程序默认截图
- }
- },
-
- goBack() {
- wx.navigateBack()
- },
-
- // 解析商品描述,获取书名和章节
- parseProductDescription(description, productType) {
- if (!description) {
- return {
- bookTitle: '未知商品',
- chapterTitle: ''
- }
- }
-
- // 匹配格式:《书名》- 章节名
- const match = description.match(/《(.+?)》(?:\s*-\s*(.+))?/)
-
- if (match) {
- return {
- bookTitle: match[1] || '未知书籍',
- chapterTitle: match[2] || (productType === 'fullbook' ? '全书购买' : '')
- }
- }
-
- // 如果匹配失败,直接返回原始描述
- return {
- bookTitle: description.split('-')[0] || description,
- chapterTitle: description.split('-')[1] || ''
- }
- },
-
- // 格式化日期
- formatDate(dateStr) {
- if (!dateStr) return '--'
- const d = new Date(dateStr)
- const month = (d.getMonth() + 1).toString().padStart(2, '0')
- const day = d.getDate().toString().padStart(2, '0')
- return `${month}-${day}`
- }
-})
diff --git a/归档/miniprogram/pages/referral/referral.json b/归档/miniprogram/pages/referral/referral.json
deleted file mode 100644
index 64f02792..00000000
--- a/归档/miniprogram/pages/referral/referral.json
+++ /dev/null
@@ -1,6 +0,0 @@
-{
- "usingComponents": {},
- "navigationStyle": "custom",
- "enableShareAppMessage": true,
- "enableShareTimeline": true
-}
diff --git a/归档/miniprogram/pages/referral/referral.wxml b/归档/miniprogram/pages/referral/referral.wxml
deleted file mode 100644
index 38c587aa..00000000
--- a/归档/miniprogram/pages/referral/referral.wxml
+++ /dev/null
@@ -1,332 +0,0 @@
-
-
-
-
-
-
-
-
-
- 分销中心
-
-
-
-
-
-
-
-
-
-
-
- {{expiringCount}} 位用户绑定即将过期
- {{bindingDays}}天内未付款将解除绑定关系
-
-
-
-
-
-
-
-
-
- {{availableEarningsNum <= 0 ? '暂无可提现' : !hasWechatId ? '请先绑定微信号' : '申请提现 ¥' + availableEarnings}}
-
- 为便于提现到账,请先到「设置」中绑定微信号
- 查看提现记录
-
-
-
-
-
-
- {{bindingCount}}
- 绑定中
-
-
- {{paidCount}}
- 已付款
-
-
- {{unboughtCount}}
- 即将过期
-
-
- {{referralCount}}
- 总邀请
-
-
-
-
-
-
-
- • 好友通过你的链接购买,立享{{userDiscount}}%优惠
- • 好友成功付款后,你获得 {{shareRate}}% 收益
- • 绑定期{{bindingDays}}天,期满未付款自动解除
-
-
-
-
-
-
-
-
-
-
- 绑定中 ({{activeBindings.length}})
- 已付款 ({{convertedBindings.length}})
- 已过期 ({{expiredBindings.length}})
-
-
-
-
-
-
- 👤
- 暂无用户
-
-
-
-
-
- ✓
- ⏰
- {{item.nickname[0] || '用'}}
-
-
- {{item.nickname || '匿名用户'}}
- 绑定于 {{item.bindingDate}}
-
-
-
- +¥{{item.commission}}
- 已购{{item.purchaseCount || 1}}次
-
-
- 已过期
- {{item.expiryDate}}
-
-
-
- {{item.daysRemaining}}天
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 生成推广海报
- 一键生成精美海报分享
-
-
-
-
-
-
-
-
-
- 分享到朋友圈
- 复制文案发朋友圈
-
-
-
-
-
-
-
-
-
- 更多分享方式
- 使用系统分享功能
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{item.buyerNickname.charAt(0)}}
-
-
-
-
-
-
- {{item.buyerNickname}}
- +¥{{item.commission}}
-
-
- {{item.bookTitle}}
- - {{item.chapterTitle}}
-
- {{item.payTime}}
-
-
-
-
-
-
-
-
-
-
- 暂无收益记录
- 分享专属链接,好友购买即可获得 {{shareRate}}% 返利
-
-
-
-
-
-
- ✕
-
-
-
-
-
-
-
-
-
-
-
- 真实商业案例
- 每日更新
-
-
-
-
- 一场SOUL的
- 创业实验场
-
- 来自Soul派对房的真实商业故事
-
-
-
-
- {{posterCaseCount}}
- 真实案例
-
-
- {{userDiscount}}%
- 好友优惠
-
-
- {{shareRate}}%
- 你的收益
-
-
-
-
-
- 人性观察
- 行业揭秘
- 赚钱逻辑
- 创业复盘
- 资源对接
-
-
-
-
-
- {{posterNicknameInitial}}
-
- {{posterNickname}} 推荐你来读
-
-
-
-
- 通过我的链接购买,立省{{userDiscount}}%
-
-
-
-
-
-
-
- 长按识别 · 立即试读
- 邀请码: {{referralCode}}
-
-
-
-
-
-
-
-
diff --git a/归档/miniprogram/pages/referral/referral.wxss b/归档/miniprogram/pages/referral/referral.wxss
deleted file mode 100644
index b75eb535..00000000
--- a/归档/miniprogram/pages/referral/referral.wxss
+++ /dev/null
@@ -1,302 +0,0 @@
-/* ???????? - 1:1??Web?? */
-.page { min-height: 100vh; background: #000; padding-bottom: 64rpx; }
-
-/* ??? */
-.nav-bar { position: fixed; top: 0; left: 0; right: 0; z-index: 100; background: rgba(0,0,0,0.9); backdrop-filter: blur(40rpx); display: flex; align-items: center; justify-content: space-between; padding: 0 32rpx; height: 88rpx; }
-.nav-left { display: flex; gap: 16rpx; align-items: center; }
-.nav-back { width: 64rpx; height: 64rpx; background: #1c1c1e; border-radius: 50%; display: flex; align-items: center; justify-content: center; }
-.nav-icon { width: 40rpx; height: 40rpx; display: block; filter: brightness(0) saturate(100%) invert(60%) sepia(0%) saturate(0%) hue-rotate(0deg) brightness(95%) contrast(85%); }
-.nav-title { font-size: 34rpx; font-weight: 600; color: #fff; flex: 1; text-align: center; }
-.nav-right-placeholder { width: 144rpx; }
-.nav-btn { width: 64rpx; height: 64rpx; background: #1c1c1e; border-radius: 50%; display: flex; align-items: center; justify-content: center; }
-
-.content { padding: 24rpx; width: 100%; box-sizing: border-box; }
-
-/* ?????? */
-.expiring-banner { display: flex; align-items: center; gap: 24rpx; padding: 24rpx; background: rgba(255,165,0,0.1); border: 2rpx solid rgba(255,165,0,0.3); border-radius: 24rpx; margin-bottom: 24rpx; }
-.banner-icon { width: 80rpx; height: 80rpx; background: rgba(255,165,0,0.2); border-radius: 50%; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
-.icon-bell-warning { width: 40rpx; height: 40rpx; display: block; filter: brightness(0) saturate(100%) invert(64%) sepia(89%) saturate(1363%) hue-rotate(4deg) brightness(101%) contrast(102%); }
-.banner-content { flex: 1; }
-.banner-title { font-size: 28rpx; font-weight: 500; color: #fff; display: block; }
-.banner-desc { font-size: 24rpx; color: rgba(255,165,0,0.8); margin-top: 4rpx; display: block; }
-
-/* ???? - ?? Next.js */
-.earnings-card { position: relative; background: rgba(28, 28, 30, 0.8); backdrop-filter: blur(40rpx); border: 2rpx solid rgba(255,255,255,0.1); border-radius: 32rpx; padding: 48rpx; margin-bottom: 24rpx; overflow: hidden; width: 100%; box-sizing: border-box; }
-.earnings-bg { position: absolute; top: 0; right: 0; width: 256rpx; height: 256rpx; background: rgba(0,206,209,0.15); border-radius: 50%; filter: blur(100rpx); }
-.earnings-main { position: relative; }
-.earnings-header { display: flex; align-items: flex-start; justify-content: space-between; margin-bottom: 32rpx; }
-.earnings-left { display: flex; align-items: center; gap: 16rpx; }
-.wallet-icon { width: 80rpx; height: 80rpx; background: rgba(0,206,209,0.2); border-radius: 20rpx; display: flex; align-items: center; justify-content: center; }
-.icon-wallet { width: 40rpx; height: 40rpx; display: block; filter: brightness(0) saturate(100%) invert(72%) sepia(54%) saturate(2933%) hue-rotate(134deg) brightness(101%) contrast(101%); }
-.earnings-info { display: flex; flex-direction: column; gap: 8rpx; }
-.earnings-label { font-size: 24rpx; color: rgba(255,255,255,0.6); }
-.commission-rate { font-size: 24rpx; color: #00CED1; font-weight: 500; }
-.earnings-right { text-align: right; }
-.earnings-value { font-size: 60rpx; font-weight: 700; color: #fff; display: block; line-height: 1; }
-.pending-text { font-size: 24rpx; color: rgba(255,255,255,0.5); margin-top: 8rpx; display: block; }
-
-.withdraw-btn { padding: 28rpx; background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%); color: #fff; font-size: 32rpx; font-weight: 600; text-align: center; border-radius: 24rpx; box-shadow: 0 8rpx 24rpx rgba(0,206,209,0.3); }
-.withdraw-btn.btn-disabled { background: rgba(0,206,209,0.2); color: rgba(255,255,255,0.3); box-shadow: none; }
-.wechat-tip { display: block; font-size: 24rpx; color: rgba(255,165,0,0.9); margin-top: 16rpx; text-align: center; }
-.withdraw-records-link { display: block; margin-top: 16rpx; text-align: center; font-size: 26rpx; color: #00CED1; }
-
-/* ???? - ?? Next.js 4??? */
-.stats-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8rpx; margin-bottom: 24rpx; width: 100%; box-sizing: border-box; }
-.stat-card { background: rgba(28, 28, 30, 0.8); backdrop-filter: blur(40rpx); border: 2rpx solid rgba(255,255,255,0.1); border-radius: 24rpx; padding: 24rpx 12rpx; text-align: center; }
-.stat-value { font-size: 40rpx; font-weight: 700; color: #fff; display: block; }
-.stat-value.orange { color: #FFA500; }
-.stat-label { font-size: 20rpx; color: rgba(255,255,255,0.6); margin-top: 8rpx; display: block; }
-
-/* ????? */
-.visit-stat { display: flex; align-items: center; justify-content: center; gap: 12rpx; padding: 20rpx 32rpx; background: rgba(255,255,255,0.05); border-radius: 16rpx; margin-bottom: 24rpx; }
-.visit-label { font-size: 24rpx; color: rgba(255,255,255,0.5); }
-.visit-value { font-size: 32rpx; font-weight: 700; color: #00CED1; }
-.visit-tip { font-size: 24rpx; color: rgba(255,255,255,0.5); }
-
-/* ???? - ?? Next.js */
-.rules-card { background: rgba(0,206,209,0.05); border: 2rpx solid rgba(0,206,209,0.2); border-radius: 24rpx; padding: 32rpx; margin-bottom: 24rpx; width: 100%; box-sizing: border-box; }
-.rules-header { display: flex; align-items: center; gap: 16rpx; margin-bottom: 16rpx; }
-.rules-icon { width: 64rpx; height: 64rpx; background: rgba(0,206,209,0.2); border-radius: 16rpx; display: flex; align-items: center; justify-content: center; }
-.icon-alert { width: 32rpx; height: 32rpx; display: block; filter: brightness(0) saturate(100%) invert(72%) sepia(54%) saturate(2933%) hue-rotate(134deg) brightness(101%) contrast(101%); }
-.rules-title { font-size: 28rpx; font-weight: 500; color: #fff; }
-.rules-list { padding-left: 8rpx; }
-.rule-item { font-size: 24rpx; color: rgba(255,255,255,0.6); line-height: 2; display: block; margin-bottom: 4rpx; }
-.rule-item .gold { color: #FFD700; font-weight: 500; }
-.rule-item .brand { color: #00CED1; font-weight: 500; }
-.rule-item .orange { color: #FFA500; font-weight: 500; }
-
-/* ?????? - ?? Next.js */
-.binding-card { background: rgba(28, 28, 30, 0.8); backdrop-filter: blur(40rpx); border: 2rpx solid rgba(255,255,255,0.1); border-radius: 32rpx; overflow: hidden; margin-bottom: 24rpx; width: 100%; box-sizing: border-box; }
-.binding-header { display: flex; align-items: center; justify-content: space-between; padding: 28rpx 32rpx; border-bottom: 2rpx solid rgba(255,255,255,0.05); }
-.binding-title { display: flex; align-items: center; gap: 12rpx; }
-.binding-icon-img { width: 40rpx; height: 40rpx; display: block; filter: brightness(0) saturate(100%) invert(72%) sepia(54%) saturate(2933%) hue-rotate(134deg) brightness(101%) contrast(101%); }
-.title-text { font-size: 30rpx; font-weight: 600; color: #fff; }
-.binding-count { font-size: 26rpx; color: rgba(255,255,255,0.5); }
-.toggle-icon { font-size: 24rpx; color: rgba(255,255,255,0.5); }
-
-/* Tab?? */
-.binding-tabs { display: flex; border-bottom: 2rpx solid rgba(255,255,255,0.05); }
-.tab-item { flex: 1; padding: 24rpx 0; text-align: center; font-size: 26rpx; color: rgba(255,255,255,0.5); position: relative; }
-.tab-item.tab-active { color: #00CED1; }
-.tab-item.tab-active::after { content: ''; position: absolute; bottom: 0; left: 50%; transform: translateX(-50%); width: 80rpx; height: 4rpx; background: #00CED1; border-radius: 4rpx; }
-
-/* ???? */
-.binding-list { max-height: 640rpx; overflow-y: auto; }
-.empty-state { padding: 80rpx 0; text-align: center; }
-.empty-icon { font-size: 64rpx; display: block; margin-bottom: 16rpx; }
-.empty-text { font-size: 26rpx; color: rgba(255,255,255,0.5); }
-
-/* ?????? - ??? */
-.binding-item { display: flex; align-items: center; padding: 24rpx 32rpx; border-bottom: 2rpx solid rgba(255,255,255,0.05); gap: 24rpx; }
-.binding-item:last-child { border-bottom: none; }
-
-/* ?? */
-.user-avatar { width: 88rpx; height: 88rpx; border-radius: 50%; background: rgba(0,206,209,0.2); display: flex; align-items: center; justify-content: center; font-size: 32rpx; font-weight: 600; color: #00CED1; flex-shrink: 0; }
-.user-avatar.avatar-converted { background: rgba(76,175,80,0.2); color: #4CAF50; }
-.user-avatar.avatar-expired { background: rgba(158,158,158,0.2); color: #9E9E9E; }
-
-/* ???? */
-.user-info { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 4rpx; }
-.user-name { font-size: 28rpx; color: #fff; font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
-.user-time { font-size: 22rpx; color: rgba(255,255,255,0.5); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
-
-/* ???? */
-.user-status { flex-shrink: 0; text-align: right; display: flex; flex-direction: column; align-items: flex-end; gap: 4rpx; min-width: 100rpx; }
-.status-amount { font-size: 28rpx; color: #4CAF50; font-weight: 600; white-space: nowrap; }
-.status-order { font-size: 20rpx; color: rgba(255,255,255,0.5); white-space: nowrap; }
-.status-time { font-size: 20rpx; color: rgba(255,255,255,0.5); white-space: nowrap; }
-
-/* ???? */
-.status-tag { font-size: 24rpx; font-weight: 600; padding: 6rpx 16rpx; border-radius: 16rpx; white-space: nowrap; }
-.status-tag.tag-green { background: rgba(76,175,80,0.2); color: #4CAF50; }
-.status-tag.tag-orange { background: rgba(255,165,0,0.2); color: #FFA500; }
-.status-tag.tag-red { background: rgba(244,67,54,0.2); color: #F44336; }
-.status-tag.tag-gray { background: rgba(158,158,158,0.2); color: #9E9E9E; }
-
-/* ????? - ?? Next.js */
-.invite-card { background: rgba(28, 28, 30, 0.8); backdrop-filter: blur(40rpx); border: 2rpx solid rgba(255,255,255,0.1); border-radius: 32rpx; padding: 40rpx; margin-bottom: 24rpx; width: 100%; box-sizing: border-box; }
-.invite-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 24rpx; }
-.invite-title { font-size: 30rpx; font-weight: 600; color: #fff; }
-.invite-code-box { background: rgba(0,206,209,0.2); padding: 12rpx 24rpx; border-radius: 16rpx; }
-.invite-code { font-size: 28rpx; font-weight: 600; color: #00CED1; font-family: 'Courier New', monospace; letter-spacing: 2rpx; }
-.invite-tip { font-size: 24rpx; color: rgba(255,255,255,0.6); line-height: 1.5; display: block; }
-.invite-tip .gold { color: #FFD700; }
-.invite-tip .brand { color: #00CED1; }
-
-/* ?????? - ??? */
-.earnings-detail-card { background: rgba(28, 28, 30, 0.8); backdrop-filter: blur(40rpx); border: 2rpx solid rgba(255,255,255,0.1); border-radius: 32rpx; overflow: hidden; margin-bottom: 24rpx; width: 100%; box-sizing: border-box; }
-.detail-header { padding: 40rpx 40rpx 24rpx; border-bottom: 2rpx solid rgba(255,255,255,0.05); }
-.detail-title { font-size: 30rpx; font-weight: 600; color: #fff; }
-.detail-list { max-height: 480rpx; overflow-y: auto; padding: 16rpx 0; }
-
-/* ??????? */
-.earnings-detail-card .detail-item { display: flex; align-items: center; gap: 24rpx; padding: 24rpx 40rpx; background: transparent; border-bottom: 2rpx solid rgba(255,255,255,0.03); }
-.earnings-detail-card .detail-item:last-child { border-bottom: none; }
-.earnings-detail-card .detail-item:active { background: rgba(255, 255, 255, 0.05); }
-
-/* ???? */
-.earnings-detail-card .detail-avatar-wrap { width: 88rpx; height: 88rpx; flex-shrink: 0; }
-.earnings-detail-card .detail-avatar { width: 100%; height: 100%; border-radius: 50%; border: 2rpx solid rgba(56, 189, 172, 0.2); }
-.earnings-detail-card .detail-avatar-text { width: 100%; height: 100%; border-radius: 50%; background: linear-gradient(135deg, #38bdac 0%, #2da396 100%); display: flex; align-items: center; justify-content: center; font-size: 36rpx; font-weight: 700; color: #ffffff; }
-
-/* ???? */
-.earnings-detail-card .detail-content { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 8rpx; }
-.earnings-detail-card .detail-top { display: flex; align-items: center; justify-content: space-between; gap: 16rpx; }
-.earnings-detail-card .detail-buyer { font-size: 28rpx; font-weight: 500; color: #ffffff; flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
-.earnings-detail-card .detail-amount { font-size: 32rpx; font-weight: 700; color: #38bdac; flex-shrink: 0; white-space: nowrap; }
-
-/* ???? */
-.earnings-detail-card .detail-product { display: flex; align-items: baseline; gap: 4rpx; font-size: 24rpx; color: rgba(255, 255, 255, 0.6); min-width: 0; overflow: hidden; }
-.earnings-detail-card .detail-book { color: rgba(255, 255, 255, 0.7); font-weight: 500; max-width: 50%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
-.earnings-detail-card .detail-chapter { color: rgba(255, 255, 255, 0.5); flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
-.earnings-detail-card .detail-time { font-size: 22rpx; color: rgba(255, 255, 255, 0.4); }
-
-/* ???? - ?? Next.js */
-.share-section { display: flex; flex-direction: column; gap: 12rpx; width: 100%; margin-bottom: 24rpx; }
-.share-item { display: flex; align-items: center; background: rgba(28, 28, 30, 0.8); backdrop-filter: blur(40rpx); border: 2rpx solid rgba(255,255,255,0.1); border-radius: 24rpx; padding: 32rpx; border: none; margin: 0; text-align: left; width: 100%; box-sizing: border-box; }
-.share-item::after { border: none; }
-.share-icon { width: 96rpx; height: 96rpx; border-radius: 20rpx; display: flex; align-items: center; justify-content: center; margin-right: 24rpx; flex-shrink: 0; }
-.share-icon.poster { background: rgba(103,58,183,0.2); }
-.share-icon.wechat { background: rgba(7,193,96,0.2); }
-.share-icon.link { background: rgba(158,158,158,0.2); }
-.icon-share-btn { width: 48rpx; height: 48rpx; display: block; }
-.share-icon.poster .icon-share-btn { filter: brightness(0) saturate(100%) invert(37%) sepia(73%) saturate(2296%) hue-rotate(252deg) brightness(96%) contrast(92%); }
-.share-icon.wechat .icon-share-btn { filter: brightness(0) saturate(100%) invert(58%) sepia(91%) saturate(1255%) hue-rotate(105deg) brightness(96%) contrast(97%); }
-.share-icon.link .icon-share-btn { filter: brightness(0) saturate(100%) invert(60%) sepia(0%) saturate(0%) hue-rotate(0deg) brightness(95%) contrast(85%); }
-.share-info { flex: 1; text-align: left; }
-.share-title { font-size: 28rpx; color: #fff; font-weight: 500; display: block; text-align: left; }
-.share-desc { font-size: 22rpx; color: rgba(255,255,255,0.5); margin-top: 4rpx; display: block; text-align: left; }
-.share-arrow-icon { width: 40rpx; height: 40rpx; display: block; flex-shrink: 0; filter: brightness(0) saturate(100%) invert(60%) sepia(0%) saturate(0%) hue-rotate(0deg) brightness(95%) contrast(85%); }
-.share-btn-wechat { line-height: normal; font-size: inherit; padding: 24rpx 32rpx !important; margin: 0 !important; width: 100% !important; }
-
-/* ?????????????? + ???? + ???????? */
-/* ???????? backdrop-filter??????????????? */
-.modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.8); display: flex; align-items: center; justify-content: center; z-index: 1000; padding: 32rpx; box-sizing: border-box; }
-
-.poster-dialog { width: 686rpx; border-radius: 24rpx; overflow: hidden; position: relative; background: transparent; }
-.poster-close { position: absolute; top: 20rpx; right: 20rpx; width: 56rpx; height: 56rpx; border-radius: 28rpx; background: rgba(0,0,0,0.25); color: rgba(255,255,255,0.9); display: flex; align-items: center; justify-content: center; z-index: 5; font-size: 28rpx; }
-
-/* ???? */
-.poster-card { position: relative; background: linear-gradient(135deg, #0a1628 0%, #0f2137 50%, #1a3a5c 100%); color: #fff; padding: 44rpx 40rpx 36rpx; }
-.poster-inner { position: relative; z-index: 2; display: flex; flex-direction: column; align-items: center; text-align: center; }
-
-/* ???? */
-/* ???????? filter: blur ??????????????? + ???? */
-.poster-glow { position: absolute; width: 320rpx; height: 320rpx; border-radius: 50%; opacity: 0.6; z-index: 1; }
-.poster-glow-left { top: -120rpx; left: -160rpx; background: rgba(0,206,209,0.12); box-shadow: 0 0 140rpx 40rpx rgba(0,206,209,0.18); }
-.poster-glow-right { bottom: -140rpx; right: -160rpx; background: rgba(255,215,0,0.10); box-shadow: 0 0 160rpx 50rpx rgba(255,215,0,0.14); }
-.poster-ring { position: absolute; width: 520rpx; height: 520rpx; border-radius: 50%; border: 2rpx solid rgba(0,206,209,0.06); left: 50%; top: 50%; transform: translate(-50%, -50%); z-index: 1; }
-
-/* ???? */
-.poster-badges { display: flex; gap: 16rpx; margin-bottom: 24rpx; }
-.poster-badge { padding: 10rpx 22rpx; font-size: 20rpx; font-weight: 700; border-radius: 999rpx; border: 2rpx solid transparent; }
-.poster-badge-gold { background: rgba(255,215,0,0.18); color: #FFD700; border-color: rgba(255,215,0,0.28); }
-.poster-badge-brand { background: rgba(0,206,209,0.18); color: #00CED1; border-color: rgba(0,206,209,0.28); }
-
-/* ?? */
-.poster-title { margin-bottom: 8rpx; }
-.poster-title-line1 { display: block; font-size: 44rpx; font-weight: 900; line-height: 1.1; color: #00CED1; }
-.poster-title-line2 { display: block; font-size: 44rpx; font-weight: 900; line-height: 1.1; color: #fff; margin-top: 6rpx; }
-.poster-subtitle { display: block; font-size: 22rpx; color: rgba(255,255,255,0.6); margin-bottom: 28rpx; }
-
-/* ???? */
-.poster-stats { width: 100%; display: flex; gap: 16rpx; justify-content: center; margin-bottom: 24rpx; }
-.poster-stat { flex: 1; max-width: 190rpx; background: rgba(255,255,255,0.05); border: 2rpx solid rgba(255,255,255,0.10); border-radius: 16rpx; padding: 18rpx 10rpx; }
-.poster-stat-value { display: block; font-size: 44rpx; font-weight: 900; line-height: 1; }
-.poster-stat-label { display: block; font-size: 20rpx; color: rgba(255,255,255,0.5); margin-top: 8rpx; }
-.poster-stat-gold { color: #FFD700; }
-.poster-stat-brand { color: #00CED1; }
-.poster-stat-pink { color: #E91E63; }
-
-/* ?? */
-.poster-tags { display: flex; flex-wrap: wrap; justify-content: center; gap: 10rpx; margin: 0 24rpx 26rpx; }
-.poster-tag { font-size: 20rpx; color: rgba(255,255,255,0.7); background: rgba(255,255,255,0.05); border: 2rpx solid rgba(255,255,255,0.10); border-radius: 12rpx; padding: 6rpx 14rpx; }
-
-/* ??? */
-.poster-recommender { display: flex; align-items: center; gap: 12rpx; background: rgba(0,206,209,0.10); border: 2rpx solid rgba(0,206,209,0.20); border-radius: 999rpx; padding: 12rpx 22rpx; margin-bottom: 22rpx; }
-.poster-avatar { width: 44rpx; height: 44rpx; border-radius: 22rpx; background: rgba(0,206,209,0.30); display: flex; align-items: center; justify-content: center; }
-.poster-avatar-text { font-size: 20rpx; font-weight: 800; color: #00CED1; }
-.poster-recommender-text { font-size: 22rpx; color: #00CED1; }
-
-/* ??? */
-.poster-discount { width: 100%; background: linear-gradient(90deg, rgba(255,215,0,0.10) 0%, rgba(233,30,99,0.10) 100%); border: 2rpx solid rgba(255,215,0,0.20); border-radius: 18rpx; padding: 18rpx 18rpx; margin-bottom: 26rpx; }
-.poster-discount-text { font-size: 22rpx; color: rgba(255,255,255,0.80); }
-.poster-discount-highlight { color: #00CED1; font-weight: 800; }
-
-/* ??? */
-.poster-qr-wrap { background: #fff; padding: 14rpx; border-radius: 16rpx; margin-bottom: 12rpx; box-shadow: 0 16rpx 40rpx rgba(0,0,0,0.35); }
-.poster-qr-img { width: 240rpx; height: 240rpx; display: block; }
-.poster-qr-tip { font-size: 20rpx; color: rgba(255,255,255,0.40); margin-bottom: 8rpx; }
-.poster-code { font-size: 22rpx; font-family: monospace; letter-spacing: 2rpx; color: rgba(0,206,209,0.80); }
-
-/* ??????? */
-.poster-footer { background: #fff; padding: 28rpx 28rpx 32rpx; display: flex; flex-direction: column; gap: 18rpx; }
-.poster-footer-tip { font-size: 22rpx; color: rgba(0,0,0,0.55); text-align: center; }
-.poster-footer-btn { height: 72rpx; border-radius: 18rpx; border: 2rpx solid rgba(0,0,0,0.15); display: flex; align-items: center; justify-content: center; font-size: 28rpx; color: rgba(0,0,0,0.75); background: #fff; }
-
-
-
-/* ????- ?? Next.js */
-.empty-earnings { background: rgba(28, 28, 30, 0.8); backdrop-filter: blur(40rpx); border: 2rpx solid rgba(255,255,255,0.1); border-radius: 32rpx; padding: 64rpx 40rpx; text-align: center; margin-bottom: 24rpx; }
-.empty-icon-wrapper { width: 128rpx; height: 128rpx; border-radius: 50%; background: rgba(28, 28, 30, 0.8); display: flex; align-items: center; justify-content: center; margin: 0 auto 32rpx; }
-.empty-gift-icon { width: 64rpx; height: 64rpx; display: block; filter: brightness(0) saturate(100%) invert(60%) sepia(0%) saturate(0%) hue-rotate(0deg) brightness(95%) contrast(85%); }
-.empty-title { font-size: 30rpx; font-weight: 500; color: #fff; display: block; margin-bottom: 16rpx; }
-.empty-desc { font-size: 26rpx; color: rgba(255,255,255,0.6); display: block; line-height: 1.5; }
-
-
-/* ===== Loading 遮罩(备用) ===== */
-.loading-overlay {
- position: fixed;
- top: 0;
- left: 0;
- right: 0;
- bottom: 0;
- background: rgba(0,0,0,0.7);
- backdrop-filter: blur(10rpx);
- z-index: 999;
- display: flex;
- align-items: center;
- justify-content: center;
-}
-.loading-content {
- display: flex;
- flex-direction: column;
- align-items: center;
- gap: 24rpx;
-}
-.loading-spinner {
- width: 80rpx;
- height: 80rpx;
- border: 6rpx solid rgba(56,189,172,0.2);
- border-top-color: #38bdac;
- border-radius: 50%;
- animation: spin 1s linear infinite;
-}
-@keyframes spin {
- 0% { transform: rotate(0deg); }
- 100% { transform: rotate(360deg); }
-}
-.loading-text {
- font-size: 28rpx;
- color: rgba(255,255,255,0.8);
- font-weight: 500;
-}
-.content-loading {
- opacity: 0.3;
- pointer-events: none;
-}
-
-/* ===== 收益明细独立块 ===== */
-.detail-item { display: flex; align-items: center; gap: 24rpx; padding: 24rpx; background: rgba(255,255,255,0.02); border-radius: 16rpx; margin-bottom: 16rpx; transition: all 0.3s; }
-.detail-item:active { background: rgba(255,255,255,0.05); }
-.detail-avatar-wrap { flex-shrink: 0; }
-.detail-avatar { width: 88rpx; height: 88rpx; border-radius: 50%; border: 2rpx solid rgba(56,189,172,0.2); }
-.detail-avatar-text { width: 88rpx; height: 88rpx; border-radius: 50%; background: linear-gradient(135deg, #38bdac 0%, #2da396 100%); display: flex; align-items: center; justify-content: center; font-size: 36rpx; font-weight: 700; color: #ffffff; }
-.detail-content { flex: 1; display: flex; flex-direction: column; gap: 8rpx; min-width: 0; }
-.detail-top { display: flex; align-items: center; justify-content: space-between; gap: 16rpx; }
-.detail-buyer { font-size: 28rpx; font-weight: 500; color: #ffffff; flex-shrink: 0; }
-.detail-amount { font-size: 32rpx; font-weight: 700; color: #38bdac; flex-shrink: 0; }
-.detail-product { display: flex; align-items: center; font-size: 24rpx; color: rgba(255,255,255,0.6); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
-.detail-book { color: rgba(255,255,255,0.7); font-weight: 500; }
-.detail-chapter { color: rgba(255,255,255,0.5); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
-.detail-time { font-size: 22rpx; color: rgba(255,255,255,0.4); }
diff --git a/归档/miniprogram/pages/referral/referral.wxss.backup b/归档/miniprogram/pages/referral/referral.wxss.backup
deleted file mode 100644
index 3c9e7c32..00000000
--- a/归档/miniprogram/pages/referral/referral.wxss.backup
+++ /dev/null
@@ -1,379 +0,0 @@
-/* ???????? - 1:1??Web?? */
-.page { min-height: 100vh; background: #000; padding-bottom: 64rpx; }
-
-/* ??? */
-.nav-bar { position: fixed; top: 0; left: 0; right: 0; z-index: 100; background: rgba(0,0,0,0.9); backdrop-filter: blur(40rpx); display: flex; align-items: center; justify-content: space-between; padding: 0 32rpx; height: 88rpx; }
-.nav-left { display: flex; gap: 16rpx; align-items: center; }
-.nav-back { width: 64rpx; height: 64rpx; background: #1c1c1e; border-radius: 50%; display: flex; align-items: center; justify-content: center; }
-.nav-icon { width: 40rpx; height: 40rpx; display: block; filter: brightness(0) saturate(100%) invert(60%) sepia(0%) saturate(0%) hue-rotate(0deg) brightness(95%) contrast(85%); }
-.nav-title { font-size: 34rpx; font-weight: 600; color: #fff; flex: 1; text-align: center; }
-.nav-right-placeholder { width: 144rpx; }
-.nav-btn { width: 64rpx; height: 64rpx; background: #1c1c1e; border-radius: 50%; display: flex; align-items: center; justify-content: center; }
-
-.content { padding: 24rpx; width: 100%; box-sizing: border-box; }
-
-/* ?????? */
-.expiring-banner { display: flex; align-items: center; gap: 24rpx; padding: 24rpx; background: rgba(255,165,0,0.1); border: 2rpx solid rgba(255,165,0,0.3); border-radius: 24rpx; margin-bottom: 24rpx; }
-.banner-icon { width: 80rpx; height: 80rpx; background: rgba(255,165,0,0.2); border-radius: 50%; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
-.icon-bell-warning { width: 40rpx; height: 40rpx; display: block; filter: brightness(0) saturate(100%) invert(64%) sepia(89%) saturate(1363%) hue-rotate(4deg) brightness(101%) contrast(102%); }
-.banner-content { flex: 1; }
-.banner-title { font-size: 28rpx; font-weight: 500; color: #fff; display: block; }
-.banner-desc { font-size: 24rpx; color: rgba(255,165,0,0.8); margin-top: 4rpx; display: block; }
-
-/* ???? - ?? Next.js */
-.earnings-card { position: relative; background: rgba(28, 28, 30, 0.8); backdrop-filter: blur(40rpx); border: 2rpx solid rgba(255,255,255,0.1); border-radius: 32rpx; padding: 48rpx; margin-bottom: 24rpx; overflow: hidden; width: 100%; box-sizing: border-box; }
-.earnings-bg { position: absolute; top: 0; right: 0; width: 256rpx; height: 256rpx; background: rgba(0,206,209,0.15); border-radius: 50%; filter: blur(100rpx); }
-.earnings-main { position: relative; }
-.earnings-header { display: flex; align-items: flex-start; justify-content: space-between; margin-bottom: 32rpx; }
-.earnings-left { display: flex; align-items: center; gap: 16rpx; }
-.wallet-icon { width: 80rpx; height: 80rpx; background: rgba(0,206,209,0.2); border-radius: 20rpx; display: flex; align-items: center; justify-content: center; }
-.icon-wallet { width: 40rpx; height: 40rpx; display: block; filter: brightness(0) saturate(100%) invert(72%) sepia(54%) saturate(2933%) hue-rotate(134deg) brightness(101%) contrast(101%); }
-.earnings-info { display: flex; flex-direction: column; gap: 8rpx; }
-.earnings-label { font-size: 24rpx; color: rgba(255,255,255,0.6); }
-.commission-rate { font-size: 24rpx; color: #00CED1; font-weight: 500; }
-.earnings-right { text-align: right; }
-.earnings-value { font-size: 60rpx; font-weight: 700; color: #fff; display: block; line-height: 1; }
-.pending-text { font-size: 24rpx; color: rgba(255,255,255,0.5); margin-top: 8rpx; display: block; }
-
-.withdraw-btn { padding: 28rpx; background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%); color: #fff; font-size: 32rpx; font-weight: 600; text-align: center; border-radius: 24rpx; box-shadow: 0 8rpx 24rpx rgba(0,206,209,0.3); }
-.withdraw-btn.btn-disabled { background: rgba(0,206,209,0.2); color: rgba(255,255,255,0.3); box-shadow: none; }
-
-/* ???? - ?? Next.js 4??? */
-.stats-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8rpx; margin-bottom: 24rpx; width: 100%; box-sizing: border-box; }
-.stat-card { background: rgba(28, 28, 30, 0.8); backdrop-filter: blur(40rpx); border: 2rpx solid rgba(255,255,255,0.1); border-radius: 24rpx; padding: 24rpx 12rpx; text-align: center; }
-.stat-value { font-size: 40rpx; font-weight: 700; color: #fff; display: block; }
-.stat-value.orange { color: #FFA500; }
-.stat-label { font-size: 20rpx; color: rgba(255,255,255,0.6); margin-top: 8rpx; display: block; }
-
-/* ????? */
-.visit-stat { display: flex; align-items: center; justify-content: center; gap: 12rpx; padding: 20rpx 32rpx; background: rgba(255,255,255,0.05); border-radius: 16rpx; margin-bottom: 24rpx; }
-.visit-label { font-size: 24rpx; color: rgba(255,255,255,0.5); }
-.visit-value { font-size: 32rpx; font-weight: 700; color: #00CED1; }
-.visit-tip { font-size: 24rpx; color: rgba(255,255,255,0.5); }
-
-/* ???? - ?? Next.js */
-.rules-card { background: rgba(0,206,209,0.05); border: 2rpx solid rgba(0,206,209,0.2); border-radius: 24rpx; padding: 32rpx; margin-bottom: 24rpx; width: 100%; box-sizing: border-box; }
-.rules-header { display: flex; align-items: center; gap: 16rpx; margin-bottom: 16rpx; }
-.rules-icon { width: 64rpx; height: 64rpx; background: rgba(0,206,209,0.2); border-radius: 16rpx; display: flex; align-items: center; justify-content: center; }
-.icon-alert { width: 32rpx; height: 32rpx; display: block; filter: brightness(0) saturate(100%) invert(72%) sepia(54%) saturate(2933%) hue-rotate(134deg) brightness(101%) contrast(101%); }
-.rules-title { font-size: 28rpx; font-weight: 500; color: #fff; }
-.rules-list { padding-left: 8rpx; }
-.rule-item { font-size: 24rpx; color: rgba(255,255,255,0.6); line-height: 2; display: block; margin-bottom: 4rpx; }
-.rule-item .gold { color: #FFD700; font-weight: 500; }
-.rule-item .brand { color: #00CED1; font-weight: 500; }
-.rule-item .orange { color: #FFA500; font-weight: 500; }
-
-/* ?????? - ?? Next.js */
-.binding-card { background: rgba(28, 28, 30, 0.8); backdrop-filter: blur(40rpx); border: 2rpx solid rgba(255,255,255,0.1); border-radius: 32rpx; overflow: hidden; margin-bottom: 24rpx; width: 100%; box-sizing: border-box; }
-.binding-header { display: flex; align-items: center; justify-content: space-between; padding: 28rpx 32rpx; border-bottom: 2rpx solid rgba(255,255,255,0.05); }
-.binding-title { display: flex; align-items: center; gap: 12rpx; }
-.binding-icon-img { width: 40rpx; height: 40rpx; display: block; filter: brightness(0) saturate(100%) invert(72%) sepia(54%) saturate(2933%) hue-rotate(134deg) brightness(101%) contrast(101%); }
-.title-text { font-size: 30rpx; font-weight: 600; color: #fff; }
-.binding-count { font-size: 26rpx; color: rgba(255,255,255,0.5); }
-.toggle-icon { font-size: 24rpx; color: rgba(255,255,255,0.5); }
-
-/* Tab?? */
-.binding-tabs { display: flex; border-bottom: 2rpx solid rgba(255,255,255,0.05); }
-.tab-item { flex: 1; padding: 24rpx 0; text-align: center; font-size: 26rpx; color: rgba(255,255,255,0.5); position: relative; }
-.tab-item.tab-active { color: #00CED1; }
-.tab-item.tab-active::after { content: ''; position: absolute; bottom: 0; left: 50%; transform: translateX(-50%); width: 80rpx; height: 4rpx; background: #00CED1; border-radius: 4rpx; }
-
-/* ???? */
-.binding-list { max-height: 640rpx; overflow-y: auto; }
-.empty-state { padding: 80rpx 0; text-align: center; }
-.empty-icon { font-size: 64rpx; display: block; margin-bottom: 16rpx; }
-.empty-text { font-size: 26rpx; color: rgba(255,255,255,0.5); }
-
-.binding-item { display: flex; align-items: center; padding: 24rpx 32rpx; border-bottom: 2rpx solid rgba(255,255,255,0.05); }
-.binding-item:last-child { border-bottom: none; }
-.user-avatar { width: 80rpx; height: 80rpx; border-radius: 50%; background: rgba(0,206,209,0.2); display: flex; align-items: center; justify-content: center; font-size: 28rpx; font-weight: 600; color: #00CED1; margin-right: 24rpx; flex-shrink: 0; }
-.user-avatar.avatar-converted { background: rgba(76,175,80,0.2); color: #4CAF50; }
-.user-avatar.avatar-expired { background: rgba(158,158,158,0.2); color: #9E9E9E; }
-.user-info { flex: 1; }
-.user-name { font-size: 28rpx; color: #fff; font-weight: 500; display: block; }
-.user-time { font-size: 22rpx; color: rgba(255,255,255,0.5); margin-top: 4rpx; display: block; }
-.user-status { text-align: right; }
-.status-amount { font-size: 28rpx; color: #4CAF50; font-weight: 600; display: block; }
-.status-order { font-size: 22rpx; color: rgba(255,255,255,0.5); }
-.status-tag { font-size: 22rpx; padding: 8rpx 16rpx; border-radius: 16rpx; }
-.status-tag.tag-green { background: rgba(76,175,80,0.2); color: #4CAF50; }
-.status-tag.tag-orange { background: rgba(255,165,0,0.2); color: #FFA500; }
-.status-tag.tag-red { background: rgba(244,67,54,0.2); color: #F44336; }
-
-/* ????? - ?? Next.js */
-.invite-card { background: rgba(28, 28, 30, 0.8); backdrop-filter: blur(40rpx); border: 2rpx solid rgba(255,255,255,0.1); border-radius: 32rpx; padding: 40rpx; margin-bottom: 24rpx; width: 100%; box-sizing: border-box; }
-.invite-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 24rpx; }
-.invite-title { font-size: 30rpx; font-weight: 600; color: #fff; }
-.invite-code-box { background: rgba(0,206,209,0.2); padding: 12rpx 24rpx; border-radius: 16rpx; }
-.invite-code { font-size: 28rpx; font-weight: 600; color: #00CED1; font-family: 'Courier New', monospace; letter-spacing: 2rpx; }
-.invite-tip { font-size: 24rpx; color: rgba(255,255,255,0.6); line-height: 1.5; display: block; }
-.invite-tip .gold { color: #FFD700; }
-.invite-tip .brand { color: #00CED1; }
-
-/* ?????? - ?? Next.js */
-.earnings-detail-card { background: rgba(28, 28, 30, 0.8); backdrop-filter: blur(40rpx); border: 2rpx solid rgba(255,255,255,0.1); border-radius: 32rpx; overflow: hidden; margin-bottom: 24rpx; width: 100%; box-sizing: border-box; }
-.detail-header { padding: 40rpx 40rpx 24rpx; border-bottom: 2rpx solid rgba(255,255,255,0.05); }
-.detail-title { font-size: 30rpx; font-weight: 600; color: #fff; }
-.detail-list { max-height: 480rpx; overflow-y: auto; }
-.detail-item { display: flex; align-items: center; justify-content: space-between; padding: 32rpx 40rpx; border-bottom: 2rpx solid rgba(255,255,255,0.05); }
-.detail-item:last-child { border-bottom: none; }
-.detail-left { display: flex; align-items: center; gap: 24rpx; flex: 1; }
-.detail-icon { width: 80rpx; height: 80rpx; border-radius: 20rpx; background: rgba(0,206,209,0.2); display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
-.icon-gift { width: 40rpx; height: 40rpx; display: block; filter: brightness(0) saturate(100%) invert(72%) sepia(54%) saturate(2933%) hue-rotate(134deg) brightness(101%) contrast(101%); }
-.detail-info { flex: 1; }
-.detail-type { font-size: 28rpx; color: #fff; font-weight: 500; display: block; }
-.detail-time { font-size: 24rpx; color: rgba(255,255,255,0.5); margin-top: 4rpx; display: block; }
-.detail-amount { font-size: 30rpx; font-weight: 600; color: #00CED1; }
-
-/* ???? - ?? Next.js */
-.share-section { display: flex; flex-direction: column; gap: 12rpx; width: 100%; margin-bottom: 24rpx; }
-.share-item { display: flex; align-items: center; background: rgba(28, 28, 30, 0.8); backdrop-filter: blur(40rpx); border: 2rpx solid rgba(255,255,255,0.1); border-radius: 24rpx; padding: 32rpx; border: none; margin: 0; text-align: left; width: 100%; box-sizing: border-box; }
-.share-item::after { border: none; }
-.share-icon { width: 96rpx; height: 96rpx; border-radius: 20rpx; display: flex; align-items: center; justify-content: center; margin-right: 24rpx; flex-shrink: 0; }
-.share-icon.poster { background: rgba(103,58,183,0.2); }
-.share-icon.wechat { background: rgba(7,193,96,0.2); }
-.share-icon.link { background: rgba(158,158,158,0.2); }
-.icon-share-btn { width: 48rpx; height: 48rpx; display: block; }
-.share-icon.poster .icon-share-btn { filter: brightness(0) saturate(100%) invert(37%) sepia(73%) saturate(2296%) hue-rotate(252deg) brightness(96%) contrast(92%); }
-.share-icon.wechat .icon-share-btn { filter: brightness(0) saturate(100%) invert(58%) sepia(91%) saturate(1255%) hue-rotate(105deg) brightness(96%) contrast(97%); }
-.share-icon.link .icon-share-btn { filter: brightness(0) saturate(100%) invert(60%) sepia(0%) saturate(0%) hue-rotate(0deg) brightness(95%) contrast(85%); }
-.share-info { flex: 1; text-align: left; }
-.share-title { font-size: 28rpx; color: #fff; font-weight: 500; display: block; text-align: left; }
-.share-desc { font-size: 22rpx; color: rgba(255,255,255,0.5); margin-top: 4rpx; display: block; text-align: left; }
-.share-arrow-icon { width: 40rpx; height: 40rpx; display: block; flex-shrink: 0; filter: brightness(0) saturate(100%) invert(60%) sepia(0%) saturate(0%) hue-rotate(0deg) brightness(95%) contrast(85%); }
-.share-btn-wechat { line-height: normal; font-size: inherit; padding: 24rpx 32rpx !important; margin: 0 !important; width: 100% !important; }
-
-/* ?????????????? + ???? + ???????? */
-/* ???????? backdrop-filter??????????????? */
-.modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.8); display: flex; align-items: center; justify-content: center; z-index: 1000; padding: 32rpx; box-sizing: border-box; }
-
-.poster-dialog { width: 686rpx; border-radius: 24rpx; overflow: hidden; position: relative; background: transparent; }
-.poster-close { position: absolute; top: 20rpx; right: 20rpx; width: 56rpx; height: 56rpx; border-radius: 28rpx; background: rgba(0,0,0,0.25); color: rgba(255,255,255,0.9); display: flex; align-items: center; justify-content: center; z-index: 5; font-size: 28rpx; }
-
-/* ???? */
-.poster-card { position: relative; background: linear-gradient(135deg, #0a1628 0%, #0f2137 50%, #1a3a5c 100%); color: #fff; padding: 44rpx 40rpx 36rpx; }
-.poster-inner { position: relative; z-index: 2; display: flex; flex-direction: column; align-items: center; text-align: center; }
-
-/* ???? */
-/* ???????? filter: blur ??????????????? + ???? */
-.poster-glow { position: absolute; width: 320rpx; height: 320rpx; border-radius: 50%; opacity: 0.6; z-index: 1; }
-.poster-glow-left { top: -120rpx; left: -160rpx; background: rgba(0,206,209,0.12); box-shadow: 0 0 140rpx 40rpx rgba(0,206,209,0.18); }
-.poster-glow-right { bottom: -140rpx; right: -160rpx; background: rgba(255,215,0,0.10); box-shadow: 0 0 160rpx 50rpx rgba(255,215,0,0.14); }
-.poster-ring { position: absolute; width: 520rpx; height: 520rpx; border-radius: 50%; border: 2rpx solid rgba(0,206,209,0.06); left: 50%; top: 50%; transform: translate(-50%, -50%); z-index: 1; }
-
-/* ???? */
-.poster-badges { display: flex; gap: 16rpx; margin-bottom: 24rpx; }
-.poster-badge { padding: 10rpx 22rpx; font-size: 20rpx; font-weight: 700; border-radius: 999rpx; border: 2rpx solid transparent; }
-.poster-badge-gold { background: rgba(255,215,0,0.18); color: #FFD700; border-color: rgba(255,215,0,0.28); }
-.poster-badge-brand { background: rgba(0,206,209,0.18); color: #00CED1; border-color: rgba(0,206,209,0.28); }
-
-/* ?? */
-.poster-title { margin-bottom: 8rpx; }
-.poster-title-line1 { display: block; font-size: 44rpx; font-weight: 900; line-height: 1.1; color: #00CED1; }
-.poster-title-line2 { display: block; font-size: 44rpx; font-weight: 900; line-height: 1.1; color: #fff; margin-top: 6rpx; }
-.poster-subtitle { display: block; font-size: 22rpx; color: rgba(255,255,255,0.6); margin-bottom: 28rpx; }
-
-/* ???? */
-.poster-stats { width: 100%; display: flex; gap: 16rpx; justify-content: center; margin-bottom: 24rpx; }
-.poster-stat { flex: 1; max-width: 190rpx; background: rgba(255,255,255,0.05); border: 2rpx solid rgba(255,255,255,0.10); border-radius: 16rpx; padding: 18rpx 10rpx; }
-.poster-stat-value { display: block; font-size: 44rpx; font-weight: 900; line-height: 1; }
-.poster-stat-label { display: block; font-size: 20rpx; color: rgba(255,255,255,0.5); margin-top: 8rpx; }
-.poster-stat-gold { color: #FFD700; }
-.poster-stat-brand { color: #00CED1; }
-.poster-stat-pink { color: #E91E63; }
-
-/* ?? */
-.poster-tags { display: flex; flex-wrap: wrap; justify-content: center; gap: 10rpx; margin: 0 24rpx 26rpx; }
-.poster-tag { font-size: 20rpx; color: rgba(255,255,255,0.7); background: rgba(255,255,255,0.05); border: 2rpx solid rgba(255,255,255,0.10); border-radius: 12rpx; padding: 6rpx 14rpx; }
-
-/* ??? */
-.poster-recommender { display: flex; align-items: center; gap: 12rpx; background: rgba(0,206,209,0.10); border: 2rpx solid rgba(0,206,209,0.20); border-radius: 999rpx; padding: 12rpx 22rpx; margin-bottom: 22rpx; }
-.poster-avatar { width: 44rpx; height: 44rpx; border-radius: 22rpx; background: rgba(0,206,209,0.30); display: flex; align-items: center; justify-content: center; }
-.poster-avatar-text { font-size: 20rpx; font-weight: 800; color: #00CED1; }
-.poster-recommender-text { font-size: 22rpx; color: #00CED1; }
-
-/* ??? */
-.poster-discount { width: 100%; background: linear-gradient(90deg, rgba(255,215,0,0.10) 0%, rgba(233,30,99,0.10) 100%); border: 2rpx solid rgba(255,215,0,0.20); border-radius: 18rpx; padding: 18rpx 18rpx; margin-bottom: 26rpx; }
-.poster-discount-text { font-size: 22rpx; color: rgba(255,255,255,0.80); }
-.poster-discount-highlight { color: #00CED1; font-weight: 800; }
-
-/* ??? */
-.poster-qr-wrap { background: #fff; padding: 14rpx; border-radius: 16rpx; margin-bottom: 12rpx; box-shadow: 0 16rpx 40rpx rgba(0,0,0,0.35); }
-.poster-qr-img { width: 240rpx; height: 240rpx; display: block; }
-.poster-qr-tip { font-size: 20rpx; color: rgba(255,255,255,0.40); margin-bottom: 8rpx; }
-.poster-code { font-size: 22rpx; font-family: monospace; letter-spacing: 2rpx; color: rgba(0,206,209,0.80); }
-
-/* ??????? */
-.poster-footer { background: #fff; padding: 28rpx 28rpx 32rpx; display: flex; flex-direction: column; gap: 18rpx; }
-.poster-footer-tip { font-size: 22rpx; color: rgba(0,0,0,0.55); text-align: center; }
-.poster-footer-btn { height: 72rpx; border-radius: 18rpx; border: 2rpx solid rgba(0,0,0,0.15); display: flex; align-items: center; justify-content: center; font-size: 28rpx; color: rgba(0,0,0,0.75); background: #fff; }
-
-
-
-/* ????- ?? Next.js */
-.empty-earnings { background: rgba(28, 28, 30, 0.8); backdrop-filter: blur(40rpx); border: 2rpx solid rgba(255,255,255,0.1); border-radius: 32rpx; padding: 64rpx 40rpx; text-align: center; margin-bottom: 24rpx; }
-.empty-icon-wrapper { width: 128rpx; height: 128rpx; border-radius: 50%; background: rgba(28, 28, 30, 0.8); display: flex; align-items: center; justify-content: center; margin: 0 auto 32rpx; }
-.empty-gift-icon { width: 64rpx; height: 64rpx; display: block; filter: brightness(0) saturate(100%) invert(60%) sepia(0%) saturate(0%) hue-rotate(0deg) brightness(95%) contrast(85%); }
-.empty-title { font-size: 30rpx; font-weight: 500; color: #fff; display: block; margin-bottom: 16rpx; }
-.empty-desc { font-size: 26rpx; color: rgba(255,255,255,0.6); display: block; line-height: 1.5; }
-
-
- / * = = = = = TrGm5 ? = = = = = * /
- . l o a d i n g - o v e r l a y {
- p o s i t i o n : f i x e d ;
- t o p : 0 ;
- l e f t : 0 ;
- r i g h t : 0 ;
- b o t t o m : 0 ;
- b a c k g r o u n d : r g b a ( 0 , 0 , 0 , 0 . 7 ) ;
- b a c k d r o p - f i l t e r : b l u r ( 1 0 r p x ) ;
- z - i n d e x : 9 9 9 ;
- d i s p l a y : f l e x ;
- a l i g n - i t e m s : c e n t e r ;
- j u s t i f y - c o n t e n t : c e n t e r ;
- }
-
- . l o a d i n g - c o n t e n t {
- d i s p l a y : f l e x ;
- f l e x - d i r e c t i o n : c o l u m n ;
- a l i g n - i t e m s : c e n t e r ;
- g a p : 2 4 r p x ;
- }
-
- . l o a d i n g - s p i n n e r {
- w i d t h : 8 0 r p x ;
- h e i g h t : 8 0 r p x ;
- b o r d e r : 6 r p x s o l i d r g b a ( 5 6 , 1 8 9 , 1 7 2 , 0 . 2 ) ;
- b o r d e r - t o p - c o l o r : # 3 8 b d a c ;
- b o r d e r - r a d i u s : 5 0 % ;
- a n i m a t i o n : s p i n 1 s l i n e a r i n f i n i t e ;
- }
-
- @ k e y f r a m e s s p i n {
- 0 % { t r a n s f o r m : r o t a t e ( 0 d e g ) ; }
- 1 0 0 % { t r a n s f o r m : r o t a t e ( 3 6 0 d e g ) ; }
- }
-
- . l o a d i n g - t e x t {
- f o n t - s i z e : 2 8 r p x ;
- c o l o r : r g b a ( 2 5 5 , 2 5 5 , 2 5 5 , 0 . 8 ) ;
- f o n t - w e i g h t : 5 0 0 ;
- }
-
- . c o n t e n t - l o a d i n g {
- o p a c i t y : 0 . 3 ;
- p o i n t e r - e v e n t s : n o n e ;
- }
-
- / * = = = = = ARlē^|op]͓\!} = = = = = * /
- . d e t a i l - i t e m {
- d i s p l a y : f l e x ;
- a l i g n - i t e m s : c e n t e r ;
- g a p : 2 4 r p x ;
- p a d d i n g : 2 4 r p x ;
- b a c k g r o u n d : r g b a ( 2 5 5 , 2 5 5 , 2 5 5 , 0 . 0 2 ) ;
- b o r d e r - r a d i u s : 1 6 r p x ;
- m a r g i n - b o t t o m : 1 6 r p x ;
- t r a n s i t i o n : a l l 0 . 3 s ;
- }
-
- . d e t a i l - i t e m : a c t i v e {
- b a c k g r o u n d : r g b a ( 2 5 5 , 2 5 5 , 2 5 5 , 0 . 0 5 ) ;
- }
-
- . d e t a i l - a v a t a r - w r a p {
- f l e x - s h r i n k : 0 ;
- }
-
- . d e t a i l - a v a t a r {
- w i d t h : 8 8 r p x ;
- h e i g h t : 8 8 r p x ;
- b o r d e r - r a d i u s : 5 0 % ;
- b o r d e r : 2 r p x s o l i d r g b a ( 5 6 , 1 8 9 , 1 7 2 , 0 . 2 ) ;
- }
-
- . d e t a i l - a v a t a r - t e x t {
- w i d t h : 8 8 r p x ;
- h e i g h t : 8 8 r p x ;
- b o r d e r - r a d i u s : 5 0 % ;
- b a c k g r o u n d : l i n e a r - g r a d i e n t ( 1 3 5 d e g , # 3 8 b d a c 0 % , # 2 d a 3 9 6 1 0 0 % ) ;
- d i s p l a y : f l e x ;
- a l i g n - i t e m s : c e n t e r ;
- j u s t i f y - c o n t e n t : c e n t e r ;
- f o n t - s i z e : 3 6 r p x ;
- f o n t - w e i g h t : 7 0 0 ;
- c o l o r : # f f f f f f ;
- }
-
- . d e t a i l - c o n t e n t {
- f l e x : 1 ;
- d i s p l a y : f l e x ;
- f l e x - d i r e c t i o n : c o l u m n ;
- g a p : 8 r p x ;
- m i n - w i d t h : 0 ;
- }
-
- . d e t a i l - t o p {
- d i s p l a y : f l e x ;
- a l i g n - i t e m s : c e n t e r ;
- j u s t i f y - c o n t e n t : s p a c e - b e t w e e n ;
- g a p : 1 6 r p x ;
- }
-
- . d e t a i l - b u y e r {
- f o n t - s i z e : 2 8 r p x ;
- f o n t - w e i g h t : 5 0 0 ;
- c o l o r : # f f f f f f ;
- f l e x - s h r i n k : 0 ;
- }
-
- . d e t a i l - a m o u n t {
- f o n t - s i z e : 3 2 r p x ;
- f o n t - w e i g h t : 7 0 0 ;
- c o l o r : # 3 8 b d a c ;
- f l e x - s h r i n k : 0 ;
- }
-
- . d e t a i l - p r o d u c t {
- d i s p l a y : f l e x ;
- a l i g n - i t e m s : c e n t e r ;
- f o n t - s i z e : 2 4 r p x ;
- c o l o r : r g b a ( 2 5 5 , 2 5 5 , 2 5 5 , 0 . 6 ) ;
- o v e r f l o w : h i d d e n ;
- t e x t - o v e r f l o w : e l l i p s i s ;
- w h i t e - s p a c e : n o w r a p ;
- }
-
- . d e t a i l - b o o k {
- c o l o r : r g b a ( 2 5 5 , 2 5 5 , 2 5 5 , 0 . 7 ) ;
- f o n t - w e i g h t : 5 0 0 ;
- f l e x - s h r i n k : 0 ;
- }
-
- . d e t a i l - c h a p t e r {
- c o l o r : r g b a ( 2 5 5 , 2 5 5 , 2 5 5 , 0 . 5 ) ;
- o v e r f l o w : h i d d e n ;
- t e x t - o v e r f l o w : e l l i p s i s ;
- w h i t e - s p a c e : n o w r a p ;
- }
-
- . d e t a i l - t i m e {
- f o n t - s i z e : 2 2 r p x ;
- c o l o r : r g b a ( 2 5 5 , 2 5 5 , 2 5 5 , 0 . 4 ) ;
- }
-
- / * = = = ARlē^|͓\!}m2 = = = * /
- . d e t a i l - i t e m { d i s p l a y : f l e x ; a l i g n - i t e m s : c e n t e r ; g a p : 2 4 r p x ; p a d d i n g : 2 4 r p x ; b a c k g r o u n d : r g b a ( 2 5 5 , 2 5 5 , 2 5 5 , 0 . 0 2 ) ; b o r d e r - r a d i u s : 1 6 r p x ; m a r g i n - b o t t o m : 1 6 r p x ; }
- . d e t a i l - a v a t a r - w r a p { f l e x - s h r i n k : 0 ; }
- . d e t a i l - a v a t a r { w i d t h : 8 8 r p x ; h e i g h t : 8 8 r p x ; b o r d e r - r a d i u s : 5 0 % ; }
- . d e t a i l - a v a t a r - t e x t { w i d t h : 8 8 r p x ; h e i g h t : 8 8 r p x ; b o r d e r - r a d i u s : 5 0 % ; b a c k g r o u n d : l i n e a r - g r a d i e n t ( 1 3 5 d e g , # 3 8 b d a c 0 % , # 2 d a 3 9 6 1 0 0 % ) ; d i s p l a y : f l e x ; a l i g n - i t e m s : c e n t e r ; j u s t i f y - c o n t e n t : c e n t e r ; f o n t - s i z e : 3 6 r p x ; f o n t - w e i g h t : 7 0 0 ; c o l o r : # f f f f f f ; }
- . d e t a i l - c o n t e n t { f l e x : 1 ; d i s p l a y : f l e x ; f l e x - d i r e c t i o n : c o l u m n ; g a p : 8 r p x ; m i n - w i d t h : 0 ; o v e r f l o w : h i d d e n ; }
- . d e t a i l - t o p { d i s p l a y : f l e x ; a l i g n - i t e m s : c e n t e r ; j u s t i f y - c o n t e n t : s p a c e - b e t w e e n ; g a p : 1 6 r p x ; }
- . d e t a i l - b u y e r { f o n t - s i z e : 2 8 r p x ; f o n t - w e i g h t : 5 0 0 ; c o l o r : # f f f f f f ; f l e x : 1 ; o v e r f l o w : h i d d e n ; t e x t - o v e r f l o w : e l l i p s i s ; w h i t e - s p a c e : n o w r a p ; }
- . d e t a i l - a m o u n t { f o n t - s i z e : 3 2 r p x ; f o n t - w e i g h t : 7 0 0 ; c o l o r : # 3 8 b d a c ; f l e x - s h r i n k : 0 ; }
- . d e t a i l - p r o d u c t { d i s p l a y : f l e x ; a l i g n - i t e m s : b a s e l i n e ; f o n t - s i z e : 2 4 r p x ; c o l o r : r g b a ( 2 5 5 , 2 5 5 , 2 5 5 , 0 . 6 ) ; o v e r f l o w : h i d d e n ; }
- . d e t a i l - b o o k { c o l o r : r g b a ( 2 5 5 , 2 5 5 , 2 5 5 , 0 . 7 ) ; f o n t - w e i g h t : 5 0 0 ; m a x - w i d t h : 2 0 0 r p x ; o v e r f l o w : h i d d e n ; t e x t - o v e r f l o w : e l l i p s i s ; w h i t e - s p a c e : n o w r a p ; f l e x - s h r i n k : 1 ; }
- . d e t a i l - c h a p t e r { c o l o r : r g b a ( 2 5 5 , 2 5 5 , 2 5 5 , 0 . 5 ) ; f l e x : 1 ; o v e r f l o w : h i d d e n ; t e x t - o v e r f l o w : e l l i p s i s ; w h i t e - s p a c e : n o w r a p ; m a r g i n - l e f t : 4 r p x ; }
- . d e t a i l - t i m e { f o n t - s i z e : 2 2 r p x ; c o l o r : r g b a ( 2 5 5 , 2 5 5 , 2 5 5 , 0 . 4 ) ; }
-
-
\ No newline at end of file
diff --git a/归档/miniprogram/pages/referral/referral.wxss.broken b/归档/miniprogram/pages/referral/referral.wxss.broken
deleted file mode 100644
index 3c9e7c32..00000000
--- a/归档/miniprogram/pages/referral/referral.wxss.broken
+++ /dev/null
@@ -1,379 +0,0 @@
-/* ???????? - 1:1??Web?? */
-.page { min-height: 100vh; background: #000; padding-bottom: 64rpx; }
-
-/* ??? */
-.nav-bar { position: fixed; top: 0; left: 0; right: 0; z-index: 100; background: rgba(0,0,0,0.9); backdrop-filter: blur(40rpx); display: flex; align-items: center; justify-content: space-between; padding: 0 32rpx; height: 88rpx; }
-.nav-left { display: flex; gap: 16rpx; align-items: center; }
-.nav-back { width: 64rpx; height: 64rpx; background: #1c1c1e; border-radius: 50%; display: flex; align-items: center; justify-content: center; }
-.nav-icon { width: 40rpx; height: 40rpx; display: block; filter: brightness(0) saturate(100%) invert(60%) sepia(0%) saturate(0%) hue-rotate(0deg) brightness(95%) contrast(85%); }
-.nav-title { font-size: 34rpx; font-weight: 600; color: #fff; flex: 1; text-align: center; }
-.nav-right-placeholder { width: 144rpx; }
-.nav-btn { width: 64rpx; height: 64rpx; background: #1c1c1e; border-radius: 50%; display: flex; align-items: center; justify-content: center; }
-
-.content { padding: 24rpx; width: 100%; box-sizing: border-box; }
-
-/* ?????? */
-.expiring-banner { display: flex; align-items: center; gap: 24rpx; padding: 24rpx; background: rgba(255,165,0,0.1); border: 2rpx solid rgba(255,165,0,0.3); border-radius: 24rpx; margin-bottom: 24rpx; }
-.banner-icon { width: 80rpx; height: 80rpx; background: rgba(255,165,0,0.2); border-radius: 50%; display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
-.icon-bell-warning { width: 40rpx; height: 40rpx; display: block; filter: brightness(0) saturate(100%) invert(64%) sepia(89%) saturate(1363%) hue-rotate(4deg) brightness(101%) contrast(102%); }
-.banner-content { flex: 1; }
-.banner-title { font-size: 28rpx; font-weight: 500; color: #fff; display: block; }
-.banner-desc { font-size: 24rpx; color: rgba(255,165,0,0.8); margin-top: 4rpx; display: block; }
-
-/* ???? - ?? Next.js */
-.earnings-card { position: relative; background: rgba(28, 28, 30, 0.8); backdrop-filter: blur(40rpx); border: 2rpx solid rgba(255,255,255,0.1); border-radius: 32rpx; padding: 48rpx; margin-bottom: 24rpx; overflow: hidden; width: 100%; box-sizing: border-box; }
-.earnings-bg { position: absolute; top: 0; right: 0; width: 256rpx; height: 256rpx; background: rgba(0,206,209,0.15); border-radius: 50%; filter: blur(100rpx); }
-.earnings-main { position: relative; }
-.earnings-header { display: flex; align-items: flex-start; justify-content: space-between; margin-bottom: 32rpx; }
-.earnings-left { display: flex; align-items: center; gap: 16rpx; }
-.wallet-icon { width: 80rpx; height: 80rpx; background: rgba(0,206,209,0.2); border-radius: 20rpx; display: flex; align-items: center; justify-content: center; }
-.icon-wallet { width: 40rpx; height: 40rpx; display: block; filter: brightness(0) saturate(100%) invert(72%) sepia(54%) saturate(2933%) hue-rotate(134deg) brightness(101%) contrast(101%); }
-.earnings-info { display: flex; flex-direction: column; gap: 8rpx; }
-.earnings-label { font-size: 24rpx; color: rgba(255,255,255,0.6); }
-.commission-rate { font-size: 24rpx; color: #00CED1; font-weight: 500; }
-.earnings-right { text-align: right; }
-.earnings-value { font-size: 60rpx; font-weight: 700; color: #fff; display: block; line-height: 1; }
-.pending-text { font-size: 24rpx; color: rgba(255,255,255,0.5); margin-top: 8rpx; display: block; }
-
-.withdraw-btn { padding: 28rpx; background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%); color: #fff; font-size: 32rpx; font-weight: 600; text-align: center; border-radius: 24rpx; box-shadow: 0 8rpx 24rpx rgba(0,206,209,0.3); }
-.withdraw-btn.btn-disabled { background: rgba(0,206,209,0.2); color: rgba(255,255,255,0.3); box-shadow: none; }
-
-/* ???? - ?? Next.js 4??? */
-.stats-grid { display: grid; grid-template-columns: repeat(4, 1fr); gap: 8rpx; margin-bottom: 24rpx; width: 100%; box-sizing: border-box; }
-.stat-card { background: rgba(28, 28, 30, 0.8); backdrop-filter: blur(40rpx); border: 2rpx solid rgba(255,255,255,0.1); border-radius: 24rpx; padding: 24rpx 12rpx; text-align: center; }
-.stat-value { font-size: 40rpx; font-weight: 700; color: #fff; display: block; }
-.stat-value.orange { color: #FFA500; }
-.stat-label { font-size: 20rpx; color: rgba(255,255,255,0.6); margin-top: 8rpx; display: block; }
-
-/* ????? */
-.visit-stat { display: flex; align-items: center; justify-content: center; gap: 12rpx; padding: 20rpx 32rpx; background: rgba(255,255,255,0.05); border-radius: 16rpx; margin-bottom: 24rpx; }
-.visit-label { font-size: 24rpx; color: rgba(255,255,255,0.5); }
-.visit-value { font-size: 32rpx; font-weight: 700; color: #00CED1; }
-.visit-tip { font-size: 24rpx; color: rgba(255,255,255,0.5); }
-
-/* ???? - ?? Next.js */
-.rules-card { background: rgba(0,206,209,0.05); border: 2rpx solid rgba(0,206,209,0.2); border-radius: 24rpx; padding: 32rpx; margin-bottom: 24rpx; width: 100%; box-sizing: border-box; }
-.rules-header { display: flex; align-items: center; gap: 16rpx; margin-bottom: 16rpx; }
-.rules-icon { width: 64rpx; height: 64rpx; background: rgba(0,206,209,0.2); border-radius: 16rpx; display: flex; align-items: center; justify-content: center; }
-.icon-alert { width: 32rpx; height: 32rpx; display: block; filter: brightness(0) saturate(100%) invert(72%) sepia(54%) saturate(2933%) hue-rotate(134deg) brightness(101%) contrast(101%); }
-.rules-title { font-size: 28rpx; font-weight: 500; color: #fff; }
-.rules-list { padding-left: 8rpx; }
-.rule-item { font-size: 24rpx; color: rgba(255,255,255,0.6); line-height: 2; display: block; margin-bottom: 4rpx; }
-.rule-item .gold { color: #FFD700; font-weight: 500; }
-.rule-item .brand { color: #00CED1; font-weight: 500; }
-.rule-item .orange { color: #FFA500; font-weight: 500; }
-
-/* ?????? - ?? Next.js */
-.binding-card { background: rgba(28, 28, 30, 0.8); backdrop-filter: blur(40rpx); border: 2rpx solid rgba(255,255,255,0.1); border-radius: 32rpx; overflow: hidden; margin-bottom: 24rpx; width: 100%; box-sizing: border-box; }
-.binding-header { display: flex; align-items: center; justify-content: space-between; padding: 28rpx 32rpx; border-bottom: 2rpx solid rgba(255,255,255,0.05); }
-.binding-title { display: flex; align-items: center; gap: 12rpx; }
-.binding-icon-img { width: 40rpx; height: 40rpx; display: block; filter: brightness(0) saturate(100%) invert(72%) sepia(54%) saturate(2933%) hue-rotate(134deg) brightness(101%) contrast(101%); }
-.title-text { font-size: 30rpx; font-weight: 600; color: #fff; }
-.binding-count { font-size: 26rpx; color: rgba(255,255,255,0.5); }
-.toggle-icon { font-size: 24rpx; color: rgba(255,255,255,0.5); }
-
-/* Tab?? */
-.binding-tabs { display: flex; border-bottom: 2rpx solid rgba(255,255,255,0.05); }
-.tab-item { flex: 1; padding: 24rpx 0; text-align: center; font-size: 26rpx; color: rgba(255,255,255,0.5); position: relative; }
-.tab-item.tab-active { color: #00CED1; }
-.tab-item.tab-active::after { content: ''; position: absolute; bottom: 0; left: 50%; transform: translateX(-50%); width: 80rpx; height: 4rpx; background: #00CED1; border-radius: 4rpx; }
-
-/* ???? */
-.binding-list { max-height: 640rpx; overflow-y: auto; }
-.empty-state { padding: 80rpx 0; text-align: center; }
-.empty-icon { font-size: 64rpx; display: block; margin-bottom: 16rpx; }
-.empty-text { font-size: 26rpx; color: rgba(255,255,255,0.5); }
-
-.binding-item { display: flex; align-items: center; padding: 24rpx 32rpx; border-bottom: 2rpx solid rgba(255,255,255,0.05); }
-.binding-item:last-child { border-bottom: none; }
-.user-avatar { width: 80rpx; height: 80rpx; border-radius: 50%; background: rgba(0,206,209,0.2); display: flex; align-items: center; justify-content: center; font-size: 28rpx; font-weight: 600; color: #00CED1; margin-right: 24rpx; flex-shrink: 0; }
-.user-avatar.avatar-converted { background: rgba(76,175,80,0.2); color: #4CAF50; }
-.user-avatar.avatar-expired { background: rgba(158,158,158,0.2); color: #9E9E9E; }
-.user-info { flex: 1; }
-.user-name { font-size: 28rpx; color: #fff; font-weight: 500; display: block; }
-.user-time { font-size: 22rpx; color: rgba(255,255,255,0.5); margin-top: 4rpx; display: block; }
-.user-status { text-align: right; }
-.status-amount { font-size: 28rpx; color: #4CAF50; font-weight: 600; display: block; }
-.status-order { font-size: 22rpx; color: rgba(255,255,255,0.5); }
-.status-tag { font-size: 22rpx; padding: 8rpx 16rpx; border-radius: 16rpx; }
-.status-tag.tag-green { background: rgba(76,175,80,0.2); color: #4CAF50; }
-.status-tag.tag-orange { background: rgba(255,165,0,0.2); color: #FFA500; }
-.status-tag.tag-red { background: rgba(244,67,54,0.2); color: #F44336; }
-
-/* ????? - ?? Next.js */
-.invite-card { background: rgba(28, 28, 30, 0.8); backdrop-filter: blur(40rpx); border: 2rpx solid rgba(255,255,255,0.1); border-radius: 32rpx; padding: 40rpx; margin-bottom: 24rpx; width: 100%; box-sizing: border-box; }
-.invite-header { display: flex; align-items: center; justify-content: space-between; margin-bottom: 24rpx; }
-.invite-title { font-size: 30rpx; font-weight: 600; color: #fff; }
-.invite-code-box { background: rgba(0,206,209,0.2); padding: 12rpx 24rpx; border-radius: 16rpx; }
-.invite-code { font-size: 28rpx; font-weight: 600; color: #00CED1; font-family: 'Courier New', monospace; letter-spacing: 2rpx; }
-.invite-tip { font-size: 24rpx; color: rgba(255,255,255,0.6); line-height: 1.5; display: block; }
-.invite-tip .gold { color: #FFD700; }
-.invite-tip .brand { color: #00CED1; }
-
-/* ?????? - ?? Next.js */
-.earnings-detail-card { background: rgba(28, 28, 30, 0.8); backdrop-filter: blur(40rpx); border: 2rpx solid rgba(255,255,255,0.1); border-radius: 32rpx; overflow: hidden; margin-bottom: 24rpx; width: 100%; box-sizing: border-box; }
-.detail-header { padding: 40rpx 40rpx 24rpx; border-bottom: 2rpx solid rgba(255,255,255,0.05); }
-.detail-title { font-size: 30rpx; font-weight: 600; color: #fff; }
-.detail-list { max-height: 480rpx; overflow-y: auto; }
-.detail-item { display: flex; align-items: center; justify-content: space-between; padding: 32rpx 40rpx; border-bottom: 2rpx solid rgba(255,255,255,0.05); }
-.detail-item:last-child { border-bottom: none; }
-.detail-left { display: flex; align-items: center; gap: 24rpx; flex: 1; }
-.detail-icon { width: 80rpx; height: 80rpx; border-radius: 20rpx; background: rgba(0,206,209,0.2); display: flex; align-items: center; justify-content: center; flex-shrink: 0; }
-.icon-gift { width: 40rpx; height: 40rpx; display: block; filter: brightness(0) saturate(100%) invert(72%) sepia(54%) saturate(2933%) hue-rotate(134deg) brightness(101%) contrast(101%); }
-.detail-info { flex: 1; }
-.detail-type { font-size: 28rpx; color: #fff; font-weight: 500; display: block; }
-.detail-time { font-size: 24rpx; color: rgba(255,255,255,0.5); margin-top: 4rpx; display: block; }
-.detail-amount { font-size: 30rpx; font-weight: 600; color: #00CED1; }
-
-/* ???? - ?? Next.js */
-.share-section { display: flex; flex-direction: column; gap: 12rpx; width: 100%; margin-bottom: 24rpx; }
-.share-item { display: flex; align-items: center; background: rgba(28, 28, 30, 0.8); backdrop-filter: blur(40rpx); border: 2rpx solid rgba(255,255,255,0.1); border-radius: 24rpx; padding: 32rpx; border: none; margin: 0; text-align: left; width: 100%; box-sizing: border-box; }
-.share-item::after { border: none; }
-.share-icon { width: 96rpx; height: 96rpx; border-radius: 20rpx; display: flex; align-items: center; justify-content: center; margin-right: 24rpx; flex-shrink: 0; }
-.share-icon.poster { background: rgba(103,58,183,0.2); }
-.share-icon.wechat { background: rgba(7,193,96,0.2); }
-.share-icon.link { background: rgba(158,158,158,0.2); }
-.icon-share-btn { width: 48rpx; height: 48rpx; display: block; }
-.share-icon.poster .icon-share-btn { filter: brightness(0) saturate(100%) invert(37%) sepia(73%) saturate(2296%) hue-rotate(252deg) brightness(96%) contrast(92%); }
-.share-icon.wechat .icon-share-btn { filter: brightness(0) saturate(100%) invert(58%) sepia(91%) saturate(1255%) hue-rotate(105deg) brightness(96%) contrast(97%); }
-.share-icon.link .icon-share-btn { filter: brightness(0) saturate(100%) invert(60%) sepia(0%) saturate(0%) hue-rotate(0deg) brightness(95%) contrast(85%); }
-.share-info { flex: 1; text-align: left; }
-.share-title { font-size: 28rpx; color: #fff; font-weight: 500; display: block; text-align: left; }
-.share-desc { font-size: 22rpx; color: rgba(255,255,255,0.5); margin-top: 4rpx; display: block; text-align: left; }
-.share-arrow-icon { width: 40rpx; height: 40rpx; display: block; flex-shrink: 0; filter: brightness(0) saturate(100%) invert(60%) sepia(0%) saturate(0%) hue-rotate(0deg) brightness(95%) contrast(85%); }
-.share-btn-wechat { line-height: normal; font-size: inherit; padding: 24rpx 32rpx !important; margin: 0 !important; width: 100% !important; }
-
-/* ?????????????? + ???? + ???????? */
-/* ???????? backdrop-filter??????????????? */
-.modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,0.8); display: flex; align-items: center; justify-content: center; z-index: 1000; padding: 32rpx; box-sizing: border-box; }
-
-.poster-dialog { width: 686rpx; border-radius: 24rpx; overflow: hidden; position: relative; background: transparent; }
-.poster-close { position: absolute; top: 20rpx; right: 20rpx; width: 56rpx; height: 56rpx; border-radius: 28rpx; background: rgba(0,0,0,0.25); color: rgba(255,255,255,0.9); display: flex; align-items: center; justify-content: center; z-index: 5; font-size: 28rpx; }
-
-/* ???? */
-.poster-card { position: relative; background: linear-gradient(135deg, #0a1628 0%, #0f2137 50%, #1a3a5c 100%); color: #fff; padding: 44rpx 40rpx 36rpx; }
-.poster-inner { position: relative; z-index: 2; display: flex; flex-direction: column; align-items: center; text-align: center; }
-
-/* ???? */
-/* ???????? filter: blur ??????????????? + ???? */
-.poster-glow { position: absolute; width: 320rpx; height: 320rpx; border-radius: 50%; opacity: 0.6; z-index: 1; }
-.poster-glow-left { top: -120rpx; left: -160rpx; background: rgba(0,206,209,0.12); box-shadow: 0 0 140rpx 40rpx rgba(0,206,209,0.18); }
-.poster-glow-right { bottom: -140rpx; right: -160rpx; background: rgba(255,215,0,0.10); box-shadow: 0 0 160rpx 50rpx rgba(255,215,0,0.14); }
-.poster-ring { position: absolute; width: 520rpx; height: 520rpx; border-radius: 50%; border: 2rpx solid rgba(0,206,209,0.06); left: 50%; top: 50%; transform: translate(-50%, -50%); z-index: 1; }
-
-/* ???? */
-.poster-badges { display: flex; gap: 16rpx; margin-bottom: 24rpx; }
-.poster-badge { padding: 10rpx 22rpx; font-size: 20rpx; font-weight: 700; border-radius: 999rpx; border: 2rpx solid transparent; }
-.poster-badge-gold { background: rgba(255,215,0,0.18); color: #FFD700; border-color: rgba(255,215,0,0.28); }
-.poster-badge-brand { background: rgba(0,206,209,0.18); color: #00CED1; border-color: rgba(0,206,209,0.28); }
-
-/* ?? */
-.poster-title { margin-bottom: 8rpx; }
-.poster-title-line1 { display: block; font-size: 44rpx; font-weight: 900; line-height: 1.1; color: #00CED1; }
-.poster-title-line2 { display: block; font-size: 44rpx; font-weight: 900; line-height: 1.1; color: #fff; margin-top: 6rpx; }
-.poster-subtitle { display: block; font-size: 22rpx; color: rgba(255,255,255,0.6); margin-bottom: 28rpx; }
-
-/* ???? */
-.poster-stats { width: 100%; display: flex; gap: 16rpx; justify-content: center; margin-bottom: 24rpx; }
-.poster-stat { flex: 1; max-width: 190rpx; background: rgba(255,255,255,0.05); border: 2rpx solid rgba(255,255,255,0.10); border-radius: 16rpx; padding: 18rpx 10rpx; }
-.poster-stat-value { display: block; font-size: 44rpx; font-weight: 900; line-height: 1; }
-.poster-stat-label { display: block; font-size: 20rpx; color: rgba(255,255,255,0.5); margin-top: 8rpx; }
-.poster-stat-gold { color: #FFD700; }
-.poster-stat-brand { color: #00CED1; }
-.poster-stat-pink { color: #E91E63; }
-
-/* ?? */
-.poster-tags { display: flex; flex-wrap: wrap; justify-content: center; gap: 10rpx; margin: 0 24rpx 26rpx; }
-.poster-tag { font-size: 20rpx; color: rgba(255,255,255,0.7); background: rgba(255,255,255,0.05); border: 2rpx solid rgba(255,255,255,0.10); border-radius: 12rpx; padding: 6rpx 14rpx; }
-
-/* ??? */
-.poster-recommender { display: flex; align-items: center; gap: 12rpx; background: rgba(0,206,209,0.10); border: 2rpx solid rgba(0,206,209,0.20); border-radius: 999rpx; padding: 12rpx 22rpx; margin-bottom: 22rpx; }
-.poster-avatar { width: 44rpx; height: 44rpx; border-radius: 22rpx; background: rgba(0,206,209,0.30); display: flex; align-items: center; justify-content: center; }
-.poster-avatar-text { font-size: 20rpx; font-weight: 800; color: #00CED1; }
-.poster-recommender-text { font-size: 22rpx; color: #00CED1; }
-
-/* ??? */
-.poster-discount { width: 100%; background: linear-gradient(90deg, rgba(255,215,0,0.10) 0%, rgba(233,30,99,0.10) 100%); border: 2rpx solid rgba(255,215,0,0.20); border-radius: 18rpx; padding: 18rpx 18rpx; margin-bottom: 26rpx; }
-.poster-discount-text { font-size: 22rpx; color: rgba(255,255,255,0.80); }
-.poster-discount-highlight { color: #00CED1; font-weight: 800; }
-
-/* ??? */
-.poster-qr-wrap { background: #fff; padding: 14rpx; border-radius: 16rpx; margin-bottom: 12rpx; box-shadow: 0 16rpx 40rpx rgba(0,0,0,0.35); }
-.poster-qr-img { width: 240rpx; height: 240rpx; display: block; }
-.poster-qr-tip { font-size: 20rpx; color: rgba(255,255,255,0.40); margin-bottom: 8rpx; }
-.poster-code { font-size: 22rpx; font-family: monospace; letter-spacing: 2rpx; color: rgba(0,206,209,0.80); }
-
-/* ??????? */
-.poster-footer { background: #fff; padding: 28rpx 28rpx 32rpx; display: flex; flex-direction: column; gap: 18rpx; }
-.poster-footer-tip { font-size: 22rpx; color: rgba(0,0,0,0.55); text-align: center; }
-.poster-footer-btn { height: 72rpx; border-radius: 18rpx; border: 2rpx solid rgba(0,0,0,0.15); display: flex; align-items: center; justify-content: center; font-size: 28rpx; color: rgba(0,0,0,0.75); background: #fff; }
-
-
-
-/* ????- ?? Next.js */
-.empty-earnings { background: rgba(28, 28, 30, 0.8); backdrop-filter: blur(40rpx); border: 2rpx solid rgba(255,255,255,0.1); border-radius: 32rpx; padding: 64rpx 40rpx; text-align: center; margin-bottom: 24rpx; }
-.empty-icon-wrapper { width: 128rpx; height: 128rpx; border-radius: 50%; background: rgba(28, 28, 30, 0.8); display: flex; align-items: center; justify-content: center; margin: 0 auto 32rpx; }
-.empty-gift-icon { width: 64rpx; height: 64rpx; display: block; filter: brightness(0) saturate(100%) invert(60%) sepia(0%) saturate(0%) hue-rotate(0deg) brightness(95%) contrast(85%); }
-.empty-title { font-size: 30rpx; font-weight: 500; color: #fff; display: block; margin-bottom: 16rpx; }
-.empty-desc { font-size: 26rpx; color: rgba(255,255,255,0.6); display: block; line-height: 1.5; }
-
-
- / * = = = = = TrGm5 ? = = = = = * /
- . l o a d i n g - o v e r l a y {
- p o s i t i o n : f i x e d ;
- t o p : 0 ;
- l e f t : 0 ;
- r i g h t : 0 ;
- b o t t o m : 0 ;
- b a c k g r o u n d : r g b a ( 0 , 0 , 0 , 0 . 7 ) ;
- b a c k d r o p - f i l t e r : b l u r ( 1 0 r p x ) ;
- z - i n d e x : 9 9 9 ;
- d i s p l a y : f l e x ;
- a l i g n - i t e m s : c e n t e r ;
- j u s t i f y - c o n t e n t : c e n t e r ;
- }
-
- . l o a d i n g - c o n t e n t {
- d i s p l a y : f l e x ;
- f l e x - d i r e c t i o n : c o l u m n ;
- a l i g n - i t e m s : c e n t e r ;
- g a p : 2 4 r p x ;
- }
-
- . l o a d i n g - s p i n n e r {
- w i d t h : 8 0 r p x ;
- h e i g h t : 8 0 r p x ;
- b o r d e r : 6 r p x s o l i d r g b a ( 5 6 , 1 8 9 , 1 7 2 , 0 . 2 ) ;
- b o r d e r - t o p - c o l o r : # 3 8 b d a c ;
- b o r d e r - r a d i u s : 5 0 % ;
- a n i m a t i o n : s p i n 1 s l i n e a r i n f i n i t e ;
- }
-
- @ k e y f r a m e s s p i n {
- 0 % { t r a n s f o r m : r o t a t e ( 0 d e g ) ; }
- 1 0 0 % { t r a n s f o r m : r o t a t e ( 3 6 0 d e g ) ; }
- }
-
- . l o a d i n g - t e x t {
- f o n t - s i z e : 2 8 r p x ;
- c o l o r : r g b a ( 2 5 5 , 2 5 5 , 2 5 5 , 0 . 8 ) ;
- f o n t - w e i g h t : 5 0 0 ;
- }
-
- . c o n t e n t - l o a d i n g {
- o p a c i t y : 0 . 3 ;
- p o i n t e r - e v e n t s : n o n e ;
- }
-
- / * = = = = = ARlē^|op]͓\!} = = = = = * /
- . d e t a i l - i t e m {
- d i s p l a y : f l e x ;
- a l i g n - i t e m s : c e n t e r ;
- g a p : 2 4 r p x ;
- p a d d i n g : 2 4 r p x ;
- b a c k g r o u n d : r g b a ( 2 5 5 , 2 5 5 , 2 5 5 , 0 . 0 2 ) ;
- b o r d e r - r a d i u s : 1 6 r p x ;
- m a r g i n - b o t t o m : 1 6 r p x ;
- t r a n s i t i o n : a l l 0 . 3 s ;
- }
-
- . d e t a i l - i t e m : a c t i v e {
- b a c k g r o u n d : r g b a ( 2 5 5 , 2 5 5 , 2 5 5 , 0 . 0 5 ) ;
- }
-
- . d e t a i l - a v a t a r - w r a p {
- f l e x - s h r i n k : 0 ;
- }
-
- . d e t a i l - a v a t a r {
- w i d t h : 8 8 r p x ;
- h e i g h t : 8 8 r p x ;
- b o r d e r - r a d i u s : 5 0 % ;
- b o r d e r : 2 r p x s o l i d r g b a ( 5 6 , 1 8 9 , 1 7 2 , 0 . 2 ) ;
- }
-
- . d e t a i l - a v a t a r - t e x t {
- w i d t h : 8 8 r p x ;
- h e i g h t : 8 8 r p x ;
- b o r d e r - r a d i u s : 5 0 % ;
- b a c k g r o u n d : l i n e a r - g r a d i e n t ( 1 3 5 d e g , # 3 8 b d a c 0 % , # 2 d a 3 9 6 1 0 0 % ) ;
- d i s p l a y : f l e x ;
- a l i g n - i t e m s : c e n t e r ;
- j u s t i f y - c o n t e n t : c e n t e r ;
- f o n t - s i z e : 3 6 r p x ;
- f o n t - w e i g h t : 7 0 0 ;
- c o l o r : # f f f f f f ;
- }
-
- . d e t a i l - c o n t e n t {
- f l e x : 1 ;
- d i s p l a y : f l e x ;
- f l e x - d i r e c t i o n : c o l u m n ;
- g a p : 8 r p x ;
- m i n - w i d t h : 0 ;
- }
-
- . d e t a i l - t o p {
- d i s p l a y : f l e x ;
- a l i g n - i t e m s : c e n t e r ;
- j u s t i f y - c o n t e n t : s p a c e - b e t w e e n ;
- g a p : 1 6 r p x ;
- }
-
- . d e t a i l - b u y e r {
- f o n t - s i z e : 2 8 r p x ;
- f o n t - w e i g h t : 5 0 0 ;
- c o l o r : # f f f f f f ;
- f l e x - s h r i n k : 0 ;
- }
-
- . d e t a i l - a m o u n t {
- f o n t - s i z e : 3 2 r p x ;
- f o n t - w e i g h t : 7 0 0 ;
- c o l o r : # 3 8 b d a c ;
- f l e x - s h r i n k : 0 ;
- }
-
- . d e t a i l - p r o d u c t {
- d i s p l a y : f l e x ;
- a l i g n - i t e m s : c e n t e r ;
- f o n t - s i z e : 2 4 r p x ;
- c o l o r : r g b a ( 2 5 5 , 2 5 5 , 2 5 5 , 0 . 6 ) ;
- o v e r f l o w : h i d d e n ;
- t e x t - o v e r f l o w : e l l i p s i s ;
- w h i t e - s p a c e : n o w r a p ;
- }
-
- . d e t a i l - b o o k {
- c o l o r : r g b a ( 2 5 5 , 2 5 5 , 2 5 5 , 0 . 7 ) ;
- f o n t - w e i g h t : 5 0 0 ;
- f l e x - s h r i n k : 0 ;
- }
-
- . d e t a i l - c h a p t e r {
- c o l o r : r g b a ( 2 5 5 , 2 5 5 , 2 5 5 , 0 . 5 ) ;
- o v e r f l o w : h i d d e n ;
- t e x t - o v e r f l o w : e l l i p s i s ;
- w h i t e - s p a c e : n o w r a p ;
- }
-
- . d e t a i l - t i m e {
- f o n t - s i z e : 2 2 r p x ;
- c o l o r : r g b a ( 2 5 5 , 2 5 5 , 2 5 5 , 0 . 4 ) ;
- }
-
- / * = = = ARlē^|͓\!}m2 = = = * /
- . d e t a i l - i t e m { d i s p l a y : f l e x ; a l i g n - i t e m s : c e n t e r ; g a p : 2 4 r p x ; p a d d i n g : 2 4 r p x ; b a c k g r o u n d : r g b a ( 2 5 5 , 2 5 5 , 2 5 5 , 0 . 0 2 ) ; b o r d e r - r a d i u s : 1 6 r p x ; m a r g i n - b o t t o m : 1 6 r p x ; }
- . d e t a i l - a v a t a r - w r a p { f l e x - s h r i n k : 0 ; }
- . d e t a i l - a v a t a r { w i d t h : 8 8 r p x ; h e i g h t : 8 8 r p x ; b o r d e r - r a d i u s : 5 0 % ; }
- . d e t a i l - a v a t a r - t e x t { w i d t h : 8 8 r p x ; h e i g h t : 8 8 r p x ; b o r d e r - r a d i u s : 5 0 % ; b a c k g r o u n d : l i n e a r - g r a d i e n t ( 1 3 5 d e g , # 3 8 b d a c 0 % , # 2 d a 3 9 6 1 0 0 % ) ; d i s p l a y : f l e x ; a l i g n - i t e m s : c e n t e r ; j u s t i f y - c o n t e n t : c e n t e r ; f o n t - s i z e : 3 6 r p x ; f o n t - w e i g h t : 7 0 0 ; c o l o r : # f f f f f f ; }
- . d e t a i l - c o n t e n t { f l e x : 1 ; d i s p l a y : f l e x ; f l e x - d i r e c t i o n : c o l u m n ; g a p : 8 r p x ; m i n - w i d t h : 0 ; o v e r f l o w : h i d d e n ; }
- . d e t a i l - t o p { d i s p l a y : f l e x ; a l i g n - i t e m s : c e n t e r ; j u s t i f y - c o n t e n t : s p a c e - b e t w e e n ; g a p : 1 6 r p x ; }
- . d e t a i l - b u y e r { f o n t - s i z e : 2 8 r p x ; f o n t - w e i g h t : 5 0 0 ; c o l o r : # f f f f f f ; f l e x : 1 ; o v e r f l o w : h i d d e n ; t e x t - o v e r f l o w : e l l i p s i s ; w h i t e - s p a c e : n o w r a p ; }
- . d e t a i l - a m o u n t { f o n t - s i z e : 3 2 r p x ; f o n t - w e i g h t : 7 0 0 ; c o l o r : # 3 8 b d a c ; f l e x - s h r i n k : 0 ; }
- . d e t a i l - p r o d u c t { d i s p l a y : f l e x ; a l i g n - i t e m s : b a s e l i n e ; f o n t - s i z e : 2 4 r p x ; c o l o r : r g b a ( 2 5 5 , 2 5 5 , 2 5 5 , 0 . 6 ) ; o v e r f l o w : h i d d e n ; }
- . d e t a i l - b o o k { c o l o r : r g b a ( 2 5 5 , 2 5 5 , 2 5 5 , 0 . 7 ) ; f o n t - w e i g h t : 5 0 0 ; m a x - w i d t h : 2 0 0 r p x ; o v e r f l o w : h i d d e n ; t e x t - o v e r f l o w : e l l i p s i s ; w h i t e - s p a c e : n o w r a p ; f l e x - s h r i n k : 1 ; }
- . d e t a i l - c h a p t e r { c o l o r : r g b a ( 2 5 5 , 2 5 5 , 2 5 5 , 0 . 5 ) ; f l e x : 1 ; o v e r f l o w : h i d d e n ; t e x t - o v e r f l o w : e l l i p s i s ; w h i t e - s p a c e : n o w r a p ; m a r g i n - l e f t : 4 r p x ; }
- . d e t a i l - t i m e { f o n t - s i z e : 2 2 r p x ; c o l o r : r g b a ( 2 5 5 , 2 5 5 , 2 5 5 , 0 . 4 ) ; }
-
-
\ No newline at end of file
diff --git a/归档/miniprogram/pages/search/search.js b/归档/miniprogram/pages/search/search.js
deleted file mode 100644
index 1ac887d9..00000000
--- a/归档/miniprogram/pages/search/search.js
+++ /dev/null
@@ -1,109 +0,0 @@
-/**
- * Soul创业派对 - 章节搜索页
- * 搜索章节标题和内容
- */
-const app = getApp()
-
-Page({
- data: {
- statusBarHeight: 44,
- keyword: '',
- results: [],
- loading: false,
- searched: false,
- total: 0,
- // 热门搜索关键词
- hotKeywords: ['私域', '电商', '流量', '赚钱', '创业', 'Soul', '抖音', '变现'],
- // 热门章节推荐
- hotChapters: [
- { id: '1.1', title: '荷包:电动车出租的被动收入模式', tag: '免费', part: '真实的人' },
- { id: '9.12', title: '美业整合:一个人的公司如何月入十万', tag: '热门', part: '真实的赚钱' },
- { id: '3.1', title: '3000万流水如何跑出来', tag: '热门', part: '真实的行业' },
- { id: '8.1', title: '流量杠杆:抖音、Soul、飞书', tag: '推荐', part: '真实的赚钱' },
- { id: '9.13', title: 'AI工具推广:一个隐藏的高利润赛道', tag: '最新', part: '真实的赚钱' }
- ]
- },
-
- onLoad() {
- this.setData({
- statusBarHeight: app.globalData.statusBarHeight || 44
- })
- // 加载热门章节
- this.loadHotChapters()
- },
-
- // 加载热门章节(从服务器获取点击量高的章节)
- async loadHotChapters() {
- try {
- const res = await app.request('/api/miniprogram/book/hot')
- if (res && res.success && res.chapters?.length > 0) {
- this.setData({ hotChapters: res.chapters })
- }
- } catch (e) {
- console.log('加载热门章节失败,使用默认数据')
- }
- },
-
- // 输入关键词
- onInput(e) {
- this.setData({ keyword: e.detail.value })
- },
-
- // 清空搜索
- clearSearch() {
- this.setData({
- keyword: '',
- results: [],
- searched: false,
- total: 0
- })
- },
-
- // 点击热门关键词
- onHotKeyword(e) {
- const keyword = e.currentTarget.dataset.keyword
- this.setData({ keyword })
- this.doSearch()
- },
-
- // 执行搜索
- async doSearch() {
- const { keyword } = this.data
- if (!keyword || keyword.trim().length < 1) {
- wx.showToast({ title: '请输入搜索关键词', icon: 'none' })
- return
- }
-
- this.setData({ loading: true, searched: true })
-
- try {
- const res = await app.request(`/api/miniprogram/book/search?q=${encodeURIComponent(keyword.trim())}`)
-
- if (res && res.success) {
- this.setData({
- results: res.results || [],
- total: res.total || 0
- })
- } else {
- this.setData({ results: [], total: 0 })
- }
- } catch (e) {
- console.error('搜索失败:', e)
- wx.showToast({ title: '搜索失败', icon: 'none' })
- this.setData({ results: [], total: 0 })
- } finally {
- this.setData({ loading: false })
- }
- },
-
- // 跳转阅读
- goToRead(e) {
- const id = e.currentTarget.dataset.id
- wx.navigateTo({ url: `/pages/read/read?id=${id}` })
- },
-
- // 返回上一页
- goBack() {
- wx.navigateBack()
- }
-})
diff --git a/归档/miniprogram/pages/search/search.json b/归档/miniprogram/pages/search/search.json
deleted file mode 100644
index 877955df..00000000
--- a/归档/miniprogram/pages/search/search.json
+++ /dev/null
@@ -1,5 +0,0 @@
-{
- "usingComponents": {},
- "navigationStyle": "custom",
- "navigationBarTitleText": "搜索"
-}
diff --git a/归档/miniprogram/pages/search/search.wxml b/归档/miniprogram/pages/search/search.wxml
deleted file mode 100644
index 60fa91fe..00000000
--- a/归档/miniprogram/pages/search/search.wxml
+++ /dev/null
@@ -1,113 +0,0 @@
-
-
-
-
-
-
-
- ←
-
-
- 🔍
-
- ×
-
- 搜索
-
-
-
-
-
-
-
-
- 热门搜索
-
- {{item}}
-
-
-
-
-
- 热门章节
-
-
- {{index + 1}}
-
- {{item.title}}
- {{item.part}}
-
- {{item.tag}}
-
-
-
-
-
-
-
-
-
- 搜索中...
-
-
-
-
-
-
-
-
-
- {{item.title}}
- {{item.part}}
-
- {{item.matchedContent}}
-
- →
-
-
-
-
-
-
- 🔍
- 未找到相关章节
- 换个关键词试试
-
-
-
-
diff --git a/归档/miniprogram/pages/search/search.wxss b/归档/miniprogram/pages/search/search.wxss
deleted file mode 100644
index aa56b19b..00000000
--- a/归档/miniprogram/pages/search/search.wxss
+++ /dev/null
@@ -1,335 +0,0 @@
-/* 章节搜索页样式 */
-.page {
- min-height: 100vh;
- background: linear-gradient(180deg, #0a0a0a 0%, #111111 100%);
-}
-
-/* 导航栏 */
-.nav-bar {
- position: fixed;
- top: 0;
- left: 0;
- right: 0;
- z-index: 100;
- background: rgba(10, 10, 10, 0.95);
- backdrop-filter: blur(20px);
- -webkit-backdrop-filter: blur(20px);
-}
-
-.nav-content {
- display: flex;
- align-items: center;
- padding: 8rpx 24rpx;
- height: 88rpx;
-}
-
-.back-btn {
- width: 60rpx;
- height: 60rpx;
- display: flex;
- align-items: center;
- justify-content: center;
-}
-
-.back-icon {
- font-size: 40rpx;
- color: #00CED1;
-}
-
-.search-input-wrap {
- flex: 1;
- display: flex;
- align-items: center;
- background: rgba(255,255,255,0.08);
- border-radius: 40rpx;
- padding: 0 24rpx;
- height: 64rpx;
- margin: 0 16rpx;
-}
-
-.search-icon-small {
- font-size: 28rpx;
- margin-right: 12rpx;
-}
-
-.search-input {
- flex: 1;
- font-size: 28rpx;
- color: #fff;
-}
-
-.search-input::placeholder {
- color: rgba(255,255,255,0.4);
-}
-
-.clear-btn {
- width: 40rpx;
- height: 40rpx;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 32rpx;
- color: rgba(255,255,255,0.5);
-}
-
-.search-btn {
- font-size: 28rpx;
- color: #00CED1;
- padding: 0 16rpx;
-}
-
-/* 主内容 */
-.main-content {
- padding: 24rpx;
-}
-
-/* 热门搜索 */
-.hot-section {
- padding: 24rpx 0;
-}
-
-.section-title {
- font-size: 28rpx;
- color: rgba(255,255,255,0.6);
- margin-bottom: 24rpx;
- display: block;
-}
-
-.hot-tags {
- display: flex;
- flex-wrap: wrap;
- gap: 20rpx;
-}
-
-.hot-tag {
- background: rgba(0, 206, 209, 0.15);
- color: #00CED1;
- padding: 16rpx 32rpx;
- border-radius: 32rpx;
- font-size: 28rpx;
- border: 1rpx solid rgba(0, 206, 209, 0.3);
-}
-
-/* 热门章节 */
-.hot-chapters {
- padding: 32rpx 0;
- margin-top: 16rpx;
-}
-
-.chapter-list {
- display: flex;
- flex-direction: column;
- gap: 16rpx;
-}
-
-.chapter-item {
- display: flex;
- align-items: center;
- background: rgba(255,255,255,0.05);
- border-radius: 20rpx;
- padding: 24rpx;
- gap: 20rpx;
-}
-
-.chapter-rank {
- width: 48rpx;
- height: 48rpx;
- background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%);
- border-radius: 50%;
- display: flex;
- align-items: center;
- justify-content: center;
- font-size: 26rpx;
- font-weight: 600;
- color: #000;
- flex-shrink: 0;
-}
-
-.chapter-item:nth-child(1) .chapter-rank { background: linear-gradient(135deg, #FFD700 0%, #FFA500 100%); }
-.chapter-item:nth-child(2) .chapter-rank { background: linear-gradient(135deg, #C0C0C0 0%, #A9A9A9 100%); }
-.chapter-item:nth-child(3) .chapter-rank { background: linear-gradient(135deg, #CD7F32 0%, #8B4513 100%); }
-
-.chapter-info {
- flex: 1;
- min-width: 0;
-}
-
-.chapter-title {
- font-size: 28rpx;
- color: #fff;
- display: block;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
-}
-
-.chapter-part {
- font-size: 22rpx;
- color: rgba(255,255,255,0.4);
- margin-top: 6rpx;
- display: block;
-}
-
-.chapter-tag {
- padding: 8rpx 16rpx;
- border-radius: 16rpx;
- font-size: 22rpx;
- flex-shrink: 0;
-}
-
-.chapter-tag.tag-free { background: rgba(76, 175, 80, 0.2); color: #4CAF50; }
-.chapter-tag.tag-hot { background: rgba(255, 87, 34, 0.2); color: #FF5722; }
-.chapter-tag.tag-new { background: rgba(233, 30, 99, 0.2); color: #E91E63; }
-
-/* 搜索结果 */
-.results-section {
- padding: 16rpx 0;
-}
-
-.results-header {
- margin-bottom: 24rpx;
-}
-
-.results-count {
- font-size: 26rpx;
- color: rgba(255,255,255,0.5);
-}
-
-.results-list {
- display: flex;
- flex-direction: column;
- gap: 24rpx;
-}
-
-.result-item {
- background: rgba(255,255,255,0.05);
- border-radius: 24rpx;
- padding: 28rpx;
- position: relative;
- border: 1rpx solid rgba(255,255,255,0.08);
-}
-
-.result-header {
- display: flex;
- align-items: center;
- justify-content: space-between;
- margin-bottom: 16rpx;
-}
-
-.result-chapter {
- font-size: 24rpx;
- color: #00CED1;
- font-weight: 500;
-}
-
-.result-tags {
- display: flex;
- gap: 12rpx;
-}
-
-.tag {
- font-size: 20rpx;
- padding: 6rpx 16rpx;
- border-radius: 20rpx;
-}
-
-.tag-match {
- background: rgba(147, 112, 219, 0.2);
- color: #9370DB;
-}
-
-.tag-free {
- background: rgba(76, 175, 80, 0.2);
- color: #4CAF50;
-}
-
-.result-title {
- font-size: 30rpx;
- color: #fff;
- font-weight: 500;
- line-height: 1.5;
- display: block;
- margin-bottom: 8rpx;
-}
-
-.result-part {
- font-size: 24rpx;
- color: rgba(255,255,255,0.5);
- display: block;
-}
-
-.result-content {
- margin-top: 16rpx;
- padding-top: 16rpx;
- border-top: 1rpx solid rgba(255,255,255,0.1);
-}
-
-.content-preview {
- font-size: 24rpx;
- color: rgba(255,255,255,0.6);
- line-height: 1.6;
- display: -webkit-box;
- -webkit-line-clamp: 3;
- -webkit-box-orient: vertical;
- overflow: hidden;
-}
-
-.result-arrow {
- position: absolute;
- right: 28rpx;
- top: 50%;
- transform: translateY(-50%);
- font-size: 32rpx;
- color: rgba(255,255,255,0.3);
-}
-
-/* 加载状态 */
-.loading-wrap {
- display: flex;
- flex-direction: column;
- align-items: center;
- padding: 100rpx 0;
-}
-
-.loading-spinner {
- width: 60rpx;
- height: 60rpx;
- border: 4rpx solid rgba(0, 206, 209, 0.3);
- border-top-color: #00CED1;
- border-radius: 50%;
- animation: spin 1s linear infinite;
-}
-
-@keyframes spin {
- to { transform: rotate(360deg); }
-}
-
-.loading-text {
- margin-top: 24rpx;
- font-size: 28rpx;
- color: rgba(255,255,255,0.5);
-}
-
-/* 空状态 */
-.empty-wrap {
- display: flex;
- flex-direction: column;
- align-items: center;
- padding: 100rpx 0;
-}
-
-.empty-icon {
- font-size: 80rpx;
- margin-bottom: 24rpx;
-}
-
-.empty-text {
- font-size: 32rpx;
- color: rgba(255,255,255,0.6);
- margin-bottom: 12rpx;
-}
-
-.empty-hint {
- font-size: 26rpx;
- color: rgba(255,255,255,0.4);
-}
diff --git a/归档/miniprogram/pages/settings/settings.js b/归档/miniprogram/pages/settings/settings.js
deleted file mode 100644
index 157af2f0..00000000
--- a/归档/miniprogram/pages/settings/settings.js
+++ /dev/null
@@ -1,497 +0,0 @@
-/**
- * Soul创业派对 - 设置页
- * 账号绑定功能
- */
-const app = getApp()
-
-Page({
- data: {
- statusBarHeight: 44,
- isLoggedIn: false,
- userInfo: null,
- version: '1.0.0',
-
- // 绑定信息
- phoneNumber: '',
- wechatId: '',
- alipayAccount: '',
- address: '',
-
- // 自动提现(默认开启)
- autoWithdrawEnabled: true,
-
- // 绑定弹窗
- showBindModal: false,
- bindType: '', // phone | wechat | alipay
- bindValue: ''
- },
-
- onLoad() {
- this.setData({
- statusBarHeight: app.globalData.statusBarHeight,
- isLoggedIn: app.globalData.isLoggedIn,
- userInfo: app.globalData.userInfo
- })
- this.loadBindingInfo()
- },
-
- onShow() {
- this.loadBindingInfo()
- },
-
- // 加载绑定信息
- loadBindingInfo() {
- const { userInfo, isLoggedIn } = app.globalData
- if (isLoggedIn && userInfo) {
- // 从本地存储或用户信息中获取绑定数据
- const phoneNumber = wx.getStorageSync('user_phone') || userInfo.phone || ''
- const wechatId = wx.getStorageSync('user_wechat') || userInfo.wechat || ''
- const alipayAccount = wx.getStorageSync('user_alipay') || userInfo.alipay || ''
- const address = wx.getStorageSync('user_address') || userInfo.address || ''
- // 默认开启自动提现
- const autoWithdrawEnabled = wx.getStorageSync('auto_withdraw_enabled') !== false
-
- this.setData({
- isLoggedIn: true,
- userInfo,
- phoneNumber,
- wechatId,
- alipayAccount,
- address,
- autoWithdrawEnabled
- })
- }
- },
-
- // 一键获取收货地址
- getAddress() {
- wx.chooseAddress({
- success: (res) => {
- console.log('[Settings] 获取地址成功:', res)
- const fullAddress = `${res.provinceName || ''}${res.cityName || ''}${res.countyName || ''}${res.detailInfo || ''}`
-
- if (fullAddress.trim()) {
- wx.setStorageSync('user_address', fullAddress)
- this.setData({ address: fullAddress })
-
- // 更新用户信息
- if (app.globalData.userInfo) {
- app.globalData.userInfo.address = fullAddress
- wx.setStorageSync('userInfo', app.globalData.userInfo)
- }
-
- // 同步到服务器
- this.syncAddressToServer(fullAddress)
-
- wx.showToast({ title: '地址已获取', icon: 'success' })
- }
- },
- fail: (e) => {
- console.log('[Settings] 获取地址失败:', e)
- if (e.errMsg?.includes('cancel')) {
- // 用户取消,不提示
- return
- }
- if (e.errMsg?.includes('auth deny') || e.errMsg?.includes('authorize')) {
- wx.showModal({
- title: '需要授权',
- content: '请在设置中允许获取收货地址',
- confirmText: '去设置',
- success: (res) => {
- if (res.confirm) wx.openSetting()
- }
- })
- } else {
- wx.showToast({ title: '获取失败,请重试', icon: 'none' })
- }
- }
- })
- },
-
- // 同步地址到服务器
- async syncAddressToServer(address) {
- try {
- const userId = app.globalData.userInfo?.id
- if (!userId) return
-
- await app.request('/api/miniprogram/user/update', {
- method: 'POST',
- data: { userId, address }
- })
- console.log('[Settings] 地址已同步到服务器')
- } catch (e) {
- console.log('[Settings] 同步地址失败:', e)
- }
- },
-
- // 切换自动提现
- async toggleAutoWithdraw(e) {
- const enabled = e.detail.value
-
- // 检查是否绑定了支付方式
- if (enabled && !this.data.wechatId && !this.data.alipayAccount) {
- wx.showToast({ title: '请先绑定微信号或支付宝', icon: 'none' })
- this.setData({ autoWithdrawEnabled: false })
- return
- }
-
- // 开启时需要确认
- if (enabled) {
- wx.showModal({
- title: '开启自动提现',
- content: `收益将自动打款到您的${this.data.alipayAccount ? '支付宝' : '微信'}账户,确认开启吗?`,
- success: async (res) => {
- if (res.confirm) {
- this.setData({ autoWithdrawEnabled: true })
- wx.setStorageSync('auto_withdraw_enabled', true)
-
- // 同步到服务器
- try {
- await app.request('/api/miniprogram/user/update', {
- method: 'POST',
- data: {
- userId: app.globalData.userInfo?.id,
- autoWithdraw: true,
- withdrawAccount: this.data.alipayAccount || this.data.wechatId
- }
- })
- } catch (e) {
- console.log('同步自动提现设置失败', e)
- }
-
- wx.showToast({ title: '已开启自动提现', icon: 'success' })
- } else {
- this.setData({ autoWithdrawEnabled: false })
- }
- }
- })
- } else {
- this.setData({ autoWithdrawEnabled: false })
- wx.setStorageSync('auto_withdraw_enabled', false)
- wx.showToast({ title: '已关闭自动提现', icon: 'success' })
- }
- },
-
- // 绑定手机号
- bindPhone() {
- this.setData({
- showBindModal: true,
- bindType: 'phone',
- bindValue: ''
- })
- },
-
- // 微信号输入
- onWechatInput(e) {
- this.setData({ wechatId: e.detail.value })
- },
-
- // 保存微信号
- async saveWechat() {
- const { wechatId } = this.data
- if (!wechatId || wechatId.length < 6) return
-
- wx.setStorageSync('user_wechat', wechatId)
-
- // 更新用户信息
- if (app.globalData.userInfo) {
- app.globalData.userInfo.wechat = wechatId
- wx.setStorageSync('userInfo', app.globalData.userInfo)
- }
-
- // 同步到服务器
- try {
- await app.request('/api/miniprogram/user/update', {
- method: 'POST',
- data: {
- userId: app.globalData.userInfo?.id,
- wechat: wechatId
- }
- })
- wx.showToast({ title: '微信号已保存', icon: 'success' })
- } catch (e) {
- console.log('保存微信号失败', e)
- }
- },
-
- // 输入绑定值
- onBindInput(e) {
- let value = e.detail.value
- if (this.data.bindType === 'phone') {
- value = value.replace(/\D/g, '').slice(0, 11)
- }
- this.setData({ bindValue: value })
- },
-
- // 确认绑定
- confirmBind() {
- const { bindType, bindValue } = this.data
-
- if (!bindValue) {
- wx.showToast({ title: '请输入内容', icon: 'none' })
- return
- }
-
- // 验证
- if (bindType === 'phone' && !/^1[3-9]\d{9}$/.test(bindValue)) {
- wx.showToast({ title: '请输入正确的手机号', icon: 'none' })
- return
- }
-
- if (bindType === 'wechat' && bindValue.length < 6) {
- wx.showToast({ title: '微信号至少6位', icon: 'none' })
- return
- }
-
- if (bindType === 'alipay' && !bindValue.includes('@') && !/^1[3-9]\d{9}$/.test(bindValue)) {
- wx.showToast({ title: '请输入正确的支付宝账号', icon: 'none' })
- return
- }
-
- // 保存绑定信息到本地
- if (bindType === 'phone') {
- wx.setStorageSync('user_phone', bindValue)
- this.setData({ phoneNumber: bindValue })
- } else if (bindType === 'wechat') {
- wx.setStorageSync('user_wechat', bindValue)
- this.setData({ wechatId: bindValue })
- } else if (bindType === 'alipay') {
- wx.setStorageSync('user_alipay', bindValue)
- this.setData({ alipayAccount: bindValue })
- }
-
- // 同步到服务器
- this.syncProfileToServer()
-
- this.setData({ showBindModal: false })
- wx.showToast({ title: '绑定成功', icon: 'success' })
- },
-
- // 同步资料到服务器
- async syncProfileToServer() {
- try {
- const userId = app.globalData.userInfo?.id
- if (!userId) return
-
- const res = await app.request('/api/miniprogram/user/profile', {
- method: 'POST',
- data: {
- userId,
- phone: this.data.phoneNumber || undefined,
- wechatId: this.data.wechatId || undefined
- }
- })
-
- if (res.success) {
- console.log('[Settings] 资料同步成功')
- // 更新本地用户信息
- if (app.globalData.userInfo) {
- app.globalData.userInfo.phone = this.data.phoneNumber
- app.globalData.userInfo.wechatId = this.data.wechatId
- wx.setStorageSync('userInfo', app.globalData.userInfo)
- }
- }
- } catch (e) {
- console.log('[Settings] 资料同步失败:', e)
- }
- },
-
- // 获取微信头像(新版授权)
- async getWechatAvatar() {
- try {
- const res = await wx.getUserProfile({
- desc: '用于完善会员资料'
- })
-
- if (res.userInfo) {
- const { nickName, avatarUrl: tempAvatarUrl } = res.userInfo
-
- wx.showLoading({ title: '上传中...', mask: true })
-
- // 1. 先上传图片到服务器
- console.log('[Settings] 开始上传头像:', tempAvatarUrl)
-
- const uploadRes = await new Promise((resolve, reject) => {
- wx.uploadFile({
- url: app.globalData.baseUrl + '/api/miniprogram/upload',
- filePath: tempAvatarUrl,
- name: 'file',
- formData: {
- folder: 'avatars'
- },
- success: (uploadResult) => {
- try {
- const data = JSON.parse(uploadResult.data)
- if (data.success) {
- resolve(data)
- } else {
- reject(new Error(data.error || '上传失败'))
- }
- } catch (err) {
- reject(new Error('解析响应失败'))
- }
- },
- fail: (err) => {
- reject(err)
- }
- })
- })
-
- // 2. 获取上传后的完整URL
- const avatarUrl = app.globalData.baseUrl + uploadRes.data.url
- console.log('[Settings] 头像上传成功:', avatarUrl)
-
- // 3. 更新本地
- this.setData({
- userInfo: {
- ...this.data.userInfo,
- nickname: nickName,
- avatar: avatarUrl
- }
- })
-
- // 4. 同步到服务器数据库
- const userId = app.globalData.userInfo?.id
- if (userId) {
- await app.request('/api/miniprogram/user/profile', {
- method: 'POST',
- data: { userId, nickname: nickName, avatar: avatarUrl }
- })
- }
-
- // 5. 更新全局
- if (app.globalData.userInfo) {
- app.globalData.userInfo.nickname = nickName
- app.globalData.userInfo.avatar = avatarUrl
- wx.setStorageSync('userInfo', app.globalData.userInfo)
- }
-
- wx.hideLoading()
- wx.showToast({ title: '头像更新成功', icon: 'success' })
- }
- } catch (e) {
- wx.hideLoading()
- console.error('[Settings] 获取头像失败:', e)
- wx.showToast({
- title: e.message || '获取头像失败',
- icon: 'none'
- })
- }
- },
-
- // 一键获取微信手机号(button组件回调)
- async onGetPhoneNumber(e) {
- console.log('[Settings] 获取手机号回调:', e.detail)
-
- if (e.detail.errMsg !== 'getPhoneNumber:ok') {
- wx.showToast({ title: '授权失败', icon: 'none' })
- return
- }
-
- try {
- // 需要将code发送到服务器解密获取手机号
- const code = e.detail.code
- if (!code) {
- // 如果没有code,弹出手动输入
- this.bindPhone()
- return
- }
-
- wx.showLoading({ title: '获取中...', mask: true })
-
- // 调用服务器解密手机号(传入userId以便同步到数据库)
- const userId = app.globalData.userInfo?.id
- const res = await app.request('/api/miniprogram/phone', {
- method: 'POST',
- data: { code, userId }
- })
-
- wx.hideLoading()
-
- if (res.success && res.phoneNumber) {
- wx.setStorageSync('user_phone', res.phoneNumber)
- this.setData({ phoneNumber: res.phoneNumber })
-
- // 更新用户信息
- if (app.globalData.userInfo) {
- app.globalData.userInfo.phone = res.phoneNumber
- wx.setStorageSync('userInfo', app.globalData.userInfo)
- }
-
- // 同步到服务器
- this.syncProfileToServer()
-
- wx.showToast({ title: '手机号绑定成功', icon: 'success' })
- } else {
- // 获取失败,弹出手动输入
- this.bindPhone()
- }
- } catch (e) {
- wx.hideLoading()
- console.log('[Settings] 获取手机号失败:', e)
- // 获取失败,弹出手动输入
- this.bindPhone()
- }
- },
-
- // 关闭绑定弹窗
- closeBindModal() {
- this.setData({ showBindModal: false })
- },
-
- // 清除缓存
- clearCache() {
- wx.showModal({
- title: '清除缓存',
- content: '确定要清除本地缓存吗?',
- success: (res) => {
- if (res.confirm) {
- // 保留登录信息,只清除其他缓存
- const token = wx.getStorageSync('token')
- const userInfo = wx.getStorageSync('userInfo')
- wx.clearStorageSync()
- if (token) wx.setStorageSync('token', token)
- if (userInfo) wx.setStorageSync('userInfo', userInfo)
- wx.showToast({ title: '缓存已清除', icon: 'success' })
- }
- }
- })
- },
-
- // 退出登录
- handleLogout() {
- wx.showModal({
- title: '退出登录',
- content: '确定要退出登录吗?',
- success: (res) => {
- if (res.confirm) {
- app.logout()
- this.setData({
- isLoggedIn: false,
- userInfo: null,
- phoneNumber: '',
- wechatId: '',
- alipayAccount: ''
- })
- wx.showToast({ title: '已退出登录', icon: 'success' })
- setTimeout(() => wx.navigateBack(), 1500)
- }
- }
- })
- },
-
- // 联系客服 - 跳转到Soul派对房
- contactService() {
- wx.showToast({ title: '请在Soul派对房联系客服', icon: 'none' })
- },
-
- // 阻止冒泡
- stopPropagation() {},
-
- goBack() { wx.navigateBack() },
-
- // 跳转到地址管理页
- goToAddresses() {
- wx.navigateTo({ url: '/pages/addresses/addresses' })
- }
-})
diff --git a/归档/miniprogram/pages/settings/settings.json b/归档/miniprogram/pages/settings/settings.json
deleted file mode 100644
index e90e9960..00000000
--- a/归档/miniprogram/pages/settings/settings.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "usingComponents": {},
- "navigationStyle": "custom"
-}
diff --git a/归档/miniprogram/pages/settings/settings.wxml b/归档/miniprogram/pages/settings/settings.wxml
deleted file mode 100644
index 2338a79b..00000000
--- a/归档/miniprogram/pages/settings/settings.wxml
+++ /dev/null
@@ -1,146 +0,0 @@
-
-
-
-
- ‹
-
- 设置
-
-
-
-
-
-
-
-
-
-
-
-
-
- 📱
-
- 手机号
- {{phoneNumber || '未绑定'}}
-
-
-
- ✓
-
-
-
-
-
-
-
- 💬
-
- 微信号
-
-
-
-
- ✓
-
-
-
-
-
-
- 📍
-
- 收货地址
- 管理收货地址,用于发货与邮寄
-
-
-
- 管理
-
-
-
-
-
-
-
-
-
-
-
- 开启自动提现
-
-
-
-
-
- 提现方式
- 微信零钱
-
-
- 提现账户
- {{wechatId}}
-
- 收益将在每笔订单完成后自动打款
-
-
-
-
-
-
- 提示:绑定微信号才能使用提现功能
-
-
- 退出登录
-
-
-
-
-
-
-
-
-
-
-
-
-
- {{bindType === 'phone' ? '绑定手机号后可用于找伙伴匹配' : bindType === 'wechat' ? '绑定微信号后可用于找伙伴匹配和好友添加' : '绑定支付宝后可用于提现收益'}}
-
-
-
- 确认绑定
-
-
-
-
-
diff --git a/归档/miniprogram/pages/settings/settings.wxss b/归档/miniprogram/pages/settings/settings.wxss
deleted file mode 100644
index 5e7a4254..00000000
--- a/归档/miniprogram/pages/settings/settings.wxss
+++ /dev/null
@@ -1,114 +0,0 @@
-/* 设置页样式 */
-.page { min-height: 100vh; background: #000; padding-bottom: 64rpx; }
-
-/* 导航栏 */
-.nav-bar { position: fixed; top: 0; left: 0; right: 0; z-index: 100; background: rgba(0,0,0,0.9); backdrop-filter: blur(40rpx); display: flex; align-items: center; justify-content: space-between; padding: 0 32rpx; height: 88rpx; }
-.nav-back { width: 64rpx; height: 64rpx; background: #1c1c1e; border-radius: 50%; display: flex; align-items: center; justify-content: center; }
-.back-icon { font-size: 40rpx; color: rgba(255,255,255,0.6); font-weight: 300; }
-.nav-title { font-size: 34rpx; font-weight: 600; color: #fff; }
-.nav-placeholder { width: 64rpx; }
-
-.content { padding: 24rpx; }
-
-/* 账号绑定卡片 */
-.bind-card { background: #1c1c1e; border-radius: 32rpx; padding: 32rpx; margin-bottom: 24rpx; border: 2rpx solid rgba(0,206,209,0.2); }
-.card-header { display: flex; align-items: flex-start; gap: 16rpx; margin-bottom: 24rpx; padding-bottom: 24rpx; border-bottom: 2rpx solid rgba(255,255,255,0.05); }
-.card-icon { font-size: 40rpx; }
-.card-title-wrap { flex: 1; }
-.card-title { font-size: 30rpx; font-weight: 600; color: #fff; display: block; }
-.card-desc { font-size: 24rpx; color: rgba(255,255,255,0.5); margin-top: 4rpx; display: block; }
-
-.bind-list { display: flex; flex-direction: column; gap: 24rpx; }
-.bind-item { display: flex; align-items: center; justify-content: space-between; padding: 16rpx 0; }
-.bind-left { display: flex; align-items: center; gap: 20rpx; }
-.bind-icon { width: 72rpx; height: 72rpx; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 32rpx; }
-.bind-icon.phone-icon { background: rgba(0,206,209,0.2); }
-.bind-icon.wechat-icon { background: rgba(158,158,158,0.2); }
-.bind-icon.alipay-icon { background: rgba(158,158,158,0.2); }
-.bind-info { display: flex; flex-direction: column; gap: 4rpx; flex: 1; }
-.bind-label { font-size: 28rpx; color: #fff; font-weight: 500; }
-.bind-value { font-size: 24rpx; color: rgba(255,255,255,0.5); }
-.address-text { max-width: 360rpx; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
-.bind-icon.address-icon { background: rgba(255,165,0,0.2); }
-.required { color: #FF6B6B; font-size: 24rpx; }
-.bind-input { font-size: 24rpx; color: #00CED1; background: transparent; padding: 8rpx 0; }
-.bind-right { display: flex; align-items: center; }
-.bind-check { color: #00CED1; font-size: 32rpx; }
-.bind-btn { color: #00CED1; font-size: 26rpx; }
-.bind-manage { color: #00CED1; font-size: 26rpx; }
-.brand-color { color: #00CED1; }
-
-/* 一键获取手机号按钮 */
-.get-phone-btn {
- padding: 12rpx 24rpx;
- background: rgba(0,206,209,0.2);
- border: 2rpx solid rgba(0,206,209,0.3);
- border-radius: 16rpx;
- font-size: 24rpx;
- color: #00CED1;
- line-height: normal;
-}
-.get-phone-btn::after { border: none; }
-
-/* 自动提现卡片 */
-.auto-withdraw-card { margin-top: 24rpx; }
-.auto-withdraw-content { padding-top: 16rpx; }
-.withdraw-switch-row {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 16rpx 0;
-}
-.switch-label { font-size: 28rpx; color: #fff; }
-.withdraw-info {
- background: rgba(0,206,209,0.1);
- border-radius: 16rpx;
- padding: 20rpx;
- margin-top: 16rpx;
-}
-.info-item {
- display: flex;
- justify-content: space-between;
- padding: 8rpx 0;
-}
-.info-label { font-size: 26rpx; color: rgba(255,255,255,0.6); }
-.info-value { font-size: 26rpx; color: #00CED1; }
-.withdraw-tip {
- display: block;
- font-size: 22rpx;
- color: rgba(255,255,255,0.4);
- margin-top: 12rpx;
- text-align: center;
-}
-
-/* 提现提示 */
-.tip-banner { background: rgba(255,165,0,0.1); border: 2rpx solid rgba(255,165,0,0.3); border-radius: 20rpx; padding: 20rpx 24rpx; margin-bottom: 24rpx; }
-.tip-text { font-size: 24rpx; color: #FFA500; line-height: 1.5; }
-
-/* 设置组 */
-.settings-group { background: #1c1c1e; border-radius: 32rpx; overflow: hidden; margin-bottom: 24rpx; }
-.settings-item { display: flex; align-items: center; justify-content: space-between; padding: 28rpx 32rpx; border-bottom: 2rpx solid rgba(255,255,255,0.05); }
-.settings-item:last-child { border-bottom: none; }
-.item-left { display: flex; align-items: center; gap: 16rpx; }
-.item-icon { font-size: 36rpx; }
-.item-title { font-size: 28rpx; color: #fff; }
-.item-arrow { font-size: 28rpx; color: rgba(255,255,255,0.3); }
-.item-value { font-size: 26rpx; color: rgba(255,255,255,0.5); }
-
-/* 退出登录按钮 */
-.logout-btn { margin-top: 48rpx; padding: 28rpx; background: rgba(244,67,54,0.1); border: 2rpx solid rgba(244,67,54,0.3); border-radius: 24rpx; text-align: center; font-size: 28rpx; color: #F44336; }
-
-/* 弹窗 - 简洁大气风格 */
-.modal-overlay { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.8); backdrop-filter: blur(20rpx); display: flex; align-items: center; justify-content: center; z-index: 1000; padding: 48rpx; }
-.modal-content { width: 100%; max-width: 640rpx; background: linear-gradient(180deg, #1c1c1e 0%, #0d0d0d 100%); border-radius: 40rpx; overflow: hidden; border: 2rpx solid rgba(255,255,255,0.08); }
-.modal-header { display: flex; align-items: center; justify-content: space-between; padding: 40rpx 40rpx 24rpx; }
-.modal-title { font-size: 36rpx; font-weight: 700; color: #fff; }
-.modal-close { width: 64rpx; height: 64rpx; background: rgba(255,255,255,0.08); border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 32rpx; color: rgba(255,255,255,0.5); }
-.modal-body { padding: 16rpx 40rpx 48rpx; }
-.input-wrapper { margin-bottom: 32rpx; }
-.form-input { width: 100%; padding: 32rpx 24rpx; background: rgba(255,255,255,0.05); border: 2rpx solid rgba(255,255,255,0.1); border-radius: 24rpx; font-size: 32rpx; color: #fff; box-sizing: border-box; transition: all 0.2s; }
-.form-input:focus { border-color: rgba(0,206,209,0.5); background: rgba(0,206,209,0.05); }
-.input-placeholder { color: rgba(255,255,255,0.25); }
-.bind-tip { font-size: 24rpx; color: rgba(255,255,255,0.4); margin-bottom: 40rpx; display: block; line-height: 1.6; text-align: center; }
-.btn-primary { padding: 32rpx; background: linear-gradient(135deg, #00CED1 0%, #20B2AA 100%); color: #000; font-size: 32rpx; font-weight: 600; text-align: center; border-radius: 28rpx; }
-.btn-primary.btn-disabled { background: rgba(255,255,255,0.1); color: rgba(255,255,255,0.3); }
diff --git a/归档/miniprogram/pages/vip/vip.js b/归档/miniprogram/pages/vip/vip.js
deleted file mode 100644
index 1f9795b0..00000000
--- a/归档/miniprogram/pages/vip/vip.js
+++ /dev/null
@@ -1,132 +0,0 @@
-const app = getApp()
-
-Page({
- data: {
- statusBarHeight: 44,
- isVip: false,
- daysRemaining: 0,
- expireDateStr: '',
- price: 1980,
- originalPrice: 6980,
- contentRights: [
- { title: '解锁全部章节', desc: '365天全部章节内容' },
- { title: '案例库', desc: '30-100个创业项目案例' },
- { title: '智能纪要', desc: '每天推送派对精华' },
- { title: '会议纪要库', desc: '之前所有场次的会议纪要' }
- ],
- socialRights: [
- { title: '匹配创业伙伴', desc: '匹配所有创业伙伴' },
- { title: '创业老板排行', desc: '排行榜展示您的项目' },
- { title: '链接资源', desc: '进群聊天、链接资源的权利' },
- { title: '专属VIP标识', desc: '头像金色VIP光圈' }
- ],
- profile: { name: '', project: '', contact: '', bio: '' },
- purchasing: false
- },
-
- onLoad() {
- this.setData({ statusBarHeight: app.globalData.statusBarHeight })
- this.loadVipInfo()
- },
-
- async loadVipInfo() {
- const userId = app.globalData.userInfo?.id
- if (!userId) return
- try {
- const res = await app.request({ url: `/api/vip/status?userId=${userId}`, silent: true })
- if (res?.success) {
- const d = res.data
- let expStr = ''
- if (d.expireDate) {
- const dt = new Date(d.expireDate)
- expStr = `${dt.getFullYear()}-${String(dt.getMonth()+1).padStart(2,'0')}-${String(dt.getDate()).padStart(2,'0')}`
- }
- this.setData({
- isVip: d.isVip,
- daysRemaining: d.daysRemaining,
- expireDateStr: expStr,
- price: d.price || 1980
- })
- if (d.isVip) this.loadProfile(userId)
- }
- } catch (e) { console.log('[VIP] 加载失败', e) }
- },
-
- async loadProfile(userId) {
- try {
- const res = await app.request(`/api/vip/profile?userId=${userId}`)
- if (res?.success) this.setData({ profile: res.data })
- } catch (e) { console.log('[VIP] 资料加载失败', e) }
- },
-
- async handlePurchase() {
- let userId = app.globalData.userInfo?.id
- let openId = app.globalData.openId || app.globalData.userInfo?.open_id
- if (!userId || !openId) {
- wx.showLoading({ title: '登录中...', mask: true })
- try {
- await app.login()
- userId = app.globalData.userInfo?.id
- openId = app.globalData.openId || app.globalData.userInfo?.open_id
- wx.hideLoading()
- if (!userId || !openId) {
- wx.showToast({ title: '登录失败,请重试', icon: 'none' })
- return
- }
- } catch (e) {
- wx.hideLoading()
- wx.showToast({ title: '登录失败', icon: 'none' })
- return
- }
- }
- this.setData({ purchasing: true })
- try {
- const payRes = await app.request('/api/miniprogram/pay', {
- method: 'POST',
- data: {
- openId,
- userId,
- productType: 'vip',
- productId: 'vip_annual',
- amount: this.data.price,
- description: '卡若创业派对VIP年度会员(365天)'
- }
- })
- if (payRes?.success && payRes.data?.payParams) {
- wx.requestPayment({
- ...payRes.data.payParams,
- success: () => {
- wx.showToast({ title: 'VIP开通成功', icon: 'success' })
- this.loadVipInfo()
- },
- fail: () => wx.showToast({ title: '支付取消', icon: 'none' })
- })
- } else {
- wx.showToast({ title: payRes?.error || '支付参数获取失败', icon: 'none' })
- }
- } catch (e) {
- console.error('[VIP] 购买失败', e)
- wx.showToast({ title: '购买失败,请稍后重试', icon: 'none' })
- } finally { this.setData({ purchasing: false }) }
- },
-
- onNameInput(e) { this.setData({ 'profile.name': e.detail.value }) },
- onProjectInput(e) { this.setData({ 'profile.project': e.detail.value }) },
- onContactInput(e) { this.setData({ 'profile.contact': e.detail.value }) },
- onBioInput(e) { this.setData({ 'profile.bio': e.detail.value }) },
-
- async saveProfile() {
- const userId = app.globalData.userInfo?.id
- if (!userId) return
- const p = this.data.profile
- try {
- const res = await app.request('/api/vip/profile', {
- method: 'POST', data: { userId, name: p.name, project: p.project, contact: p.contact, bio: p.bio }
- })
- if (res?.success) wx.showToast({ title: '资料已保存', icon: 'success' })
- else wx.showToast({ title: res?.error || '保存失败', icon: 'none' })
- } catch (e) { wx.showToast({ title: '保存失败', icon: 'none' }) }
- },
-
- goBack() { wx.navigateBack() }
-})
diff --git a/归档/miniprogram/pages/vip/vip.json b/归档/miniprogram/pages/vip/vip.json
deleted file mode 100644
index 52bdd937..00000000
--- a/归档/miniprogram/pages/vip/vip.json
+++ /dev/null
@@ -1 +0,0 @@
-{ "usingComponents": {}, "navigationStyle": "custom" }
diff --git a/归档/miniprogram/pages/vip/vip.wxml b/归档/miniprogram/pages/vip/vip.wxml
deleted file mode 100644
index c7ebfb09..00000000
--- a/归档/miniprogram/pages/vip/vip.wxml
+++ /dev/null
@@ -1,78 +0,0 @@
-
-
-
- ‹
- 卡若创业派对
-
-
-
-
-
-
- 卡若创业派对
- 加入卡若的创业派对会员
- 有效期至 {{expireDateStr}}(剩余{{daysRemaining}}天)
- 专属会员尊享权益
-
-
-
-
- 内容权益
-
- ✓
-
- {{item.title}}
- {{item.desc}}
-
-
-
-
-
-
- 社交权益
-
- ✓
-
- {{item.title}}
- {{item.desc}}
-
-
-
-
-
-
-
- ¥{{originalPrice}}
- ¥{{price}}
- /年
-
-
- 加入卡若创业派对,获取创业资讯与优质人脉资源
-
-
-
-
- 会员资料(展示在创业老板排行)
-
- 姓名
-
-
-
- 项目名称
-
-
-
- 联系方式
-
-
-
- 一句话简介
-
-
-
-
-
-
-
diff --git a/归档/miniprogram/pages/vip/vip.wxss b/归档/miniprogram/pages/vip/vip.wxss
deleted file mode 100644
index 8bbf1960..00000000
--- a/归档/miniprogram/pages/vip/vip.wxss
+++ /dev/null
@@ -1,46 +0,0 @@
-.page { background: #000; min-height: 100vh; color: #fff; }
-.nav-bar { position: fixed; top: 0; left: 0; right: 0; z-index: 100; display: flex; align-items: center; justify-content: space-between; height: 44px; padding: 0 24rpx; background: rgba(0,0,0,0.9); }
-.nav-back { width: 60rpx; height: 60rpx; display: flex; align-items: center; justify-content: center; }
-.back-icon { font-size: 44rpx; color: #fff; }
-.nav-title { font-size: 34rpx; font-weight: 600; color: #fff; }
-.nav-placeholder-r { width: 60rpx; }
-
-.vip-hero { margin: 24rpx; padding: 48rpx 32rpx; border-radius: 24rpx; background: linear-gradient(135deg, rgba(0,206,209,0.08), rgba(255,215,0,0.06)); border: 1rpx solid rgba(0,206,209,0.2); }
-.vip-hero-active { border-color: rgba(255,215,0,0.4); background: linear-gradient(135deg, rgba(255,215,0,0.15), rgba(0,206,209,0.08)); }
-.vip-hero-tag { display: inline-block; background: rgba(0,206,209,0.15); color: #00CED1; font-size: 22rpx; padding: 6rpx 16rpx; border-radius: 16rpx; margin-bottom: 20rpx; }
-.vip-hero-title { display: block; font-size: 44rpx; font-weight: bold; color: #fff; margin-top: 12rpx; }
-.gold { color: #FFD700; }
-.vip-hero-sub { display: block; font-size: 24rpx; color: rgba(255,255,255,0.5); margin-top: 12rpx; }
-
-.rights-card { margin: 24rpx; }
-.rights-item { display: flex; align-items: flex-start; gap: 20rpx; padding: 24rpx; margin-bottom: 16rpx; background: rgba(255,255,255,0.04); border: 1rpx solid rgba(255,255,255,0.06); border-radius: 16rpx; }
-.rights-check-wrap { width: 44rpx; height: 44rpx; border-radius: 50%; background: rgba(0,206,209,0.15); display: flex; align-items: center; justify-content: center; flex-shrink: 0; margin-top: 4rpx; }
-.rights-check { color: #00CED1; font-size: 24rpx; font-weight: bold; }
-.rights-info { display: flex; flex-direction: column; }
-.rights-title { font-size: 30rpx; font-weight: 600; color: rgba(255,255,255,0.95); }
-.rights-desc { font-size: 24rpx; color: rgba(255,255,255,0.45); margin-top: 6rpx; }
-
-.rights-section-title { display: block; font-size: 26rpx; color: #00CED1; font-weight: 600; margin-bottom: 16rpx; padding-bottom: 12rpx; border-bottom: 1rpx solid rgba(0,206,209,0.15); }
-
-.buy-area { margin: 24rpx; padding: 32rpx; text-align: center; background: rgba(255,255,255,0.03); border-radius: 20rpx; }
-.price-row { display: flex; align-items: baseline; justify-content: center; gap: 12rpx; margin-bottom: 24rpx; }
-.price-original { font-size: 28rpx; color: rgba(255,255,255,0.35); text-decoration: line-through; }
-.price-current { font-size: 64rpx; font-weight: bold; color: #FF4444; }
-.price-unit { font-size: 26rpx; color: rgba(255,255,255,0.5); }
-.buy-btn { width: 90%; height: 88rpx; line-height: 88rpx; background: linear-gradient(135deg, #FFD700, #FFA500); color: #000; font-size: 32rpx; font-weight: bold; border-radius: 44rpx; border: none; margin: 0 auto; }
-.buy-btn[disabled] { opacity: 0.5; }
-.buy-sub { display: block; font-size: 22rpx; color: rgba(255,255,255,0.4); margin-top: 16rpx; }
-
-.profile-card { margin: 24rpx; padding: 28rpx; background: #1c1c1e; border-radius: 20rpx; }
-.profile-title { font-size: 30rpx; font-weight: 600; color: rgba(255,255,255,0.9); display: block; margin-bottom: 24rpx; }
-.form-group { margin-bottom: 20rpx; }
-.form-label { font-size: 24rpx; color: rgba(255,255,255,0.5); display: block; margin-bottom: 8rpx; }
-.form-input { background: rgba(255,255,255,0.06); border: 1rpx solid rgba(255,255,255,0.1); border-radius: 12rpx; padding: 16rpx 20rpx; font-size: 28rpx; color: #fff; }
-.save-btn { margin-top: 24rpx; width: 100%; height: 80rpx; line-height: 80rpx; background: #00CED1; color: #000; font-size: 30rpx; font-weight: 600; border-radius: 40rpx; border: none; }
-
-.author-section { margin: 32rpx 24rpx; padding: 24rpx; border-top: 1rpx solid rgba(255,255,255,0.08); }
-.author-row { display: flex; justify-content: space-between; padding: 8rpx 0; }
-.author-label { font-size: 24rpx; color: rgba(255,255,255,0.4); }
-.author-name { font-size: 24rpx; color: rgba(255,255,255,0.7); }
-
-.bottom-space { height: 120rpx; }
diff --git a/归档/miniprogram/pages/withdraw-records/withdraw-records.js b/归档/miniprogram/pages/withdraw-records/withdraw-records.js
deleted file mode 100644
index 5ef1c2fd..00000000
--- a/归档/miniprogram/pages/withdraw-records/withdraw-records.js
+++ /dev/null
@@ -1,123 +0,0 @@
-/**
- * 提现记录 - 独立页面
- */
-const app = getApp()
-
-Page({
- data: {
- statusBarHeight: 44,
- list: [],
- loading: true
- },
-
- onLoad() {
- this.setData({ statusBarHeight: app.globalData.statusBarHeight || 44 })
- this.loadRecords()
- },
-
- onShow() {
- this.loadRecords()
- },
-
- async loadRecords() {
- const userInfo = app.globalData.userInfo
- if (!app.globalData.isLoggedIn || !userInfo || !userInfo.id) {
- this.setData({ list: [], loading: false })
- return
- }
- this.setData({ loading: true })
- try {
- const res = await app.request('/api/miniprogram/withdraw/records?userId=' + userInfo.id)
- if (res && res.success && res.data && Array.isArray(res.data.list)) {
- const list = (res.data.list || []).map(item => ({
- id: item.id,
- amount: (item.amount != null ? item.amount : 0).toFixed(2),
- status: this.statusText(item.status),
- statusRaw: item.status,
- createdAt: (item.createdAt ?? item.created_at) ? this.formatDate(item.createdAt ?? item.created_at) : '--',
- canReceive: !!item.canReceive
- }))
- this.setData({ list, loading: false })
- } else {
- this.setData({ list: [], loading: false })
- }
- } catch (e) {
- console.log('[WithdrawRecords] 加载失败:', e)
- this.setData({ list: [], loading: false })
- }
- },
-
- statusText(status) {
- const map = {
- pending: '待审核',
- pending_confirm: '待确认收款',
- processing: '处理中',
- success: '已到账',
- failed: '已拒绝'
- }
- return map[status] || status || '--'
- },
-
- formatDate(dateStr) {
- if (!dateStr) return '--'
- const d = new Date(dateStr)
- const y = d.getFullYear()
- const m = (d.getMonth() + 1).toString().padStart(2, '0')
- const day = d.getDate().toString().padStart(2, '0')
- return `${y}-${m}-${day}`
- },
-
- goBack() {
- wx.navigateBack()
- },
-
- async onReceiveTap(e) {
- const id = e.currentTarget.dataset.id
- if (!id) return
- wx.showLoading({ title: '加载中...' })
- try {
- const res = await app.request('/api/miniprogram/withdraw/confirm-info?id=' + encodeURIComponent(id))
- wx.hideLoading()
- if (!res || !res.success || !res.data) {
- wx.showToast({ title: res?.message || '获取领取信息失败', icon: 'none' })
- return
- }
- const { mchId, appId, package: pkg } = res.data
- if (!pkg || pkg === '') {
- wx.showToast({
- title: '打款已发起,请到微信零钱中查看',
- icon: 'none',
- duration: 2500
- })
- return
- }
- if (!wx.canIUse('requestMerchantTransfer')) {
- wx.showToast({ title: '当前微信版本过低,请更新后重试', icon: 'none' })
- return
- }
- wx.requestMerchantTransfer({
- mchId: mchId || '',
- appId: appId || wx.getAccountInfoSync().miniProgram.appId,
- package: pkg,
- success: (res) => {
- if (res.errMsg === 'requestMerchantTransfer:ok') {
- wx.showToast({ title: '已调起收款页', icon: 'success' })
- this.loadRecords()
- } else {
- wx.showToast({ title: res.errMsg || '操作完成', icon: 'none' })
- }
- },
- fail: (err) => {
- if (err.errMsg && err.errMsg.indexOf('cancel') !== -1) {
- wx.showToast({ title: '已取消', icon: 'none' })
- } else {
- wx.showToast({ title: err.errMsg || '调起失败', icon: 'none' })
- }
- }
- })
- } catch (e) {
- wx.hideLoading()
- wx.showToast({ title: '网络异常,请重试', icon: 'none' })
- }
- }
-})
diff --git a/归档/miniprogram/pages/withdraw-records/withdraw-records.json b/归档/miniprogram/pages/withdraw-records/withdraw-records.json
deleted file mode 100644
index e90e9960..00000000
--- a/归档/miniprogram/pages/withdraw-records/withdraw-records.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
- "usingComponents": {},
- "navigationStyle": "custom"
-}
diff --git a/归档/miniprogram/pages/withdraw-records/withdraw-records.wxml b/归档/miniprogram/pages/withdraw-records/withdraw-records.wxml
deleted file mode 100644
index def185dc..00000000
--- a/归档/miniprogram/pages/withdraw-records/withdraw-records.wxml
+++ /dev/null
@@ -1,25 +0,0 @@
-
-
- ←
- 提现记录
-
-
-
-
-
- 加载中...
- 暂无提现记录
-
-
-
- ¥{{item.amount}}
- {{item.createdAt}}
-
-
- {{item.status}}
-
-
-
-
-
-
diff --git a/归档/miniprogram/pages/withdraw-records/withdraw-records.wxss b/归档/miniprogram/pages/withdraw-records/withdraw-records.wxss
deleted file mode 100644
index 4de9e2bb..00000000
--- a/归档/miniprogram/pages/withdraw-records/withdraw-records.wxss
+++ /dev/null
@@ -1,71 +0,0 @@
-.page {
- min-height: 100vh;
- background: #000;
-}
-.nav-bar {
- position: fixed;
- top: 0;
- left: 0;
- right: 0;
- z-index: 100;
- background: rgba(0,0,0,0.9);
- display: flex;
- align-items: center;
- padding: 0 24rpx;
- height: 88rpx;
-}
-.nav-back {
- color: #00CED1;
- font-size: 36rpx;
- padding: 16rpx;
-}
-.nav-title {
- flex: 1;
- text-align: center;
- font-size: 34rpx;
- font-weight: 600;
- color: #fff;
-}
-.nav-placeholder { width: 80rpx; }
-.nav-placeholder-bar { width: 100%; }
-
-.content {
- padding: 32rpx;
-}
-.loading-tip, .empty {
- text-align: center;
- color: rgba(255,255,255,0.6);
- font-size: 28rpx;
- padding: 80rpx 0;
-}
-.list { }
-.item {
- display: flex;
- align-items: center;
- justify-content: space-between;
- padding: 28rpx 0;
- border-bottom: 2rpx solid rgba(255,255,255,0.06);
-}
-.item:last-child { border-bottom: none; }
-.item-left { display: flex; flex-direction: column; gap: 8rpx; }
-.item-right { display: flex; flex-direction: column; align-items: flex-end; gap: 12rpx; }
-.amount { font-size: 32rpx; font-weight: 600; color: #fff; }
-.time { font-size: 24rpx; color: rgba(255,255,255,0.5); }
-.status { font-size: 26rpx; }
-.status.status-pending { color: #FFA500; }
-.status.status-processing { color: #4CAF50; }
-.status.status-pending_confirm { color: #4CAF50; }
-.status.status-success { color: #4CAF50; }
-.status.status-failed { color: rgba(255,255,255,0.5); }
-.btn-receive {
- margin: 0;
- padding: 0 24rpx;
- height: 56rpx;
- line-height: 56rpx;
- font-size: 24rpx;
- color: #00CED1;
- background: transparent;
- border: 2rpx solid #00CED1;
- border-radius: 8rpx;
-}
-.btn-receive::after { border: none; }
diff --git a/归档/miniprogram/project.config.json b/归档/miniprogram/project.config.json
deleted file mode 100644
index b1752da7..00000000
--- a/归档/miniprogram/project.config.json
+++ /dev/null
@@ -1,58 +0,0 @@
-{
- "compileType": "miniprogram",
- "miniprogramRoot": "",
- "description": "Soul创业派对 - 来自派对房的真实商业故事",
- "appid": "wxb8bbb2b10dec74aa",
- "setting": {
- "urlCheck": false,
- "es6": true,
- "enhance": true,
- "postcss": true,
- "preloadBackgroundData": false,
- "minified": true,
- "newFeature": true,
- "coverView": true,
- "nodeModules": false,
- "autoAudits": false,
- "showShadowRootInWxmlPanel": true,
- "scopeDataCheck": false,
- "uglifyFileName": false,
- "checkInvalidKey": true,
- "checkSiteMap": true,
- "uploadWithSourceMap": true,
- "compileHotReLoad": true,
- "lazyloadPlaceholderEnable": false,
- "useMultiFrameRuntime": true,
- "babelSetting": {
- "ignore": [],
- "disablePlugins": [],
- "outputPath": ""
- },
- "enableEngineNative": false,
- "useIsolateContext": true,
- "userConfirmedBundleSwitch": false,
- "packNpmManually": false,
- "packNpmRelationList": [],
- "minifyWXSS": true,
- "disableUseStrict": false,
- "minifyWXML": true,
- "showES6CompileOption": false,
- "useCompilerPlugins": false,
- "ignoreUploadUnusedFiles": true,
- "compileWorklet": false,
- "localPlugins": false,
- "condition": false,
- "swc": false,
- "disableSWC": true
- },
- "condition": {},
- "editorSetting": {
- "tabIndent": "insertSpaces",
- "tabSize": 2
- },
- "simulatorPluginLibVersion": {},
- "packOptions": {
- "ignore": [],
- "include": []
- }
-}
\ No newline at end of file
diff --git a/归档/miniprogram/project.private.config.json b/归档/miniprogram/project.private.config.json
deleted file mode 100644
index 9df0293d..00000000
--- a/归档/miniprogram/project.private.config.json
+++ /dev/null
@@ -1,85 +0,0 @@
-{
- "description": "项目私有配置文件。此文件中的内容将覆盖 project.config.json 中的相同字段。项目的改动优先同步到此文件中。详见文档:https://developers.weixin.qq.com/miniprogram/dev/devtools/projectconfig.html",
- "projectname": "miniprogram",
- "setting": {
- "compileHotReLoad": true,
- "urlCheck": true,
- "coverView": true,
- "lazyloadPlaceholderEnable": false,
- "skylineRenderEnable": false,
- "preloadBackgroundData": false,
- "autoAudits": false,
- "useApiHook": true,
- "showShadowRootInWxmlPanel": true,
- "useStaticServer": false,
- "useLanDebug": false,
- "showES6CompileOption": false,
- "checkInvalidKey": true,
- "ignoreDevUnusedFiles": true,
- "bigPackageSizeSupport": false,
- "useIsolateContext": true
- },
- "libVersion": "3.13.2",
- "condition": {
- "miniprogram": {
- "list": [
- {
- "name": "pages/read/read",
- "pathName": "pages/read/read",
- "query": "id=1.1",
- "launchMode": "default",
- "scene": null
- },
- {
- "name": "pages/match/match",
- "pathName": "pages/match/match",
- "query": "",
- "launchMode": "default",
- "scene": null
- },
- {
- "name": "看书",
- "pathName": "pages/read/read",
- "query": "id=1.4",
- "launchMode": "default",
- "scene": null
- },
- {
- "name": "分销中心",
- "pathName": "pages/referral/referral",
- "query": "",
- "launchMode": "default",
- "scene": null
- },
- {
- "name": "阅读",
- "pathName": "pages/read/read",
- "query": "id=1.1",
- "launchMode": "default",
- "scene": null
- },
- {
- "name": "分销中心",
- "pathName": "pages/referral/referral",
- "query": "",
- "launchMode": "default",
- "scene": null
- },
- {
- "name": "我的",
- "pathName": "pages/my/my",
- "query": "",
- "launchMode": "default",
- "scene": null
- },
- {
- "name": "新增地址",
- "pathName": "pages/addresses/edit",
- "query": "",
- "launchMode": "default",
- "scene": null
- }
- ]
- }
- }
-}
\ No newline at end of file
diff --git a/归档/miniprogram/sitemap.json b/归档/miniprogram/sitemap.json
deleted file mode 100644
index 55d1d29e..00000000
--- a/归档/miniprogram/sitemap.json
+++ /dev/null
@@ -1,7 +0,0 @@
-{
- "desc": "关于本文件的更多信息,请参考文档 https://developers.weixin.qq.com/miniprogram/dev/framework/sitemap.html",
- "rules": [{
- "action": "allow",
- "page": "*"
- }]
-}
diff --git a/归档/miniprogram/utils/chapterAccessManager.js b/归档/miniprogram/utils/chapterAccessManager.js
deleted file mode 100644
index 8b66c587..00000000
--- a/归档/miniprogram/utils/chapterAccessManager.js
+++ /dev/null
@@ -1,201 +0,0 @@
-/**
- * 章节权限管理器
- * 统一管理章节权限判断、状态流转、异常处理
- */
-
-const app = getApp()
-
-class ChapterAccessManager {
- constructor() {
- this.accessStates = {
- UNKNOWN: 'unknown',
- FREE: 'free',
- LOCKED_NOT_LOGIN: 'locked_not_login',
- LOCKED_NOT_PURCHASED: 'locked_not_purchased',
- UNLOCKED_PURCHASED: 'unlocked_purchased',
- ERROR: 'error'
- }
- }
-
- /**
- * 拉取最新配置(免费章节列表、价格等)
- */
- async fetchLatestConfig() {
- try {
- const res = await app.request({ url: '/api/miniprogram/config', silent: true, timeout: 3000 })
- if (res.success && res.freeChapters) {
- return {
- freeChapters: res.freeChapters,
- prices: res.prices || { section: 1, fullbook: 9.9 }
- }
- }
- } catch (e) {
- console.warn('[AccessManager] 获取配置失败,使用默认配置:', e)
- }
-
- // 默认配置
- return {
- freeChapters: ['preface', 'epilogue', '1.1', 'appendix-1', 'appendix-2', 'appendix-3'],
- prices: { section: 1, fullbook: 9.9 }
- }
- }
-
- /**
- * 判断章节是否免费
- */
- isFreeChapter(sectionId, freeList) {
- return freeList.includes(sectionId)
- }
-
- /**
- * 【核心方法】确定章节权限状态
- * @param {string} sectionId - 章节ID
- * @param {Array} freeList - 免费章节列表
- * @returns {Promise} accessState
- */
- async determineAccessState(sectionId, freeList) {
- try {
- // 1. 检查是否免费
- if (this.isFreeChapter(sectionId, freeList)) {
- console.log('[AccessManager] 免费章节:', sectionId)
- return this.accessStates.FREE
- }
-
- // 2. 检查是否登录
- const userId = app.globalData.userInfo?.id
- if (!userId) {
- console.log('[AccessManager] 未登录,需要登录:', sectionId)
- return this.accessStates.LOCKED_NOT_LOGIN
- }
-
- // 3. 请求服务端校验是否已购买(带重试)
- const res = await this.requestWithRetry(
- `/api/miniprogram/user/check-purchased?userId=${encodeURIComponent(userId)}&type=section&productId=${encodeURIComponent(sectionId)}`,
- { timeout: 5000 },
- 2 // 最多重试2次
- )
-
- if (res.success && res.data?.isPurchased) {
- console.log('[AccessManager] 已购买:', sectionId, res.data.reason)
-
- // 同步更新本地缓存(仅用于展示,不作权限依据)
- this.syncLocalCache(sectionId, res.data)
-
- return this.accessStates.UNLOCKED_PURCHASED
- }
-
- console.log('[AccessManager] 未购买:', sectionId)
- return this.accessStates.LOCKED_NOT_PURCHASED
-
- } catch (error) {
- console.error('[AccessManager] 权限判断失败:', error)
- // 网络/服务端错误 → 保守策略:返回错误状态
- return this.accessStates.ERROR
- }
- }
-
- /**
- * 带重试的请求
- */
- async requestWithRetry(url, options = {}, maxRetries = 3) {
- let lastError = null
-
- for (let i = 0; i < maxRetries; i++) {
- try {
- const res = await app.request(url, options)
- return res
- } catch (e) {
- lastError = e
- console.warn(`[AccessManager] 第 ${i+1} 次请求失败:`, url, e.message)
-
- // 如果不是最后一次,等待后重试(指数退避)
- if (i < maxRetries - 1) {
- await this.sleep(1000 * (i + 1))
- }
- }
- }
-
- throw lastError
- }
-
- /**
- * 同步更新本地购买缓存(仅用于展示,不作权限依据)
- */
- syncLocalCache(sectionId, purchaseData) {
- if (purchaseData.reason === 'has_full_book') {
- app.globalData.hasFullBook = true
- }
-
- if (!app.globalData.purchasedSections.includes(sectionId)) {
- app.globalData.purchasedSections = [...app.globalData.purchasedSections, sectionId]
- }
-
- // 更新 storage
- const userInfo = app.globalData.userInfo || {}
- userInfo.hasFullBook = app.globalData.hasFullBook
- userInfo.purchasedSections = app.globalData.purchasedSections
- wx.setStorageSync('userInfo', userInfo)
- }
-
- /**
- * 刷新用户购买状态(从 orders 表拉取最新)
- */
- async refreshUserPurchaseStatus() {
- const userId = app.globalData.userInfo?.id
- if (!userId) return
-
- try {
- const res = await app.request(`/api/miniprogram/user/purchase-status?userId=${encodeURIComponent(userId)}`)
-
- if (res.success && res.data) {
- app.globalData.hasFullBook = res.data.hasFullBook || false
- app.globalData.purchasedSections = res.data.purchasedSections || []
-
- const userInfo = app.globalData.userInfo || {}
- userInfo.hasFullBook = res.data.hasFullBook
- userInfo.purchasedSections = res.data.purchasedSections
- wx.setStorageSync('userInfo', userInfo)
-
- console.log('[AccessManager] 购买状态已刷新:', {
- hasFullBook: res.data.hasFullBook,
- purchasedCount: res.data.purchasedSections.length
- })
- }
- } catch (e) {
- console.error('[AccessManager] 刷新购买状态失败:', e)
- }
- }
-
- /**
- * 获取状态对应的用户提示文案
- */
- getStateMessage(accessState) {
- const messages = {
- [this.accessStates.UNKNOWN]: '加载中...',
- [this.accessStates.FREE]: '免费阅读',
- [this.accessStates.LOCKED_NOT_LOGIN]: '登录后继续阅读',
- [this.accessStates.LOCKED_NOT_PURCHASED]: '购买后继续阅读',
- [this.accessStates.UNLOCKED_PURCHASED]: '已解锁',
- [this.accessStates.ERROR]: '网络异常,请重试'
- }
- return messages[accessState] || '未知状态'
- }
-
- /**
- * 判断是否可访问全文
- */
- canAccessFullContent(accessState) {
- return [this.accessStates.FREE, this.accessStates.UNLOCKED_PURCHASED].includes(accessState)
- }
-
- /**
- * 工具:延迟
- */
- sleep(ms) {
- return new Promise(resolve => setTimeout(resolve, ms))
- }
-}
-
-// 导出单例
-const accessManager = new ChapterAccessManager()
-export default accessManager
diff --git a/归档/miniprogram/utils/payment.js b/归档/miniprogram/utils/payment.js
deleted file mode 100644
index 9e048d1a..00000000
--- a/归档/miniprogram/utils/payment.js
+++ /dev/null
@@ -1,211 +0,0 @@
-// miniprogram/utils/payment.js
-// 微信支付工具类
-
-const app = getApp()
-
-/**
- * 发起微信支付
- * @param {Object} options - 支付选项
- * @param {String} options.orderId - 订单ID
- * @param {Number} options.amount - 支付金额(元)
- * @param {String} options.description - 商品描述
- * @param {Function} options.success - 成功回调
- * @param {Function} options.fail - 失败回调
- */
-function wxPay(options) {
- const { orderId, amount, description, success, fail } = options
-
- wx.showLoading({
- title: '正在支付...',
- mask: true
- })
-
- // 1. 调用后端创建支付订单
- wx.request({
- url: `${app.globalData.apiBase}/payment/create`,
- method: 'POST',
- header: {
- 'Authorization': `Bearer ${wx.getStorageSync('token')}`
- },
- data: {
- orderId,
- amount,
- description,
- paymentMethod: 'wechat'
- },
- success: (res) => {
- wx.hideLoading()
-
- if (res.statusCode === 200) {
- const paymentData = res.data
-
- // 2. 调起微信支付
- wx.requestPayment({
- timeStamp: paymentData.timeStamp,
- nonceStr: paymentData.nonceStr,
- package: paymentData.package,
- signType: paymentData.signType || 'RSA',
- paySign: paymentData.paySign,
- success: (payRes) => {
- console.log('支付成功', payRes)
-
- // 3. 通知后端支付成功
- notifyPaymentSuccess(orderId, paymentData.prepayId)
-
- wx.showToast({
- title: '支付成功',
- icon: 'success',
- duration: 2000
- })
-
- success && success(payRes)
- },
- fail: (payErr) => {
- console.error('支付失败', payErr)
-
- if (payErr.errMsg.indexOf('cancel') !== -1) {
- wx.showToast({
- title: '支付已取消',
- icon: 'none'
- })
- } else {
- wx.showToast({
- title: '支付失败',
- icon: 'none'
- })
- }
-
- fail && fail(payErr)
- }
- })
- } else {
- wx.showToast({
- title: res.data.message || '创建订单失败',
- icon: 'none'
- })
- fail && fail(res)
- }
- },
- fail: (err) => {
- wx.hideLoading()
- console.error('请求失败', err)
-
- wx.showToast({
- title: '网络请求失败',
- icon: 'none'
- })
-
- fail && fail(err)
- }
- })
-}
-
-/**
- * 通知后端支付成功
- * @param {String} orderId
- * @param {String} prepayId
- */
-function notifyPaymentSuccess(orderId, prepayId) {
- wx.request({
- url: `${app.globalData.apiBase}/payment/notify`,
- method: 'POST',
- header: {
- 'Authorization': `Bearer ${wx.getStorageSync('token')}`
- },
- data: {
- orderId,
- prepayId,
- status: 'success'
- },
- success: (res) => {
- console.log('支付通知成功', res)
- },
- fail: (err) => {
- console.error('支付通知失败', err)
- }
- })
-}
-
-/**
- * 查询订单状态
- * @param {String} orderId
- * @param {Function} callback
- */
-function queryOrderStatus(orderId, callback) {
- wx.request({
- url: `${app.globalData.apiBase}/payment/query`,
- method: 'GET',
- header: {
- 'Authorization': `Bearer ${wx.getStorageSync('token')}`
- },
- data: { orderId },
- success: (res) => {
- if (res.statusCode === 200) {
- callback && callback(true, res.data)
- } else {
- callback && callback(false, null)
- }
- },
- fail: () => {
- callback && callback(false, null)
- }
- })
-}
-
-/**
- * 购买完整电子书
- * @param {Function} success
- * @param {Function} fail
- */
-function purchaseFullBook(success, fail) {
- // 计算动态价格:9.9 + (天数 * 1元)
- const basePrice = 9.9
- const startDate = new Date('2025-01-01') // 书籍上架日期
- const today = new Date()
- const daysPassed = Math.floor((today - startDate) / (1000 * 60 * 60 * 24))
- const currentPrice = basePrice + daysPassed
-
- const orderId = `ORDER_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
-
- wxPay({
- orderId,
- amount: currentPrice,
- description: 'Soul派对·创业实验 完整版',
- success: (res) => {
- // 更新本地购买状态
- updatePurchaseStatus(true)
- success && success(res)
- },
- fail
- })
-}
-
-/**
- * 更新购买状态
- * @param {Boolean} isPurchased
- */
-function updatePurchaseStatus(isPurchased) {
- const userInfo = app.getUserInfo()
- if (userInfo) {
- userInfo.isPurchased = isPurchased
- wx.setStorageSync('userInfo', userInfo)
- app.globalData.userInfo = userInfo
- }
-}
-
-/**
- * 检查是否已购买
- * @returns {Boolean}
- */
-function checkPurchaseStatus() {
- const userInfo = app.getUserInfo()
- return userInfo ? userInfo.isPurchased : false
-}
-
-module.exports = {
- wxPay,
- queryOrderStatus,
- purchaseFullBook,
- checkPurchaseStatus,
- updatePurchaseStatus
-}
diff --git a/归档/miniprogram/utils/readingTracker.js b/归档/miniprogram/utils/readingTracker.js
deleted file mode 100644
index a52fb896..00000000
--- a/归档/miniprogram/utils/readingTracker.js
+++ /dev/null
@@ -1,246 +0,0 @@
-/**
- * 阅读进度追踪器
- * 记录阅读进度、时长、是否读完,支持断点续读
- */
-
-const app = getApp()
-
-class ReadingTracker {
- constructor() {
- this.activeTracker = null
- this.reportInterval = null
- }
-
- /**
- * 初始化阅读追踪
- */
- init(sectionId) {
- // 清理旧的追踪器
- this.cleanup()
-
- this.activeTracker = {
- sectionId,
- startTime: Date.now(),
- lastScrollTime: Date.now(),
- totalDuration: 0,
- maxProgress: 0,
- lastPosition: 0,
- isCompleted: false,
- completedAt: null,
- scrollTimer: null
- }
-
- console.log('[ReadingTracker] 初始化追踪:', sectionId)
-
- // 恢复上次阅读位置
- this.restoreLastPosition(sectionId)
-
- // 开始定期上报(每30秒)
- this.startProgressReport()
- }
-
- /**
- * 恢复上次阅读位置(断点续读)
- */
- restoreLastPosition(sectionId) {
- try {
- const progressData = wx.getStorageSync('reading_progress') || {}
- const lastProgress = progressData[sectionId]
-
- if (lastProgress && lastProgress.lastPosition > 100) {
- setTimeout(() => {
- wx.pageScrollTo({
- scrollTop: lastProgress.lastPosition,
- duration: 300
- })
-
- wx.showToast({
- title: `继续阅读 (${lastProgress.progress}%)`,
- icon: 'none',
- duration: 2000
- })
- }, 500)
- }
- } catch (e) {
- console.warn('[ReadingTracker] 恢复位置失败:', e)
- }
- }
-
- /**
- * 更新阅读进度(由页面滚动事件调用)
- */
- updateProgress(scrollInfo) {
- if (!this.activeTracker) return
-
- const { scrollTop, scrollHeight, clientHeight } = scrollInfo
- const totalScrollable = scrollHeight - clientHeight
-
- if (totalScrollable <= 0) return
-
- const progress = Math.min(100, Math.round((scrollTop / totalScrollable) * 100))
-
- // 更新最大进度
- if (progress > this.activeTracker.maxProgress) {
- this.activeTracker.maxProgress = progress
- this.activeTracker.lastPosition = scrollTop
- this.saveProgressLocal()
-
- console.log('[ReadingTracker] 进度更新:', progress + '%')
- }
-
- // 检查是否读完(≥90%)
- if (progress >= 90 && !this.activeTracker.isCompleted) {
- this.checkCompletion()
- }
- }
-
- /**
- * 检查是否读完(需要停留3秒)
- */
- async checkCompletion() {
- if (!this.activeTracker || this.activeTracker.isCompleted) return
-
- // 等待3秒,确认用户真的读到底部
- await this.sleep(3000)
-
- if (this.activeTracker && this.activeTracker.maxProgress >= 90 && !this.activeTracker.isCompleted) {
- this.activeTracker.isCompleted = true
- this.activeTracker.completedAt = Date.now()
-
- console.log('[ReadingTracker] 阅读完成:', this.activeTracker.sectionId)
-
- // 标记已读(app.js 里的已读章节列表)
- app.markSectionAsRead(this.activeTracker.sectionId)
-
- // 立即上报完成状态
- await this.reportProgressToServer(true)
-
- // 触发埋点
- this.trackEvent('chapter_completed', {
- sectionId: this.activeTracker.sectionId,
- duration: this.activeTracker.totalDuration
- })
-
- wx.showToast({
- title: '已完成阅读',
- icon: 'success',
- duration: 1500
- })
- }
- }
-
- /**
- * 保存进度到本地
- */
- saveProgressLocal() {
- if (!this.activeTracker) return
-
- try {
- const progressData = wx.getStorageSync('reading_progress') || {}
- progressData[this.activeTracker.sectionId] = {
- progress: this.activeTracker.maxProgress,
- lastPosition: this.activeTracker.lastPosition,
- lastOpenAt: Date.now()
- }
- wx.setStorageSync('reading_progress', progressData)
- } catch (e) {
- console.warn('[ReadingTracker] 保存本地进度失败:', e)
- }
- }
-
- /**
- * 开始定期上报
- */
- startProgressReport() {
- // 每30秒上报一次
- this.reportInterval = setInterval(() => {
- this.reportProgressToServer(false)
- }, 30000)
- }
-
- /**
- * 上报进度到服务端
- */
- async reportProgressToServer(isCompletion = false) {
- if (!this.activeTracker) return
-
- const userId = app.globalData.userInfo?.id
- if (!userId) return
-
- // 计算本次上报的时长
- const now = Date.now()
- const duration = Math.round((now - this.activeTracker.lastScrollTime) / 1000)
- this.activeTracker.totalDuration += duration
- this.activeTracker.lastScrollTime = now
-
- try {
- await app.request('/api/miniprogram/user/reading-progress', {
- method: 'POST',
- data: {
- userId,
- sectionId: this.activeTracker.sectionId,
- progress: this.activeTracker.maxProgress,
- duration: this.activeTracker.totalDuration,
- status: this.activeTracker.isCompleted ? 'completed' : 'reading',
- completedAt: this.activeTracker.completedAt
- }
- })
-
- if (isCompletion) {
- console.log('[ReadingTracker] 完成状态已上报')
- }
- } catch (e) {
- console.warn('[ReadingTracker] 上报进度失败,下次重试:', e)
- }
- }
-
- /**
- * 页面隐藏/卸载时调用(立即上报)
- */
- onPageHide() {
- if (this.activeTracker) {
- this.reportProgressToServer(false)
- }
- }
-
- /**
- * 清理追踪器
- */
- cleanup() {
- if (this.reportInterval) {
- clearInterval(this.reportInterval)
- this.reportInterval = null
- }
-
- if (this.activeTracker) {
- this.reportProgressToServer(false)
- this.activeTracker = null
- }
- }
-
- /**
- * 获取当前章节的阅读进度(用于展示)
- */
- getCurrentProgress() {
- return this.activeTracker ? this.activeTracker.maxProgress : 0
- }
-
- /**
- * 数据埋点(可对接统计平台)
- */
- trackEvent(eventName, eventData) {
- console.log('[Analytics]', eventName, eventData)
- // TODO: 接入微信小程序数据助手 / 第三方统计
- }
-
- /**
- * 工具:延迟
- */
- sleep(ms) {
- return new Promise(resolve => setTimeout(resolve, ms))
- }
-}
-
-// 导出单例
-const readingTracker = new ReadingTracker()
-export default readingTracker
diff --git a/归档/miniprogram/utils/util.js b/归档/miniprogram/utils/util.js
deleted file mode 100644
index 855e96fd..00000000
--- a/归档/miniprogram/utils/util.js
+++ /dev/null
@@ -1,182 +0,0 @@
-/**
- * Soul创业实验 - 工具函数
- */
-
-// 格式化时间
-const formatTime = date => {
- const year = date.getFullYear()
- const month = date.getMonth() + 1
- const day = date.getDate()
- const hour = date.getHours()
- const minute = date.getMinutes()
- const second = date.getSeconds()
-
- return `${[year, month, day].map(formatNumber).join('/')} ${[hour, minute, second].map(formatNumber).join(':')}`
-}
-
-const formatNumber = n => {
- n = n.toString()
- return n[1] ? n : `0${n}`
-}
-
-// 格式化日期
-const formatDate = date => {
- const year = date.getFullYear()
- const month = date.getMonth() + 1
- const day = date.getDate()
- return `${year}-${formatNumber(month)}-${formatNumber(day)}`
-}
-
-// 格式化金额
-const formatMoney = (amount, decimals = 2) => {
- return Number(amount).toFixed(decimals)
-}
-
-// 防抖函数
-const debounce = (fn, delay = 300) => {
- let timer = null
- return function (...args) {
- if (timer) clearTimeout(timer)
- timer = setTimeout(() => {
- fn.apply(this, args)
- }, delay)
- }
-}
-
-// 节流函数
-const throttle = (fn, delay = 300) => {
- let last = 0
- return function (...args) {
- const now = Date.now()
- if (now - last >= delay) {
- fn.apply(this, args)
- last = now
- }
- }
-}
-
-// 生成唯一ID
-const generateId = () => {
- return 'id_' + Date.now().toString(36) + Math.random().toString(36).substr(2)
-}
-
-// 检查手机号格式
-const isValidPhone = phone => {
- return /^1[3-9]\d{9}$/.test(phone)
-}
-
-// 检查微信号格式
-const isValidWechat = wechat => {
- return wechat && wechat.length >= 6 && wechat.length <= 20
-}
-
-// 深拷贝
-const deepClone = obj => {
- if (obj === null || typeof obj !== 'object') return obj
- if (obj instanceof Date) return new Date(obj)
- if (obj instanceof Array) return obj.map(item => deepClone(item))
- if (obj instanceof Object) {
- const copy = {}
- Object.keys(obj).forEach(key => {
- copy[key] = deepClone(obj[key])
- })
- return copy
- }
-}
-
-// 获取URL参数
-const getQueryParams = url => {
- const params = {}
- const queryString = url.split('?')[1]
- if (queryString) {
- queryString.split('&').forEach(pair => {
- const [key, value] = pair.split('=')
- params[decodeURIComponent(key)] = decodeURIComponent(value || '')
- })
- }
- return params
-}
-
-// 存储操作
-const storage = {
- get(key) {
- try {
- return wx.getStorageSync(key)
- } catch (e) {
- console.error('获取存储失败:', e)
- return null
- }
- },
- set(key, value) {
- try {
- wx.setStorageSync(key, value)
- return true
- } catch (e) {
- console.error('设置存储失败:', e)
- return false
- }
- },
- remove(key) {
- try {
- wx.removeStorageSync(key)
- return true
- } catch (e) {
- console.error('删除存储失败:', e)
- return false
- }
- },
- clear() {
- try {
- wx.clearStorageSync()
- return true
- } catch (e) {
- console.error('清除存储失败:', e)
- return false
- }
- }
-}
-
-// 显示Toast
-const showToast = (title, icon = 'none', duration = 2000) => {
- wx.showToast({ title, icon, duration })
-}
-
-// 显示Loading
-const showLoading = (title = '加载中...') => {
- wx.showLoading({ title, mask: true })
-}
-
-// 隐藏Loading
-const hideLoading = () => {
- wx.hideLoading()
-}
-
-// 显示确认框
-const showConfirm = (title, content) => {
- return new Promise((resolve) => {
- wx.showModal({
- title,
- content,
- success: res => resolve(res.confirm)
- })
- })
-}
-
-module.exports = {
- formatTime,
- formatDate,
- formatMoney,
- formatNumber,
- debounce,
- throttle,
- generateId,
- isValidPhone,
- isValidWechat,
- deepClone,
- getQueryParams,
- storage,
- showToast,
- showLoading,
- hideLoading,
- showConfirm
-}