/common/
/* @font-face {font-family: "iconfont";
src:url('/static/font_1365296_2ijcbdrmsg.ttf') format('truetype')
} */
@font-face {font-family: "iconfont";
src:url('data:application/x-font-woff2;charset=utf-8;base64,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') format('woff2')
}
.iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
/* @font-face {font-family: "iconfont";
src:url('data:application/x-font-woff2;charset=utf-8;base64,d09GMgABAAAAACNcAAsAAAAAP2AAACMNAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHEIGVgCKfgrjQM8IATYCJAOBbAt4AAQgBYRtB4R3G+czFeNYS8DGAbBs5iFk/39J4MZQsYb2uoT1RoIE0QHQk41EnGRBJjuN+uRox8y9yi7Mbi9D6VL8Aw2EF/WvXjDihlWzE4LCYbehlD/Pz+3Pfe8tkve2geQYjJYY1Rs1yjFSESmBETVCwGQYjRLiB/UrzviChVFYWI3YCIr6P/zf5o/apqq5qefbsHFY0urtyf5tzdESSMKmALHAsYS4scOkW21iDRIPHHvvV64ylYY6TYEJ9lsEQJqfqw+OTVZHVfE0Q1R/rzyXgwB4a4eRPZRTayZQmJEDrTRy7JZth/YA8HXPkVQgx4FF9mf0le45GhkSlmXLSVpOKVDkFex993k+/zc/f2Zx1vExwbLASws4sQjvOEX1x6L/91NLal3rvkuD15T60gGqiIWA/76cXX1JWyTfJf7WNtvXtE6xtspXSmNJePBNUMIkb4l0Vd4qp9qpFZArPIVmeCYEHoQBsHRAWAAMD5QtWRaKbxJyVNoy6niMzTyWIQ5zih1g/vfYAAEAG8TgCigqJjENmEDAfTgEAKiitDgfmIYkEA5xBMxGOuWsBAO1AQ2Y2BrsLgBsJFce/QReiAmAAQ2H+7jUmuhCkE/iP07gOoMuc/Cb6EB0HAC2ZwPAAcAVAAgA7CQ1hwYAV/KugAP7bCfQcjVy+/iiFCAmLEm48BYolJxSApVZspWoUGdpjuRYLk/enXw6OTb5bvKXyf8KtuG3crcnDIbggaeKp/pfGyN+evFDGNh3sKxAbqYeptK1U5yeH9zpf8xb2bFny7ZN89ZN6eHlwLAuM+OGVAiU4ZjoAq2YdGJAk70+a3oVWZs1YcSyRS1Sg441IAwBNpxr8zXq0Kpdc6Z1QKF6790GFF/R4+cEF4EdQgB7hAZsETqwTRjAJmEC84QFrBM2MEU4QA8IF9gnPOCA8IFhYqQwayQAY4QCxokAGCJCoAIiUjhpxgCUQUyAEoglUAcRA1UQa+CMSIAV4qIIonmD6IQAAyQAaIIEAkskFOgjcmCNxAC9RAkUQeKBGZIAzBIVMEFmASMkG1gmxcAiKQFaIBXAAqkDBslS4JgMAA2QI0AB5BhQA7msKKZ2F4AN8lTqRtoYAG2Qd8AR+QUYJf8Bh/z461aJtgGAXX4rgTn+3XTToE3AJjoAF9IZ7lY+oed+8OXeWniQpWFEJVCHBFUckFV5SOEy4WFKNEUVzaoIqiYlKEwgTfJZRewOiUmhUQcVSHhXpN1Iq1PgYSNylwp8KJ6ju7bterXYinffIFmJ42T6khEwKmRDcoz/kmuaB2uyndRxWksf03htbcyNCpKi7tzZJSukUgrx8WX379u779//kaRCSsru3JORfibJvbBo9+6dek7G4+va1t/Xlz+oKLIspfoG4E5J+tUOSO3eufceeo8k7ers7OtLp+VdEqjdR7lHqV39iPzhfdsJpPOu/v7wCol0QJIGUH8foJQakqSMtHvnFNWm9PEyR8iXvTQHUBmo5h2MbfXqfqFCE5JaQSBO4k3QbMUBUMkhSwgIhatg3UKW7MkVoAM6pao0AtibWZUr07+MqlyL+JlaFgq1Puhiw1H5x1UlGIyULZrLWWiSWjtUEl7+tTSTgWO4qhucxqEz+VyCEwF/yZQA26Gy5ZQwcyP4zY5KuhEMHDKtHEdZsI+IjEmcoxlS7ahxa/nowT9p6vS0fKQFYqpDaXeflTFhffBhwG4jLxYn1MbZaVFdGHbX3NovPXT5177SaRdmvOQgfogb+UYAgLW82oTsnmpiH54cxdQb4rIL6x+lxhhQ4rQs4nzOix0kiFIN8rYiSl4aVO30lDAElwFTHMdN5nSCCiFzDoDlHVc9JqcdxaHGfGnLlDxSo10TAllAt+Og62q5OT4VBFkQ8SiyuMwA2LIvWvLMrS2lZINnTn4pICbPALRo2vgpaSF/hPs4wWWrInc5VXsZdpttCoVwtnexhu2q76Xou7I8QDDHbsJPuQcwFth12GmKj3Qvt4QB0iSrbS7jlB1UW3os6jbtBnhCkpht00oICuvU47GjIwx7kVZ1LDh5ajsX40EKSCUk/MAxzJl33IXjoZ0aWIjy51m1i3Hh8jOZ7sLZiyNGA5s+gXtQsdVheuPfITD8CpqbBeStA5AA7qpu5HubW9luDD/EAL3T0917Bneg42K5nQbAU3rRNYkuLJlq4zQFS9bZQnics1cBxlYQ8HcErHM2deLxTMPn4ailiEfBzs0NpvBlA1o7Hk/GLpksDlbEQhevZa7edZXahE90XVReBJlS7lWqikZ5tdFQDc3BdrNOdMNNVo42UUVvUayBeWnrKdd+bKZnNeuJxVg9yM6/+m852Gm69ZogkAjiSqM+ULIgYAGuN9LRMm1H+vQkSrimIZ59D6TZvpQUU7bDOJIur+/ZNrPxq/6rW/s7Tdd2ADYf9ixhqoaubCZTblhkTNTqTaXoVGX+ORICimIl7+OgRf89EoLPeSlsDcafARFbYL9PNZgPczHZiqJttWQqOkkqXrIEsCB1uGZflTyTxGUUINj/yvUu1F0yfqPphq5Tqk2MnTmulwytSMt6QRCxv17MGlm5YBja14eDC6A7NMm8IlfS08POdUVzHAAcoWAvtreY7CI2qW6tEWFm69XjKXamZX4ZUW89DYot9AZB846hGN/obMJcp8rTaohZ6etgNsUvJuquEtXh2ariNc/AwSW6DsfxrGy741hk7Y2iEm091+2b0N3blOlXd50kVOGJyjDAQb5oj2K4nD1d36Qc6aKONecE9/wSLqNRNcaOFzXzbEFw7igNlnvRPix5Tn/rDC6w80NysK3eTH7jjesKIXsrbDXvQXdFP7OJWafti+alUerSa+FPFEYUVN7ltViAd9vLZrvwMUqfDs1mmAIM3LTQP/MMuelXPuSK5ovBS/5dfOW1vKaUbNPqyLIx8h8bzLnvgr4MhGoc3fMPTNg1P7Yffq3/mSUXSe4k2vH3/8HduW/vIRSCRM/hD/Lbo8m7/b1oDBHPIZ+I+Hwz0VYpX4jCklc1UJTHDF5JqjdjfKlROwZLSZS0n0DHQdjvrkT6GkySsb9O65FJvs5lJUwRCUurzSQII32XtSwd1H8p01E0rBNBCM3rA4bsJPowHDJU1Y2R6Wkjpw2qxhAe0RRHUYyClqEQRRsBo1jmgwMGLOjpQUFyeUMndCDrF7ZCHdF8hsvHzXWqDRa47KA5oGVzjkIh1YxM2lG4uYaNwoCrvrCmGnou76pn2JC1Bqq6Yey72CN+dt/cTQW7JEP8G4WbCaiMjUDKJaR941djrdStJgaMG8gSpqtix2H8m1NCIMR9tDcf5AR3cL/tQvPLuDYC0onTGHjperTUWgGmNMERYGl8sox0IHwieWnaW67CWE+Z6axtjnNvgpX5fDiG07bN0e8ytxFU5GcJZ8prBvFArkneiFqUd1+uJiEue8CGQuBjedt2+pnvgRTmXTacpU3CW194UxXvWcSfzRr1q9dn0pR71GoTvXZucx3Qqis5Ti1S8Gfb4/l8YybtcFlq5waN5vWZlRzlWrD7KcZKU8emFqdNTi1lzvByHyPSySCLQSl1+L7XNk+wFpIoRFYbdprLuNcOQNbaprllb7D1otxUqsNhFAZ6rmVILFi0i0Ju06nKIZgg+hDKNNyN8WOsZY0hJ9W4XFCeGJ/8RhtZo1ju4TAT93axnL271qY9XiutLGgOaSqWspm32pnO+sjaHdTV1ZcGvCnxoS2qO6CrEr+mKu2CxZiKWilwuOAAmh6KLoey1YgDlPVI5sNY1CXWzsZtS0A4t/Ub3KsbMuoZCJ29liCQwT2FMF2Mr2zsBYwPHcImHdQo00udryHVEemArrqdMze9fqxn6o/1bsxaZVftFqRnAWFz8Ac9a5u5GMXWb3qvbACCrX/7d2P5vllz1Nfd/qoLmbYeWL6rEqpu2FvmprVeDh84epLkzeLXg4FMBHGPxtGou8/IVBcVl1SG+zBs2jpCMVYcg5A1z5bFPOE0vAljM4ZHDDMm4ymXA0t07CS5lYAx6rBkSeEkfU7uccqiFufS18mf0q0wth7yi2VkXZbHW2xYC+zRKXcOwiXaqEq5onrCWttqpUFfmcWgGhwQ2FjVvklzozpcTkAzkdQE2LEI45gHoAgxZ3sFB92RnkVmQN8qDP297czaZrGVqwzh5txnU8vmm9fQ+GTbWmfDE0mzje2pJwL7u9+RYnnsT8eoLnPKfSLac6BqK1pjIc21nd8cL+8o99ixLresd5vpjuPnoM96GJJ19+mst79f/TGdGmjT0qpdjJChTuVHxzijawNbNcZww2wvVkqcD38id3SxxLMN44mG/sT8LhlrKeTLyXTwmyvL++bav1xradONO9aM+Q/KO6pnvD4HIAWldFQ64C25+lz/0Im2V1yP8s3vpKJG2mvjEcljVp2/FmDPB5Uw9AiVG7HETf6iKH9Ywbw6HI10paI4zdHkoG2f8pkfXWA/XakEzZ8kojWf/vFPQSN7N6554bnMA6DhNlC5ovUKojXfmNMhjq5m6msU6YuLRHjHk25EdLxBlT85CvjCYS2L/JnxcGC7DBCD6XRq8tYQD88KaK8mfL/oC1uxp2sHTG5ds26UEVxGGwi60L9GATAwDuJp/C6arXEM0KjFvrHDE1I3CfHWD/rO450hP+WaZJL7klMTaeCTwrSgkVrly69+XnfTk78Zyev3zwBbb46FKc3f/h924dwZQdEVG1GLlNqdojI+U60PZ7JvMN977n2eUPIMyPKdNZl6+MFiTWHcCbi1bVATs/omrBjwswFwlofW+WazKDNsEXiksp7r8TchNLcGDfequlBvE7EmLl00vWTol9caGwOG5xOYN5cfRdNfy10LX3HEv5xrDa2Hqso4m8nEes86FoAgU/gUnZmcbEd4FePStui4W0PUXl3ZupjLxv/7yRwBnr9YtFj/7I2Nwb2OIcNr4UtfuTn7NlGwr6uDPco9mlGEe34PKOzQVDubpDKxXEe6YhfnDci+pR8PpI+yftK+nescHMwfEZ57a1pSwnfzvMM3fc2//HRbuCdelHGUN8pq37yvgLjNcinv+0w1gQSjqiWCCwKdFqph+JF9MP6K5STbjKkZEkEmJVmYNzOKEP5g4/COO6JmvKQn67lvuMeS6W90DT40HT+LL9VW80eiyo2rqxkM/zC2SGYjlDDUaLPMif0KC7a/YqStMaMuCAgbUjkmkdznHbXPwicYD2oRJfOcC9RUfDSBSX6UbZjqaCMpDFslxKuqEGgDtG2mrAmgBoYuOhdphkdsx1+bvGaZLr7tJydtBqzadOyWEzcVEhyPDvoDek9vEuoemE6IsuyODT0qTq1t3H9zmbZQQkmwSIiAIfp6vpCIum3Cbp86FIZor5KZOmrj320dUxummVorNpN6xzfPbNABzAkb798UaF+wDuHI5NnH1exvOAs0gQ6n89q72QtFAu33bIugR+Qs9sJ27V/sbD6zhSh438f0ZUQ3tDH7zw+J9QcmWskwmcbmmGFYrhR5EEZu048QGLIC5MQ8ZYyhRAR073RmHE12jBuLD7CDjhLoS8pS2rwbdASJCKPZOSzyvMLj2JF+n3iSEZsxrtMxSAAEkWVXeUocJFI27z3nFee9GXBfceHAUuAYzAwcimekENyFJHCNTI24FM9gZsSbeXogLQcumL3/Kupphp9i8INHOSEdIce9XPgyYxnfqVZCkHv0B9DoEXw9h28m5mJ1Zza1Y+3oTCc279bB5+DrfzsPlJWhjVVYcmiQaZWhcrRBFzYIS19XWbVGjSrVpkHmlSo1SkYVKvMXyQ972nhXQNfyOW0TuSZyso2fN00FHQRKKChIQPIUn/J+K5pxTExysjKGRlMqk9U33u+1ciexRTN7tihN1UolRVGTT88ijOzqWkUmoIIClACw+2iYefLA9YFk8zBWaEExSzEgGFCwigtCydDrYdfLdwmKBQezIg5FLDuIQh6CxuNdXYeObtpEtz0rPpeZedb6bEzMVk1qnWrG/yowfGm1mcJULVGbKszU5E5Zv3O/06DzoOzZB4es1tYshxzH4SCTXOSZcswZrnWUSCX5A1Ex8iLyAKQrgvVhmXkZdmnSlhZpml3GEGSeqqUlTZphN4Si5oVLmr74h3//OcZcujS6BII+fBfEO/KXXfWb+Omm0+Mhl+8RXlH06uOHXf60WOaW3PW63h3L2VxVnYXzn1/8mqJdQNaelo+1tKA8lC8jH9MQy3mclvvWdrvhh7NS5P/5tq9/viWpzleY9Gt4MVHmSauzeanI/1EHnrv0cvsZzZBENGz/uhFrwHZ8vSqAlJSlUdJm956HtJWA8SgeqeBhwNx885JvFzntp9bZ2djChVhuyF64KBs5X7QIvdwXLYQA0IE6NpYGtDKcGjivTMY5AP0gMo2J/8rR2LiejmX/Jmr8brJB9L/xDPiu8eX39BSznuWpHb6bTIXyYis/FuFtDzgstzNxC6Jqfa/yaJxf/KW+HnLY/FwOGZuNgcEwC9XXo1m9Qerxs5Cu0K/SSq0eGh4pXi83mx9ewsdyzINHmZMcwdyOxRrkRGSVwyHNKR7NuFbfXcfV8ylLiq9fe3QLa+BxWn96P4ir05vhBoOh//2UWV0hlqfiiOv17acDqrY7EVkdN2cYV5e7MDyOvTZP2/ueFFJ+tiB46F1vGH2yb/+ujvgaXIHmli1XI/Hgl+1gutc4INfOyjbC3O/QM5dUyu3sL6m79GG122gkSfdZtFRoXcOj2Banck7GII8zGZape1J1tDiknd81G2Whpi5MSygTdJeqsZmDh7FdcaO5fBF6THaTV5HIOv82fC49CdOi6VFes2NXCHWjNgdbCnipHjTLZ+EPsRGM99HSV+ugoLccEIy2jZtBET3KNLpst9qsaDXMwbTtX1WhStTxFdJCVtEatVlLVHuBK1TNXB+JRWFGGMgV5ka2Z9mgnPmLM5JajOU4qR1Z2Gzsw/+x7cQfLNYd4VkWPSeoahrz7Kod02wKeu/8aaYb2hPib2bFwDDHz8jMUoGhFPcAU+vqZJkZZw1/px0bO6oCLMS1b5ysxvuxmhdt6/k2xnblO5GQU/B2vXuhlatVoft2Nj2CI2R+Vfi2VVyo2x26q6VAvL2NHsGQRqmjShkJBe6tHwu4Umc7TgSdfci90I3B0YtXdZwIP9le6N76psDEXpliNWVE0NsOuUNs8qEsssGuUZAlKLbTkCMv+nQNTL2gQdog0KPEoqJE1EdqLPkSVzClTTW6fXGxoC8RFRWhRFc9SF+XvjaNewFN0UjRNC+ZPtOgZyIa+nYQTQB1zQDWdXmM251Mneeh8AWzskt3aqkk1SxVhj6PO6uKM4yMlAX3Cf5674UbX+vS2YLIg5JM2zBlCOueT/z1f/5e4ty0bqJztrNt5/9Xln9Z5DgYXyMOTzpS8773wAUZkl3IXtcAYctG/FQYiszMjET+Y5ydCZGYn+pyoXFzDypj7v0OrqzhD0y0kkd77VFZc4+d9LZE6YJ7pM0XyD0tIlFmJvJ6IfsBdgz3kVSI5mq2fK+8HOXYLb3kkWtdU71cJMrBNAfNFpgrksJIAs9l/6sE3r1/VWQ0u69deXl1Sthj8cbaa7X2Xcc7d59YmjqydlVY2qqsrOCQkRUWjkOOrZKjCYlZc9Tz3desfrRM/e8xaXvt9Vpx59XvJk8sVB1dtyI0dWlWYkJIh63jkP8WcYJzr3+vZEq00lpZPCOnNDFj9ZbOOy3JV/YwaCvqJVt+st88r0F/fPPxtSl7dqctDs2szC6d2SoRTvWg6maXJM5cv3fnqoyrm49fnVfvuPUn6676hkdPNl/dmH63pytzeUhWVkK1NBFpNCjRiRBvpxUEtsTHm5qOhhYSETq2+g5/xXScLgmWYO2UOC99nkeiDMVkZ8cgJVJFKAZ5qTalm5euh2zuX0RAkOmHEu0gs515ltovePWHKdtY/VS/rk0n+EKg1sEuktHN3KpCKZWVKRkFVVRiKaotjH0MRyXjAIvF7GfiXnuQYuZMhckIQrmqgBGlrWSNqzmdTLdJN6K7uAa4utCNtOqSdHPXtRIJeH2sLRBH40FuA6Zz9dYxc4cbxJvEDcP5wxreJp5GtbomLo4+bczK085i0KOBQdtKjLYGyw/EH7Br+kZPD9rn+1uJSfHsK80LCC+63Dy70aRp9mVd6ASV7dU1Z7qvZTlmrqe9XB+s2Ba/LVj+stXCSpavmhHf0HVBC+FyI6fbUYpoNGcOikafk4VRqwCi+JZfBBOGLhnaDoMe3N3O15el7OmoSL32ilqxZC+P1OdapKaJ1isKLNIK7PxqS+dx9zg5X3eocAo8QnRULpl9kuzITRP6Z1pFez4X+RF1abeeu8f52jKPnI45c68vc8+p2MBdS+qTUv4oFLYqCo1G4Ka6xdmj1Ade95sTCMbnWbGkIIRqz00T+XlromgW+pcx1EJApC2zDVhmLGgLPsY9OqR5mPnygDd0tLEpk+D8qpl3l2IulHlJxiLHokVeC6TUFKVQc5Xge46eePac2InvfPp8Nz4X9s7xS05D2NKItnT32E2qtFb8wnk8F7VeuFCDRFgt52ioMSojMGpL3MUV2uVXrmgEmtGXRcK7Qs2dO0eOGsaMFejX8ikLG0sbC+r0NaNoSbp1uriom7/aSBINEtxzIOiR8ySPqGWigFwDBAoVRaJ+p36Rn9/GdQtehG8ZPlsk6yEKiyw77tIvKjTui/ARyfuGie/DoboiwNjYS8K3PKwJK1i8LAfZZ6Mly1BBU5hPueilkPv5SpmxLtMcV42V7EFO9NXDVea6zDLjK59zxr6tOsOUKp2TFWkz5Zjp+3i/f49bZrhryG7zvXHeRSfmuGmwKuiItqOnB8PKn1n7mefhLpxnDlKD2vNUOnWesf9H5n6GyrQWj9i17sf+kvenzgeeCqJS0WySWGhHpdumU5IH+A1L29h6dtuIMDQtg/YwegT+t9Xb/g8kumx48u3Jb1Z8aNiuL1qRra4fqLuw/9HF/fOKcpKXvynevrOh568EM8Hz4QISXPecJpHQnhupiOs1B69z0wFiaopAPlAR6lchBAuKfYpFr5zGZdbeBieDo0HZAyP+d/zfyeBtLRt3eiUr9i4msfGNA0Q9MrasRxRg/AhHwOkKD8UL33+IN0bTLLef/kcpxdCqkeuYk0JWKhHZgzBeLDaJG6GhKPu/79pHoG5UP4riBR9cL7fqPMOJ8Xh4HTkqwleDMG5fFA82oshJLBIWOUTAKwTPyyNjptvvlZZQztQvv4AbWVM7OCQYIw6+EK52fPhAsDqJaqTc3VGjtBhNS1As1ZCGuEbaUAVocBFgBgnesCk8VS1KptXQIxn4FRSLo2k5Opc6I6M6fTAmOeATC1dEDFpkC02YnJe6NWLT5jDzJl8ibcUaGldi8LYoeM+l2V26AE3NqamNsRDbmHrvbIo3xK+ShVVTmiRrBxjCzxP//kukfL4hbKcVBOnx589xZH1FqF/5OUffWXxyRXkMy4tHDbPqmz37yLOFUNOc81AAsc1+bwHAN1XXH4dyclBcf5B5QhwyDIUepZr37Ozf2WPeTwZ0ar92bQFpY02Rd0nxkq0F5IJr13osoixO9en373HlA2bLauvNTm3yqz9NJa2NgNO6PyZn3xrMyY35YtVi8xn2c1dvP9DF+FS2Td0YEtzvtfzbjd00Hf3KksA6m1jimmWqtWrZqkRxsniYvsoMgAQABIiCVhvKWpvnQQk88must9pSHnlaa0pgXZPv8ZiNrGcjrMX64jnveJdJpkkkl6u22oD0Th4JxBD8lFjLfw50qHHI8n+CpP/233S6dGO+j2W29sm7RKrDOqt+J2bO+Ovs/Jb7fkx5WlWk9F4J62GKxfxOXOWQZ5pkqkieVb0SZcfEZpgYL6VfXUrLiCNp7+ASVVj23fiXSX756P3MKlUlEiYVLs6srDkgF+2O2uMfuj/Y0yWGrneXfyY2dMT0jXtf9nwxejxxnw2no5KmF5fjMWidjc1ChSrH8XKkvCGP1oOqVpsEQU5OQfa5rsbLxb1H2gSw4yIU+WQY1oytWbeBBMx1eo+Zt1lPeqcRsgKjXm/2eOQE+uXdgQM54TOwBTt3LsBmRNifXPl900m1R24Xdu063jXBR2LEvxgRfWaNmdSsx6uzHvKq1gje+oxAuK2lohcUctih2JEIRRqU4PTOo3fm6tIb0QtyOfQKEt+boIQiTSL0Kkxd3K947IjYEQCPdMkcLsCL+qT4BFMz3+iubbftHTe3c7bnkt3Rnv0ubKAU+aEJXXpKerLYuhq0irEKzPThDtv2xS+TxR/Epybxg6ZPTeFF+ORUiHjZst0Gw7ldLbmDfVmLnT6NmbyuIVCNIK/FH16eHBvjgQXJHxvnUTyMP+zjY3zSAnhWaHTlUszTN2hsFnGDuCnEcYLFkm/g4HZWHCS+mySQD8AObgomJ/ED+EHcwLvOuE4bm2TRaVH9taIy5ms1eet5j09eXBKnk2NFeT9Mfz9TOtNGbIfDaap6ZO2vlCoB4MY/z3j/WljynvAsVX3YxOfy+nhcCx6vn/ffFhu+blg9obOmLM372uVkZU8Gv/JwZF04KihfkYypsYoVlEEo4us6dh3YN8nQh/VYcBbPq/HLubzaEN24IMElRdjKlViREyGu15zrfuTkcfzTzXOiOYC9go81ng2quqBKmDg+kTCDn5yxMlBB83NCIiGSjmz7EdrpT+AMr4RvGsoTvad/4tG9E0+uX6HlW974A5PoaBMyXZIOzdUGQ/VvhrcAAB6LtRGvdTgbwPDBNpwj5Hsswato8Q24Uf6hw7iHMb4Ge2u9dQwXg7XKe3wR5IfMwOVOcR32dNljl3FXIv4XbgniZvfxWOH7gZuJw3jL/9//n0Y7jN+kxf/GftC76yIZTXNsX5TTe8wG7MEqPoTtWHzdhdsYxg3hUnMKjRD1eNat+47iXsAuObEIYB+LoPK9TrloM4zag9vBMgOejD1adsMV3D/fbRIvSZI4QtagszErSKaeeri5BBdfbA7Ji7Kz1T94DWrGXri0mrY/gw+hCWRu821Uyy46g2vzVw3iJk7xC9inZa9txyWqrioS/7geDOS1l3tyjUK+MDl/zF43+vWF+2T7amxlAvafPA3+Td1MTP2ZZTitv9TdWfD12IMoHMYEro6orjYkFXxFMEYHYwsDAPbjOIB/abcL4FX1UA8JjSNg7hIgYsAAKxEHJtgJxHRXkQYc8BHpwIRwkQ3TQTkIB4RQCzgg4p+8DStAREDCYREDPlwUcSDhnkBMH2c0aPgFRDqQCBfZkIGEITkzdChgm0bQGQa1nxX2TCYeza3c9zdY8Trq68jS/xATrlO3VzfvT34AQYwiIJ3sXc5GmcijehfGB96zmiIfoM9XLufp7fra0Cq46nlctZERdIZB2c/SvbFnMs+cW9Pl/w1WvI4OOz3f+z/EhM9fuXXlxoPwoZKnnWalOJ3snYy4UeKWkUflHXHwD5PVH/JvyQfozSsugDW9uUYHM17Vq+QbRvZUS9L6dnn/E7tAQERCRkFFQ8fAxMLGwcXDJyAkIiYhJSOnoKSipqGlo2dgZGJmyYo1G7bs2HPgyIkzF67cuPPgyYs3H778+C88pmXZYs+VZz0g2dLLjHTbGKAzarK3P9rBRJLT1DvZaXy6LTJLkp/W7OyLilbrAEeybtBU+1aL0BbVyWlW5iw9rbLuxh+EFjHofWvkgDRABGpmzTNifrtLbgYsF5xFU3MEJ26tbYLcJMOWsPgRiowHfkk1aVyc5vqoaUbM68QxIujVlJGlXqiyAXYQLmaWM5ZZeqFHZ2RxqKnF2WhSzEJCe2tCTcU2Y6SbNhhIDxJuNmt7mV3PRhJEgq+bh9LSbHss92yKxGpXSLrOsF2ESxMf9K+IbR41X+5xr4BkPxv02OetsuCutnUZk8PKwIQb6/SwwmhECW2tE6yL8rJvywQ69m6rh2GHZHg3avSbcd4ZpGEFAA==') format('woff2')
}
.iconfont {
font-family: "iconfont" !important;
font-size: 16px;
font-style: normal;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
} */
/pages/common/search/
<template>
<view class="page">
<!--Navigation bar-->
<free-nav-bar title="My Collection" showBack :showRight="false">
<input type="text" v-model="keyword" placeholder="Please enter keywords" style="width: 650rpx;" class="font-md" @confirm="confirm"/>
</free-nav-bar>
<block v-if="searchType==''&&===0">
<view class="py-3 flex align-center justify-center">
<text class="font text-light-muted">Search for specified content</text>
</view>
<view class="px-4 flex flex-wrap">
<view class="flex align-center justify-center mb-3" style="width: 223rpx;" v-for="(item,index) in typeList" :key="index" @click="changeSearchType(item)">
<text class="font text-hover-primary">{{item.name}}</text>
</view>
</view>
</block>
<free-list-item v-for="(item,index) in list" :key="index" :title=" ? : " :cover=" ? : '/static/images/'" @click="open()"></free-list-item>
</view>
</template>
<script>
import freeNavBar from '@/components/free-ui/';
import freeListItem from '@/components/free-ui/';
import $H from '@/common/free-lib/';
export default {
components:{
freeNavBar,
freeListItem
},
data() {
return {
typeList:[{
name:'Chat history',
key:'history'
},
{
name:'user',
key:'user'
},
{
name:'Group Chat',
key:'group'
}],
keyword:'',
list:[],
searchType:''
}
},
methods: {
confirm(){
$H.post('/search/user',{keyword:this.keyword}).then(res=>{
this.list=[];
if(res){
this.list.push(res);
}
})
},
// Open user profile
open(id){
uni.navigateTo({
url:'../../mail/user-base/user-base?user_id='+id
})
},
changeSearchType(item){
console.log(item);
this.searchType = item.key;
}
}
}
</script>
<style>
</style>
/pages/chat/chat-history/
<template>
<view class="page">
<!--Navigation bar-->
<free-nav-bar title="Chat history" showBack :showRight="false">
</free-nav-bar>
<!--Search box-->
<view class="p-3 bg-light position-fixed left-0 right-0" :style="'top:'+top+'px;'">
<input type="text" value="" v-model="keyword" placeholder="search" class="bg-white rounded" placeholder-class="text-center" style="height: 80rpx;"/>
</view>
<view style="height:140rpx;"></view>
<!--Contact list-->
<view class="px-2 py-1 bg-light">
<text class="font-sm text-muted">{{keyword ? 'Search results' : 'Recent Contact'}}</text>
</view>
<view v-for="(item,index) in allList" :key="index" :id="'chatItem_'+index">
<free-chat-item :item="item" :index="index" ref="chatItem"
:pretime=" index > 0 ? list[index-1].create_time : 0"
:shownickname="true"></free-chat-item>
</view>
<view style="height:100rpx;" class="flex align-center justify-center" v-if="keyword !== '' && === 0">
<text class="font text-light-muted">No search results yet</text>
</view>
</view>
</template>
<script>
import freeNavBar from '@/components/free-ui/';
import freeMainButton from '@/components/free-ui/';
import freeChatItem from '@/components/free-ui/';
import freeAvatar from '@/components/free-ui/';
import {mapState} from 'vuex';
export default {
components:{
freeNavBar,
freeMainButton,
freeChatItem,
freeAvatar
},
data() {
return {
keyword:'',
top:0,
list:[]
}
},
computed:{
...mapState({
user:state=>state.user.user,
chat:state=>state.user.chat
}),
// Final list
allList(){
return this.keyword === '' ? this.list : this.searchList;
},
// Search result list
searchList(){
if(this.keyword === ''){
return [];
}
return this.list.filter(item=>{
return item.data.indexOf(this.keyword) !== -1;
})
},
// Select the list
selectList(){
return this.list.filter(item=>item.checked)
},
// Select quantity
selectCount(){
return this.selectList.length;
}
},
methods: {
// Click on the navigation bar
handlenNav(){
if(!this.muliSelect){
return this.muliSelect = true;
}
// send
console.log('send')
},
// Select, uncheck
selectItem(item){
// Select, uncheck
if(this.muliSelect){
// Select
if(!item.checked && (this.selectCount === 9)){
// Limit the selected number
return uni.showToast({
title:'Select up to 9',
icon:'none'
})
}
// Uncheck
return item.checked = !item.checked;
}
// send
this.$refs.confirm.show((close)=>{
console.log('Click OK');
close();
});
}
},
onLoad() {
let res = uni.getSystemInfoSync();
let statusBarHeight = 0;
// #ifndef MP
statusBarHeight = res.statusBarHeight;
// #endif
this.top = statusBarHeight + uni.upx2px(90);
this.list = this.chat.getChatDetail();
}
}
</script>
<style>
</style>
/pages/chat/group-remark/
<template>
<view class="page">
<!--Navigation bar-->
<free-nav-bar title="Group Announcement" showBack :showRight="true" bgColor="bg-white">
<free-main-button name="Push" slot="right" @click="submit"></free-main-button>
</free-nav-bar>
<textarea v-model="remark" placeholder="No announcement yet..." class="bg-white p-2 font-md" style="width:750rpx;" />
</view>
</template>
<script>
import freeNavBar from '@/components/free-ui/';
import freeMainButton from '@/components/free-ui/';
import $H from '@/common/free-lib/';
import auth from '@/common/mixin/';
export default {
mixins:[auth],
components:{
freeNavBar,
freeMainButton,
},
data() {
return {
remark:'',
id:0
}
},
onLoad(e) {
if(!e.params){
return this.backToast()
}
let params = JSON.parse(decodeURIComponent(e.params));
console.log(params)
this.remark = params.remark;
this.id = params.id;
},
methods: {
submit(){
$H.post('/group/remark',{
id:this.id,
remark:this.remark
}).then(res=>{
uni.showToast({
title:'Push successful',
icon:'none'
});
uni.navigateBack({
delta:res
});
})
}
}
}
</script>
<style>
</style>
/pages/mail/apply-list/
<template>
<view class="page">
<!--Navigation bar-->
<free-nav-bar title="Friend application list" showBack :showRight="false">
</free-nav-bar>
<free-list-item v-for="(item,index) in applyList" :key="index" :title=" ? : " :cover=" ? : '/static/images/'" :showRight="true" :showRightIcon="false">
<view slot="right">
<free-main-button v-if="==='pending'" name="agree" @click='handle(item)'></free-main-button>
<text v-else class="text-muted font-sm">{{item|formatTitle}}</text>
</view>
</free-list-item>
<!--Pull-up loading-->
<view class="flex align-center justify-center py-4 bg-light" v-if=" >= 10">
<text class="text-muted font">{{loadmore}}</text>
</view>
</view>
</template>
<script>
import freeMainButton from '@/components/free-ui/';
import freeNavBar from '@/components/free-ui/';
import freeListItem from '@/components/free-ui/';
import freeDivider from '@/components/free-ui/';
import $H from '@/common/free-lib/';
import auth from '@/common/mixin/';
import { mapState } from 'vuex';
export default {
mixins:[auth],
components: {
freeNavBar,
freeMainButton,
freeListItem,
freeDivider
},
filters:{
formatTitle(value){
let obj = {
agree:'Passed',
refuse:'Rejected',
ignore:'Ignored'
}
return obj[value.status];
}
},
computed:{
...mapState({
applyList:state=>state.user.apply.rows
})
},
data() {
return {
form:{
friend_id:0,
nickname:"",
lookme:1,
lookhim:1
},
id:0,
page:1,
loadmore:'Pull-up Load More',// No more, loading...
}
},
// Listen to pull down to refresh
onPullDownRefresh() {
this.page = 1;
this.$store.dispatch('getApply',this.page).then(res=>{
uni.showToast({
title:'Refreshed successfully',
icon:'none'
})
uni.stopPullDownRefresh()
})
},
onLoad(e) {
},
// Listen to bottoming events
onReachBottom() {
if(this.loadmore !== 'Pull-up Load More'){
return;
}
this.loadmore = 'loading...';
this.page = this.page+1;
this.$store.dispatch('getApply',this.page).then(res=>{
console.log(res)
this.loadmore = this.applyList.length == this.page*10 ? 'Pull-up Load More' : 'No more';
}).catch(err=>{
this.page = this.page-1;
this.loadmore = 'Pull-up Load More';
})
},
onShow() {
if(this.loadmore !== 'Pull-up Load More'){
return;
}
this.loadmore = 'loading...';
this.page = this.page+1;
this.$store.dispatch('getApply',this.page).then(res=>{
console.log(res)
this.loadmore = this.applyList.length == this.page*10 ? 'Pull-up Load More' : 'No more';
}).catch(err=>{
this.page = this.page-1;
this.loadmore = 'Pull-up Load More';
})
},
methods: {
handle(item){
uni.navigateTo({
url:'../add-friend/add-friend?id='+item.id,
})
}
}
}
</script>
<style>
</style>
/components/free-ui/
<template>
<view>
<view :class="getClass">
<!--Status bar-->
<view :style="'height:'+statusBarHeight+'px'"></view>
<!--navigation-->
<view class="w-100 flex align-center justify-between" style="height: 90rpx;">
<!--left-->
<view class="flex align-center">
<!--Return button-->
<!-- #ifndef MP -->
<free-icon-button v-if="showBack" @click="back"><text class="iconfont font-md"></text></free-icon-button>
<!-- #endif -->
<!--title-->
<slot>
<text v-if="title" class="font-md ml-3">{{getTitle}}</text>
</slot>
</view>
<!--right-->
<view class="flex align-center" v-if="showRight">
<slot name="right">
<free-icon-button @click="search"><text class="iconfont font-md"></text></free-icon-button>
<free-icon-button @click="openExtend"><text class="iconfont font-md"></text></free-icon-button>
</slot>
</view>
</view>
</view>
<!--Placeholder-->
<view v-if="fixed" :style="fixedStyle"></view>
<!--Extended menu-->
<free-popup v-if="showRight" ref="extend" :bodyWidth="320" :bodyHeight="525"
bodyBgColor="bg-dark" transformOrigin="right top">
<view class="flex flex-column"
style="width: 320rpx;height: 525rpx;">
<view class="flex-1 flex align-center"
hover-class="bg-hover-dark"
v-for="(item,index) in menus"
:key="index"
@click="clickEvent(item)">
<text class="iconfont pl-3 pr-2 font-md text-white">{{item.icon}}</text>
<text class="font-md text-white">{{item.name}}</text>
</view>
</view>
</free-popup>
</view>
</template>
<script>
import freeIconButton from "./"
import freePopup from "./"
export default {
props: {
showBack:{
type:Boolean,
default:false
},
backEvent:{
type:Boolean,
default:true
},
title: {
type: [String,Boolean],
default:false
},
fixed:{
type:Boolean,
default:true
},
noreadnum:{
type:[Number,String],
default:0
},
bgColor:{
type:String,
default:"bg-light"
},
showRight:{
type:Boolean,
default:true
}
},
components:{
freeIconButton,
freePopup
},
data() {
return {
statusBarHeight:0,
navBarHeight:0,
menus:[
{
name:"Start a group chat",
event:"navigateTo",
path:"/pages/mail/mail/mail?type=createGroup",
icon:"\ue633"
},
{
name:"Add a friend",
event:"navigateTo",
path:"/pages/common/search/search",
icon:"\ue65d"
},
// #ifndef H5
{
name:"Scan",
event:"",
icon:"\ue614"
},
// #endif
{
name:"Come and Pay",
event:"",
icon:"\ue66c"
},
{
name:"Help and Feedback",
event:"",
icon:"\ue66c"
}
],
}
},
mounted() {
// #ifdef APP-PLUS-NVUE
this.statusBarHeight = plus.navigator.getStatusbarHeight()
// #endif
this.navBarHeight = this.statusBarHeight + uni.upx2px(90)
},
computed: {
fixedStyle() {
return `height:${this.navBarHeight}px`
},
getTitle(){
let noreadnum = this.noreadnum > 0 ? '('+this.noreadnum+')' : ''
return this.title + noreadnum
},
getClass(){
let fixed = this.fixed?'fixed-top':''
return `${fixed} ${this.bgColor}`
}
},
methods: {
openExtend() {
this.$refs.extend.show(uni.upx2px(415),uni.upx2px(150))
},
// return
back(){
if(this.backEvent){
return uni.navigateBack({
delta: 1
});
}
this.$emit('back')
},
search(){
uni.navigateTo({
url: '/pages/common/search/search'
});
},
clickEvent(item){
this.$refs.extend.hide()
switch (item.event){
case 'navigateTo':
uni.navigateTo({
url: item.path,
});
break;
default:
uni.showToast({
title: 'Beautiful boy, play for yourself',
icon: 'none'
});
break;
}
}
},
}
</script>
<style>
</style>
pages/chat/chat/
<template>
<view>
<!--Navigation bar-->
<free-nav-bar :title="" :noreadnum="totalNoreadnum" showBack>
<free-icon-button slot="right" @click="openChatSet"><text class="iconfont font-lg"></text>
</free-icon-button>
</free-nav-bar>
<!--Chat content area-->
<scroll-view scroll-y class="bg-light position-fixed left-0 right-0 px-3"
style="bottom: 105rpx;box-sizing: border-box;" :style="chatBodyBottom" :show-scrollbar="false"
:scroll-into-view="scrollIntoView" :scroll-with-animation="true" @click="clickPage">
<!--Chat information list component-->
<view v-for="(item,index) in list" :key="index" :id="'chatItem_'+index">
<free-chat-item :item="item" :index="index" ref="chatItem"
:pretime=" index > 0 ? list[index-1].create_time : 0" @long="long" @preview="previewImage"
:shownickname=""></free-chat-item>
</view>
</scroll-view>
<!-- #ifdef APP-PLUS-NVUE -->
<div v-if="mode === 'action' || mode === 'emoticon'" class="position-fixed top-0 right-0 left-0"
:style="'bottom:'+maskBottom+'px;'" @click="clickPage"></div>
<!-- #endif -->
<!--Bottom input box-->
<view class="position-fixed left-0 right-0 border-top flex align-center"
style="background-color: #F7F7F6;height: 105rpx;" :style="'bottom:'+KeyboardHeight+'px;'">
<free-icon-button v-if="mode === 'audio'" @click="changeVoiceOrText"><text
class="iconfont font-lg"></text></free-icon-button>
<free-icon-button v-else @click="changeVoiceOrText"><text class="iconfont font-lg"></text>
</free-icon-button>
<view class="flex-1">
<view v-if="mode === 'audio'" class="rounded flex align-center justify-center" style="height: 80rpx;"
:class="isRecording?'bg-hover-light':'bg-white'" @touchstart="voiceTouchStart"
@touchend="voiceTouchEnd" @touchcancel="voiceTouchCancel" @touchmove="voiceTouchMove">
<text class="font">{{isRecording ? 'Relax end':'Hold and hold to speak'}}</text>
</view>
<textarea v-else fixed class="bg-white rounded p-2 font-md" style="height: 50rpx;max-width: 450rpx;"
:adjust-position="false" v-model="text" @focus="mode = 'text'" />
</view>
<!--expression-->
<free-icon-button @click="openActionOrEmoticon('emoticon')"><text class="iconfont font-lg"></text>
</free-icon-button>
<template v-if=" === 0">
<!--Extended menu-->
<free-icon-button @click="openActionOrEmoticon('action')"><text class="iconfont font-lg"></text>
</free-icon-button>
</template>
<view v-else class="flex-shrink">
<!--Send button-->
<free-main-button name="send" @click="send('text')"></free-main-button>
</view>
</view>
<!--Extended menu-->
<free-popup ref="action" bottom transformOrigin="center bottom" @hide="KeyboardHeight = 0" :mask="false">
<view style="height: 580rpx;" class="border-top border-light-secondary bg-light">
<swiper :indicator-dots=" > 1" style="height: 510rpx;">
<swiper-item class="row" v-for="(item,index) in emoticonOrActionList" :key="index">
<view class="col-3 flex flex-column align-center justify-center" style="height: 255rpx;"
v-for="(item2,index2) in item" :key="index2" @click="actionEvent(item2)">
<image :src="" mode="widthFix" style="width: 100rpx;height: 100rpx;"></image>
<text class="font-sm text-muted mt-2">{{item2.name}}</text>
</view>
</swiper-item>
</swiper>
</view>
</free-popup>
<!--Pop-up layer-->
<free-popup ref="extend" :bodyWidth="240" :bodyHeight="450" :tabbarHeight="105">
<view class="flex flex-column" style="width: 240rpx;" :style="getMenusStyle">
<view class="flex-1 flex align-center" hover-class="bg-light" v-for="(item,index) in menusList"
:key="index" @click="clickEvent()">
<text class="font-md pl-3">{{item.name}}</text>
</view>
</view>
</free-popup>
<!--Recording prompts-->
<view v-if="isRecording" class="position-fixed top-0 left-0 right-0 flex align-center justify-center"
style="bottom: 105rpx;">
<view style="width: 360rpx;height: 360rpx;background-color: rgba(0,0,0,0.5);"
class="rounded flex flex-column align-center justify-center">
<image src="/static/images/audio/audio/" style="width: 150rpx;height: 150rpx;"></image>
<text class="font text-white mt-3">{{unRecord ? 'Relax your finger and cancel send':'Swipe your fingers up, cancel send'}}</text>
</view>
</view>
</view>
</template>
<script>
// #ifdef APP-PLUS-NVUE
const dom = weex.requireModule('dom')
// #endif
import freeNavBar from "@/components/free-ui/"
import freeIconButton from "@/components/free-ui/"
import freeChatItem from '@/components/free-ui/';
import freePopup from "@/components/free-ui/"
import freeMainButton from '@/components/free-ui/';
import {
mapState,
mapMutations
} from 'vuex'
import auth from '@/common/mixin/';
import $U from '@/common/free-lib/';
import $H from '@/common/free-lib/';
import $C from '@/common/free-lib/';
export default {
mixins: [auth],
components: {
freeNavBar,
freeIconButton,
freeChatItem,
freePopup,
freeMainButton
},
data() {
return {
scrollIntoView: "",
// Mode text input text, emoticon expression, action operation, audio audio
mode: "text",
// Extended menu list
actionList: [
[{
name: "Photo Album",
icon: "/static/images/extends/",
event: "uploadImage"
}, {
name: "Photography",
icon: "/static/images/extends/",
event: "uploadVideo"
}, {
name: "collect",
icon: "/static/images/extends/",
event: "openFava"
}, {
name: "business card",
icon: "/static/images/extends/",
event: "sendCard"
}, {
name: "Voice Call",
icon: "/static/images/extends/",
event: ""
}, {
name: "Location",
icon: "/static/images/extends/",
event: ""
}]
],
emoticonList: [],
// Keyboard height
KeyboardHeight: 0,
menusList: [],
navBarHeight: 0,
list: [],
// Bubble index of the current operation
propIndex: -1,
// Enter text
text: "",
// Audio recording status
isRecording: false,
RecordingStartY: 0,
// Cancel recording
unRecord: false,
detail: {
id: 0,
name: "",
avatar: "",
chat_type: "user"
}
}
},
mounted() {
var statusBarHeight = 0
// #ifdef APP-PLUS-NVUE
statusBarHeight = plus.navigator.getStatusbarHeight()
// #endif
this.navBarHeight = statusBarHeight + uni.upx2px(90)
// Monitor keyboard height changes
uni.onKeyboardHeightChange(res => {
if (this.mode !== 'action' && this.mode !== 'emoticon') {
this.KeyboardHeight = res.height
}
if (this.KeyboardHeight > 0) {
this.pageToBottom()
}
})
// Register to send audio events
this.regSendVoiceEvent((url) => {
if (!this.unRecord) {
this.send('audio', url, {
time: this.RecordTime
})
}
})
this.pageToBottom()
},
computed: {
...mapState({
chatList: state => state.user.chatList,
RECORD: state => state.audio.RECORD,
RecordTime: state => state.audio.RecordTime,
chat: state => state.user.chat,
totalNoreadnum: state => state.user.totalNoreadnum,
user: state => state.user.user
}),
// Current session configuration information
currentChatItem() {
let index = this.chatList.findIndex(item => item.id === this.detail.id && item.chat_type === this.detail
.chat_type)
if (index !== -1) {
return this.chatList[index]
}
return {}
},
// Get the mask position
maskBottom() {
return this.KeyboardHeight + uni.upx2px(105)
},
// Dynamically get menu height
getMenusHeight() {
let H = 100
return this.menusList.length * H
},
// Get the menu style
getMenusStyle() {
return `height: ${this.getMenusHeight}rpx;`
},
// Determine whether to operate your information
isdoSelf() {
// Get your id (assuming it is obtained)
let id = 1
let user_id = this.propIndex > -1 ? this.list[this.propIndex].user_id : 0
return user_id === id
},
// Chat area bottom
chatBodyBottom() {
return `bottom:${uni.upx2px(105) + this.KeyboardHeight}px;top:${this.navBarHeight}px;`
},
// Get the action or emoticon list
emoticonOrActionList() {
return (this.mode === 'emoticon' || this.mode === 'action') ? this[this.mode + 'List'] : []
},
// Picture address of all information
imageList() {
let arr = []
this.list.forEach((item) => {
if (item.type === 'emoticon' || item.type === 'image') {
arr.push(item.data)
}
})
return arr
}
},
watch: {
mode(newValue, oldValue) {
if (newValue !== 'action' && newValue !== 'emoticon') {
this.$refs.action.hide()
}
if (newValue !== 'text') {
uni.hideKeyboard()
}
}
},
onLoad(e) {
if (!e.params) {
return this.backToast()
}
this.detail = JSON.parse(decodeURIComponent(e.params))
console.log(this.detail);
// Initialization
this.__init()
// Create a chat object
this.chat.createChatObject(this.detail)
// Get history
this.list = this.chat.getChatDetail()
// Listen to receive chat information
uni.$on('onMessage', this.onMessage)
uni.$on('updateHistory', this.updateHistory)
// Listen to send collections and business cards
uni.$on('sendItem', this.onSendItem)
},
destroyed() {
// Destroy the chat object
this.chat.destoryChatObject()
// Destroy the monitor and receive chat messages
uni.$off('onMessage', this.onMessage)
uni.$off('updateHistory', this.updateHistory)
uni.$off('sendItem', this.onSendItem)
},
methods: {
...mapMutations(['regSendVoiceEvent']),
onSendItem(e) {
if (e.sendType === 'fava' || e.sendType === 'card') {
this.send(e.type, e.data, e.options)
}
},
updateHistory(isclear = true) {
if (isclear) {
this.list = []
} else {
this.list = this.chat.getChatDetail()
}
},
onMessage(message) {
console.log('[Chat Page] Listen to receive chat information', message);
if ((message.from_id === this.detail.id && message.chat_type === 'user') || (message.chat_type ===
'group' && message.to_id === this.detail.id)) {
if (message.isremove !== 1) {
this.list.push(message)
// Place at the bottom
return this.pageToBottom()
}
// Retract the message
let index = this.list.findIndex(item => item.id === message.id)
if (index !== -1) {
this.list[index].isremove = 1
}
}
},
__init() {
var total = 24;
var page = Math.ceil(total / 8);
var arr = [];
for (var i = 0; i < page; i++) {
var start = i * 8;
arr[i] = [];
for (var j = 0; j <= 8; j++) {
arr[i].push({
name: 'expression' + (start + j),
icon: '/static/images/emoticon/5497/' + (start + j) + '.gif',
event: 'sendEmoticon'
})
}
}
this.emoticonList = arr;
// var total = 20
// var page = (total/8)
// var arr = []
// for (var i = 0; i < page; i++) {
// var start = i*8
// arr[i] = []
// for (var j = 0; j < 8; j++) {
// var no = start + j
// if ((no+1) > total) {
// continue;
// }
// arr[i].push({
// name:"Expression"+no,
// icon: $ + no +'.gif',
// event:"sendEmoticon"
// })
// }
// }
// = arr
// Initialize the session list
this.chat.initChatListItem({
chat_type: this.detail.chat_type,
to_id: this.detail.id,
to_name: this.detail.name,
to_avatar: this.detail.avatar,
data: this.detail.chat_type === 'user' ? 'You are already friends, you can start chatting' : 'You have joined the group chat and can start chatting'
})
},
// Open the extended menu or emoticon package
openActionOrEmoticon(mode = 'action') {
this.mode = mode
this.$refs.action.show()
uni.hideKeyboard()
this.KeyboardHeight = uni.upx2px(580)
},
// send
send(type, data = '', options = {}) {
// Organize data format
switch (type) {
case 'text':
data = data || this.text
break;
}
let message = this.chat.formatSendData({
type,
data,
options
})
// Render to page
let index = this.list.length
this.list.push(message)
// Monitor the upload progress
let onProgress = false
if (message.type !== 'text' && message.type !== 'emoticon' && message.type !== 'card' && !message.data
.startsWith('http')) {
onProgress = (progress) => {
console.log('Upload progress:', progress);
}
}
// Send to the server
this.chat.send(message, onProgress).then(res => {
console.log(res);
// Send successfully
this.list[index].id = res.id
this.list[index].data = res.data;
this.list[index].sendStatus = 'success'
}).catch(err => {
// Send failed
this.list[index].sendStatus = 'fail'
console.log(err);
})
// Send the text successfully, clear the input box
if (type === 'text') {
this.text = ''
}
// Place at the bottom
this.pageToBottom()
},
// Back to the bottom
pageToBottom() {
// #ifdef APP-PLUS-NVUE
let chatItem = this.$refs.chatItem
let lastIndex = chatItem.length > 0 ? chatItem.length - 1 : 0
if (chatItem[lastIndex]) {
dom.scrollToElement(chatItem[lastIndex], {})
}
// #endif
// #ifndef APP-NVUE
setTimeout(() => {
let lastIndex = this.list.length - 1
this.scrollIntoView = 'chatItem_' + lastIndex
}, 300)
// #endif
},
// Long press the message bubble
long({
x,
y,
index
}) {
// Initialize index
this.propIndex = index
// Assembly menu
let menus = [{
name: "Send to a friend",
event: 'sendToChatItem'
}, {
name: "collect",
event: 'fava'
}, {
name: "delete",
event: 'delete'
}]
let item = this.list[this.propIndex]
let isSelf = this.user.id === item.from_id
if (isSelf) {
menus.push({
name: "withdraw",
event: 'removeChatItem'
})
}
// #ifndef H5
if (item.type === 'text') {
menus.unshift({
name: "copy",
event: 'copy',
})
}
// #endif
this.menusList = menus
// Show the extended menu
this.$refs.extend.show(x, y)
},
// Operation menu method distribution
clickEvent(event) {
let item = this.list[this.propIndex]
let isSelf = this.user.id === item.from_id
switch (event) {
case 'removeChatItem': // Retract the message
// Get the information currently being operated
this.chat.recall(item).then(res => {
item.isremove = 1
})
break;
case 'sendToChatItem':
uni.navigateTo({
url: '../chat-list/chat-list?params=' + encodeURIComponent(JSON.stringify(item)),
});
break;
case 'copy': // copy
uni.setClipboardData({
data: item.data,
success: () => {
uni.showToast({
title: 'Copy successfully',
icon: 'none'
});
}
});
break;
case 'delete':
uni.showModal({
content: 'Do you want to delete the record? ',
success: (res) => {
if (!res.confirm) return;
this.chat.deleteChatDetailItem(item, isSelf)
this.list.splice(this.propIndex, 1)
// Delete the last message
if (this.list.length === this.propIndex) {
this.chat.updateChatItem({
id: this.detail.id,
chat_type: this.detail.chat_type
}, (v) => {
let o = this.list[this.propIndex - 1]
let data = ''
if (o) {
data = this.chat.formatChatItemData(o, isSelf)
}
v.data = data
return v
})
}
}
});
break;
case 'fava': // Add to favorites
uni.showModal({
content: 'Do you want to add it to favorites? ',
success: (res) => {
if (res.confirm) {
$H.post('/fava/create', {
type: item.type,
data: item.data,
options: JSON.stringify(item.options)
}).then(res => {
uni.showToast({
title: 'Add to favorites successfully',
icon: 'none'
});
})
}
}
});
break;
}
// Close the menu
this.$refs.extend.hide()
},
// Extended menu
actionEvent(e) {
switch (e.event) {
case 'uploadImage': // Select photo album
uni.chooseImage({
count: 9,
success: (res) => {
// Send to the server
// Render to page
res.tempFilePaths.forEach((item) => {
this.send('image', item)
})
}
})
break;
case 'uploadVideo': // Send short videos
uni.chooseVideo({
maxDuration: 10,
success: (res) => {
this.send('video', res.tempFilePath)
// Render page
// Send to the server (get the video cover, return to the url)
// Modify the local sending status
}
})
break;
case 'sendEmoticon': // Send emoticon packets
this.send('emoticon', e.icon)
break;
case 'openFava': // Send to favorites
uni.navigateTo({
url: '../../my/fava/fava?type=send',
});
break;
case 'sendCard': // Send business card
uni.navigateTo({
url: '../../mail/mail/mail?type=sendCard&limit=1',
});
break;
}
},
// Click on the page
clickPage() {
this.mode = ''
},
// Preview the picture
previewImage(url) {
uni.previewImage({
current: url,
urls: this.imageList,
indicator: "default"
})
},
// Toggle audio recording and text input
changeVoiceOrText() {
this.mode = this.mode !== 'audio' ? 'audio' : 'text'
},
// Related recording
// Recording starts
voiceTouchStart(e) {
// Initialization
this.isRecording = true
this.RecordingStartY = e.changedTouches[0].screenY
this.unRecord = false
// Start recording
this.RECORD.start({
format: "mp3"
})
},
// The recording ends
voiceTouchEnd() {
this.isRecording = false
// Stop recording
this.RECORD.stop()
},
// The recording was interrupted
voiceTouchCancel() {
this.isRecording = false
this.unRecord = true
// Stop recording
this.RECORD.stop()
},
voiceTouchMove(e) {
let Y = Math.abs(e.changedTouches[0].screenY - this.RecordingStartY)
this.unRecord = (Y >= 50)
},
// Turn on chat information settings
openChatSet() {
uni.navigateTo({
url: '../chat-set/chat-set?params=' + JSON.stringify({
id: this.detail.id,
chat_type: this.detail.chat_type
}),
});
}
}
}
</script>
<style>
</style>
/pages/my/code/
<template>
<view class="page">
<!--Navigation bar-->
<free-nav-bar title="QR code business card" showBack :showRight="false"></free-nav-bar>
<view class="p-5">
<view class="bg-white rounded p-4">
<view class="flex align-center mb-4">
<free-avatar :src=" || '/static/images/demo/'"></free-avatar>
<view class="pl-4 flex flex-column">
<text class="font-md">{{detail.name}}</text>
<text class="font text-light-muted">area</text>
</view>
</view>
<view class="flex flex-column align-center justify-center">
<image :src="src" mode="" style="width: 550rpx;height: 550rpx;" class="bg-secondary mb-4"></image>
<text class="font text-light-muted">Scan the QR code above and add my WeChat</text>
</view>
</view>
</view>
</view>
</template>
<script>
import freeNavBar from '@/components/free-ui/';
import freeAvatar from '@/components/free-ui/';
import {mapState} from 'vuex';
import $C from '@/common/free-lib/';
export default {
components:{
freeNavBar,
freeAvatar
},
computed:{
...mapState({
user:state=>state.user.user
})
},
data() {
return {
detail:{
id:0,
name:"",
avatar:''
}
}
},
onLoad(e) {
if(e.params){
this.detail = JSON.parse(decodeURIComponent(e.params));
this.src = `${$C.codeUrl}/${e.type}_qrcode/${this.detail.id}?token=${this.user.token}`;
console.log(this.src);
}
},
methods: {
}
}
</script>
<style>
</style>
/
<script>
export default {
onLaunch: function() {
// #ifdef APP-PLUS-NVUE
// Load the public icon library
const domModule = weex.requireModule('dom')
domModule.addRule('fontFace', {
'fontFamily': "iconfont",
'src': "url('/static/font_1365296_2ijcbdrmsg.ttf')"
});
// #endif
// Initialize the recording manager
this.$store.commit('initRECORD');
// Initialize login status
this.$store.dispatch('initLogin');
console.log('App Launch')
},
onShow: function() {
this.$store.dispatch('reconnect');
console.log('App Show')
},
onHide: function() {
console.log('App Hide')
}
}
</script>
<style>
/*Public css per page */
@import "./common/";
@import "./common/";
/* #ifndef APP-PLUS-NVUE */
@import "./common/";
/* #endif */
/* #ifdef MP */
::-webkit-scrollbar{
display: none;
}
/* #endif */
</style>
/common/
/* 1. Page background color */
.page{
background-color: #EDEDED;
/* #ifndef APP-PLUS-NVUE */
min-height: 100vh;
height: auto;
/* #endif */
/* #ifdef APP-PLUS-NVUE */
flex: 1;
/* #endif */
}
/* 2. Main background color (forgive green) */
.main-bg-color{
background-color: #08C060;
}
.main-bg-hover-color{
background-color: #08D869;
}
/* 3. Main text color (forgive green) */
.main-text-color{
color: #08C060;
}
.border-main{
border-color:#08C060 !important;
}
.bg-chat-item{
background-color: #08C060;
}
.text-chat-item{
color: #08C060;
}
/pages/chat/group-user/
<template>
<view class="page">
<!--Navigation bar-->
<free-nav-bar title="choose" showBack :showRight="false">
</free-nav-bar>
<!--Search box-->
<view class="p-3 bg-light position-fixed left-0 right-0" :style="'top:'+top+'px;'">
<input type="text" value="" v-model="keyword" placeholder="search" class="bg-white rounded" placeholder-class="text-center" style="height: 80rpx;"/>
</view>
<view style="height:140rpx;"></view>
<!--Contact list-->
<view class="px-2 py-1 bg-light">
<text class="font-sm text-muted">{{keyword ? 'Search results' : 'Recent Contact'}}</text>
</view>
<free-list-item v-for="(item,index) in allList" :key="index" :title="" :cover=" || '/static/images/'" showRight :showRightIcon="false" @click="selectItem(item)">
<view v-if="muliSelect" slot="right" class="border rounded-circle flex align-center" style="width: 40rpx;height: 40rpx;" >
<view v-if="" class="main-bg-color rounded-circle" style="width: 39rpx;height: 39rpx;">
</view>
</view>
</free-list-item>
<view style="height:100rpx;" class="flex align-center justify-center" v-if="keyword !== '' && === 0">
<text class="font text-light-muted">No search results yet</text>
</view>
</view>
</template>
<script>
import freeNavBar from '@/components/free-ui/';
import freeMainButton from '@/components/free-ui/';
import freeListItem from '@/components/free-ui/';
import freeAvatar from '@/components/free-ui/';
import $H from '@/common/free-lib/';
export default {
components:{
freeNavBar,
freeMainButton,
freeListItem,
freeAvatar
},
data() {
return {
keyword:'',
muliSelect:false,
top:0,
list:[],
group_id:0,
}
},
computed:{
// Final list
allList(){
return this.keyword === '' ? this.list : this.searchList;
},
// Search result list
searchList(){
if(this.keyword === ''){
return [];
}
return this.list.filter(item=>{
return item.name.indexOf(this.keyword) !== -1;
})
},
// Select the list
selectList(){
return this.list.filter(item=>item.checked)
},
// Select quantity
selectCount(){
return this.selectList.length;
}
},
methods: {
update_data(e){
this.group_id = e.id;
$H.get('/group_info/'+e.id).then(res=>{
this.list = res.group_users.map(item=>{
return {
id:item.user_id,
name:item.nickname || item.user.nickname || item.user.username,
avatar:item.user.avatar
}
})
})
},
// Select, uncheck
selectItem(item){
uni.showModal({
content:'Do you want to kick out the member? ',
success:(res)=>{
if(res.confirm){
$H.post('/group/kickoff',{
id:this.group_id,
user_id:item.id
}).then(res=>{
console.log(res);
uni.showToast({
title:'Kicking out successful',
icon:'none'
});
uni.navigateBack({
delta:1
})
})
}
// Refresh the page
}
})
}
},
onLoad(e) {
let res = uni.getSystemInfoSync();
let statusBarHeight = 0;
// #ifndef MP
statusBarHeight = res.statusBarHeight;
// #endif
this.top = statusBarHeight + uni.upx2px(90);
if(e.id){
this.update_data(e);
}
}
}
</script>
<style>
</style>
/pages/mail/user-base/
<template>
<view class="page">
<!--Navigation bar-->
<free-nav-bar showBack :showRight="" bgColor="bg-white">
<view slot="right">
<free-icon-button v-if=""><text class="iconfont font-md"
@click="openAction"></text></free-icon-button>
</view>
</free-nav-bar>
<view class="px-3 py-4 flex align-center bg-white border-bottom">
<free-avatar :src="" size="120"></free-avatar>
<view class="flex flex-column ml-3 flex-1">
<view class="font-lg font-weight-bold flex justify-between">
<text class="font-lg font-weight-bold mb-1">{{detail.nickname}}</text>
<image v-if="" src="/static/images/" style="width: 40rpx;height: 40rpx;"></image>
</view>
<text class="font-md text-light-muted mb-1">account:{{detail.username}}</text>
<!-- <text class="font-md text-light-muted">Region: Guangzhou, Guangdong</text> -->
</view>
</view>
<free-list-item v-if="" showRight :showLeftIcon="false" @click="navigate(tagPath)">
<view class="flex align-center">
<text class="font-md text-dark mr-3">Label</text>
<text class="font-md text-light-muted mr-2" v-for="(item,index) in "
:key="index">{{item}}</text>
</view>
</free-list-item>
<free-divider></free-divider>
<free-list-item v-if="" showRight :showLeftIcon="false">
<view class="flex align-center">
<text class="font-md text-dark mr-3">Friends circle</text>
<image src="/static/images/demo/cate_01.png" style="width: 90rpx; height: 90rpx;" class=" mr-2"></image>
<image src="/static/images/demo/cate_01.png" style="width: 90rpx; height: 90rpx;" class=" mr-2"></image>
<image src="/static/images/demo/cate_01.png" style="width: 90rpx; height: 90rpx;" class=" mr-2"></image>
</view>
</free-list-item>
<free-list-item title="More Information" showRight :showLeftIcon="false"></free-list-item>
<free-divider></free-divider>
<view v-if="" class="py-3 flex align-center justify-center bg-white" hover-class="bg-light" @click="doEvent">
<text class="iconfont text-primary mr-1" v-if="!"></text>
<text class="font-md text-primary">{{detail.isblack ? 'Remove blacklist' : 'Send a message'}}</text>
</view>
<view v-else class="py-3 flex align-center justify-center bg-white" hover-class="bg-light"
@click="navigate(addFriend())">
<text class="font-md text-primary">Add friends</text>
</view>
<!--Extended menu-->
<free-popup ref="action" bottom transformOrigin="center bottom" maskColor>
<scroll-view style="height: 580rpx;" scroll-y="true" class="bg-white" :show-scrollbar="false">
<free-list-item v-for="(item,index) in actions" :key="index" :title="" :showRight="false"
:border="false" @click="popupEvent(item)">
<text slot="icon" class="iconfont font-lg py-1">{{item.icon}}</text>
</free-list-item>
</scroll-view>
</free-popup>
</view>
</template>
<script>
import freeNavBar from '@/components/free-ui/';
import freeIconButton from '@/components/free-ui/';
import freeChatItem from '@/components/free-ui/';
import freePopup from '@/components/free-ui/';
import freeListItem from '@/components/free-ui/';
import freeDivider from '@/components/free-ui/';
import freeAvatar from '@/components/free-ui/';
import auth from '@/common/mixin/';
import $H from '@/common/free-lib/';
export default {
mixins: [auth],
components: {
freeNavBar,
freeIconButton,
freeChatItem,
freePopup,
freeListItem,
freeDivider,
freeAvatar
},
data() {
return {
detail: {
id: 0,
username: '',
nickname: '',
avatar: '',
sex: '',
sign: '',
area: '',
friend: false,
lookhim: 1,
lookme: 1,
star: 0,
isblack: 0,
tags: []
},
}
},
onShow() {
this.getData();
},
onLoad(e) {
uni.$on('saveRemarkTag', (e) => {
this.detail.tagList = e.detail.tagList
this.nickname = e.nickname;
})
if (!e.user_id) {
return this.backToast();
}
this.detail.id = e.user_id;
// Get current user profile
this.getData();
},
beforeDestroy() {
this.$refs.action.hide();
uni.$off('saveRemarkTag')
},
computed: {
tagPath() {
return "mail/user-remark-tag/user-remark-tag?params="+JSON.stringify({
user_id:this.detail.id,
nickname:this.detail.nickname,
tags:this.detail.tags ? this.detail.tags.join(',') : ''
})
},
actions() {
return [{
icon: "\ue6b3",
title: "Set Notes and Tags",
type: "navigate",
path: "mail/user-remark-tag/user-remark-tag?params="+JSON.stringify({
user_id:this.detail.id,
nickname:this.detail.nickname,
tags:this.detail.tags ? this.detail.tags.join(',') : ''
})
}, {
icon: "\ue613",
title: "Recommend him to a friend",
type: "navigate",
path: "mail/send-card/send-card"
}, {
icon: "\ue6b0",
title: this.detail.star ? 'Cancel star friend' : "Set as a star friend",
type: "event",
event: "setStar"
}, {
icon: "\ue667",
title: "Set Moments and Dynamic Permissions",
type: "navigate",
path: "mail/user-moments-auth/user-moments-auth?user_token operator">+this.detail.id+"¶ms="+JSON.stringify({
lookme:this.detail.lookme,
lookhim:this.detail.lookhim,
})
}, {
icon: "\ue638",
title: this.detail.isblack ? 'Move out of the blacklist' : "Add to the blacklist",
type: "event",
event: "setBlack"
}, {
icon: "\ue61c",
title: "complaint",
type: "navigate",
path: "mail/user-report/user-report?params="+JSON.stringify({
user_id:this.detail.id,
type:'user'
})
}, {
icon: "\ue638",
title: "delete",
type: "event",
event: "deleteItem"
}]
}
},
methods: {
addFriend() {
let obj = {
friend_id: this.detail.id,
nickname: this.detail.nickname,
lookme: typeof this.detail.lookme === 'number' ? this.detail.lookme : 1,
lookhim: typeof this.detail.lookhim === 'number' ? this.detail.lookhim : 1,
};
return 'mail/add-friend/add-friend?params=' + JSON.stringify(obj);
},
getData() {
$H.get('/friend/read/' + this.detail.id).then(res => {
if (!res) {
return this.backToast('This user does not exist');
}
this.detail = res;
console.log(res);
});
},
openAction() {
this.$refs.action.show()
},
navigate(url) {
console.log(url)
uni.navigateTo({
url: '/pages/' + url,
});
},
// Operation menu event
popupEvent(e) {
if (!e.type) {
return;
}
setTimeout(() => {
// Close the pop-up layer
this.$refs.action.hide()
}, 300)
switch (e.type) {
case 'navigate':
this.navigate(e.path);
break;
case 'event':
this[e.event](e);
break;
}
},
// Delete friends
deleteItem(){
uni.showModal({
title: 'Do you want to delete a friend? ',
success: res => {
if(res.confirm){
$H.post('/friend/destroy',{friend_id:this.detail.id}).then(res=>{
uni.showToast({
title:'Delete friends successfully',
icon:'none'
});
uni.reLaunch({
url:'/pages/tabbar/index/index'
})
})
}
},
fail: () => {},
complete: () => {}
});
},
// Set as star
setStar(e) {
let star = this.detail.star == 0 ? 1 : 0;
$H.post('/friend/setstar/' + this.detail.id, {
star
}).then(res => {
this.detail.star = star;
e.title = this.detail.star ? 'Cancel star friend' : 'Set as a star friend';
});
},
// Add to blacklist
setBlack(e) {
let msg = this.detail.isblack ? 'Move out of the blacklist' : 'Add to the blacklist';
uni.showModal({
content: 'Do you want it?' + msg,
success: (res) => {
if (res.confirm) {
let isblack = this.detail.isblack == 0 ? 1:0
$H.post('/friend/setblack/' + this.detail.id, {
isblack
}).then(res => {
this.detail.isblack = isblack;
});
// = !;
// = ? 'Move out of blacklist' : 'Add to blacklist';
uni.showToast({
title: msg + 'success',
icon: 'none'
})
}
}
})
},
// Send a message
doEvent(e){
if(this.detail.isblack){
return this.setBlack();
}
uni.navigateTo({
url:'../../chat/chat/chat?params='+encodeURIComponent(JSON.stringify({
id:this.detail.id,
name:this.detail.nickname ? this.detail.nickname : this.detail.username,
avatar:this.detail.avatar,
chat_type:'user'
}))
})
}
}
}
</script>
<style>
</style>
/pages/tabbar/mail/
<template>
<view>
<!--Navigation bar-->
<free-nav-bar title="Address Book"></free-nav-bar>
<!--Contacts List-->
<scroll-view scroll-y="true" :style="'height:'+scrollHeight+'px;'" :scroll-into-view="scrollInto">
<free-list-item v-for="(item,index) in topList" :key="" :title="" :cover="" :showRight="==='friend'" @click="navigate()" :showRightIcon='false'>
<view slot="right">
<free-badge v-if="applyCount>0" :num="applyCount"></free-badge>
</view>
</free-list-item>
<view v-for="(item,index) in list" :key="index" v-if=">0" :id="'item-'+">
<view class="py-2 px-3 border-bottom bg-light">
<text class="font-md text-dark">{{item.title}}</text>
</view>
<free-list-item v-for="(item2,index2) in " :key="index2" :title="" :cover=" ? : '/static/images/'" @click="navigate('mail/user-base/user-base?user_id='+item2.user_id)"></free-list-item>
</view>
</scroll-view>
<!--Side navigation bar-->
<view class="position-fixed right-0 bottom-0 flex flex-column" :style="'top:'+top+'rpx;'" style="width: 50rpx;" @touchstart="touchstart" @touchmove="touchmove" @touchend="touchend" >
<view class="flex-1 flex align-center justify-center" v-for="(item,index) in list" :key="index">
<text class="font-sm text-muted">{{item.title}}</text>
</view>
</view>
<!-- <block v-if="current"> -->
<view class="position-fixed rounded-circle bg-light border flex align-center justify-center" style="width: 150rpx;height: 150rpx; left: 300rpx;" :style="'top:'+modalTop+'px;'" v-if="current!=''">
<text class="font-lg" >{{current}}</text>
</view>
<!-- </block> -->
</view>
</template>
<script>
import freeNavBar from "@/components/free-ui/";
import freeListItem from '@/components/free-ui/';
import freeBadge from '@/components/free-ui/';
import { mapState } from 'vuex';
import auth from '@/common/mixin/';
export default {
mixins:[auth],
components:{
freeNavBar,
freeListItem,
freeBadge
},
computed:{
...mapState({
applyCount:state=>state.user.apply.count,
list:state=>state.user.mailList
}),//
modalTop(){
return (this.scrollHeight-uni.upx2px(150))/2;
},
itemHeight(){
let count = this.list.length;
if(count<1){
return 0;
}
return this.scrollHeight/count;
}
},
onLoad() {
console.log('mail/mail')
let res = uni.getSystemInfoSync();
this.top = res.statusBarHeight + uni.upx2px(90);
this.scrollHeight = res.windowHeight-this.top;
this.$store.dispatch('getMailList');
},
data() {
return {
scrollInto:'',
top:0,
scrollHeight:0,
current:'',
topList:[
{
id:'friend',
title:"New Friend",
cover:"/static/images/mail/",
path:"mail/apply-list/apply-list"
},
{
id:'group',
title:"Group Chat",
cover:"/static/images/mail/",
path:"mail/group-list/group-list"
},
{
id:'tag',
title:"Label",
cover:"/static/images/mail/",
path:"mail/tag-list/tag-list"
}
]
}
},
methods: {
touchstart(e){
this.changeScrollInto(e);
},
touchmove(e){
this.changeScrollInto(e);
},
touchend(){
this.current = '';
},
// Linkage
changeScrollInto(e){
// let Y = [0].pageY;
// let index = (Y / );
// let item = [index];
// if(item){
// = 'item-'+;
// = ;
// }
let Y = e.touches[0].pageY
// #ifdef MP
Y = Y - this.top
// #endif
let index = Math.floor(Y / this.itemHeight)
let item = this.list[index]
if(item){
this.scrollInto = 'item-'+item.letter
this.current = item.letter
}
}
}
}
</script>
<style>
</style>
/store/modules/
import $U from '@/common/free-lib/';
import $H from '@/common/free-lib/';
import Chat from '@/common/free-lib/';
import $C from '@/common/free-lib/';
export default {
state: {
user: false,
apply: {
rows: [],
count: 0,
},
mailList: [],
chat: null,
// Session list
chatList: [],
// Total unread
totalNoreadnum: 0,
notice: {
avatar: '',
user_id: 0,
num: 0
}
},
mutations: {
updateUser(state, {
k,
v
}) {
if (state.user) {
state.user[k] = v;
$U.setStorage('user', JSON.stringify(state.user));
}
}
},
actions: {
// After login processing
login({
state,
dispatch
}, user) {
// Save to state type
state.user = user;
//Storage to local storage
$U.setStorage('token', user.token);
$U.setStorage('user', JSON.stringify(user));
$U.setStorage('user_id', user.id);
// Get a friend application list
dispatch('getApply');
// Update corner mark prompts
dispatch('updateMailBadge');
// Connect to socket
state.chat = new Chat({
url: $C.socketUrl
})
// Get the session list
dispatch('getChatList');
// Initialize the total unread angle mark
dispatch('updateBadge');
// Get notifications on Moments
dispatch('getNotice');
},
// Log out
logout({
state
}) {
// Clear login status
state.user = false;
// Clear local stored data
$U.removeStorage('token');
$U.removeStorage('user');
$U.removeStorage('user_id');
// Close the socket connection
if(state.chat){
state.chat.close();
state.chat = null;
}
// Jump to login page
uni.reLaunch({
url: '/pages/common/login/login'
})
// Log out the listening event
uni.$off('onUpdateChatList')
uni.$off('momentNotice')
uni.$off('totalNoreadnum')
},
// Initialize login status
initLogin({
state,
dispatch
}) {
// Get the stored data
let user = $U.getStorage('user');
if (user) {
// Initialize login status
state.user = JSON.parse(user);
// Connect to socket
state.chat = new Chat({
url: $C.socketUrl
})
// Get the session list
dispatch('getChatList');
// Get offline information
// Get a friend application list
dispatch('getApply');
// Initialize the total unread angle mark
dispatch('updateBadge');
// Get notifications on Moments
dispatch('getNotice');
}
},
// Get a friend application list
getApply({
state,
dispatch
}, page = 1) {
$H.get('/apply/' + page).then(res => {
if (page === 1) {
state.apply = res
} else {
// Pull down to refresh
state.apply.rows = [...state.apply.rows, ...res.rows]
state.apply.count = res.count
}
// Update address book corner mark prompts
dispatch('updateMailBadge');
});
},
// Update address book corner mark prompts
updateMailBadge({
state
}) {
let count = state.apply.count > 99 ? '99+' : state.apply.count.toString();
console.log(state.apply.count);
if (state.apply.count > 0) {
return uni.setTabBarBadge({
index: 1,
text: count
})
}
uni.removeTabBarBadge({
index: 1
})
},
// Get the address book list
getMailList({
state
}) {
$H.get('/friend/list').then(res => {
state.mailList = res.rows.newList ? res.rows.newList : [];
})
},
// Get the session list
getChatList({
state
}) {
state.chatList = state.chat.getChatList()
// Listen to the session list changes
uni.$on('onUpdateChatList', (list) => {
state.chatList = list
})
},
// Get notifications on Moments
getNotice({
state
}) {
state.notice = state.chat.getNotice();
if (state.notice.num > 0) {
uni.setTabBarBadge({
index: 2,
text: state.notice.num > 99 ? '99+' : state.notice.num.toString()
})
} else {
uni.removeTabBarBadge({
index: 2
})
}
uni.$on('momentNotice', (notice) => {
state.notice = notice
})
},
// Initialize the total unread angle mark
// Update unread
async updateBadge(list = false) {
// Turn on the total unread changes in the monitor
uni.$on('totalNoreadnum', (num) => {
state.totalNoreadnum = num
})
state.chat.updateBadge()
},
// Initialize the total unread angle mark
updateBadge({
state
}) {
// Turn on the total unread changes in the monitor
uni.$on('totalNoreadnum', (num) => {
console.log('totalNoreadnum:', num);
state.totalNoreadnum = num
})
state.chat.updateBadge()
},
// Automatic reconnection after disconnection
reconnect({state}){
if(state.user && state.chat){
state.chat.reconnect()
}
}
},
}
Thank you for watching, see you next time