list.vue 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410
  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. let link = ''
  223. const isInternational = ref(import.meta.env.VITE_IS_INTERNATIONAL)
  224. console.log('room', room)
  225. let avatar = room.head || user.avatar
  226. console.log('avatar', avatar)
  227. if (isInternational) {
  228. link = roomStore.getShareUrl({
  229. roomId: room.id,
  230. num: m,
  231. role: 'leader',
  232. name: encodeURIComponent(scenes.roomHostName),
  233. avatar: encodeURIComponent(avatar) || '',
  234. userId: String(user.userId || 1),
  235. isTour: '0',
  236. lang: unref(getLocale) || 'zh',
  237. platform: 'ucenter'
  238. })
  239. } else {
  240. link = roomStore.getShareUrl({
  241. roomId: room.id,
  242. num: m,
  243. role: 'leader',
  244. name: encodeURIComponent(scenes.roomHostName),
  245. avatar: encodeURIComponent(avatar) || '',
  246. userId: String(user.userId || 1),
  247. isTour: '0',
  248. lang: unref(getLocale) || 'zh'
  249. })
  250. }
  251. console.log('带看link', link)
  252. window.open(link, '_blank')
  253. }
  254. const editRoom = async (room?: Room) => {
  255. if (room) {
  256. // if (!res) {
  257. // message.error(t('room.roomOnfired'))
  258. // return
  259. // }
  260. const scenes = await roomStore.setRoomScenes(room)
  261. room = Object.assign({}, room, scenes)
  262. }
  263. // debugger
  264. renderModal(EditRoom, {
  265. room,
  266. async onSave(actionRoom) {
  267. if (room) {
  268. const res = await checkRoomEditOrDel(room)
  269. if (res) {
  270. await roomStore.update(actionRoom)
  271. }
  272. } else {
  273. await roomStore.insert(actionRoom)
  274. }
  275. message.success(t('base.saveSuccess'))
  276. }
  277. })
  278. }
  279. const headerRef = ref<HTMLElement>()
  280. const headerVisible = useVisible({
  281. target: headerRef,
  282. parent: contentRef,
  283. bound: -140
  284. })
  285. </script>
  286. <style scoped lang="scss">
  287. .header {
  288. padding: 0 30px;
  289. height: 80px;
  290. display: flex;
  291. align-items: center;
  292. justify-content: space-between;
  293. background-color: #fff;
  294. margin: 30px 0;
  295. .room-search {
  296. width: 290px;
  297. height: 40px;
  298. border-radius: 20px;
  299. }
  300. .room-search-icon {
  301. color: #cfd0d3;
  302. }
  303. }
  304. .add-room {
  305. height: 321px;
  306. cursor: pointer;
  307. display: flex;
  308. text-align: center;
  309. align-items: center;
  310. justify-content: center;
  311. .button {
  312. width: 60px;
  313. height: 60px;
  314. background: linear-gradient(144deg, #00aefb 0%, #0076f6 100%);
  315. }
  316. .add-room-icon {
  317. font-size: 24px;
  318. }
  319. p {
  320. font-size: 14px;
  321. color: #333;
  322. margin-top: 10px;
  323. }
  324. }
  325. .goto-layer {
  326. position: fixed;
  327. left: 50%;
  328. transform: translateX(-50%);
  329. pointer-events: none;
  330. }
  331. .goto-top {
  332. pointer-events: all;
  333. right: 20px;
  334. bottom: 20px;
  335. background-color: #fff;
  336. display: flex;
  337. align-items: center;
  338. justify-content: center;
  339. border-radius: 8px;
  340. width: 60px;
  341. height: 60px;
  342. font-size: 24px;
  343. color: #747575;
  344. position: absolute;
  345. opacity: 0.5;
  346. transition: opacity 0.3s ease;
  347. cursor: pointer;
  348. &:hover {
  349. opacity: 1;
  350. }
  351. }
  352. .header-title-tab {
  353. display: flex;
  354. flex: 1;
  355. flex-direction: row;
  356. justify-content: space-between;
  357. padding-right: 30px;
  358. }
  359. </style>
  360. <style lang="scss">
  361. .room-search,
  362. .room-search input {
  363. background: #f7f8fa;
  364. }
  365. .select-group {
  366. .ant-radio-button-wrapper {
  367. &:nth-child(1) {
  368. border-top-left-radius: 15px;
  369. border-bottom-left-radius: 15px;
  370. }
  371. &:nth-child(3) {
  372. border-top-right-radius: 15px;
  373. border-bottom-right-radius: 15px;
  374. }
  375. }
  376. }
  377. .page-list {
  378. .ant-card {
  379. min-height: 332px;
  380. }
  381. }
  382. </style>