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+"¶ms="+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()
			}
		}
	},
}

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

你可能感兴趣的:(uni-app,小程序,vue.js,javascript)