自动检测更新
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
parent
8c8ffa5ea5
commit
58f8586232
19
src/main.js
19
src/main.js
|
@ -1,10 +1,9 @@
|
|||
|
||||
import Vue from 'vue'
|
||||
import 'normalize.css/normalize.css' // A modern alternative to CSS resets
|
||||
|
||||
import { createVersionPolling } from "@/utils/version-polling.esm.js";
|
||||
import ElementUI, { MessageBox } from 'element-ui'
|
||||
import { getBasicDataAllSelect, getFrontInternationalizationList, getCurrentPublishInfo } from '@/api/dictionary/dictionary'
|
||||
import { sendMFAEmail } from "@/api/user.js";
|
||||
import { resetReadingRestTime } from '@/api/trials/reading'
|
||||
// import 'element-ui/lib/theme-chalk/index.css'
|
||||
import './assets/css/theme-blue/index.css' // 浅绿色主题
|
||||
|
@ -529,5 +528,21 @@ async function VueInit() {
|
|||
} : () => { }, process.env.VUE_APP_LOCK_FOR_TIME)
|
||||
}
|
||||
VueInit()
|
||||
createVersionPolling({
|
||||
appETagKey: "__APP_ETAG__",
|
||||
pollingInterval: 5 * 1000, // 单位为毫秒
|
||||
silent: process.env.NODE_ENV === false, // 开发环境下不检测
|
||||
onUpdate: (self) => {
|
||||
// 当检测到有新版本时,执行的回调函数,可以在这里提示用户刷新页面
|
||||
const result = confirm("页面有更新,点击确定刷新页面!");
|
||||
if (result) {
|
||||
self.onRefresh();
|
||||
} else {
|
||||
self.onCancel();
|
||||
}
|
||||
// 强制更新可以用alert
|
||||
// alert('有新版本,请刷新页面');
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
|
|
|
@ -0,0 +1,222 @@
|
|||
/*!
|
||||
* version-polling v1.2.6
|
||||
* (c) 2023 JoeshuTT
|
||||
* @license MIT
|
||||
*/
|
||||
function _defineProperty(obj, key, value) {
|
||||
key = _toPropertyKey(key);
|
||||
if (key in obj) {
|
||||
Object.defineProperty(obj, key, {
|
||||
value: value,
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
writable: true
|
||||
});
|
||||
} else {
|
||||
obj[key] = value;
|
||||
}
|
||||
return obj;
|
||||
}
|
||||
function _toPrimitive(input, hint) {
|
||||
if (typeof input !== "object" || input === null) return input;
|
||||
var prim = input[Symbol.toPrimitive];
|
||||
if (prim !== undefined) {
|
||||
var res = prim.call(input, hint || "default");
|
||||
if (typeof res !== "object") return res;
|
||||
throw new TypeError("@@toPrimitive must return a primitive value.");
|
||||
}
|
||||
return (hint === "string" ? String : Number)(input);
|
||||
}
|
||||
function _toPropertyKey(arg) {
|
||||
var key = _toPrimitive(arg, "string");
|
||||
return typeof key === "symbol" ? key : String(key);
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否有值
|
||||
* @param {*} val
|
||||
*/
|
||||
/**
|
||||
* 创建一个 Web Work 实例
|
||||
* @param func
|
||||
*/
|
||||
function createWorker(func) {
|
||||
const blob = new Blob(["(" + func.toString() + ")()"]);
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const worker = new Worker(url);
|
||||
window.URL.revokeObjectURL(url);
|
||||
return worker;
|
||||
}
|
||||
function createWorkerFunc() {
|
||||
let timerId = null;
|
||||
let options;
|
||||
self.onmessage = event => {
|
||||
let code = event.data["code"];
|
||||
options = Object.assign({}, options, event.data["data"]);
|
||||
const {
|
||||
htmlFileUrl,
|
||||
lastEtag,
|
||||
appETagKey,
|
||||
pollingInterval,
|
||||
silentPollingInterval
|
||||
} = options;
|
||||
const runReq = () => {
|
||||
fetch(htmlFileUrl, {
|
||||
method: "HEAD",
|
||||
cache: "no-cache"
|
||||
}).then(response => {
|
||||
if (Number(response.status) !== 200) {
|
||||
return;
|
||||
}
|
||||
const etag = response.headers.get("etag");
|
||||
if (lastEtag !== etag) {
|
||||
self.postMessage({
|
||||
appETagKey,
|
||||
lastEtag,
|
||||
etag
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
const startPollingTask = () => {
|
||||
timerId = setInterval(runReq, pollingInterval);
|
||||
};
|
||||
const pausePollingTask = () => {
|
||||
if (timerId) {
|
||||
clearInterval(timerId);
|
||||
timerId = null;
|
||||
}
|
||||
};
|
||||
if (code === "pause") {
|
||||
pausePollingTask();
|
||||
} else {
|
||||
runReq(); // 立即执行一次
|
||||
if (!silentPollingInterval) {
|
||||
startPollingTask();
|
||||
}
|
||||
}
|
||||
};
|
||||
return self;
|
||||
}
|
||||
function closeWorker(worker) {
|
||||
worker.terminate();
|
||||
}
|
||||
|
||||
let APP_ETAG_KEY = "__APP_ETAG__";
|
||||
let myWorker;
|
||||
const defaultOptions = {
|
||||
appETagKey: APP_ETAG_KEY,
|
||||
pollingInterval: 5 * 60 * 1000,
|
||||
htmlFileUrl: `${location.origin}${location.pathname}`,
|
||||
silent: false,
|
||||
silentPollingInterval: false,
|
||||
silentPageVisibility: false,
|
||||
forceUpdate: false
|
||||
};
|
||||
let attached = false;
|
||||
/**
|
||||
* 页面隐藏时停止轮询任务,页面再度可见时在继续
|
||||
*/
|
||||
function handleVisibilityChange() {
|
||||
if (document.visibilityState === "visible") {
|
||||
myWorker.postMessage({
|
||||
code: "resume"
|
||||
});
|
||||
} else {
|
||||
myWorker.postMessage({
|
||||
code: "pause"
|
||||
});
|
||||
}
|
||||
}
|
||||
class VersionPolling {
|
||||
constructor(options) {
|
||||
_defineProperty(this, "options", void 0);
|
||||
_defineProperty(this, "appEtag", "");
|
||||
this.options = Object.assign({}, defaultOptions, options);
|
||||
this.init();
|
||||
}
|
||||
async init() {
|
||||
const {
|
||||
htmlFileUrl
|
||||
} = this.options;
|
||||
if (!htmlFileUrl) {
|
||||
throw new Error("[version-polling]: htmlFileUrl is required");
|
||||
}
|
||||
const response = await fetch(htmlFileUrl, {
|
||||
method: "HEAD",
|
||||
cache: "no-cache"
|
||||
});
|
||||
if (Number(response.status) !== 200) {
|
||||
throw new Error(`[version-polling]: status is ${response.status}`);
|
||||
}
|
||||
const etag = response.headers.get("etag");
|
||||
// eslint-disable-next-line no-eq-null
|
||||
if (etag == null) {
|
||||
throw new Error(`[version-polling]: etag is null`);
|
||||
}
|
||||
this.appEtag = etag;
|
||||
localStorage.setItem(`${this.options.appETagKey}`, etag);
|
||||
this.start();
|
||||
}
|
||||
start() {
|
||||
const {
|
||||
appETagKey,
|
||||
pollingInterval,
|
||||
htmlFileUrl,
|
||||
silent,
|
||||
silentPollingInterval,
|
||||
silentPageVisibility
|
||||
} = this.options;
|
||||
if (silent) {
|
||||
return;
|
||||
}
|
||||
myWorker = createWorker(createWorkerFunc);
|
||||
myWorker.postMessage({
|
||||
code: "start",
|
||||
data: {
|
||||
appETagKey,
|
||||
pollingInterval,
|
||||
htmlFileUrl,
|
||||
silentPollingInterval,
|
||||
lastEtag: this.appEtag
|
||||
}
|
||||
});
|
||||
myWorker.onmessage = event => {
|
||||
const {
|
||||
lastEtag,
|
||||
etag
|
||||
} = event.data;
|
||||
if (lastEtag !== etag) {
|
||||
this.stop();
|
||||
this.options.onUpdate(this);
|
||||
}
|
||||
};
|
||||
if (!silentPageVisibility) {
|
||||
if (!attached) {
|
||||
document.addEventListener("visibilitychange", handleVisibilityChange);
|
||||
attached = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
stop() {
|
||||
if (myWorker) {
|
||||
closeWorker(myWorker);
|
||||
if (attached) {
|
||||
document.removeEventListener("visibilitychange", handleVisibilityChange);
|
||||
attached = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
onRefresh() {
|
||||
window.location.reload();
|
||||
}
|
||||
onCancel() {
|
||||
this.options.forceUpdate && this.start();
|
||||
}
|
||||
}
|
||||
function createVersionPolling(options) {
|
||||
const versionPolling = new VersionPolling(options);
|
||||
return versionPolling;
|
||||
}
|
||||
|
||||
export { VersionPolling, createVersionPolling };
|
Loading…
Reference in New Issue