diff --git a/portal-manager-ui/admin/src/api/user.js b/portal-manager-ui/admin/src/api/user.js index 890ab1181f140ef69a837f9d84da624164e36c2e..cc39d4616e21cab983c807ecf644d7095d3e331e 100644 --- a/portal-manager-ui/admin/src/api/user.js +++ b/portal-manager-ui/admin/src/api/user.js @@ -16,6 +16,10 @@ export function LogoutInterface(params) { export function changePassword(params) { return http.post(`${baseURL}/zwfw/user/change/password`, params); } +// 1.2.5. 鏌ヨ闂ㄦ埛鍙e彿 +export function getSlogan(params) { + return http.post(`${baseURL}/base/param/key?key=${params}`); +} // 绠$悊鍛樹慨鏀瑰瘑鐮� export function editPassword(params) { return http.post(`${baseURL}/zwfw/user/reset/password`, params); @@ -24,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 0000000000000000000000000000000000000000..7ac03934e54f2c5b4ee1841874643eb0ed6a52e2 --- /dev/null +++ b/portal-manager-ui/admin/src/components/licenseHint/LicenseHint.vue @@ -0,0 +1,238 @@ +<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"> + <div class="read" @click="handleUpload"> + <div class="loading-btn" v-if="loading"> + <span>涓婁紶涓�</span><Loading></Loading> + </div> + <span v-else>鐐瑰嚮涓婁紶鏂拌瘉涔�</span> + </div> + + <div class="mark-as-read" :disabled="isDisabled" @click="handleClose"> + 鍏抽棴寮圭獥<span v-if="time">锛坽{ time }}锛�</span> + </div> + </div> + </div> + </div> +</template> + +<script> +import { uploadCipher, checkCipher } from "@/api/user"; +import { mapMutations, mapState } from "vuex"; +import Loading from "./Loading.vue"; +export default { + components: { + Loading, + }, + 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() { + if (this.loading) return; + 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-size: 14px; + font-weight: 600; + } + + .read { + background-color: #0857e8; + color: rgba(255, 255, 255, 1); + } + .loading-btn { + display: flex; + justify-content: center; + gap: 10px; + } + .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/Loading.vue b/portal-manager-ui/admin/src/components/licenseHint/Loading.vue new file mode 100644 index 0000000000000000000000000000000000000000..b99b80ae603add1435c020b81451f21d434df679 --- /dev/null +++ b/portal-manager-ui/admin/src/components/licenseHint/Loading.vue @@ -0,0 +1,69 @@ +<template> + <div class="loader"> + <div class="dot dot-1"></div> + <div class="dot dot-2"></div> + <div class="dot dot-3"></div> + <!-- <div class="dot dot-4"></div> + <div class="dot dot-5"></div> --> + </div> +</template> + +<script> +export default {}; +</script> + +<style lang="less" scoped> +.loader { + display: flex; + justify-content: center; + align-items: center; + gap: 5px; +} + +.dot { + display: inline-block; + width: 6px; + height: 6px; + border-radius: 50%; + background-color: #fff; + -webkit-animation: dot-pulse2 1.5s ease-in-out infinite; + animation: dot-pulse2 1.5s ease-in-out infinite; +} + +.dot-1 { + animation-delay: 0s; +} + +.dot-2 { + animation-delay: 0.3s; +} + +.dot-3 { + animation-delay: 0.6s; +} + +// .dot-4 { +// animation-delay: 0.9s; +// } + +// .dot-5 { +// animation-delay: 1.2s; +// } + +@keyframes dot-pulse2 { + 0% { + transform: scale(0.5); + opacity: 0.5; + } + + 50% { + transform: scale(1); + opacity: 1; + } + + 100% { + transform: scale(0.5); + opacity: 0.5; + } +} +</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 0000000000000000000000000000000000000000..fc922ed9628d421d5cff5acaf687ccd586d439c2 --- /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 3a08de92b2ef012a0ee38017bbb3db586ffacfd1..20ce2922e73d329dbfc0f3a11474e7c195b669ba 100644 --- a/portal-manager-ui/admin/src/main.js +++ b/portal-manager-ui/admin/src/main.js @@ -61,6 +61,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/router/index.js b/portal-manager-ui/admin/src/router/index.js index d362a67764f3acd07fa6d4afb11be85295d1abb0..5f4803abaef590927103a72de74bf026558ff559 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 a0b786d1653e23a4858ed9eeecb9eaaabac7a55f..6217484f65a91afb7cb808578af082e1f2d9ffb1 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 7cf729f7600f09d6be0dcda85d69d07359c0afda..fbea0ebac67434dec73792ddf03c8c2ab1395710 100644 --- a/portal-manager-ui/admin/src/views/signIn/signIn.vue +++ b/portal-manager-ui/admin/src/views/signIn/signIn.vue @@ -245,7 +245,11 @@ <script> import Swiper from "swiper"; -import { LoginInterface, changeForgotPassword } from "@/api/user.js"; +import { + LoginInterface, + changeForgotPassword, + checkCipher, +} from "@/api/user.js"; import { mapMutations, mapState } from "vuex"; import { changeAccount, changePassWord } from "@/utils/js/validate"; import { encrypt, findSitesById } from "@/utils"; @@ -323,7 +327,9 @@ export default { this.createCode(); }, mounted() { + this.checkCipher(); this.initSwiper(); + this.$licenseHintModal(); }, methods: { ...mapMutations("user", [ @@ -331,7 +337,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(); + } + }, initSwiper() { this.mySwiper = new Swiper(".mySwiper", { effect: "cube", // 鏂瑰潡鍔ㄧ敾