App.vue 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336
  1. <template>
  2. <n-config-provider
  3. data-cy="app"
  4. inline-theme-disabled
  5. :theme="theme"
  6. :theme-overrides="themeOverrides"
  7. :class="{ dark: mode }"
  8. :locale="zhCN"
  9. :date-locale="dateZhCN"
  10. >
  11. <n-layout position="absolute" style="height: 100vh">
  12. <n-layout-header style="padding: 24px" bordered>
  13. <div class="flex justify-between items-center">
  14. <div class="back" style="position: relative; top: 4px; color: #fff">
  15. <n-icon size="24" :component="ChevronBackOutline" />
  16. <span style="position: relative; top: -6px">返回我的场景</span>
  17. </div>
  18. <n-button type="primary" @click="handleEditorSave"> 保存</n-button>
  19. </div>
  20. </n-layout-header>
  21. <n-layout
  22. class="bg-white dark:bg-gray-800 dark:text-white text-gray-800 h-screen w-screen"
  23. position="absolute"
  24. style="top: 83px; bottom: 64px"
  25. has-sider
  26. >
  27. <!-- collapse-mode="transform"
  28. @mouseover="collapsed = false"
  29. @mouseleave="collapsed = true" -->
  30. <n-layout-sider
  31. data-cy="sidebar"
  32. :width="180"
  33. :collapsed="collapsed"
  34. :native-scrollbar="false"
  35. bordered
  36. >
  37. <n-menu
  38. class="menu-class"
  39. v-model:value="activeKey"
  40. :options="menuOptions"
  41. @update:value="handleUpdateValue"
  42. />
  43. </n-layout-sider>
  44. <n-layout-content
  45. content-class="layoutContent"
  46. :native-scrollbar="false"
  47. >
  48. <div class="app-wrap">
  49. <div
  50. class="app-scene"
  51. ref="sceneRef"
  52. :style="{
  53. width: sceneRefWidth,
  54. visibility: showScene ? 'hidden' : 'visible'
  55. // display: showScene ? 'none' : 'block'
  56. }"
  57. >
  58. <iframe
  59. v-if="sceneURL"
  60. :src="sceneURL"
  61. frameborder="0"
  62. :style="sceneMobileMode && { width: '375px', height: '677px' }"
  63. @load="setupSDK($event.target)"
  64. ></iframe>
  65. </div>
  66. </div>
  67. <div
  68. :class="{ isTran: showScene }"
  69. class="app-view"
  70. id="drawer-target"
  71. >
  72. <n-message-provider>
  73. <n-dialog-provider>
  74. <router-view />
  75. </n-dialog-provider>
  76. </n-message-provider>
  77. </div>
  78. </n-layout-content>
  79. </n-layout>
  80. </n-layout>
  81. </n-config-provider>
  82. </template>
  83. <script setup lang="ts">
  84. import { setupSDK } from '@/sdk'
  85. import {
  86. darkTheme,
  87. // lightTheme,
  88. NDialogProvider,
  89. NMessageProvider,
  90. zhCN,
  91. dateZhCN,
  92. createDiscreteApi
  93. } from 'naive-ui'
  94. import { computed, ref, toRaw, watchEffect, h, onMounted, watch } from 'vue'
  95. import type { Component } from 'vue'
  96. import { useMainStore } from '@/store'
  97. import { themeOverrides } from '@/styles/theme'
  98. import { editorSave } from '@/api/module/editor'
  99. import type { ConfigProviderProps } from 'naive-ui'
  100. const configProviderPropsRef = computed<ConfigProviderProps>(() => ({
  101. theme: darkTheme
  102. }))
  103. const { message } = createDiscreteApi(['message'], {
  104. configProviderProps: configProviderPropsRef
  105. })
  106. import { useRouter, useRoute } from 'vue-router'
  107. import {
  108. ChevronBackOutline,
  109. Layers,
  110. Cog,
  111. HomeOutline as HomeIcon,
  112. RepeatOutline,
  113. PeopleOutline,
  114. ChatboxEllipses
  115. } from '@vicons/ionicons5'
  116. const darkStore = localStorage.getItem('dark')
  117. import { RouterLink } from 'vue-router'
  118. import { NIcon } from 'naive-ui'
  119. import { useUrlSearchParams } from '@vueuse/core'
  120. const params = useUrlSearchParams('hash')
  121. const route = useRoute()
  122. const activeKey = ref(route.name)
  123. const showScene = ref(false)
  124. watch(
  125. () => route.name,
  126. () => {
  127. console.log('activeKeys', route.name)
  128. activeKey.value = route.name
  129. if (route.name === 'textToaudio') {
  130. showScene.value = true
  131. } else {
  132. showScene.value = false
  133. }
  134. console.log('watch', route.name)
  135. }
  136. )
  137. const prefersDark: boolean = darkStore
  138. ? darkStore === 'true'
  139. : window.matchMedia('(prefers-color-scheme: dark)').matches
  140. const mode = ref<boolean>(prefersDark)
  141. const theme = computed(() => (mode.value ? darkTheme : darkTheme))
  142. const sceneRefWidth = computed(() => {
  143. console.log('route', route, route.name)
  144. if (
  145. route.name === 'basicSettings' ||
  146. route.name === 'topicNavigation' ||
  147. route.name === 'digitalHuman'
  148. )
  149. return `calc(100% - ${240}px)`
  150. if (route.name === 'message') return `calc(100% - ${0}px)`
  151. return `calc(100% - ${mode.sceneRefWidth}px)`
  152. })
  153. const sceneURL = computed(() => main.sceneURL)
  154. const sceneMobileMode = computed(() => main.sceneMode == 'mobile')
  155. const sceneRef = ref()
  156. const main = useMainStore()
  157. const { setSceneLink, setSceneRef } = main
  158. watchEffect(() => {
  159. localStorage.setItem('dark', `${mode.value}`)
  160. })
  161. function renderIcon(icon: Component) {
  162. return () => h(NIcon, null, { default: () => h(icon) })
  163. }
  164. const collapsed = ref(false)
  165. const menuOptions = [
  166. {
  167. label: () =>
  168. h(
  169. RouterLink,
  170. {
  171. to: {
  172. name: 'basicSettings'
  173. // params: {
  174. // lang: 'zh-CN'
  175. // }
  176. }
  177. },
  178. { default: () => '基础设置' }
  179. ),
  180. key: 'basicSettings',
  181. icon: renderIcon(Cog)
  182. },
  183. {
  184. label: () =>
  185. h(
  186. RouterLink,
  187. {
  188. to: {
  189. name: 'digitalHuman'
  190. // params: {
  191. // lang: 'zh-CN'
  192. // }
  193. }
  194. },
  195. { default: () => '数字人播报' }
  196. ),
  197. key: 'digitalHuman',
  198. icon: renderIcon(PeopleOutline)
  199. },
  200. {
  201. label: () =>
  202. h(
  203. RouterLink,
  204. {
  205. to: {
  206. name: 'textToaudio'
  207. // params: {
  208. // lang: 'zh-CN'
  209. // }
  210. }
  211. },
  212. { default: () => '文字语音互转' }
  213. ),
  214. key: 'textToaudio',
  215. icon: renderIcon(RepeatOutline)
  216. },
  217. {
  218. label: () =>
  219. h(
  220. RouterLink,
  221. {
  222. to: {
  223. name: 'message'
  224. // params: {
  225. // lang: 'zh-CN'
  226. // }
  227. }
  228. },
  229. { default: () => '留言互动' }
  230. ),
  231. key: 'message',
  232. icon: renderIcon(ChatboxEllipses)
  233. },
  234. {
  235. label: () =>
  236. h(
  237. RouterLink,
  238. {
  239. to: {
  240. name: 'topicNavigation'
  241. // params: {
  242. // lang: 'zh-CN'
  243. // }
  244. }
  245. },
  246. { default: () => '专题导航' }
  247. ),
  248. key: 'topicNavigation',
  249. icon: renderIcon(Layers)
  250. }
  251. ]
  252. const handleUpdateValue = (value: string) => {
  253. console.log(value)
  254. }
  255. onMounted(() => {
  256. const sceneCode = params.m ? String(params.m) : 'KJ-t-3Y6dxgyehDk'
  257. main.setSceneCode(sceneCode)
  258. main.getSceneInfo()
  259. setSceneLink(`${import.meta.env.VITE_KANKAN_PREFIX}/spg.html?m=${main.sceneCode}`)
  260. setSceneRef(sceneRef.value)
  261. })
  262. const handleEditorSave = async () => {
  263. const data = main.getEditorData
  264. await editorSave(data)
  265. message.success('保存成功!')
  266. }
  267. </script>
  268. <style lang="sass">
  269. #app
  270. font-family: Inter, Avenir, Helvetica, Arial, sans-serif
  271. -webkit-font-smoothing: antialiased
  272. -moz-osx-font-smoothing: grayscale
  273. .layoutContent
  274. position: relative
  275. padding: 0px
  276. height: calc(100vh - 83px)
  277. overflow: hidden
  278. .app-wrap, .app-view
  279. position: absolute
  280. left: 0
  281. top: 0
  282. width: 100%
  283. height: 100%
  284. z-index: 10
  285. .app-view
  286. z-index: 100
  287. pointer-events: none
  288. &.isTran
  289. pointer-events: all
  290. .app-scene
  291. width: 100%
  292. height: 100%
  293. display: flex
  294. align-items: flex-start
  295. justify-content: center
  296. .app-scene iframe
  297. width: 100%
  298. height: 100%
  299. .required
  300. padding-left: 10px
  301. position: relative
  302. &:after
  303. content: " *"
  304. color: red
  305. position: absolute
  306. left: 0
  307. </style>