list.vue 9.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389
  1. <template>
  2. <div ref="headerRef" class="header">
  3. <div style="display: flex; flex-direction: row" class="header-title-tab">
  4. <h3>{{ t('room.myRoom') }}({{ roomStore.list.length }})</h3>
  5. <a-radio-group
  6. v-model:value="roomType"
  7. button-style="solid"
  8. class="select-group"
  9. >
  10. <a-radio-button :value="0">{{ t('base.all') }}</a-radio-button>
  11. <a-radio-button :value="1">{{ t('base.using') }}</a-radio-button>
  12. <a-radio-button :value="2">{{ t('base.end') }}</a-radio-button>
  13. </a-radio-group>
  14. <!-- <a-button type="text" @click="roomStore.setRoomStatus(2)">
  15. <h3>{{ t('room.myEndRoom') }} ({{ roomStore.endlist.length }})</h3>
  16. </a-button> -->
  17. </div>
  18. <a-input
  19. v-model:value="keyword"
  20. :placeholder="t('room.searchRoom')"
  21. class="room-search"
  22. allow-clear
  23. >
  24. <template #prefix><search-outlined class="room-search-icon" /></template>
  25. </a-input>
  26. </div>
  27. <DataList
  28. un-search-desc=" "
  29. :data-source="roomList.filter(room => room !== addMarked)"
  30. :keyword="keyword"
  31. name="作品"
  32. >
  33. <template v-if="!keyword" #undata>
  34. <a-button
  35. type="primary"
  36. shape="round"
  37. size="middle"
  38. @click="editRoom()"
  39. v-if="isRoomAll"
  40. >
  41. {{ t('room.createRoom') }}
  42. </a-button>
  43. <span v-else>{{ t('room.nodata') }}</span>
  44. </template>
  45. <a-list
  46. :grid="{ gutter: 20, xl: 5, lg: 4, md: 3, sm: 2, xs: 1, column: 5 }"
  47. :data-source="roomList"
  48. class="page-list"
  49. >
  50. <template #renderItem="{ item }">
  51. <a-list-item>
  52. <RoomSign
  53. v-if="item !== addMarked"
  54. :room="item"
  55. :disabled="item.roomStatus === 2"
  56. @web-sync="webSyncRoom(item)"
  57. @delete="deleteRoom(item)"
  58. @share="shareRoom(item)"
  59. @mini-sync="miniSyncRoom(item, 'leader')"
  60. @edit="editRoom(item)"
  61. />
  62. <template v-else>
  63. <a-card
  64. v-if="isRoomAll"
  65. class="add-room"
  66. hoverable
  67. @click="editRoom()"
  68. >
  69. <a-button shape="circle" class="button" type="primary">
  70. <plus-outlined class="add-room-icon" />
  71. </a-button>
  72. <p>{{ t('room.createRoom') }}</p>
  73. </a-card>
  74. </template>
  75. </a-list-item>
  76. </template>
  77. </a-list>
  78. </DataList>
  79. <teleport v-if="!headerVisible" to="#app">
  80. <div class="content-layout goto-layer">
  81. <div
  82. class="goto-top"
  83. @click="contentRef?.scroll({ left: 0, top: 0, behavior: 'smooth' })"
  84. >
  85. <vertical-align-top-outlined />
  86. </div>
  87. </div>
  88. </teleport>
  89. </template>
  90. <script setup lang="ts">
  91. import { useRoomStore } from '@/store/modules/room'
  92. import { useUserStore } from '@/store/modules/user'
  93. import { ref, computed, createVNode, unref, watch } from 'vue'
  94. import { message, Modal } from 'ant-design-vue'
  95. import { copyText } from '@/shared'
  96. import { renderModal } from '@/helper'
  97. import { userApp } from '@/main'
  98. import { useVisible } from '@/hook'
  99. import { contentRef } from '@/App.vue'
  100. import EditRoom from './edit-room'
  101. import RoomSign from './sign.vue'
  102. import Share from './modal/share.vue'
  103. import MiniSync from './modal/mini-sync.vue'
  104. import DataList from '@/components/data-list/index.vue'
  105. import { useI18n } from '@/hook/useI18n'
  106. import type { Room } from '@/store/modules/room'
  107. import { fetchRoomDetail, checkRoomEditOrDel } from '@/api'
  108. import { useLocale } from '@/locales/useLocale'
  109. defineOptions({ name: 'RoomList' })
  110. const isMiniApp = ref(import.meta.env.VITE_IS_MINIAPP)
  111. const addMarked = Symbol('add-room')
  112. const roomStore = useRoomStore()
  113. const { t } = useI18n()
  114. const keyword = ref('')
  115. const roomType = ref(roomStore.roomStatus)
  116. watch(
  117. () => roomType,
  118. val => {
  119. console.log('roomType', unref(val))
  120. switch (unref(val)) {
  121. case 0:
  122. roomStore.setRoomStatus(0)
  123. break
  124. case 1:
  125. roomStore.setRoomStatus(1)
  126. break
  127. case 2:
  128. roomStore.setRoomStatus(2)
  129. break
  130. default:
  131. roomStore.setRoomStatus(0)
  132. break
  133. }
  134. },
  135. {
  136. deep: true,
  137. immediate: true
  138. }
  139. )
  140. const roomList = computed(() =>
  141. roomStore.roomStatus === 0
  142. ? [addMarked, ...roomStore.filter(keyword.value)]
  143. : roomStore.filter(keyword.value)
  144. )
  145. const isRoomAll = computed(() => roomStore.roomStatus === 0)
  146. const deleteRoom = async (room: Room) => {
  147. // xxx
  148. const res = await checkRoomEditOrDel(room)
  149. if (!res) {
  150. message.error(t('room.roomOnfired'))
  151. return
  152. }
  153. Modal.confirm({
  154. content: t('room.deletedScenesWaring'),
  155. title: t('room.deletedRoom'),
  156. width: '400px',
  157. okText: t('base.delete'),
  158. icon: null,
  159. cancelText: t('base.cancel'),
  160. onOk: () => roomStore.delete(room)
  161. })
  162. }
  163. const shareRoom = async (room: Room) => {
  164. if (Number(unref(isMiniApp)) === 1) {
  165. await roomStore.setRoomMiniCode(room)
  166. miniSyncRoom(room)
  167. } else {
  168. const scenes = await fetchRoomDetail(room.id)
  169. const { app } = userApp()
  170. const m = scenes.sceneData[0].num
  171. Modal.confirm({
  172. content: createVNode(Share, { room, num: m }),
  173. title: t('base.share'),
  174. icon: null,
  175. width: '500px',
  176. okText: t('room.copyLink'),
  177. appContext: app._context,
  178. cancelText: t('base.cancel'),
  179. onOk: async () => {
  180. const { getLocale } = useLocale()
  181. const link = roomStore.getShareUrl({
  182. roomId: room.id,
  183. num: m,
  184. role: 'customer',
  185. isTour: '1',
  186. lang: unref(getLocale) || 'zh'
  187. })
  188. await copyText(link)
  189. message.success(t('room.linkCopySuccess'))
  190. }
  191. })
  192. }
  193. }
  194. const miniSyncRoom = async (room: Room, key?: 'leader') => {
  195. let miniCode: string
  196. let descs: string[]
  197. let title: string
  198. if (key === 'leader') {
  199. await roomStore.setLeaderRoomMiniCode(room)
  200. miniCode = room.leaderMiniCode!
  201. descs = [t('room.openScanMiniApp'), t('room.enterWechat')]
  202. title = t('room.startMiniAppLivestreaming')
  203. } else {
  204. await roomStore.setRoomMiniCode(room)
  205. miniCode = room.miniCode!
  206. descs = [t('room.shareFriends'), t('room.scanMiniApp')]
  207. title = t('base.share')
  208. }
  209. renderModal(MiniSync, {
  210. title,
  211. miniCode,
  212. descs
  213. })
  214. }
  215. const webSyncRoom = async (room: Room) => {
  216. // debugger
  217. const scenes = await fetchRoomDetail(room.id)
  218. const { getLocale } = useLocale()
  219. const user = useUserStore().current
  220. console.log('scenes', scenes)
  221. const m = scenes.sceneData[0].num
  222. const link = roomStore.getShareUrl({
  223. roomId: room.id,
  224. num: m,
  225. role: 'leader',
  226. name: encodeURIComponent(scenes.roomHostName),
  227. avatar: encodeURIComponent(user.avatar) || '',
  228. userId: '1',
  229. isTour: '0',
  230. lang: unref(getLocale) || 'zh'
  231. })
  232. console.log('带看link', link)
  233. window.open(link, '_blank')
  234. }
  235. const editRoom = async (room?: Room) => {
  236. if (room) {
  237. // if (!res) {
  238. // message.error(t('room.roomOnfired'))
  239. // return
  240. // }
  241. const scenes = await roomStore.setRoomScenes(room)
  242. room = Object.assign({}, room, scenes)
  243. }
  244. // debugger
  245. renderModal(EditRoom, {
  246. room,
  247. async onSave(actionRoom) {
  248. if (room) {
  249. const res = await checkRoomEditOrDel(room)
  250. if (res) {
  251. await roomStore.update(actionRoom)
  252. }
  253. } else {
  254. await roomStore.insert(actionRoom)
  255. }
  256. message.success(t('base.saveSuccess'))
  257. }
  258. })
  259. }
  260. const headerRef = ref<HTMLElement>()
  261. const headerVisible = useVisible({
  262. target: headerRef,
  263. parent: contentRef,
  264. bound: -140
  265. })
  266. </script>
  267. <style scoped lang="scss">
  268. .header {
  269. padding: 0 30px;
  270. height: 80px;
  271. display: flex;
  272. align-items: center;
  273. justify-content: space-between;
  274. background-color: #fff;
  275. margin: 30px 0;
  276. .room-search {
  277. width: 290px;
  278. height: 40px;
  279. border-radius: 20px;
  280. }
  281. .room-search-icon {
  282. color: #cfd0d3;
  283. }
  284. }
  285. .add-room {
  286. height: 321px;
  287. cursor: pointer;
  288. display: flex;
  289. text-align: center;
  290. align-items: center;
  291. justify-content: center;
  292. .button {
  293. width: 60px;
  294. height: 60px;
  295. background: linear-gradient(144deg, #00aefb 0%, #0076f6 100%);
  296. }
  297. .add-room-icon {
  298. font-size: 24px;
  299. }
  300. p {
  301. font-size: 14px;
  302. color: #333;
  303. margin-top: 10px;
  304. }
  305. }
  306. .goto-layer {
  307. position: fixed;
  308. left: 50%;
  309. transform: translateX(-50%);
  310. pointer-events: none;
  311. }
  312. .goto-top {
  313. pointer-events: all;
  314. right: 20px;
  315. bottom: 20px;
  316. background-color: #fff;
  317. display: flex;
  318. align-items: center;
  319. justify-content: center;
  320. border-radius: 8px;
  321. width: 60px;
  322. height: 60px;
  323. font-size: 24px;
  324. color: #747575;
  325. position: absolute;
  326. opacity: 0.5;
  327. transition: opacity 0.3s ease;
  328. cursor: pointer;
  329. &:hover {
  330. opacity: 1;
  331. }
  332. }
  333. .header-title-tab {
  334. display: flex;
  335. flex: 1;
  336. flex-direction: row;
  337. justify-content: space-between;
  338. padding-right: 30px;
  339. }
  340. </style>
  341. <style lang="scss">
  342. .room-search,
  343. .room-search input {
  344. background: #f7f8fa;
  345. }
  346. .select-group {
  347. .ant-radio-button-wrapper {
  348. &:nth-child(1) {
  349. border-top-left-radius: 15px;
  350. border-bottom-left-radius: 15px;
  351. }
  352. &:nth-child(3) {
  353. border-top-right-radius: 15px;
  354. border-bottom-right-radius: 15px;
  355. }
  356. }
  357. }
  358. .page-list {
  359. .ant-card {
  360. min-height: 332px;
  361. }
  362. }
  363. </style>