UI优化,红包领取后缺少头像,名称字段
This commit is contained in:
2
.env
2
.env
@@ -9,4 +9,4 @@ VITE_CHANNEL_ID = "7b81ec142eca42baa045820793b821bb"
|
||||
VITE_GROUP_ID = "793db3a29c7846e198d71a1100d9d67b"
|
||||
|
||||
#文档地址
|
||||
VITE_DOC_URL = "http://f69dbab6.natappfree.cc"
|
||||
VITE_DOC_URL = "http://b3967d34.natappfree.cc"
|
||||
11
TUIKit/assets/icon/add-frien-icon.svg
Normal file
11
TUIKit/assets/icon/add-frien-icon.svg
Normal file
@@ -0,0 +1,11 @@
|
||||
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Frame 427319313">
|
||||
<rect width="48" height="48" rx="24" fill="#FF4C54"/>
|
||||
<g id="Group 65">
|
||||
<path id="Vector" d="M24.0115 24.2552C22.4048 24.255 20.8478 23.6835 19.6056 22.6379C18.3634 21.5923 17.5129 20.1372 17.1989 18.5206C16.8849 16.9039 17.1268 15.2256 17.8834 13.7713C18.64 12.3171 19.8645 11.1768 21.3485 10.5448C22.8324 9.9127 24.4841 9.82789 26.0221 10.3048C27.5602 10.7816 28.8896 11.7907 29.7839 13.1602C30.6783 14.5297 31.0823 16.1748 30.9272 17.8156C30.7722 19.4563 30.0675 20.9912 28.9333 22.1589C28.289 22.8251 27.5222 23.3534 26.6773 23.7133C25.8324 24.0731 24.9263 24.2573 24.0115 24.2552ZM24.0115 12.0964C23.0451 12.0964 22.1004 12.3904 21.2969 12.9412C20.4933 13.492 19.8671 14.275 19.4972 15.191C19.1274 16.107 19.0306 17.1149 19.2192 18.0874C19.4077 19.0598 19.8731 19.953 20.5564 20.6541C21.2398 21.3552 22.1104 21.8327 23.0583 22.0261C24.0061 22.2195 24.9886 22.1202 25.8814 21.7408C26.7743 21.3614 27.5374 20.7189 28.0743 19.8945C28.6112 19.0701 28.8978 18.1009 28.8978 17.1094C28.8978 15.7798 28.383 14.5048 27.4667 13.5646C26.5503 12.6245 25.3075 12.0964 24.0115 12.0964Z" fill="white"/>
|
||||
<path id="Vector_2" d="M13.1373 37.2344C12.8899 37.2325 12.6515 37.1393 12.4656 36.972C12.2797 36.8046 12.1586 36.5743 12.1245 36.3229C12.0395 35.7193 11.9979 35.11 12.0001 34.5C11.9899 32.0798 12.6766 29.7105 13.9741 27.6892C15.2716 25.668 17.122 24.0849 19.2932 23.1385C21.4644 22.1922 23.8596 21.9249 26.1784 22.37C28.4972 22.8151 30.6361 23.9529 32.3269 25.6406C32.5757 25.8837 32.8126 26.1328 33.0377 26.388C33.1275 26.4922 33.1965 26.6134 33.2406 26.7449C33.2848 26.8764 33.3032 27.0155 33.295 27.1543C33.2867 27.2931 33.2519 27.4289 33.1925 27.5539C33.1331 27.6789 33.0503 27.7907 32.9488 27.8828C32.8473 27.975 32.7291 28.0458 32.601 28.0911C32.4728 28.1364 32.3372 28.1553 32.2019 28.1468C32.0666 28.1384 31.9343 28.1027 31.8124 28.0417C31.6906 27.9808 31.5817 27.8958 31.4918 27.7917L30.9055 27.1719C29.5065 25.7901 27.7429 24.8603 25.8334 24.4977C23.9239 24.1352 21.9527 24.3559 20.1644 25.1325C18.3761 25.9091 16.8495 27.2073 15.774 28.8662C14.6985 30.525 14.1214 32.4713 14.1145 34.4636C14.1147 34.9638 14.1503 35.4634 14.2211 35.9584C14.2413 36.0961 14.2347 36.2365 14.2018 36.3715C14.1689 36.5066 14.1103 36.6337 14.0294 36.7455C13.9485 36.8573 13.8469 36.9516 13.7304 37.023C13.6139 37.0943 13.4848 37.1414 13.3505 37.1615L13.1373 37.2344Z" fill="white"/>
|
||||
<path id="Vector_3" d="M26.3747 37.3256H13.3328C13.0595 37.3256 12.7974 37.2142 12.6041 37.0159C12.4108 36.8176 12.3022 36.5487 12.3022 36.2683C12.3022 35.9879 12.4108 35.7189 12.6041 35.5207C12.7974 35.3224 13.0595 35.211 13.3328 35.211H26.3747C26.648 35.211 26.9101 35.3224 27.1034 35.5207C27.2966 35.7189 27.4052 35.9879 27.4052 36.2683C27.4052 36.5487 27.2966 36.8176 27.1034 37.0159C26.9101 37.2142 26.648 37.3256 26.3747 37.3256ZM31.6518 36.9428L31.7584 30.2891C31.7584 30.0087 31.867 29.7398 32.0603 29.5415C32.2535 29.3432 32.5157 29.2318 32.789 29.2318C33.0623 29.2318 33.3244 29.3432 33.5177 29.5415C33.711 29.7398 33.8195 30.0087 33.8195 30.2891L33.7129 36.9428C33.7129 37.0816 33.6863 37.2191 33.6345 37.3474C33.5827 37.4756 33.5068 37.5922 33.4111 37.6904C33.3154 37.7885 33.2018 37.8664 33.0767 37.9196C32.9517 37.9727 32.8177 38 32.6824 38C32.547 38 32.413 37.9727 32.288 37.9196C32.163 37.8664 32.0494 37.7885 31.9537 37.6904C31.858 37.5922 31.782 37.4756 31.7303 37.3474C31.6785 37.2191 31.6518 37.0816 31.6518 36.9428Z" fill="white"/>
|
||||
<path id="Vector_4" d="M35.9696 34.3907H29.4842C29.2108 34.3907 28.9487 34.2793 28.7555 34.081C28.5622 33.8828 28.4536 33.6138 28.4536 33.3334C28.4536 33.053 28.5622 32.7841 28.7555 32.5858C28.9487 32.3875 29.2108 32.2761 29.4842 32.2761H35.9696C36.2429 32.2761 36.505 32.3875 36.6983 32.5858C36.8915 32.7841 37.0001 33.053 37.0001 33.3334C37.0001 33.6138 36.8915 33.8828 36.6983 34.081C36.505 34.2793 36.2429 34.3907 35.9696 34.3907Z" fill="white"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.9 KiB |
1
TUIKit/assets/icon/add-friend.svg
Normal file
1
TUIKit/assets/icon/add-friend.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1768829653243" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="8654" xmlns:xlink="http://www.w3.org/1999/xlink" width="80" height="80"><path d="M137 778.1c-21.7 7.8-35.8 25.8-35.8 42.3v19.2c0 12.1 9.3 19.9 28.9 19.9h658.2c19.6 0 28.9-7.8 28.9-19.9v-19.2c0-16.9-14.4-35.8-34-42.8l-182-70.7c-2.3-0.9-5.8-2.3-10.2-4.2-7.4-3.2-14.7-6.7-21.8-10.4-12.5-6.6-23-13.4-31.6-21.4-0.8-0.8-0.8-0.8-1.6-1.5-26.7-26.2-26.6-61.7-13.6-105 3.7-12.4 9-21.4 24-44 2.4-3.7 4.9-7.4 7.3-11 3.1-4.6 5.4-8.2 7.8-11.8 5.5-8.4 10.9-16.9 16.1-25.5 21.8-36.1 35.7-67.2 40.9-94.6 1.4-7.5 2.1-14.6 2.1-21.2 0-118.8-75.3-211.4-162.3-211.4-87 0-162.3 92.6-162.3 211.4 0 9.8 1.5 20.9 4.5 33 6.3 25.4 18.7 53.9 35.9 84.6 12.4 22.1 45 72.3 46.6 75.3 18.6 35.6 16.9 88.7-7.5 116.1-11.4 12.2-25.5 20.4-44.5 28.7-7.8 3.4-15.9 6.5-23.9 9.3-3.1 1.1-5.8 2-8 2.7L137 778.1z m663.4-48.7c38.1 13.6 67.9 50.6 67.9 91v19.2c0 44.1-35.9 71.1-80.1 71.1H130.1c-44.1 0-80.1-27-80.1-71.1v-19.2c0-40.4 29.8-76.2 67.9-89.9l163-72.5s44.2-13.5 56.3-27.1c8-9 10.5-38.7 0.3-58.2S244.7 446 244.7 356c0-145 95.6-262.6 213.5-262.6S671.7 211 671.7 356c0 93.9-92.7 197.3-100.3 222.7-7.6 25.4-8.2 45.4 0.4 53.8 13.4 13.1 47.9 26.5 47.9 26.5l180.7 70.4z m0 0" p-id="8655" fill="#333333"></path><path d="M864.9 509.3c0 11.9-9.7 21.6-21.6 21.6-11.9 0-21.6-9.7-21.6-21.6v-240c0-11.9 9.7-21.6 21.6-21.6 11.9 0 21.6 9.7 21.6 21.6v240z m98.3-141.6c12 0 21.6 9.7 21.6 21.6 0 11.9-9.7 21.6-21.6 21.6H723.3c-11.9 0-21.6-9.7-21.6-21.6 0-11.9 9.7-21.6 21.6-21.6h239.9z m0 0" p-id="8656" fill="#333333"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.7 KiB |
6
TUIKit/assets/icon/blacklist-icon.svg
Normal file
6
TUIKit/assets/icon/blacklist-icon.svg
Normal file
@@ -0,0 +1,6 @@
|
||||
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Frame 427319315">
|
||||
<rect width="48" height="48" rx="24" fill="#2599FF"/>
|
||||
<path id="Vector" d="M24 8C15.1794 8 8 15.1794 8 24C8 32.8206 15.1743 40 24 40C32.8206 40 40 32.8206 40 24C40 15.1794 32.8206 8 24 8ZM9.81391 24C9.81391 16.1804 16.1753 9.81391 24 9.81391C27.511 9.81391 30.7323 11.0994 33.2118 13.2232L13.2232 33.2118C11.0994 30.7323 9.81391 27.511 9.81391 24ZM38.1861 24C38.1861 31.8196 31.8247 38.1861 24 38.1861C20.489 38.1861 17.2677 36.9006 14.7882 34.7768L34.7768 14.7882C36.9006 17.2677 38.1861 20.489 38.1861 24Z" fill="white"/>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 657 B |
10
TUIKit/assets/icon/friend-request-icon.svg
Normal file
10
TUIKit/assets/icon/friend-request-icon.svg
Normal file
@@ -0,0 +1,10 @@
|
||||
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Frame 427319312">
|
||||
<rect width="48" height="48" rx="24" fill="#00D9C5"/>
|
||||
<g id="Group 66">
|
||||
<path id="Vector" d="M35.9796 34.3005H27.4134C27.1813 34.3005 26.9587 34.2083 26.7946 34.0442C26.6305 33.8801 26.5384 33.6576 26.5384 33.4255C26.5384 33.1935 26.6305 32.9709 26.7946 32.8068C26.9587 32.6427 27.1813 32.5505 27.4134 32.5505H35.9796C36.2117 32.5505 36.4342 32.6427 36.5983 32.8068C36.7624 32.9709 36.8546 33.1935 36.8546 33.4255C36.8546 33.6576 36.7624 33.8801 36.5983 34.0442C36.4342 34.2083 36.2117 34.3005 35.9796 34.3005ZM24.4541 25.771C20.1071 25.771 16.5686 22.2343 16.5686 17.8855C16.5686 13.5368 20.1071 10 24.4541 10C28.8011 10 32.3396 13.5368 32.3396 17.8855C32.3396 22.2343 28.8011 25.771 24.4541 25.771ZM24.4541 11.75C21.0714 11.75 18.3186 14.5028 18.3186 17.8855C18.3186 21.2683 21.0714 24.021 24.4541 24.021C27.8386 24.021 30.5896 21.2683 30.5896 17.8855C30.5896 14.5028 27.8386 11.75 24.4541 11.75Z" fill="white"/>
|
||||
<path id="Vector_2" d="M31.1111 26.5847C30.947 26.5854 30.7862 26.5393 30.6474 26.4517C29.7892 25.9156 28.8651 25.4932 27.8981 25.1952C27.7883 25.1615 27.6861 25.1064 27.5976 25.0331C27.509 24.9599 27.4357 24.8699 27.3819 24.7683C27.3281 24.6668 27.2949 24.5556 27.2841 24.4412C27.2732 24.3268 27.2851 24.2113 27.3189 24.1015C27.3518 23.9913 27.4064 23.8887 27.4793 23.7998C27.5522 23.7109 27.6421 23.6373 27.7437 23.5834C27.8453 23.5295 27.9566 23.4964 28.0711 23.4859C28.1857 23.4754 28.3012 23.4877 28.4109 23.5222C29.5236 23.8651 30.5872 24.351 31.5749 24.9677C31.736 25.0684 31.86 25.2188 31.928 25.3962C31.9961 25.5736 32.0046 25.7683 31.9522 25.9509C31.8998 26.1336 31.7894 26.2942 31.6377 26.4085C31.486 26.5229 31.3011 26.5847 31.1111 26.5847ZM11.8751 37.2597C11.643 37.2597 11.4205 37.1675 11.2564 37.0035C11.0923 36.8394 11.0001 36.6168 11.0001 36.3847C10.9884 33.5041 11.9062 30.6965 13.6171 28.3789C15.3281 26.0614 17.741 24.3575 20.4974 23.5205C20.6072 23.4867 20.7226 23.4749 20.8371 23.4857C20.9515 23.4965 21.0626 23.5298 21.1642 23.5836C21.2658 23.6374 21.3557 23.7106 21.429 23.7992C21.5022 23.8878 21.5573 23.9899 21.5911 24.0997C21.6249 24.2096 21.6367 24.325 21.6259 24.4394C21.6151 24.5538 21.5818 24.665 21.528 24.7666C21.4742 24.8681 21.401 24.9581 21.3124 25.0314C21.2238 25.1046 21.1217 25.1597 21.0119 25.1935C18.6139 25.9215 16.5147 27.4037 15.0263 29.4199C13.5378 31.4361 12.7396 33.8787 12.7501 36.3847C12.7501 36.6168 12.6579 36.8394 12.4938 37.0035C12.3297 37.1675 12.1072 37.2597 11.8751 37.2597ZM36.6061 34.0537C36.4798 34.0535 36.355 34.026 36.2402 33.9732C36.1254 33.9203 36.0234 33.8433 35.9411 33.7475L32.9819 30.293C32.9072 30.2057 32.8504 30.1045 32.8148 29.9952C32.7793 29.8859 32.7655 29.7707 32.7745 29.6562C32.7834 29.5416 32.8148 29.4299 32.8669 29.3275C32.919 29.2251 32.9908 29.1339 33.0781 29.0592C33.1655 28.9845 33.2666 28.9278 33.3759 28.8922C33.4852 28.8566 33.6004 28.8429 33.7149 28.8518C33.8295 28.8608 33.9412 28.8922 34.0436 28.9443C34.146 28.9964 34.2372 29.0682 34.3119 29.1555L37.2711 32.61C37.3799 32.7371 37.4499 32.8927 37.4729 33.0584C37.4959 33.2241 37.4709 33.3929 37.4009 33.5448C37.331 33.6967 37.2189 33.8254 37.078 33.9156C36.9372 34.0058 36.7734 34.0537 36.6061 34.0537Z" fill="white"/>
|
||||
<path id="Vector_3" d="M33.6469 38C33.4842 38.0003 33.3247 37.9553 33.1862 37.87C33.0478 37.7847 32.9358 37.6625 32.8629 37.5171C32.79 37.3717 32.7591 37.2088 32.7736 37.0468C32.7882 36.8848 32.8476 36.7301 32.9451 36.6L35.9044 32.6537C36.0487 32.4831 36.2527 32.374 36.4747 32.3488C36.6967 32.3235 36.92 32.3839 37.0989 32.5178C37.2778 32.6516 37.3989 32.8486 37.4374 33.0687C37.476 33.2888 37.429 33.5153 37.3061 33.702L34.3469 37.65C34.2654 37.7586 34.1597 37.8468 34.0382 37.9076C33.9167 37.9683 33.7827 38 33.6469 38Z" fill="white"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.8 KiB |
9
TUIKit/assets/icon/my-group-chat.svg
Normal file
9
TUIKit/assets/icon/my-group-chat.svg
Normal file
@@ -0,0 +1,9 @@
|
||||
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g id="Frame 427319314">
|
||||
<rect width="48" height="48" rx="24" fill="#24DB5A"/>
|
||||
<g id="群聊 1">
|
||||
<path id="Vector" d="M33.5747 38.9863C33.1461 38.9863 32.7337 38.8644 32.3815 38.6333L31.2455 37.901C31.1875 37.8701 31.104 37.8201 31.0204 37.742L30.3602 37.3052C27.8147 37.6443 25.3319 37.2208 23.1578 36.0747C22.7575 35.8634 22.6072 35.373 22.8226 34.9799C22.9256 34.7905 23.1013 34.6494 23.3104 34.5875C23.5197 34.5256 23.7449 34.5481 23.9379 34.6501C25.8818 35.6754 28.1251 36.0257 30.4238 35.6612C30.6297 35.6288 30.8404 35.6743 31.0146 35.7888L32.0277 36.4579C32.0615 36.4805 32.0945 36.5058 32.125 36.5329L33.2899 37.2848C33.4435 37.3862 33.61 37.3964 33.8649 37.316C34.0104 37.2156 34.0956 37.0617 34.0956 36.8938V34.8321C34.0957 34.563 34.2322 34.3112 34.4598 34.161C36.7456 32.6538 38.11 30.2883 38.11 27.8338C38.11 27.2566 38.0433 26.6941 37.9121 26.1613C37.794 25.6863 37.6148 25.2057 37.3816 24.7334C37.183 24.3317 37.3535 23.8478 37.7627 23.6527C38.1727 23.458 38.664 23.6252 38.8626 24.0268C39.1472 24.6016 39.3652 25.1911 39.5122 25.7789C39.6746 26.4377 39.7566 27.1287 39.7566 27.8338C39.7566 30.693 38.2644 33.4365 35.7422 35.2522V36.8938C35.7422 37.6597 35.3138 38.3729 34.6239 38.755C34.5848 38.7769 34.5439 38.7955 34.5017 38.8106C34.172 38.9287 33.8689 38.9863 33.5747 38.9863ZM15.8978 35.8578C15.5384 35.8578 15.179 35.7675 14.8598 35.5961C14.1788 35.2484 13.7302 34.5195 13.7302 33.7472V31.106C10.2858 28.7777 8.24365 25.1653 8.24365 21.3599C8.24365 14.5522 14.6347 9.01367 22.4898 9.01367C30.3459 9.01367 36.7368 14.5522 36.7368 21.3599C36.7368 28.1675 30.3459 33.7058 22.4898 33.7058C21.7116 33.7058 20.9147 33.6462 20.118 33.5286L18.8485 34.3541C18.7874 34.4039 18.7335 34.4379 18.6966 34.4607L17.0516 35.5255C16.7452 35.7355 16.3287 35.8578 15.8978 35.8578ZM22.4898 10.6309C15.5424 10.6309 9.89027 15.4441 9.89027 21.3599C9.89027 24.7493 11.7997 27.9764 14.9982 29.9922C15.2342 30.1409 15.377 30.3975 15.3768 30.6729V33.7472C15.3768 33.9182 15.483 34.0914 15.6341 34.1693C15.8464 34.283 16.0723 34.2237 16.1269 34.1874L17.7647 33.1262C17.7897 33.1109 17.8001 33.1042 17.8017 33.1042C17.8017 33.1042 17.8017 33.1049 17.8008 33.106C17.8293 33.0807 17.8596 33.0576 17.8917 33.037L19.4757 32.0061C19.6513 31.8926 19.8628 31.8487 20.069 31.883C20.8834 32.0196 21.6979 32.0886 22.4898 32.0886C29.438 32.0886 35.0902 27.2756 35.0902 21.3599C35.0903 15.4441 29.4381 10.6309 22.4898 10.6309ZM17.7655 33.1381C17.7655 33.1385 17.7646 33.1388 17.7646 33.1392C17.7646 33.1388 17.7655 33.1386 17.7655 33.1381Z" fill="white"/>
|
||||
<path id="Vector_2" d="M18.8262 21.2477C18.8262 22.0079 18.1949 22.6281 17.4209 22.6281C16.6468 22.6281 16.0154 22.0079 16.0154 21.2477C16.0154 20.4876 16.6468 19.8676 17.4209 19.8676C18.1949 19.8676 18.8262 20.4876 18.8262 21.2477ZM23.9999 21.512C23.9999 22.1991 23.429 22.7596 22.7293 22.7596C22.0295 22.7596 21.4589 22.1991 21.4589 21.512C21.4589 20.8248 22.0295 20.2643 22.7293 20.2643C23.429 20.2643 23.9999 20.8248 23.9999 21.512ZM28.6769 21.512C28.6769 22.1991 28.106 22.7596 27.4064 22.7596C26.7066 22.7596 26.1359 22.1991 26.1359 21.512C26.1359 20.8248 26.7066 20.2643 27.4064 20.2643C28.106 20.2643 28.6769 20.8248 28.6769 21.512Z" fill="white"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 3.3 KiB |
@@ -103,4 +103,9 @@
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped></style>
|
||||
<style lang="scss" scoped>
|
||||
.tui-navigation {
|
||||
border-bottom: 2rpx solid #0000000a;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -40,7 +40,7 @@
|
||||
}
|
||||
|
||||
.message-input-toolbar-uni {
|
||||
background-color: #ebf0f6;
|
||||
background-color: #ffffff;
|
||||
flex-direction: column;
|
||||
z-index: 100;
|
||||
|
||||
|
||||
@@ -1,5 +1,15 @@
|
||||
@import "../../../../../assets/styles/common";
|
||||
@import "./color";
|
||||
@import "./web";
|
||||
@import "./h5";
|
||||
@import "./uni";
|
||||
@import '../../../../../assets/styles/common';
|
||||
@import './color';
|
||||
@import './web';
|
||||
@import './h5';
|
||||
@import './uni';
|
||||
|
||||
.toolbar-item-container-icon {
|
||||
background: #f4f4f4;
|
||||
}
|
||||
|
||||
.toolbar-item-container-uni-title {
|
||||
font-weight: 500;
|
||||
font-size: 24rpx;
|
||||
color: #666666;
|
||||
}
|
||||
|
||||
@@ -212,7 +212,7 @@ defineExpose({
|
||||
flex-direction: column;
|
||||
border: none;
|
||||
overflow: hidden;
|
||||
background: #ebf0f6;
|
||||
background: #ffffff;
|
||||
|
||||
&-h5 {
|
||||
padding: 10px 10px 15px;
|
||||
|
||||
@@ -2,22 +2,21 @@
|
||||
<div
|
||||
:class="{
|
||||
'message-input-container': true,
|
||||
'message-input-container-h5': !isPC,
|
||||
'message-input-container-h5': !isPC
|
||||
}"
|
||||
>
|
||||
<div
|
||||
v-if="props.isMuted"
|
||||
class="message-input-mute"
|
||||
>
|
||||
<div v-if="props.isMuted" class="message-input-mute">
|
||||
{{ props.muteText }}
|
||||
</div>
|
||||
<!-- #ifdef APP-PLUS -->
|
||||
<div
|
||||
v-if="inputToolbarDisplayType === 'emojiPicker' || inputToolbarDisplayType === 'tools'"
|
||||
v-if="
|
||||
inputToolbarDisplayType === 'emojiPicker' ||
|
||||
inputToolbarDisplayType === 'tools'
|
||||
"
|
||||
class="input-click-mask"
|
||||
@tap.stop.prevent="handleMaskClick"
|
||||
>
|
||||
</div>
|
||||
></div>
|
||||
<!-- #endif -->
|
||||
<input
|
||||
id="editor"
|
||||
@@ -38,245 +37,273 @@
|
||||
@input="onInput"
|
||||
@blur="onBlur"
|
||||
@focus="onFocus"
|
||||
>
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, watch, onMounted, onUnmounted } from '../../../adapter-vue';
|
||||
import { TUIStore, StoreName, IConversationModel, IMessageModel } from '@tencentcloud/chat-uikit-engine-lite';
|
||||
import { TUIGlobal } from '@tencentcloud/universal-api';
|
||||
import DraftManager from '../utils/conversationDraft';
|
||||
import { transformTextWithEmojiNamesToKeys } from '../emoji-config';
|
||||
import { isPC } from '../../../utils/env';
|
||||
import { sendMessages } from '../utils/sendMessage';
|
||||
import { ISendMessagePayload, ToolbarDisplayType } from '../../../interface';
|
||||
import {
|
||||
ref,
|
||||
watch,
|
||||
onMounted,
|
||||
onUnmounted
|
||||
} from '../../../adapter-vue'
|
||||
import {
|
||||
TUIStore,
|
||||
StoreName,
|
||||
IConversationModel,
|
||||
IMessageModel
|
||||
} from '@tencentcloud/chat-uikit-engine-lite'
|
||||
import { TUIGlobal } from '@tencentcloud/universal-api'
|
||||
import DraftManager from '../utils/conversationDraft'
|
||||
import { transformTextWithEmojiNamesToKeys } from '../emoji-config'
|
||||
import { isPC } from '../../../utils/env'
|
||||
import { sendMessages } from '../utils/sendMessage'
|
||||
import {
|
||||
ISendMessagePayload,
|
||||
ToolbarDisplayType
|
||||
} from '../../../interface'
|
||||
|
||||
const props = defineProps({
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: 'this is placeholder',
|
||||
default: 'this is placeholder'
|
||||
},
|
||||
replayOrReferenceMessage: {
|
||||
type: Object,
|
||||
default: () => ({}),
|
||||
required: false,
|
||||
required: false
|
||||
},
|
||||
isMuted: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
default: true
|
||||
},
|
||||
muteText: {
|
||||
type: String,
|
||||
default: '',
|
||||
default: ''
|
||||
},
|
||||
enableInput: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
default: true
|
||||
},
|
||||
enableAt: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
default: true
|
||||
},
|
||||
enableTyping: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
default: true
|
||||
},
|
||||
isGroup: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
default: false
|
||||
},
|
||||
inputToolbarDisplayType: {
|
||||
type: String,
|
||||
defult: '',
|
||||
defult: ''
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
const emits = defineEmits(['onTyping', 'onFocus', 'onAt']);
|
||||
const inputText = ref('');
|
||||
const inputRef = ref();
|
||||
const inputBlur = ref(true);
|
||||
const programmaticFocus = ref(false);
|
||||
const inputContentEmpty = ref(true);
|
||||
const allInsertedAtInfo = new Map();
|
||||
const currentConversation = ref<IConversationModel>();
|
||||
const currentConversationID = ref<string>('');
|
||||
const currentQuoteMessage = ref<{ message: IMessageModel; type: string }>();
|
||||
const emits = defineEmits(['onTyping', 'onFocus', 'onAt'])
|
||||
const inputText = ref('')
|
||||
const inputRef = ref()
|
||||
const inputBlur = ref(true)
|
||||
const programmaticFocus = ref(false)
|
||||
const inputContentEmpty = ref(true)
|
||||
const allInsertedAtInfo = new Map()
|
||||
const currentConversation = ref<IConversationModel>()
|
||||
const currentConversationID = ref<string>('')
|
||||
const currentQuoteMessage = ref<{
|
||||
message: IMessageModel
|
||||
type: string
|
||||
}>()
|
||||
|
||||
onMounted(() => {
|
||||
TUIStore.watch(StoreName.CONV, {
|
||||
currentConversation: onCurrentConversationUpdated,
|
||||
});
|
||||
currentConversation: onCurrentConversationUpdated
|
||||
})
|
||||
|
||||
TUIStore.watch(StoreName.CHAT, {
|
||||
quoteMessage: onQuoteMessageUpdated,
|
||||
});
|
||||
quoteMessage: onQuoteMessageUpdated
|
||||
})
|
||||
|
||||
uni.$on('insert-emoji', (data) => {
|
||||
inputText.value += data?.emoji?.name;
|
||||
});
|
||||
uni.$on('insert-emoji', data => {
|
||||
inputText.value += data?.emoji?.name
|
||||
})
|
||||
|
||||
uni.$on('send-message-in-emoji-picker', () => {
|
||||
handleSendMessage();
|
||||
});
|
||||
});
|
||||
handleSendMessage()
|
||||
})
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
if (currentConversationID.value) {
|
||||
DraftManager.setStore(currentConversationID.value, inputText.value, inputText.value, currentQuoteMessage.value);
|
||||
DraftManager.setStore(
|
||||
currentConversationID.value,
|
||||
inputText.value,
|
||||
inputText.value,
|
||||
currentQuoteMessage.value
|
||||
)
|
||||
}
|
||||
|
||||
uni.$off('insertEmoji');
|
||||
uni.$off('send-message-in-emoji-picker');
|
||||
uni.$off('insertEmoji')
|
||||
uni.$off('send-message-in-emoji-picker')
|
||||
|
||||
TUIStore.unwatch(StoreName.CONV, {
|
||||
currentConversation: onCurrentConversationUpdated,
|
||||
});
|
||||
currentConversation: onCurrentConversationUpdated
|
||||
})
|
||||
|
||||
TUIStore.unwatch(StoreName.CHAT, {
|
||||
quoteMessage: onQuoteMessageUpdated,
|
||||
});
|
||||
quoteMessage: onQuoteMessageUpdated
|
||||
})
|
||||
|
||||
reset();
|
||||
});
|
||||
reset()
|
||||
})
|
||||
|
||||
const handleSendMessage = () => {
|
||||
const messageList = getEditorContent();
|
||||
resetEditor();
|
||||
sendMessages(messageList as any, currentConversation.value!);
|
||||
};
|
||||
const messageList = getEditorContent()
|
||||
resetEditor()
|
||||
sendMessages(messageList as any, currentConversation.value!)
|
||||
}
|
||||
|
||||
const insertAt = (atInfo: any) => {
|
||||
if (!allInsertedAtInfo?.has(atInfo?.id)) {
|
||||
allInsertedAtInfo?.set(atInfo?.id, atInfo?.label);
|
||||
allInsertedAtInfo?.set(atInfo?.id, atInfo?.label)
|
||||
}
|
||||
inputText.value += atInfo?.label
|
||||
}
|
||||
inputText.value += atInfo?.label;
|
||||
};
|
||||
|
||||
const getEditorContent = () => {
|
||||
let text = inputText.value;
|
||||
text = transformTextWithEmojiNamesToKeys(text);
|
||||
const atUserList: string[] = [];
|
||||
let text = inputText.value
|
||||
text = transformTextWithEmojiNamesToKeys(text)
|
||||
const atUserList: string[] = []
|
||||
allInsertedAtInfo?.forEach((value: string, key: string) => {
|
||||
if (text?.includes('@' + value)) {
|
||||
atUserList.push(key);
|
||||
atUserList.push(key)
|
||||
}
|
||||
});
|
||||
})
|
||||
const payload: ISendMessagePayload = {
|
||||
text,
|
||||
};
|
||||
text
|
||||
}
|
||||
if (atUserList?.length) {
|
||||
payload.atUserList = atUserList;
|
||||
payload.atUserList = atUserList
|
||||
}
|
||||
return [
|
||||
{
|
||||
type: 'text',
|
||||
payload,
|
||||
},
|
||||
];
|
||||
};
|
||||
payload
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
const resetEditor = () => {
|
||||
inputText.value = '';
|
||||
inputContentEmpty.value = true;
|
||||
allInsertedAtInfo?.clear();
|
||||
};
|
||||
inputText.value = ''
|
||||
inputContentEmpty.value = true
|
||||
allInsertedAtInfo?.clear()
|
||||
}
|
||||
|
||||
const setEditorContent = (content: any) => {
|
||||
inputText.value = content;
|
||||
};
|
||||
inputText.value = content
|
||||
}
|
||||
|
||||
const onBlur = () => {
|
||||
inputBlur.value = true;
|
||||
programmaticFocus.value = false;
|
||||
};
|
||||
inputBlur.value = true
|
||||
programmaticFocus.value = false
|
||||
}
|
||||
|
||||
const onFocus = (e: any) => {
|
||||
inputBlur.value = false;
|
||||
emits('onFocus', e?.detail?.height);
|
||||
uni.$emit('scroll-to-bottom');
|
||||
};
|
||||
inputBlur.value = false
|
||||
emits('onFocus', e?.detail?.height)
|
||||
uni.$emit('scroll-to-bottom')
|
||||
}
|
||||
|
||||
const isEditorContentEmpty = () => {
|
||||
inputContentEmpty.value = inputText?.value?.length ? false : true;
|
||||
};
|
||||
inputContentEmpty.value = inputText?.value?.length ? false : true
|
||||
}
|
||||
|
||||
const onInput = (e: any) => {
|
||||
// uni-app recognizes mention messages
|
||||
const text = e?.detail?.value;
|
||||
isEditorContentEmpty();
|
||||
const text = e?.detail?.value
|
||||
isEditorContentEmpty()
|
||||
if (props.isGroup && (text.endsWith('@') || text.endsWith('@\n'))) {
|
||||
TUIGlobal?.hideKeyboard();
|
||||
emits('onAt', true);
|
||||
TUIGlobal?.hideKeyboard()
|
||||
emits('onAt', true)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
watch(
|
||||
() => [inputContentEmpty.value, inputBlur.value],
|
||||
(newVal: any, oldVal: any) => {
|
||||
if (newVal !== oldVal) {
|
||||
emits('onTyping', inputContentEmpty.value, inputBlur.value);
|
||||
emits('onTyping', inputContentEmpty.value, inputBlur.value)
|
||||
}
|
||||
},
|
||||
{
|
||||
immediate: true,
|
||||
deep: true,
|
||||
},
|
||||
);
|
||||
deep: true
|
||||
}
|
||||
)
|
||||
|
||||
function onCurrentConversationUpdated(conversation: IConversationModel) {
|
||||
const prevConversationID = currentConversationID.value;
|
||||
currentConversation.value = conversation;
|
||||
currentConversationID.value = conversation?.conversationID;
|
||||
function onCurrentConversationUpdated(
|
||||
conversation: IConversationModel
|
||||
) {
|
||||
const prevConversationID = currentConversationID.value
|
||||
currentConversation.value = conversation
|
||||
currentConversationID.value = conversation?.conversationID
|
||||
if (prevConversationID !== currentConversationID.value) {
|
||||
if (prevConversationID) {
|
||||
DraftManager.setStore(
|
||||
prevConversationID,
|
||||
inputText.value,
|
||||
inputText.value,
|
||||
currentQuoteMessage.value,
|
||||
);
|
||||
currentQuoteMessage.value
|
||||
)
|
||||
}
|
||||
resetEditor();
|
||||
resetEditor()
|
||||
if (currentConversationID.value) {
|
||||
DraftManager.getStore(currentConversationID.value, setEditorContent);
|
||||
DraftManager.getStore(
|
||||
currentConversationID.value,
|
||||
setEditorContent
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onQuoteMessageUpdated(options?: { message: IMessageModel; type: string }) {
|
||||
currentQuoteMessage.value = options;
|
||||
function onQuoteMessageUpdated(options?: {
|
||||
message: IMessageModel
|
||||
type: string
|
||||
}) {
|
||||
currentQuoteMessage.value = options
|
||||
}
|
||||
|
||||
function reset() {
|
||||
inputBlur.value = true;
|
||||
currentConversation.value = null;
|
||||
currentConversationID.value = '';
|
||||
currentQuoteMessage.value = null;
|
||||
resetEditor();
|
||||
inputBlur.value = true
|
||||
currentConversation.value = null
|
||||
currentConversationID.value = ''
|
||||
currentQuoteMessage.value = null
|
||||
resetEditor()
|
||||
}
|
||||
|
||||
|
||||
function handleMaskClick(e: Event) {
|
||||
// #ifdef APP-PLUS
|
||||
e.stopPropagation();
|
||||
emits('onFocus');
|
||||
uni.$emit('scroll-to-bottom');
|
||||
e.stopPropagation()
|
||||
emits('onFocus')
|
||||
uni.$emit('scroll-to-bottom')
|
||||
setTimeout(() => {
|
||||
programmaticFocus.value = true;
|
||||
programmaticFocus.value = true
|
||||
// IOS set 500ms timeout
|
||||
}, 100);
|
||||
}, 100)
|
||||
// #endif
|
||||
}
|
||||
defineExpose({
|
||||
insertAt,
|
||||
resetEditor,
|
||||
setEditorContent,
|
||||
getEditorContent,
|
||||
});
|
||||
getEditorContent
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@import "../../../assets/styles/common";
|
||||
@import '../../../assets/styles/common';
|
||||
|
||||
.message-input-container {
|
||||
display: flex;
|
||||
@@ -285,6 +312,8 @@ defineExpose({
|
||||
padding: 3px 10px 10px;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
border-radius: 64rpx !important;
|
||||
background: #f4f4f4 !important;
|
||||
|
||||
&-h5 {
|
||||
flex: 1;
|
||||
|
||||
@@ -616,16 +616,35 @@
|
||||
const onClaim = (item: IMessageModel, index: number) => {
|
||||
const { conversationType, cloudCustomData, flow, payload } = item
|
||||
const data = JSON.parse(payload.data)
|
||||
|
||||
getRedEnvelopeDetail(data.id).then(async (res: any) => {
|
||||
// 群聊
|
||||
if (conversationType === TYPES.value.CONV_GROUP) {
|
||||
console.log(item)
|
||||
console.log(data)
|
||||
receiveRedEnvelope({
|
||||
redPacketId: data.id
|
||||
if (res.data.hasReceived) {
|
||||
// 直接去详情页
|
||||
navigateTo('/pages/red-packet/details', {
|
||||
id: data.id,
|
||||
type: conversationType
|
||||
})
|
||||
} else {
|
||||
const show = await showDialog('提示', '是否领取该红包?')
|
||||
if (show) {
|
||||
// newMessage.in = true
|
||||
// customRefMessage.value[index].updateClaimStatus(newMessage)
|
||||
// item.modifyMessage({
|
||||
// cloudCustomData: JSON.stringify(newMessage)
|
||||
// })
|
||||
await receiveRedEnvelope({
|
||||
redPacketId: data.id
|
||||
})
|
||||
navigateTo('/pages/red-packet/details', {
|
||||
id: data.id,
|
||||
type: conversationType
|
||||
})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 个人红包
|
||||
getRedEnvelopeDetail(data.id).then(async (res: any) => {
|
||||
console.log(res)
|
||||
let newMessage = {
|
||||
// ...data,
|
||||
@@ -639,23 +658,37 @@
|
||||
if (flow === 'in') {
|
||||
if (res.data.hasReceived) {
|
||||
// 直接去详情页
|
||||
navigateTo('/pages/red-packet/details', {
|
||||
id: data.id,
|
||||
type: conversationType
|
||||
})
|
||||
} else {
|
||||
const show = await showDialog('提示', '是否领取该红包?')
|
||||
if (show) {
|
||||
newMessage.in = true
|
||||
customRefMessage.value[index].updateClaimStatus(newMessage)
|
||||
item.modifyMessage({
|
||||
cloudCustomData: JSON.stringify(newMessage)
|
||||
// newMessage.in = true
|
||||
// customRefMessage.value[index].updateClaimStatus(newMessage)
|
||||
// item.modifyMessage({
|
||||
// cloudCustomData: JSON.stringify(newMessage)
|
||||
// })
|
||||
await receiveRedEnvelope({
|
||||
redPacketId: data.id
|
||||
})
|
||||
navigateTo('/pages/red-packet/details', {
|
||||
id: data.id,
|
||||
type: conversationType
|
||||
})
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
newMessage.out = true
|
||||
customRefMessage.value[index].updateClaimStatus(newMessage)
|
||||
item.modifyMessage({
|
||||
cloudCustomData: JSON.stringify(newMessage)
|
||||
navigateTo('/pages/red-packet/details', {
|
||||
id: data.id,
|
||||
type: conversationType
|
||||
})
|
||||
// newMessage.out = true
|
||||
// customRefMessage.value[index].updateClaimStatus(newMessage)
|
||||
// item.modifyMessage({
|
||||
// cloudCustomData: JSON.stringify(newMessage)
|
||||
// })
|
||||
// .then(() => {
|
||||
// navigateTo('/pages/red-packet/details', {
|
||||
// id: data.id,
|
||||
@@ -663,23 +696,8 @@
|
||||
// })
|
||||
// })
|
||||
}
|
||||
})
|
||||
|
||||
return
|
||||
if (flow === 'in') {
|
||||
// 修改后的消息
|
||||
const newMessage = {
|
||||
...data,
|
||||
isOpen: true
|
||||
}
|
||||
item.modifyMessage({
|
||||
cloudCustomData: JSON.stringify(newMessage)
|
||||
})
|
||||
receiveRedEnvelope({
|
||||
redPacketId: data.id
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const resendMessageConfirm = () => {
|
||||
|
||||
@@ -23,17 +23,22 @@
|
||||
:url="message.avatar || ''"
|
||||
:style="{ flex: '0 0 auto' }"
|
||||
/>
|
||||
<main
|
||||
class="message-body"
|
||||
@click.stop
|
||||
>
|
||||
<main class="message-body" @click.stop>
|
||||
<div
|
||||
v-if="message.flow === 'in' && message.conversationType === 'GROUP'"
|
||||
v-if="
|
||||
message.flow === 'in' &&
|
||||
message.conversationType === 'GROUP'
|
||||
"
|
||||
class="message-body-nick-name"
|
||||
>
|
||||
{{ props.content.showName }}
|
||||
</div>
|
||||
<div :class="['message-body-main', message.flow === 'out' && 'message-body-main-reverse']">
|
||||
<div
|
||||
:class="[
|
||||
'message-body-main',
|
||||
message.flow === 'out' && 'message-body-main-reverse'
|
||||
]"
|
||||
>
|
||||
<div
|
||||
:class="[
|
||||
'blink',
|
||||
@@ -42,18 +47,22 @@
|
||||
message.hasRiskContent && 'content-has-risk',
|
||||
isNoPadding ? 'content-no-padding' : '',
|
||||
isNoPadding && isBlink ? 'blink-shadow' : '',
|
||||
!isNoPadding && isBlink ? 'blink-content' : '',
|
||||
!isNoPadding && isBlink ? 'blink-content' : ''
|
||||
]"
|
||||
>
|
||||
<div class="content-main">
|
||||
<img
|
||||
v-if="
|
||||
(message.type === TYPES.MSG_IMAGE || message.type === TYPES.MSG_VIDEO) &&
|
||||
(message.type === TYPES.MSG_IMAGE ||
|
||||
message.type === TYPES.MSG_VIDEO) &&
|
||||
message.hasRiskContent
|
||||
"
|
||||
:class="['message-risk-replace', !isPC && 'message-risk-replace-h5']"
|
||||
:class="[
|
||||
'message-risk-replace',
|
||||
!isPC && 'message-risk-replace-h5'
|
||||
]"
|
||||
:src="riskImageReplaceUrl"
|
||||
>
|
||||
/>
|
||||
<template v-else>
|
||||
<slot />
|
||||
</template>
|
||||
@@ -67,10 +76,7 @@
|
||||
</div>
|
||||
</div>
|
||||
<!-- audio unplay mark -->
|
||||
<div
|
||||
v-if="isDisplayUnplayMark"
|
||||
class="audio-unplay-mark"
|
||||
/>
|
||||
<div v-if="isDisplayUnplayMark" class="audio-unplay-mark" />
|
||||
<!-- Fail Icon -->
|
||||
<div
|
||||
v-if="message.status === 'fail' || message.hasRiskContent"
|
||||
@@ -81,7 +87,10 @@
|
||||
</div>
|
||||
<!-- Loading Icon -->
|
||||
<Icon
|
||||
v-if="message.status === 'unSend' && needLoadingIconMessageType.includes(message.type)"
|
||||
v-if="
|
||||
message.status === 'unSend' &&
|
||||
needLoadingIconMessageType.includes(message.type)
|
||||
"
|
||||
class="message-label loading-circle"
|
||||
:file="loadingIcon"
|
||||
:width="'15px'"
|
||||
@@ -122,39 +131,49 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, toRefs } from '../../../../adapter-vue';
|
||||
import TUIChatEngine, { TUITranslateService, IMessageModel } from '@tencentcloud/chat-uikit-engine-lite';
|
||||
import Icon from '../../../common/Icon.vue';
|
||||
import ReadStatus from './read-status/index.vue';
|
||||
import MessageQuote from './message-quote/index.vue';
|
||||
import Avatar from '../../../common/Avatar/index.vue';
|
||||
import MessageTranslate from './message-translate/index.vue';
|
||||
import MessageConvert from './message-convert/index.vue';
|
||||
import RadioSelect from '../../../common/RadioSelect/index.vue';
|
||||
import loadingIcon from '../../../../assets/icon/loading.png';
|
||||
import { shallowCopyMessage } from '../../utils/utils';
|
||||
import { isPC } from '../../../../utils/env';
|
||||
import { computed, toRefs } from '../../../../adapter-vue'
|
||||
import TUIChatEngine, {
|
||||
TUITranslateService,
|
||||
IMessageModel
|
||||
} from '@tencentcloud/chat-uikit-engine-lite'
|
||||
import Icon from '../../../common/Icon.vue'
|
||||
import ReadStatus from './read-status/index.vue'
|
||||
import MessageQuote from './message-quote/index.vue'
|
||||
import Avatar from '../../../common/Avatar/index.vue'
|
||||
import MessageTranslate from './message-translate/index.vue'
|
||||
import MessageConvert from './message-convert/index.vue'
|
||||
import RadioSelect from '../../../common/RadioSelect/index.vue'
|
||||
import loadingIcon from '../../../../assets/icon/loading.png'
|
||||
import { shallowCopyMessage } from '../../utils/utils'
|
||||
import { isPC } from '../../../../utils/env'
|
||||
|
||||
interface IProps {
|
||||
messageItem: IMessageModel;
|
||||
content?: any;
|
||||
classNameList?: string[];
|
||||
blinkMessageIDList?: string[];
|
||||
isMultipleSelectMode?: boolean;
|
||||
isAudioPlayed?: boolean | undefined;
|
||||
multipleSelectedMessageIDList?: string[];
|
||||
messageItem: IMessageModel
|
||||
content?: any
|
||||
classNameList?: string[]
|
||||
blinkMessageIDList?: string[]
|
||||
isMultipleSelectMode?: boolean
|
||||
isAudioPlayed?: boolean | undefined
|
||||
multipleSelectedMessageIDList?: string[]
|
||||
}
|
||||
|
||||
interface IEmits {
|
||||
(e: 'resendMessage'): void;
|
||||
(e: 'blinkMessage', messageID: string): void;
|
||||
(e: 'setReadReceiptPanelVisible', visible: boolean, message?: IMessageModel): void;
|
||||
(e: 'changeSelectMessageIDList', options: { type: 'add' | 'remove' | 'clearAll'; messageID: string }): void;
|
||||
(e: 'resendMessage'): void
|
||||
(e: 'blinkMessage', messageID: string): void
|
||||
(
|
||||
e: 'setReadReceiptPanelVisible',
|
||||
visible: boolean,
|
||||
message?: IMessageModel
|
||||
): void
|
||||
(
|
||||
e: 'changeSelectMessageIDList',
|
||||
options: { type: 'add' | 'remove' | 'clearAll'; messageID: string }
|
||||
): void
|
||||
// Only for uni-app
|
||||
(e: 'scrollTo', scrollHeight: number): void;
|
||||
(e: 'scrollTo', scrollHeight: number): void
|
||||
}
|
||||
|
||||
const emits = defineEmits<IEmits>();
|
||||
const emits = defineEmits<IEmits>()
|
||||
|
||||
const props = withDefaults(defineProps<IProps>(), {
|
||||
isAudioPlayed: false,
|
||||
@@ -163,86 +182,93 @@ const props = withDefaults(defineProps<IProps>(), {
|
||||
blinkMessageIDList: () => [],
|
||||
classNameList: () => [],
|
||||
isMultipleSelectMode: false,
|
||||
multipleSelectedMessageIDList: () => [],
|
||||
});
|
||||
multipleSelectedMessageIDList: () => []
|
||||
})
|
||||
|
||||
const TYPES = TUIChatEngine.TYPES;
|
||||
const riskImageReplaceUrl = 'https://web.sdk.qcloud.com/component/TUIKit/assets/has_risk_default.png';
|
||||
const TYPES = TUIChatEngine.TYPES
|
||||
const riskImageReplaceUrl =
|
||||
'https://web.sdk.qcloud.com/component/TUIKit/assets/has_risk_default.png'
|
||||
const needLoadingIconMessageType = [
|
||||
TYPES.MSG_LOCATION,
|
||||
TYPES.MSG_TEXT,
|
||||
TYPES.MSG_CUSTOM,
|
||||
TYPES.MSG_MERGER,
|
||||
TYPES.MSG_FACE,
|
||||
];
|
||||
TYPES.MSG_FACE
|
||||
]
|
||||
|
||||
const { blinkMessageIDList, messageItem: message } = toRefs(props);
|
||||
const { blinkMessageIDList, messageItem: message } = toRefs(props)
|
||||
|
||||
const isMultipleSelected = computed<boolean>(() => {
|
||||
return props.multipleSelectedMessageIDList.includes(message.value.ID);
|
||||
});
|
||||
return props.multipleSelectedMessageIDList.includes(message.value.ID)
|
||||
})
|
||||
|
||||
const isDisplayUnplayMark = computed<boolean>(() => {
|
||||
return message.value.flow === 'in'
|
||||
&& message.value.status === 'success'
|
||||
&& message.value.type === TYPES.MSG_AUDIO
|
||||
&& !props.isAudioPlayed;
|
||||
});
|
||||
return (
|
||||
message.value.flow === 'in' &&
|
||||
message.value.status === 'success' &&
|
||||
message.value.type === TYPES.MSG_AUDIO &&
|
||||
!props.isAudioPlayed
|
||||
)
|
||||
})
|
||||
|
||||
const containerClassNameList = computed(() => {
|
||||
return [
|
||||
'message-bubble',
|
||||
isMultipleSelected.value ? 'multiple-selected' : '',
|
||||
...props.classNameList,
|
||||
];
|
||||
});
|
||||
...props.classNameList
|
||||
]
|
||||
})
|
||||
|
||||
const isNoPadding = computed(() => {
|
||||
return [TYPES.MSG_IMAGE, TYPES.MSG_VIDEO, TYPES.MSG_MERGER].includes(message.value.type);
|
||||
});
|
||||
return [TYPES.MSG_IMAGE, TYPES.MSG_VIDEO, TYPES.MSG_MERGER].includes(
|
||||
message.value.type
|
||||
)
|
||||
})
|
||||
|
||||
const riskContentText = computed<string>(() => {
|
||||
let content = TUITranslateService.t('TUIChat.涉及敏感内容') + ', ';
|
||||
let content = TUITranslateService.t('TUIChat.涉及敏感内容') + ', '
|
||||
if (message.value.flow === 'out') {
|
||||
content += TUITranslateService.t('TUIChat.发送失败');
|
||||
content += TUITranslateService.t('TUIChat.发送失败')
|
||||
} else {
|
||||
content += TUITranslateService.t(
|
||||
message.value.type === TYPES.MSG_AUDIO ? 'TUIChat.无法收听' : 'TUIChat.无法查看',
|
||||
);
|
||||
message.value.type === TYPES.MSG_AUDIO
|
||||
? 'TUIChat.无法收听'
|
||||
: 'TUIChat.无法查看'
|
||||
)
|
||||
}
|
||||
return content;
|
||||
});
|
||||
return content
|
||||
})
|
||||
|
||||
const isBlink = computed(() => {
|
||||
if (message.value?.ID) {
|
||||
return blinkMessageIDList?.value?.includes(message.value.ID);
|
||||
return blinkMessageIDList?.value?.includes(message.value.ID)
|
||||
}
|
||||
return false;
|
||||
});
|
||||
return false
|
||||
})
|
||||
|
||||
function toggleMultipleSelect(isSelected: boolean) {
|
||||
emits('changeSelectMessageIDList', {
|
||||
type: isSelected ? 'add' : 'remove',
|
||||
messageID: message.value.ID,
|
||||
});
|
||||
messageID: message.value.ID
|
||||
})
|
||||
}
|
||||
|
||||
function resendMessage() {
|
||||
if (!message.value?.hasRiskContent) {
|
||||
emits('resendMessage');
|
||||
emits('resendMessage')
|
||||
}
|
||||
}
|
||||
|
||||
function blinkMessage(messageID: string) {
|
||||
emits('blinkMessage', messageID);
|
||||
emits('blinkMessage', messageID)
|
||||
}
|
||||
|
||||
function scrollTo(scrollHeight: number) {
|
||||
emits('scrollTo', scrollHeight);
|
||||
emits('scrollTo', scrollHeight)
|
||||
}
|
||||
|
||||
function openReadUserPanel() {
|
||||
emits('setReadReceiptPanelVisible', true, message.value);
|
||||
emits('setReadReceiptPanelVisible', true, message.value)
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -344,9 +370,9 @@ function openReadUserPanel() {
|
||||
flex-direction: column;
|
||||
min-width: 0;
|
||||
box-sizing: border-box;
|
||||
padding: 12px;
|
||||
padding: 16rpx 20rpx;
|
||||
font-size: 14px;
|
||||
color: #000;
|
||||
color: #333;
|
||||
letter-spacing: 0;
|
||||
word-wrap: break-word;
|
||||
word-break: break-all;
|
||||
@@ -380,12 +406,12 @@ function openReadUserPanel() {
|
||||
}
|
||||
|
||||
.content-in {
|
||||
background: #fbfbfb;
|
||||
background: #f4f4f4;
|
||||
border-radius: 0 10px 10px;
|
||||
}
|
||||
|
||||
.content-out {
|
||||
background: #dceafd;
|
||||
background: #00D9C5;
|
||||
border-radius: 10px 0 10px 10px;
|
||||
}
|
||||
|
||||
|
||||
@@ -9,3 +9,11 @@
|
||||
box-sizing: border-box;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.tui-message-list {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.message-li {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
@@ -89,7 +89,12 @@ export const sendMessages = async (
|
||||
options.payload.atUserList = content.payload.atUserList;
|
||||
await TUIChatService.sendTextAtMessage(options, sendMessageOptions);
|
||||
} else {
|
||||
try {
|
||||
await TUIChatService.sendTextMessage(options, sendMessageOptions);
|
||||
} catch (err) {
|
||||
console.log('发送失败,对方不是你的好友')
|
||||
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'image':
|
||||
|
||||
@@ -1,28 +1,34 @@
|
||||
<template>
|
||||
<div
|
||||
v-if="typeof contactInfoData === 'object' && Object.keys(contactInfoData).length"
|
||||
v-if="
|
||||
typeof contactInfoData === 'object' &&
|
||||
Object.keys(contactInfoData).length
|
||||
"
|
||||
:class="['tui-contact-info', !isPC && 'tui-contact-info-h5']"
|
||||
>
|
||||
<Navigation>
|
||||
<template #left>
|
||||
<div @click="resetContactSearchingUIData">
|
||||
<Icon
|
||||
:file="backSVG"
|
||||
/>
|
||||
<Icon :file="backSVG" />
|
||||
</div>
|
||||
</template>
|
||||
</Navigation>
|
||||
<div :class="['tui-contact-info-basic', !isPC && 'tui-contact-info-h5-basic']">
|
||||
<div
|
||||
:class="[
|
||||
'tui-contact-info-basic',
|
||||
!isPC && 'tui-contact-info-h5-basic'
|
||||
]"
|
||||
>
|
||||
<div
|
||||
:class="[
|
||||
'tui-contact-info-basic-text',
|
||||
!isPC && 'tui-contact-info-h5-basic-text',
|
||||
!isPC && 'tui-contact-info-h5-basic-text'
|
||||
]"
|
||||
>
|
||||
<div
|
||||
:class="[
|
||||
'tui-contact-info-basic-text-name',
|
||||
!isPC && 'tui-contact-info-h5-basic-text-name',
|
||||
!isPC && 'tui-contact-info-h5-basic-text-name'
|
||||
]"
|
||||
>
|
||||
{{ generateContactInfoName(contactInfoData) }}
|
||||
@@ -32,7 +38,7 @@
|
||||
:key="item.label"
|
||||
:class="[
|
||||
'tui-contact-info-basic-text-other',
|
||||
!isPC && 'tui-contact-info-h5-basic-text-other',
|
||||
!isPC && 'tui-contact-info-h5-basic-text-other'
|
||||
]"
|
||||
>
|
||||
{{
|
||||
@@ -44,14 +50,17 @@
|
||||
<img
|
||||
:class="[
|
||||
'tui-contact-info-basic-avatar',
|
||||
!isPC && 'tui-contact-info-h5-basic-avatar',
|
||||
!isPC && 'tui-contact-info-h5-basic-avatar'
|
||||
]"
|
||||
:src="generateAvatar(contactInfoData)"
|
||||
>
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-if="contactInfoMoreList[0]"
|
||||
:class="['tui-contact-info-more', !isPC && 'tui-contact-info-h5-more']"
|
||||
:class="[
|
||||
'tui-contact-info-more',
|
||||
!isPC && 'tui-contact-info-h5-more'
|
||||
]"
|
||||
>
|
||||
<div
|
||||
v-for="item in contactInfoMoreList"
|
||||
@@ -61,13 +70,13 @@
|
||||
!isPC && 'tui-contact-info-h5-more-item',
|
||||
item.labelPosition === CONTACT_INFO_LABEL_POSITION.TOP
|
||||
? 'tui-contact-info-more-item-top'
|
||||
: 'tui-contact-info-more-item-left',
|
||||
: 'tui-contact-info-more-item-left'
|
||||
]"
|
||||
>
|
||||
<div
|
||||
:class="[
|
||||
'tui-contact-info-more-item-label',
|
||||
!isPC && 'tui-contact-info-h5-more-item-label',
|
||||
!isPC && 'tui-contact-info-h5-more-item-label'
|
||||
]"
|
||||
>
|
||||
{{ `${TUITranslateService.t(`TUIContact.${item.label}`)}` }}
|
||||
@@ -75,20 +84,20 @@
|
||||
<div
|
||||
:class="[
|
||||
'tui-contact-info-more-item-content',
|
||||
!isPC && 'tui-contact-info-h5-more-item-content',
|
||||
!isPC && 'tui-contact-info-h5-more-item-content'
|
||||
]"
|
||||
>
|
||||
<div
|
||||
v-if="!item.editing"
|
||||
:class="[
|
||||
'tui-contact-info-more-item-content-text',
|
||||
!isPC && 'tui-contact-info-h5-more-item-content-text',
|
||||
!isPC && 'tui-contact-info-h5-more-item-content-text'
|
||||
]"
|
||||
>
|
||||
<div
|
||||
:class="[
|
||||
'tui-contact-info-more-item-content-text-data',
|
||||
!isPC && 'tui-contact-info-h5-more-item-content-text-data',
|
||||
!isPC && 'tui-contact-info-h5-more-item-content-text-data'
|
||||
]"
|
||||
>
|
||||
{{ item.data }}
|
||||
@@ -97,39 +106,41 @@
|
||||
v-if="item.editable"
|
||||
:class="[
|
||||
'tui-contact-info-more-item-content-text-icon',
|
||||
!isPC && 'tui-contact-info-h5-more-item-content-text-icon',
|
||||
!isPC && 'tui-contact-info-h5-more-item-content-text-icon'
|
||||
]"
|
||||
@click="setEditing(item)"
|
||||
>
|
||||
<Icon
|
||||
:file="editSVG"
|
||||
width="14px"
|
||||
height="14px"
|
||||
/>
|
||||
<Icon :file="editSVG" width="14px" height="14px" />
|
||||
</div>
|
||||
</div>
|
||||
<input
|
||||
v-else-if="item.editType === CONTACT_INFO_MORE_EDIT_TYPE.INPUT"
|
||||
v-else-if="
|
||||
item.editType === CONTACT_INFO_MORE_EDIT_TYPE.INPUT
|
||||
"
|
||||
v-model="item.data"
|
||||
:class="[
|
||||
'tui-contact-info-more-item-content-input',
|
||||
!isPC && 'tui-contact-info-h5-more-item-content-input',
|
||||
!isPC && 'tui-contact-info-h5-more-item-content-input'
|
||||
]"
|
||||
type="text"
|
||||
@confirm="onContactInfoEmitSubmit(item)"
|
||||
@keyup.enter="onContactInfoEmitSubmit(item)"
|
||||
>
|
||||
/>
|
||||
<textarea
|
||||
v-else-if="item.editType === CONTACT_INFO_MORE_EDIT_TYPE.TEXTAREA"
|
||||
v-else-if="
|
||||
item.editType === CONTACT_INFO_MORE_EDIT_TYPE.TEXTAREA
|
||||
"
|
||||
v-model="item.data"
|
||||
:class="[
|
||||
'tui-contact-info-more-item-content-textarea',
|
||||
!isPC && 'tui-contact-info-h5-more-item-content-textarea',
|
||||
!isPC && 'tui-contact-info-h5-more-item-content-textarea'
|
||||
]"
|
||||
confirm-type="done"
|
||||
/>
|
||||
<div
|
||||
v-else-if="item.editType === CONTACT_INFO_MORE_EDIT_TYPE.SWITCH"
|
||||
v-else-if="
|
||||
item.editType === CONTACT_INFO_MORE_EDIT_TYPE.SWITCH
|
||||
"
|
||||
@click="onContactInfoEmitSubmit(item)"
|
||||
>
|
||||
<SwitchBar :value="item.data" />
|
||||
@@ -140,7 +151,7 @@
|
||||
<div
|
||||
:class="[
|
||||
'tui-contact-info-button',
|
||||
!isPC && 'tui-contact-info-h5-button',
|
||||
!isPC && 'tui-contact-info-h5-button'
|
||||
]"
|
||||
>
|
||||
<button
|
||||
@@ -151,7 +162,7 @@
|
||||
!isPC && 'tui-contact-info-h5-button-item',
|
||||
item.type === CONTACT_INFO_BUTTON_TYPE.CANCEL
|
||||
? `tui-contact-info-button-item-cancel`
|
||||
: `tui-contact-info-button-item-submit`,
|
||||
: `tui-contact-info-button-item-submit`
|
||||
]"
|
||||
@click="onContactInfoButtonClicked(item)"
|
||||
>
|
||||
@@ -168,256 +179,332 @@ import TUIChatEngine, {
|
||||
IGroupModel,
|
||||
Friend,
|
||||
FriendApplication,
|
||||
} from '@tencentcloud/chat-uikit-engine-lite';
|
||||
import { TUIGlobal } from '@tencentcloud/universal-api';
|
||||
import { ref, computed, onMounted, onUnmounted } from '../../../adapter-vue';
|
||||
import { isPC } from '../../../utils/env';
|
||||
TUIUserService
|
||||
} from '@tencentcloud/chat-uikit-engine-lite'
|
||||
import { TUIGlobal } from '@tencentcloud/universal-api'
|
||||
import {
|
||||
ref,
|
||||
computed,
|
||||
onMounted,
|
||||
onUnmounted
|
||||
} from '../../../adapter-vue'
|
||||
import { isPC } from '../../../utils/env'
|
||||
|
||||
import {
|
||||
generateAvatar,
|
||||
generateContactInfoName,
|
||||
generateContactInfoBasic,
|
||||
isFriend,
|
||||
isApplicationType,
|
||||
} from '../utils/index';
|
||||
isApplicationType
|
||||
} from '../utils/index'
|
||||
import {
|
||||
contactMoreInfoConfig,
|
||||
contactButtonConfig,
|
||||
} from './contact-info-config';
|
||||
import Navigation from '../../common/Navigation/index.vue';
|
||||
import Icon from '../../common/Icon.vue';
|
||||
import editSVG from '../../../assets/icon/edit.svg';
|
||||
import backSVG from '../../../assets/icon/back.svg';
|
||||
import SwitchBar from '../../common/SwitchBar/index.vue';
|
||||
contactButtonConfig
|
||||
} from './contact-info-config'
|
||||
import Navigation from '../../common/Navigation/index.vue'
|
||||
import Icon from '../../common/Icon.vue'
|
||||
import editSVG from '../../../assets/icon/edit.svg'
|
||||
import backSVG from '../../../assets/icon/back.svg'
|
||||
import SwitchBar from '../../common/SwitchBar/index.vue'
|
||||
import {
|
||||
IBlackListUserItem,
|
||||
IContactInfoMoreItem,
|
||||
IContactInfoButton,
|
||||
} from '../../../interface';
|
||||
IContactInfoButton
|
||||
} from '../../../interface'
|
||||
import {
|
||||
CONTACT_INFO_LABEL_POSITION,
|
||||
CONTACT_INFO_MORE_EDIT_TYPE,
|
||||
CONTACT_INFO_BUTTON_TYPE,
|
||||
CONTACT_INFO_TITLE,
|
||||
} from '../../../constant';
|
||||
import { deepCopy } from '../../TUIChat/utils/utils';
|
||||
CONTACT_INFO_TITLE
|
||||
} from '../../../constant'
|
||||
import { deepCopy } from '../../TUIChat/utils/utils'
|
||||
import { useUI } from '../../../../utils/use-ui'
|
||||
|
||||
type IContactInfoType = IGroupModel | Friend | FriendApplication | IBlackListUserItem;
|
||||
type IContactInfoType =
|
||||
| IGroupModel
|
||||
| Friend
|
||||
| FriendApplication
|
||||
| IBlackListUserItem
|
||||
|
||||
const emits = defineEmits(['switchConversation']);
|
||||
const { showLoading, hideLoading } = useUI()
|
||||
|
||||
const contactInfoData = ref<IContactInfoType>({} as IContactInfoType);
|
||||
const contactInfoBasicList = ref<Array<{ label: string; data: string }>>([]);
|
||||
const contactInfoMoreList = ref<IContactInfoMoreItem[]>([]);
|
||||
const contactInfoButtonList = ref<IContactInfoButton[]>([]);
|
||||
const contactInfoTitle = ref<string>('');
|
||||
const emits = defineEmits(['switchConversation'])
|
||||
|
||||
const contactInfoData = ref<IContactInfoType>({} as IContactInfoType)
|
||||
const contactInfoBasicList = ref<
|
||||
Array<{ label: string; data: string }>
|
||||
>([])
|
||||
const contactInfoMoreList = ref<IContactInfoMoreItem[]>([])
|
||||
const contactInfoButtonList = ref<IContactInfoButton[]>([])
|
||||
const contactInfoTitle = ref<string>('')
|
||||
|
||||
const setEditing = (item: any) => {
|
||||
item.editing = true;
|
||||
};
|
||||
item.editing = true
|
||||
}
|
||||
|
||||
const isGroup = computed((): boolean =>
|
||||
(contactInfoData.value as IGroupModel)?.groupID ? true : false,
|
||||
);
|
||||
(contactInfoData.value as IGroupModel)?.groupID ? true : false
|
||||
)
|
||||
|
||||
const isApplication = computed((): boolean => {
|
||||
return isApplicationType(contactInfoData?.value);
|
||||
});
|
||||
return isApplicationType(contactInfoData?.value)
|
||||
})
|
||||
|
||||
// is both friend, if is group type always false
|
||||
const isBothFriend = ref<boolean>(false);
|
||||
const isBothFriend = ref<boolean>(false)
|
||||
|
||||
// is group member, including ordinary member, admin, group owner
|
||||
const isGroupMember = computed((): boolean => {
|
||||
return (contactInfoData.value as IGroupModel)?.selfInfo?.userID ? true : false;
|
||||
});
|
||||
return (contactInfoData.value as IGroupModel)?.selfInfo?.userID
|
||||
? true
|
||||
: false
|
||||
})
|
||||
|
||||
// is in black list, if is group type always false
|
||||
const isInBlackList = computed((): boolean => {
|
||||
return (
|
||||
!isGroup.value
|
||||
&& blackList.value?.findIndex(
|
||||
!isGroup.value &&
|
||||
blackList.value?.findIndex(
|
||||
(item: IBlackListUserItem) =>
|
||||
item?.userID === (contactInfoData.value as IBlackListUserItem)?.userID,
|
||||
item?.userID ===
|
||||
(contactInfoData.value as IBlackListUserItem)?.userID
|
||||
) >= 0
|
||||
);
|
||||
});
|
||||
)
|
||||
})
|
||||
|
||||
const blackList = ref<IBlackListUserItem[]>([]);
|
||||
const blackList = ref<IBlackListUserItem[]>([])
|
||||
|
||||
onMounted(() => {
|
||||
TUIStore.watch(StoreName.CUSTOM, {
|
||||
currentContactInfo: onCurrentContactInfoUpdated,
|
||||
currentContactListKey: onCurrentContactListKeyUpdated,
|
||||
});
|
||||
currentContactListKey: onCurrentContactListKeyUpdated
|
||||
})
|
||||
TUIStore.watch(StoreName.USER, {
|
||||
userBlacklist: onUserBlacklistUpdated,
|
||||
});
|
||||
});
|
||||
userBlacklist: onUserBlacklistUpdated
|
||||
})
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
TUIStore.unwatch(StoreName.CUSTOM, {
|
||||
currentContactInfo: onCurrentContactInfoUpdated,
|
||||
currentContactListKey: onCurrentContactListKeyUpdated,
|
||||
});
|
||||
currentContactListKey: onCurrentContactListKeyUpdated
|
||||
})
|
||||
TUIStore.unwatch(StoreName.USER, {
|
||||
userBlacklist: onUserBlacklistUpdated,
|
||||
});
|
||||
});
|
||||
userBlacklist: onUserBlacklistUpdated
|
||||
})
|
||||
})
|
||||
const onCurrentContactListKeyUpdated = (key: string) => {
|
||||
if (CONTACT_INFO_TITLE[key]) {
|
||||
contactInfoTitle.value = TUITranslateService.t(`TUIContact.${CONTACT_INFO_TITLE[key]}`);
|
||||
contactInfoTitle.value = TUITranslateService.t(
|
||||
`TUIContact.${CONTACT_INFO_TITLE[key]}`
|
||||
)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const resetContactInfoUIData = () => {
|
||||
contactInfoData.value = {} as IContactInfoType;
|
||||
contactInfoBasicList.value = [];
|
||||
contactInfoMoreList.value = [];
|
||||
contactInfoButtonList.value = [];
|
||||
};
|
||||
contactInfoData.value = {} as IContactInfoType
|
||||
contactInfoBasicList.value = []
|
||||
contactInfoMoreList.value = []
|
||||
contactInfoButtonList.value = []
|
||||
}
|
||||
|
||||
const resetContactSearchingUIData = () => {
|
||||
TUIStore.update(StoreName.CUSTOM, 'currentContactInfo', {});
|
||||
TUIStore.update(StoreName.CUSTOM, 'currentContactSearchingStatus', false);
|
||||
TUIGlobal?.closeSearching && TUIGlobal?.closeSearching();
|
||||
};
|
||||
TUIStore.update(StoreName.CUSTOM, 'currentContactInfo', {})
|
||||
TUIStore.update(
|
||||
StoreName.CUSTOM,
|
||||
'currentContactSearchingStatus',
|
||||
false
|
||||
)
|
||||
TUIGlobal?.closeSearching && TUIGlobal?.closeSearching()
|
||||
}
|
||||
|
||||
const onContactInfoEmitSubmit = (item: any) => {
|
||||
item.editSubmitHandler
|
||||
&& item.editSubmitHandler({
|
||||
if (item.key === 'blackList') {
|
||||
// item.data = true
|
||||
// resetContactSearchingUIData()
|
||||
const userID = (contactInfoData.value as { userID: string }).userID
|
||||
|
||||
if (item.data) {
|
||||
showLoading()
|
||||
TUIUserService.removeFromBlacklist({ userIDList: [userID] })
|
||||
.then(() => {
|
||||
item.data = false
|
||||
})
|
||||
.finally(() => {
|
||||
hideLoading()
|
||||
})
|
||||
} else {
|
||||
showLoading()
|
||||
TUIUserService.addToBlacklist({ userIDList: [userID] })
|
||||
.then(() => {
|
||||
item.data = true
|
||||
})
|
||||
.finally(() => {
|
||||
hideLoading()
|
||||
})
|
||||
}
|
||||
} else {
|
||||
item.editSubmitHandler &&
|
||||
item.editSubmitHandler({
|
||||
item,
|
||||
contactInfoData: contactInfoData.value,
|
||||
isBothFriend: isBothFriend.value,
|
||||
isInBlackList: isInBlackList.value,
|
||||
});
|
||||
};
|
||||
isInBlackList: isInBlackList.value
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const onContactInfoButtonClicked = (item: any) => {
|
||||
item.onClick
|
||||
&& item.onClick({
|
||||
item.onClick &&
|
||||
item.onClick({
|
||||
contactInfoData: contactInfoData.value,
|
||||
contactInfoMoreList: contactInfoMoreList.value,
|
||||
});
|
||||
contactInfoMoreList: contactInfoMoreList.value
|
||||
})
|
||||
if (
|
||||
item.key === 'enterGroupConversation'
|
||||
|| item.key === 'enterC2CConversation'
|
||||
item.key === 'enterGroupConversation' ||
|
||||
item.key === 'enterC2CConversation'
|
||||
) {
|
||||
emits('switchConversation', contactInfoData.value);
|
||||
resetContactSearchingUIData();
|
||||
emits('switchConversation', contactInfoData.value)
|
||||
resetContactSearchingUIData()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const generateMoreInfo = async () => {
|
||||
if (!isApplication.value) {
|
||||
if (
|
||||
(!isGroup.value && !isBothFriend.value && !isInBlackList.value)
|
||||
|| (isGroup.value
|
||||
&& !isGroupMember.value
|
||||
&& (contactInfoData.value as IGroupModel)?.type !== TUIChatEngine?.TYPES?.GRP_AVCHATROOM)
|
||||
(!isGroup.value && !isBothFriend.value && !isInBlackList.value) ||
|
||||
(isGroup.value &&
|
||||
!isGroupMember.value &&
|
||||
(contactInfoData.value as IGroupModel)?.type !==
|
||||
TUIChatEngine?.TYPES?.GRP_AVCHATROOM)
|
||||
) {
|
||||
contactMoreInfoConfig.setWords.data = '';
|
||||
contactInfoMoreList.value.push(contactMoreInfoConfig.setWords);
|
||||
contactMoreInfoConfig.setWords.data = ''
|
||||
contactInfoMoreList.value.push(contactMoreInfoConfig.setWords)
|
||||
}
|
||||
if (!isGroup.value && !isInBlackList.value) {
|
||||
contactMoreInfoConfig.setRemark.data
|
||||
= (contactInfoData.value as Friend)?.remark || '';
|
||||
contactMoreInfoConfig.setRemark.editing = false;
|
||||
contactInfoMoreList.value.push(contactMoreInfoConfig.setRemark);
|
||||
contactMoreInfoConfig.setRemark.data =
|
||||
(contactInfoData.value as Friend)?.remark || ''
|
||||
contactMoreInfoConfig.setRemark.editing = false
|
||||
contactInfoMoreList.value.push(contactMoreInfoConfig.setRemark)
|
||||
}
|
||||
if (!isGroup.value && (isBothFriend.value || isInBlackList.value)) {
|
||||
contactMoreInfoConfig.blackList.data = isInBlackList.value || false;
|
||||
contactInfoMoreList.value.push(contactMoreInfoConfig.blackList);
|
||||
contactMoreInfoConfig.blackList.data =
|
||||
isInBlackList.value || false
|
||||
contactInfoMoreList.value.push(contactMoreInfoConfig.blackList)
|
||||
}
|
||||
} else {
|
||||
contactMoreInfoConfig.displayWords.data
|
||||
= (contactInfoData.value as FriendApplication)?.wording || '';
|
||||
contactInfoMoreList.value.push(contactMoreInfoConfig.displayWords);
|
||||
contactMoreInfoConfig.displayWords.data =
|
||||
(contactInfoData.value as FriendApplication)?.wording || ''
|
||||
contactInfoMoreList.value.push(contactMoreInfoConfig.displayWords)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const generateButton = () => {
|
||||
if (isInBlackList.value) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
if (isApplication.value) {
|
||||
if (
|
||||
(contactInfoData.value as FriendApplication)?.type
|
||||
=== TUIChatEngine?.TYPES?.SNS_APPLICATION_SENT_TO_ME
|
||||
(contactInfoData.value as FriendApplication)?.type ===
|
||||
TUIChatEngine?.TYPES?.SNS_APPLICATION_SENT_TO_ME
|
||||
) {
|
||||
contactInfoButtonList?.value?.push(
|
||||
contactButtonConfig.refuseFriendApplication,
|
||||
);
|
||||
contactButtonConfig.refuseFriendApplication
|
||||
)
|
||||
contactInfoButtonList?.value?.push(
|
||||
contactButtonConfig.acceptFriendApplication,
|
||||
);
|
||||
contactButtonConfig.acceptFriendApplication
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if (isGroup.value && isGroupMember.value) {
|
||||
switch ((contactInfoData.value as IGroupModel)?.selfInfo?.role) {
|
||||
case 'Owner':
|
||||
contactInfoButtonList?.value?.push(contactButtonConfig.dismissGroup);
|
||||
break;
|
||||
contactInfoButtonList?.value?.push(
|
||||
contactButtonConfig.dismissGroup
|
||||
)
|
||||
break
|
||||
default:
|
||||
contactInfoButtonList?.value?.push(contactButtonConfig.quitGroup);
|
||||
break;
|
||||
contactInfoButtonList?.value?.push(
|
||||
contactButtonConfig.quitGroup
|
||||
)
|
||||
break
|
||||
}
|
||||
contactInfoButtonList?.value?.push(
|
||||
contactButtonConfig.enterGroupConversation,
|
||||
);
|
||||
contactButtonConfig.enterGroupConversation
|
||||
)
|
||||
} else if (!isGroup.value && isBothFriend.value) {
|
||||
contactInfoButtonList?.value?.push(contactButtonConfig.deleteFriend);
|
||||
contactInfoButtonList?.value?.push(
|
||||
contactButtonConfig.enterC2CConversation,
|
||||
);
|
||||
contactButtonConfig.deleteFriend
|
||||
)
|
||||
contactInfoButtonList?.value?.push(
|
||||
contactButtonConfig.enterC2CConversation
|
||||
)
|
||||
} else {
|
||||
if (isGroup.value) {
|
||||
contactInfoButtonList?.value?.push(
|
||||
(contactInfoData.value as IGroupModel)?.type === TUIChatEngine?.TYPES?.GRP_AVCHATROOM
|
||||
(contactInfoData.value as IGroupModel)?.type ===
|
||||
TUIChatEngine?.TYPES?.GRP_AVCHATROOM
|
||||
? contactButtonConfig.joinAVChatGroup
|
||||
: contactButtonConfig.joinGroup,
|
||||
);
|
||||
: contactButtonConfig.joinGroup
|
||||
)
|
||||
} else {
|
||||
contactInfoButtonList?.value?.push(contactButtonConfig.addFriend);
|
||||
contactInfoButtonList?.value?.push(
|
||||
contactButtonConfig.addFriend
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function onUserBlacklistUpdated(userBlacklist: IBlackListUserItem[]) {
|
||||
blackList.value = userBlacklist;
|
||||
blackList.value = userBlacklist
|
||||
}
|
||||
|
||||
async function onCurrentContactInfoUpdated(contactInfo: IContactInfoType) {
|
||||
if (
|
||||
contactInfoData.value
|
||||
&& contactInfo
|
||||
&& JSON.stringify(contactInfoData.value) === JSON.stringify(contactInfo)
|
||||
async function onCurrentContactInfoUpdated(
|
||||
contactInfo: IContactInfoType
|
||||
) {
|
||||
return;
|
||||
if (
|
||||
contactInfoData.value &&
|
||||
contactInfo &&
|
||||
JSON.stringify(contactInfoData.value) ===
|
||||
JSON.stringify(contactInfo)
|
||||
) {
|
||||
return
|
||||
}
|
||||
resetContactInfoUIData();
|
||||
resetContactInfoUIData()
|
||||
// deep clone
|
||||
contactInfoData.value = deepCopy(contactInfo) || {};
|
||||
if (!contactInfoData.value || Object.keys(contactInfoData.value)?.length === 0) {
|
||||
return;
|
||||
contactInfoData.value = deepCopy(contactInfo) || {}
|
||||
if (
|
||||
!contactInfoData.value ||
|
||||
Object.keys(contactInfoData.value)?.length === 0
|
||||
) {
|
||||
return
|
||||
}
|
||||
contactInfoBasicList.value = generateContactInfoBasic(
|
||||
contactInfoData.value,
|
||||
);
|
||||
isBothFriend.value = await isFriend(contactInfoData.value);
|
||||
generateMoreInfo();
|
||||
generateButton();
|
||||
contactInfoData.value
|
||||
)
|
||||
isBothFriend.value = await isFriend(contactInfoData.value)
|
||||
generateMoreInfo()
|
||||
generateButton()
|
||||
if (contactInfo.infoKeyList) {
|
||||
contactInfoMoreList.value = contactInfo.infoKeyList.map((key: string) => {
|
||||
return (contactMoreInfoConfig as any)[key];
|
||||
});
|
||||
contactInfoMoreList.value = contactInfo.infoKeyList.map(
|
||||
(key: string) => {
|
||||
return (contactMoreInfoConfig as any)[key]
|
||||
}
|
||||
)
|
||||
}
|
||||
if (contactInfo.btnKeyList) {
|
||||
contactInfoButtonList.value = contactInfo.btnKeyList.map((key: string) => {
|
||||
return (contactButtonConfig as any)[key];
|
||||
});
|
||||
contactInfoButtonList.value = contactInfo.btnKeyList.map(
|
||||
(key: string) => {
|
||||
return (contactButtonConfig as any)[key]
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped src="./style/index.scss"></style>
|
||||
<style lang="scss" scoped>
|
||||
.tui-contact-info-basic-avatar {
|
||||
border-radius: 100rpx;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
<template>
|
||||
<div
|
||||
<view>
|
||||
<view
|
||||
v-if="Object.keys(friendListData.map).length > 0"
|
||||
class="friend-list"
|
||||
>
|
||||
@@ -22,7 +23,12 @@
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</view>
|
||||
<cb-empty
|
||||
v-if="Object.keys(friendListData.map).length === 0"
|
||||
name="您还没有好友"
|
||||
></cb-empty>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
@@ -83,7 +89,7 @@
|
||||
|
||||
.friend-group-title {
|
||||
padding: 8px 16px;
|
||||
background-color: #f8f9fa;
|
||||
background-color: #ffffff;
|
||||
font-size: 14px;
|
||||
font-weight: 500;
|
||||
color: #666;
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
<div class="tui-contact-list-card-left">
|
||||
<Avatar
|
||||
useSkeletonAnimation
|
||||
size="30px"
|
||||
size="62rpx"
|
||||
:url="generateAvatar(props.item)"
|
||||
/>
|
||||
<div
|
||||
|
||||
@@ -3,9 +3,9 @@
|
||||
v-if="!contactSearchingStatus"
|
||||
:class="['tui-contact-list', !isPC && 'tui-contact-list-h5']"
|
||||
>
|
||||
<div v-if="!currentContactListKey">
|
||||
<ul>
|
||||
<li
|
||||
<view v-if="!currentContactListKey">
|
||||
<view class="top-list_box">
|
||||
<view
|
||||
v-for="(contactListObj, key) in contactListMap"
|
||||
:key="key"
|
||||
class="tui-contact-list-item"
|
||||
@@ -14,11 +14,11 @@
|
||||
class="tui-contact-list-item-header"
|
||||
@click="toggleCurrentContactList(key)"
|
||||
>
|
||||
<div class="tui-contact-list-item-header-left">
|
||||
<view class="tui-contact-list-item-header-left">
|
||||
<Icon
|
||||
v-if="contactListObj.icon"
|
||||
:file="contactListObj.icon"
|
||||
size="30px"
|
||||
size="96rpx"
|
||||
/>
|
||||
<span
|
||||
v-if="contactListObj.unreadCount"
|
||||
@@ -26,25 +26,21 @@
|
||||
>
|
||||
{{ contactListObj.unreadCount }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="tui-contact-list-item-header-right">
|
||||
<div>
|
||||
{{
|
||||
TUITranslateService.t(
|
||||
`TUIContact.${contactListObj.title}`
|
||||
)
|
||||
}}
|
||||
</div>
|
||||
<Icon
|
||||
</view>
|
||||
<view class="tui-contact-list-item-header-right">
|
||||
<text>
|
||||
{{ contactListObj.title }}
|
||||
</text>
|
||||
<!-- <Icon
|
||||
:file="currentContactListKey === key ? downSVG : rightSVG"
|
||||
size="20px"
|
||||
/>
|
||||
</div>
|
||||
/> -->
|
||||
</view>
|
||||
</header>
|
||||
</li>
|
||||
</ul>
|
||||
</view>
|
||||
</view>
|
||||
<FriendList @enterConversation="selectFriend" />
|
||||
</div>
|
||||
</view>
|
||||
<template v-else>
|
||||
<li
|
||||
v-for="contactListItem in contactListMap[currentContactListKey]
|
||||
@@ -119,9 +115,10 @@
|
||||
|
||||
import downSVG from '../../../assets/icon/down-icon.svg'
|
||||
import rightSVG from '../../../assets/icon/right-icon.svg'
|
||||
import newContactsSVG from '../../../assets/icon/new-contacts.svg'
|
||||
import groupSVG from '../../../assets/icon/groups.svg'
|
||||
import blackListSVG from '../../../assets/icon/black-list.svg'
|
||||
import newContactsSVG from '../../../assets/icon/friend-request-icon.svg'
|
||||
import groupSVG from '../../../assets/icon/my-group-chat.svg'
|
||||
import blackListSVG from '../../../assets/icon/blacklist-icon.svg'
|
||||
import addFrienIcon from '../../../assets/icon/add-frien-icon.svg'
|
||||
|
||||
import type {
|
||||
IContactList,
|
||||
@@ -143,10 +140,16 @@
|
||||
friendApplicationList: {
|
||||
icon: newContactsSVG,
|
||||
key: 'friendApplicationList',
|
||||
title: '新的联系人',
|
||||
title: '好友请求',
|
||||
list: [] as FriendApplication[],
|
||||
unreadCount: 0
|
||||
},
|
||||
currentContactSearchingStatus: {
|
||||
icon: addFrienIcon,
|
||||
key: 'currentContactSearchingStatus',
|
||||
title: '添加好友',
|
||||
list: [] as IGroupModel[]
|
||||
},
|
||||
groupList: {
|
||||
icon: groupSVG,
|
||||
key: 'groupList',
|
||||
@@ -233,6 +236,10 @@
|
||||
})
|
||||
|
||||
function toggleCurrentContactList(key: keyof IContactList) {
|
||||
if (key === 'currentContactSearchingStatus') {
|
||||
TUIStore.update(StoreName.CUSTOM, key, true)
|
||||
return
|
||||
}
|
||||
if (currentContactListKey.value === key) {
|
||||
currentContactListKey.value = ''
|
||||
currentContactInfo.value = {} as IContactInfoType
|
||||
@@ -465,4 +472,31 @@
|
||||
height: 100% !important;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.tui-contact-search-list-title {
|
||||
padding: 16rpx 0;
|
||||
}
|
||||
|
||||
.tui-contact-list-item-header {
|
||||
&::after {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
|
||||
.top-list_box {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
padding: 32rpx 50rpx;
|
||||
.tui-contact-list-item {
|
||||
.tui-contact-list-item-header {
|
||||
padding: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
&:active {
|
||||
background: none !important;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -3,121 +3,117 @@
|
||||
<div
|
||||
:class="[
|
||||
'tui-contact-search-main',
|
||||
!isPC && 'tui-contact-search-h5-main',
|
||||
!isPC && 'tui-contact-search-h5-main'
|
||||
]"
|
||||
>
|
||||
<input
|
||||
v-model="searchValue"
|
||||
class="tui-contact-search-main-input"
|
||||
type="text"
|
||||
:placeholder="searchingPlaceholder"
|
||||
placeholder="请输入用户 / 群组搜索"
|
||||
enterkeyhint="search"
|
||||
@keyup.enter="search"
|
||||
@blur="search"
|
||||
@confirm="search"
|
||||
>
|
||||
<div
|
||||
class="tui-contact-search-main-cancel"
|
||||
@click="cancel"
|
||||
>
|
||||
{{ TUITranslateService.t("取消") }}
|
||||
/>
|
||||
<div class="tui-contact-search-main-cancel" @click="cancel">
|
||||
{{ TUITranslateService.t('取消') }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import { onMounted, ref, watch } from '../../../adapter-vue';
|
||||
import { onMounted, ref, watch } from '../../../adapter-vue'
|
||||
import {
|
||||
TUITranslateService,
|
||||
TUIStore,
|
||||
StoreName,
|
||||
} from '@tencentcloud/chat-uikit-engine-lite';
|
||||
import TUICore, { TUIConstants } from '@tencentcloud/tui-core-lite';
|
||||
import { TUIGlobal } from '@tencentcloud/universal-api';
|
||||
import { isPC } from '../../../utils/env';
|
||||
import { IContactSearchResult } from '../../../interface';
|
||||
StoreName
|
||||
} from '@tencentcloud/chat-uikit-engine-lite'
|
||||
import TUICore, { TUIConstants } from '@tencentcloud/tui-core-lite'
|
||||
import { TUIGlobal } from '@tencentcloud/universal-api'
|
||||
import { isPC } from '../../../utils/env'
|
||||
import { IContactSearchResult } from '../../../interface'
|
||||
|
||||
const searchingPlaceholder = TUITranslateService.t('TUIContact.输入ID');
|
||||
const searchValue = ref<string>('');
|
||||
const searchValue = ref<string>('')
|
||||
const searchResult = ref<IContactSearchResult>({
|
||||
user: {
|
||||
label: '联系人',
|
||||
list: [],
|
||||
list: []
|
||||
},
|
||||
group: {
|
||||
label: '群聊',
|
||||
list: [],
|
||||
},
|
||||
});
|
||||
list: []
|
||||
}
|
||||
})
|
||||
|
||||
const cancel = () => {
|
||||
TUIStore.update(
|
||||
StoreName.CUSTOM,
|
||||
'currentContactSearchingStatus',
|
||||
false,
|
||||
);
|
||||
};
|
||||
false
|
||||
)
|
||||
}
|
||||
|
||||
const search = async () => {
|
||||
if (!searchValue.value) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
TUICore.callService({
|
||||
serviceName: TUIConstants.TUISearch.SERVICE.NAME,
|
||||
method: TUIConstants.TUISearch.SERVICE.METHOD.SEARCH_USER,
|
||||
params: {
|
||||
userID: searchValue.value,
|
||||
},
|
||||
userID: searchValue.value
|
||||
}
|
||||
})
|
||||
.then((res: any) => {
|
||||
searchResult.value.user.list = res.data;
|
||||
searchResult.value.user.list = res.data
|
||||
})
|
||||
.catch((error: any) => {
|
||||
searchResult.value.user.list = [];
|
||||
console.warn('search user error', error);
|
||||
});
|
||||
searchResult.value.user.list = []
|
||||
console.warn('search user error', error)
|
||||
})
|
||||
TUICore.callService({
|
||||
serviceName: TUIConstants.TUISearch.SERVICE.NAME,
|
||||
method: TUIConstants.TUISearch.SERVICE.METHOD.SEARCH_GROUP,
|
||||
params: {
|
||||
groupID: searchValue.value,
|
||||
},
|
||||
groupID: searchValue.value
|
||||
}
|
||||
})
|
||||
.then((res: any) => {
|
||||
searchResult.value.group.list = [res.data.group];
|
||||
searchResult.value.group.list = [res.data.group]
|
||||
})
|
||||
.catch((error: any) => {
|
||||
searchResult.value.group.list = [];
|
||||
console.warn('search group error', error);
|
||||
});
|
||||
};
|
||||
searchResult.value.group.list = []
|
||||
console.warn('search group error', error)
|
||||
})
|
||||
}
|
||||
watch(
|
||||
() => searchResult.value,
|
||||
() => {
|
||||
TUIStore.update(
|
||||
StoreName.CUSTOM,
|
||||
'currentContactSearchResult',
|
||||
searchResult.value,
|
||||
);
|
||||
searchResult.value
|
||||
)
|
||||
},
|
||||
{
|
||||
deep: true,
|
||||
immediate: true,
|
||||
},
|
||||
);
|
||||
immediate: true
|
||||
}
|
||||
)
|
||||
|
||||
onMounted(() => {
|
||||
searchValue.value = '';
|
||||
searchResult.value.user.list = [];
|
||||
searchResult.value.group.list = [];
|
||||
});
|
||||
searchValue.value = ''
|
||||
searchResult.value.user.list = []
|
||||
searchResult.value.group.list = []
|
||||
})
|
||||
|
||||
TUIGlobal.updateContactSearch = search;
|
||||
TUIGlobal.updateContactSearch = search
|
||||
TUIGlobal.closeSearching = () => {
|
||||
searchValue.value = '';
|
||||
searchResult.value.user.list = [];
|
||||
searchResult.value.group.list = [];
|
||||
};
|
||||
searchValue.value = ''
|
||||
searchResult.value.user.list = []
|
||||
searchResult.value.group.list = []
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.tui-contact-search {
|
||||
@@ -174,4 +170,12 @@ TUIGlobal.closeSearching = () => {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.tui-contact-search-main-input {
|
||||
border-radius: 64rpx;
|
||||
height: 64rpx;
|
||||
padding: 0 32rpx;
|
||||
|
||||
background: #f4f4f4;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -4,13 +4,7 @@
|
||||
v-else-if="isShowContactList"
|
||||
:class="['tui-contact', !isPC && 'tui-contact-h5']"
|
||||
>
|
||||
<Navigation
|
||||
:title="
|
||||
currentContactKey
|
||||
? contactInfoTitle
|
||||
: '通讯录'
|
||||
"
|
||||
>
|
||||
<Navigation :title="currentContactKey ? contactInfoTitle : '通讯录'">
|
||||
<template #left>
|
||||
<div v-show="currentContactKey" @click="resetContactType">
|
||||
<Icon :file="backSVG" />
|
||||
@@ -67,7 +61,7 @@
|
||||
import ContactList from './contact-list/index.vue'
|
||||
import ContactInfo from './contact-info/index.vue'
|
||||
|
||||
import addCircle from '../../assets/icon/add-circle.svg'
|
||||
import addCircle from '../../assets/icon/add-friend.svg'
|
||||
import backSVG from '../../assets/icon/back.svg'
|
||||
import { CONTACT_INFO_TITLE } from '../../constant'
|
||||
|
||||
|
||||
@@ -379,4 +379,12 @@
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
.tui-conversation-item {
|
||||
&::after {
|
||||
display: none !important;
|
||||
}
|
||||
&:active {
|
||||
background: #f4f4f4 !important;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -53,19 +53,19 @@
|
||||
<!-- <Icon :file="rightIcon" /> -->
|
||||
</span>
|
||||
</div>
|
||||
<article v-if="!isPC" class="group-h5-list-item-introduction">
|
||||
<!-- <article v-if="!isPC" class="group-h5-list-item-introduction">
|
||||
<label class="introduction-name">
|
||||
{{ groupTypeDetail.label }}:
|
||||
</label>
|
||||
<span class="introduction-detail">
|
||||
{{ groupTypeDetail.detail }}
|
||||
</span>
|
||||
<!-- <a :href="documentLink.product.url" target="view_window">
|
||||
<a :href="documentLink.product.url" target="view_window">
|
||||
{{
|
||||
TUITranslateService.t(`TUIGroup.${groupTypeDetail.src}`)
|
||||
}}
|
||||
</a> -->
|
||||
</article>
|
||||
</a>
|
||||
</article> -->
|
||||
</li>
|
||||
</ul>
|
||||
</ul>
|
||||
@@ -370,6 +370,9 @@
|
||||
</script>
|
||||
<style lang="scss" scoped src="./style/index.scss"></style>
|
||||
<style lang="scss" scoped>
|
||||
.group-list {
|
||||
margin: 0 !important;
|
||||
}
|
||||
.popup-content {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
'tui-search',
|
||||
!isPC && 'tui-search-h5',
|
||||
`tui-search-main-${currentSearchType}`,
|
||||
isFullScreen && 'tui-search-h5-full-screen',
|
||||
isFullScreen && 'tui-search-h5-full-screen'
|
||||
]"
|
||||
>
|
||||
<div
|
||||
@@ -16,7 +16,7 @@
|
||||
<div
|
||||
:class="[
|
||||
'tui-search-global-header',
|
||||
!isPC && 'tui-search-h5-global-header',
|
||||
!isPC && 'tui-search-h5-global-header'
|
||||
]"
|
||||
>
|
||||
<SearchInput
|
||||
@@ -40,12 +40,13 @@
|
||||
</div>
|
||||
<div
|
||||
v-else-if="
|
||||
(currentSearchType === 'conversation' && isShowInConversationSearch) ||
|
||||
(currentSearchType === 'conversation' &&
|
||||
isShowInConversationSearch) ||
|
||||
isUniFrameWork
|
||||
"
|
||||
:class="[
|
||||
'tui-search-conversation',
|
||||
!isPC && 'tui-search-h5-conversation',
|
||||
!isPC && 'tui-search-h5-conversation'
|
||||
]"
|
||||
>
|
||||
<SearchContainer
|
||||
@@ -55,9 +56,7 @@
|
||||
@closeInConversationSearch="closeInConversationSearch"
|
||||
>
|
||||
<template #input>
|
||||
<SearchInput
|
||||
:searchType="currentSearchType"
|
||||
/>
|
||||
<SearchInput :searchType="currentSearchType" />
|
||||
</template>
|
||||
<template #result>
|
||||
<SearchResult
|
||||
@@ -76,143 +75,170 @@ import {
|
||||
computed,
|
||||
withDefaults,
|
||||
onUnmounted,
|
||||
watch,
|
||||
} from '../../adapter-vue';
|
||||
import { TUIStore, StoreName } from '@tencentcloud/chat-uikit-engine-lite';
|
||||
import { TUIGlobal, outsideClick } from '@tencentcloud/universal-api';
|
||||
import SearchInput from './search-input/index.vue';
|
||||
import SearchContainer from './search-container/index.vue';
|
||||
import SearchResult from './search-result/index.vue';
|
||||
import { searchMessageTypeDefault } from './search-type-list';
|
||||
import { searchMessageTimeDefault } from './search-time-list';
|
||||
import { isPC, isUniFrameWork } from '../../utils/env';
|
||||
import { ISearchingStatus, SEARCH_TYPE } from './type';
|
||||
watch
|
||||
} from '../../adapter-vue'
|
||||
import {
|
||||
TUIStore,
|
||||
StoreName
|
||||
} from '@tencentcloud/chat-uikit-engine-lite'
|
||||
import { TUIGlobal, outsideClick } from '@tencentcloud/universal-api'
|
||||
import SearchInput from './search-input/index.vue'
|
||||
import SearchContainer from './search-container/index.vue'
|
||||
import SearchResult from './search-result/index.vue'
|
||||
import { searchMessageTypeDefault } from './search-type-list'
|
||||
import { searchMessageTimeDefault } from './search-time-list'
|
||||
import { isPC, isUniFrameWork } from '../../utils/env'
|
||||
import { ISearchingStatus, SEARCH_TYPE } from './type'
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
searchType?: SEARCH_TYPE;
|
||||
searchType?: SEARCH_TYPE
|
||||
}>(),
|
||||
{
|
||||
searchType: () => {
|
||||
return 'global';
|
||||
},
|
||||
},
|
||||
);
|
||||
return 'global'
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
const globalSearchRef = ref<HTMLElement | null>();
|
||||
const currentConversationID = ref<string>('');
|
||||
const searchingStatus = ref<boolean>(false);
|
||||
const currentSearchType = ref<SEARCH_TYPE>('global');
|
||||
const isShowSearch = ref<boolean>(false);
|
||||
const globalSearchRef = ref<HTMLElement | null>()
|
||||
const currentConversationID = ref<string>('')
|
||||
const searchingStatus = ref<boolean>(false)
|
||||
const currentSearchType = ref<SEARCH_TYPE>('global')
|
||||
const isShowSearch = ref<boolean>(false)
|
||||
// Whether to display the search in the chat
|
||||
const isShowInConversationSearch = ref<boolean>(isUniFrameWork);
|
||||
const isShowInConversationSearch = ref<boolean>(isUniFrameWork)
|
||||
// Whether to search in full screen - Search in full screen when the mobile terminal is searching
|
||||
const isFullScreen = computed(
|
||||
() =>
|
||||
!isPC
|
||||
&& ((currentSearchType.value === 'global' && searchingStatus.value)
|
||||
|| (currentSearchType.value === 'conversation' && isShowInConversationSearch.value)),
|
||||
);
|
||||
!isPC &&
|
||||
((currentSearchType.value === 'global' && searchingStatus.value) ||
|
||||
(currentSearchType.value === 'conversation' &&
|
||||
isShowInConversationSearch.value))
|
||||
)
|
||||
|
||||
watch(() => [currentConversationID.value, isShowInConversationSearch.value], (data) => {
|
||||
watch(
|
||||
() => [currentConversationID.value, isShowInConversationSearch.value],
|
||||
data => {
|
||||
if (isUniFrameWork && data[0]) {
|
||||
currentSearchType.value = 'conversation';
|
||||
currentSearchType.value = 'conversation'
|
||||
} else {
|
||||
currentSearchType.value = props.searchType;
|
||||
currentSearchType.value = props.searchType
|
||||
}
|
||||
isShowSearch.value = currentSearchType.value === 'global'
|
||||
|| ((currentSearchType.value === 'conversation' || (!currentSearchType.value && isUniFrameWork))
|
||||
&& !!data[1]);
|
||||
}, { immediate: true, deep: true });
|
||||
isShowSearch.value =
|
||||
currentSearchType.value === 'global' ||
|
||||
((currentSearchType.value === 'conversation' ||
|
||||
(!currentSearchType.value && isUniFrameWork)) &&
|
||||
!!data[1])
|
||||
},
|
||||
{ immediate: true, deep: true }
|
||||
)
|
||||
|
||||
const initSearchValue = (searchType: SEARCH_TYPE) => {
|
||||
TUIStore.update(StoreName.SEARCH, 'currentSearchInputValue', {
|
||||
value: '',
|
||||
searchType: searchType,
|
||||
});
|
||||
searchType: searchType
|
||||
})
|
||||
TUIStore.update(StoreName.SEARCH, 'currentSearchMessageType', {
|
||||
value: searchMessageTypeDefault[searchType],
|
||||
searchType: searchType,
|
||||
});
|
||||
searchType: searchType
|
||||
})
|
||||
TUIStore.update(StoreName.SEARCH, 'currentSearchMessageTime', {
|
||||
value: searchMessageTimeDefault,
|
||||
searchType: searchType,
|
||||
});
|
||||
searchType: searchType
|
||||
})
|
||||
TUIStore.update(StoreName.SEARCH, 'currentSearchingStatus', {
|
||||
isSearching: false,
|
||||
searchType: currentSearchType.value,
|
||||
});
|
||||
};
|
||||
searchType: currentSearchType.value
|
||||
})
|
||||
}
|
||||
|
||||
function onCurrentConversationIDUpdate(conversationID: string) {
|
||||
if (!isUniFrameWork && currentConversationID.value !== conversationID) {
|
||||
if (
|
||||
!isUniFrameWork &&
|
||||
currentConversationID.value !== conversationID
|
||||
) {
|
||||
// PC side single page switch session, close search
|
||||
closeInConversationSearch();
|
||||
closeInConversationSearch()
|
||||
}
|
||||
if (!conversationID && isUniFrameWork) {
|
||||
initSearchValue('global');
|
||||
initSearchValue('global')
|
||||
}
|
||||
currentConversationID.value = conversationID;
|
||||
currentConversationID.value = conversationID
|
||||
}
|
||||
function onCurrentSearchingStatusChange(value: ISearchingStatus) {
|
||||
if (value?.searchType === currentSearchType.value) {
|
||||
searchingStatus.value = value?.isSearching;
|
||||
searchingStatus.value = value?.isSearching
|
||||
// global search ui bind on click outside close
|
||||
if (value?.searchType === 'global' && globalSearchRef.value) {
|
||||
if (isPC && value.isSearching) {
|
||||
outsideClick.listen({
|
||||
domRefs: globalSearchRef.value,
|
||||
handler: closeGlobalSearch,
|
||||
});
|
||||
handler: closeGlobalSearch
|
||||
})
|
||||
}
|
||||
}
|
||||
if (value?.searchType === 'global' && isUniFrameWork) {
|
||||
// hide tab bar in uni-app when global searching
|
||||
value.isSearching ? TUIGlobal?.hideTabBar()?.catch(() => { /* ignore */ }) : TUIGlobal?.showTabBar()?.catch(() => { /* ignore */ });
|
||||
value.isSearching
|
||||
? TUIGlobal?.hideTabBar()?.catch(() => {
|
||||
/* ignore */
|
||||
})
|
||||
: TUIGlobal?.showTabBar()?.catch(() => {
|
||||
/* ignore */
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function onIsShowInConversationSearchChange(value: boolean) {
|
||||
isShowInConversationSearch.value = value ? true : false;
|
||||
isShowInConversationSearch.value && initSearchValue(currentSearchType.value);
|
||||
isShowInConversationSearch.value = value ? true : false
|
||||
isShowInConversationSearch.value &&
|
||||
initSearchValue(currentSearchType.value)
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
// init with default value
|
||||
['global', 'conversation'].forEach((type: string) => {
|
||||
initSearchValue(type as SEARCH_TYPE);
|
||||
});
|
||||
;['global', 'conversation'].forEach((type: string) => {
|
||||
initSearchValue(type as SEARCH_TYPE)
|
||||
})
|
||||
// watch store change
|
||||
TUIStore.watch(StoreName.CONV, {
|
||||
currentConversationID: onCurrentConversationIDUpdate,
|
||||
});
|
||||
currentConversationID: onCurrentConversationIDUpdate
|
||||
})
|
||||
TUIStore.watch(StoreName.SEARCH, {
|
||||
currentSearchingStatus: onCurrentSearchingStatusChange,
|
||||
isShowInConversationSearch: onIsShowInConversationSearchChange,
|
||||
});
|
||||
});
|
||||
isShowInConversationSearch: onIsShowInConversationSearchChange
|
||||
})
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
// unwatch store change
|
||||
TUIStore.unwatch(StoreName.CONV, {
|
||||
currentConversationID: onCurrentConversationIDUpdate,
|
||||
});
|
||||
currentConversationID: onCurrentConversationIDUpdate
|
||||
})
|
||||
TUIStore.unwatch(StoreName.SEARCH, {
|
||||
currentSearchingStatus: onCurrentSearchingStatusChange,
|
||||
isShowInConversationSearch: onIsShowInConversationSearchChange,
|
||||
});
|
||||
});
|
||||
isShowInConversationSearch: onIsShowInConversationSearchChange
|
||||
})
|
||||
})
|
||||
|
||||
function closeGlobalSearch() {
|
||||
TUIStore.update(StoreName.SEARCH, 'currentSearchingStatus', {
|
||||
isSearching: false,
|
||||
searchType: currentSearchType.value,
|
||||
});
|
||||
searchType: currentSearchType.value
|
||||
})
|
||||
}
|
||||
|
||||
function closeInConversationSearch() {
|
||||
TUIStore.update(StoreName.SEARCH, 'isShowInConversationSearch', false);
|
||||
TUIStore.update(StoreName.SEARCH, 'isShowInConversationSearch', false)
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped src="./style/index.scss"></style>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.tui-search-global {
|
||||
// background: red;
|
||||
padding: 0 !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -12,10 +12,7 @@
|
||||
v-if="!searchingStatus && props.searchType === 'global'"
|
||||
:class="['tui-search-input', !isPC && 'tui-search-input-h5']"
|
||||
>
|
||||
<div
|
||||
class="tui-search-input-place"
|
||||
@click="onSearchInputClick"
|
||||
>
|
||||
<div class="tui-search-input-place" @click="onSearchInputClick">
|
||||
<Icon
|
||||
class="icon"
|
||||
:file="searchIcon"
|
||||
@@ -47,123 +44,118 @@
|
||||
@blur="onBlur"
|
||||
@keyup.enter="search"
|
||||
@confirm="search"
|
||||
>
|
||||
/>
|
||||
<div
|
||||
v-if="searchingStatus"
|
||||
class="tui-search-input-right"
|
||||
@click="endSearching"
|
||||
>
|
||||
<Icon
|
||||
class="icon"
|
||||
:file="closeIcon"
|
||||
width="14px"
|
||||
height="14px"
|
||||
/>
|
||||
<Icon class="icon" :file="closeIcon" width="14px" height="14px" />
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
v-if="!isPC && searchingStatus && props.searchType === 'global'"
|
||||
:class="[
|
||||
'tui-search-input-cancel',
|
||||
!isPC && 'tui-search-input-h5-cancel',
|
||||
!isPC && 'tui-search-input-h5-cancel'
|
||||
]"
|
||||
@click="endSearching"
|
||||
>
|
||||
{{ TUITranslateService.t("TUISearch.取消") }}
|
||||
{{ TUITranslateService.t('TUISearch.取消') }}
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted, onUnmounted } from '../../../adapter-vue';
|
||||
import { ref, onMounted, onUnmounted } from '../../../adapter-vue'
|
||||
import {
|
||||
TUIStore,
|
||||
StoreName,
|
||||
TUITranslateService,
|
||||
} from '@tencentcloud/chat-uikit-engine-lite';
|
||||
import { TUIGlobal } from '@tencentcloud/universal-api';
|
||||
import Icon from '../../common/Icon.vue';
|
||||
import searchIcon from '../../../assets/icon/search.svg';
|
||||
import closeIcon from '../../../assets/icon/input-close.svg';
|
||||
import { isPC } from '../../../utils/env';
|
||||
import { ISearchInputValue, ISearchingStatus } from '../type';
|
||||
TUITranslateService
|
||||
} from '@tencentcloud/chat-uikit-engine-lite'
|
||||
import { TUIGlobal } from '@tencentcloud/universal-api'
|
||||
import Icon from '../../common/Icon.vue'
|
||||
import searchIcon from '../../../assets/icon/search.svg'
|
||||
import closeIcon from '../../../assets/icon/input-close.svg'
|
||||
import { isPC } from '../../../utils/env'
|
||||
import { ISearchInputValue, ISearchingStatus } from '../type'
|
||||
const props = defineProps({
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: () => TUITranslateService.t('TUISearch.搜索'),
|
||||
default: () => TUITranslateService.t('TUISearch.搜索')
|
||||
},
|
||||
searchType: {
|
||||
type: String,
|
||||
default: 'global', // "global" / "conversation"
|
||||
validator(value: string) {
|
||||
return ['global', 'conversation'].includes(value);
|
||||
},
|
||||
},
|
||||
});
|
||||
return ['global', 'conversation'].includes(value)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
const searchValueModel = ref<string>('');
|
||||
const currentSearchInputValue = ref<string>('');
|
||||
const searchingStatus = ref<boolean>(false);
|
||||
const searchValueModel = ref<string>('')
|
||||
const currentSearchInputValue = ref<string>('')
|
||||
const searchingStatus = ref<boolean>(false)
|
||||
|
||||
function onCurrentSearchInputValueChange(obj: ISearchInputValue) {
|
||||
if (obj?.searchType === props?.searchType) {
|
||||
currentSearchInputValue.value = obj?.value;
|
||||
searchValueModel.value = obj?.value;
|
||||
currentSearchInputValue.value = obj?.value
|
||||
searchValueModel.value = obj?.value
|
||||
}
|
||||
}
|
||||
|
||||
function onCurrentSearchingStatusChange(obj: ISearchingStatus) {
|
||||
if (obj?.searchType === props?.searchType) {
|
||||
searchingStatus.value = obj?.isSearching;
|
||||
searchingStatus.value = obj?.isSearching
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
TUIStore.watch(StoreName.SEARCH, {
|
||||
currentSearchInputValue: onCurrentSearchInputValueChange,
|
||||
currentSearchingStatus: onCurrentSearchingStatusChange,
|
||||
});
|
||||
});
|
||||
currentSearchingStatus: onCurrentSearchingStatusChange
|
||||
})
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
TUIStore.unwatch(StoreName.SEARCH, {
|
||||
currentSearchInputValue: onCurrentSearchInputValueChange,
|
||||
currentSearchingStatus: onCurrentSearchingStatusChange,
|
||||
});
|
||||
});
|
||||
currentSearchingStatus: onCurrentSearchingStatusChange
|
||||
})
|
||||
})
|
||||
|
||||
const search = () => {
|
||||
// Avoid duplicate searches
|
||||
if (searchValueModel.value === currentSearchInputValue.value) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
TUIStore.update(StoreName.SEARCH, 'currentSearchInputValue', {
|
||||
value: searchValueModel.value,
|
||||
searchType: props.searchType,
|
||||
});
|
||||
};
|
||||
searchType: props.searchType
|
||||
})
|
||||
}
|
||||
|
||||
const endSearching = () => {
|
||||
searchingStatus.value = false;
|
||||
searchingStatus.value = false
|
||||
TUIStore.update(StoreName.SEARCH, 'currentSearchingStatus', {
|
||||
isSearching: false,
|
||||
searchType: props.searchType,
|
||||
});
|
||||
searchType: props.searchType
|
||||
})
|
||||
TUIStore.update(StoreName.SEARCH, 'currentSearchInputValue', {
|
||||
value: '',
|
||||
searchType: props.searchType,
|
||||
});
|
||||
};
|
||||
searchType: props.searchType
|
||||
})
|
||||
}
|
||||
|
||||
const onSearchInputClick = () => {
|
||||
TUIStore.update(StoreName.SEARCH, 'currentSearchingStatus', {
|
||||
isSearching: true,
|
||||
searchType: props.searchType,
|
||||
});
|
||||
};
|
||||
searchType: props.searchType
|
||||
})
|
||||
}
|
||||
|
||||
const onBlur = () => {
|
||||
TUIGlobal?.hideKeyboard?.();
|
||||
};
|
||||
TUIGlobal?.hideKeyboard?.()
|
||||
}
|
||||
</script>
|
||||
<style lang="scss" scoped>
|
||||
.tui-search-input-container {
|
||||
@@ -172,7 +164,6 @@ const onBlur = () => {
|
||||
box-sizing: border-box;
|
||||
border-radius: 8px;
|
||||
padding: 0 2px;
|
||||
|
||||
&-global {
|
||||
flex: 1;
|
||||
}
|
||||
@@ -181,12 +172,14 @@ const onBlur = () => {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
margin: 10px;
|
||||
background: #FEFEFE;
|
||||
margin: 0 26rpx;
|
||||
margin-top: 12rpx;
|
||||
background: #F4F4F4;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 28px;
|
||||
border-radius: 8px;
|
||||
height: 64rpx;
|
||||
border-radius: 64rpx;
|
||||
margin-bottom: 12rpx;
|
||||
|
||||
&-main {
|
||||
flex: 1;
|
||||
@@ -219,11 +212,12 @@ const onBlur = () => {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
justify-content: center;
|
||||
justify-content: flex-start;
|
||||
align-items: center;
|
||||
font-family: PingFang SC;
|
||||
font-weight: 400;
|
||||
color: #BBBBBB;
|
||||
color: #bbbbbb;
|
||||
padding-left: 32rpx;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -240,7 +234,7 @@ const onBlur = () => {
|
||||
color: #007aff;
|
||||
font-size: 16px;
|
||||
padding: 7px 10px 7px 3px;
|
||||
font-family: "PingFang SC", sans-serif;
|
||||
font-family: 'PingFang SC', sans-serif;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
.tui-search {
|
||||
background: #EBF0F6;
|
||||
background: #fff;
|
||||
|
||||
&-main-global {
|
||||
width: 100%;
|
||||
@@ -14,7 +14,7 @@
|
||||
}
|
||||
|
||||
.tui-search-global {
|
||||
padding-bottom: 10px;
|
||||
padding-bottom: 32rpx;
|
||||
&-header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
:style="{
|
||||
width: avatarSize,
|
||||
height: avatarSize,
|
||||
borderRadius: avatarBorderRadius,
|
||||
borderRadius: '80rpx'
|
||||
}"
|
||||
>
|
||||
<template v-if="isUniFrameWork">
|
||||
@@ -29,7 +29,7 @@
|
||||
:src="avatarImageUrl || defaultAvatarUrl"
|
||||
@load="avatarLoadSuccess"
|
||||
@error="avatarLoadFailed"
|
||||
>
|
||||
/>
|
||||
<div
|
||||
v-if="useAvatarSkeletonAnimation && !isImgLoaded"
|
||||
:class="{
|
||||
@@ -42,58 +42,60 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, toRefs } from '../../../adapter-vue';
|
||||
import { isUniFrameWork } from '../../../utils/env';
|
||||
import { ref, toRefs } from '../../../adapter-vue'
|
||||
import { isUniFrameWork } from '../../../utils/env'
|
||||
|
||||
interface IProps {
|
||||
url: string;
|
||||
size?: string;
|
||||
borderRadius?: string;
|
||||
useSkeletonAnimation?: boolean;
|
||||
url: string
|
||||
size?: string
|
||||
borderRadius?: string
|
||||
useSkeletonAnimation?: boolean
|
||||
}
|
||||
|
||||
interface IEmits {
|
||||
(key: 'onLoad', e: Event): void;
|
||||
(key: 'onError', e: Event): void;
|
||||
(key: 'onLoad', e: Event): void
|
||||
(key: 'onError', e: Event): void
|
||||
}
|
||||
|
||||
const defaultAvatarUrl = ref('https://web.sdk.qcloud.com/component/TUIKit/assets/avatar_21.png');
|
||||
const emits = defineEmits<IEmits>();
|
||||
const defaultAvatarUrl = ref(
|
||||
'https://web.sdk.qcloud.com/component/TUIKit/assets/avatar_21.png'
|
||||
)
|
||||
const emits = defineEmits<IEmits>()
|
||||
const props = withDefaults(defineProps<IProps>(), {
|
||||
// uniapp vue2 does not support constants in defineProps
|
||||
url: 'https://web.sdk.qcloud.com/component/TUIKit/assets/avatar_21.png',
|
||||
size: '36px',
|
||||
borderRadius: '5px',
|
||||
useSkeletonAnimation: false,
|
||||
});
|
||||
useSkeletonAnimation: false
|
||||
})
|
||||
|
||||
const {
|
||||
size: avatarSize,
|
||||
url: avatarImageUrl,
|
||||
borderRadius: avatarBorderRadius,
|
||||
useSkeletonAnimation: useAvatarSkeletonAnimation,
|
||||
} = toRefs(props);
|
||||
useSkeletonAnimation: useAvatarSkeletonAnimation
|
||||
} = toRefs(props)
|
||||
|
||||
let reloadAvatarTime = 0;
|
||||
const isImgLoaded = ref<boolean>(false);
|
||||
const loadErrorInUniapp = ref<boolean>(false);
|
||||
let reloadAvatarTime = 0
|
||||
const isImgLoaded = ref<boolean>(false)
|
||||
const loadErrorInUniapp = ref<boolean>(false)
|
||||
|
||||
function avatarLoadSuccess(e: Event) {
|
||||
isImgLoaded.value = true;
|
||||
emits('onLoad', e);
|
||||
isImgLoaded.value = true
|
||||
emits('onLoad', e)
|
||||
}
|
||||
|
||||
function avatarLoadFailed(e: Event) {
|
||||
reloadAvatarTime += 1;
|
||||
reloadAvatarTime += 1
|
||||
if (reloadAvatarTime > 3) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
if (isUniFrameWork) {
|
||||
loadErrorInUniapp.value = true;
|
||||
loadErrorInUniapp.value = true
|
||||
} else {
|
||||
(e.currentTarget as HTMLImageElement).src = defaultAvatarUrl.value;
|
||||
;(e.currentTarget as HTMLImageElement).src = defaultAvatarUrl.value
|
||||
}
|
||||
emits('onError', e);
|
||||
emits('onError', e)
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -119,9 +121,7 @@ function avatarLoadFailed(e: Event) {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background-color: #ececec;
|
||||
transition:
|
||||
opacity 0.3s,
|
||||
background-color 0.1s ease-out;
|
||||
transition: opacity 0.3s, background-color 0.1s ease-out;
|
||||
|
||||
&.skeleton-animation {
|
||||
animation: breath 2s linear 0.3s infinite;
|
||||
@@ -131,11 +131,12 @@ function avatarLoadFailed(e: Event) {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.avatar-image {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
width: 80rpx !important;
|
||||
height: 80rpx !important;
|
||||
border-radius: 80rpx !important;
|
||||
}
|
||||
|
||||
@keyframes breath {
|
||||
|
||||
@@ -9,10 +9,7 @@
|
||||
<slot name="left" />
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="title"
|
||||
class="tui-navigation-title"
|
||||
>
|
||||
<div v-if="title" class="tui-navigation-title">
|
||||
<h1 class="tui-navigation-title-text">
|
||||
{{ title }}
|
||||
</h1>
|
||||
@@ -25,29 +22,29 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, onMounted } from '../../../adapter-vue';
|
||||
import { isH5 } from '../../../utils/env';
|
||||
import { ref, onMounted } from '../../../adapter-vue'
|
||||
import { isH5 } from '../../../utils/env'
|
||||
|
||||
interface Props {
|
||||
title?: string;
|
||||
customStyle?: string;
|
||||
title?: string
|
||||
customStyle?: string
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<Props>(), {
|
||||
title: '',
|
||||
customStyle: '',
|
||||
});
|
||||
customStyle: ''
|
||||
})
|
||||
|
||||
const statusBarHeight = ref<number>(0);
|
||||
const statusBarHeight = ref<number>(0)
|
||||
|
||||
onMounted(() => {
|
||||
if (isH5) {
|
||||
statusBarHeight.value = 0;
|
||||
statusBarHeight.value = 0
|
||||
} else {
|
||||
const sysInfo = uni.getSystemInfoSync();
|
||||
statusBarHeight.value = sysInfo.statusBarHeight;
|
||||
const sysInfo = uni.getSystemInfoSync()
|
||||
statusBarHeight.value = sysInfo.statusBarHeight
|
||||
}
|
||||
});
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
@@ -55,9 +52,12 @@ onMounted(() => {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
background: #EBF0F6;
|
||||
// background: #ebf0f6;
|
||||
background: #fff;
|
||||
min-height: 44px;
|
||||
padding: 0 12px;
|
||||
border-bottom: 2rpx solid #0000000a;
|
||||
box-sizing: border-box;
|
||||
|
||||
&-left {
|
||||
display: flex;
|
||||
@@ -100,7 +100,7 @@ onMounted(() => {
|
||||
|
||||
&-back-text {
|
||||
font-size: 16px;
|
||||
color: #007AFF;
|
||||
color: #007aff;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
<template>
|
||||
<div
|
||||
class="transfer"
|
||||
:class="[!isPC ? 'transfer-h5' : '', isWeChat ? 'transfer-h5-wechat' : '']"
|
||||
:class="[
|
||||
!isPC ? 'transfer-h5' : '',
|
||||
isWeChat ? 'transfer-h5-wechat' : ''
|
||||
]"
|
||||
>
|
||||
<header
|
||||
v-if="!isPC && transferTitle"
|
||||
class="transfer-header transfer-h5-header"
|
||||
>
|
||||
<div
|
||||
v-if="!props.isHiddenBackIcon"
|
||||
@click="cancel"
|
||||
>
|
||||
<div v-if="!props.isHiddenBackIcon" @click="cancel">
|
||||
<Icon
|
||||
class="icon"
|
||||
:file="backIcon"
|
||||
@@ -29,22 +29,22 @@
|
||||
v-if="isPC && isTransferSearch"
|
||||
type="text"
|
||||
:value="searchValue"
|
||||
:placeholder="TUITranslateService.t('component.请输入userID')"
|
||||
placeholder="搜素"
|
||||
enterkeyhint="search"
|
||||
:class="[isUniFrameWork ? 'left-uniapp-input' : '']"
|
||||
@keyup.enter="handleInput"
|
||||
>
|
||||
/>
|
||||
<!-- not PC triggers blur -->
|
||||
<input
|
||||
v-if="!isPC && isTransferSearch"
|
||||
type="text"
|
||||
:placeholder="TUITranslateService.t('component.请输入userID')"
|
||||
placeholder="搜素"
|
||||
enterkeyhint="search"
|
||||
:value="searchValue"
|
||||
:class="[isUniFrameWork ? 'left-uniapp-input' : '']"
|
||||
@blur="handleInput"
|
||||
@confirm="handleInput"
|
||||
>
|
||||
/>
|
||||
</header>
|
||||
<main class="transfer-left-main">
|
||||
<ul class="transfer-list">
|
||||
@@ -59,13 +59,10 @@
|
||||
:width="'18px'"
|
||||
:height="'18px'"
|
||||
/>
|
||||
<i
|
||||
v-else
|
||||
class="icon-unselected"
|
||||
/>
|
||||
<span class="select-all">{{
|
||||
TUITranslateService.t("component.全选")
|
||||
}}</span>
|
||||
<i v-else class="icon-unselected" />
|
||||
<span class="select-all">
|
||||
{{ TUITranslateService.t('component.全选') }}
|
||||
</span>
|
||||
</li>
|
||||
<li
|
||||
v-for="item in transferList"
|
||||
@@ -82,7 +79,10 @@
|
||||
/>
|
||||
<i
|
||||
v-else
|
||||
:class="[item.isDisabled && 'disabled', 'icon-unselected']"
|
||||
:class="[
|
||||
item.isDisabled && 'disabled',
|
||||
'icon-unselected'
|
||||
]"
|
||||
/>
|
||||
<template v-if="!isTransferCustomItem">
|
||||
<img
|
||||
@@ -92,15 +92,14 @@
|
||||
'https://web.sdk.qcloud.com/component/TUIKit/assets/avatar_21.png'
|
||||
"
|
||||
onerror="this.onerror=null;this.src='https://web.sdk.qcloud.com/component/TUIKit/assets/avatar_21.png'"
|
||||
>
|
||||
/>
|
||||
<span class="name">{{ item.nick || item.userID }}</span>
|
||||
<span v-if="item.isDisabled">({{ TUITranslateService.t("component.已在群中") }})</span>
|
||||
<span v-if="item.isDisabled">
|
||||
({{ TUITranslateService.t('component.已在群中') }})
|
||||
</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
<slot
|
||||
name="left"
|
||||
:data="item"
|
||||
/>
|
||||
<slot name="left" :data="item" />
|
||||
</template>
|
||||
</li>
|
||||
<li
|
||||
@@ -108,29 +107,23 @@
|
||||
class="transfer-list-item more"
|
||||
@click="getMore"
|
||||
>
|
||||
{{ TUITranslateService.t("component.查看更多") }}
|
||||
{{ TUITranslateService.t('component.查看更多') }}
|
||||
</li>
|
||||
</ul>
|
||||
</main>
|
||||
</div>
|
||||
<div class="right">
|
||||
<header
|
||||
v-if="isPC"
|
||||
class="transfer-header"
|
||||
>
|
||||
<header v-if="isPC" class="transfer-header">
|
||||
{{ transferTitle }}
|
||||
</header>
|
||||
<ul
|
||||
v-if="resultShow"
|
||||
class="transfer-list"
|
||||
>
|
||||
<ul v-if="resultShow" class="transfer-list">
|
||||
<p
|
||||
v-if="transferSelectedList.length > 0 && isPC"
|
||||
class="transfer-text"
|
||||
>
|
||||
{{ TUITranslateService.t("component.已选中")
|
||||
{{ TUITranslateService.t('component.已选中')
|
||||
}}{{ transferSelectedList.length
|
||||
}}{{ TUITranslateService.t("component.人") }}
|
||||
}}{{ TUITranslateService.t('component.人') }}
|
||||
</p>
|
||||
<li
|
||||
v-for="(item, index) in transferSelectedList"
|
||||
@@ -146,51 +139,33 @@
|
||||
'https://web.sdk.qcloud.com/component/TUIKit/assets/avatar_21.png'
|
||||
"
|
||||
onerror="this.onerror=null;this.src='https://web.sdk.qcloud.com/component/TUIKit/assets/avatar_21.png'"
|
||||
>
|
||||
<span
|
||||
v-if="isPC"
|
||||
class="name"
|
||||
>{{ item.nick || item.userID }}</span>
|
||||
/>
|
||||
<span v-if="isPC" class="name">
|
||||
{{ item.nick || item.userID }}
|
||||
</span>
|
||||
</template>
|
||||
<template v-else>
|
||||
<slot
|
||||
name="right"
|
||||
:data="item"
|
||||
/>
|
||||
<slot name="right" :data="item" />
|
||||
</template>
|
||||
</aside>
|
||||
<span
|
||||
v-if="isPC"
|
||||
@click="selected(item)"
|
||||
>
|
||||
<Icon
|
||||
:file="cancelIcon"
|
||||
:width="'18px'"
|
||||
:height="'18px'"
|
||||
/>
|
||||
<span v-if="isPC" @click="selected(item)">
|
||||
<Icon :file="cancelIcon" :width="'18px'" :height="'18px'" />
|
||||
</span>
|
||||
</li>
|
||||
</ul>
|
||||
<footer class="transfer-right-footer">
|
||||
<button
|
||||
class="btn btn-cancel"
|
||||
@click="cancel"
|
||||
>
|
||||
{{ TUITranslateService.t("component.取消") }}
|
||||
<button class="btn btn-cancel" @click="cancel">
|
||||
{{ TUITranslateService.t('component.取消') }}
|
||||
</button>
|
||||
<button
|
||||
v-if="transferSelectedList.length > 0"
|
||||
class="btn"
|
||||
class="btn btn-confirm"
|
||||
@click="submit"
|
||||
>
|
||||
{{ TUITranslateService.t("component.完成") }}
|
||||
{{ TUITranslateService.t('component.完成') }}
|
||||
</button>
|
||||
<button
|
||||
v-else
|
||||
class="btn btn-no"
|
||||
@click="submit"
|
||||
>
|
||||
{{ TUITranslateService.t("component.完成") }}
|
||||
<button v-else class="btn btn-no" @click="submit">
|
||||
{{ TUITranslateService.t('component.完成') }}
|
||||
</button>
|
||||
</footer>
|
||||
</div>
|
||||
@@ -199,134 +174,175 @@
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { ref, watchEffect, computed } from '../../../adapter-vue';
|
||||
import { TUITranslateService } from '@tencentcloud/chat-uikit-engine-lite';
|
||||
import { ITransferListItem } from '../../../interface';
|
||||
import Icon from '../Icon.vue';
|
||||
import selectedIcon from '../../../assets/icon/selected.svg';
|
||||
import backIcon from '../../../assets/icon/back.svg';
|
||||
import cancelIcon from '../../../assets/icon/cancel.svg';
|
||||
import { isPC, isUniFrameWork, isWeChat } from '../../../utils/env';
|
||||
import { ref, watchEffect, computed } from '../../../adapter-vue'
|
||||
import { TUITranslateService } from '@tencentcloud/chat-uikit-engine-lite'
|
||||
import { ITransferListItem } from '../../../interface'
|
||||
import Icon from '../Icon.vue'
|
||||
import selectedIcon from '../../../assets/icon/selected.svg'
|
||||
import backIcon from '../../../assets/icon/back.svg'
|
||||
import cancelIcon from '../../../assets/icon/cancel.svg'
|
||||
import searchIcon from '../../../assets/icon/search.svg'
|
||||
import { isPC, isUniFrameWork, isWeChat } from '../../../utils/env'
|
||||
|
||||
const props = defineProps({
|
||||
list: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
default: () => []
|
||||
},
|
||||
selectedList: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
default: () => []
|
||||
},
|
||||
isSearch: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
default: true
|
||||
},
|
||||
isRadio: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
default: false
|
||||
},
|
||||
isCustomItem: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
default: false
|
||||
},
|
||||
title: {
|
||||
type: String,
|
||||
default: '',
|
||||
default: ''
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: '',
|
||||
default: ''
|
||||
},
|
||||
resultShow: {
|
||||
type: Boolean,
|
||||
default: true,
|
||||
default: true
|
||||
},
|
||||
total: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
default: 0
|
||||
},
|
||||
isHiddenBackIcon: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
});
|
||||
default: false
|
||||
}
|
||||
})
|
||||
|
||||
const transferList = ref<ITransferListItem[]>([]);
|
||||
const transferTotal = ref<number>(0);
|
||||
const transferSelectedList = ref<ITransferListItem[]>([]);
|
||||
const isTransferSearch = ref(true);
|
||||
const isTransferCustomItem = ref(false);
|
||||
const transferTitle = ref('');
|
||||
const searchValue = ref('');
|
||||
const transferList = ref<ITransferListItem[]>([])
|
||||
const transferTotal = ref<number>(0)
|
||||
const transferSelectedList = ref<ITransferListItem[]>([])
|
||||
const isTransferSearch = ref(true)
|
||||
const isTransferCustomItem = ref(false)
|
||||
const transferTitle = ref('')
|
||||
const searchValue = ref('')
|
||||
|
||||
watchEffect(() => {
|
||||
if (props.isCustomItem) {
|
||||
for (let index = 0; index < props.list.length; index++) {
|
||||
if (
|
||||
(props.list[index] as any).conversationID.indexOf('@TIM#SYSTEM') > -1
|
||||
(props.list[index] as any).conversationID.indexOf(
|
||||
'@TIM#SYSTEM'
|
||||
) > -1
|
||||
) {
|
||||
// eslint-disable-next-line vue/no-mutating-props
|
||||
props.list.splice(index, 1);
|
||||
props.list.splice(index, 1)
|
||||
}
|
||||
transferList.value = props.list as ITransferListItem[];
|
||||
transferList.value = props.list as ITransferListItem[]
|
||||
}
|
||||
} else {
|
||||
transferList.value = props.list as ITransferListItem[];
|
||||
transferList.value = props.list as ITransferListItem[]
|
||||
}
|
||||
transferTotal.value = props.total ? props.total : props.list.length;
|
||||
transferSelectedList.value = (props.selectedList && props.selectedList.length > 0 ? props.selectedList : transferSelectedList.value) as any;
|
||||
isTransferSearch.value = props.isSearch;
|
||||
isTransferCustomItem.value = props.isCustomItem;
|
||||
transferTitle.value = props.title;
|
||||
});
|
||||
transferTotal.value = props.total ? props.total : props.list.length
|
||||
transferSelectedList.value = (
|
||||
props.selectedList && props.selectedList.length > 0
|
||||
? props.selectedList
|
||||
: transferSelectedList.value
|
||||
) as any
|
||||
isTransferSearch.value = props.isSearch
|
||||
isTransferCustomItem.value = props.isCustomItem
|
||||
transferTitle.value = props.title
|
||||
})
|
||||
|
||||
const emit = defineEmits(['search', 'submit', 'cancel', 'getMore']);
|
||||
const emit = defineEmits(['search', 'submit', 'cancel', 'getMore'])
|
||||
|
||||
const optional = computed(() =>
|
||||
transferList.value.filter((item: any) => !item.isDisabled),
|
||||
);
|
||||
transferList.value.filter((item: any) => !item.isDisabled)
|
||||
)
|
||||
|
||||
const handleInput = (e: any) => {
|
||||
searchValue.value = e.target.value || e.detail.value;
|
||||
emit('search', searchValue.value);
|
||||
};
|
||||
searchValue.value = e.target.value || e.detail.value
|
||||
emit('search', searchValue.value)
|
||||
}
|
||||
const selected = (item: any) => {
|
||||
if (item.isDisabled) {
|
||||
return;
|
||||
return
|
||||
}
|
||||
let list: ITransferListItem[] = transferSelectedList.value;
|
||||
const index: number = list.indexOf(item);
|
||||
let list: ITransferListItem[] = transferSelectedList.value
|
||||
const index: number = list.indexOf(item)
|
||||
if (index > -1) {
|
||||
return transferSelectedList.value.splice(index, 1);
|
||||
return transferSelectedList.value.splice(index, 1)
|
||||
}
|
||||
if (props.isRadio) {
|
||||
list = [];
|
||||
list = []
|
||||
}
|
||||
list.push(item)
|
||||
transferSelectedList.value = list
|
||||
}
|
||||
list.push(item);
|
||||
transferSelectedList.value = list;
|
||||
};
|
||||
|
||||
const selectedAll = () => {
|
||||
if (transferSelectedList.value.length === optional.value.length) {
|
||||
transferSelectedList.value = [];
|
||||
transferSelectedList.value = []
|
||||
} else {
|
||||
transferSelectedList.value = [...optional.value];
|
||||
transferSelectedList.value = [...optional.value]
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const submit = () => {
|
||||
emit('submit', transferSelectedList.value);
|
||||
searchValue.value = '';
|
||||
};
|
||||
emit('submit', transferSelectedList.value)
|
||||
searchValue.value = ''
|
||||
}
|
||||
|
||||
const cancel = () => {
|
||||
emit('cancel');
|
||||
searchValue.value = '';
|
||||
};
|
||||
emit('cancel')
|
||||
searchValue.value = ''
|
||||
}
|
||||
|
||||
const getMore = () => {
|
||||
emit('getMore');
|
||||
};
|
||||
emit('getMore')
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped src="./style/transfer.scss"></style>
|
||||
<style lang="scss" scoped>
|
||||
.transfer-left-main {
|
||||
// background: #f4f4f4;
|
||||
}
|
||||
.transfer-header {
|
||||
padding: 26rpx 32rpx !important;
|
||||
box-shadow: 0rpx 2rpx 8rpx 0rpx rgba(0, 0, 0, 0.04) !important;
|
||||
margin-bottom: 12rpx !important;
|
||||
}
|
||||
.left-uniapp-input {
|
||||
border-radius: 80rpx !important;
|
||||
}
|
||||
|
||||
.avatar {
|
||||
width: 80rpx !important;
|
||||
height: 80rpx !important;
|
||||
border-radius: 80rpx !important;
|
||||
}
|
||||
|
||||
.transfer-right-footer {
|
||||
button {
|
||||
height: 64rpx;
|
||||
padding: 0 36rpx;
|
||||
line-height: 64rpx;
|
||||
border-radius: 64rpx;
|
||||
&::after {
|
||||
display: none !important;
|
||||
}
|
||||
}
|
||||
.btn-confirm {
|
||||
background: #00D993;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -10,10 +10,11 @@ export const useAuthUser = () => {
|
||||
const tokenStore = useTokenStore()
|
||||
|
||||
// 响应式状态(state & getters)
|
||||
const { userInfo, tencentUserSig, fontSizeData } = storeToRefs(userStore)
|
||||
const { userInfo, tencentUserSig, fontSizeData, integralData } = storeToRefs(userStore)
|
||||
const { token } = storeToRefs(tokenStore)
|
||||
|
||||
return {
|
||||
integralData,
|
||||
userInfo,
|
||||
tencentUserSig,
|
||||
fontSizeData,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
"name" : "uniapp-imitate-wx",
|
||||
"appid" : "__UNI__9EFDC69",
|
||||
"description" : "",
|
||||
"versionName" : "1.0.0",
|
||||
"versionName" : "1.0.1",
|
||||
"versionCode" : "100",
|
||||
"transformPx" : false,
|
||||
/* 5+App特有相关 */
|
||||
|
||||
@@ -111,14 +111,16 @@
|
||||
{
|
||||
"path": "pages/discover/discover",
|
||||
"style": {
|
||||
"navigationBarTitleText": "发现"
|
||||
"navigationBarTitleText": "发现",
|
||||
"navigationBarBackgroundColor": "#ffffff"
|
||||
}
|
||||
},
|
||||
{
|
||||
"path": "pages/my-index/my-index",
|
||||
"style": {
|
||||
"navigationBarTitleText": "我的",
|
||||
"backgroundColor": "#f7f7f7"
|
||||
"backgroundColor": "#f7f7f7",
|
||||
"navigationBarBackgroundColor": "#ffffff"
|
||||
}
|
||||
},
|
||||
{
|
||||
|
||||
@@ -6,10 +6,11 @@
|
||||
import { useUI } from '@/utils/use-ui'
|
||||
import { useAuthUser } from '@/composables/useAuthUser'
|
||||
import { useUserStore } from '@/stores/user'
|
||||
import { formatNumberWithWan } from '../../utils'
|
||||
|
||||
const { showToast } = useUI()
|
||||
const { userInfo } = useAuthUser()
|
||||
const { refreshUserInfo } = useUserStore()
|
||||
const { integralData } = useAuthUser()
|
||||
const { getIntegral } = useUserStore()
|
||||
|
||||
const weekList = [
|
||||
{ name: '一', value: 1 },
|
||||
@@ -45,7 +46,7 @@
|
||||
)
|
||||
|
||||
if (msg) {
|
||||
await refreshUserInfo()
|
||||
await getIntegral()
|
||||
showToast(msg)
|
||||
}
|
||||
|
||||
@@ -161,22 +162,6 @@
|
||||
getData()
|
||||
}
|
||||
|
||||
const formatNumberWithWan = num => {
|
||||
if (num < 10000) {
|
||||
return num.toString()
|
||||
}
|
||||
|
||||
// 保留小数:根据需求可调整 toFixed 的位数
|
||||
let wan = num / 10000
|
||||
|
||||
// 如果是整数万,不显示小数;否则保留两位小数(或你想要的位数)
|
||||
if (wan % 1 === 0) {
|
||||
return wan + '万'
|
||||
} else {
|
||||
return wan.toFixed(2).replace(/\.?0+$/, '') + '万' // 去掉不必要的尾随零
|
||||
}
|
||||
}
|
||||
|
||||
onLoad(() => {
|
||||
const now = new Date()
|
||||
currentYear.value = now.getFullYear()
|
||||
@@ -200,8 +185,8 @@
|
||||
<view class="public-header—box">
|
||||
<view class="integral-box">
|
||||
<text>我的积分</text>
|
||||
<text style="font-size: 80rpx; margin-top: 30rpx;">
|
||||
{{ formatNumberWithWan(userInfo.totalPoints) }}
|
||||
<text style="font-size: 80rpx; margin-top: 30rpx">
|
||||
{{ formatNumberWithWan(integralData) }}
|
||||
</text>
|
||||
</view>
|
||||
<image
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
import { ref } from 'vue'
|
||||
import { onLoad, onPageScroll } from '@dcloudio/uni-app'
|
||||
import { getUserIntegralRank } from '@/api'
|
||||
import { formatNumberWithWan } from '../../utils'
|
||||
|
||||
const cbNavBar = ref({})
|
||||
const listData = ref([])
|
||||
@@ -104,7 +105,9 @@
|
||||
</uni-col>
|
||||
<uni-col :span="6">
|
||||
<view class="table-right">
|
||||
<text class="item-text">{{ item.totalPoints }}</text>
|
||||
<text class="item-text">
|
||||
{{ formatNumberWithWan(item.totalPoints) }}
|
||||
</text>
|
||||
</view>
|
||||
</uni-col>
|
||||
</uni-row>
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
import { onLoad } from '@dcloudio/uni-app'
|
||||
import { navigateTo } from '@/utils/router'
|
||||
import { useUI } from '@/utils/use-ui'
|
||||
import { formatNumberWithWan } from '../../utils'
|
||||
|
||||
const bottomList = [
|
||||
{
|
||||
@@ -40,7 +41,7 @@
|
||||
]
|
||||
|
||||
const { showDialog } = useUI()
|
||||
const { userInfo } = useAuthUser()
|
||||
const { userInfo, integralData } = useAuthUser()
|
||||
|
||||
onLoad(() => {
|
||||
// 获取用户信息
|
||||
@@ -78,7 +79,7 @@
|
||||
<view class="top-box">
|
||||
<view class="left-name">
|
||||
<text>账户积分</text>
|
||||
<text>{{ userInfo?.totalPoints }}</text>
|
||||
<text>{{ formatNumberWithWan(integralData) }}</text>
|
||||
</view>
|
||||
<view class="right-btn">
|
||||
<button
|
||||
|
||||
@@ -5,8 +5,9 @@
|
||||
import { getUserPayPwd } from '@/api/my-index'
|
||||
import { useAuthUser } from '@/composables/useAuthUser'
|
||||
import { useUI } from '@/utils/use-ui'
|
||||
import { formatNumberWithWan } from '../../../utils'
|
||||
|
||||
const { userInfo } = useAuthUser()
|
||||
const { integralData } = useAuthUser()
|
||||
const { showDialog } = useUI()
|
||||
|
||||
const itemList = ref([])
|
||||
@@ -57,7 +58,7 @@
|
||||
<view class="top-card">
|
||||
<view class="left-box">
|
||||
<text>我的资产</text>
|
||||
<text>{{ userInfo?.totalPoints }}</text>
|
||||
<text>{{ formatNumberWithWan(integralData) }}</text>
|
||||
</view>
|
||||
<view class="right-box">
|
||||
<button
|
||||
|
||||
@@ -7,13 +7,14 @@
|
||||
getUserThirdPayList,
|
||||
getUserBankList,
|
||||
getUserWithdrawConfig,
|
||||
getUserIntegral,
|
||||
addUserWithdraw,
|
||||
getUserPayPwd
|
||||
} from '@/api/my-index'
|
||||
import { useUI } from '@/utils/use-ui'
|
||||
import { useAuthUser } from '@/composables/useAuthUser'
|
||||
|
||||
const { showToast, showDialog } = useUI()
|
||||
const { integralData } = useAuthUser()
|
||||
|
||||
const tixian = ref(null)
|
||||
const popup = ref(null)
|
||||
@@ -43,12 +44,6 @@
|
||||
return Number(value.toFixed(2))
|
||||
})
|
||||
|
||||
/** 获取积分 */
|
||||
const getIntegral = async () => {
|
||||
const res = await getUserIntegral()
|
||||
withdrawData.availablePoints = res.data.availablePoints
|
||||
}
|
||||
|
||||
const getData = async () => {
|
||||
const res = await getUserWithdrawConfig(topData.value.withdrawalType)
|
||||
withdrawData.minAmount = res.data.minAmount
|
||||
@@ -134,7 +129,7 @@
|
||||
}
|
||||
|
||||
onShow(() => {
|
||||
getIntegral()
|
||||
withdrawData.availablePoints = integralData.value
|
||||
getList()
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -60,28 +60,44 @@
|
||||
|
||||
<!-- 红包信息 -->
|
||||
<view class="red-envelope-info">
|
||||
<text v-if="isPersonal">
|
||||
<text>
|
||||
{{
|
||||
viewData.hasReceived
|
||||
? `${viewData.remainCount}个红包${viewData.remainAmount}积分`
|
||||
viewData.receiveList.length > 0
|
||||
? `${viewData.totalCount}个红包${viewData.totalAmount}积分`
|
||||
: `红包${viewData.remainAmount}积分,等待对方领取`
|
||||
}}
|
||||
</text>
|
||||
<text v-else>1个红包共5.00元</text>
|
||||
<!-- <text v-else>1个红包共5.00元</text> -->
|
||||
</view>
|
||||
|
||||
<!-- 领取人卡片信息 -->
|
||||
<view v-if="viewData.receiveList.length > 0">
|
||||
<view
|
||||
v-if="isPersonal && viewData.hasReceived"
|
||||
v-for="item in viewData.receiveList"
|
||||
:key="item.id"
|
||||
class="red-envelope-card"
|
||||
>
|
||||
<view class="avatar"></view>
|
||||
<view class="right-box">
|
||||
<view class="top-name">
|
||||
<text>用户名称</text>
|
||||
<text>5积分</text>
|
||||
<text>{{ item.receiveAmount }}积分</text>
|
||||
</view>
|
||||
<view class="bottom-name">
|
||||
<text class="date">{{ item.receiveTime }}</text>
|
||||
<view
|
||||
v-if="viewData.luckyReceive.userId === item.userId"
|
||||
class="tisp"
|
||||
>
|
||||
<image
|
||||
src="/static/images/best.svg"
|
||||
mode="heightFix"
|
||||
class="best-icon"
|
||||
></image>
|
||||
<text>手气最佳</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<text class="date">用户名称</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
@@ -123,10 +139,25 @@
|
||||
color: #333333;
|
||||
}
|
||||
}
|
||||
.date {
|
||||
.bottom-name {
|
||||
font-size: 28rpx;
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
.date {
|
||||
color: #a1a1a1;
|
||||
}
|
||||
.tisp {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.best-icon {
|
||||
height: 34rpx;
|
||||
margin-right: 10rpx;
|
||||
}
|
||||
text {
|
||||
color: #e7ba24;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
1
static/images/best.svg
Normal file
1
static/images/best.svg
Normal file
@@ -0,0 +1 @@
|
||||
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1768836218802" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="9875" width="80" height="80" xmlns:xlink="http://www.w3.org/1999/xlink"><path d="M0 0h1024v1024H0z" fill-opacity="0" p-id="9876"></path><path d="M985.6 352c0-40.32-32-72.32-71.04-72.32-39.68 0-71.04 32.64-71.04 72.32 0 15.36 4.48 29.44 12.8 41.6-43.52 74.24-74.88 174.72-135.68 174.72-9.6 0-19.2-0.64-28.16-2.56-96-17.28-147.2-253.44-152.32-286.72 23.04-12.16 39.68-37.12 39.68-65.92 0-40.32-32-72.32-71.04-72.32a72.512 72.512 0 0 0-24.96 140.16c-5.12 37.12-53.76 276.48-147.2 289.92-7.68 1.28-15.36 1.92-23.68 1.92-60.16 0-102.4-104.96-137.6-174.72 3.84-8.32 5.76-17.92 5.76-28.16 0-40.32-32-72.32-71.04-72.32-19.2 0-36.48 7.68-49.28 20.48-14.08 13.44-22.4 32-22.4 51.84 0 40.32 32 72.32 71.04 72.32 124.16 310.4 185.6 465.92 185.6 465.92s106.88 51.84 216.32 51.84 217.6-51.84 217.6-51.84 64.64-161.28 193.28-484.48c35.2-3.84 63.36-34.56 63.36-71.68z" fill="#FBD30F" p-id="9877"></path><path d="M294.4 904.32c0 29.44 106.88 59.52 215.04 59.52s220.16-29.44 220.16-59.52c0-29.44-112.64-59.52-220.16-59.52s-215.04 29.44-215.04 59.52z" fill="#FFA706" p-id="9878"></path></svg>
|
||||
|
After Width: | Height: | Size: 1.3 KiB |
@@ -22,6 +22,7 @@ import { reLaunch } from '@/utils/router'
|
||||
import { getTencentUserSig } from '@/api'
|
||||
import { TUILogin } from '@tencentcloud/tui-core-lite'
|
||||
import { TUIChatEngine } from '@tencentcloud/chat-uikit-engine-lite'
|
||||
import { getUserIntegral } from '@/api/my-index'
|
||||
|
||||
export const useUserStore = defineStore('user', () => {
|
||||
const { clearToken } = useTokenStore()
|
||||
@@ -36,6 +37,9 @@ export const useUserStore = defineStore('user', () => {
|
||||
/** 腾讯 IM 存储数据 */
|
||||
const tencentUserSig = ref(getSig() ? JSON?.parse(getSig()) : {})
|
||||
|
||||
/** 用户积分数 */
|
||||
const integralData = ref(0)
|
||||
|
||||
/**
|
||||
* 获取用户信息(可从缓存或接口)
|
||||
*/
|
||||
@@ -51,6 +55,7 @@ export const useUserStore = defineStore('user', () => {
|
||||
loginTencentIM()
|
||||
return
|
||||
}
|
||||
await getIntegral()
|
||||
const res = await getUserData()
|
||||
await setUserInfo(res.data)
|
||||
loginTencentIM()
|
||||
@@ -68,6 +73,12 @@ export const useUserStore = defineStore('user', () => {
|
||||
setSig(res.data)
|
||||
}
|
||||
|
||||
/** 获取用户积分 */
|
||||
const getIntegral = async () => {
|
||||
const res = await getUserIntegral()
|
||||
integralData.value = res.data.availablePoints
|
||||
}
|
||||
|
||||
/**
|
||||
* 登录腾讯 IM
|
||||
*/
|
||||
@@ -142,6 +153,7 @@ export const useUserStore = defineStore('user', () => {
|
||||
/** 刷新用户信息(如用户信息被修改) */
|
||||
const refreshUserInfo = async () => {
|
||||
const res = await getUserData()
|
||||
await getIntegral()
|
||||
await setUserInfoData(res.data)
|
||||
userInfo.value = res.data
|
||||
}
|
||||
@@ -163,8 +175,10 @@ export const useUserStore = defineStore('user', () => {
|
||||
|
||||
return {
|
||||
userInfo,
|
||||
integralData,
|
||||
tencentUserSig,
|
||||
fontSizeData,
|
||||
getIntegral,
|
||||
clearAllUserInfo,
|
||||
updateFontSize,
|
||||
logout,
|
||||
|
||||
@@ -29,3 +29,20 @@ export const formatRMB = amount => {
|
||||
|
||||
return `${formattedInteger}.${decimal}`
|
||||
}
|
||||
|
||||
/** 积分格式化 */
|
||||
export const formatNumberWithWan = num => {
|
||||
if (num < 10000) {
|
||||
return num.toString()
|
||||
}
|
||||
|
||||
// 保留小数:根据需求可调整 toFixed 的位数
|
||||
let wan = num / 10000
|
||||
|
||||
// 如果是整数万,不显示小数;否则保留两位小数(或你想要的位数)
|
||||
if (wan % 1 === 0) {
|
||||
return wan + '万'
|
||||
} else {
|
||||
return wan.toFixed(2).replace(/\.?0+$/, '') + '万' // 去掉不必要的尾随零
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user