Kaynağa Gözat

添加升级维护确认

shmily1213 1 ay önce
ebeveyn
işleme
0251af6315

+ 4 - 2
src/App.vue

@@ -4,8 +4,10 @@ import { checkUpdate } from "@/utils/update.ts";
 export default {
   onLaunch: function () {
     console.log('App Launch')
-    const appStore = useAppStore();
-    appStore.init();
+    // const appStore = useAppStore();
+    // if (!appStore.isMaintaining) {
+    //   appStore.init();
+    // }
     // #ifdef MP-WEIXIN
     checkUpdate();
     // #endif

+ 8 - 0
src/api/modules/system.ts

@@ -102,3 +102,11 @@ export function getCaptchaImage() {
 export function validateSms(params: SmsRequestDTO) {
   return flyio.post('/front/comm/validateSms', null, { params }) as Promise<ApiResponse<any>>;
 }
+
+/**
+ * 获取系统公告
+ * @returns 
+ */
+export function getSystemNotice(noticeType: number) {
+  return flyio.get('/front/news/getSystemNotice', { noticeType, status: '0' }) as Promise<ApiResponseList<System.SystemNotice>>;
+}

+ 10 - 1
src/api/modules/voluntary.ts

@@ -1,4 +1,4 @@
-import {ApiResponse, DictItem} from "@/types";
+import type {ApiResponse, ApiResponseList, DictItem, News} from "@/types";
 import flyio from "../flyio";
 import {
     EnrollRule,
@@ -289,3 +289,12 @@ export function sortVoluntaryByUniversity(universityIdList: string[]) {
     // 按传入院校id顺序,保存排序优先级
     return flyio.post('/voluntary/sortVoluntaryByUniversity', {universityIdList})
 }
+
+
+/**
+ * 获取志愿填报规则
+ * @returns 
+ */
+export function getVoluntaryRule() {
+  return flyio.get('/front/news/getMainListV2') as Promise<ApiResponseList<News.Guide>>
+}

+ 1 - 24
src/global.d.ts

@@ -13,30 +13,7 @@ declare global {
    * 微信小程序 API 接口
    */
   interface Wx {
-    /**
-     * 打开浏览器
-     * @param options - 配置选项
-     * @param options.url - 要打开的网址
-     * @param options.success - 成功回调
-     * @param options.fail - 失败回调
-     */
-    openBrowser?: (options: {
-      url: string;
-      success?: () => void;
-      fail?: (err: any) => void;
-    }) => void;
-  }
-  
-  /**
-   * 支付宝小程序 API 接口
-   */
-  interface My {
-    /**
-     * 打开浏览器
-     * @param options - 配置选项
-     * @param options.url - 要打开的网址
-     */
-    openBrowser?: (options: { url: string }) => void;
+    exitMiniProgram: (options?: { success?: () => void; fail?: (err: any) => void; complete?: () => void }) => void;
   }
   
   /**

+ 24 - 24
src/pages.json

@@ -195,30 +195,30 @@
             "navigationBarTitleText": ""
           }
         },
-         {
-           "path": "pages/vhs/index/index",
-           "style": {
-             "navigationBarTitleText": ""
-           }
-         },
-         {
-           "path": "pages/vhs/detail/detail",
-           "style": {
-             "navigationBarTitleText": ""
-           }
-         },
-         {
-           "path": "pages/vhs/edit/edit",
-           "style": {
-             "navigationBarTitleText": ""
-           }
-         },
-         {
-           "path": "pages/vhs/list/list",
-           "style": {
-             "navigationBarTitleText": ""
-           }
-         },
+         // {
+         //   "path": "pages/vhs/index/index",
+         //   "style": {
+         //     "navigationBarTitleText": ""
+         //   }
+         // },
+         // {
+         //   "path": "pages/vhs/detail/detail",
+         //   "style": {
+         //     "navigationBarTitleText": ""
+         //   }
+         // },
+         // {
+         //   "path": "pages/vhs/edit/edit",
+         //   "style": {
+         //     "navigationBarTitleText": ""
+         //   }
+         // },
+         // {
+         //   "path": "pages/vhs/list/list",
+         //   "style": {
+         //     "navigationBarTitleText": ""
+         //   }
+         // },
         {
           "path": "pages/ranking/ranking",
           "style": {

+ 43 - 0
src/pagesMain/pages/index/components/index-maintain-popup.vue

@@ -0,0 +1,43 @@
+<template>
+  <ie-popup title="" mode="center" ref="popupRef" :close-on-click-overlay="false" :showToolbar="false">
+    <view class="w-[88vw] relative">
+      <ie-image :is-oss="true" src="/study-bg15.png" custom-class="absolute top-0 left-0 w-full h-392 z-0"
+        mode="aspectFill" />
+      <view class="text-center my-40 text-[16px] text-fore-title font-bold">系统维护公告</view>
+      <view class="px-30 mt-30 text-[15px] whitespace-pre leading-[24px]">
+        {{ maintainContent }}
+      </view>
+      <view class="px-30 my-40">
+        <uv-button type="primary" shape="circle" @click="handleConfirm">知道了</uv-button>
+      </view>
+    </view>
+  </ie-popup>
+</template>
+<script lang="ts" setup>
+import { useAppStore } from '@/store/appStore';
+
+const appStore = useAppStore();
+const popupRef = ref();
+let callback: ((value: void | PromiseLike<void>) => void) | null = null;
+const maintainContent = computed(() => {
+  return appStore.maintainNotice?.content || '';
+});
+const handleConfirm = () => {
+  appStore.maintainNotice!.read = true;
+  if (callback !== null) {
+    callback();
+  }
+  close();
+}
+const open = () => {
+  return new Promise((resolve) => {
+    callback = resolve;
+    popupRef.value.open();
+  });
+}
+const close = () => {
+  popupRef.value.close();
+}
+defineExpose({ open, close })
+</script>
+<style lang="scss" scoped></style>

+ 16 - 1
src/pagesMain/pages/index/components/index-map.vue

@@ -37,6 +37,7 @@
 import { routes } from "@/common/routes";
 import { useTransferPage } from "@/hooks/useTransferPage";
 import { useUserStore } from "@/store/userStore";
+import { getVoluntaryRule } from "@/api/modules/voluntary";
 
 interface SiteMap {
   title: string;
@@ -47,6 +48,7 @@ interface SiteMap {
 
 const userStore = useUserStore()
 const { transferTo } = useTransferPage()
+const ruleRefId = ref('');
 
 const goSimulate = async () => {
   const isLogin = await userStore.checkLogin();
@@ -66,7 +68,7 @@ const goSimulate = async () => {
 const maps = computed<SiteMap[]>(() => [{
   title: '本省规则',
   desc: '填志愿不踩坑',
-  pagePath: routes.newsDetail + '?id=' + (userStore.isHN ? 1065 : 1078)
+  pagePath: ruleRefId.value ? routes.newsDetail + '?id=' + ruleRefId.value : ''
 }, {
   title: '自我评价',
   desc: '了解自身优势',
@@ -103,6 +105,19 @@ const handleMap = (m: SiteMap) => {
   if (!m.pagePath) return
   transferTo(m.pagePath)
 }
+const loadData = () => {
+  getVoluntaryRule().then(res => {
+    if (res.rows.length) {
+      ruleRefId.value = res.rows[0].refIds;
+    } else {
+      ruleRefId.value = '';
+    }
+  });
+}
+
+onLoad(() => {
+  loadData();
+});
 </script>
 
 <style scoped></style>

+ 10 - 1
src/pagesMain/pages/index/index.vue

@@ -25,6 +25,7 @@
     <template #tabbar>
       <ie-tabbar :active="0" />
       <index-popup ref="popupRef" />
+      <index-maintain-popup ref="maintainPopupRef" />
     </template>
   </ie-page>
 </template>
@@ -35,13 +36,16 @@ import IndexGuide from './components/index-guide.vue';
 import IndexNews from './components/index-news.vue';
 import IndexMap from "./components/index-map.vue";
 import indexPopup from './components/index-popup.vue';
+import indexMaintainPopup from './components/index-maintain-popup.vue';
 
 import { useUserStore } from '@/store/userStore';
 // @ts-ignore
 import { useTransferPage } from '@/hooks/useTransferPage';
+import { useAppStore } from '@/store/appStore';
 import { useNavbar } from '@/hooks/useNavbar';
 import { onPageShow } from '@dcloudio/uni-app';
 
+const appStore = useAppStore();
 const { routes, transferTo } = useTransferPage();
 const { baseStickyTop } = useNavbar();
 const scrollTop = ref(0);
@@ -96,7 +100,12 @@ onPageScroll((e) => {
     scrollTop.value = e.scrollTop;
   }
 });
-onMounted(() => {
+const maintainPopupRef = ref();
+onMounted(async () => {
+  if (appStore.hasMaintain && !appStore.maintainNotice!.read) {
+    await uni.$uv.sleep(500);
+    await maintainPopupRef.value?.open();
+  }
   checkProvinceInfo();
   if (userStore.isLogin) {
     checkInfo();

+ 44 - 0
src/pagesMain/pages/splash/components/maintain-popup.vue

@@ -0,0 +1,44 @@
+<template>
+  <ie-popup title="" mode="center" ref="popupRef" :close-on-click-overlay="false" :showToolbar="false">
+    <view class="w-[88vw] relative">
+      <ie-image :is-oss="true" src="/study-bg15.png" custom-class="absolute top-0 left-0 w-full h-392 z-0"
+        mode="aspectFill" />
+      <view class="text-center my-40 text-[16px] text-fore-title font-bold">系统维护中</view>
+      <view class="px-30 mt-30 text-[15px] whitespace-pre leading-[24px]">
+        <view>预计结束时间:{{ endTime }}</view>
+        <view>系统维护期间暂停使用,敬请谅解!</view>
+      </view>
+      <view class="px-30 my-40">
+        <uv-button type="primary" shape="circle" @click="handleConfirm">知道了</uv-button>
+      </view>
+    </view>
+  </ie-popup>
+</template>
+<script lang="ts" setup>
+import { useAppStore } from '@/store/appStore';
+
+const appStore = useAppStore();
+const popupRef = ref();
+let callback: ((value: void | PromiseLike<void>) => void) | null = null;
+const endTime = computed(() => {
+  return appStore.maintainNotice?.endTime || '';
+});
+const handleConfirm = () => {
+  appStore.maintainNotice!.read = true;
+  if (callback !== null) {
+    callback();
+  }
+  close();
+}
+const open = () => {
+  return new Promise((resolve) => {
+    callback = resolve;
+    popupRef.value.open();
+  });
+}
+const close = () => {
+  popupRef.value.close();
+}
+defineExpose({ open, close })
+</script>
+<style lang="scss" scoped></style>

+ 43 - 18
src/pagesMain/pages/splash/splash.vue

@@ -7,49 +7,74 @@
         </template>
       </uv-image>
     </view>
+    <maintain-popup ref="maintainPopupRef" />
   </ie-page>
 </template>
 
 <script lang="ts" setup>
 import config from '@/config';
+import { useEnv } from '@/hooks/useEnv';
 import { useAppStore } from '@/store/appStore';
-import { useAppConfig } from '@/hooks/useAppConfig';
-import { useUserStore } from '@/store/userStore';
 import { useTransferPage } from '@/hooks/useTransferPage';
 import { load } from '@/utils/loadFont';
+import MaintainPopup from './components/maintain-popup.vue';
 
+const { platform } = useEnv();
 const splashTimeout = 1200;
 const appStore = useAppStore();
-const userStore = useUserStore();
 const { transferTo, routes } = useTransferPage();
+
 // #ifdef H5
 uni.hideTabBar();
 // #endif
 const imgLaunch = computed(() => {
   return config.ossUrl + '/launch.png'
 });
+const maintainPopupRef = ref();
 const handleLoad = () => {
   if (typeof window !== 'undefined' && window !== null && window.platform === 'wap2app') {
     window.webviewBridge.getStatusBarHeight().then(height => {
       appStore.statusBarHeight = height;
     });
   }
-  // 执行初始化的操作:预加载字典、提前校验token是否有效等
-  appStore.init().then(async () => {
-    try {
-      const usedTime = await load();
-      if (usedTime < splashTimeout) {
-        await new Promise(resolve => setTimeout(resolve, splashTimeout - usedTime));
-      }
-    } catch (error) {
-      console.error('初始化失败: ', error);
-    } finally {
-      transferTo('/pagesMain/pages/index/index', {
-        type: 'reLaunch'
+  appStore.getMaintainNotice().then(async (isMaintaining) => {
+    // 执行初始化的操作:预加载字典、提前校验token是否有效等
+    if (!isMaintaining) {
+      appStore.init().then(async () => {
+        try {
+          const usedTime = await load();
+          if (usedTime < splashTimeout) {
+            await new Promise(resolve => setTimeout(resolve, splashTimeout - usedTime));
+          }
+        } catch (error) {
+          console.error('初始化失败: ', error);
+        } finally {
+          transferTo('/pagesMain/pages/index/index', {
+            type: 'reLaunch'
+          });
+          // transferTo('/pagesStudy/pages/knowledge-practice/knowledge-practice', {
+          //   type: 'reLaunch'
+          // });
+        }
       });
-      // transferTo('/pagesStudy/pages/knowledge-practice/knowledge-practice', {
-      //   type: 'reLaunch'
-      // });
+    } else {
+      setTimeout(() => {
+        nextTick(() => {
+          maintainPopupRef.value?.open().then(() => {
+            // #ifdef H5
+            if (platform.value === 'wap2app') {
+              plus.runtime.quit();
+            } else {
+              window.close();
+            }
+            // #endif
+            // #ifdef MP-WEIXIN
+            wx.exitMiniProgram();
+            // #endif
+          });
+        });
+      }, 800);
+
     }
   });
 };

+ 78 - 4
src/store/appStore.ts

@@ -1,9 +1,10 @@
 import { defineStore } from 'pinia';
-import { AppStoreState, ConfigItem, DictItem, PickerItem } from '@/types';
-import { getConfig, getProvinces } from '@/api/modules/system';
+import type { AppStoreState, ConfigItem, DictItem, PickerItem, System } from '@/types';
+import { getConfig, getProvinces, getSystemNotice } from '@/api/modules/system';
 import { useDictStore } from '@/store/dictStore';
 import { EnumDictName } from '@/common/enum';
 import { useUserStore } from '@/store/userStore';
+
 const preloadDicts: string[] = [
   EnumDictName.EXAM_TYPE
 ];
@@ -15,7 +16,8 @@ export const useAppStore = defineStore('ie-app', {
       activeTabbar: 0,
       statusBarHeight: 0,
       systemInfo: null as UniApp.GetSystemInfoResult | null,
-      appConfig: [] as ConfigItem[]
+      appConfig: [] as ConfigItem[],
+      maintainNotice: null
     }
   },
   getters: {
@@ -31,6 +33,12 @@ export const useAppStore = defineStore('ie-app', {
     isMp(): boolean {
       return this.sysInfo.uniPlatform === 'mp-weixin';
     },
+    hasMaintain(): boolean {
+      return this.maintainNotice !== null;
+    },
+    isMaintaining(): boolean {
+      return isBetweenTime(this.maintainNotice?.beginTime, this.maintainNotice?.endTime);
+    },
   },
   actions: {
     async init() {
@@ -84,7 +92,31 @@ export const useAppStore = defineStore('ie-app', {
     getAppConfig(key: string): string | undefined {
       return this.appConfig.find(item => item.configKey === key)?.configValue;
     },
-
+    async getMaintainNotice() {
+      try {
+        const { rows } = await getSystemNotice(2);
+        if (rows && rows.length) {
+          const notice = extractMaintenanceContent(rows[0].noticeContent);
+          if (notice) {
+            const { beginTime, endTime, content } = notice;
+            this.maintainNotice = {
+              beginTime,
+              endTime,
+              content,
+              read: this.maintainNotice?.read || false
+            };
+            return Promise.resolve(isBetweenTime(beginTime, endTime));
+          }
+          return Promise.resolve(false);
+        } else {
+          this.maintainNotice = null;
+          return Promise.resolve(false);
+        }
+      } catch (error) {
+        console.error('获取维护公告失败:', error);
+        return Promise.resolve(false);
+      }
+    },
     setActiveTabbar(index: number) {
       this.activeTabbar = index;
     },
@@ -102,3 +134,45 @@ export const useAppStore = defineStore('ie-app', {
     omit: [],
   }
 });
+
+// 将中文格式转换为标准格式
+const convertToStandardFormat = (dateStr: string): string => {
+  // 使用正则提取各部分并补零
+  const match = dateStr.match(/(\d{4})年(\d{1,2})月(\d{1,2})日(\d{1,2}):(\d{1,2}):(\d{1,2})/);
+  if (!match) {
+    return dateStr
+      .replace(/年/g, '-')
+      .replace(/月/g, '-')
+      .replace(/日/g, ' ');
+  }
+
+  // 对月、日、时、分、秒补零
+  const [, year, month, day, hour, minute, second] = match;
+  return `${year}-${month.padStart(2, '0')}-${day.padStart(2, '0')} ${hour.padStart(2, '0')}:${minute.padStart(2, '0')}:${second.padStart(2, '0')}`;
+};
+
+function extractMaintenanceContent(htmlString: string): System.MaintainNotice | null {
+  const content = htmlString.replaceAll("</p><p>", "\n").replaceAll("</p>", "").replaceAll("<br>", "").replaceAll("<p>", "");
+  // 使用正则表达式匹配维护时间范围(完整的一次匹配)
+  const timeMatch = content.match(/维护开始时间:(\d{4}年\d{1,2}月\d{1,2}日\d{1,2}:\d{1,2}:\d{1,2})\n维护结束时间:(\d{4}年\d{1,2}月\d{1,2}日\d{1,2}:\d{1,2}:\d{1,2})/);
+  if (timeMatch) {
+    // 提取开始时间和结束时间
+    const startTimeStr = timeMatch[1];
+    const endTimeStr = timeMatch[2];
+    const beginTime = convertToStandardFormat(startTimeStr);
+    const endTime = convertToStandardFormat(endTimeStr);
+
+    return { content, beginTime, endTime, read: false };
+  }
+  return null;
+}
+
+const isBetweenTime = (beginTime?: string, endTime?: string) => {
+  const now = Date.now();
+  if (beginTime && endTime) {
+    beginTime = beginTime.replaceAll('-', '/');
+    endTime = endTime.replaceAll('-', '/');
+    return new Date(beginTime).getTime() <= now && new Date(endTime).getTime() >= now;
+  }
+  return false;
+}

+ 1 - 0
src/types/index.ts

@@ -82,6 +82,7 @@ export interface AppStoreState {
   statusBarHeight: number;
   systemInfo: UniApp.GetSystemInfoResult | null;
   appConfig: ConfigItem[];
+  maintainNotice: System.MaintainNotice | null;
 }
 
 export interface DictStoreState {

+ 16 - 0
src/types/system.ts

@@ -3,4 +3,20 @@ export type ProvinceItem = {
   areaName: string;
   provinceName: string;
   children: ProvinceItem[]
+}
+
+export type SystemNotice = {
+  id: number;
+  noticeTitle: string;
+  noticeContent: string;
+  noticeType: string;
+  status: string;
+  createdAt: string;
+}
+
+export interface MaintainNotice {
+  content: string;
+  beginTime: string;
+  endTime: string;
+  read: boolean;
 }

+ 1 - 20
src/utils/uni-tool.ts

@@ -209,28 +209,9 @@ const tool: IeTool = {
     })
     // #endif
 
-    // #ifdef MP-ALIPAY
-    // 支付宝小程序
-    if (my.openBrowser) {
-      my.openBrowser({ url: url })
-    } else {
-      // 降级处理
-      uni.setClipboardData({
-        data: url,
-        success: () => {
-          uni.showModal({
-            title: '提示',
-            content: '链接已复制,请在浏览器中打开',
-            showCancel: false
-          })
-        }
-      })
-    }
-    // #endif
-
     // 其他平台,如App、快应用等,可以根据需要补充
     // 对于不支持直接打开浏览器的平台,使用降级方案
-    // #ifndef H5 || MP-WEIXIN || MP-ALIPAY
+    // #ifndef H5 || MP-WEIXIN
     uni.setClipboardData({
       data: url,
       success: () => {