xiaoxian521
3 years ago
24 changed files with 703 additions and 157 deletions
-
14index.html
-
10package.json
-
248pnpm-lock.yaml
-
2public/serverConfig.json
-
4src/layout/components/sidebar/sidebarItem.vue
-
63src/layout/frameView.vue
-
1src/layout/types.ts
-
5src/main.ts
-
11src/router/index.ts
-
9src/router/modules/externalLink.ts
-
7src/router/utils.ts
-
2src/store/modules/multiTags.ts
-
4src/style/index.scss
-
4src/utils/http/index.ts
-
4src/utils/http/types.d.ts
-
3src/utils/is.ts
-
3src/utils/link.ts
-
54src/utils/loaders/index.ts
-
15src/utils/operate/index.ts
-
226src/utils/print.ts
-
35src/utils/resize/index.ts
-
116src/utils/watermark.ts
-
9types/global.d.ts
-
11vite.config.ts
@ -0,0 +1,63 @@ |
|||||
|
<template> |
||||
|
<div class="frame" v-loading="loading"> |
||||
|
<iframe :src="frameSrc" class="frame-iframe" ref="frameRef"></iframe> |
||||
|
</div> |
||||
|
</template> |
||||
|
<script lang="ts" setup> |
||||
|
import { useRoute } from "vue-router"; |
||||
|
import { ref, unref, onMounted, nextTick } from "vue"; |
||||
|
|
||||
|
const loading = ref(false); |
||||
|
const currentRoute = useRoute(); |
||||
|
const frameSrc = ref<string>(""); |
||||
|
const frameRef = ref<HTMLElement | null>(null); |
||||
|
|
||||
|
if (unref(currentRoute.meta)?.frameSrc) { |
||||
|
frameSrc.value = unref(currentRoute.meta)?.frameSrc as string; |
||||
|
} |
||||
|
|
||||
|
function hideLoading() { |
||||
|
loading.value = false; |
||||
|
} |
||||
|
|
||||
|
function init() { |
||||
|
nextTick(() => { |
||||
|
const iframe = unref(frameRef); |
||||
|
if (!iframe) return; |
||||
|
const _frame = iframe as any; |
||||
|
if (_frame.attachEvent) { |
||||
|
_frame.attachEvent("onload", () => { |
||||
|
hideLoading(); |
||||
|
}); |
||||
|
} else { |
||||
|
iframe.onload = () => { |
||||
|
hideLoading(); |
||||
|
}; |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
onMounted(() => { |
||||
|
loading.value = true; |
||||
|
init(); |
||||
|
}); |
||||
|
</script> |
||||
|
|
||||
|
<style lang="scss" scoped> |
||||
|
.frame { |
||||
|
height: 100vh; |
||||
|
z-index: 998; |
||||
|
|
||||
|
.frame-iframe { |
||||
|
width: 100%; |
||||
|
height: 100%; |
||||
|
overflow: hidden; |
||||
|
border: 0; |
||||
|
box-sizing: border-box; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
.main-content { |
||||
|
margin: 0 !important; |
||||
|
} |
||||
|
</style> |
@ -0,0 +1,54 @@ |
|||||
|
interface ProxyLoader { |
||||
|
loadCss(src: string): any; |
||||
|
loadScript(src: string): Promise<any>; |
||||
|
loadScriptConcurrent(src: Array<string>): Promise<any>; |
||||
|
} |
||||
|
|
||||
|
class loaderProxy implements ProxyLoader { |
||||
|
constructor() {} |
||||
|
|
||||
|
protected scriptLoaderCache: Array<string> = []; |
||||
|
|
||||
|
public loadCss = (src: string): any => { |
||||
|
const element: HTMLLinkElement = document.createElement("link"); |
||||
|
element.rel = "stylesheet"; |
||||
|
element.href = src; |
||||
|
document.body.appendChild(element); |
||||
|
}; |
||||
|
|
||||
|
public loadScript = async (src: string): Promise<any> => { |
||||
|
if (this.scriptLoaderCache.includes(src)) { |
||||
|
return src; |
||||
|
} else { |
||||
|
const element: HTMLScriptElement = document.createElement("script"); |
||||
|
element.src = src; |
||||
|
document.body.appendChild(element); |
||||
|
element.onload = () => { |
||||
|
return this.scriptLoaderCache.push(src); |
||||
|
}; |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
public loadScriptConcurrent = async ( |
||||
|
srcList: Array<string> |
||||
|
): Promise<any> => { |
||||
|
if (Array.isArray(srcList)) { |
||||
|
const len: number = srcList.length; |
||||
|
if (len > 0) { |
||||
|
let count = 0; |
||||
|
srcList.map(src => { |
||||
|
if (src) { |
||||
|
this.loadScript(src).then(() => { |
||||
|
count++; |
||||
|
if (count === len) { |
||||
|
return; |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
} |
||||
|
|
||||
|
export const loader = new loaderProxy(); |
@ -0,0 +1,226 @@ |
|||||
|
interface PrintFunction { |
||||
|
extendOptions: Function; |
||||
|
getStyle: Function; |
||||
|
setDomHeight: Function; |
||||
|
toPrint: Function; |
||||
|
} |
||||
|
|
||||
|
const Print = function (dom, options?: object): PrintFunction { |
||||
|
options = options || {}; |
||||
|
// @ts-expect-error
|
||||
|
if (!(this instanceof Print)) return new Print(dom, options); |
||||
|
this.conf = { |
||||
|
styleStr: "", |
||||
|
// Elements that need to dynamically get and set the height
|
||||
|
setDomHeightArr: [], |
||||
|
// Echart dom List
|
||||
|
echartDomArr: [], |
||||
|
// Callback before printing
|
||||
|
printBeforeFn: null, |
||||
|
// Callback after printing
|
||||
|
printDoneCallBack: null |
||||
|
}; |
||||
|
for (const key in this.conf) { |
||||
|
// eslint-disable-next-line no-prototype-builtins
|
||||
|
if (key && options.hasOwnProperty(key)) { |
||||
|
this.conf[key] = options[key]; |
||||
|
} |
||||
|
} |
||||
|
if (typeof dom === "string") { |
||||
|
this.dom = document.querySelector(dom); |
||||
|
} else { |
||||
|
this.dom = this.isDOM(dom) ? dom : dom.$el; |
||||
|
} |
||||
|
if (this.conf.setDomHeightArr && this.conf.setDomHeightArr.length) { |
||||
|
this.setDomHeight(this.conf.setDomHeightArr); |
||||
|
} |
||||
|
this.init(); |
||||
|
}; |
||||
|
|
||||
|
Print.prototype = { |
||||
|
/** |
||||
|
* init |
||||
|
*/ |
||||
|
init: function (): void { |
||||
|
const content = this.getStyle() + this.getHtml(); |
||||
|
this.writeIframe(content); |
||||
|
}, |
||||
|
/** |
||||
|
* Configuration property extension |
||||
|
* @param {Object} obj |
||||
|
* @param {Object} obj2 |
||||
|
*/ |
||||
|
extendOptions: function <T>(obj, obj2: T): T { |
||||
|
for (const k in obj2) { |
||||
|
obj[k] = obj2[k]; |
||||
|
} |
||||
|
return obj; |
||||
|
}, |
||||
|
/** |
||||
|
Copy all styles of the original page |
||||
|
*/ |
||||
|
getStyle: function (): string { |
||||
|
let str = ""; |
||||
|
const styles: NodeListOf<Element> = document.querySelectorAll("style,link"); |
||||
|
for (let i = 0; i < styles.length; i++) { |
||||
|
str += styles[i].outerHTML; |
||||
|
} |
||||
|
str += `<style>.no-print{display:none;}${this.conf.styleStr}</style>`; |
||||
|
return str; |
||||
|
}, |
||||
|
// form assignment
|
||||
|
getHtml: function (): Element { |
||||
|
const inputs = document.querySelectorAll("input"); |
||||
|
const selects = document.querySelectorAll("select"); |
||||
|
const textareas = document.querySelectorAll("textarea"); |
||||
|
for (let k = 0; k < inputs.length; k++) { |
||||
|
if (inputs[k].type == "checkbox" || inputs[k].type == "radio") { |
||||
|
if (inputs[k].checked == true) { |
||||
|
inputs[k].setAttribute("checked", "checked"); |
||||
|
} else { |
||||
|
inputs[k].removeAttribute("checked"); |
||||
|
} |
||||
|
} else if (inputs[k].type == "text") { |
||||
|
inputs[k].setAttribute("value", inputs[k].value); |
||||
|
} else { |
||||
|
inputs[k].setAttribute("value", inputs[k].value); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
for (let k2 = 0; k2 < textareas.length; k2++) { |
||||
|
if (textareas[k2].type == "textarea") { |
||||
|
textareas[k2].innerHTML = textareas[k2].value; |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
for (let k3 = 0; k3 < selects.length; k3++) { |
||||
|
if (selects[k3].type == "select-one") { |
||||
|
const child = selects[k3].children; |
||||
|
for (const i in child) { |
||||
|
if (child[i].tagName == "OPTION") { |
||||
|
// @ts-ignore
|
||||
|
if (child[i].selected == true) { |
||||
|
child[i].setAttribute("selected", "selected"); |
||||
|
} else { |
||||
|
child[i].removeAttribute("selected"); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return this.dom.outerHTML; |
||||
|
}, |
||||
|
/** |
||||
|
create iframe |
||||
|
*/ |
||||
|
writeIframe: function (content) { |
||||
|
let w: Document | Window; |
||||
|
let doc: Document; |
||||
|
const iframe: HTMLIFrameElement = document.createElement("iframe"); |
||||
|
const f: HTMLIFrameElement = document.body.appendChild(iframe); |
||||
|
iframe.id = "myIframe"; |
||||
|
iframe.setAttribute( |
||||
|
"style", |
||||
|
"position:absolute;width:0;height:0;top:-10px;left:-10px;" |
||||
|
); |
||||
|
// eslint-disable-next-line prefer-const
|
||||
|
w = f.contentWindow || f.contentDocument; |
||||
|
// eslint-disable-next-line prefer-const
|
||||
|
doc = f.contentDocument || f.contentWindow.document; |
||||
|
doc.open(); |
||||
|
doc.write(content); |
||||
|
doc.close(); |
||||
|
// eslint-disable-next-line @typescript-eslint/no-this-alias
|
||||
|
const _this = this; |
||||
|
iframe.onload = function (): void { |
||||
|
// Before popping, callback
|
||||
|
if (_this.conf.printBeforeFn) { |
||||
|
_this.conf.printBeforeFn({ doc }); |
||||
|
} |
||||
|
|
||||
|
_this.drawEchartImg(doc).then(() => { |
||||
|
_this.toPrint(w); |
||||
|
setTimeout(function () { |
||||
|
document.body.removeChild(iframe); |
||||
|
// After popup, callback
|
||||
|
if (_this.conf.printDoneCallBack) { |
||||
|
_this.conf.printDoneCallBack(); |
||||
|
} |
||||
|
}, 100); |
||||
|
}); |
||||
|
}; |
||||
|
}, |
||||
|
/** |
||||
|
* echarts printing |
||||
|
* @param {Object} doc iframe window |
||||
|
*/ |
||||
|
drawEchartImg(doc): Promise<void> { |
||||
|
return new Promise<void>(resolve => { |
||||
|
if (this.conf.echartDomArr && this.conf.echartDomArr.length > 0) { |
||||
|
this.conf.echartDomArr.forEach(e => { |
||||
|
const dom = doc.querySelector("#" + e.$el.id); |
||||
|
const img = new Image(); |
||||
|
const w = dom.offsetWidth + "px"; |
||||
|
const H = dom.offsetHeight + "px"; |
||||
|
|
||||
|
img.style.width = w; |
||||
|
img.style.height = H; |
||||
|
img.src = e.imgSrc; |
||||
|
dom.innerHTML = ""; |
||||
|
dom.appendChild(img); |
||||
|
}); |
||||
|
} |
||||
|
resolve(); |
||||
|
}); |
||||
|
}, |
||||
|
/** |
||||
|
Print |
||||
|
*/ |
||||
|
toPrint: function (frameWindow): void { |
||||
|
try { |
||||
|
setTimeout(function () { |
||||
|
frameWindow.focus(); |
||||
|
try { |
||||
|
if (!frameWindow.document.execCommand("print", false, null)) { |
||||
|
frameWindow.print(); |
||||
|
} |
||||
|
} catch (e) { |
||||
|
frameWindow.print(); |
||||
|
} |
||||
|
frameWindow.close(); |
||||
|
}, 10); |
||||
|
} catch (err) { |
||||
|
console.error(err); |
||||
|
} |
||||
|
}, |
||||
|
isDOM: |
||||
|
typeof HTMLElement === "object" |
||||
|
? function (obj) { |
||||
|
return obj instanceof HTMLElement; |
||||
|
} |
||||
|
: function (obj) { |
||||
|
return ( |
||||
|
obj && |
||||
|
typeof obj === "object" && |
||||
|
obj.nodeType === 1 && |
||||
|
typeof obj.nodeName === "string" |
||||
|
); |
||||
|
}, |
||||
|
/** |
||||
|
* Set the height of the specified dom element by getting the existing height of the dom element and setting |
||||
|
* @param {Array} arr |
||||
|
*/ |
||||
|
setDomHeight(arr) { |
||||
|
if (arr && arr.length) { |
||||
|
arr.forEach(name => { |
||||
|
const domArr = document.querySelectorAll(name); |
||||
|
domArr.forEach(dom => { |
||||
|
dom.style.height = dom.offsetHeight + "px"; |
||||
|
}); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
export default Print; |
@ -0,0 +1,35 @@ |
|||||
|
import ResizeObserver from "resize-observer-polyfill"; |
||||
|
|
||||
|
const isServer = typeof window === "undefined"; |
||||
|
|
||||
|
const resizeHandler = (entries: any[]): void => { |
||||
|
for (const entry of entries) { |
||||
|
const listeners = entry.target.__resizeListeners__ || []; |
||||
|
if (listeners.length) { |
||||
|
listeners.forEach((fn: () => any) => { |
||||
|
fn(); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
}; |
||||
|
|
||||
|
export const addResizeListener = (element: any, fn: () => any): any => { |
||||
|
if (isServer) return; |
||||
|
if (!element.__resizeListeners__) { |
||||
|
element.__resizeListeners__ = []; |
||||
|
element.__ro__ = new ResizeObserver(resizeHandler); |
||||
|
element.__ro__.observe(element); |
||||
|
} |
||||
|
element.__resizeListeners__.push(fn); |
||||
|
}; |
||||
|
|
||||
|
export const removeResizeListener = (element: any, fn: () => any): any => { |
||||
|
if (!element || !element.__resizeListeners__) return; |
||||
|
element.__resizeListeners__.splice( |
||||
|
element.__resizeListeners__.indexOf(fn), |
||||
|
1 |
||||
|
); |
||||
|
if (!element.__resizeListeners__.length) { |
||||
|
element.__ro__.disconnect(); |
||||
|
} |
||||
|
}; |
@ -0,0 +1,116 @@ |
|||||
|
import { |
||||
|
ref, |
||||
|
Ref, |
||||
|
unref, |
||||
|
shallowRef, |
||||
|
onBeforeUnmount, |
||||
|
getCurrentInstance |
||||
|
} from "vue"; |
||||
|
import { isDef } from "/@/utils/is"; |
||||
|
import { useRafThrottle } from "/@/utils/operate"; |
||||
|
import { addResizeListener, removeResizeListener } from "/@/utils/resize"; |
||||
|
|
||||
|
const domSymbol = Symbol("watermark-dom"); |
||||
|
|
||||
|
type attr = { |
||||
|
font?: string; |
||||
|
fillStyle?: string; |
||||
|
}; |
||||
|
|
||||
|
export function useWatermark( |
||||
|
appendEl: Ref<HTMLElement | null> = ref(document.body) as Ref<HTMLElement> |
||||
|
) { |
||||
|
const func = useRafThrottle(function () { |
||||
|
const el = unref(appendEl); |
||||
|
if (!el) return; |
||||
|
const { clientHeight: height, clientWidth: width } = el; |
||||
|
updateWatermark({ height, width }); |
||||
|
}); |
||||
|
const id = domSymbol.toString(); |
||||
|
const watermarkEl = shallowRef<HTMLElement>(); |
||||
|
|
||||
|
const clear = () => { |
||||
|
const domId = unref(watermarkEl); |
||||
|
watermarkEl.value = undefined; |
||||
|
const el = unref(appendEl); |
||||
|
if (!el) return; |
||||
|
domId && el.removeChild(domId); |
||||
|
removeResizeListener(el, func); |
||||
|
}; |
||||
|
|
||||
|
function createBase64(str: string, attr?: attr) { |
||||
|
const can = document.createElement("canvas"); |
||||
|
const width = 300; |
||||
|
const height = 240; |
||||
|
Object.assign(can, { width, height }); |
||||
|
|
||||
|
const cans = can.getContext("2d"); |
||||
|
if (cans) { |
||||
|
cans.rotate((-20 * Math.PI) / 120); |
||||
|
cans.font = attr?.font ?? "15px Reggae One"; |
||||
|
cans.fillStyle = attr?.fillStyle ?? "rgba(0, 0, 0, 0.15)"; |
||||
|
cans.textAlign = "left"; |
||||
|
cans.textBaseline = "middle"; |
||||
|
cans.fillText(str, width / 20, height); |
||||
|
} |
||||
|
return can.toDataURL("image/png"); |
||||
|
} |
||||
|
|
||||
|
function updateWatermark( |
||||
|
options: { |
||||
|
width?: number; |
||||
|
height?: number; |
||||
|
str?: string; |
||||
|
attr?: attr; |
||||
|
} = {} |
||||
|
) { |
||||
|
const el = unref(watermarkEl); |
||||
|
if (!el) return; |
||||
|
if (isDef(options.width)) { |
||||
|
el.style.width = `${options.width}px`; |
||||
|
} |
||||
|
if (isDef(options.height)) { |
||||
|
el.style.height = `${options.height}px`; |
||||
|
} |
||||
|
if (isDef(options.str)) { |
||||
|
el.style.background = `url(${createBase64( |
||||
|
options.str, |
||||
|
options.attr |
||||
|
)}) left top repeat`;
|
||||
|
} |
||||
|
} |
||||
|
|
||||
|
const createWatermark = (str: string, attr?: attr) => { |
||||
|
if (unref(watermarkEl)) { |
||||
|
updateWatermark({ str, attr }); |
||||
|
return id; |
||||
|
} |
||||
|
const div = document.createElement("div"); |
||||
|
watermarkEl.value = div; |
||||
|
div.id = id; |
||||
|
div.style.pointerEvents = "none"; |
||||
|
div.style.top = "0px"; |
||||
|
div.style.left = "0px"; |
||||
|
div.style.position = "absolute"; |
||||
|
div.style.zIndex = "100000"; |
||||
|
const el = unref(appendEl); |
||||
|
if (!el) return id; |
||||
|
const { clientHeight: height, clientWidth: width } = el; |
||||
|
updateWatermark({ str, width, height, attr }); |
||||
|
el.appendChild(div); |
||||
|
return id; |
||||
|
}; |
||||
|
|
||||
|
function setWatermark(str: string, attr?: attr) { |
||||
|
createWatermark(str, attr); |
||||
|
addResizeListener(document.documentElement, func); |
||||
|
const instance = getCurrentInstance(); |
||||
|
if (instance) { |
||||
|
onBeforeUnmount(() => { |
||||
|
clear(); |
||||
|
}); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
return { setWatermark, clear }; |
||||
|
} |
Write
Preview
Loading…
Cancel
Save
Reference in new issue