index.vue 10 KB

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