From 7ecb4ef976e5298a19b7c814a5c6f9df7336bb96 Mon Sep 17 00:00:00 2001 From: zhangjianjun Date: Thu, 20 Aug 2026 14:13:36 +0800 Subject: [PATCH] ud --- .gitignore | 1 + src/api/user.ts | 14 + src/app.config.ts | 8 +- src/app.scss | 3 + src/assets/images/empty-orders.png | Bin 0 -> 18227 bytes src/assets/images/icon-camera.png | Bin 0 -> 2770 bytes src/assets/images/icon-close.png | Bin 0 -> 1075 bytes src/assets/images/icon-input-clear.png | Bin 0 -> 1171 bytes src/assets/images/icon-logo.png | Bin 0 -> 2722 bytes src/assets/images/icon-pen.png | Bin 0 -> 965 bytes src/assets/images/icon-search.png | Bin 0 -> 1468 bytes src/lib/assets.ts | 17 + src/lib/cdn.ts | 11 +- src/lib/feedback.test.ts | 38 ++ src/lib/feedback.ts | 33 ++ src/lib/nav.ts | 20 + src/lib/order.test.ts | 49 +- src/lib/order.ts | 49 +- src/lib/orderDraft.ts | 29 ++ src/lib/status.test.ts | 73 +++ src/lib/status.ts | 56 ++- src/pages/complaint/index.config.ts | 6 + src/pages/complaint/index.scss | 284 ++++++++++++ src/pages/complaint/index.tsx | 216 +++++++++ src/pages/index/contact.tsx | 12 +- src/pages/index/detail.tsx | 8 +- src/pages/index/index.config.ts | 8 +- src/pages/index/index.scss | 617 +++++++++---------------- src/pages/index/index.tsx | 288 +++++------- src/pages/index/resubmit.tsx | 331 ------------- src/pages/resubmit/index.config.ts | 6 + src/pages/resubmit/index.scss | 218 +++++++++ src/pages/resubmit/index.tsx | 362 +++++++++++++++ 33 files changed, 1813 insertions(+), 944 deletions(-) create mode 100644 src/assets/images/empty-orders.png create mode 100644 src/assets/images/icon-camera.png create mode 100644 src/assets/images/icon-close.png create mode 100644 src/assets/images/icon-input-clear.png create mode 100644 src/assets/images/icon-logo.png create mode 100644 src/assets/images/icon-pen.png create mode 100644 src/assets/images/icon-search.png create mode 100644 src/lib/assets.ts create mode 100644 src/lib/feedback.test.ts create mode 100644 src/lib/feedback.ts create mode 100644 src/lib/nav.ts create mode 100644 src/lib/orderDraft.ts create mode 100644 src/lib/status.test.ts create mode 100644 src/pages/complaint/index.config.ts create mode 100644 src/pages/complaint/index.scss create mode 100644 src/pages/complaint/index.tsx delete mode 100644 src/pages/index/resubmit.tsx create mode 100644 src/pages/resubmit/index.config.ts create mode 100644 src/pages/resubmit/index.scss create mode 100644 src/pages/resubmit/index.tsx diff --git a/.gitignore b/.gitignore index c91abac..19526e2 100644 --- a/.gitignore +++ b/.gitignore @@ -7,3 +7,4 @@ node_modules/ .swc *.local .env +design/ \ No newline at end of file diff --git a/src/api/user.ts b/src/api/user.ts index d362ee9..6973fba 100644 --- a/src/api/user.ts +++ b/src/api/user.ts @@ -22,6 +22,20 @@ export function initCorp (params: Record = {}) { }) } +export function report (data: { + contact: string + content: string + images?: string[] + type: string + [property: string]: any +}) { + return request({ + url: '/api/user/feedback', + method: 'POST', + data, + }) +} + export function presign (ext = '') { return request({ url: '/api/presign', diff --git a/src/app.config.ts b/src/app.config.ts index 1184111..4caf298 100644 --- a/src/app.config.ts +++ b/src/app.config.ts @@ -1,13 +1,15 @@ export default defineAppConfig({ pages: [ 'pages/index/index', + 'pages/resubmit/index', + 'pages/complaint/index', 'pages/map/index', ], window: { backgroundTextStyle: 'light', - navigationBarBackgroundColor: '#2F4FEE', - navigationBarTitleText: '网上快办平台', - navigationBarTextStyle: 'white' + navigationBarBackgroundColor: '#FFFFFF', + navigationBarTitleText: '网上快办', + navigationBarTextStyle: 'black' }, permission: { 'scope.userLocation': { diff --git a/src/app.scss b/src/app.scss index e69de29..3a01f8c 100644 --- a/src/app.scss +++ b/src/app.scss @@ -0,0 +1,3 @@ +page { + background: #f6f8fc; +} diff --git a/src/assets/images/empty-orders.png b/src/assets/images/empty-orders.png new file mode 100644 index 0000000000000000000000000000000000000000..138dffccf4f8b949086c9782671c55d09dd284e4 GIT binary patch literal 18227 zcmeFXd{ zvQpYUIj5Vx?UZ@}%#V-?aBmf{eOt?FBZs9?BX{fh;1^3SQU|U~YqW3$X%rr{Ej%<{ zWdaZ*{kVuT^-n7crXaX1u5Mn1yk#t=&)gR12vN&j7r=uL->m=RG>uv|9}4(ilNQgjq3=oIe!4Y;Gv+7JT7rKmy0rF_dyLFAJb$*jc14**)%NpA z%}6dfPIfeLa04EvwCpMrJo697Xg7bK5mi-WGnG=i>rNc|alSCb?>+V@}&MY`C&Y%&&T;bsWAZq-#UT;P1fY7N<^uzBKU>aPrKlN0kw$%8M};< zK94~9IzErZ-=s(L4B{gdy>E~i zZhwN?k`R9v#5)@`f-J+IK;C{xj+DOmLSp=9`8bIK(Hv8~0V#zyl+ea8=X-tFZAGED zClEBSS6Sz;nMQ~l70sh(g7HB%_q0O8w<&&CsmNtyPl{-@yp2)leCt6u*h4ctw4&Z> z_TI+atzu^f9dm#FnVsB(nI+w{xzG(e_(ek1IJ(@^CpM&fNKA-L1Zn0AvayC^e-8t4 z9<=3O$1(1m6NT;{J+)KM!$1e)T|7+zb?tT+75KyV{Kp4pX@vYUNv$i#hNN=zu`%cf z8mNBY;72B;AQpuA^)P4fBD7kx{N-qV{nqJ^USzaJWF+H|^Loc+ru__egWH9}x1A~Z%eS)V+{y*@igH?47xR(J}|;fb~O;WwW<4`>zS41#Q}TFx>*k6 zc0jq9M3FPBvn-cJI(Rm{Au|=|x5syH84us_x&kNUDo#wHTrd7gzpbrylKS|249vUd zo-(Oir$^VI#g!Qz$y(`}s2|dBZ^Xnrtj2ImFTMf^36YAD!zE)pF=e-qcN{=?Y_MVkY!%+=r{MQMBjO2ygMk~+ z-<$F!!HY`D@lW+SUceJ_uxZI|7o=gFO;6_FcE*U7!gn`Gf9!gcW4%*7TX@ zGUutspP`*?nyg#U2;3?egA)b~LGw__-xs*JlPJ~(`6ONcxFx3wRA+|FaG--Ph8)nZ%mD*l$KkI~Z z?^fkLwL*pbtowINJI+9Oo>JPgoI=s|Ncy@#wTIyWvtWd6o_s2FCAd&IS`RgAq_?Px zU*E`Blsn&m68+jZNWU_$ts#*Bk3lCk15w!JCVek2X}woQw~9fZp~?#(u;>yJyD|G& z`S#$STEXva^c;!D76^$8psd8k`S>N@9^LXvc5}V^LJ2=XX=JdoKMj?R9nE8MV|yI0 z*)d@I34F0|P*YYomGbe8va_@4(=}vU*Pk{8w+O(DwqOJ{L&bOZ7z?PzUSG&JdJ57o ze_1MOD#;Hz%T77wU3D(P8Ab{LZb^u0uQ^R?2No7=aY1jl-QP;8LB!sLa2Sg6U6w|h zO|3N}KWV{p>yaR(`=-PPJ8q`kCkorfTS-bde(o7&>+pqqk}9-i%dD*;df@l(?V0Tub@^n- zrIQ!b++g#io1{M}^k%ZvML#~mz$EyMb?{ z;Qz|puv-s}A*(RGaQ(C<*U@^$?eJ#UBe-&n0i_I0uEEms1}F6o@#ZE+Z|d(i z#H&p{>&k!oV^h?Q<8U_jC8p`bv-M;{e7)OdY}tzT)D_#zv{IGQW zCtLTuK&Aa}M8E#)mxzqT_Q?l&)gAQT8GS&r1F?_;rstm~`AaMz-ZWw{3iHo?atH{= z3iznEt*t}>Pf?`gy6itX%~Q)9E(&5)s-X2iI}4}G7JYXsKsWOTZ(dRHqSjBL zl41u3t_Cd*41vGQ_{5D>u%v-$TU&PeS!QB!4;0Ko0{p70%TDUJ zaXU&XRhS6hA|k2%KRR!By|h4ki{DC(ku%`b?mr)|*J@@}>*C?R4Q&fg(7j6=i3!`! zu^^<-C9+t0(OepR^Q9Q|`um|`Jjnk~?fhgDK(5Hk5_S+vh{me8P=pzrkIcs9N#`x`%SXIW`EdMF3F zq@BF_C88@R#2&u-ielV08u8Z%yt^5o#Z?DCETHyl7+CfLyA6DPFg|Hiutrin zkB!sB8WHjpiM?6;ZcuJKI6<>dplu4EWN=(!*!qg5(KEU4cMWf31Xnj3-F(2Yjm;AUr zKR@zvUEy8H8#s17+tfJ7Q@_f?r5!YTN%b+WtCx+OB5FzN!l9VU;B0K`)}%6#!BXVx z+lNa1=h-#uKZzwWqYnUuY%|Nc+(+LtwG1T7vB?=xv~8{Ti!`YZAhSo8m)tBWQ4I<% zsRHA5KiNQ3jF%D}^k7wn-;e9wKSH%qin3@I1leLVC6MDG1}uF@*4^~HH{3Fe_0xHB z_9|_6A8h_Osjz6JHfWmH2_TW6Exc2#pon_lst^3ou;iP>4-R3x6hxRvOfmwvE>$w2 zU-K;EB3!TJKWV<)aD0+pcn>o~Tb?M#P~4jv^m;Z2ALFtHH4#RLaSJ43NVnzoZ#Lzf zN^RUn+U7R9@*po;(RK?Zqo8=lM=an!0D>=0MR#YzQGVm;@#JC6B zXNEoKMhVg0xJ!D$6QJl)h0e=<*r2ujOR_CRTSI2CX`5x96E5EM0?~ z33�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 literal 0 HcmV?d00001 diff --git a/src/assets/images/icon-input-clear.png b/src/assets/images/icon-input-clear.png new file mode 100644 index 0000000000000000000000000000000000000000..4c0aff048a96e66f3adc178dc96b89a3e2df5b1e GIT binary patch literal 1171 zcmV;E1Z?|>P)7qtrFMpP^)C{|P|RkWxJ2PmQo zQxIGfN;h2vTF4`F3rPn^Qb$DCo1~fgJGZ$X_uhBk9nz*PeVp^Y>74)hf9E^j z_g+LjW}hdA(7?bz8Tv+lfB&b0gM&wDj}8qD9j5;p{o}voUa&Zfk6Pft(G378ZVGRG*ur=5noFfNV7VqZ9MZG__ScqwnlyH2Cs z8k41udwYBLSn+LyqS2`D)h;S3QZ+R-DmORRieu~N<>eVRg@uK-d6z+qn6?Di2ys*g zT%2rE1qB5vBO^nVmzTSe5;A3FWrmH|+ML}7Z(64{A#PSF3+1S%OifKG>P&I8Nu*3N zvL-fS>j`#mv614=zt>et6Ixqat2ZaT+Qr31H8C+^BbAerV`NJ*W0Db@rKKe=D03}6 zJ3G799B4xPPVF-%{aPV4IXS6GRa8_M*|L%m8$TR#9X~l8W2gyHKRRv3iqF@=!h*7r z0@NTVnF&Ki!f?Dgww2J9EItZlGdi{=#G8-^L&jOa?9#Q+gJ42P$JT^6vTH${#YWyB zYXOgskGmGS*alHHqho7Aye&gPoP~_MLF`J`jNB%v8*5ETq08+HfVrNPm36_i(}e2k z>ZX~zgnCLzNr`bZ?8?ZB5HTH`g@%qVQW6pe98dDzyh2UsmsI9=sFyc3Hrl{UNk(uz35h{-b93+9+*~#N z@6bShaIn)tMiOl3%h}o4=UZA@T#rc`)A1#Qjq2>|yw%v)_|@|A@;<;1qj7XY)>7yi z>*rYeKzy2-nvQjKbp<;5K!gA`q@$xF0r;3` literal 0 HcmV?d00001 diff --git a/src/assets/images/icon-logo.png b/src/assets/images/icon-logo.png new file mode 100644 index 0000000000000000000000000000000000000000..6d83997eb89f6f5ad5d262970c0caf766f7f1553 GIT binary patch literal 2722 zcmV;T3SISyP)^o8$)}tnOE+$)0Le6~-88|>y zHbiQp`DALNKyIbsG{DN7Jcf0US%PLq!C37epJt`4MOF5UJ~KQ(r3X5|5rxdB*wIWx zYhX6tfgJ203R&gJ%uOty=>$mxf0CN)Ou(3DP%_(pK$o`;Whz|b*_>Hg7B(0XER?j) zEp2@FQT+q-JuBm&vIashXBbry@TNn1+LVb46|c-rZvkb&4ptCt^{EyP)Hn) zGOa;BO6hYqp^61-8EUJ{^PyYf?!H9eYnVEB_`dRD`=tURB^cq`&Eq4c z{CI6>YkM+Z(1UMciy!Z+d&pyfVF*%X$HZ(YR2rbk@q-eFc|hwVZ#t?AcA#v~ryafC zuS$Cx@Vf>DibjVNkQOB>4bUjxY(P{vf>j_ys<3XaRKUZ1g>A2-i!*|~`R=mmy_Mco zeQ4_F6Ko&ABV(W9*>+6ZR2z@79?Z?ELP+&K{`bP7?A0 zw?}`Lbht#)Q6npKvzRRetlVu1RDxzr1r$Y#N(a=$tQnTTmkGTULyEvzuAc57rufPY zbH3N=OZ}UD`S;yyG;vo+!}l39Fo4{t z=5}9ikPn?LRJ*oZSKDdR-hCL$@O-DB-_3vl!ipjPGU|<6k9x? zIul7ocY0RCOzi~?qwnXF9|rzOZ532qgKYFM8WYd?CqgDK3&(<1%Y`#h30f50l|Ozj zua{~R{TycHAZ|o;c5I%H{j$Xa+8&(`@^S+{wJ?~0_+i>bns~<}jQ|lT^lq#u|2yDY zYj<#yZ@qt@z4%m^{TeG$8K{TPgYb{iLHnn0zm(kp05zO+Xdzr3o=h3_vTHfc-Zu06Uw8Jf2GWPtAx zHpc&knU9XI<32~l%Ax>C5dD%s{4L{o*(~PtRn~^<{O^+$PPYnC8wWJIJGa1qks#YpGA>Gtzn_BRoI%_rz?g5UNa%}yIUMvBk})8fwBrw8v%I$zT>mz%q(u=%Z zEE>-9AS2!3cId8K+J*6@iM#L!cL1#P07rSNrs36AAB?k*BQ%>kRU4 zu`KZ@GQnKSB+jv{oTMhFso@R-njK`KdJeVGuMi&w zSK1M->*+r}$*$Nv9Mi)pNk#V)x>5vl2pr^&@~TVXp26qT4k7Gh?5av^GcV%$l7-3O3S82uQKbr9a;TVbX~*^4yFjWm&wwpGUZDR+Tpg6ps} zc*wWT4b~D&(6+XbYIYgOj7QrF_&c0o5TU1wVU9qa5%MWd|bgW#mF#8q7w0a37YHuW@=8@rkpRVcysd*1v)9=-~I!kZ96OaHVaN6Ezo&S73%u z&u`u66A!8&LL=XGFpu~n;0LO5vehTYXePJ;jmf{@F}j^w7pn0=(;kBxuspHM$6Rmq za;Y{Xe+s`ZkXIy+9~d?U9lx*nWLSomXp0=D)bUFBhQec}l;@I8D1W#9pI zOOl2rwjh&w$ya!b^ZB0>-7E*H^92JMs5FcB^Ch6-=jy*>`5yoP0RR8tu-4oF000I_ cL_t&o04Z5-rA+L;>Hq)$07*qoM6N<$f~ns!^#A|> literal 0 HcmV?d00001 diff --git a/src/assets/images/icon-pen.png b/src/assets/images/icon-pen.png new file mode 100644 index 0000000000000000000000000000000000000000..eda6135afec2d93cda50212067c2b58ba13e0bea GIT binary patch literal 965 zcmV;$13LVPP)Ee)+uLGhuuKPZBTAfkvq6h!dB2Nx_NC}NAX z#k%5#h>9yk1bq-Jic3|rZn(tnPjZ9PJ546ZG!5tD+_`h^`OnPUCEWsD{PVSda5%gX z9z(9z=kl!pM7JWk2PQ%T*9G{b=1T#H?LcD?R z9O7ngP!X%JG;&Yh-xRTnzHiVALSR`+1(00v2g$MqenBNq zy92)fK7y8C6LAAz9M@(-KBT=3EK@?O)2)Mq-RZ9ZO|+l*3dC#idfL1Ad}xI^a1kUv z27eFoh<%T@%u@k~_h95PSO(uAM8Y%pxN&`-i#(9f4E#vA1Nq$hfOoZ|1t8v&ktd)M zzCtMpoA7R?Iq@fU;}*VvSj`Wb?v}6s#5H+NF|-`opoD~%@RkufiOwr99Bx1%v7hk? zt-}IHs3ld;=u%LkA`+SWpc9$AmKoU=^JZFX;y0zX)@96G-7Aca{&g!IatJ#Yu(fIW{v_# zn8Pq7(o%XyTgykwh^EtsolVbq7z7PahPYM;P45;jfP{Jqy~J}-0VFzFe zyo9+Ve2TXWaUHGNfHX<9H!LAmYu}3X+F=S96&tXcgwJDbheN!Kjylj0t6gy^u^EYz zD_{x;>H=J6Fya;5I|GIq@%l62$cDJK;Nx^^OV&wPg}DCx(3QCv zVeRYJ}G-xHE`|AfP{yqY)4z@H)|k;&ZsS8Q*CZDL~l~YKie0{}x)o(}||>=@Ku1 zZ$2I-XAzwt(K&#qj@@TG#e*zz9j#~qG+N*ZyvQQA(TWzpw{C_BFxGDpxlxrNY@$-oA`WJo&?aq7H|FL*W(~tlTUp17 z>1*1$f_|v=1$D?2=0K*dsOa3=m#OT<1V6MYwHntZ{+`RZQns9%q)A|dl=JWN-1G8( z{^z;p-g{1RZ8z9oR&b_oST@7i3}2Ojv@D69o}OE~ySpFn>+5>~yb3ycdwbic-;96v zRWVb#oVVci_Vz`H@`4jNIXRc}^77t8$v&_XY^Sa*H#heX{wYNNLcb0sbqCiem81oa zj*jLLd}~=**?B}A0QZ3$rQ%;~C>o7k27lq3Whs5OGpRF(mvXb&kgQEgFt>Qg#Kgqs z1m8~}JFp?_TkJOMs$ei!>UO)AdOV(G;HLci{M+gK4DBC)D#pO~(_B=5#tQBHYFJ8piwq3cx^rfB)0igiBn( zot>S<2pt2rz#1Vlbp&oBn-Q*)E==-cM6YMyZy=YuB%<%ZPpHHdTvAf92PTtnH@EmL zu2U+qd?1iahe<>+6Bw3!YK^z_3lhXrczo64r$@b-C!g#4|DA5&Iqw1N@4PK}xlg+gB_ zKkcTarDcj(UkWk89}sH=tzZTjn-)mUvbN-tOI2nHF-{6I!dHs5f|X$7d(V3}Xho2Y zHiTYOJPW(Jx=NKF(`W@lBHw)*FZOwTAYw+-R1#~-Y~mBX$n;}btzd4Gx!bawaV*kg z>qyG~B8!TO%(yXWw1Vk<3D}tZa^^Eg%`Mc9k;{{&StxhEPp6W*cyk!A)<|siqy>qh;@;GgKF%btX;SA`A=VF1FEAc7Y$>PfI&4BE5y3VdhABjEB*R||E7@7ghF*^9 zd)0#@)mm^mF5sbcav(U(ST~SYr7X9Q8uSW%UR1uNNl36T$a*d;EZl$}08)zC80}&r z7s%SXU|8 z$ { + it('uses the five Chinese labels from the design as type values', () => { + expect(COMPLAINT_TYPES.map((item) => item.value)).toEqual([ + '莫名扣费', + '超过7天未解决', + '交易被骗', + '不满意商家服务', + '其他问题', + ]) + }) +}) + +describe('buildFeedbackPayload', () => { + it('sends contact, content, images, Chinese type and order identifiers', () => { + expect(buildFeedbackPayload({ + contact: '13800138000', + content: '好好好', + images: ['https://cdn.example/a.jpg'], + type: '其他问题', + order: { + order_id: '9', + out_trade_no: '123456789562323256', + goods_name: '地图标注代办服务', + }, + })).toEqual({ + contact: '13800138000', + content: '好好好', + images: ['https://cdn.example/a.jpg'], + type: '其他问题', + order_id: '9', + out_trade_no: '123456789562323256', + goods_name: '地图标注代办服务', + }) + }) +}) diff --git a/src/lib/feedback.ts b/src/lib/feedback.ts new file mode 100644 index 0000000..edd3888 --- /dev/null +++ b/src/lib/feedback.ts @@ -0,0 +1,33 @@ +export const COMPLAINT_TYPES = [ + { value: '莫名扣费', label: '莫名扣费' }, + { value: '超过7天未解决', label: '超过7天未解决' }, + { value: '交易被骗', label: '交易被骗' }, + { value: '不满意商家服务', label: '不满意商家服务' }, + { value: '其他问题', label: '其他问题' }, +] as const + +export type ComplaintType = (typeof COMPLAINT_TYPES)[number]['value'] + +export type FeedbackOrder = { + order_id?: string + out_trade_no?: string + goods_name?: string +} + +export function buildFeedbackPayload (input: { + contact: string + content: string + images?: string[] + type: string + order?: FeedbackOrder | null +}) { + return { + contact: input.contact, + content: input.content, + images: input.images || [], + type: input.type, + order_id: String(input.order?.order_id || ''), + out_trade_no: String(input.order?.out_trade_no || ''), + goods_name: String(input.order?.goods_name || ''), + } +} diff --git a/src/lib/nav.ts b/src/lib/nav.ts new file mode 100644 index 0000000..56d74f1 --- /dev/null +++ b/src/lib/nav.ts @@ -0,0 +1,20 @@ +import Taro from '@tarojs/taro' + +export function getNavMetrics () { + const sys = Taro.getSystemInfoSync() + const statusBarHeight = sys.statusBarHeight || 20 + if (Taro.getEnv() === Taro.ENV_TYPE.WEAPP) { + const menu = Taro.getMenuButtonBoundingClientRect() + const gap = Math.max((menu.top || statusBarHeight) - statusBarHeight, 4) + return { + statusBarHeight, + navBarHeight: (menu.height || 32) + gap * 2, + paddingRight: Math.max((sys.windowWidth || 375) - (menu.left || 280) + 12, 24), + } + } + return { + statusBarHeight, + navBarHeight: 44, + paddingRight: 24, + } +} diff --git a/src/lib/order.test.ts b/src/lib/order.test.ts index 3a3436c..5f834e4 100644 --- a/src/lib/order.test.ts +++ b/src/lib/order.test.ts @@ -4,11 +4,14 @@ import { filterMapLabelOrders, filterOrdersForProduct, formatMapCoordinate, + formatMapCoordinateDisplay, getOrderAddressName, getOrderExtraImages, getOrderMapAddress, + joinStorefrontSlots, joinUploadedImageIds, parseCoordinate, + parseStorefrontSlots, } from './order' const mapOrder = { @@ -88,8 +91,42 @@ describe('order images', () => { }) }) +describe('storefront slots', () => { + const front = { id: '123', url: 'https://cdn.example/front.jpg', preview: 'https://cdn.example/front.jpg' } + const left = { id: '45', url: 'https://cdn.example/left.jpg', preview: 'https://cdn.example/left.jpg' } + + it('joins three slots and writes null for empty ones', () => { + expect(joinStorefrontSlots([front, left, null])).toBe('123,45,null') + expect(joinStorefrontSlots([front, null, null])).toBe('123,null,null') + expect(joinStorefrontSlots([null, null, null])).toBe('null,null,null') + }) + + it('parses comma ids into front/left/right, treating null as empty', () => { + expect(parseStorefrontSlots({ + extra: { + entity_storefront_image: '123,45,null', + entity_storefront_image_url: 'https://cdn.example/front.jpg,https://cdn.example/left.jpg,null', + }, + })).toEqual([front, left, null]) + }) + + it('pads legacy two-image storefront data into three slots', () => { + expect(parseStorefrontSlots(mapOrder as any)).toEqual([ + { id: '11', url: 'https://cdn.example/a.jpg', preview: 'https://cdn.example/a.jpg' }, + { id: '12', url: 'https://cdn.example/b.jpg', preview: 'https://cdn.example/b.jpg' }, + null, + ]) + }) +}) + +describe('map coordinate display', () => { + it('renders northing and easting for the resubmit map field', () => { + expect(formatMapCoordinateDisplay({ lng: 120.156892, lat: 30.278956 })).toBe('N30.278956°, E120.156892°') + }) +}) + describe('buildResubmitPayload', () => { - it('updates extra while dropping upload-only keys', () => { + it('updates extra while dropping upload-only keys and keeping storefront null placeholders', () => { expect( buildResubmitPayload({ order: mapOrder as any, @@ -100,8 +137,12 @@ describe('buildResubmitPayload', () => { entity_phone: '13900139000', entity_phone2: '13700137000', }, - storefrontImages: [{ id: '21', url: 'https://cdn.example/c.jpg', preview: 'p' }], - licenseImages: [{ id: '31', url: 'https://cdn.example/d.jpg', preview: 'p' }], + storefrontSlots: [ + { id: '123', url: 'https://cdn.example/c.jpg', preview: 'p' }, + { id: '45', url: 'https://cdn.example/d.jpg', preview: 'p' }, + null, + ], + licenseImages: [{ id: '31', url: 'https://cdn.example/e.jpg', preview: 'p' }], }) ).toEqual({ id: '4', @@ -113,7 +154,7 @@ describe('buildResubmitPayload', () => { entity_address_name: '淮海路2号', entity_address: '121.480000,31.240000', entity_phone2: '13700137000', - entity_storefront_image: '21', + entity_storefront_image: '123,45,null', entity_business_license_image: '31', }, }) diff --git a/src/lib/order.ts b/src/lib/order.ts index 27b307c..dafdd54 100644 --- a/src/lib/order.ts +++ b/src/lib/order.ts @@ -44,6 +44,47 @@ function splitValues (value: unknown) { .filter(Boolean) } +function isEmptySlotToken (value?: string) { + return !value || value === 'null' +} + +function splitSlotValues (value: unknown) { + if (value === undefined || value === null || value === '') return [] + return String(value).split(',').map((item) => item.trim()) +} + +export type StorefrontSlot = UploadedImage | null + +export function joinStorefrontSlots (slots: Array = []) { + return [0, 1, 2].map((index) => { + const id = slots[index]?.id?.trim() + return id ? id : 'null' + }).join(',') +} + +export function parseStorefrontSlots (order: OrderLike | null | undefined): StorefrontSlot[] { + const extra = order?.extra || {} + const ids = splitSlotValues(extra.entity_storefront_image) + const urls = splitSlotValues(extra.entity_storefront_image_url) + + return [0, 1, 2].map((index) => { + const id = ids[index] || '' + const url = urls[index] || '' + if (isEmptySlotToken(id) && isEmptySlotToken(url)) return null + if (isEmptySlotToken(id) && !isEmptySlotToken(url)) { + return { id: '', url, preview: url } + } + const resolvedUrl = !isEmptySlotToken(url) + ? url + : (/^https?:\/\//i.test(id) ? id : '') + return { + id: /^https?:\/\//i.test(id) ? '' : id, + url: resolvedUrl, + preview: resolvedUrl, + } + }) +} + export function isCoordinateAddress (address?: string) { if (!address) return false const parts = address.split(',').map((part) => part.trim()) @@ -61,6 +102,10 @@ export function formatMapCoordinate (point: { lng: number, lat: number }) { return `${point.lng.toFixed(6)},${point.lat.toFixed(6)}` } +export function formatMapCoordinateDisplay (point: { lng: number, lat: number }) { + return `N${point.lat.toFixed(6)}°, E${point.lng.toFixed(6)}°` +} + export function getOrderAddressName (order: OrderLike) { const extra = order.extra || {} return extra.entity_address_name || (!isCoordinateAddress(extra.entity_address) ? extra.entity_address : '') || '' @@ -143,7 +188,7 @@ export function joinUploadedImageIds (images: UploadedImage[]) { export function buildResubmitPayload (input: { order: OrderLike values: ResubmitValues - storefrontImages: UploadedImage[] + storefrontSlots: StorefrontSlot[] licenseImages: UploadedImage[] }) { const existingExtra = Object.entries(input.order.extra || {}).reduce>((result, [key, value]) => { @@ -164,7 +209,7 @@ export function buildResubmitPayload (input: { entity_address_name: input.values.entity_address_name || '', entity_address: input.values.entity_address || '', entity_phone2: input.values.entity_phone2 || '', - entity_storefront_image: joinUploadedImageIds(input.storefrontImages), + entity_storefront_image: joinStorefrontSlots(input.storefrontSlots), entity_business_license_image: joinUploadedImageIds(input.licenseImages), }, } diff --git a/src/lib/orderDraft.ts b/src/lib/orderDraft.ts new file mode 100644 index 0000000..54c3332 --- /dev/null +++ b/src/lib/orderDraft.ts @@ -0,0 +1,29 @@ +import Taro from '@tarojs/taro' +import type { OrderLike } from './order' + +const ORDER_DRAFT_KEY = 'orderDraft' + +let memoryDraft: OrderLike | null = null + +export function setOrderDraft (order: OrderLike) { + memoryDraft = order + try { + Taro.setStorageSync(ORDER_DRAFT_KEY, order) + } catch { + // ignore quota / private-mode failures; in-memory draft still works + } +} + +export function getOrderDraft (): OrderLike | null { + if (memoryDraft) return memoryDraft + try { + const stored = Taro.getStorageSync(ORDER_DRAFT_KEY) + if (stored && typeof stored === 'object') { + memoryDraft = stored + return stored + } + } catch { + // ignore + } + return null +} diff --git a/src/lib/status.test.ts b/src/lib/status.test.ts new file mode 100644 index 0000000..9d65688 --- /dev/null +++ b/src/lib/status.test.ts @@ -0,0 +1,73 @@ +import { describe, expect, it } from 'vitest' +import { + filterOrdersByTab, + getOrderCardActions, + getStatusMeta, + ORDER_TABS, +} from './status' + +describe('order status labels', () => { + it('uses the redesigned tab labels for known process statuses', () => { + expect(getStatusMeta('4').result).toBe('办理中') + expect(getStatusMeta('6').result).toBe('待处理') + expect(getStatusMeta('1').result).toBe('待办理') + expect(getStatusMeta('3').result).toBe('待办理') + expect(getStatusMeta('2').result).toBe('已完成') + expect(ORDER_TABS.map((tab) => tab.label)).toEqual(['全部', '办理中', '待处理', '待办理', '已完成']) + }) +}) + +describe('filterOrdersByTab', () => { + const orders = [ + { order_id: 'doing', process_status: '4' }, + { order_id: 'rejected', process_status: '6' }, + { order_id: 'waiting', process_status: '1' }, + { order_id: 'waiting-3', process_status: '3' }, + { order_id: 'done', process_status: '2' }, + ] + + it('filters list orders into the five redesigned tabs', () => { + expect(filterOrdersByTab(orders, 'all').map((item) => item.order_id)).toEqual([ + 'doing', 'rejected', 'waiting', 'waiting-3', 'done', + ]) + expect(filterOrdersByTab(orders, 'doing').map((item) => item.order_id)).toEqual(['doing']) + expect(filterOrdersByTab(orders, 'rejected').map((item) => item.order_id)).toEqual(['rejected']) + expect(filterOrdersByTab(orders, 'waiting').map((item) => item.order_id)).toEqual(['waiting', 'waiting-3']) + expect(filterOrdersByTab(orders, 'done').map((item) => item.order_id)).toEqual(['done']) + }) +}) + +describe('getOrderCardActions', () => { + it('shows complaint plus submit on 办理中 and 待处理', () => { + expect(getOrderCardActions({ process_status: '4', goods_type: 'map_label' })).toEqual({ + complaint: true, + submit: true, + detail: false, + }) + expect(getOrderCardActions({ process_status: '6', goods_type: 'map_label' })).toEqual({ + complaint: true, + submit: true, + detail: false, + }) + }) + + it('shows only complaint on 待办理', () => { + expect(getOrderCardActions({ process_status: '1', goods_type: 'map_label' })).toEqual({ + complaint: true, + submit: false, + detail: false, + }) + }) + + it('shows view-detail on completed orders and hides complaint', () => { + expect(getOrderCardActions({ process_status: '2', goods_type: 'map_label' })).toEqual({ + complaint: false, + submit: false, + detail: true, + }) + }) + + it('hides submit for corp goods even when status would otherwise allow it', () => { + expect(getOrderCardActions({ process_status: '4', goods_type: 'license_year' }).submit).toBe(false) + }) +}) diff --git a/src/lib/status.ts b/src/lib/status.ts index 46d42bb..0120d47 100644 --- a/src/lib/status.ts +++ b/src/lib/status.ts @@ -1,12 +1,14 @@ +import { isCorpGoodsType } from './detail' + export const STATUS_CONFIG: Record = { - '4': { color: '#FF8E13', bg: '#FFF2E4', result: '处理中' }, - '1': { color: '#FF431D', bg: '#fff1e6', result: '待办理' }, - '3': { color: '#FF431D', bg: '#fff1e6', result: '待办理' }, - '2': { color: '#37D2AD', bg: '#DFF8F2', result: '已完成' }, - '5': { color: '#37D2AD', bg: '#DFF8F2', result: '已完成' }, - '7': { color: '#37D2AD', bg: '#DFF8F2', result: '已完成' }, - '8': { color: '#37D2AD', bg: '#DFF8F2', result: '已完成' }, - '6': { color: '#FF8E13', bg: '#FFF2E4', result: '待提交' }, + '4': { color: '#2F7BFF', bg: '#EAF2FF', result: '办理中' }, + '1': { color: '#FF5A5A', bg: '#FFF1F0', result: '待办理' }, + '3': { color: '#FF5A5A', bg: '#FFF1F0', result: '待办理' }, + '2': { color: '#22C58B', bg: '#E8F9F2', result: '已完成' }, + '5': { color: '#22C58B', bg: '#E8F9F2', result: '已完成' }, + '7': { color: '#22C58B', bg: '#E8F9F2', result: '已完成' }, + '8': { color: '#22C58B', bg: '#E8F9F2', result: '已完成' }, + '6': { color: '#FF8E13', bg: '#FFF2E4', result: '待处理' }, } export const PAY_ICON: Record = { @@ -14,10 +16,42 @@ export const PAY_ICON: Record = { weixin: 'https://cdn.u8t.cn/frontend-static/corp-h5/static/images/weixin.png', } +export const ORDER_TABS = [ + { key: 'all', label: '全部', statuses: null }, + { key: 'doing', label: '办理中', statuses: ['4'] }, + { key: 'rejected', label: '待处理', statuses: ['6'] }, + { key: 'waiting', label: '待办理', statuses: ['1', '3'] }, + { key: 'done', label: '已完成', statuses: ['2', '5', '7', '8'] }, +] as const + +export type OrderTabKey = (typeof ORDER_TABS)[number]['key'] + +const DONE_STATUSES = new Set(['2', '5', '7', '8']) +const SUBMIT_STATUSES = new Set(['4', '6']) + export function getStatusMeta (status?: string, name?: string) { - const config = STATUS_CONFIG[String(status || '')] || { color: '#666666', bg: '#F5F5F5', result: '未知' } + const config = STATUS_CONFIG[String(status || '')] + if (config) return { ...config } return { - ...config, - result: name || config.result, + color: '#666666', + bg: '#F5F5F5', + result: name || '未知', + } +} + +export function filterOrdersByTab> (orders: T[] = [], tab: OrderTabKey) { + const current = ORDER_TABS.find((item) => item.key === tab) + if (!current || !current.statuses) return orders + const statuses = new Set(current.statuses) + return orders.filter((order) => statuses.has(String(order.process_status || ''))) +} + +export function getOrderCardActions (order: { process_status?: string, goods_type?: string }) { + const status = String(order.process_status || '') + const detail = DONE_STATUSES.has(status) + return { + complaint: !detail, + submit: SUBMIT_STATUSES.has(status) && !isCorpGoodsType(order.goods_type), + detail, } } diff --git a/src/pages/complaint/index.config.ts b/src/pages/complaint/index.config.ts new file mode 100644 index 0000000..6d26eb2 --- /dev/null +++ b/src/pages/complaint/index.config.ts @@ -0,0 +1,6 @@ +export default definePageConfig({ + navigationBarTitleText: '投诉', + navigationBarBackgroundColor: '#00C261', + navigationBarTextStyle: 'white', + backgroundColor: '#F6F8FC', +}) diff --git a/src/pages/complaint/index.scss b/src/pages/complaint/index.scss new file mode 100644 index 0000000..870ad99 --- /dev/null +++ b/src/pages/complaint/index.scss @@ -0,0 +1,284 @@ +.complaint { + min-height: 100vh; + box-sizing: border-box; + padding-bottom: 180px; + background: #f6f8fc; +} + +.complaint-hero { + padding: 8px 32px 56px; + background: #00c261; +} + +.complaint-hero__row { + display: flex; + align-items: flex-start; + justify-content: space-between; +} + +.complaint-hero__name { + flex: 1; + min-width: 0; + padding-right: 16px; + color: #fff; + font-size: 32px; + font-weight: 700; + line-height: 44px; +} + +.complaint-hero__paid { + flex-shrink: 0; + color: #fff; + font-size: 26px; + line-height: 44px; +} + +.complaint-hero__meta { + margin-top: 20px; + padding: 20px 24px; + border-radius: 16px; + background: rgba(255, 255, 255, 0.16); +} + +.complaint-hero__meta-row { + display: flex; + align-items: flex-start; +} + +.complaint-hero__meta-row + .complaint-hero__meta-row { + margin-top: 12px; +} + +.complaint-hero__meta-label { + width: 140px; + color: rgba(255, 255, 255, 0.85); + font-size: 24px; + line-height: 36px; +} + +.complaint-hero__meta-value { + flex: 1; + min-width: 0; + color: #fff; + font-size: 24px; + line-height: 36px; + word-break: break-all; +} + +.complaint-sheet { + margin-top: -32px; + padding: 32px 32px 0; + border-radius: 32px 32px 0 0; + background: #f6f8fc; +} + +.complaint-field { + margin-bottom: 32px; +} + +.complaint-label { + display: flex; + align-items: center; + margin-bottom: 16px; + color: #1d2129; + font-size: 30px; + font-weight: 700; +} + +.complaint-label__required { + margin-left: 4px; + color: #f53f3f; +} + +.complaint-label__extra { + margin-left: 8px; + color: #86909c; + font-size: 24px; + font-weight: 400; +} + +.complaint-types { + display: flex; + flex-wrap: wrap; + padding: 8px 8px 0; + border-radius: 24px; + background: #fff; +} + +.complaint-type { + box-sizing: border-box; + display: flex; + align-items: center; + width: calc(50% - 16px); + margin: 8px; + padding: 20px 16px; + border: 2px solid #e5e6eb; + border-radius: 16px; + background: #fff; +} + +.complaint-type--active { + border-color: #165dff; +} + +.complaint-type__icon { + display: flex; + align-items: center; + justify-content: center; + width: 44px; + height: 44px; + margin-right: 8px; + border-radius: 22px; + background: #f2f3f5; + color: #4e5969; + font-size: 22px; + font-weight: 700; +} + +.complaint-type--active .complaint-type__icon { + background: #e8f3ff; + color: #165dff; +} + +.complaint-type__text { + flex: 1; + min-width: 0; + color: #1d2129; + font-size: 26px; + line-height: 36px; +} + +.complaint-type--active .complaint-type__text { + color: #165dff; +} + +.complaint-textarea { + position: relative; + box-sizing: border-box; + min-height: 280px; + padding: 20px 24px 48px; + border-radius: 20px; + background: #fff; +} + +.complaint-textarea__input { + width: 100%; + height: 220px; + color: #1d2129; + font-size: 28px; + line-height: 40px; +} + +.complaint-textarea__count { + position: absolute; + right: 24px; + bottom: 16px; + color: #c9cdd4; + font-size: 24px; +} + +.complaint-placeholder { + color: #c9cdd4; +} + +.complaint-uploads { + display: flex; + flex-wrap: wrap; +} + +.complaint-upload { + position: relative; + width: 160px; + height: 160px; + margin: 0 16px 16px 0; + overflow: hidden; + border-radius: 16px; + background: #fff; +} + +.complaint-upload--add { + display: flex; + align-items: center; + justify-content: center; + border: 2px solid #e5e6eb; +} + +.complaint-upload__image, +.complaint-upload__camera { + width: 100%; + height: 100%; +} + +.complaint-upload__camera { + width: 56px; + height: 56px; +} + +.complaint-upload__remove { + position: absolute; + top: 8px; + right: 8px; + display: flex; + align-items: center; + justify-content: center; + width: 36px; + height: 36px; + border-radius: 18px; + background: rgba(29, 33, 41, 0.45); +} + +.complaint-upload__remove-text { + color: #fff; + font-size: 28px; + line-height: 32px; +} + +.complaint-contact { + display: flex; + align-items: center; + box-sizing: border-box; + height: 88px; + padding: 0 24px; + border-radius: 16px; + background: #fff; +} + +.complaint-contact__input { + flex: 1; + min-width: 0; + height: 88px; + color: #1d2129; + font-size: 28px; +} + +.complaint-contact__clear { + width: 36px; + height: 36px; + margin-left: 12px; +} + +.complaint-footer { + position: fixed; + right: 0; + bottom: 0; + left: 0; + z-index: 10; + padding: 16px 32px calc(24px + env(safe-area-inset-bottom)); + background: #f6f8fc; +} + +.complaint-submit { + height: 88px; + margin: 0; + border: 0; + border-radius: 16px; + background: #165dff; + color: #fff; + font-size: 32px; + font-weight: 700; + line-height: 88px; +} + +.complaint-submit::after { + border: 0; +} diff --git a/src/pages/complaint/index.tsx b/src/pages/complaint/index.tsx new file mode 100644 index 0000000..ff62d5e --- /dev/null +++ b/src/pages/complaint/index.tsx @@ -0,0 +1,216 @@ +import { Button, Image, Input, Text, Textarea, View } from '@tarojs/components' +import Taro, { useLoad } from '@tarojs/taro' +import { useState } from 'react' +import { report, uploadImage } from '@/api/user' +import { LOCAL_IMAGES } from '@/lib/assets' +import { buildFeedbackPayload, COMPLAINT_TYPES } from '@/lib/feedback' +import { getOrderDraft } from '@/lib/orderDraft' +import type { OrderLike, UploadedImage } from '@/lib/order' +import './index.scss' + +const TYPE_ICONS: Record = { + 莫名扣费: '¥', + 超过7天未解决: '7', + 交易被骗: '¥', + 不满意商家服务: '服', + 其他问题: '…', +} + +export default function ComplaintPage () { + const [order, setOrder] = useState(null) + const [type, setType] = useState('') + const [content, setContent] = useState('') + const [contact, setContact] = useState('') + const [images, setImages] = useState([]) + const [uploading, setUploading] = useState(false) + const [submitting, setSubmitting] = useState(false) + + useLoad(() => { + const draft = getOrderDraft() + if (!draft) { + Taro.showToast({ title: '订单不存在', icon: 'none' }) + setTimeout(() => Taro.navigateBack(), 400) + return + } + setOrder(draft) + setContact(String(draft.entity_phone || '')) + }) + + const handleChooseImage = async () => { + if (uploading || images.length >= 5) return + const res = await Taro.chooseImage({ + count: Math.max(1, 5 - images.length), + sizeType: ['compressed'], + sourceType: ['album', 'camera'], + }) + const paths = res.tempFilePaths || [] + if (!paths.length) return + + setUploading(true) + try { + const uploaded: UploadedImage[] = [] + for (const filePath of paths) { + const result = await uploadImage(filePath) + uploaded.push({ id: result.id, url: result.url, preview: filePath }) + } + setImages((current) => [...current, ...uploaded].slice(0, 5)) + } catch (error) { + Taro.showToast({ + title: error instanceof Error ? error.message : '图片上传失败', + icon: 'none', + }) + } finally { + setUploading(false) + } + } + + const handleSubmit = async () => { + if (!order || submitting) return + if (!type) { + Taro.showToast({ title: '请选择投诉类型', icon: 'none' }) + return + } + if (!content.trim()) { + Taro.showToast({ title: '请填写问题描述', icon: 'none' }) + return + } + if (uploading) { + Taro.showToast({ title: '图片正在上传,请稍候', icon: 'none' }) + return + } + + setSubmitting(true) + try { + await report(buildFeedbackPayload({ + contact: contact.trim(), + content: content.trim(), + images: images.map((image) => image.url), + type, + order, + })) + Taro.showToast({ title: '提交成功', icon: 'success' }) + setTimeout(() => Taro.navigateBack(), 500) + } catch { + Taro.showToast({ title: '提交失败,请稍后再试', icon: 'none' }) + } finally { + setSubmitting(false) + } + } + + return ( + + + + {order?.goods_name || '-'} + 已支付 ¥{order?.total_fee || '-'} + + + + 交易ID + {order?.out_trade_no || '-'} + + + 交易时间 + {order?.create_time || order?.pay_time || '-'} + + + + + + + + 投诉类型 + * + + + {COMPLAINT_TYPES.map((item) => ( + setType(item.value)} + > + + {TYPE_ICONS[item.value]} + + {item.label} + + ))} + + + + + + 问题描述 + * + + +