list.vue 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  1. <template>
  2. <div ref="headerRef" class="header">
  3. <h3>我的房间({{ roomStore.list.length }})</h3>
  4. <a-input
  5. v-model:value="keyword"
  6. placeholder="搜索房间"
  7. class="room-search"
  8. allow-clear
  9. >
  10. <template #prefix><search-outlined class="room-search-icon" /></template>
  11. </a-input>
  12. </div>
  13. <DataList
  14. :data-source="roomList.filter(room => room !== addMarked)"
  15. :keyword="keyword"
  16. name="作品"
  17. >
  18. <template #undata>
  19. <a-button type="primary" shape="round" size="middle" @click="editRoom()">
  20. 创建房间
  21. </a-button>
  22. </template>
  23. <a-list
  24. :grid="{ gutter: 20, xl: 5, lg: 4, md: 3, sm: 2, xs: 1, column: 5 }"
  25. :data-source="roomList"
  26. >
  27. <template #renderItem="{ item }">
  28. <a-list-item>
  29. <RoomSign
  30. v-if="item !== addMarked"
  31. :room="item"
  32. @web-sync="webSyncRoom(item)"
  33. @delete="deleteRoom(item)"
  34. @share="miniSyncRoom(item)"
  35. @mini-sync="miniSyncRoom(item, 'leader')"
  36. @edit="editRoom(item)"
  37. />
  38. <a-card v-else class="add-room" hoverable @click="editRoom()">
  39. <a-button shape="circle" class="button" type="primary">
  40. <plus-outlined class="add-room-icon" />
  41. </a-button>
  42. <p>创建作品</p>
  43. </a-card>
  44. </a-list-item>
  45. </template>
  46. </a-list>
  47. </DataList>
  48. <teleport v-if="!headerVisible" to="#app">
  49. <div class="content-layout goto-layer">
  50. <div
  51. class="goto-top"
  52. @click="contentRef?.scroll({ left: 0, top: 0, behavior: 'smooth' })"
  53. >
  54. <vertical-align-top-outlined />
  55. </div>
  56. </div>
  57. </teleport>
  58. </template>
  59. <script setup lang="ts">
  60. import { useRoomStore } from '@/store'
  61. import { ref, computed, createVNode } from 'vue'
  62. import { message, Modal } from 'ant-design-vue'
  63. import { copyText } from '@/shared'
  64. import { renderModal } from '@/helper'
  65. import { app } from '@/main'
  66. import { useVisible } from '@/hook'
  67. import { contentRef } from '@/App.vue'
  68. import EditRoom from './edit-room'
  69. import RoomSign from './sign.vue'
  70. import Share from './modal/share.vue'
  71. import MiniSync from './modal/mini-sync.vue'
  72. import DataList from '@/components/data-list/index.vue'
  73. import type { Room } from '@/store'
  74. defineOptions({ name: 'RoomList' })
  75. const addMarked = Symbol('add-room')
  76. const roomStore = useRoomStore()
  77. roomStore.fetchList()
  78. const keyword = ref('')
  79. const roomList = computed(() => [addMarked, ...roomStore.filter(keyword.value)])
  80. const deleteRoom = (room: Room) => {
  81. Modal.confirm({
  82. content: '删除后无法恢复,是否确认?',
  83. title: '删除作品',
  84. width: '400px',
  85. okText: '删除',
  86. icon: null,
  87. cancelText: '取消',
  88. onOk: () => roomStore.delete(room)
  89. })
  90. }
  91. const shareRoom = async (room: Room) => {
  92. await roomStore.setRoomMiniCode(room)
  93. Modal.confirm({
  94. content: createVNode(Share, { room }),
  95. title: '分享',
  96. icon: null,
  97. width: '500px',
  98. okText: '复制链接',
  99. appContext: app._context,
  100. cancelText: '取消',
  101. onOk: async (room: Room) => {
  102. await copyText(roomStore.getShareUrl(room))
  103. message.success('链接复制成功')
  104. }
  105. })
  106. }
  107. const miniSyncRoom = async (room: Room, key?: 'leader') => {
  108. let miniCode: string
  109. if (key === 'leader') {
  110. await roomStore.setLeaderRoomMiniCode(room)
  111. miniCode = room.leaderMiniCode!
  112. } else {
  113. await roomStore.setRoomMiniCode(room)
  114. miniCode = room.miniCode!
  115. }
  116. Modal.info({
  117. content: createVNode(MiniSync, { miniCode }),
  118. title: '小程序带看',
  119. width: '500px',
  120. icon: null,
  121. okText: '确定',
  122. cancelText: null
  123. })
  124. }
  125. const webSyncRoom = (room: Room) => window.open(roomStore.getShareUrl(room))
  126. const editRoom = async (room?: Room) => {
  127. if (room) {
  128. await roomStore.setRoomScenes(room)
  129. }
  130. renderModal(EditRoom, {
  131. room,
  132. onSave(actionRoom) {
  133. if (room) {
  134. roomStore.update(actionRoom)
  135. } else {
  136. roomStore.insert(actionRoom)
  137. }
  138. }
  139. })
  140. }
  141. const headerRef = ref<HTMLElement>()
  142. const headerVisible = useVisible({
  143. target: headerRef,
  144. parent: contentRef,
  145. bound: -140
  146. })
  147. </script>
  148. <style scoped lang="scss">
  149. .header {
  150. padding: 0 30px;
  151. height: 80px;
  152. display: flex;
  153. align-items: center;
  154. justify-content: space-between;
  155. background-color: #fff;
  156. margin: 30px 0;
  157. .room-search {
  158. width: 290px;
  159. height: 40px;
  160. border-radius: 20px;
  161. }
  162. .room-search-icon {
  163. color: #cfd0d3;
  164. }
  165. }
  166. .add-room {
  167. height: 321px;
  168. cursor: pointer;
  169. display: flex;
  170. align-items: center;
  171. justify-content: center;
  172. .button {
  173. width: 60px;
  174. height: 60px;
  175. background: linear-gradient(144deg, #00aefb 0%, #0076f6 100%);
  176. }
  177. .add-room-icon {
  178. font-size: 24px;
  179. }
  180. p {
  181. font-size: 14px;
  182. color: #333;
  183. margin-top: 10px;
  184. }
  185. }
  186. .goto-layer {
  187. position: fixed;
  188. left: 50%;
  189. transform: translateX(-50%);
  190. pointer-events: none;
  191. }
  192. .goto-top {
  193. pointer-events: all;
  194. right: 20px;
  195. bottom: 20px;
  196. background-color: #fff;
  197. display: flex;
  198. align-items: center;
  199. justify-content: center;
  200. border-radius: 8px;
  201. width: 60px;
  202. height: 60px;
  203. font-size: 24px;
  204. color: #747575;
  205. position: absolute;
  206. opacity: 0.5;
  207. transition: opacity 0.3s ease;
  208. cursor: pointer;
  209. &:hover {
  210. opacity: 1;
  211. }
  212. }
  213. </style>
  214. <style>
  215. .room-search,
  216. .room-search input {
  217. background: #f7f8fa;
  218. }
  219. </style>