index.vue 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365
  1. <template>
  2. <headerBar title="首页">
  3. <template #title><div @click="handleSettingClick" v-log="{ describe: { action: '点击首页标题' } }">首页</div></template>
  4. </headerBar>
  5. <div class="home-container" v-loading="loading || !syncCompleted" :element-loading-text="!syncCompleted ? '正在同步配置...' : '正在加载...'">
  6. <!-- 背景图片 -->
  7. <img src="@/assets/images/home/bg.png" alt="背景图片" class="background-image" />
  8. <!-- 左侧图片区域 -->
  9. <div class="image-container left-image" @click="goCheck" v-log="{ describe: { action: '点击拍照检查入口' } }">
  10. <img src="@/assets/images/home/left.jpg" alt="拍摄产品并处理图像" class="zoom-on-hover" />
  11. <div class="overlay-text">拍摄产品<br>并处理图像</div>
  12. </div>
  13. <!-- 右侧图片区域 -->
  14. <div class="image-container right-image" @click="goShot" v-log="{ describe: { action: '点击仅处理图像入口' } }">
  15. <img src="@/assets/images/home/right.png" alt="仅处理图像" class="zoom-on-hover" />
  16. <div class="overlay-text" style="line-height: 80px;">仅处理图像</div>
  17. </div>
  18. </div>
  19. </template>
  20. <script setup lang="ts">
  21. import headerBar from "@/components/header-bar/index.vue";
  22. import { useRouter } from "vue-router";
  23. import configInfo from '@/stores/modules/config';
  24. import { ref, onMounted, onUnmounted } from 'vue';
  25. import axios from 'axios';
  26. import client from "@/stores/modules/client";
  27. import icpList from '@/utils/ipc';
  28. import packageJson from '@/../../package.json';
  29. import { getRouterUrl } from '@/utils/appfun';
  30. import useUserInfo from "@/stores/modules/user";
  31. import tokenInfo from "@/stores/modules/token";
  32. const router = useRouter();
  33. const loading = ref(true);
  34. const syncLoading = ref(false); // 同步配置的loading状态
  35. const syncCompleted = ref(false); // 同步是否完成
  36. // 用户状态管理 - 在 onMounted 中初始化
  37. let configInfoStore: any;
  38. let useUserInfoStore: any;
  39. let tokenInfoStore: any;
  40. // 版本检查相关
  41. const currentVersion = ref(packageJson.version);
  42. const latestVersion = ref('');
  43. const isLatest = ref(true);
  44. import socket from "@/stores/modules/socket";
  45. import {ElMessage} from "element-plus";
  46. // 初始化 WebSocket 状态管理
  47. const socketStore = socket();
  48. function socketConnect(){
  49. socketStore.connectSocket();
  50. }
  51. const goCheck = async () => {
  52. // 检查登录状态
  53. if (!tokenInfoStore.getToken) {
  54. useUserInfoStore.updateLoginShow(true);
  55. return;
  56. }
  57. // 如果正在同步,显示提示
  58. if (syncLoading.value) {
  59. console.log('正在同步配置,请稍候...');
  60. return;
  61. }
  62. // 如果未同步完成,等待同步
  63. if (!syncCompleted.value) {
  64. ElMessage.error('等待配置同步完成');
  65. return;
  66. }
  67. configInfoStore.updateAppModel(1);
  68. router.push({
  69. name: 'PhotographyCheck'
  70. });
  71. };
  72. const goShot = async () => {
  73. // 检查登录状态
  74. if (!tokenInfoStore.getToken) {
  75. useUserInfoStore.updateLoginShow(true);
  76. return;
  77. }
  78. // 如果正在同步,显示提示
  79. if (syncLoading.value) {
  80. console.log('正在同步配置,请稍候...');
  81. return;
  82. }
  83. // 如果未同步完成,等待同步
  84. if (!syncCompleted.value) {
  85. console.log('等待配置同步完成...');
  86. return;
  87. }
  88. socketConnect();
  89. configInfoStore.updateAppModel(2);
  90. router.push({
  91. name: 'PhotographyShot'
  92. });
  93. };
  94. // 健康检查函数
  95. const checkHealth = async () => {
  96. loading.value = false;
  97. try {
  98. const healthUrl = configInfoStore?.appConfig?.pyapp ? 'http://'+configInfoStore?.appConfig?.pyapp+':7074' : 'http://127.0.0.1:7074'
  99. const response = await axios.get(healthUrl);
  100. if (response.status === 200) {
  101. loading.value = false; // 健康检查成功,关闭 loading
  102. // 健康检查成功后,如果用户已登录则执行数据同步
  103. if (tokenInfoStore && tokenInfoStore.getToken) {
  104. const token = tokenInfoStore.getToken;
  105. if (token && token.trim() !== '') {
  106. try {
  107. syncLoading.value = true; // 开始同步
  108. syncCompleted.value = false; // 重置同步状态
  109. // 导入同步函数
  110. const { syncAfterLogin } = await import('@/apis/setting');
  111. await syncAfterLogin();
  112. console.log('健康检查后数据同步成功');
  113. syncCompleted.value = true; // 同步完成
  114. } catch (syncError) {
  115. console.error('健康检查后数据同步失败:', syncError);
  116. syncCompleted.value = false; // 同步失败
  117. // 同步失败不影响主流程
  118. } finally {
  119. syncLoading.value = false; // 结束同步loading
  120. }
  121. } else {
  122. // 未登录状态,直接设置同步完成
  123. syncCompleted.value = true;
  124. }
  125. } else {
  126. // 未登录状态,直接设置同步完成
  127. syncCompleted.value = true;
  128. }
  129. }
  130. } catch (error) {
  131. console.error('健康检查失败:', error);
  132. setTimeout(() => {
  133. checkHealth(); // 延迟检查
  134. }, 2000);
  135. // 可以在这里处理错误,例如显示错误提示
  136. }
  137. };
  138. const settingClickCount = ref(0);
  139. // 修改headerBar的点击处理函数
  140. function handleSettingClick() {
  141. console.log('handleSettingClickhandleSettingClick')
  142. settingClickCount.value++;
  143. if (settingClickCount.value >= 5) {
  144. openResourceDirectory()
  145. settingClickCount.value = 0;
  146. }
  147. setTimeout(() => {
  148. settingClickCount.value = 0;
  149. }, 3000); // 3秒内未再次点击则重置计数器
  150. }
  151. function openResourceDirectory() {
  152. const clientStore = client();
  153. clientStore.ipc.removeAllListeners(icpList.utils.shellFun);
  154. let params = {
  155. action: 'openPath',
  156. params: configInfoStore.appConfig.userDataPath.replaceAll('/', '\\')
  157. };
  158. clientStore.ipc.send(icpList.utils.shellFun, params);
  159. }
  160. // 版本号比较函数
  161. const compareVersions = (v1, v2) => {
  162. const parts1 = v1.split('.').map(Number);
  163. const parts2 = v2.split('.').map(Number);
  164. for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
  165. const num1 = parts1[i] || 0;
  166. const num2 = parts2[i] || 0;
  167. if (num1 > num2) return 1;
  168. if (num1 < num2) return -1;
  169. }
  170. return 0;
  171. };
  172. // 打开OTA窗口
  173. const openOTA = () => {
  174. const { href } = router.resolve({
  175. name: 'ota'
  176. });
  177. const clientStore = client();
  178. clientStore.ipc.removeAllListeners(icpList.utils.openMain);
  179. let params = {
  180. title: '版本更新',
  181. width: 900,
  182. height: 700,
  183. frame: true,
  184. id: 'ota',
  185. url: getRouterUrl(href)
  186. };
  187. clientStore.ipc.send(icpList.utils.openMain, params);
  188. };
  189. // 获取版本信息并检查更新
  190. const checkForUpdates = async () => {
  191. try {
  192. // 添加时间戳避免缓存问题
  193. const timestamp = new Date().getTime();
  194. const response = await axios.get('https://ossimg.valimart.net/frontend/html/zhihuiyin/yi/version.json', {
  195. params: {
  196. _t: timestamp
  197. }
  198. });
  199. // 确保 response.data 是 JSON 数据
  200. let data;
  201. if (typeof response.data === 'string') {
  202. data = JSON.parse(response.data);
  203. } else {
  204. data = response.data;
  205. }
  206. if (data.length > 0) {
  207. const latest = data[data.length - 1];
  208. latestVersion.value = latest.version;
  209. // 比较版本号
  210. isLatest.value = compareVersions(currentVersion.value, latest.version) >= 0;
  211. // 如果发现新版本,自动打开OTA窗口
  212. if (!isLatest.value) {
  213. openOTA();
  214. }
  215. }
  216. } catch (error) {
  217. console.error('检查版本更新失败:', error);
  218. // 静默处理错误,不影响用户体验
  219. }
  220. };
  221. // 监听登录成功事件
  222. const handleLoginSuccess = () => {
  223. console.log('检测到登录成功,重新检查同步状态');
  224. // 重新执行健康检查和同步
  225. checkHealth();
  226. };
  227. // 在组件挂载时执行健康检查和版本检查
  228. onMounted(() => {
  229. // 初始化 store
  230. configInfoStore = configInfo();
  231. useUserInfoStore = useUserInfo();
  232. tokenInfoStore = tokenInfo();
  233. // 监听登录成功事件
  234. window.addEventListener('login-success', handleLoginSuccess);
  235. checkHealth();
  236. // 延迟执行版本检查,避免影响健康检查
  237. setTimeout(() => {
  238. checkForUpdates();
  239. }, 1000);
  240. });
  241. // 组件卸载时清理事件监听器
  242. onUnmounted(() => {
  243. window.removeEventListener('login-success', handleLoginSuccess);
  244. });
  245. </script>
  246. <style lang="scss" scoped>
  247. .home-container {
  248. position: relative;
  249. width: 100%;
  250. height: calc(100vh - 30px);
  251. overflow: hidden;
  252. }
  253. .background-image {
  254. position: absolute;
  255. top: 0;
  256. left: 0;
  257. width: 100%;
  258. height: 100%;
  259. object-fit: cover;
  260. z-index: -1;
  261. }
  262. .image-container {
  263. position: absolute;
  264. cursor: pointer;
  265. width: 400px; /* 设置宽度 */
  266. overflow: hidden;
  267. // box-shadow: 0 4px 10px rgba(0, 0, 0, 0.6); /* 添加阴影效果 */
  268. border-radius: 30px;
  269. transition: transform 0.3s ease;
  270. .zoom-on-hover {
  271. transition: transform 0.3s ease;
  272. width: 100%; /* 确保图片充满容器 */
  273. height: 100%; /* 确保图片充满容器 */
  274. object-fit: cover; /* 裁剪图片以适应容器 */
  275. display: block;
  276. }
  277. &:hover {
  278. box-shadow: 0 4px 10px rgba(0, 0, 0, 0.6);
  279. transform: translateY(-55%);
  280. transform:translateY(-55%) scale(1.05);
  281. }
  282. }
  283. .left-image {
  284. top: 50%;
  285. right: 50%;
  286. transform: translateY(-50%);
  287. margin-right: 50px;
  288. }
  289. .right-image {
  290. top: 50%;
  291. left: 50%;
  292. transform: translateY(-50%);
  293. margin-left: 50px;
  294. }
  295. .overlay-text {
  296. position: absolute;
  297. top: 50%;
  298. left: 50%;
  299. transform: translate(-50%, -50%);
  300. color: white;
  301. font-size: 24px;
  302. text-align: center;
  303. z-index: 1;
  304. background-color: rgba(0, 0, 0, 0.5);
  305. padding: 10px 40px;
  306. line-height: 50px;
  307. min-height: 80px;
  308. min-width: 250px;
  309. }
  310. </style>