App.vue 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. <template>
  2. <el-config-provider :locale="zhCn">
  3. <!-- 全局加载界面(Splash Screen) -->
  4. <transition name="splash-fade">
  5. <div v-if="showSplash" class="splash-screen">
  6. <div class="splash-content">
  7. <div class="splash-logo">智媒通</div>
  8. <div class="splash-dots">
  9. <span></span>
  10. <span></span>
  11. <span></span>
  12. </div>
  13. <p class="splash-text">
  14. <template v-if="!servicesReady">
  15. 正在启动服务...
  16. <span class="service-status">
  17. Node: {{ serviceStatus.node ? '✓' : '⟳' }}
  18. Python: {{ serviceStatus.python ? '✓' : '⟳' }}
  19. </span>
  20. </template>
  21. <template v-else>加载完成!</template>
  22. </p>
  23. </div>
  24. </div>
  25. </transition>
  26. <router-view />
  27. </el-config-provider>
  28. </template>
  29. <script setup lang="ts">
  30. import { ref, onMounted, onUnmounted } from 'vue';
  31. import { useRouter } from 'vue-router';
  32. import zhCn from 'element-plus/es/locale/lang/zh-cn';
  33. import { useServerStore } from '@/stores/server';
  34. const showSplash = ref(true);
  35. const servicesReady = ref(false);
  36. const serviceStatus = ref({ node: false, python: false });
  37. const router = useRouter();
  38. const serverStore = useServerStore();
  39. // 自动配置本地服务 URL(不写入数据库,只写入本地 store)
  40. async function autoConfigLocalServices() {
  41. try {
  42. const urls = await window.electronAPI?.getLocalUrls?.();
  43. if (urls) {
  44. // 直接覆盖 store 中的配置,指向本地服务
  45. serverStore.setSingleServer({
  46. name: '本地服务',
  47. url: urls.nodeUrl,
  48. pythonServiceUrl: urls.pythonUrl,
  49. });
  50. console.log('[App] 本地服务已自动配置:', urls.nodeUrl, urls.pythonUrl);
  51. }
  52. } catch (e) {
  53. console.warn('[App] 获取本地 URL 失败:', e);
  54. }
  55. }
  56. // 监听服务状态变化
  57. async function onServicesStatusChanged(status: { nodeOk: boolean; pythonOk: boolean }) {
  58. serviceStatus.value = { node: status.nodeOk, python: status.pythonOk };
  59. if (status.nodeOk && status.pythonOk) {
  60. servicesReady.value = true;
  61. serverStore.servicesReady = true; // 通知全局服务已就绪
  62. // 自动配置本地服务
  63. autoConfigLocalServices();
  64. // 路由首次导航完成后隐藏加载界面
  65. router.isReady().then(() => {
  66. setTimeout(() => {
  67. showSplash.value = false;
  68. }, 300);
  69. });
  70. }
  71. }
  72. onMounted(() => {
  73. // 监听服务状态
  74. window.electronAPI?.onServicesStatusChanged(onServicesStatusChanged);
  75. });
  76. onUnmounted(() => {
  77. // 清理监听
  78. window.electronAPI?.removeServicesStatusListener?.();
  79. });
  80. </script>
  81. <style>
  82. html, body, #app {
  83. height: 100%;
  84. margin: 0;
  85. padding: 0;
  86. }
  87. </style>
  88. <style scoped>
  89. .splash-screen {
  90. position: fixed;
  91. top: 0;
  92. left: 0;
  93. width: 100%;
  94. height: 100%;
  95. background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  96. display: flex;
  97. align-items: center;
  98. justify-content: center;
  99. z-index: 99999;
  100. }
  101. .splash-content {
  102. text-align: center;
  103. color: #fff;
  104. }
  105. .splash-logo {
  106. font-size: 42px;
  107. font-weight: 700;
  108. letter-spacing: 4px;
  109. margin-bottom: 40px;
  110. text-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
  111. }
  112. .splash-dots {
  113. display: flex;
  114. justify-content: center;
  115. gap: 8px;
  116. margin-bottom: 24px;
  117. }
  118. .splash-dots span {
  119. width: 10px;
  120. height: 10px;
  121. border-radius: 50%;
  122. background: rgba(255, 255, 255, 0.6);
  123. animation: dot-bounce 1.4s ease-in-out infinite;
  124. }
  125. .splash-dots span:nth-child(1) {
  126. animation-delay: 0s;
  127. }
  128. .splash-dots span:nth-child(2) {
  129. animation-delay: 0.2s;
  130. }
  131. .splash-dots span:nth-child(3) {
  132. animation-delay: 0.4s;
  133. }
  134. @keyframes dot-bounce {
  135. 0%, 80%, 100% {
  136. transform: scale(0.6);
  137. opacity: 0.4;
  138. background: rgba(255, 255, 255, 0.4);
  139. }
  140. 40% {
  141. transform: scale(1);
  142. opacity: 1;
  143. background: #fff;
  144. }
  145. }
  146. .splash-text {
  147. font-size: 16px;
  148. opacity: 0.8;
  149. margin: 0;
  150. animation: text-pulse 2s ease-in-out infinite;
  151. }
  152. .service-status {
  153. display: block;
  154. margin-top: 8px;
  155. font-size: 14px;
  156. opacity: 0.7;
  157. }
  158. @keyframes text-pulse {
  159. 0%, 100% { opacity: 0.5; }
  160. 50% { opacity: 1; }
  161. }
  162. .splash-fade-leave-active {
  163. transition: opacity 0.4s ease;
  164. }
  165. .splash-fade-leave-to {
  166. opacity: 0;
  167. }
  168. </style>