uni-app 173小程序端兼容处理

简介: uni-app 173小程序端兼容处理

/common/free-icon.css

/* @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,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;
}
/* @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;
} */

/pages/common/search/search.vue

<template>
  <view class="page">
    <!-- 导航栏 -->
    <free-nav-bar title="我的收藏" showBack :showRight="false">
      <input type="text" v-model="keyword" placeholder="请输入关键字" style="width: 650rpx;" class="font-md" @confirm="confirm"/>
    </free-nav-bar>
    
    <block v-if="searchType==''&&list.length===0">
    <view class="py-3 flex align-center justify-center">
      <text class="font text-light-muted">搜索指定内容</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="item.nickname ? item.nickname : item.username" :cover="item.avatar ? item.avatar : '/static/images/userpic.png'" @click="open(item.id)"></free-list-item>
  </view>
</template>
<script>
  import freeNavBar from '@/components/free-ui/free-nav-bar.vue';
  import freeListItem from '@/components/free-ui/free-list-item.vue';
  import $H from '@/common/free-lib/request.js';
  export default {
    components:{
      freeNavBar,
      freeListItem
    },
    data() {
      return {
        typeList:[{
          name:'聊天记录',
          key:'history'
        },
        {
          name:'用户',
          key:'user'
        },
        {
          name:'群聊',
          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(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/chat-history.vue

<template>
  <view class="page">
    <!-- 导航栏 -->
    <free-nav-bar title="聊天记录" showBack :showRight="false">
    </free-nav-bar>
    <!-- 搜索框 -->
    <view class="p-3 bg-light position-fixed left-0 right-0" :style="'top:'+top+'px;'">
      <input type="text" value="" v-model="keyword" placeholder="搜索" class="bg-white rounded" placeholder-class="text-center" style="height: 80rpx;"/>
    </view>
    <view style="height:140rpx;"></view>
    <!-- 联系人列表 -->
    <view class="px-2 py-1 bg-light">
      <text class="font-sm text-muted">{{keyword ? '搜索结果' : '最近联系人'}}</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 !== '' && searchList.length === 0">
      <text class="font text-light-muted">暂无搜索结果</text>
    </view>
    
  </view>
</template>
<script>
  import freeNavBar from '@/components/free-ui/free-nav-bar.vue';
  import freeMainButton from '@/components/free-ui/free-main-button.vue';
  import freeChatItem from '@/components/free-ui/free-chat-item.vue';
  import freeAvatar from '@/components/free-ui/free-avatar.vue';
  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
      }),
      // 最终列表
      allList(){
        return this.keyword === '' ? this.list : this.searchList;
      },
      // 搜索结果列表
      searchList(){
        if(this.keyword === ''){
          return [];
        }
        return this.list.filter(item=>{
          return item.data.indexOf(this.keyword) !== -1;
        })
      },
      // 选中列表
      selectList(){
        return this.list.filter(item=>item.checked)
      },
      // 选中数量
      selectCount(){
        return this.selectList.length;
      }
    },
    methods: {
      // 点击导航栏
      handlenNav(){
        if(!this.muliSelect){
          return this.muliSelect = true;
        }
        // 发送
        console.log('发送')
      },
      // 选中、取消选中
      selectItem(item){
        // 选中、取消选中
        if(this.muliSelect){
          // 选中
          if(!item.checked && (this.selectCount === 9)){
            // 限制选中数量
            return uni.showToast({
                title:'最多选中9个',
                icon:'none'
            })
          }
          // 取消选中
          return item.checked = !item.checked;
        }
        // 发送
        this.$refs.confirm.show((close)=>{
          console.log('点击了确定');
          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/group-remark.vue

<template>
  <view class="page">
    <!-- 导航栏 -->
    <free-nav-bar title="群公告" showBack :showRight="true" bgColor="bg-white">
      <free-main-button name="推送" slot="right" @click="submit"></free-main-button>
    </free-nav-bar>
  
    <textarea v-model="remark" placeholder="暂无公告..." class="bg-white p-2 font-md" style="width:750rpx;" />
  </view>
</template>
<script>
  import freeNavBar from '@/components/free-ui/free-nav-bar.vue';
  import freeMainButton from '@/components/free-ui/free-main-button.vue';
  import $H from '@/common/free-lib/request.js';
  import auth from '@/common/mixin/auth.js';
  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:'推送成功',
            icon:'none'
          });
          uni.navigateBack({
            delta:res
          });
        })
      }
    }
  }
</script>
<style>
</style>

/pages/mail/apply-list/apply-list.vue

<template>
  <view class="page">
    <!-- 导航栏 -->
    <free-nav-bar title="好友申请列表" showBack :showRight="false">
    </free-nav-bar>
    
    <free-list-item v-for="(item,index) in applyList" :key="index" :title="item.user.nickname ? item.user.nickname : item.user.username" :cover="item.user.avatar ? item.user.avatar : '/static/images/userpic.png'" :showRight="true" :showRightIcon="false">
      <view slot="right">
        <free-main-button  v-if="item.status==='pending'" name="同意" @click='handle(item)'></free-main-button>
        <text v-else class="text-muted font-sm">{{item|formatTitle}}</text>
      </view>
      
    </free-list-item>
    <!-- 上拉加载 -->
    <view class="flex align-center justify-center py-4 bg-light" v-if="applyList.length >= 10">
      <text class="text-muted font">{{loadmore}}</text>
    </view>
  </view>
</template>
<script>
  import freeMainButton from '@/components/free-ui/free-main-button.vue';
  import freeNavBar from '@/components/free-ui/free-nav-bar.vue';
  import freeListItem from '@/components/free-ui/free-list-item.vue';
  import freeDivider from '@/components/free-ui/free-divider.vue';
  import $H from '@/common/free-lib/request.js';
  import auth from '@/common/mixin/auth.js';
  import { mapState } from 'vuex';
  export default {
    mixins:[auth],
    components: {
      freeNavBar,
      freeMainButton,
      freeListItem,
      freeDivider
    },
    filters:{
      formatTitle(value){
        let obj = {
          agree:'已通过',
          refuse:'已拒绝',
          ignore:'已忽略'
        }
        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:'上拉加载更多',// 没有更多了,加载中...
      }
    },
    // 监听下拉刷新
    onPullDownRefresh() {
      this.page = 1;
      this.$store.dispatch('getApply',this.page).then(res=>{
        uni.showToast({
          title:'刷新成功',
          icon:'none'
        })
        uni.stopPullDownRefresh()
      })
    },
    onLoad(e) {
      
    },
    // 监听触底事件
    onReachBottom() {
      if(this.loadmore !== '上拉加载更多'){
        return;
      }
      this.loadmore = '加载中...';
      this.page = this.page+1;
      this.$store.dispatch('getApply',this.page).then(res=>{
        console.log(res)
        
        this.loadmore = this.applyList.length == this.page*10 ? '上拉加载更多' : '没有更多了';
      }).catch(err=>{
        this.page = this.page-1;
        this.loadmore = '上拉加载更多';
      })
    },
    onShow() {
      if(this.loadmore !== '上拉加载更多'){
        return;
      }
      this.loadmore = '加载中...';
      this.page = this.page+1;
      this.$store.dispatch('getApply',this.page).then(res=>{
        console.log(res)
        
        this.loadmore = this.applyList.length == this.page*10 ? '上拉加载更多' : '没有更多了';
      }).catch(err=>{
        this.page = this.page-1;
        this.loadmore = '上拉加载更多';
      })
    },
    methods: {
      handle(item){
        uni.navigateTo({
          url:'../add-friend/add-friend?id='+item.id,
        })
      }
    }
  }
</script>
<style>
</style>

/components/free-ui/free-nav-bar.vue

<template>
  <view>
    <view :class="getClass">
      <!-- 状态栏 -->
      <view :style="'height:'+statusBarHeight+'px'"></view>
      <!-- 导航 -->
      <view class="w-100 flex align-center justify-between" style="height: 90rpx;">
        <!-- 左边 -->
        <view class="flex align-center">
          <!-- 返回按钮 -->
          <!-- #ifndef MP -->
          <free-icon-button v-if="showBack" @click="back"><text class="iconfont font-md">&#xe60d;</text></free-icon-button>
          <!-- #endif -->
          <!-- 标题 -->
          <slot>
            <text v-if="title" class="font-md ml-3">{{getTitle}}</text>
          </slot>
        </view>
        <!-- 右边 -->
        <view class="flex align-center" v-if="showRight">
          <slot name="right">
            <free-icon-button @click="search"><text class="iconfont font-md">&#xe6e3;</text></free-icon-button>
            <free-icon-button @click="openExtend"><text class="iconfont font-md">&#xe682;</text></free-icon-button>
          </slot>
        </view>
      </view>
    </view>
    <!-- 占位 -->
    <view v-if="fixed" :style="fixedStyle"></view>
    
    <!-- 扩展菜单 -->
    <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 "./free-icon-button.vue"
  import freePopup from "./free-popup.vue"
  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:"发起群聊",
            event:"navigateTo",
            path:"/pages/mail/mail/mail?type=createGroup",
            icon:"\ue633"
          },
          {
            name:"添加好友",
            event:"navigateTo",
            path:"/pages/common/search/search",
            icon:"\ue65d"
          },
          // #ifndef H5
          {
            name:"扫一扫",
            event:"",
            icon:"\ue614"
          },
          // #endif
          {
            name:"收付款",
            event:"",
            icon:"\ue66c"
          },
          {
            name:"帮助与反馈",
            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))
      },
      // 返回
      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: '靓仔,自己发挥',
            icon: 'none'
          });
            break;
        }
      }
    },
  }
</script>
<style>
</style>

pages/chat/chat/chat.vue

<template>
  <view>
    <!-- 导航栏 -->
    <free-nav-bar :title="detail.name" :noreadnum="totalNoreadnum" showBack>
      <free-icon-button slot="right" @click="openChatSet"><text class="iconfont font-lg">&#xe6fd;</text>
      </free-icon-button>
    </free-nav-bar>
    <!-- 聊天内容区域 -->
    <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">
      <!-- 聊天信息列表组件 -->
      <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="currentChatItem.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 -->
    <!-- 底部输入框 -->
    <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">&#xe607;</text></free-icon-button>
      <free-icon-button v-else @click="changeVoiceOrText"><text class="iconfont font-lg">&#xe606;</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 ? '松开 结束':'按住 说话'}}</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>
      <!-- 表情 -->
      <free-icon-button @click="openActionOrEmoticon('emoticon')"><text class="iconfont font-lg">&#xe605;</text>
      </free-icon-button>
      <template v-if="text.length === 0">
        <!-- 扩展菜单 -->
        <free-icon-button @click="openActionOrEmoticon('action')"><text class="iconfont font-lg">&#xe603;</text>
        </free-icon-button>
      </template>
      <view v-else class="flex-shrink">
        <!-- 发送按钮 -->
        <free-main-button name="发送" @click="send('text')"></free-main-button>
      </view>
    </view>
    <!-- 扩展菜单 -->
    <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="emoticonOrActionList.length > 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="item2.icon" 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>
    <!-- 弹出层 -->
    <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(item.event)">
          <text class="font-md pl-3">{{item.name}}</text>
        </view>
      </view>
    </free-popup>
    <!-- 录音提示 -->
    <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/recording.gif" style="width: 150rpx;height: 150rpx;"></image>
        <text class="font text-white mt-3">{{unRecord ? '松开手指,取消发送':'手指上滑,取消发送'}}</text>
      </view>
    </view>
  </view>
</template>
<script>
  // #ifdef APP-PLUS-NVUE
  const dom = weex.requireModule('dom')
  // #endif
  import freeNavBar from "@/components/free-ui/free-nav-bar.vue"
  import freeIconButton from "@/components/free-ui/free-icon-button.vue"
  import freeChatItem from '@/components/free-ui/free-chat-item.vue';
  import freePopup from "@/components/free-ui/free-popup.vue"
  import freeMainButton from '@/components/free-ui/free-main-button.vue';
  import {
    mapState,
    mapMutations
  } from 'vuex'
  import auth from '@/common/mixin/auth.js';
  import $U from '@/common/free-lib/util.js';
  import $H from '@/common/free-lib/request.js';
  import $C from '@/common/free-lib/config.js';
  export default {
    mixins: [auth],
    components: {
      freeNavBar,
      freeIconButton,
      freeChatItem,
      freePopup,
      freeMainButton
    },
    data() {
      return {
        scrollIntoView: "",
        // 模式 text输入文字,emoticon表情,action操作,audio音频
        mode: "text",
        // 扩展菜单列表
        actionList: [
          [{
            name: "相册",
            icon: "/static/images/extends/pic.png",
            event: "uploadImage"
          }, {
            name: "拍摄",
            icon: "/static/images/extends/video.png",
            event: "uploadVideo"
          }, {
            name: "收藏",
            icon: "/static/images/extends/shoucan.png",
            event: "openFava"
          }, {
            name: "名片",
            icon: "/static/images/extends/man.png",
            event: "sendCard"
          }, {
            name: "语音通话",
            icon: "/static/images/extends/phone.png",
            event: ""
          }, {
            name: "位置",
            icon: "/static/images/extends/path.png",
            event: ""
          }]
        ],
        emoticonList: [],
        // 键盘高度
        KeyboardHeight: 0,
        menusList: [],
        navBarHeight: 0,
        list: [],
        // 当前操作的气泡索引
        propIndex: -1,
        // 输入文字
        text: "",
        // 音频录制状态
        isRecording: false,
        RecordingStartY: 0,
        // 取消录音
        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)
      // 监听键盘高度变化
      uni.onKeyboardHeightChange(res => {
        if (this.mode !== 'action' && this.mode !== 'emoticon') {
          this.KeyboardHeight = res.height
        }
        if (this.KeyboardHeight > 0) {
          this.pageToBottom()
        }
      })
      // 注册发送音频事件
      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
      }),
      // 当前会话配置信息
      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 {}
      },
      // 获取蒙版的位置
      maskBottom() {
        return this.KeyboardHeight + uni.upx2px(105)
      },
      // 动态获取菜单高度
      getMenusHeight() {
        let H = 100
        return this.menusList.length * H
      },
      // 获取菜单的样式
      getMenusStyle() {
        return `height: ${this.getMenusHeight}rpx;`
      },
      // 判断是否操作本人信息
      isdoSelf() {
        // 获取本人id(假设拿到了)
        let id = 1
        let user_id = this.propIndex > -1 ? this.list[this.propIndex].user_id : 0
        return user_id === id
      },
      // 聊天区域bottom
      chatBodyBottom() {
        return `bottom:${uni.upx2px(105) + this.KeyboardHeight}px;top:${this.navBarHeight}px;`
      },
      // 获取操作或者表情列表
      emoticonOrActionList() {
        return (this.mode === 'emoticon' || this.mode === 'action') ? this[this.mode + 'List'] : []
      },
      // 所有信息的图片地址
      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);
      // 初始化
      this.__init()
      // 创建聊天对象
      this.chat.createChatObject(this.detail)
      // 获取历史记录
      this.list = this.chat.getChatDetail()
      // 监听接收聊天信息
      uni.$on('onMessage', this.onMessage)
      uni.$on('updateHistory', this.updateHistory)
      // 监听发送收藏和名片
      uni.$on('sendItem', this.onSendItem)
    },
    destroyed() {
      // 销毁聊天对象
      this.chat.destoryChatObject()
      // 销毁监听接收聊天消息
      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('[聊天页] 监听接收聊天信息', 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)
            // 置于底部
            return this.pageToBottom()
          }
          // 撤回消息
          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: '表情' + (start + j),
              icon: '/static/images/emoticon/5497/' + (start + j) + '.gif',
              event: 'sendEmoticon'
            })
          }
        }
        this.emoticonList = arr;
        // var total = 20
        // 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++) {
        //    var no = start + j
        //    if ((no+1) > total) {
        //      continue;
        //    }
        //    arr[i].push({
        //      name:"表情"+no,
        //      icon: $C.emoticonUrl + no +'.gif',
        //      event:"sendEmoticon"
        //    })
        //  }
        // }
        // this.emoticonList = arr
        // 初始化会话列表
        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' ? '你们已经是好友,可以开始聊天了' : '你已经加入群聊,可以开始聊天了'
        })
      },
      // 打开扩展菜单或者表情包
      openActionOrEmoticon(mode = 'action') {
        this.mode = mode
        this.$refs.action.show()
        uni.hideKeyboard()
        this.KeyboardHeight = uni.upx2px(580)
      },
      // 发送
      send(type, data = '', options = {}) {
        // 组织数据格式
        switch (type) {
          case 'text':
            data = data || this.text
            break;
        }
        let message = this.chat.formatSendData({
          type,
          data,
          options
        })
        // 渲染到页面
        let index = this.list.length
        this.list.push(message)
        // 监听上传进度
        let onProgress = false
        if (message.type !== 'text' && message.type !== 'emoticon' && message.type !== 'card' && !message.data
          .startsWith('http')) {
          onProgress = (progress) => {
            console.log('上传进度:', progress);
          }
        }
        // 发送到服务端
        this.chat.send(message, onProgress).then(res => {
          console.log(res);
          // 发送成功
          this.list[index].id = res.id
          this.list[index].data = res.data;
          this.list[index].sendStatus = 'success'
        }).catch(err => {
          // 发送失败
          this.list[index].sendStatus = 'fail'
          console.log(err);
        })
        // 发送文字成功,清空输入框
        if (type === 'text') {
          this.text = ''
        }
        // 置于底部
        this.pageToBottom()
      },
      // 回到底部
      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({
        x,
        y,
        index
      }) {
        // 初始化 索引
        this.propIndex = index
        // 组装菜单
        let menus = [{
          name: "发送给朋友",
          event: 'sendToChatItem'
        }, {
          name: "收藏",
          event: 'fava'
        }, {
          name: "删除",
          event: 'delete'
        }]
        let item = this.list[this.propIndex]
        let isSelf = this.user.id === item.from_id
        if (isSelf) {
          menus.push({
            name: "撤回",
            event: 'removeChatItem'
          })
        }
        // #ifndef H5
        if (item.type === 'text') {
          menus.unshift({
            name: "复制",
            event: 'copy',
          })
        }
        // #endif
        this.menusList = menus
        // 显示扩展菜单
        this.$refs.extend.show(x, y)
      },
      // 操作菜单方法分发
      clickEvent(event) {
        let item = this.list[this.propIndex]
        let isSelf = this.user.id === item.from_id
        switch (event) {
          case 'removeChatItem': // 撤回消息
            // 拿到当前被操作的信息
            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': // 复制
            uni.setClipboardData({
              data: item.data,
              success: () => {
                uni.showToast({
                  title: '复制成功',
                  icon: 'none'
                });
              }
            });
            break;
          case 'delete':
            uni.showModal({
              content: '是否要删除该记录?',
              success: (res) => {
                if (!res.confirm) return;
                this.chat.deleteChatDetailItem(item, isSelf)
                this.list.splice(this.propIndex, 1)
                // 删除最后一条消息
                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': // 加入收藏
            uni.showModal({
              content: '是否要加入收藏?',
              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: '加入收藏成功',
                      icon: 'none'
                    });
                  })
                }
              }
            });
            break;
        }
        // 关闭菜单
        this.$refs.extend.hide()
      },
      // 扩展菜单
      actionEvent(e) {
        switch (e.event) {
          case 'uploadImage': // 选择相册
            uni.chooseImage({
              count: 9,
              success: (res) => {
                // 发送到服务器
                // 渲染到页面
                res.tempFilePaths.forEach((item) => {
                  this.send('image', item)
                })
              }
            })
            break;
          case 'uploadVideo': // 发送短视频
            uni.chooseVideo({
              maxDuration: 10,
              success: (res) => {
                this.send('video', res.tempFilePath)
                // 渲染页面
                // 发送到服务端(获取视频封面,返回url)
                // 修改本地的发送状态
              }
            })
            break;
          case 'sendEmoticon': // 发送表情包
            this.send('emoticon', e.icon)
            break;
          case 'openFava': // 发送收藏
            uni.navigateTo({
              url: '../../my/fava/fava?type=send',
            });
            break;
          case 'sendCard': // 发送名片
            uni.navigateTo({
              url: '../../mail/mail/mail?type=sendCard&limit=1',
            });
            break;
        }
      },
      // 点击页面
      clickPage() {
        this.mode = ''
      },
      // 预览图片
      previewImage(url) {
        uni.previewImage({
          current: url,
          urls: this.imageList,
          indicator: "default"
        })
      },
      // 切换音频录制和文本输入
      changeVoiceOrText() {
        this.mode = this.mode !== 'audio' ? 'audio' : 'text'
      },
      // 录音相关
      // 录音开始
      voiceTouchStart(e) {
        // 初始化
        this.isRecording = true
        this.RecordingStartY = e.changedTouches[0].screenY
        this.unRecord = false
        // 开始录音
        this.RECORD.start({
          format: "mp3"
        })
      },
      // 录音结束
      voiceTouchEnd() {
        this.isRecording = false
        // 停止录音
        this.RECORD.stop()
      },
      // 录音被打断
      voiceTouchCancel() {
        this.isRecording = false
        this.unRecord = true
        // 停止录音
        this.RECORD.stop()
      },
      voiceTouchMove(e) {
        let Y = Math.abs(e.changedTouches[0].screenY - this.RecordingStartY)
        this.unRecord = (Y >= 50)
      },
      // 打开聊天信息设置
      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/code.vue

<template>
  <view class="page">
    <!-- 导航栏 -->
    <free-nav-bar title="二维码名片" 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="detail.avatar || '/static/images/demo/demo6.jpg'"></free-avatar>
          <view class="pl-4 flex flex-column">
            <text class="font-md">{{detail.name}}</text>
            <text class="font text-light-muted">地区</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">扫一扫上面的二维码,加我的微信</text>
        </view>
      </view>
    </view>
  </view>
</template>
<script>
  import freeNavBar from '@/components/free-ui/free-nav-bar.vue';
  import freeAvatar from '@/components/free-ui/free-avatar.vue';
  import {mapState} from 'vuex';
  import $C from '@/common/free-lib/config.js';
  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>

/App.vue

<script>
  export default {
    onLaunch: function() {
      // #ifdef APP-PLUS-NVUE
      // 加载公共图标库
      const domModule = weex.requireModule('dom')
      domModule.addRule('fontFace', {
          'fontFamily': "iconfont",
          'src': "url('/static/font_1365296_2ijcbdrmsg.ttf')"
      });
      // #endif
      // 初始化录音管理器
      this.$store.commit('initRECORD');
      // 初始化登录状态
      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>
  /*每个页面公共css */
  @import "./common/free.css";
    @import "./common/common.css";
  /* #ifndef APP-PLUS-NVUE */
  @import  "./common/free-icon.css";
  /* #endif */
  /* #ifdef MP */
  ::-webkit-scrollbar{
    display: none;
  }
  /* #endif */
</style>

/common/common.css

/* 1.页面背景色 */
.page{
  background-color: #EDEDED;
  /* #ifndef APP-PLUS-NVUE */
  min-height: 100vh;
  height: auto;
  /* #endif */
  /* #ifdef APP-PLUS-NVUE */
  flex: 1;
  /* #endif */
}
/* 2.主背景色(原谅绿) */
.main-bg-color{
  background-color: #08C060;
}
.main-bg-hover-color{
  background-color: #08D869;
}
/* 3.主文字色(原谅绿) */
.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/group-user.vue

<template>
  <view class="page">
    <!-- 导航栏 -->
    <free-nav-bar title="选择" showBack :showRight="false">
      
    </free-nav-bar>
    <!-- 搜索框 -->
    <view class="p-3 bg-light position-fixed left-0 right-0" :style="'top:'+top+'px;'">
      <input type="text" value="" v-model="keyword" placeholder="搜索" class="bg-white rounded" placeholder-class="text-center" style="height: 80rpx;"/>
    </view>
    <view style="height:140rpx;"></view>
    <!-- 联系人列表 -->
    
    <view class="px-2 py-1 bg-light">
      <text class="font-sm text-muted">{{keyword ? '搜索结果' : '最近联系人'}}</text>
    </view>
    
    
    <free-list-item v-for="(item,index) in allList" :key="index" :title="item.name" :cover="item.avatar || '/static/images/userpic.png'" 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="item.checked" 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 !== '' && searchList.length === 0">
      <text class="font text-light-muted">暂无搜索结果</text>
    </view>
    
  </view>
</template>
<script>
  import freeNavBar from '@/components/free-ui/free-nav-bar.vue';
  import freeMainButton from '@/components/free-ui/free-main-button.vue';
  import freeListItem from '@/components/free-ui/free-list-item.vue';
  
  import freeAvatar from '@/components/free-ui/free-avatar.vue';
  import $H from '@/common/free-lib/request.js';
  export default {
    components:{
      freeNavBar,
      freeMainButton,
      freeListItem,
      freeAvatar
    },
    data() {
      return {
        keyword:'',
        muliSelect:false,
        top:0,
        list:[],
        group_id:0,
      }
    },
    computed:{
      // 最终列表
      allList(){
        return this.keyword === '' ? this.list : this.searchList;
      },
      // 搜索结果列表
      searchList(){
        if(this.keyword === ''){
          return [];
        }
        return this.list.filter(item=>{
          return item.name.indexOf(this.keyword) !== -1;
        })
      },
      // 选中列表
      selectList(){
        return this.list.filter(item=>item.checked)
      },
      // 选中数量
      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
            }
          })
          
        })
      },
      // 选中、取消选中
      selectItem(item){
        uni.showModal({
          content:'是否要踢出该成员?',
          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:'踢出成功',
                  icon:'none'
                });
                uni.navigateBack({
                  delta:1
                })
              })
            }
            // 刷新页面
          }
        })
      }
    },
    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/user-base.vue

<template>
  <view class="page">
    <!-- 导航栏 -->
    <free-nav-bar showBack :showRight="detail.friend" bgColor="bg-white">
      <view slot="right">
        <free-icon-button  v-if="detail.friend"><text class="iconfont font-md"
            @click="openAction">&#xe6fd;</text></free-icon-button>
      </view>
      
    </free-nav-bar>
    <view class="px-3 py-4 flex align-center bg-white border-bottom">
      <free-avatar :src="detail.avatar" 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="detail.star" src="/static/images/star.png" style="width: 40rpx;height: 40rpx;"></image>
        </view>
        <text class="font-md text-light-muted mb-1">账号:{{detail.username}}</text>
        <!-- <text class="font-md text-light-muted">地区:广东广州</text> -->
      </view>
    </view>
    <free-list-item v-if="detail.friend" showRight :showLeftIcon="false" @click="navigate(tagPath)">
      <view class="flex align-center">
        <text class="font-md text-dark mr-3">标签</text>
        <text class="font-md text-light-muted mr-2" v-for="(item,index) in detail.tags"
          :key="index">{{item}}</text>
      </view>
    </free-list-item>
    <free-divider></free-divider>
    <free-list-item v-if="detail.friend" showRight :showLeftIcon="false">
      <view class="flex align-center">
        <text class="font-md text-dark mr-3">朋友圈</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="更多信息" showRight :showLeftIcon="false"></free-list-item>
    <free-divider></free-divider>
    <view v-if="detail.friend" 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="!detail.isBlack">&#xe64e;</text>
      <text class="font-md text-primary">{{detail.isblack ? '移除黑名单' : '发信息'}}</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">添加好友</text>
    </view>
    <!-- 扩展菜单 -->
    <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="item.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/free-nav-bar.vue';
  import freeIconButton from '@/components/free-ui/free-icon-button.vue';
  import freeChatItem from '@/components/free-ui/free-chat-item.vue';
  import freePopup from '@/components/free-ui/free-popup.vue';
  import freeListItem from '@/components/free-ui/free-list-item.vue';
  import freeDivider from '@/components/free-ui/free-divider.vue';
  import freeAvatar from '@/components/free-ui/free-avatar.vue';
  import auth from '@/common/mixin/auth.js';
  import $H from '@/common/free-lib/request.js';
  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;
      // 获取当前用户资料
      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: "设置备注和标签",
          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: "把他推荐给朋友",
          type: "navigate",
          path: "mail/send-card/send-card"
        }, {
          icon: "\ue6b0",
          title: this.detail.star ? '取消星标好友' : "设为星标朋友",
          type: "event",
          event: "setStar"
        }, {
          icon: "\ue667",
          title: "设置朋友圈和动态权限",
          type: "navigate",
          path: "mail/user-moments-auth/user-moments-auth?user_id="+this.detail.id+"&params="+JSON.stringify({
            lookme:this.detail.lookme,
            lookhim:this.detail.lookhim,
          })
        }, {
          icon: "\ue638",
          title: this.detail.isblack ? '移出黑名单' : "加入黑名单",
          type: "event",
          event: "setBlack"
        }, {
          icon: "\ue61c",
          title: "投诉",
          type: "navigate",
          path: "mail/user-report/user-report?params="+JSON.stringify({
            user_id:this.detail.id,
            type:'user'
          })
        }, {
          icon: "\ue638",
          title: "删除",
          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.detail = res;
          console.log(res);
        });
      },
      openAction() {
        this.$refs.action.show()
      },
      navigate(url) {
        console.log(url)
        uni.navigateTo({
          url: '/pages/' + url,
        });
      },
      // 操作菜单事件
      popupEvent(e) {
        if (!e.type) {
          return;
        }
        setTimeout(() => {
          // 关闭弹出层
          this.$refs.action.hide()
        }, 300)
        switch (e.type) {
          case 'navigate':
            this.navigate(e.path);
            break;
          case 'event':
            this[e.event](e);
            break;
        }
      },
      // 删除好友
      deleteItem(){
        uni.showModal({
          title: '是否要删除好友?',
          success: res => {
            if(res.confirm){
              $H.post('/friend/destroy',{friend_id:this.detail.id}).then(res=>{
                uni.showToast({
                  title:'删除好友成功',
                  icon:'none'
                });
                uni.reLaunch({
                  url:'/pages/tabbar/index/index'
                })
              })
            }
          },
          fail: () => {},
          complete: () => {}
        });
      },
      // 设为星标
      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 ? '取消标星好友' : '设为标星好友';
        });
      },
      // 加入黑名单
      setBlack(e) {
        let msg = this.detail.isblack ? '移出黑名单' : '加入黑名单';
      
        uni.showModal({
          content: '是否要' + 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;
              });
              // this.detail.isBlack = !this.detail.isBlack;
              // e.title = this.isBlack ? '移出黑名单' : '加入黑名单';
              uni.showToast({
                title: msg + '成功',
                icon: 'none'
              })
            }
          }
        })
      },
        // 发送消息
      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/mail.vue

<template>
  <view>
    <!-- 导航栏 -->
    <free-nav-bar title="通讯录"></free-nav-bar>
    <!-- 通讯录列表 -->
      <scroll-view scroll-y="true" :style="'height:'+scrollHeight+'px;'"  :scroll-into-view="scrollInto">
        <free-list-item v-for="(item,index) in topList" :key="item.id"  :title="item.title" :cover="item.cover" :showRight="item.id==='friend'"  @click="navigate(item.path)"  :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="item.list.length>0" :id="'item-'+item.title">
          <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 item.list" :key="index2"  :title="item2.name" :cover="item2.avatar ? item2.avatar : '/static/images/userpic.png'" @click="navigate('mail/user-base/user-base?user_id='+item2.user_id)"></free-list-item>
        </view>
      </scroll-view>
      <!-- 侧边导航条 -->
      <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/free-nav-bar.vue";
  import freeListItem from '@/components/free-ui/free-list-item.vue';
  import freeBadge from '@/components/free-ui/free-badge.vue';
  import { mapState } from 'vuex'; 
  import auth from '@/common/mixin/auth.js';
  export default {
    mixins:[auth],
    components:{
      freeNavBar,
      freeListItem,
      freeBadge
    },
    computed:{
      ...mapState({
        applyCount:state=>state.user.apply.count,
        list:state=>state.user.mailList
      }),//state.user.apply.count
      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:"新的朋友",
            cover:"/static/images/mail/friend.png",
            path:"mail/apply-list/apply-list"
          },
          {
            id:'group',
            title:"群聊",
            cover:"/static/images/mail/group.png",
            path:"mail/group-list/group-list"
          },
          {
            id:'tag',
            title:"标签",
            cover:"/static/images/mail/tag.png",
            path:"mail/tag-list/tag-list"
          }
        ]
        
      }
    },
    methods: {
      touchstart(e){
        this.changeScrollInto(e);
      },
      touchmove(e){
        this.changeScrollInto(e);
      },
      touchend(){
        this.current = '';
      },
      // 联动
      changeScrollInto(e){
        // let Y = e.touches[0].pageY;
        // let index = Math.floor(Y / this.itemHeight);
        // let item = this.list[index];
        // if(item){
        //  this.scrollInto = 'item-'+item.letter;
        //  this.current = item.letter;
        // }
        
        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/user.js

import $U from '@/common/free-lib/util.js';
import $H from '@/common/free-lib/request.js';
import Chat from '@/common/free-lib/chat.js';
import $C from '@/common/free-lib/config.js';
export default {
  state: {
    user: false,
    apply: {
      rows: [],
      count: 0,
    },
    mailList: [],
    chat: null,
    // 会话列表
    chatList: [],
    // 总未读数
    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: {
    // 登录后处理
    login({
      state,
      dispatch
    }, user) {
      // 存到状态种
      state.user = user;
      // 存储到本地存储中
      $U.setStorage('token', user.token);
      $U.setStorage('user', JSON.stringify(user));
      $U.setStorage('user_id', user.id);
      // 获取好友申请列表
      dispatch('getApply');
      // 更新角标提示
      dispatch('updateMailBadge');
      // 连接socket
      state.chat = new Chat({
        url: $C.socketUrl
      })
      // 获取会话列表
      dispatch('getChatList');
      // 初始化总未读数角标
      dispatch('updateBadge');
      // 获取朋友圈动态通知
      dispatch('getNotice');
    },
    // 退出登录
    logout({
      state
    }) {
      // 清除登录状态
      state.user = false;
      // 清除本地存储数据
      $U.removeStorage('token');
      $U.removeStorage('user');
      $U.removeStorage('user_id');
      // 关闭socket连接
      if(state.chat){
        state.chat.close();
        state.chat = null;
      }
      
      // 跳转到登录页
      uni.reLaunch({
        url: '/pages/common/login/login'
      })
      // 注销监听事件
      uni.$off('onUpdateChatList')
      uni.$off('momentNotice')
      uni.$off('totalNoreadnum')
    },
    // 初始化登录状态
    initLogin({
      state,
      dispatch
    }) {
      // 拿到存储的数据
      let user = $U.getStorage('user');
      if (user) {
        // 初始化登录状态
        state.user = JSON.parse(user);
        // 连接socket
        state.chat = new Chat({
          url: $C.socketUrl
        })
        // 获取会话列表
        dispatch('getChatList');
        // 获取离线信息
        // 获取好友申请列表
        dispatch('getApply');
        // 初始化总未读数角标
        dispatch('updateBadge');
        // 获取朋友圈动态通知
        dispatch('getNotice');
      }
    },
    // 获取好友申请列表
    getApply({
      state,
      dispatch
    }, page = 1) {
      $H.get('/apply/' + page).then(res => {
        if (page === 1) {
          state.apply = res
        } else {
          // 下拉刷新
          state.apply.rows = [...state.apply.rows, ...res.rows]
          state.apply.count = res.count
        }
        // 更新通讯录角标提示
        dispatch('updateMailBadge');
      });
    },
    // 更新通讯录角标提示
    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
      })
    },
    // 获取通讯录列表
    getMailList({
      state
    }) {
      $H.get('/friend/list').then(res => {
        state.mailList = res.rows.newList ? res.rows.newList : [];
      })
    },
    // 获取会话列表
    getChatList({
      state
    }) {
      state.chatList = state.chat.getChatList()
      // 监听会话列表变化
      uni.$on('onUpdateChatList', (list) => {
        state.chatList = list
      })
    },
    // 获取朋友圈动态通知
    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
      })
    },
    // 初始化总未读数角标
    // 更新未读数
    async updateBadge(list = false) {
      // 开启监听总未读数变化
      uni.$on('totalNoreadnum', (num) => {
        state.totalNoreadnum = num
      })
      state.chat.updateBadge()
    },
    // 初始化总未读数角标
    updateBadge({
      state
    }) {
      // 开启监听总未读数变化
      uni.$on('totalNoreadnum', (num) => {
        console.log('totalNoreadnum:', num);
        state.totalNoreadnum = num
      })
      state.chat.updateBadge()
    },
    // 断线自动重连
    reconnect({state}){
      if(state.user && state.chat){
        state.chat.reconnect()
      }
    }
  },
}

感谢大家观看,我们下次见

目录
相关文章
|
1月前
|
存储 小程序 API
【微信小程序】-- uni-app 项目-- 购物车 -- 首页 - 轮播图效果(五十二)
【微信小程序】-- uni-app 项目-- 购物车 -- 首页 - 轮播图效果(五十二)
【微信小程序】-- uni-app 项目-- 购物车 -- 首页 - 轮播图效果(五十二)
|
1月前
|
小程序 开发工具 git
【微信小程序】-- uni-app 项目--- 购物车 -- 配置 tabBar 效果(五十一)
【微信小程序】-- uni-app 项目--- 购物车 -- 配置 tabBar 效果(五十一)
|
3月前
|
存储 JavaScript 小程序
jQuery、vue、小程序、uni-app中的本地存储数据和接受数据
jQuery、vue、小程序、uni-app中的本地存储数据和接受数据
28 0
|
2月前
|
开发框架 JavaScript 小程序
vue,小程序,uni-app的生命周期?
vue,小程序,uni-app的生命周期?
|
1月前
|
存储 小程序 前端开发
基于APP的微信点餐小程序的设计与实现
基于APP的微信点餐小程序的设计与实现
32 3
|
1月前
|
开发框架 移动开发 小程序
【微信小程序】-- 配置uni-app的开发环境(四十八)
【微信小程序】-- 配置uni-app的开发环境(四十八)
|
1月前
|
小程序 安全 JavaScript
【微信小程序】-- uni-app 项目创建 & 目录结构讲解(四十九)
【微信小程序】-- uni-app 项目创建 & 目录结构讲解(四十九)
|
1月前
|
编解码 小程序 算法
短剧系统开发(网页版/APP/小程序)丨短剧系统开发运营版及源码出售
短剧系统开发功能旨在为用户提供观看、分享和交流短剧作品的平台,涉及多种功能和特性,
|
1月前
|
移动开发 负载均衡 小程序
代驾app开发丨代驾系统开发玩法详情丨代驾系统开发网页版/H5/小程序及源码部署
**司机/代驾员端**:司机可以通过APP接收订单,查看订单详情、路线和导航,提供现场服务并进行确认。
|
2月前
|
开发框架 小程序 JavaScript
微信小程序和uni-app的区别
微信小程序和uni-app的区别
47 1