Commit 640f1026 authored by 赵啸非's avatar 赵啸非

Merge remote-tracking branch 'origin/master'

parents 9391ec1d 531fe6df
......@@ -31,7 +31,6 @@
"secure-ls": "^1.2.6",
"swiper": "5",
"three": "^0.155.0",
"v-scale-screen": "1.0.2",
"v-viewer": "^1.6.4",
"vue": "^2.6.14",
"vue-highlightjs": "^1.3.3",
......
......@@ -16,6 +16,10 @@ export function LogoutInterface(params) {
export function changePassword(params) {
return http.post(`${baseURL}/zwfw/user/change/password`, params);
}
// 1.2.5. 查询门户口号
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);
}
<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
class="apply"
href="https://license.scsmile.cn/#/login"
target="_blank"
>点击申请</a
>
<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 date = this.$moment().format("YYYY-MM-DD");
let res = await checkCipher();
if (res.code == 1) {
let { startTime, endTime } = res.data;
let msg = `证书上传成功,证书有效期:${startTime}${endTime}`;
this.set_licenseInfo({
isExpire: false,
date,
msg: "",
});
this.$message.success(msg);
this.show = false;
} else {
this.set_licenseInfo({
isExpire: true,
date,
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;
display: flex;
flex-direction: column;
gap: 8px;
}
/deep/.ant-upload {
width: 100%;
}
.mark-as-read,
.read,
.apply {
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: #1aa06d;
color: rgba(255, 255, 255, 1);
}
.apply {
background-color: #0857e8;
color: rgba(255, 255, 255, 1);
}
.loading-btn {
display: flex;
justify-content: center;
gap: 10px;
}
.mark-as-read {
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>
<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>
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;
<template>
<canvas class="my-canvas" ref="MyCanvas" v-if="show"></canvas>
</template>
<script>
import { mapState } from "vuex";
export default {
data() {
return {
show: true,
};
},
computed: {
...mapState("user", ["licenseInfo"]),
},
watch: {
licenseInfo: {
handler(newVal) {
if (!newVal.isExpire) {
this.show = false;
}
},
deep: true,
},
},
mounted() {
this.weatherMaskCanvasFn();
},
methods: {
weatherMaskCanvasFn() {
let len = this.watermark.len || this.watermark?.text?.length || 3;
const canvas = this.$refs.MyCanvas;
let fontSize = this.watermark.fontSize || 14;
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
const context = canvas.getContext("2d");
context.clearRect(0, 0, canvas.width, canvas.height);
context.translate(canvas.width / 2, canvas.height / 2);
context.rotate(((this.watermark.rotate || -30) * Math.PI) / 180);
context.font = `${fontSize}px Vedana`;
context.fillStyle = this.watermark.color || "#C8C8C8";
context.globalAlpha = this.watermark.opacity || 0.5;
const textWidth = context.measureText(this.watermark.text).width;
const textHeight = fontSize;
const diagonal = Math.sqrt(
textWidth * textWidth + textHeight * textHeight
);
const patternWidth = diagonal * len;
const patternHeight = diagonal * len;
for (let x = -canvas.width; x < canvas.width; x += patternWidth) {
for (let y = -canvas.height; y < canvas.height; y += patternHeight) {
context.fillText(this.watermark.text, x, y);
}
}
context.setTransform(1, 0, 0, 1, 0, 0);
},
},
};
</script>
<style lang="less" scoped>
.my-canvas {
width: 100%;
height: 100%;
pointer-events: none;
position: fixed;
z-index: 999999;
left: 0;
top: 0;
}
</style>
import vue from "vue";
import WatermarkCom from "./Watermark.vue";
import store from "@/store"; // 导入 Vuex store 实例
const watermarkCom = vue.extend(WatermarkCom);
let defaultParams = {
text: "证书已失效", // 水印文案
opacity: "0.6", // 透明度
fontSize: "18", // 字体大小
color: "#C8C8C8", // 字体颜色
rotate: "-30", // 旋转角度deg
len: 2, // 水平间隔
};
function showWatermarkCom(watermark = defaultParams) {
const existingModal = document.getElementById("Watermark");
if (!existingModal) {
const dom = new watermarkCom({
store,
el: document.createElement("div"),
data() {
return {
watermark,
};
},
});
dom.$el.id = "Watermark";
document.body.appendChild(dom.$el);
}
}
function registryCom() {
vue.prototype.$watermark = showWatermarkCom;
}
export default registryCom;
......@@ -61,6 +61,12 @@ Vue.prototype.$bus = new Vue();
// swiper
import "swiper/css/swiper.min.css";
// 注册证书过期提示弹窗
import registryModal from "@/components/licenseHint";
Vue.use(registryModal);
// 注册水印
import registryCom from "@/components/watermark";
Vue.use(registryCom);
Vue.config.productionTip = false;
// 图片预览
......
......@@ -2,6 +2,7 @@ import Vue from "vue";
import VueRouter from "vue-router";
import routeConfig from "./routes";
import store from "@/store/index";
import moment from "moment";
Vue.use(VueRouter);
Vue.use(store);
......@@ -20,11 +21,23 @@ const router = new VueRouter({
base: process.env.BASE_URL,
routes: routeConfig,
});
router.beforeEach((to, from, next) => {
router.beforeEach(async (to, from, next) => {
let islogin = store.getters["user/token"];
// let routerPath = store.getters["user/routerList"];
// let toRootPathArr = to.matched.map((v) => v.path);
// let bol = hasIntersection(toRootPathArr, routerPath);
let date = moment().format("YYYY-MM-DD");
let getDate = store.getters["user/licenseInfo"].date;
if (!getDate || date != getDate) {
await store.dispatch("user/checkCipher");
}
let licenseInfo = store.getters["user/licenseInfo"];
if (licenseInfo.isExpire) {
// 打开弹窗
Vue.prototype.$licenseHintModal();
// 打开水印
Vue.prototype.$watermark();
}
if (islogin) {
next();
// if (routerPath.includes(to.path) || bol) {
......
import { censusListInterface } from "@/api/dataAdmin.js";
import { appsListInterface } from "@/api/siteArrange.js";
import { checkCipher } from "@/api/user.js";
import moment from "moment";
export default {
namespaced: true,
state: {
......@@ -12,6 +14,12 @@ export default {
siteId: "", // 站点id
searForm: {}, // 报表搜索
routerList: [], // 用户权限路由
licenseInfo: {
// 系统证书信息
date: "", // 今日日期
isExpire: false, // 是否过期,
msg: "", // 过期提示信息
},
},
getters: {
siteId: (state) => state.siteId,
......@@ -29,6 +37,9 @@ export default {
siteTreeList(state) {
return state.siteList;
},
licenseInfo(state) {
return state.licenseInfo;
},
},
mutations: {
SET_routerList(state, routerList) {
......@@ -58,6 +69,11 @@ export default {
set_siteId(state, siteId) {
state.siteId = siteId;
},
set_licenseInfo(state, { isExpire, date, msg }) {
state.licenseInfo.isExpire = isExpire;
state.licenseInfo.date = date;
state.licenseInfo.msg = msg;
},
// 重置所有仓库状态
reset: () => {},
},
......@@ -78,5 +94,23 @@ export default {
}
});
},
async checkCipher(context) {
let date = moment().format("YYYY-MM-DD");
let res = await checkCipher();
let { code } = res;
if (code == 1) {
context.commit("set_licenseInfo", {
isExpire: false,
date,
msg: "",
});
} else {
context.commit("set_licenseInfo", {
isExpire: true,
date,
msg: res.msg,
});
}
},
},
};
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment