temp
This commit is contained in:
parent
ff87689671
commit
40290e6ac8
@ -8,7 +8,7 @@
|
||||
"lint": "vue-cli-service lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"ant-design-vue": "^1.6.3",
|
||||
"ant-design-vue": "^1.7.8",
|
||||
"axios": "^0.21.1",
|
||||
"babel-plugin-import": "^1.13.0",
|
||||
"clipboard": "^2.0.6",
|
||||
|
@ -7,10 +7,6 @@
|
||||
<script>
|
||||
export default {
|
||||
name: "App",
|
||||
async created(){
|
||||
//全局初始化
|
||||
await this.$store.dispatch("globalConfig/refreshServerConfig");
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
|
@ -56,3 +56,5 @@ window.vueInstance = new Vue({
|
||||
store,
|
||||
render: h => h(App)
|
||||
}).$mount("#app");
|
||||
|
||||
|
||||
|
@ -1,7 +1,8 @@
|
||||
import Vue from "vue";
|
||||
import VueRouter from "vue-router";
|
||||
import vuex from "../store/index.js";
|
||||
import * as vuex from "../store/index.js";
|
||||
import { GLOBAL_CONFIG, SUPPORT_NO_LOGIN, TOKEN } from "@/store/modules/globalConfig";
|
||||
import { checkJwtValid } from "@/util/UserUtil";
|
||||
|
||||
Vue.use(VueRouter);
|
||||
|
||||
@ -11,7 +12,7 @@ const routes = [
|
||||
path: "/manage",
|
||||
component: () => import("@/views/manage/index"),
|
||||
children: [
|
||||
{ path: "/bookmarkTree", component: () => import("@/views/manage/bookmarkTree/index") },
|
||||
{ path: "bookmarkTree", component: () => import("@/views/manage/bookmarkTree/index") },
|
||||
{ path: "personSpace/userInfo", component: () => import("@/views/manage/personSpace/index") },
|
||||
]
|
||||
},
|
||||
@ -37,10 +38,14 @@ const router = new VueRouter({
|
||||
/**
|
||||
* 在此进行登录信息判断,以及重定向到登录页面
|
||||
*/
|
||||
router.beforeEach((to, from, next) => {
|
||||
router.beforeEach(async (to, from, next) => {
|
||||
//进入主页面/管理页面时,确认已经进行初始化操作
|
||||
if (to.path === '/' || to.path.startsWith("/manage")) {
|
||||
await vuex.loginInit();
|
||||
}
|
||||
let supportNoLogin = to.path === '/' || to.path.startsWith("/public");
|
||||
vuex.commit(GLOBAL_CONFIG + "/" + SUPPORT_NO_LOGIN, supportNoLogin);
|
||||
if (!supportNoLogin && !checkJwtValid()) {
|
||||
vuex.default.commit(GLOBAL_CONFIG + "/" + SUPPORT_NO_LOGIN, supportNoLogin);
|
||||
if (!supportNoLogin && !checkJwtValid(vuex.default.state[GLOBAL_CONFIG][TOKEN])) {
|
||||
//如不支持未登录进入,切jwt已过期,直接跳转到登录页面
|
||||
next({
|
||||
path: "/public/login?to=" + btoa(location.href),
|
||||
@ -51,23 +56,6 @@ router.beforeEach((to, from, next) => {
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* 检查jwt是否有效
|
||||
*/
|
||||
function checkJwtValid () {
|
||||
let token = vuex.state[GLOBAL_CONFIG][TOKEN];
|
||||
try {
|
||||
if (token && token.trim().length > 0) {
|
||||
//检查token是否还有效
|
||||
let content = window.atob(token.split(".")[1]);
|
||||
if (content.exp > Date.now() / 1000) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
export default router;
|
||||
|
@ -1,16 +1,55 @@
|
||||
import Vue from "vue";
|
||||
import Vuex from "vuex";
|
||||
import globalConfig from "./modules/globalConfig";
|
||||
import treeData from "./modules/treeData";
|
||||
import * as globalConfig from "./modules/globalConfig";
|
||||
import * as treeData from "./modules/treeData";
|
||||
|
||||
import { checkJwtValid } from "@/util/UserUtil";
|
||||
|
||||
Vue.use(Vuex);
|
||||
|
||||
export default new Vuex.Store({
|
||||
let store = new Vuex.Store({
|
||||
state: {},
|
||||
mutations: {},
|
||||
actions: {},
|
||||
modules: {
|
||||
globalConfig,
|
||||
treeData
|
||||
[globalConfig.GLOBAL_CONFIG]: globalConfig.store,
|
||||
[treeData.TREE_DATA]: treeData.store
|
||||
}
|
||||
});
|
||||
|
||||
let noLoginFinish = false;
|
||||
|
||||
//执行各自的非登陆初始化
|
||||
(async () => {
|
||||
await store.dispatch(globalConfig.GLOBAL_CONFIG + "/" + globalConfig.noLoginInit);
|
||||
await store.dispatch(treeData.TREE_DATA + "/" + treeData.noLoginInit);
|
||||
noLoginFinish = true;
|
||||
})();
|
||||
|
||||
/**
|
||||
* 执行各模块的登陆后初始化
|
||||
*/
|
||||
export async function loginInit () {
|
||||
if (!noLoginFinish) {
|
||||
await finishNoLogin();
|
||||
}
|
||||
console.log(store.state[globalConfig.GLOBAL_CONFIG][globalConfig.TOKEN]);
|
||||
if (checkJwtValid(store.state[globalConfig.GLOBAL_CONFIG][globalConfig.TOKEN])) {
|
||||
await store.dispatch(globalConfig.GLOBAL_CONFIG + "/" + globalConfig.loginInit);
|
||||
await store.dispatch(treeData.TREE_DATA + "/" + treeData.loginInit);
|
||||
}
|
||||
}
|
||||
|
||||
async function finishNoLogin () {
|
||||
return new Promise((resolve) => {
|
||||
let timer = setInterval(() => {
|
||||
if (noLoginFinish) {
|
||||
clearInterval(timer);
|
||||
resolve();
|
||||
}
|
||||
}, 100);
|
||||
})
|
||||
}
|
||||
|
||||
export default store;
|
||||
|
||||
|
@ -1,11 +1,15 @@
|
||||
import localforage from "localforage";
|
||||
import HttpUtil from "../../util/HttpUtil";
|
||||
|
||||
export const GLOBAL_CONFIG = "globalConfig";
|
||||
export const USER_INFO = "userInfo";
|
||||
export const TOKEN = "token";
|
||||
export const SERVER_CONFIG = "serverConfig";
|
||||
export const SUPPORT_NO_LOGIN = "supportNoLogin";
|
||||
export const IS_INIT = "isInit";
|
||||
|
||||
export const noLoginInit = "noLoginInit";
|
||||
export const loginInit = "loginInit";
|
||||
/**
|
||||
* 存储全局配置
|
||||
*/
|
||||
@ -13,7 +17,7 @@ const state = {
|
||||
/**
|
||||
* 用户信息
|
||||
*/
|
||||
[USER_INFO]: {},
|
||||
[USER_INFO]: null,
|
||||
/**
|
||||
* token,null说明未获取登录凭证
|
||||
*/
|
||||
@ -21,11 +25,11 @@ const state = {
|
||||
/**
|
||||
* 是否已经初始化完成,避免多次重复初始化
|
||||
*/
|
||||
isInit: false,
|
||||
[IS_INIT]: false,
|
||||
/**
|
||||
* 是否移动端
|
||||
*/
|
||||
isPhone: false,
|
||||
isPhone: /Android|webOS|iPhone|iPod|BlackBerry/i.test(navigator.userAgent),
|
||||
/**
|
||||
* 是否支持未登录进入页面
|
||||
*/
|
||||
@ -39,63 +43,47 @@ const state = {
|
||||
const getters = {};
|
||||
|
||||
const actions = {
|
||||
//未登录需要进行的初始化
|
||||
async [noLoginInit] ({ commit }) {
|
||||
commit(SERVER_CONFIG, await HttpUtil.get("/common/config/global"));
|
||||
let token = await localforage.getItem(TOKEN);
|
||||
if (token) {
|
||||
commit(TOKEN, token);
|
||||
window.jwtToken = token;
|
||||
}
|
||||
},
|
||||
//登陆后的,初始化数据
|
||||
async init (context) {
|
||||
async [loginInit] (context) {
|
||||
if (context.state.isInit) {
|
||||
return;
|
||||
}
|
||||
const token = await localforage.getItem(TOKEN);
|
||||
await context.dispatch("setToken", token);
|
||||
|
||||
let userInfo = await localforage.getItem(USER_INFO);
|
||||
if (userInfo) {
|
||||
context.commit(USER_INFO, userInfo);
|
||||
}
|
||||
try {
|
||||
await context.dispatch("refreshUserInfo");
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
context.commit("isInit", true);
|
||||
context.commit("isPhone", /Android|webOS|iPhone|iPod|BlackBerry/i.test(navigator.userAgent));
|
||||
},
|
||||
async refreshUserInfo ({ commit }) {
|
||||
let userInfo = await HttpUtil.get("/user/currentUserInfo");
|
||||
await localforage.setItem(USER_INFO, userInfo);
|
||||
commit(USER_INFO, userInfo);
|
||||
context.commit(USER_INFO, userInfo);
|
||||
context.commit(IS_INIT, true);
|
||||
},
|
||||
async setToken ({ commit }, token) {
|
||||
await localforage.setItem(TOKEN, token);
|
||||
window.jwtToken = token;
|
||||
commit(TOKEN, token);
|
||||
},
|
||||
//登出清除数据
|
||||
async clear (context) {
|
||||
await localforage.removeItem("userInfo");
|
||||
await localforage.removeItem("token");
|
||||
await localforage.removeItem(TOKEN);
|
||||
context.commit(USER_INFO, null);
|
||||
context.commit(TOKEN, null);
|
||||
context.commit("isInit", false);
|
||||
context.commit(IS_INIT, false);
|
||||
},
|
||||
/**
|
||||
* 从服务器读取全局配置
|
||||
*/
|
||||
async refreshServerConfig ({ commit }) {
|
||||
commit(SERVER_CONFIG, await HttpUtil.get("/common/config/global"));
|
||||
}
|
||||
};
|
||||
|
||||
const mutations = {
|
||||
userInfo (state, userInfo) {
|
||||
state.userInfo = userInfo;
|
||||
[USER_INFO] (state, userInfo) {
|
||||
state[USER_INFO] = userInfo;
|
||||
},
|
||||
token (state, token) {
|
||||
state.token = token;
|
||||
[TOKEN] (state, token) {
|
||||
state[TOKEN] = token;
|
||||
},
|
||||
isInit (state, isInit) {
|
||||
state.isInit = isInit;
|
||||
},
|
||||
isPhone (state, status) {
|
||||
state.isPhone = status;
|
||||
[IS_INIT] (state, isInit) {
|
||||
state[IS_INIT] = isInit;
|
||||
},
|
||||
[SERVER_CONFIG] (state, serverConfig) {
|
||||
state[SERVER_CONFIG] = serverConfig;
|
||||
@ -106,7 +94,7 @@ const mutations = {
|
||||
};
|
||||
|
||||
|
||||
export default {
|
||||
export const store = {
|
||||
namespaced: true,
|
||||
state,
|
||||
getters,
|
||||
|
@ -1,8 +1,26 @@
|
||||
import localforage from "localforage";
|
||||
import { } from "ant-design-vue";
|
||||
import HttpUtil from "../../util/HttpUtil";
|
||||
|
||||
const TOTAL_TREE_DATA = "totalTreeData";
|
||||
const VERSION = "version";
|
||||
export const TREE_DATA = "treeData";
|
||||
export const TOTAL_TREE_DATA = "totalTreeData";
|
||||
export const VERSION = "version";
|
||||
export const SHOW_REFRESH_TOAST = "showRefreshToast";
|
||||
export const IS_INIT = "isInit";
|
||||
export const IS_INITING = "isIniting";
|
||||
|
||||
|
||||
export const noLoginInit = "noLoginInit";
|
||||
export const loginInit = "loginInit";
|
||||
|
||||
/**
|
||||
* 版本检查定时调度
|
||||
*/
|
||||
let timer = null;
|
||||
/**
|
||||
* 刷新书签确认弹窗是否展示
|
||||
*/
|
||||
let toastShow = false;
|
||||
|
||||
/**
|
||||
* 书签树相关配置
|
||||
@ -10,12 +28,14 @@ const VERSION = "version";
|
||||
const state = {
|
||||
//全部书签数据
|
||||
[TOTAL_TREE_DATA]: {},
|
||||
//版本
|
||||
[VERSION]: null,
|
||||
isInit: false,
|
||||
/**
|
||||
* 是否正在加载数据
|
||||
*/
|
||||
isIniting: false
|
||||
//是否已经初始化书签数据
|
||||
[IS_INIT]: false,
|
||||
// 是否正在加载数据
|
||||
[IS_INITING]: false,
|
||||
//是否展示刷新书签数据弹窗
|
||||
[SHOW_REFRESH_TOAST]: false
|
||||
};
|
||||
|
||||
const getters = {
|
||||
@ -36,26 +56,20 @@ const getters = {
|
||||
};
|
||||
|
||||
const actions = {
|
||||
//登陆后的,从缓存初始化数据
|
||||
async init(context) {
|
||||
async [noLoginInit] () {
|
||||
|
||||
},
|
||||
async [loginInit] (context) {
|
||||
if (context.state.isInit || context.state.isIniting) {
|
||||
return;
|
||||
}
|
||||
try {
|
||||
context.commit("isIniting", true);
|
||||
let realVersion = await HttpUtil.get("/user/version");
|
||||
let data = await localforage.getItem(TOTAL_TREE_DATA);
|
||||
let version = await localforage.getItem(VERSION);
|
||||
if (!data || realVersion > version) {
|
||||
await context.dispatch("refresh");
|
||||
} else {
|
||||
context.commit(TOTAL_TREE_DATA, data);
|
||||
context.commit(VERSION, version);
|
||||
}
|
||||
context.commit("isInit", true);
|
||||
} finally {
|
||||
context.commit("isIniting", false);
|
||||
}
|
||||
context.commit(IS_INITING, true);
|
||||
context.commit(TOTAL_TREE_DATA, await localforage.getItem(TOTAL_TREE_DATA));
|
||||
context.commit(VERSION, await localforage.getItem(VERSION));
|
||||
await treeDataCheck(context, true);
|
||||
context.commit(IS_INIT, true);
|
||||
context.commit(IS_INITING, false);
|
||||
timer = setInterval(treeDataCheck, 5 * 60 * 1000);
|
||||
},
|
||||
/**
|
||||
* 确保数据加载完毕
|
||||
@ -64,14 +78,14 @@ const actions = {
|
||||
return new Promise((resolve, reject) => {
|
||||
let timer = setInterval(() => {
|
||||
try {
|
||||
if (context.state.isInit && context.state.isIniting == false) {
|
||||
if (context.state[IS_INIT] && context.state[IS_INITING] == false) {
|
||||
clearInterval(timer);
|
||||
resolve();
|
||||
}
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
}, 100);
|
||||
}, 50);
|
||||
});
|
||||
},
|
||||
//刷新缓存数据
|
||||
@ -96,6 +110,7 @@ const actions = {
|
||||
async clear (context) {
|
||||
context.commit(TOTAL_TREE_DATA, null);
|
||||
context.commit(VERSION, null);
|
||||
context.commit(SHOW_REFRESH_TOAST, false);
|
||||
context.commit("isInit", false);
|
||||
context.commit("isIniting", false);
|
||||
await localforage.removeItem(TOTAL_TREE_DATA);
|
||||
@ -249,22 +264,58 @@ const mutations = {
|
||||
[TOTAL_TREE_DATA]: (state, totalTreeData) => {
|
||||
state.totalTreeData = totalTreeData;
|
||||
},
|
||||
isInit(state, isInit) {
|
||||
[IS_INIT] (state, isInit) {
|
||||
state.isInit = isInit;
|
||||
},
|
||||
isIniting(state, isIniting) {
|
||||
[IS_INITING] (state, isIniting) {
|
||||
state.isIniting = isIniting;
|
||||
},
|
||||
|
||||
[VERSION]: (state, version) => {
|
||||
state[VERSION] = version;
|
||||
},
|
||||
[SHOW_REFRESH_TOAST]: (state, val) => {
|
||||
state[SHOW_REFRESH_TOAST] = val;
|
||||
}
|
||||
};
|
||||
|
||||
export default {
|
||||
|
||||
async function treeDataCheck (context, isFirst) {
|
||||
if (toastShow) {
|
||||
return;
|
||||
}
|
||||
let realVersion = await HttpUtil.get("/user/version");
|
||||
if (realVersion !== context.state[VERSION]) {
|
||||
if (SHOW_REFRESH_TOAST && !isFirst) {
|
||||
//如果在书签管理页面需要弹窗提示
|
||||
window.vueInstance.$confirm({
|
||||
title: "书签数据有更新,是否立即刷新?",
|
||||
cancelText: "稍后提醒",
|
||||
closable: false,
|
||||
keyboard: false,
|
||||
maskClosable: false,
|
||||
onOk () {
|
||||
toastShow = false;
|
||||
return new Promise(async (resolve) => {
|
||||
await context.dispatch("treeData/clear");
|
||||
resolve();
|
||||
});
|
||||
},
|
||||
afterClose () {
|
||||
toastShow = false;
|
||||
},
|
||||
});
|
||||
toastShow = true;
|
||||
} else {
|
||||
await context.dispatch("refresh");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export const store = {
|
||||
namespaced: true,
|
||||
state,
|
||||
getters,
|
||||
actions,
|
||||
mutations
|
||||
};
|
||||
|
||||
|
@ -19,7 +19,7 @@ async function request(url, method, params, body, isForm, redirect) {
|
||||
method,
|
||||
params,
|
||||
headers: {
|
||||
"jwt-token": vuex.state.globalConfig.token
|
||||
"jwt-token": window.jwtToken
|
||||
}
|
||||
};
|
||||
//如果是表单类型的请求,添加请求头
|
||||
|
@ -12,3 +12,21 @@ export async function getUesrInfo() {
|
||||
export async function getOnlineUserInfo () {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 检查jwt是否有效
|
||||
*/
|
||||
export function checkJwtValid (token) {
|
||||
try {
|
||||
if (token && token.trim().length > 0) {
|
||||
//检查token是否还有效
|
||||
let content = JSON.parse(window.atob(token.split(".")[1]));
|
||||
if (content.exp > Date.now() / 1000) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
console.error(err);
|
||||
}
|
||||
return false;
|
||||
}
|
@ -9,7 +9,9 @@ export default {
|
||||
components: {
|
||||
header,
|
||||
},
|
||||
data() {},
|
||||
data() {
|
||||
return {};
|
||||
},
|
||||
methods: {},
|
||||
};
|
||||
</script>
|
||||
|
@ -92,10 +92,11 @@
|
||||
import AddBookmark from "@/components/main/things/AddBookmark.vue";
|
||||
import Search from "@/components/main/Search.vue";
|
||||
import HttpUtil from "@/util/HttpUtil.js";
|
||||
import { mapState, mapActions } from "vuex";
|
||||
import { mapState } from "vuex";
|
||||
import { downloadFile } from "@/util/FileUtil";
|
||||
import ClipboardJS from "clipboard";
|
||||
import moment from "moment";
|
||||
import { TREE_DATA, SHOW_REFRESH_TOAST } from "@/store/modules/treeData";
|
||||
export default {
|
||||
name: "BookmarkManage",
|
||||
components: { AddBookmark, Search },
|
||||
@ -130,6 +131,7 @@ export default {
|
||||
...mapState("globalConfig", ["isPhone"]),
|
||||
},
|
||||
async mounted() {
|
||||
this.$store.commit(TREE_DATA + "/" + SHOW_REFRESH_TOAST, true);
|
||||
await this.$store.dispatch("treeData/ensureDataOk");
|
||||
this.treeData = this.totalTreeData[""];
|
||||
this.loading = false;
|
||||
@ -144,7 +146,8 @@ export default {
|
||||
e.clearSelection();
|
||||
});
|
||||
},
|
||||
destroyed() {
|
||||
beforeDestroy() {
|
||||
this.$store.commit(TREE_DATA + "/" + SHOW_REFRESH_TOAST, false);
|
||||
if (this.copyBoard != null) {
|
||||
this.copyBoard.destroy();
|
||||
}
|
||||
@ -165,11 +168,21 @@ export default {
|
||||
resolve();
|
||||
});
|
||||
},
|
||||
/**
|
||||
* 刷新书签数据
|
||||
*/
|
||||
async refresh(deleteCache) {
|
||||
if (deleteCache) {
|
||||
this.loading = true;
|
||||
await this.$store.dispatch("treeData/refresh");
|
||||
}
|
||||
this.resetData();
|
||||
this.loading = false;
|
||||
},
|
||||
/**
|
||||
* 重置当前data中书签相关数据
|
||||
*/
|
||||
resetData() {
|
||||
this.treeData = this.totalTreeData[""];
|
||||
this.expandedKeys = [];
|
||||
this.checkedKeys = [];
|
||||
@ -178,6 +191,9 @@ export default {
|
||||
this.currentSelect = null;
|
||||
this.loading = false;
|
||||
},
|
||||
/**
|
||||
* 树节点展开
|
||||
*/
|
||||
expand(expandedKeys, { expanded, node }) {
|
||||
if (expanded) {
|
||||
const item = node.dataRef;
|
||||
|
@ -13,76 +13,9 @@ import Content from "@/layout/manage/Content.vue";
|
||||
import Bottom from "@/layout/manage/Bottom.vue";
|
||||
import Top from "@/layout/manage/Top.vue";
|
||||
|
||||
import httpUtil from "../../util/HttpUtil";
|
||||
export default {
|
||||
name: "Home",
|
||||
components: { Top, Content, Bottom },
|
||||
data() {
|
||||
return {
|
||||
timer: null,
|
||||
//当前是第几轮
|
||||
count: 0,
|
||||
//计时经过多少轮后才处理
|
||||
total: 1,
|
||||
//弹窗是否处于展开状态
|
||||
isOpen: false,
|
||||
};
|
||||
},
|
||||
async mounted() {
|
||||
//数据初始化
|
||||
await this.$store.dispatch("globalConfig/init");
|
||||
console.log("globalConfig加载完毕");
|
||||
await this.$store.dispatch("treeData/init");
|
||||
console.log("treeData加载完毕");
|
||||
console.log("state数据:", this.$store.state);
|
||||
//数据初始化时会检查版本情况,因此循环检查,五分钟后开始
|
||||
this.timer = setInterval(this.checkVersion, 5 * 60 * 1000);
|
||||
},
|
||||
destroyed() {
|
||||
if (this.timer != null) {
|
||||
clearInterval(this.timer);
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
/**
|
||||
* 检查当前页面版本是否和服务器版本一致
|
||||
*/
|
||||
async checkVersion() {
|
||||
this.count++;
|
||||
if (this.count < this.total || this.isOpen) {
|
||||
return;
|
||||
}
|
||||
this.count = 0;
|
||||
let version = await httpUtil.get("/user/version");
|
||||
const _this = this;
|
||||
if (this.$store.state.treeData.version < version) {
|
||||
this.isOpen = true;
|
||||
this.$confirm({
|
||||
title: "书签数据有更新,是否立即刷新?",
|
||||
content: "点击确定将刷新整个页面,请注意!",
|
||||
cancelText: "稍后提醒",
|
||||
closable: false,
|
||||
keyboard: false,
|
||||
maskClosable: false,
|
||||
onOk() {
|
||||
_this.isOpen = false;
|
||||
return new Promise(async (resolve) => {
|
||||
await _this.$store.dispatch("treeData/clear");
|
||||
window.location.reload();
|
||||
resolve();
|
||||
});
|
||||
},
|
||||
onCancel() {
|
||||
_this.isOpen = false;
|
||||
_this.total = 5;
|
||||
},
|
||||
afterClose() {
|
||||
_this.isOpen = false;
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user