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