Main.vue 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. <script setup lang="ts">
  2. import { ScrollRect } from '@imengyu/vue-scroll-rect'
  3. import { ref, onMounted } from 'vue'
  4. import ContextMenu from '@imengyu/vue3-context-menu'
  5. import { HtmlUtils } from '@imengyu/imengyu-utils'
  6. import { AppItem } from '../model/App'
  7. import AppList from '../components/AppList.vue'
  8. import AppListItem from '../components/AppListItem.vue'
  9. // 状态管理
  10. const apps = ref<Record<string, AppItem[]>>({})
  11. const selectedApp = ref<AppItem | null>(null)
  12. const isFullScreen = ref(false)
  13. const isLoading = ref(false)
  14. const currentAspectRatio = ref('')
  15. const isLeftPanelOpen = ref(true)
  16. const menuBtn = ref<HTMLButtonElement>()
  17. // 切换左侧面板
  18. function toggleLeftPanel() {
  19. isLeftPanelOpen.value = !isLeftPanelOpen.value
  20. window.electronAPI.toggleChildSide(isLeftPanelOpen.value)
  21. }
  22. // 从本地加载应用数据
  23. async function loadApps() {
  24. try {
  25. const data = await window.electronAPI.loadAppsJson()
  26. if (Array.isArray(data)) {
  27. apps.value = {
  28. "未分组": data as AppItem[]
  29. };
  30. } else {
  31. apps.value = data
  32. }
  33. } catch (error) {
  34. console.error('Failed to load apps:', error)
  35. }
  36. }
  37. // 选择应用
  38. function selectApp(app: AppItem) {
  39. isLoading.value = true
  40. //if (app.openType === 'window') {
  41. // window.electronAPI.openWindow(app.url)
  42. //} else {
  43. selectedApp.value = app
  44. function parseAspectRatio(aspectRatio: string) {
  45. if (aspectRatio && aspectRatio.includes('/')) {
  46. const [width, height] = aspectRatio.split('/').map(Number)
  47. if (width > 0 && height > 0) {
  48. currentAspectRatio.value = `${width} : ${height}`
  49. return width / height
  50. }
  51. }
  52. return 0
  53. }
  54. window.electronAPI.loadChildUrl(app.url, app.keepScreenSize ? (
  55. parseAspectRatio(app.aspectRatio || '16:9')
  56. ) : 0, app.inputPassword ? {
  57. username: app.inputPassword.username,
  58. password: app.inputPassword.password,
  59. } : undefined)
  60. //}
  61. setTimeout(() => {
  62. isLoading.value = false
  63. }, 2000);
  64. }
  65. // 退出应用
  66. function exitApp() {
  67. window.electronAPI.exit()
  68. }
  69. // 切换全屏
  70. function toggleFullScreen() {
  71. isFullScreen.value = !isFullScreen.value
  72. window.electronAPI.toggleFullScreen(isFullScreen.value)
  73. }
  74. // 切换开发者工具
  75. function toggleDevTools() {
  76. window.electronAPI.toggleDevTools()
  77. }
  78. // 显示配置窗口
  79. function showConfig() {
  80. window.electronAPI.showConfig()
  81. }
  82. // 显示关于窗口
  83. function showAbout() {
  84. window.electronAPI.showAbout()
  85. }
  86. // 显示菜单
  87. function showMenu() {
  88. if (ContextMenu.isAnyContextMenuOpen())
  89. return
  90. ContextMenu.showContextMenu({
  91. x: HtmlUtils.getLeft(menuBtn.value!) + menuBtn.value!.offsetWidth,
  92. y: HtmlUtils.getTop(menuBtn.value!) + menuBtn.value!.offsetHeight + 8,
  93. direction: 'bl',
  94. items: [
  95. {
  96. label: '列表配置',
  97. onClick: showConfig,
  98. },
  99. {
  100. label: '全屏',
  101. shortcut: 'F11',
  102. onClick: toggleFullScreen,
  103. },
  104. {
  105. label: '开发者工具',
  106. shortcut: 'F12',
  107. onClick: toggleDevTools,
  108. },
  109. {
  110. label: '关于',
  111. divided: 'up',
  112. onClick: showAbout,
  113. },
  114. {
  115. label: '退出',
  116. shortcut: 'Alt+F4',
  117. onClick: exitApp,
  118. },
  119. ],
  120. })
  121. }
  122. // 初始化加载数据
  123. onMounted(() => {
  124. loadApps()
  125. window.ipcRenderer.on('main-side-state-changed', (_event, isOpen) => {
  126. isLeftPanelOpen.value = isOpen
  127. });
  128. window.ipcRenderer.on('main-config-changed', (_event) => {
  129. loadApps();
  130. });
  131. })
  132. </script>
  133. <template>
  134. <div class="app-container">
  135. <!-- 左侧应用列表 -->
  136. <div v-if="isLeftPanelOpen" class="left-panel">
  137. <h2 class="panel-title">
  138. <div>
  139. <img src="/icon.svg" alt="应用" />
  140. 应用列表
  141. </div>
  142. <button ref="menuBtn" class="control-btn" @click="showMenu">
  143. <img src="../assets/menu.svg" alt="菜单" />
  144. </button>
  145. </h2>
  146. <AppList>
  147. <div v-for="(group, title) in apps" :key="title">
  148. <h5>{{ title }}</h5>
  149. <AppListItem
  150. v-for="app in group"
  151. :key="app.id"
  152. :app="app"
  153. :activeAppId="selectedApp?.id"
  154. @selectApp="selectApp(app)"
  155. />
  156. </div>
  157. </AppList>
  158. <!-- 底部控制按钮 -->
  159. <div class="bottom-panel">
  160. <button class="control-btn" @click="toggleLeftPanel">
  161. <img src="../assets/left.svg" alt="折叠" />
  162. </button>
  163. </div>
  164. </div>
  165. <!-- 右侧iframe区域 -->
  166. <div class="right-panel">
  167. <div v-if="currentAspectRatio" class="aspect-ratio-info">
  168. 显示比例:{{ currentAspectRatio }}
  169. </div>
  170. </div>
  171. </div>
  172. </template>
  173. <style lang="scss">
  174. @use '../assets/colors.scss' as *;
  175. .app-container {
  176. display: flex;
  177. flex-direction: row;
  178. height: 100vh;
  179. width: 100vw;
  180. overflow: hidden;
  181. font-family: Arial, sans-serif;
  182. color: $text-color;
  183. background-color: #000;
  184. // 左侧面板
  185. .left-panel {
  186. position: relative;
  187. width: $panel-width;
  188. background: $bg-light;
  189. display: flex;
  190. flex-direction: column;
  191. border-right: 1px solid $bg-lighter;
  192. .panel-title {
  193. display: flex;
  194. flex-direction: row;
  195. align-items: center;
  196. justify-content: space-between;
  197. padding: 20px;
  198. margin: 0;
  199. font-size: 18px;
  200. color: $primary-color;
  201. font-weight: bold;
  202. > div {
  203. display: flex;
  204. flex-direction: row;
  205. align-items: center;
  206. align-content: center;
  207. img {
  208. width: 28px;
  209. height: 28px;
  210. margin-right: 8px;
  211. }
  212. }
  213. }
  214. }
  215. // 右侧面板
  216. .right-panel {
  217. position: relative;
  218. flex: 1;
  219. padding: 0;
  220. .aspect-ratio-info {
  221. position: absolute;
  222. right: 10px;
  223. top: 10px;
  224. padding: 10px;
  225. font-size: 14px;
  226. border-radius: 5px;
  227. color: $text-color;
  228. background-color: $bg-light;
  229. }
  230. }
  231. // 底部面板
  232. .bottom-panel {
  233. flex-shrink: 0;
  234. padding: 15px;
  235. display: flex;
  236. justify-content: flex-start;
  237. gap: 10px;
  238. }
  239. }
  240. .control-btn {
  241. position: relative;
  242. padding: 10px;
  243. border: none;;
  244. cursor: pointer;
  245. font-size: 14px;
  246. font-weight: bold;
  247. transition: all 0.3s ease;
  248. color: #000;
  249. border-radius: 50%;
  250. background-color: transparent;
  251. img {
  252. width: 16px;
  253. height: 16px;
  254. }
  255. &:hover {
  256. background-color: #ddd;
  257. }
  258. }
  259. </style>