From c51749d5167575f0a82180fe0e475396bfad2c5a Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E2=80=9Cyiyousong=E2=80=9D?= <鈥測ousong_yi@foxmail.com鈥�>
Date: Mon, 15 Jul 2024 13:56:24 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B7=BB=E5=8A=A0=E7=B3=BB=E7=BB=9F?=
 =?UTF-8?q?=E8=AF=81=E4=B9=A6=E6=A0=A1=E9=AA=8C?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 portal-manager-ui/admin/src/api/user.js       |  10 +
 .../components/licenseHint/LicenseHint.vue    | 229 ++++++++++++++++++
 .../admin/src/components/licenseHint/index.js |  26 ++
 portal-manager-ui/admin/src/main.js           |   3 +
 .../admin/src/request/request.js              |   4 +-
 portal-manager-ui/admin/src/router/index.js   |   4 +
 .../admin/src/store/modules/user.js           |  12 +
 .../admin/src/views/signIn/signIn.vue         |  26 +-
 8 files changed, 311 insertions(+), 3 deletions(-)
 create mode 100644 portal-manager-ui/admin/src/components/licenseHint/LicenseHint.vue
 create mode 100644 portal-manager-ui/admin/src/components/licenseHint/index.js

diff --git a/portal-manager-ui/admin/src/api/user.js b/portal-manager-ui/admin/src/api/user.js
index ca54a2b3..cc39d461 100644
--- a/portal-manager-ui/admin/src/api/user.js
+++ b/portal-manager-ui/admin/src/api/user.js
@@ -28,3 +28,13 @@ export function editPassword(params) {
 export function changeForgotPassword(params) {
   return http.post(`${baseURL}/zwfw/user/forgot/password`, params);
 }
+
+// 璇佷功鏈夋晥鏈熸娴�
+export function checkCipher(params) {
+  return http.get(`${baseURL}/zwfw/cipher/check`, params);
+}
+
+// 璇佷功涓婁紶
+export function uploadCipher(params) {
+  return http.post(`${baseURL}/zwfw/cipher/upload`, params);
+}
diff --git a/portal-manager-ui/admin/src/components/licenseHint/LicenseHint.vue b/portal-manager-ui/admin/src/components/licenseHint/LicenseHint.vue
new file mode 100644
index 00000000..b42eea63
--- /dev/null
+++ b/portal-manager-ui/admin/src/components/licenseHint/LicenseHint.vue
@@ -0,0 +1,229 @@
+<template>
+  <div class="license-hint" v-if="show">
+    <div class="card">
+      <div class="header">
+        <span class="icon">
+          <svg
+            fill="currentColor"
+            viewBox="0 0 20 20"
+            xmlns="http://www.w3.org/2000/svg"
+          >
+            <path
+              clip-rule="evenodd"
+              d="M18 3a1 1 0 00-1.447-.894L8.763 6H5a3 3 0 000 6h.28l1.771 5.316A1 1 0 008 18h1a1 1 0 001-1v-4.382l6.553 3.276A1 1 0 0018 15V3z"
+              fill-rule="evenodd"
+            ></path>
+          </svg>
+        </span>
+        <p class="alert">璇佷功杩囨湡鎻愮ず锛�</p>
+      </div>
+
+      <p class="message">{{ licenseInfo.msg }}</p>
+
+      <div class="actions">
+        <a-button class="read" :loading="loading" @click="handleUpload">
+          鐐瑰嚮涓婁紶鏂拌瘉涔�
+        </a-button>
+
+        <a-button
+          class="mark-as-read"
+          :disabled="isDisabled"
+          @click="handleClose"
+        >
+          鍏抽棴寮圭獥<span v-if="time">锛坽{ time }}锛�</span>
+        </a-button>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+import { uploadCipher, checkCipher } from "@/api/user";
+import { mapMutations, mapState } from "vuex";
+export default {
+  data() {
+    return {
+      accept: "application/x-zip-compressed",
+      loading: false,
+      timer: null,
+      isDisabled: true,
+      time: 0,
+      show: true,
+    };
+  },
+  computed: {
+    ...mapState("user", ["licenseInfo"]),
+  },
+  created() {
+    this.countDown();
+  },
+  methods: {
+    ...mapMutations("user", ["set_licenseInfo"]),
+    countDown() {
+      this.time = this.duration;
+      this.timer = setInterval(() => {
+        this.time -= 1;
+        if (this.time <= 0) {
+          this.isDisabled = false;
+          this.time = 0;
+          clearInterval(this.timer);
+        }
+      }, 1000);
+    },
+    handleClose() {
+      // if (this.isDisabled) return;
+      this.show = false;
+    },
+    async handleUpload() {
+      let file = await this.getFile();
+      if (!file) return;
+      this.loading = true;
+      let formData = new FormData();
+      formData.append("file", file[0]);
+      let res = await uploadCipher(formData);
+      this.loading = false;
+      if (res.code == 1) {
+        this.checkCipher();
+      }
+    },
+    // 妫€娴嬬郴缁熻瘉涔�
+    async checkCipher() {
+      let res = await checkCipher();
+      if (res.code == 1) {
+        let { startTime, endTime } = res.data;
+        let msg = `璇佷功涓婁紶鎴愬姛锛岃瘉涔︽湁鏁堟湡锛�${startTime} 鑷� ${endTime}`;
+        this.set_licenseInfo({
+          isExpire: false,
+          msg: "",
+        });
+        this.$message.success(msg);
+        this.show = false;
+      } else {
+        this.set_licenseInfo({
+          isExpire: true,
+          msg: res.msg,
+        });
+      }
+    },
+
+    // 鑾峰彇鏂囦欢
+    getFile() {
+      let input = document.createElement("input");
+      document.body.appendChild(input);
+      return new Promise((resolve, reject) => {
+        Object.assign(input, {
+          accept: this.accept,
+          onchange: () => {
+            input.files ? resolve(input.files) : reject();
+          },
+          multiple: false,
+          onerror: reject,
+          type: "file",
+          hidden: true,
+          value: null,
+        });
+        input.click();
+        document.body.removeChild(input);
+      });
+    },
+  },
+  beforeDestroy() {
+    clearInterval(this.timer);
+  },
+};
+</script>
+
+<style lang="less" scoped>
+.license-hint {
+  width: 100%;
+  height: 100%;
+  position: fixed;
+  left: 0px;
+  top: 0px;
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  background-color: rgba(0, 0, 0, 0.5);
+  z-index: 1000;
+  .card {
+    width: 320px;
+    border-radius: 16px;
+    background-color: #fff;
+    padding: 16px;
+  }
+
+  .header {
+    display: flex;
+    align-items: center;
+    grid-gap: 16px;
+    gap: 16px;
+  }
+
+  .icon {
+    flex-shrink: 0;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    border-radius: 50%;
+    background-color: #0857e8;
+    padding: 8px;
+    color: rgba(255, 255, 255, 1);
+  }
+
+  .icon svg {
+    height: 16px;
+    width: 16px;
+  }
+
+  .alert {
+    font-weight: 600;
+    color: rgba(107, 114, 128, 1);
+  }
+
+  .message {
+    margin-top: 16px;
+    color: rgba(107, 114, 128, 1);
+    line-height: 24px;
+  }
+
+  .actions {
+    margin-top: 20px;
+  }
+
+  /deep/.ant-upload {
+    width: 100%;
+  }
+
+  .mark-as-read,
+  .read {
+    width: 100%;
+    height: 44px;
+    // display: flex;
+    // align-items: center;
+    // justify-content: center;
+    // cursor: pointer;
+    border: none;
+    border-radius: 8px;
+    font-weight: 600;
+  }
+
+  .read {
+    background-color: #0857e8;
+    color: rgba(255, 255, 255, 1);
+  }
+
+  .mark-as-read {
+    margin-top: 8px;
+    background-color: rgba(249, 250, 251, 1);
+    color: rgba(107, 114, 128, 1);
+    transition: all 0.15s ease;
+  }
+
+  .mark-as-read:hover {
+    background-color: rgb(230, 231, 233);
+  }
+  // .mark-as-read[disabled] {
+  //   cursor: not-allowed;
+  // }
+}
+</style>
diff --git a/portal-manager-ui/admin/src/components/licenseHint/index.js b/portal-manager-ui/admin/src/components/licenseHint/index.js
new file mode 100644
index 00000000..fc922ed9
--- /dev/null
+++ b/portal-manager-ui/admin/src/components/licenseHint/index.js
@@ -0,0 +1,26 @@
+import vue from "vue";
+import LicenseHintModal from "./LicenseHint.vue";
+import store from "@/store"; // 瀵煎叆 Vuex store 瀹炰緥
+const licenseHintModal = vue.extend(LicenseHintModal);
+function showLicenseHintModal(duration = 5) {
+  const existingModal = document.getElementById("licenseHintModal");
+  if (!existingModal) {
+    const dom = new licenseHintModal({
+      store,
+      el: document.createElement("div"),
+      data() {
+        return {
+          duration: duration,
+        };
+      },
+    });
+    dom.$el.id = "licenseHintModal";
+    document.body.appendChild(dom.$el);
+  }
+}
+
+function registryModal() {
+  vue.prototype.$licenseHintModal = showLicenseHintModal;
+}
+
+export default registryModal;
diff --git a/portal-manager-ui/admin/src/main.js b/portal-manager-ui/admin/src/main.js
index 513d5034..24fd767c 100644
--- a/portal-manager-ui/admin/src/main.js
+++ b/portal-manager-ui/admin/src/main.js
@@ -63,6 +63,9 @@ Vue.prototype.$bus = new Vue();
 
 // swiper
 import "swiper/css/swiper.min.css";
+// 娉ㄥ唽璇佷功杩囨湡鎻愮ず寮圭獥
+import registryModal from "@/components/licenseHint";
+Vue.use(registryModal);
 
 Vue.config.productionTip = false;
 // 鍥剧墖棰勮
diff --git a/portal-manager-ui/admin/src/request/request.js b/portal-manager-ui/admin/src/request/request.js
index ac481913..eb3ec08b 100644
--- a/portal-manager-ui/admin/src/request/request.js
+++ b/portal-manager-ui/admin/src/request/request.js
@@ -42,7 +42,7 @@ axios.interceptors.response.use(
         message.error({
           content: msg,
           maxCount: 1,
-          duration: 1,
+          duration: 2,
         });
         if (msg === "闈炴硶鐢ㄦ埛,涓嶅彲璁块棶") {
           store.commit("user/reset");
@@ -117,7 +117,7 @@ axios.interceptors.response.use(
     message.error({
       message: error.message,
       maxCount: 1,
-      duration: 1,
+      duration: 2,
     });
 
     return Promise.resolve(error.response);
diff --git a/portal-manager-ui/admin/src/router/index.js b/portal-manager-ui/admin/src/router/index.js
index d362a677..5f4803ab 100644
--- a/portal-manager-ui/admin/src/router/index.js
+++ b/portal-manager-ui/admin/src/router/index.js
@@ -27,6 +27,10 @@ router.beforeEach((to, from, next) => {
   // let bol = hasIntersection(toRootPathArr, routerPath);
   if (islogin) {
     next();
+    let licenseInfo = store.getters["user/licenseInfo"];
+    if (licenseInfo.isExpire) {
+      Vue.prototype.$licenseHintModal();
+    }
     // if (routerPath.includes(to.path) || bol) {
     //   next();
     // }
diff --git a/portal-manager-ui/admin/src/store/modules/user.js b/portal-manager-ui/admin/src/store/modules/user.js
index a0b786d1..6217484f 100644
--- a/portal-manager-ui/admin/src/store/modules/user.js
+++ b/portal-manager-ui/admin/src/store/modules/user.js
@@ -12,6 +12,11 @@ export default {
     siteId: "", // 绔欑偣id
     searForm: {}, // 鎶ヨ〃鎼滅储
     routerList: [], // 鐢ㄦ埛鏉冮檺璺敱
+    licenseInfo: {
+      // 绯荤粺璇佷功淇℃伅
+      isExpire: false, // 鏄惁杩囨湡,
+      msg: "", // 杩囨湡鎻愮ず淇℃伅
+    },
   },
   getters: {
     siteId: (state) => state.siteId,
@@ -29,6 +34,9 @@ export default {
     siteTreeList(state) {
       return state.siteList;
     },
+    licenseInfo(state) {
+      return state.licenseInfo;
+    },
   },
   mutations: {
     SET_routerList(state, routerList) {
@@ -58,6 +66,10 @@ export default {
     set_siteId(state, siteId) {
       state.siteId = siteId;
     },
+    set_licenseInfo(state, { isExpire, msg }) {
+      state.licenseInfo.isExpire = isExpire;
+      state.licenseInfo.msg = msg;
+    },
     // 閲嶇疆鎵€鏈変粨搴撶姸鎬�
     reset: () => {},
   },
diff --git a/portal-manager-ui/admin/src/views/signIn/signIn.vue b/portal-manager-ui/admin/src/views/signIn/signIn.vue
index 7974b423..202d5917 100644
--- a/portal-manager-ui/admin/src/views/signIn/signIn.vue
+++ b/portal-manager-ui/admin/src/views/signIn/signIn.vue
@@ -248,7 +248,12 @@
 
 <script>
 import Swiper from "swiper";
-import { LoginInterface, changeForgotPassword, getSlogan } from "@/api/user.js";
+import {
+  LoginInterface,
+  changeForgotPassword,
+  getSlogan,
+  checkCipher,
+} from "@/api/user.js";
 import { mapMutations, mapState } from "vuex";
 import { changeAccount, changePassWord } from "@/utils/js/validate";
 import { encrypt, findSitesById } from "@/utils";
@@ -329,7 +334,9 @@ export default {
     this.createCode();
   },
   mounted() {
+    this.checkCipher();
     this.initSwiper();
+    this.$licenseHintModal();
   },
   methods: {
     ...mapMutations("user", [
@@ -337,7 +344,24 @@ export default {
       "SET_USERDATA",
       "set_siteList",
       "SET_routerList",
+      "set_licenseInfo",
     ]),
+    // 妫€娴嬬郴缁熻瘉涔︽槸鍚﹁繃鏈�
+    async checkCipher() {
+      let res = await checkCipher();
+      if (res.code == 1) {
+        this.set_licenseInfo({
+          isExpire: false,
+          msg: "",
+        });
+      } else {
+        this.set_licenseInfo({
+          isExpire: true,
+          msg: res.msg,
+        });
+        this.$licenseHintModal();
+      }
+    },
     getTitle() {
       getSlogan("signImg").then((res) => {
         if (res.code == 1) {
-- 
2.24.3