From 6ca0770bf6c49de667b9b5e5408f1a89fdb1b0cb Mon Sep 17 00:00:00 2001 From: Jafeng <2998840497@qq.com> Date: Thu, 4 Jun 2026 16:02:30 +0800 Subject: [PATCH] first commit --- .env.development | 1 + .env.production | 1 + .gitignore | 6 + README.md | 72 ++++++++++ assets/background.png | Bin 0 -> 27157 bytes assets/default-avatar.png | Bin 0 -> 560 bytes index.html | 46 +++++++ package.json | 15 ++ scripts/build.js | 102 ++++++++++++++ scripts/dev-server.js | 103 ++++++++++++++ src/main.js | 130 ++++++++++++++++++ src/styles.css | 281 ++++++++++++++++++++++++++++++++++++++ 12 files changed, 757 insertions(+) create mode 100644 .env.development create mode 100644 .env.production create mode 100644 .gitignore create mode 100644 README.md create mode 100644 assets/background.png create mode 100644 assets/default-avatar.png create mode 100644 index.html create mode 100644 package.json create mode 100644 scripts/build.js create mode 100644 scripts/dev-server.js create mode 100644 src/main.js create mode 100644 src/styles.css diff --git a/.env.development b/.env.development new file mode 100644 index 0000000..8b1c6ce --- /dev/null +++ b/.env.development @@ -0,0 +1 @@ +api_url=http://localhost:8080 diff --git a/.env.production b/.env.production new file mode 100644 index 0000000..d825bfc --- /dev/null +++ b/.env.production @@ -0,0 +1 @@ +api_url=https://ykt.youcan365.com diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..57f46ad --- /dev/null +++ b/.gitignore @@ -0,0 +1,6 @@ +node_modules/ +dist/ +*.zip +*.log +.DS_Store +Thumbs.db diff --git a/README.md b/README.md new file mode 100644 index 0000000..79dd69e --- /dev/null +++ b/README.md @@ -0,0 +1,72 @@ +# YKT Friend Bridge H5 + +短信好友邀请中转页。发布包名为 `sms.zip`,默认短路径为 `/sms/{recordId}`。 + +## Local Dev + +```bash +npm run dev +``` + +本地默认读取 `.env.development`: + +```text +api_url=http://localhost:8080 +``` + +默认地址: + +```text +http://localhost:5174/sms/{recordId} +``` + +本地服务端默认读取: + +```text +http://localhost:8080/sms/friend-link/{recordId}/data +``` + +## Build + +```bash +npm run build +npm run package +``` + +生产默认读取 `.env.production`: + +```text +api_url=https://ykt.youcan365.com +``` + +产物: + +```text +dist/sms/ +sms.zip +``` + +`sms.zip` 内部自带 `sms/` 目录,上传后解压到站点根目录即可: + +```bash +unzip -o sms.zip -d /var/www +``` + +`api_url` 必须配置,页面内部接口只请求: + +```text +/sms/friend-link/{recordId}/data +``` + +如需覆盖服务端域名,打包前传入: + +```bash +api_url=https://your-api-domain.com npm run package +``` + +后端必须配置 H5 域名,生成短信链接时会使用短路径: + +```text +CONFIG_SMS_FRIEND_LINK_BASE_URL=https://your-h5-domain.com +生成链接:https://your-h5-domain.com/sms/{recordId} +``` diff --git a/assets/background.png b/assets/background.png new file mode 100644 index 0000000000000000000000000000000000000000..030d4e3bf5c8f92eaf33a98f64ca1611e134d3ca GIT binary patch literal 27157 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 z zl#S(S(z&D}Yn`-S7cKsK={5SMUczOSD=PzJxPA6Kc&az5@KhV#Ao3)ZpmcwWxc330 z5{4oYuiJa8{#T11mx=yN;{WxvmX`8sKj4vt3pt5MqTkA=Wz{y$?YN4?T zvyY9hl6++GU&s|WA>qT7hPoH;Sthw-w{B*wkN5-7jFb{2gDROy-S-1i#gP<5V&n@{ zN=PfRtxVt4sr{W$Iq=b9S+?3@Mai)9^eh-Tura;|cgQN>dWFB~6%pgP!9$%hHbIJKu2J6*#WWknpYoRE5#jEDOysK=kF1g$s|-U;3>`r1_>U) zejr1Q*^OS{c>FKiObYRmVyFL2vapZ9#YWdy2S=x1Gglz?HH^U3iNxwupY zG>=2&g5$Pax3R0lHZIhgr9bgK^n{AWJgw-gGR~mqSDdsudXGrLF4=dM$KC!2suUn* z=Xt?m$vT@HG%84H>V|XQ!@~t!Ht#w7c?wR0Vd(Azmg+51OJ&D>D{Le8zBIkQKmQL* z8<+?4XahDf3nm2@V3NB%qwWX8tdosasPzxAZ3?UpjwEU~Zm?iWPcM4MX$8ohoWH{In0t^K(8`!2zd7H!TD=@ zdl08@WasHmYUjab!|=TEo!y}20tQPff6%ye*Cwjhw@xdzaaEK4KkTxqc!c&(pbQ6B)VeHpx0PyTiVxf(V{}^POstnee1aZZ#>Zc08@FxAuiliAOt1 zV_?FnYT4m!LxX!btvq5f4e*_;Q_%cCX%y3SZb=$BTB2s97Op3^kL~;tgKOcJA^Fz) zUXPJp!Hvh-TTz7^L#wo6W$A*(MM!n#% zcoU*ctn(8Xekcuy8KzRD3rZN( zOM&0@G8%PM(xse4AgANo$riRu~l%mfn}D66PMl`BmW5sdJj18C(G%l`GGUcB#NBdeWyDA z=#-3UrVu4xWoGyNj}03FQfNITQiI1XtFXegi~nBfcmNCtM+xV@oAJUGgS%VC{>kv* zzRhAe^voK<9mVT}eRsWK^+m3!2(Q>mX+~Ln9P~>Ga^Je2CXJ638^w=V!;@;23)nb5 zR~v9mp^y$xv9jZZ3l4sq@>RozD=af@JbKM`s?)VQuLk9;FBV~o-TIWoI|||d<~E_f zX$T|Z1ygRBK#@wED-?(rXR3Qc?`QFXS)7s^fA61P^ z!NK!||HL!zu1&7&_=JOhG|RH|^6tS(KKUwtL8Kz#BVBr%D$5Ridfj(_woEWPrhpIW z*AM5uOl?*ZR>-vt*AzJyO;x8K7^p28_bq~DJ@o%o?E`)Vx7B&)BNbO&?G9HbvL-vV zZ`#PW(FC7XUw-3rZE~rQBNVPKZ#u1jGEr@0N+KdIWT9~Gc~w21d4;~IebCs%tSgj3 zc7z^g{$`u1KwJkjs}vo$PZln1ee{}pYY5w+M24UUS;z_O{rCbZ#p9OXMfP!6X~~~8 z3K#A+swBM}MBkt@iv6-Wy_|dSy-E}XKW>)r7@^cCp$KiW@uk?<8JAqrLRljsIypW5 zu=cIhZ^E+yUSF*sqwEuq_*=|pL^3~`tv$tz!0+qSC+=R}-<^Xo4T$fW>2S$z&c2#rHr{*% zc{PcLI+bNQ)una*tJmeUNFW8n4~tW1HgwG*{rrrHz4s3p4?bGmP;7b^87o`Lr|0*X zWksrz?^nv(4`rU2f=K_PyC3qL=+!kF|3`CPB2scjAR)+qym3jA<$x^Fvg8*BVAU6I}opIn(QO~e8Do5QYw&AyaX4T7@=LnNo*Rseb^pg>q}?WeRsDmy%S723l@by$j3!ym*Su zsz62@2Tjx((A}z6Wqp z0&q_Str=OFGW6r~*0)j5>j_CKfg_Vm!Q< zrNwx}Op-7j235cRD9x9i`PA2|EvDxgPD=as$j!X9@_oX{OnIa@S+^gFv z!-iQtE8TQHH*I@xZZrsNB^W79gm&ISTA8w9`8vHyl%@NgUz-~^i@CiF)_)T^J&UfQ zIiMPTFOc}5TElPz?FFw9O~GTIHP3!!=1nxRYzRk4(Jrd-`dG6Id6l3swQJ~8GKuZs zgvv~LW*Yd6ZuW9nHqa7Vy+#T{14ElOm8diUrs%$z?P_v0B_{NEw{?Y{Y*i(-y;orISmO|X4&PC7&~0I=J_vRh%XW5 z(Agdrf7bB~DoZiY8NP;7gGEd(Qf1^ohm{s~X|1eWVCRzz(#QL`dp|55D5LH$;DGrz zlUNc;qdx6v=XcJ8enr5&W3dBY-Sq&ZK!X^>!q zmsHsKg=kw)!lAm~7!!_93-4|dS_48j@==;Cod!)H_iLkUUJn7Yi`{Y8)of6;wL{ra zqn_S}+S?0i>fyI~BV`v~Y-vc$TxxfYOI<1LoS0U&?o;AnY&l(rDwjU)JSkG6c&+Q$ zN#Mxm>ZsEBG(lE!Id#2#v{FfdX;M-AqN0>BTGhz*-)~ua#N77R)MYOzz%OIW*-a<8 zKg-#4nUoYEk!D=|J#vZT`Dq;hSMsE7n)_2;M4rjzl-_Vc>Vii)S63TlH3vxV8Mt0h za6T4@+Ynmy0lz%L0l;$iXo=o~pVb~-8TLeyPztxfaE3CO4_3+bc~X#6WGtv64=~V3 zL2?%eAHcJ|JLtF%8-DPRqSWKyE$Vt`+j)xsx)WlPTE4dyOeBH;v!mED~URsc@PapKa)4lrAp*4Xa)v1 z1Z469ddn^3uGtIS&%0@U9E_BjUn=LNRA}J+a1Icbw`znlABqh-pH}Yy^(hM0vd@3o zd7OCk4jjGn>!_5AA;2{Bu-bXqYaPSDjcX=yV$(+Qya;l;@qZ#TpH>yhDBgqVVWfN+ zBp%${-ll+~Qf;w?HY1TO6R5ZV03ZNKL_t&&L&tkQUr-!$wTN3#8g;TOPfgY&-|`x3 zMIL6^W>cBo-ql|Gq0BE*@aieW&eM>`RmkBe;;zhVOt!U$maDyFMq*3QJ(EjoezbV5h^zCfGm~6 zie`U+Gwh&~{c62(GV(o7Q7B^Wf3M>c%H?d|gK5-kw(nxSDNXxt9`O1IK`!$EZUhl_ zMQIfQj!CVoI<*U1^&Y?M&x@b@6w%IW8c6X0Xs+(DLYnJ#N^7}uo#?QJ!{4G%!{);f zr?9$C1~FBpWSyp})%mbY>Qb`MS7b}{gR$k{NF(U~45Zk3jlyS#272GssVo$B_+Shh zzE<~HZS`0?y^AU@ZxR?cyI$nVGyjiyDz-QuBGW0nVG&(ui-jtu&Bbi5II>i}>p2yk zfL%B*8@fr`&451Cc_M{xXd9ip_ofB%7+la{(JdWyy(E$Sxa;0{G;enNGqIA%G*aQ5Bqn4Pl%Y7MY z2d45DBX^zI-8VMnW_27i(1%8V;>NgALK16)fTOUoj+U5Z5^CZ_5;%>_4}h1k%TzBC zcz^@Bak9Oi8u6`E8yIn$hB&<5bWx6ok63L;E`T4+ z2oWF6Y;=>~G21-rS*jXqy@fC{B($d7CXU;P8`#R?WxhD7N6b^`b}4*%2;G6%~wjofpjoCwuIMKE$&or&AnTjhf8qm2>NjFcEkO-(G* zvbD+I6@U+n(P#v_=&n?wCw;6MdYl#X63?%kVG|{eTN!bIs?vs*QkUIgb&>imcAM;( zabNAzbp6PD2BO6OV;WbSIS+*b0|$^9uU2tfyr+siSh|2U`iUaA>ZJH5NMQ zHZ=gcsn453X)H*naRU|WQN&(K!-M#q+2-lrR#1wTrkJOu=O;BL2+a`6#M@0cCJczT z##ivAfzOl@eO&AV1>AqU0W7qhnI_iC&HJZS{5p%9s4(gRj*T$DWnaLhUs34ydtsjhf0pHyZ80lz zbH)v}5_`H)xa-*U&<5GZbZ;=Tt$-hi^d5sSt}X7Pz)zEg)*4%6RC;conSG-Tqz`oA zn-xlqaU}{v(&9EV^Fh;*$+%Hi?!Dk<38qpE=+n_KLfOWmJ|b=UZX}+oyvb*`cY@y* zKG&4Hkp~t*&t0SYIF^-)d@jCe>tlGQd_f@FI^5KR{h#F9wS!fWfWRieJ|pYMDB`~L zJml}H8@nlRD*~Vk);)~YIuf5it;+v^KZYQ5N>Pl=DTcx_j0n>Mp*=Jz^EaPs5TXIX z1ZchI;bku=#)vf7Q05Wj#&y?%J}@t^C~Yd!5C{D1)4YR*Y$Wi>fSqn655FOwYl9i* z6OaAAJ1O{RWcjo4(D zI-YKR{bKBq-|g`15hU5*%U`;wVNiK+k<=#NGaCje%sTg>a{JSy(JI{oxV)+Lu#0id z8t{n`lt$kY&NTZlE$%ySz@INp9Y~|{n1)Y2;6ae8GHo6{)~Z?!y?qIKrF;#B>*VOh z6hP?8l-PPwkfBpT*!#3;uTz}g_wd-doTc7eJU(nB`%iu>=5mUNAEdIvqzpOYL3=4SIgQxU zx_MMfu5#ASwe@GC>?C}xPBzpsJq#hQiBly1Y4GRVy#H$HU~gCU{Vqd|+t$silcKt5 z@QYBqAF80uQ#JLeU7b@`w_2y^^gy&q)nf}Av=(NsDw&lvZcMox)ZV5$_lWVwnph8J_WRs9^f*>>lVyenm6adrPytDir zW;#OTGHa4WX#)U3oWKB(^Zz|D67jd*W=m)2EM!8l~a%stlgd4k&r ztC7ft#Sb|AlWPp6<;1)|{;M-fXGe88ZjgOhdC0P5-?I`|K`(qQ|C6vD$67ufXgI)&RS(|@%dcX zd|S*wLY|wa8uQnG``qJ)hyTkU+`ii&IZ9_m;sFwM8b==f^lo$Vovd>{UzR8$$>wvh z6+=xT;#%I*#EK<_(_|bxyz=Nlf>g$dCE3u0unZT&i-(qxS!d*cp&tCr?{)Kmx{z!Y zH>spHec-1-+830TkWL9RZnKiIQEPs89W!{~gCzd}R^mpDISrVhNpYg~QIz>C*{rIT^z6a&HR)+N@YSOXgd2a-2+5}S246Qm&CQ<(Wb zecv0_X^Vp(2X|pLvM^F6aUY=wJwu>s9M{AT^8pv5x6+Ii^!7nF4`3xgw0ypA8+UYV z#RKcpJ-dA5LCe&K{5rg_Ye911l5*RLpseD6$8SpYKNQagA$I|$1q8>5wvNc@V2I1c z?HT<2lBG)@KEw}w6k{jdeELGqDYnfHH=iKT8e;=4m7DxghyOxx<^+%f9&~Y|cYJ81 z&duIuqhrwiPY$-brz(^0p72Ck z?5IK@&-5oB{@CjKA^EBa$JkzjjEzHdW<~A>pe$M3cwQUf$?kpEFjSv`{ z`K$N!&B|8@&L4k6b$~>PXbZ)t_K{d0$mAxPQ63cW~8_lUD1Yj z(vV}kZML>&<#}Ju^G3C4n;t8l>o+~f4_phj+pn<1yt8vbopkd$^*owK(F4N<3I?gt zWSuTT3dv~_J?^gB4{kqDA+(!Qw*R!;3`>7@C7a@fK5MQTA>NvUxt+Q&d)QHH^C5@l zlLe7QoM23Ya>K!Vz!6)i>PxlqRgF)UeY@No-@RX^v?6YCHQtM73k{seF}$-$GRaNk zHwVot4*#cs6s)1Xx_Jo=H6v`o2aW$$dtX`l`s|M*Ggm22@Ui|%c`&2<7Rt;gc)_lb~9q-H5RKz1d$FVfKb`MtaeT7+EtaUT)oR`Uz}y{ zmOBkv%QBfb7kkW6Bxs$;ye^Lf44uy=y^r`oVIwv>n{Ls+L4-;!L*s*rag!DE$5Z{g ze`)vqraz|4J(6-1+Kkr=YhrQvjO6bU1#n+%>Q4EiQYD3PV@{KH^H5;|y>6hKug`RV z?Q-`^#-HV~K9Xg=9@ zZCb7N{@ORbF#a7`?rngCE^H556u}Rf5`L~7{yMM&qsKi`Mu5SZX3RE-;5CM|x%l?G zZ<~79^uy*@pWN}d*}coMg|j7c@nSb3tW%ux8#MNaEMgvJPxIx=|DFk)XBqQawh;lK z(b(93vo#{iOSc*PKiP7(Ty1ehwBKE*Z11c#2f21V^q_-4;1LgYYPrQce<&-Q)fd^}pd z&+IMFl_jSq%wJ#C@=E9S zKHd;~Z>U5rn`*HStS7S1!N}R9Ra%)e-igBim5oBNd=)&DCR|`-WHCRvO<(06RqbF# z`FS=Ox~v{F&7rRA!*7DlSY`#TUvljp$*fg+L!VDZX_?05`rIJS`(|iM3>_(`{153W zXo6Gjk&$NVovH>M!vodQzh!ITmK6~4{>f@xu8iE(@!DM2t>DT9;=Ew`S)4BBzr3!| z2A0Q$QdB5sDBF-5cigel>ES05FK^prK$QRAapGc`Zcv@n!sR+NK0=N}`Jl+w#@`h7DhauXCdA2i?F)2$L4lHBkN$#o0>P`VOa?@g+Dfo|9GBw! zW8;r7QmT4Rysq+-ZCHSLk|v_laAOzUz*o1IFZlg=+m4W8{GwGVd3qy{PN7Wj-f zaV27sAve?&UMLdNYdkDNo<$Lt7LbD8U5UHizkbTI&MjcN7oZ+IuEqU0ksMG`!OWJS z&Ay}H3d4sc4z|N~L~Yn^0Vf-G0L_1(vgvUE9M{0<1|GDYQy&h$9WB2*Eq~jrlx=24 zcwU#GWFt#6EX3t~a}i!2wLaPfYMeuksBT00xfeP8ZsEZmpP+BmwzWJ;uPGIaaaGr= z^|(SJd+}ha!~}(SzD+{U=IxXh$-^`5LPloU~nf{k^dUpD;PRyjos|VQ;c@ zYpawDGeHTl{AD3etVDVF^{WXvy)YO?bEVoq0IU}YxB*8Cl7l3fd0icT^7hB`K`oCR z090bb5YIPsFjsh8FYQ*246~QU_gyRbY}>z|ds?AKeR4oqU-a&$oGJZ;lKVTud;_0v z4u8?|O8|sh&MrVSbaT{1`j*D>FAyfa1o|6W6gD^E_3T7U94IRhlP-|q9`seLvP*a% z(T4G+R=4L&Cf8zdb^*SmVoWRS90dRew z`k&aW^Ui2PpXYzSKi`tiN|KrQ{DMwX-F`zWbBkJ^pqzMN3*@n8CXwfwU1VhvGZmTk z1_Q37=~KPT>vt3?PguTgZ*5J}bE}k+KEO+;*5m7&)QnTy)SL05C~acGunEup_z^umE_wveOYyrflRM# zi60bjpK&8^gvrdFZeahorqIs;GxhrhE1pyKr#8pZ&CPBsIaG{BU}93ckl!{B1z-lb z3Gxd|%X9bJOf3)kb_BaoUrR6=v#oSpiWjhK}4C?7#*YKcu`?sejPQauTqFBb9lPXzh zO-2?ihTvW+-SWnY=U91UsW-uAN-WRWKr&i&rI*ubGNPp?I8c3cXkJt|W7W#rY_i=r zU5^pu1s~wL4sDD|cWjH?4eqP}oB?Keadt3PAbvOKO`X>as^6{UTWF>c|AjjYFn+H@ zB7yX&Tvb8DB}!!@<6qk1UM?%`A6T6M#}YD%{sRXy38Bodw$r+5-{Wa*j^IFjA8Rtx zWlL7U{NQr@Xx9ih%GE+ka9D`(12MfhGs9~o5>s#jsUQ}S4cI`AK2x^oz0+y*>=Vfc zo5KXK5`CTRlJxl`y-7f~xv}Ew;V4W-K~b_`EWsuZ>>q>K+@a4)jAsH4Z5uSfJ?~Q~ zZ14X)O_Ni=+2dcJt${p|^TE7oXV6Su3nadfbSg$7Zf%xXxIElOY;Q*1wF*`0J{s({ zb(GKQeN=Ps=L)U+BSxs}$qRe=P`2K!B-*SO;LeNP)Ai+jxG2N8D2n5il}`zZ`Aiij z$R(bEiHlWa^!X@iEA&xE7q(H~ksDj%XY-8V*p<#oZCC4yU8bx=vAo238b6B#+XU7G zL4M|%pjAHLd>U&9|A_BVN0zTo(B^Q(GDw457G8skJ7$*wrTG@nC%z;jVVs91sB#y- zCGPy9(!{Ojun9Zhu1T-oAt|(lk~DsE^!|GPg28V}#w^FEac{s`5z7k}Mk=zrC@f zc+39*&D^|~2Q55MEih`!V!Mm;an8g~RSytp_RX;X_4RK`a;Mu;tCX2VUL|l6U3|Vx z2$T|M;4^Y(UE6A)qh$}vdl*}olGobR;ZILf8)-D7ZT5SJg@L!SS^*shGTYGdsL#W9 ztlZXkX5)J|kaJse7W_eQ)|>X{5+Ws~70Y`wZGU@Rk^ft-QB=&I2gaLvq7#`4=||~C zP?^}j1hBO6+=TiiZZ7A(1HN$!+|+9@%mfY(sM^y%1)iaLP@%|DB0@cBK}1t8F~ z%eJ>hf;~#xnE*?FXdlJhuS4U?b_CQ-E=Wk%l9KtqWh|x-_a8D=50;OA&!&Ns7ToMx zs8iR^7?#D(4wNaEmuS8|*!#tIu{;PjCcKOp=8fyRfot61yzjMiVot_7EYEi~UiZW4 z>Gt>s5Kv!FuA1g$qrE^4Hl$Vxyvedhln&yI!{_O&yJ|QzZ=;6 z4`Go~0@Zp&rjz)5x_Bt=2gpVq{*+kh)9?~1CFa;OR}0**yaT}rhr;>oY-48?vq}91 zC=lL=^X&Ka-)XxfHW{szer_xCWytcji_!;QSDaovU;YevW8w2$X{4)lpwQEcyq?pU z+tk|5l<&Br{Ts&131-BT)q1trEh-DAk>eNDVjCh}$^$E=k70Pkn>*tkXI^F$V2hl= zCyO@k_4)cWDUlDMCtd9if2S&ZkS?ml4&X+fC`rHl^Du$C`+gvf?;@KL`-=mSG>?AT z3}d8GyS)4}>BxI?*!?Mw#lHh$W&L%B8n?vxDy>L7*rr*b$Sg*wQhw#Qt~mNx;NBHb z|L1TWKmrILj-#uzJ1Y0pYIW`F5EtNiZOxBaFUI*Q&2}SL-v1&Wk(f~_p)w3&MtHy* zIKFXJSD6h4=Tu&edcA%(%>_b%eRDEAPTm&J?B2d?Hy919HUI`0+9Hny;(Qr~23bjA zeWW9I0usdW?*hTas0|}&@*KRdoe#Xcrc%wJ{d2F^_fQLxms5irhcRRUWxfe*1rI99 zBKJ+-;$4H?6#33 z(N;qk^sz0-L5|%-H0a@1D1ibfw;jPYoLemH{ztnZ1r$%a!FcXVAcJ0g<-YgGR-D;A zWH!T95?iq^RCTfHy;oIai24)#hOzz$LH_oyC$f_JPp@Ei&NjAY@#@p^z&s0R;Ch4{ z7#@~y=Wlgs;Cdre@H!o?GRxXmnR06^3U=dE9_;-1nG;g)Vy6eO*%EPv`}8AA$Sqgh zVEJ@;X4*p(o?v+%xDu~CtTTs77NevYP`DwL0yGN*2xn_0Jl(VN;GSiC)84`K+Z#I0 zL9z$@MlMgFFy|z)F1){-ZMvRXS~E2N+h>7hOb@|}4dRUfSa453X15Or^jI_=z#UfZ zj&2`iO8N@rgJk(n^X(PKdUYDuhBrGGv&>A&UgXLM$;8O9Yw-anTi)Ir_wN`l5$dmR z-S*}PaE4$xR0mm|z>MV`ys596&1M!aN10#3d!`LLrhhi6eAuKxB|$}bz8n4NZ>RTc z@ZGKjhRY8HKfq?&Jl6G`=advW%01q`mvhKVAHar5KpP_+vG9Hm z*N5r%tM~Wpanc<1iXNi|zvhvx6P3sm_ij_HhpOd`3&oWVkNcu~)L9lk>dzPc9sA{g z>=`<_Yh56TI>PuXkXVqCBR$7~mNbYt3EcDpoqkxp#V64%e7ZiZNTU~ZR-YdeHnUth z-9`yF0Nel_+ZE^Tsm!w-bjMcQcDHofVYjFTr77_5is7bu-MlH9mZvJy^7Z8m^gN2$ z1rev_%B0gHNaBSrqp0CMg-R6|?yzW%*g3ax_f%~_L+CkxHbYtUu3DS2oa@R6QKRT5m9;`Oq34V&XY z24`{DWtNG6k6%3i`3#~9WBn*_h>3fybL`sMj z`i2!*-mb%;k&M91Dm;b>7|VI+=v}1Zp;MvF|9Jm|C`mSZ8b-%m*PNXa$SmMM$luSPop7<&*Lhf=%s21f^Dzi}XX?1E4jf zF6fBEb>va=yv_eM>IP#9IdC8D088_=24b^eHpvRq|Z+|D!n@8C*Kj;P(`rJu-4e_aAg1X zPH2A1SVMu8a6Xpsh34CP`~F218f>yNc2{=OtD(TUht55&K}TLGY*ZjNtv z?E?@3uuZj<<=M2-M6G*s=nZwhzJHAOE!5Mc+8Ob@r(|GhaQeI=4B%PRpePs{Gyaq3 z3hkFnQ!id1)UdU$*%O%o9578W+H5M~8-&ZI(j98gq41whz{7L;G?|~q9>)anJA@mN zs-JG%lRKQKnXy}9w+FVQ%_+3H$Y9xqP{Z;Fmt{klPq=J7%bleBr8+XExIc!7(^T6u z<{YF|a>R>L-$ItJQb8(iT3WwZz~_k)%4So?-uYR^I6t)?aQWJv0>WCpJjr%KX{HP2 z_WtEgIMeCCt}YH@0iv6g>dW>;i{#HBMO3?C`wx;L8LL?2d&SY9i+J z1c%?PwznKZW|C#3H>s;t8@B#=P9>ott*X+Ef$Mp8V8(E#G6>VUuO@MQkWPY$I1TqXGR1YH<1-% z_b*VZR$h!K2-*T^M*Lt;F(w# ze)|y{cKbvsxH-`23Bmx1i)i^ZiMXN-UIpH4rm>4mjnLuJSV_%81^iAmSmvr|dBgu3 z)=b>N@!){tUNvOVs4|I7z-9eEHbYS<&g11ss=lim_nUjno=JZSHG$8;V7a3zJTN~( zzCWLCqhi*keoCTwmZgL&&3q%v$6=BJ%!H9XQ<6%UlJUHY-8MGigMeHWNUErzImcvc z_=sFE%cG`{K?B
*t3tM5b6Z+p0qi+EBO?Jf^EJ0`(I?9Tq!p9Snj_BJuM(}jh}{S-9`N z59Ydwfu8hxtFBOWwQ6SSx`5?Dz@z0!XJ23L!^&p)SihVGUYMQ|Bs&Mo$`mbsrsYj{ zOisTrQ#GoIU9Cg4DAsRe`BIj57t3ecuwx=5v8k0TD)B;YT;I|1qbzS^c^{{!@p^1N z8fgd4RnhtXj$a_iGd&edvOPY@>{7wcWBEv9Q}tMGV~OHNiGAK;yDHky^4PII?Uujn z;Pad*WrrN$ByCRyi)HqM!Lp;}8@Nw1%TGq^6nK*4k%sMzG@Ma!9smbVfR=xElZM#U zYoj{29B8vWID-I>^~>@Ffqrf#70xjo*(h%7{03rzF}AJ1gF1TGNT0X%vv~!KurO+= zbYvhbtMLO=yOP-z%O*uBxpB*8Hp|qSm`pr1fQ+%G8Isu%9OESrFymTy3)@>xF4Elek^qz*+9Dj)mOgLQc8^lR){7HPR# z;q`Hp7Z3p4oBO)eTFZ~U8eidAZWErOCLf=a0 zOP)tzcS`F9x=3^jF9QTdqb;z6nZQ?FV~k8YaueStsh^G3U%ce_`fg%csfF>HtE)06(0HV? ceTL`$0e?S`AiA5mI{*Lx07*qoM6N<$g8NK%o&W#< literal 0 HcmV?d00001 diff --git a/assets/default-avatar.png b/assets/default-avatar.png new file mode 100644 index 0000000000000000000000000000000000000000..4eb587954368fc74189a34a26a96e356f0433429 GIT binary patch literal 560 zcmV-00?+-4P)*wd_wY9afva;LT-0tr1&CSm1>+QO_ zy5Qj9x3{=Gp>E`egj zB-!E$SHTP*L>1CoxM-Ugz(ZHGBg6Lz6ZRUkH!@e-47=O$R{}jTF1=$8Z(!84=r!WO zVJ<%8@h;nPuB*Ue=7Sc?zaYNO#qj0$K{FROdAxy*@svU9g8Neqf5{h5K|J>S(v1{l z1di3rJ>Z^cOuW_2(i7|r@S;oqOnXoD-lJx3)@Ucpo~xT{*J>x)-BSHLx${)-6XoVL z?L@Ofyl<~-CsIFJjoZAR^(5(KsntL85|d)y80Yk8Z}_jiN$Jw7s#Odx;{PDlDdLnX yP?aK1xlO1_`;F)-*O)H|f*=TjAP9mW?9K;~E^CkQH+8iD0000 + + + + + + 添加好友 + + + +
+ + +
+
+
+

医生服务好友

+

患者精益管理服务平台

+

+ +
+
+
+ +
+
+ + +

正在准备好友链接

+
+ + + + + diff --git a/package.json b/package.json new file mode 100644 index 0000000..ad06aaf --- /dev/null +++ b/package.json @@ -0,0 +1,15 @@ +{ + "name": "ykt-sms", + "version": "1.0.0", + "private": true, + "description": "YKT friend invite H5 bridge page", + "scripts": { + "build": "node scripts/build.js", + "build:dev": "node scripts/build.js development", + "build:prod": "node scripts/build.js production", + "package": "node scripts/build.js production && powershell -NoProfile -Command \"Compress-Archive -Path dist\\sms -DestinationPath sms.zip -Force\"", + "dev": "node scripts/dev-server.js", + "start": "node scripts/dev-server.js", + "check": "node --check scripts/dev-server.js && node --check src/main.js" + } +} diff --git a/scripts/build.js b/scripts/build.js new file mode 100644 index 0000000..ed47d5f --- /dev/null +++ b/scripts/build.js @@ -0,0 +1,102 @@ +const fs = require("fs"); +const path = require("path"); + +const root = path.resolve(__dirname, ".."); +const dist = path.join(root, "dist"); +const appDist = path.join(dist, "sms"); + +function normalizeText(value) { + return typeof value === "string" ? value.trim() : value ? String(value).trim() : ""; +} + +const mode = normalizeText(process.argv[2] || process.env.NODE_ENV || "production") || "production"; + +function assertNoLocalhost(name, value) { + if (!value) return; + if (/^(https?:\/\/)?(localhost|127\.0\.0\.1)(?::|\/|$)/i.test(value)) { + throw new Error(`${name} must not be localhost when building release package`); + } +} + +function assertRequired(name, value) { + if (!value) { + throw new Error(`${name} is required`); + } +} + +function copyDir(src, dest) { + fs.mkdirSync(dest, { recursive: true }); + for (const entry of fs.readdirSync(src, { withFileTypes: true })) { + const srcPath = path.join(src, entry.name); + const destPath = path.join(dest, entry.name); + if (entry.isDirectory()) { + copyDir(srcPath, destPath); + } else if (entry.isFile()) { + fs.copyFileSync(srcPath, destPath); + } + } +} + +function writeFile(filePath, content) { + fs.mkdirSync(path.dirname(filePath), { recursive: true }); + fs.writeFileSync(filePath, content); +} + +function parseEnvValue(value) { + const text = normalizeText(value); + if ( + (text.startsWith('"') && text.endsWith('"')) || + (text.startsWith("'") && text.endsWith("'")) + ) { + return text.slice(1, -1); + } + return text; +} + +function loadEnvFile(fileName) { + const filePath = path.join(root, fileName); + if (!fs.existsSync(filePath)) return {}; + const env = {}; + const content = fs.readFileSync(filePath, "utf8"); + for (const line of content.split(/\r?\n/)) { + const trimmed = line.trim(); + if (!trimmed || trimmed.startsWith("#")) continue; + const index = trimmed.indexOf("="); + if (index <= 0) continue; + const key = trimmed.slice(0, index).trim(); + const value = parseEnvValue(trimmed.slice(index + 1)); + env[key] = value; + } + return env; +} + +const fileEnv = { + ...loadEnvFile(".env"), + ...loadEnvFile(`.env.${mode}`), +}; + +const apiBase = normalizeText( + process.env.api_url || + fileEnv.api_url || + "", +).replace(/\/+$/, ""); + +assertRequired("api_url", apiBase); +if (mode === "production") { + assertNoLocalhost("api_url", apiBase); +} + +fs.rmSync(dist, { recursive: true, force: true }); +fs.mkdirSync(appDist, { recursive: true }); +fs.copyFileSync(path.join(root, "index.html"), path.join(appDist, "index.html")); +copyDir(path.join(root, "src"), path.join(appDist, "src")); +copyDir(path.join(root, "assets"), path.join(appDist, "assets")); +writeFile( + path.join(appDist, "config.js"), + `window.api_url=${JSON.stringify(apiBase)};\n`, +); + +console.log(`Built ykt-sms to ${appDist}`); +console.log(`Route: /sms/{id}`); +console.log(`Mode: ${mode}`); +console.log(`API base: ${apiBase}`); diff --git a/scripts/dev-server.js b/scripts/dev-server.js new file mode 100644 index 0000000..7875241 --- /dev/null +++ b/scripts/dev-server.js @@ -0,0 +1,103 @@ +const fs = require("fs"); +const http = require("http"); +const path = require("path"); + +const root = path.resolve(__dirname, ".."); +const port = Number(process.env.PORT || process.env.CONFIG_SMS_FRIEND_BRIDGE_PORT || 5174); + +function normalizeText(value) { + return typeof value === "string" ? value.trim() : value ? String(value).trim() : ""; +} + +function parseEnvValue(value) { + const text = normalizeText(value); + if ( + (text.startsWith('"') && text.endsWith('"')) || + (text.startsWith("'") && text.endsWith("'")) + ) { + return text.slice(1, -1); + } + return text; +} + +function loadEnvFile(fileName) { + const filePath = path.join(root, fileName); + if (!fs.existsSync(filePath)) return {}; + const env = {}; + const content = fs.readFileSync(filePath, "utf8"); + for (const line of content.split(/\r?\n/)) { + const trimmed = line.trim(); + if (!trimmed || trimmed.startsWith("#")) continue; + const index = trimmed.indexOf("="); + if (index <= 0) continue; + env[trimmed.slice(0, index).trim()] = parseEnvValue(trimmed.slice(index + 1)); + } + return env; +} + +const fileEnv = { + ...loadEnvFile(".env"), + ...loadEnvFile(".env.development"), +}; + +const mimeTypes = { + ".html": "text/html; charset=utf-8", + ".css": "text/css; charset=utf-8", + ".js": "application/javascript; charset=utf-8", + ".json": "application/json; charset=utf-8", + ".png": "image/png", + ".jpg": "image/jpeg", + ".jpeg": "image/jpeg", + ".svg": "image/svg+xml", +}; + +function sendText(res, text, contentType) { + res.writeHead(200, { + "Content-Type": contentType, + }); + res.end(text); +} + +function sendFile(res, filePath) { + const ext = path.extname(filePath); + res.writeHead(200, { + "Content-Type": mimeTypes[ext] || "application/octet-stream", + }); + fs.createReadStream(filePath).pipe(res); +} + +function resolveAssetPath(urlPath) { + const cleanPath = decodeURIComponent(urlPath.split("?")[0]); + const assetPath = cleanPath.replace(/^\/sms\//, "/"); + const candidate = path.resolve(root, assetPath.replace(/^\/+/, "")); + if (!candidate.startsWith(root)) return ""; + return candidate; +} + +const server = http.createServer((req, res) => { + const urlPath = (req.url || "/").split("?")[0]; + if (urlPath === "/config.js" || urlPath === "/sms/config.js") { + const apiBase = + process.env.api_url || + fileEnv.api_url || + ""; + sendText( + res, + `window.api_url=${JSON.stringify(apiBase)};\n`, + "application/javascript; charset=utf-8", + ); + return; + } + + const assetPath = resolveAssetPath(req.url || "/"); + if (assetPath && fs.existsSync(assetPath) && fs.statSync(assetPath).isFile()) { + sendFile(res, assetPath); + return; + } + + sendFile(res, path.join(root, "index.html")); +}); + +server.listen(port, () => { + console.log(`Friend bridge H5 running at http://localhost:${port}/sms/{id}`); +}); diff --git a/src/main.js b/src/main.js new file mode 100644 index 0000000..9511bbb --- /dev/null +++ b/src/main.js @@ -0,0 +1,130 @@ +(function () { + const elements = { + title: document.getElementById("mainTitle"), + corpName: document.getElementById("corpName"), + hintText: document.getElementById("hintText"), + openButton: document.getElementById("openButton"), + statusText: document.getElementById("statusText"), + primaryInitial: document.getElementById("primaryInitial"), + primaryAvatar: document.getElementById("primaryAvatar"), + primaryAvatarFallback: document.getElementById("primaryAvatarFallback"), + secondaryAvatar: document.getElementById("secondaryAvatar"), + secondaryAvatarFallback: document.getElementById("secondaryAvatarFallback"), + friendMeta: document.getElementById("friendMeta"), + }; + + let scheme = ""; + + function normalizeText(value) { + return typeof value === "string" ? value.trim() : value ? String(value).trim() : ""; + } + + function getRecordId() { + const params = new URLSearchParams(window.location.search); + const queryId = normalizeText(params.get("id")); + if (queryId) return queryId; + + const match = window.location.pathname.match(/\/sms\/([^/?#]+)/); + return match ? decodeURIComponent(match[1]) : ""; + } + + function getApiBase() { + const apiUrl = normalizeText(window.api_url); + return apiUrl ? apiUrl.replace(/\/+$/, "") : ""; + } + + function setStatus(text) { + elements.statusText.textContent = text; + } + + function setError(message) { + elements.title.textContent = "链接暂不可用"; + elements.hintText.textContent = message || "请联系工作人员重新获取好友链接"; + elements.friendMeta.textContent = ""; + elements.openButton.textContent = "无法打开"; + elements.openButton.disabled = true; + setStatus(""); + } + + function setAvatar(src, friendName, imageEl, fallbackEl) { + const avatar = normalizeText(src); + if (fallbackEl === elements.primaryAvatarFallback) { + elements.primaryInitial.textContent = friendName.slice(0, 1) || "医"; + } + fallbackEl.classList.remove("has-image"); + imageEl.removeAttribute("src"); + + if (!avatar) return; + imageEl.onload = function () { + fallbackEl.classList.add("has-image"); + }; + imageEl.onerror = function () { + fallbackEl.classList.remove("has-image"); + imageEl.removeAttribute("src"); + }; + imageEl.src = avatar; + } + + function fillPage(data) { + const friendName = normalizeText(data.friendName) || "医生"; + const corpName = normalizeText(data.corpName) || "患者精益管理服务平台"; + const friendJob = normalizeText(data.friendJob || data.job); + const friendIntro = normalizeText(data.friendIntro || data.memberTroduce); + const friendAvatar = normalizeText(data.friendAvatar || data.avatar); + const avatarList = Array.isArray(data.avatarList) ? data.avatarList.map(normalizeText).filter(Boolean) : []; + const secondaryAvatar = normalizeText(data.patientAvatar || data.customerAvatar || avatarList[1]); + const title = friendName.endsWith("医生") ? `${friendName}服务好友` : `${friendName}医生服务好友`; + + document.title = normalizeText(data.title) || "添加好友"; + elements.title.textContent = title; + elements.corpName.textContent = corpName; + elements.friendMeta.textContent = friendJob || friendIntro || ""; + elements.friendMeta.hidden = !elements.friendMeta.textContent; + elements.hintText.textContent = normalizeText(data.hintText) || "点击下方按钮,添加企业微信好友"; + elements.openButton.textContent = normalizeText(data.actionText) || "去 加 好 友"; + setAvatar(friendAvatar || avatarList[0], friendName, elements.primaryAvatar, elements.primaryAvatarFallback); + setAvatar(secondaryAvatar, "患", elements.secondaryAvatar, elements.secondaryAvatarFallback); + elements.openButton.disabled = false; + scheme = normalizeText(data.scheme); + setStatus(""); + } + + async function loadLink() { + const recordId = getRecordId(); + if (!recordId) { + setError("好友链接参数缺失"); + return; + } + + try { + const apiBase = getApiBase(); + if (!apiBase) { + setError("接口地址未配置"); + return; + } + const response = await fetch(`${apiBase}/sms/friend-link/${encodeURIComponent(recordId)}/data`, { + method: "GET", + }); + const contentType = response.headers.get("content-type") || ""; + if (!contentType.includes("application/json")) { + setError("好友链接不存在或已失效"); + return; + } + const result = await response.json(); + if (!response.ok || !result.success || !result.data?.scheme) { + setError(result.message || "好友链接不存在或已失效"); + return; + } + fillPage(result.data); + } catch (error) { + setError("好友链接加载失败,请稍后重试"); + } + } + + elements.openButton.addEventListener("click", function () { + if (!scheme) return; + window.location.href = scheme; + }); + + loadLink(); +})(); diff --git a/src/styles.css b/src/styles.css new file mode 100644 index 0000000..868b7c5 --- /dev/null +++ b/src/styles.css @@ -0,0 +1,281 @@ +:root { + color-scheme: light; + --blue: #1677ff; + --blue-deep: #1265d8; + --text: #171923; + --muted: #7a8292; + --surface: #ffffff; + --shadow: 0 18px 44px rgba(34, 88, 164, 0.14); +} + +* { + box-sizing: border-box; +} + +html, +body { + min-height: 100%; +} + +body { + margin: 0; + font-family: "PingFang SC", "Microsoft YaHei", sans-serif; + background: #fff; + color: var(--text); +} + +.login-bg { + position: relative; + display: flex; + min-height: 100vh; + flex-direction: column; + align-items: center; + overflow: hidden; + background: #fff; + padding-bottom: 38px; +} + +.bg-image { + position: absolute; + top: 0; + left: 0; + z-index: 0; + width: 100%; + height: 250px; + object-fit: cover; +} + +.doctor-card-wrap { + position: relative; + z-index: 1; + width: calc(100vw - 48px); + max-width: 300px; + margin: 160px auto 0; +} + +.doctor-card { + position: relative; + z-index: 1; + display: flex; + min-height: 210px; + flex-direction: column; + align-items: center; + padding: 50px 12px 30px; + background: linear-gradient(180deg, #dbe8ff 0%, #fff 50.25%); + border-radius: 12px; + box-shadow: 0 4px 16px rgba(59, 124, 255, 0.08); +} + +.doctor-info { + display: flex; + flex-direction: column; + align-items: center; + text-align: center; +} + +.doctor-avatar { + position: absolute; + top: 0; + left: 50%; + z-index: 2; + transform: translate(-50%, -50%); + border: 3px solid #fff; + border-radius: 8px; + background: #f2f2f2; + box-shadow: 0 1px 4px rgba(59, 124, 255, 0.08); +} + +.avatar-board { + position: relative; + width: 72px; + height: 72px; + overflow: hidden; + border-radius: 8px; + background: #f5f5f5; +} + +.avatar { + position: absolute; + display: grid; + place-items: center; + overflow: hidden; + color: #fff; + font-weight: 700; + letter-spacing: 0; + background: linear-gradient(160deg, #b9d8ff, #1d79ed); +} + +.avatar::before { + content: ""; + position: absolute; + top: 14%; + width: 34%; + aspect-ratio: 1; + border-radius: 50%; + background: rgba(255, 255, 255, 0.78); +} + +.avatar::after { + content: ""; + position: absolute; + bottom: -15%; + width: 68%; + aspect-ratio: 1.1; + border-radius: 48% 48% 0 0; + background: rgba(255, 255, 255, 0.82); +} + +.avatar span { + position: relative; + z-index: 1; + margin-top: 28px; + font-size: 16px; +} + +.avatar img { + position: absolute; + inset: 0; + z-index: 2; + display: none; + width: 100%; + height: 100%; + object-fit: cover; +} + +.avatar.has-image img { + display: block; +} + +.avatar.has-image span, +.avatar.has-image::before, +.avatar.has-image::after { + display: none; +} + +.avatar-large { + left: 0; + top: 0; + width: 36px; + height: 36px; + border-radius: 2px; +} + +.avatar-small { + right: 0; + bottom: 0; + width: 36px; + height: 36px; + border-radius: 2px; + background: linear-gradient(160deg, #d5e8ff, #48a0ff); +} + +.doctor-name { + margin: 20px 0 0; + color: #1d2129; + font-size: 20px; + line-height: 1.35; + font-weight: 600; + letter-spacing: 0; +} + +.doctor-hospital { + margin: 10px 0 0; + color: #78808f; + font-size: 14px; + line-height: 1.45; + font-weight: 400; + letter-spacing: 0; +} + +.doctor-dept { + display: inline-block; + margin: 9px 0 0; + padding: 0 5px; + border: 1px solid rgba(26, 62, 132, 0.2); + border-radius: 2px; + color: #213e80; + font-size: 11px; + line-height: 20px; + font-weight: 400; +} + +.doctor-dept:empty, +.doctor-dept[hidden] { + display: none; +} + +.login-tip { + display: block; + margin: 20px 0 0; + color: #000; + font-size: 16px; + line-height: 1.45; + font-weight: 500; + letter-spacing: 0; +} + +.login-btn-wrap { + position: relative; + z-index: 1; + display: flex; + width: 100vw; + justify-content: center; + margin-top: 68px; +} + +.login-btn { + width: calc(100vw - 56px); + max-width: 300px; + height: 48px; + line-height: 48px; + border: 0; + border-radius: 24px; + color: #fff; + background: linear-gradient(270deg, #1b5cc8 2.26%, #0877f1 94.33%); + box-shadow: 0 2px 8px rgba(59, 124, 255, 0.08); + font-size: 16px; + font-weight: 600; + letter-spacing: 0.18em; + text-indent: 0.18em; +} + +.login-btn:disabled { + background: #c7d3e4; + box-shadow: none; +} + +.status-text { + position: relative; + z-index: 1; + min-height: 20px; + margin: 16px 0 0; + color: #8b96a8; + text-align: center; + font-size: 13px; + line-height: 1.5; +} + +@media (max-width: 360px) { + .doctor-card-wrap { + width: calc(100vw - 36px); + margin-top: 150px; + } + + .doctor-card { + min-height: 198px; + padding-right: 12px; + padding-left: 12px; + } + + .doctor-name { + font-size: 19px; + } + + .login-tip { + font-size: 15px; + } + + .login-btn-wrap { + margin-top: 58px; + } +}