UserSearchList.vue 8.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. <template>
  2. <div class="search-list">
  3. <div class="user-search">
  4. <van-search
  5. v-model="username"
  6. show-action
  7. placeholder="search"
  8. @update:model-value="userChange"
  9. @clear="isSearching = false"
  10. >
  11. <template #action>
  12. <van-popover
  13. v-model:show="showPopover"
  14. :actions="actions"
  15. placement="bottom-end"
  16. @select="selectAction">
  17. <template #reference>
  18. <div style="padding-right: 6px">编辑</div>
  19. </template>
  20. </van-popover>
  21. </template>
  22. </van-search>
  23. </div>
  24. <div class="user-list-warp">
  25. <template v-if="isSearching">
  26. <template v-if="searchedUserList.length > 0">
  27. <div class="search-title">已添加的用户</div>
  28. <ul class="user-list">
  29. <li class="item" :key="item.id"
  30. v-for="item in searchedUserList" @click="selectUser(item)">
  31. <div class="common-pic">{{ item.friendName.charAt(0) }}</div>
  32. <div class="username">{{ item.friendName }}</div>
  33. </li>
  34. </ul>
  35. </template>
  36. <template v-if="searchedGlobalUserList.length > 0">
  37. <div class="search-title">全局搜索到的用户</div>
  38. <ul class="user-list">
  39. <li class="item" :key="item.id"
  40. v-for="item in searchedGlobalUserList" @click="addSearchedUser(item)">
  41. <div class="common-pic">{{ item.username.charAt(0) }}</div>
  42. <div class="username">{{ item.username }}</div>
  43. </li>
  44. </ul>
  45. </template>
  46. <div class="message" v-if="searchedUserList.length === 0 && searchedGlobalUserList.length === 0">没有数据</div>
  47. </template>
  48. <ul v-else class="user-list">
  49. <li class="item"
  50. :class="{active: currentUser.friendId===item.friendId && currentUser.friendName===item.friendName}"
  51. :key="item.id"
  52. v-for="item in store.state.userList" @click="selectUser(item)">
  53. <div class="common-pic">{{ item.friendName.charAt(0) }}</div>
  54. <div class="username">{{ item.friendName }}</div>
  55. <div class="message-num" v-if="item.noReadNum">{{ item.noReadNum }}</div>
  56. </li>
  57. </ul>
  58. </div>
  59. <van-popup v-model:show="show" class="common-pop">
  60. <van-icon name="close" class="close" @click="show=false"/>
  61. <div class="title">{{ currentAction.text }}</div>
  62. <div class="form">
  63. <van-cell-group inset>
  64. <van-field v-model="name" :label="currentAction.label"/>
  65. </van-cell-group>
  66. </div>
  67. <div class="footer" @click="submit">确定</div>
  68. </van-popup>
  69. </div>
  70. </template>
  71. <script lang="ts">
  72. import {defineComponent, reactive, toRefs, onMounted} from "vue";
  73. import {addFriend, addGroup, findUser, getFriends, noReadNum} from "../../../../api"
  74. import {useStore} from 'vuex'
  75. import {Obj, StrNumObj} from "../../../../interface"
  76. import {Toast} from "vant"
  77. interface ActionObj {
  78. type: number,
  79. key: string,
  80. text: string,
  81. label: string
  82. }
  83. interface State {
  84. username: string,
  85. showPopover: boolean,
  86. actions: ActionObj[],
  87. currentAction: StrNumObj,
  88. name: string,
  89. show: boolean,
  90. currentUser: StrNumObj,
  91. searchedUserList: Obj[],
  92. searchedGlobalUserList: Obj[],
  93. isSearching: boolean,
  94. timer: any,
  95. }
  96. export default defineComponent({
  97. setup() {
  98. const state = reactive<State>({
  99. username: '',
  100. showPopover: false,
  101. actions: [
  102. {type: 1, key: 'friendName', text: '添加好友', label: '用户名:'},
  103. {type: 2, key: 'groupName', text: '创建群', label: '群名字:'},
  104. ],
  105. currentAction: {},
  106. name: '',
  107. show: false,
  108. currentUser: {},
  109. searchedUserList: [],
  110. searchedGlobalUserList: [],
  111. isSearching: false,
  112. timer: 0,
  113. })
  114. const store = useStore()
  115. const userChange = () => {
  116. if (state.username) {
  117. state.isSearching = true
  118. state.searchedUserList = store.state.userList.filter((item: Obj) => item.friendName.includes(state.username))
  119. } else {
  120. state.isSearching = false
  121. state.searchedUserList = []
  122. }
  123. clearTimeout(state.timer)
  124. state.timer = setTimeout(() => {
  125. state.searchedGlobalUserList = []
  126. if (state.username.length >= 6 && store.state.userList.every((item: Obj) => item.friendName !== state.username)) {
  127. store.commit('setIsSearching', true)
  128. findUser({username: state.username}).then(({data}) => {
  129. state.searchedGlobalUserList = [data]
  130. }).finally(() => {
  131. store.commit('setIsSearching', false)
  132. })
  133. }
  134. }, 1000)
  135. }
  136. const addSearchedUser = async (item: StrNumObj) => {
  137. if (item.id === store.state.userInfo.id) {
  138. return Toast('不能添加自己')
  139. }
  140. await addFriend({id: item.id})
  141. state.username = ''
  142. state.isSearching = false
  143. let obj = {
  144. isGroup: 1,
  145. userId: store.state.userInfo.id,
  146. friendId: item.id,
  147. friendName: item.username
  148. }
  149. store.state.userList.unshift(obj)
  150. selectUser(obj)
  151. }
  152. const selectAction = (action: StrNumObj) => {
  153. state.currentAction = action
  154. state.show = true
  155. state.name = ''
  156. }
  157. const getUserList = async () => {
  158. if (store.state.userInfo.token) {
  159. const {data} = await getFriends()
  160. store.state.userList = data
  161. }
  162. }
  163. const getNoReadNum = async (item: StrNumObj) => {
  164. await noReadNum({
  165. fromId: item.userId,
  166. toId: item.friendId,
  167. messageType: item.isGroup
  168. })
  169. }
  170. const selectUser = (item: StrNumObj) => {
  171. state.currentUser = item
  172. state.username = ''
  173. state.isSearching = false
  174. item.noReadNum = 0
  175. getNoReadNum(item)
  176. store.commit('setChatUserInfo', item)
  177. store.commit('setIsShowRight', true)
  178. store.commit('setIsShowChat', true)
  179. store.commit('setIsShowUserInfo', false)
  180. store.commit('setIsShowGroupInfo', false)
  181. }
  182. const submit = async () => {
  183. if (!state.name.trim()) {
  184. return Toast(state.currentAction.type === 1 ? '请输入用户名' : '请输入群名字')
  185. }
  186. if (state.currentAction.type === 1) {
  187. if (state.name === store.state.userInfo.username) {
  188. return Toast('不能添加自己')
  189. }
  190. if (store.state.userList.some((item: Obj) => item.friendName === state.name)) {
  191. return Toast(state.name + '已存在')
  192. }
  193. Toast.loading('提交中...')
  194. let {data} = await findUser({username: state.name})
  195. await addFriend({id: data.id})
  196. let obj = {
  197. isGroup: 1,
  198. userId: store.state.userInfo.id,
  199. friendId: data.id,
  200. friendName: data.username
  201. }
  202. store.state.userList.unshift(obj)
  203. selectUser(obj)
  204. Toast.success('添加成功')
  205. } else {
  206. if (store.state.userList.some((item: Obj) => item.friendName === state.name)) {
  207. return Toast(state.name + '已存在')
  208. }
  209. Toast.loading('提交中...')
  210. await addGroup({groupName: state.name})
  211. getUserList()
  212. Toast.success('创建成功')
  213. }
  214. state.show = false
  215. }
  216. onMounted(() => {
  217. getUserList()
  218. })
  219. return {
  220. ...toRefs(state),
  221. store,
  222. userChange,
  223. addSearchedUser,
  224. selectAction,
  225. selectUser,
  226. submit
  227. }
  228. }
  229. })
  230. </script>
  231. <style lang="less" scoped>
  232. .search-list {
  233. flex: 1;
  234. display: flex;
  235. flex-direction: column;
  236. overflow-y: auto;
  237. }
  238. .user-list-warp {
  239. flex: 1;
  240. overflow-y: auto;
  241. }
  242. .search-title {
  243. padding: 6px 10px;
  244. color: #999;
  245. font-size: 13px;
  246. background: #f1f1f1;
  247. }
  248. .message {
  249. padding-top: 100px;
  250. color: #999;
  251. text-align: center;
  252. }
  253. .user-search {
  254. border-bottom: 1px solid #eee;
  255. }
  256. .user-list {
  257. .item {
  258. position: relative;
  259. display: flex;
  260. align-items: center;
  261. padding-left: 10px;
  262. &:hover {
  263. background: rgba(88,140,232,0.2)
  264. }
  265. &.active {
  266. background: #588ce8;
  267. .username {
  268. color: #fff;
  269. border-bottom-color: #588ce8;
  270. }
  271. }
  272. .username {
  273. flex: 1;
  274. padding: 18px 0;
  275. font-size: 16px;
  276. border-bottom: 1px solid #eee;
  277. margin-left: 10px;
  278. }
  279. .message-num {
  280. position: absolute;
  281. right: 10px;
  282. bottom: 10px;
  283. min-width: 20px;
  284. padding: 0 5px;
  285. color: #fff;
  286. background: var(--van-button-primary-background-color);
  287. border-radius: 10px;
  288. text-align: center;
  289. }
  290. }
  291. }
  292. </style>