@studio-kit/utils-browser 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +26 -0
- package/src/Code.js +2 -0
- package/src/DOM.js +63 -0
- package/src/Data.js +2 -0
- package/src/FIO.js +79 -0
- package/src/HTTP.js +20 -0
- package/src/Vue2.js +51 -0
- package/src/Vue3.js +42 -0
- package/src/index.js +11 -0
- package/src/methods/dom.js +13 -0
- package/src/methods/env.js +12 -0
- package/src/methods/loadLib.js +41 -0
- package/src/methods/message.js +5 -0
- package/src/methods/perfomance.js +21 -0
- package/src/methods/toast.js +230 -0
- package/src/vue2-plugin.js +205 -0
package/package.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@studio-kit/utils-browser",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "./src/index.js",
|
|
7
|
+
"publishConfig": {
|
|
8
|
+
"access": "public"
|
|
9
|
+
},
|
|
10
|
+
"keywords": [],
|
|
11
|
+
"author": "",
|
|
12
|
+
"license": "ISC",
|
|
13
|
+
"dependencies": {
|
|
14
|
+
"@studio-kit/utils-common": "^1.0.0",
|
|
15
|
+
"axios": "^1.7.9",
|
|
16
|
+
"nanoid": "^5.1.2"
|
|
17
|
+
},
|
|
18
|
+
"exports": {
|
|
19
|
+
".": "./src/index.js",
|
|
20
|
+
"./*": "./src/*",
|
|
21
|
+
"./Vue2": "./src/Vue2.js"
|
|
22
|
+
},
|
|
23
|
+
"scripts": {
|
|
24
|
+
"test": "echo \"Error: no test specified\" && exit 1"
|
|
25
|
+
}
|
|
26
|
+
}
|
package/src/Code.js
ADDED
package/src/DOM.js
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export default class DOM {
|
|
2
|
+
// 获取坐标在元素中的相对位置
|
|
3
|
+
static getOffsetPositionInElement({
|
|
4
|
+
targetElement, // 对应DOM元素
|
|
5
|
+
absolutePosition, // 相对于屏幕左上角的坐标 { x:..., y:.. }
|
|
6
|
+
}) {
|
|
7
|
+
// 获取元素在视口中的位置信息
|
|
8
|
+
const rect = targetElement.getBoundingClientRect();
|
|
9
|
+
// 计算坐标相对于元素左上角的位置
|
|
10
|
+
const x = absolutePosition.x - rect.left;
|
|
11
|
+
const y = absolutePosition.y - rect.top;
|
|
12
|
+
let isOutside = false
|
|
13
|
+
if (x < 0 || y < 0 || x > rect.right || y > rect.bottom) {
|
|
14
|
+
isOutside = true
|
|
15
|
+
}
|
|
16
|
+
return { x, y, isOutside };
|
|
17
|
+
}
|
|
18
|
+
static getBoundingClientRect({ targetEl, wrapperEl, needAutoDetectParent }) {
|
|
19
|
+
if (needAutoDetectParent) {
|
|
20
|
+
wrapperEl = this.getPositionedParent(element)
|
|
21
|
+
}
|
|
22
|
+
const { left, top, right, bottom, width, height } = targetEl.getBoundingClientRect()
|
|
23
|
+
const wrapperBoundingClientRect = wrapperEl.getBoundingClientRect()
|
|
24
|
+
return {
|
|
25
|
+
left: left - wrapperBoundingClientRect.left,
|
|
26
|
+
top: top - wrapperBoundingClientRect.top,
|
|
27
|
+
right, bottom,
|
|
28
|
+
width: targetEl.offsetWidth,
|
|
29
|
+
height: targetEl.offsetHeight
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
static getPositionedParent(element) {
|
|
33
|
+
const positionedParent = element.offsetParent;
|
|
34
|
+
if (positionedParent) {
|
|
35
|
+
return positionedParent
|
|
36
|
+
}
|
|
37
|
+
let parent = element.parentElement;
|
|
38
|
+
while (parent) {
|
|
39
|
+
const style = window.getComputedStyle(parent);
|
|
40
|
+
if (style.position !== 'static') {
|
|
41
|
+
return parent;
|
|
42
|
+
}
|
|
43
|
+
parent = parent.parentElement;
|
|
44
|
+
}
|
|
45
|
+
return null; // 没有找到则返回 null
|
|
46
|
+
}
|
|
47
|
+
static isScrollable(element) {
|
|
48
|
+
const style = window.getComputedStyle(element);
|
|
49
|
+
return style.overflow === 'scroll' || style.overflow === 'auto';
|
|
50
|
+
}
|
|
51
|
+
static watchSizeChange(element, onChange) {
|
|
52
|
+
if (window.ResizeObserver)
|
|
53
|
+
return new ResizeObserver(onChange).observe(element)
|
|
54
|
+
}
|
|
55
|
+
static unwatchSizeChange(watcher, element) {
|
|
56
|
+
if (window.ResizeObserver) {
|
|
57
|
+
if (watcher instanceof window.ResizeObserver) {
|
|
58
|
+
if (element) watcher.unobserve(element);
|
|
59
|
+
else watcher.disconnect();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
package/src/Data.js
ADDED
package/src/FIO.js
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
export default class FIO {
|
|
2
|
+
static loadingMap = {};
|
|
3
|
+
static loadedMap = {};
|
|
4
|
+
static loadLib(src, key, type) {
|
|
5
|
+
const {
|
|
6
|
+
loadingMap,
|
|
7
|
+
loadedMap
|
|
8
|
+
} = FIO
|
|
9
|
+
if (key && window[key]) return Promise.resolve(window['key']);
|
|
10
|
+
if (loadedMap[src]) return Promise.resolve();
|
|
11
|
+
if (loadingMap[src]) return loadingMap[src];
|
|
12
|
+
if (/\.js$/.test(src) || type === 'js')
|
|
13
|
+
return loadingMap[src] = new Promise((resolve, reject) => {
|
|
14
|
+
const script = document.createElement('script');
|
|
15
|
+
script.async = true;
|
|
16
|
+
script.src = src;
|
|
17
|
+
script.onerror = function (err) {
|
|
18
|
+
const errMsg = `加载js文件错误(src: ${src})(err: ${err.message})`
|
|
19
|
+
console.error(errMsg)
|
|
20
|
+
reject(new Error(errMsg));
|
|
21
|
+
};
|
|
22
|
+
script.onload = function () {
|
|
23
|
+
delete loadingMap[src];
|
|
24
|
+
loadedMap[src] = true;
|
|
25
|
+
resolve(window[key]);
|
|
26
|
+
};
|
|
27
|
+
document.head.appendChild(script);
|
|
28
|
+
});
|
|
29
|
+
if (/\.css$/.test(src) || type === 'css') {
|
|
30
|
+
return loadingMap[src] = new Promise((resolve, reject) => {
|
|
31
|
+
const link = document.createElement('link');
|
|
32
|
+
link.type = 'text/css';
|
|
33
|
+
link.rel = 'stylesheet';
|
|
34
|
+
link.href = src;
|
|
35
|
+
link.onerror = function (err) {
|
|
36
|
+
console.error('加载BMap文件错误', err)
|
|
37
|
+
reject(err);
|
|
38
|
+
};
|
|
39
|
+
link.onload = function () {
|
|
40
|
+
delete loadingMap[src];
|
|
41
|
+
loadedMap[src] = true;
|
|
42
|
+
resolve(window[key]);
|
|
43
|
+
};
|
|
44
|
+
document.head.appendChild(link);
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
static joinPath({ pathParts, os }) {
|
|
49
|
+
if (!Array.isArray(pathParts) || pathParts.length === 0) {
|
|
50
|
+
return ''; // 如果没有路径片段,返回空字符串
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// 拼接路径并移除多余的分隔符
|
|
54
|
+
let joinedPath = pathParts
|
|
55
|
+
.map(part => part.replace(/^\/+|\/+$/g, '')) // 去掉每部分路径的开头和结尾的多余斜杠
|
|
56
|
+
.filter(part => part !== '') // 过滤掉空字符串
|
|
57
|
+
.join('/'); // 使用正斜杠拼接
|
|
58
|
+
if (!os) {
|
|
59
|
+
for (const pathPart of pathParts) {
|
|
60
|
+
if (pathPart.indexOf('\\') !== -1) {
|
|
61
|
+
os = 'windows'
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
// 如果是 Windows 系统,替换为反斜杠
|
|
66
|
+
if (os === 'windows') {
|
|
67
|
+
joinedPath = joinedPath.replace(/\//g, '\\');
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// 确保路径以根路径开始(如果有)
|
|
71
|
+
if (pathParts[0] && pathParts[0].startsWith('/')) {
|
|
72
|
+
joinedPath = '/' + joinedPath;
|
|
73
|
+
} else if (os === 'windows' && pathParts[0] && pathParts[0].match(/^[A-Za-z]:\\/)) {
|
|
74
|
+
joinedPath = pathParts[0][0] + ':' + joinedPath; // 保留 Windows 驱动器标识
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return joinedPath;
|
|
78
|
+
}
|
|
79
|
+
}
|
package/src/HTTP.js
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import CommonHTTP from '@studio-kit/utils-common/HTTP.js'
|
|
2
|
+
export default class HTTP extends CommonHTTP {
|
|
3
|
+
static getUrlQuery() {
|
|
4
|
+
const result = {}
|
|
5
|
+
if (location.search) {
|
|
6
|
+
const parsed = this.parseUrlQuery(location.search);
|
|
7
|
+
if (parsed) {
|
|
8
|
+
Object.assign(result, parsed)
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
if (location.hash) {
|
|
12
|
+
const hashQuery = location.hash.substring(location.hash.indexOf('?') + 1)
|
|
13
|
+
const parsedHash = this.parseUrlQuery(hashQuery);
|
|
14
|
+
if (parsedHash) {
|
|
15
|
+
Object.assign(result, parsedHash)
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
return result
|
|
19
|
+
}
|
|
20
|
+
}
|
package/src/Vue2.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import Vue from 'vue'
|
|
2
|
+
export default class Vue2 {
|
|
3
|
+
static createComponentDomEl({ componentKey, componentProps, vueContext }) {
|
|
4
|
+
// // 1. 获取组件定义(假设组件已全局注册)
|
|
5
|
+
// const componentDef = Vue.options.components[componentKey];
|
|
6
|
+
// if (!componentDef) {
|
|
7
|
+
// throw new Error(`组件 "${componentKey}" 未注册`);
|
|
8
|
+
// }
|
|
9
|
+
|
|
10
|
+
// // 2. 创建组件构造函数
|
|
11
|
+
// const ComponentConstructor = Vue.extend(componentDef);
|
|
12
|
+
|
|
13
|
+
// // 3. 实例化组件并传入 props
|
|
14
|
+
// const instance = new ComponentConstructor({
|
|
15
|
+
// propsData: componentProps
|
|
16
|
+
// });
|
|
17
|
+
|
|
18
|
+
// // 4. 手动挂载(生成 DOM 但不插入文档)
|
|
19
|
+
// instance.$mount();
|
|
20
|
+
|
|
21
|
+
// // 5. 返回组件的根 DOM 元素
|
|
22
|
+
// return instance.$el;
|
|
23
|
+
// 获取当前组件实例(通过 this 获取 Vue 组件上下文)
|
|
24
|
+
const parent = vueContext
|
|
25
|
+
|
|
26
|
+
// 1. 优先查找局部注册的组件
|
|
27
|
+
const componentDef = Vue.options.components[componentKey];
|
|
28
|
+
|
|
29
|
+
if (!componentDef) {
|
|
30
|
+
throw new Error(`组件 "${tagcomponentKeyName}" 未注册`)
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// 2. 创建带上下文的构造函数
|
|
34
|
+
const ComponentConstructor = Vue.extend(componentDef)
|
|
35
|
+
console.log('vueContext', vueContext)
|
|
36
|
+
// 3. 创建实例并挂载
|
|
37
|
+
const instance = new ComponentConstructor({
|
|
38
|
+
parent,
|
|
39
|
+
propsData: componentProps,
|
|
40
|
+
// 同步父级的 provide/inject 链
|
|
41
|
+
inject: vueContext._provided // 保留组件自身的 inject 配置
|
|
42
|
+
})
|
|
43
|
+
instance.$mount()
|
|
44
|
+
return instance.$el;
|
|
45
|
+
// // 4. 返回带上下文的 DOM
|
|
46
|
+
// return {
|
|
47
|
+
// dom: instance.$el,
|
|
48
|
+
// instance // 建议返回实例用于后续销毁
|
|
49
|
+
// }
|
|
50
|
+
}
|
|
51
|
+
}
|
package/src/Vue3.js
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { getCurrentInstance, createVNode, render, h, provide, resolveComponent } from 'vue'
|
|
2
|
+
|
|
3
|
+
export default class Vue3 {
|
|
4
|
+
static createComponentDomEl({ component, componentProps, vueContext }) {
|
|
5
|
+
const parent = vueContext || getCurrentInstance()
|
|
6
|
+
console.log('getCurrentInstance()', getCurrentInstance())
|
|
7
|
+
console.log('vueContext', vueContext)
|
|
8
|
+
// console.log('componentKey', componentKey)
|
|
9
|
+
// // 1. 查找局部注册的组件
|
|
10
|
+
// const componentDef = resolveDynamicComponent('el-button');
|
|
11
|
+
// const componentDef = resolveComponent(component);
|
|
12
|
+
// if (!componentDef) {
|
|
13
|
+
// throw new Error(`组件 "${componentKey}" 未注册`)
|
|
14
|
+
// }
|
|
15
|
+
|
|
16
|
+
// // 2. 创建虚拟节点
|
|
17
|
+
const wrapperComponent = {
|
|
18
|
+
setup() {
|
|
19
|
+
console.log('vueContext.gui', vueContext.gui)
|
|
20
|
+
provide('gui', vueContext.gui); // 注入数据
|
|
21
|
+
return () => h(component, componentProps); // 使用数据渲染
|
|
22
|
+
}
|
|
23
|
+
};
|
|
24
|
+
const vnode = createVNode(wrapperComponent)
|
|
25
|
+
|
|
26
|
+
// 3. 继承父级上下文
|
|
27
|
+
// if (parent) {
|
|
28
|
+
// vnode.appContext = parent.appContext // 共享全局资源
|
|
29
|
+
// // 同步 provide/inject
|
|
30
|
+
// if (parent.provides) {
|
|
31
|
+
// vnode.appContext.provides = Object.create(parent.provides)
|
|
32
|
+
// }
|
|
33
|
+
// }
|
|
34
|
+
|
|
35
|
+
// // 4. 创建容器并渲染
|
|
36
|
+
// const instance = h(component, componentProps, )
|
|
37
|
+
const container = document.createElement('div')
|
|
38
|
+
// render(instance, container)
|
|
39
|
+
render(vnode, container)
|
|
40
|
+
return container
|
|
41
|
+
}
|
|
42
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export function watchSizeChange(element, onChange) {
|
|
2
|
+
if (window.ResizeObserver)
|
|
3
|
+
return new ResizeObserver(onChange).observe(element)
|
|
4
|
+
}
|
|
5
|
+
|
|
6
|
+
export function unwatchSizeChange(watcher, element) {
|
|
7
|
+
if (window.ResizeObserver) {
|
|
8
|
+
if (watcher instanceof window.ResizeObserver) {
|
|
9
|
+
if (element) watcher.unobserve(element);
|
|
10
|
+
else watcher.disconnect();
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export function checkIsDev() {
|
|
2
|
+
let isDev = process.env.NODE_ENV === "development";
|
|
3
|
+
if (localStorage.env === "prod") isDev = false;
|
|
4
|
+
if (localStorage.env === "dev") isDev = true;
|
|
5
|
+
if (isDev) console.log("当前环境为开发环境"); // eslint-disable-line no-console
|
|
6
|
+
else console.log("当前环境为线上环境"); // eslint-disable-line no-console
|
|
7
|
+
if (location.search.indexOf('debug=1') !== -1
|
|
8
|
+
|| location.search.indexOf('dev=1') !== -1) {
|
|
9
|
+
isDev = true;
|
|
10
|
+
}
|
|
11
|
+
return isDev;
|
|
12
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const loadingMap = {};
|
|
2
|
+
const loadedMap = {};
|
|
3
|
+
export default function (src, key, type) {
|
|
4
|
+
if (key && window[key]) return Promise.resolve(window['key']);
|
|
5
|
+
if (loadedMap[src]) return Promise.resolve();
|
|
6
|
+
if (loadingMap[src]) return loadingMap[src];
|
|
7
|
+
if (/\.js$/.test(src) || type === 'js')
|
|
8
|
+
return loadingMap[src] = new Promise((resolve, reject) => {
|
|
9
|
+
const script = document.createElement('script');
|
|
10
|
+
script.async = true;
|
|
11
|
+
script.src = src;
|
|
12
|
+
script.onerror = function (err) {
|
|
13
|
+
console.error('加载js文件错误', src, err)
|
|
14
|
+
reject(err);
|
|
15
|
+
};
|
|
16
|
+
script.onload = function () {
|
|
17
|
+
delete loadingMap[src];
|
|
18
|
+
loadedMap[src] = true;
|
|
19
|
+
resolve(window[key]);
|
|
20
|
+
};
|
|
21
|
+
document.head.appendChild(script);
|
|
22
|
+
});
|
|
23
|
+
if (/\.css$/.test(src) || type === 'css') {
|
|
24
|
+
return loadingMap[src] = new Promise((resolve, reject) => {
|
|
25
|
+
const link = document.createElement('link');
|
|
26
|
+
link.type = 'text/css';
|
|
27
|
+
link.rel = 'stylesheet';
|
|
28
|
+
link.href = src;
|
|
29
|
+
link.onerror = function (err) {
|
|
30
|
+
console.error('加载BMap文件错误', err)
|
|
31
|
+
reject(err);
|
|
32
|
+
};
|
|
33
|
+
link.onload = function () {
|
|
34
|
+
delete loadingMap[src];
|
|
35
|
+
loadedMap[src] = true;
|
|
36
|
+
resolve(window[key]);
|
|
37
|
+
};
|
|
38
|
+
document.head.appendChild(link);
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export function debounce(fn, delay, immediate) {
|
|
2
|
+
let timer
|
|
3
|
+
let result
|
|
4
|
+
return function (...args) {
|
|
5
|
+
if (timer) clearTimeout(timer)
|
|
6
|
+
|
|
7
|
+
if (immediate) {
|
|
8
|
+
// 如果timer存在,说明第二次调用的时候还没到delay时间,因为如果超过delay时间
|
|
9
|
+
// timer会被赋值为null,所以这个时候我们不应该执行fn,应该重新设置一个定时器
|
|
10
|
+
// 但如果是一次的时候,因为还没有设过定时器,所以这里timer会是undefined
|
|
11
|
+
if (timer) {
|
|
12
|
+
timer = setTimeout(() => timer = null, delay)
|
|
13
|
+
} else {
|
|
14
|
+
result = fn.apply(this, args)
|
|
15
|
+
return result
|
|
16
|
+
}
|
|
17
|
+
} else {
|
|
18
|
+
timer = setTimeout(() => fn.apply(this, args), delay)
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
export default function (config = {}, Vue) {
|
|
2
|
+
const {
|
|
3
|
+
animationOut = 'fadeInDown',
|
|
4
|
+
animationIn = 'fadeOutUp',
|
|
5
|
+
position = 'center',
|
|
6
|
+
padding = '20px 30px',
|
|
7
|
+
borderRadius = '5px',
|
|
8
|
+
deviceType = 'pc'
|
|
9
|
+
} = config;
|
|
10
|
+
if (!config.successBgColor) config.successBgColor = 'rgba(0,0,0,.5)';
|
|
11
|
+
if (!config.errorBgColor) config.errorBgColor = 'rgba(255,0,0,.5)';
|
|
12
|
+
if (!config.successColor) config.successColor = '#fff';
|
|
13
|
+
if (!config.errorColor) config.errorColor = '#fff';
|
|
14
|
+
if (!config.duration) config.duration = 2e3;
|
|
15
|
+
let loadingToastEl;
|
|
16
|
+
const maskEl = document.createElement('div');
|
|
17
|
+
maskEl.className = 'full z-10'
|
|
18
|
+
|
|
19
|
+
function removeLoading() {
|
|
20
|
+
if (loadingToastEl) {
|
|
21
|
+
if (loadingToastEl.parentElement) loadingToastEl.parentElement.removeChild(loadingToastEl);
|
|
22
|
+
loadingToastEl.style.display = 'none';
|
|
23
|
+
loadingToastEl = undefined;
|
|
24
|
+
}
|
|
25
|
+
maskEl.style.display = 'none';
|
|
26
|
+
if (maskEl.parentElement) maskEl.parentElement.removeChild(maskEl);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function open(params) {
|
|
30
|
+
const { duration = config.duration, message, type } = params;
|
|
31
|
+
const toastEl = document.createElement('div');
|
|
32
|
+
console.log('toastEl', toastEl)
|
|
33
|
+
if (maskEl.parentElement)
|
|
34
|
+
maskEl.parentElement.removeChild(maskEl);
|
|
35
|
+
// toastEl.className = `${position} animated ${animationIn}`;
|
|
36
|
+
toastEl.className = `${position}`;
|
|
37
|
+
toastEl.style.backgroundColor = config[`${type}BgColor`];
|
|
38
|
+
toastEl.style.color = config[`${type}Color`];
|
|
39
|
+
toastEl.style.borderRadius = borderRadius;
|
|
40
|
+
toastEl.style.padding = padding;
|
|
41
|
+
toastEl.style.fontSize = '16px';
|
|
42
|
+
toastEl.style.zIndex = '10000';
|
|
43
|
+
toastEl.innerHTML = message;
|
|
44
|
+
if (params.isLoading) {
|
|
45
|
+
document.body.appendChild(maskEl);
|
|
46
|
+
}
|
|
47
|
+
document.body.appendChild(toastEl);
|
|
48
|
+
if (loadingToastEl) {
|
|
49
|
+
removeLoading();
|
|
50
|
+
}
|
|
51
|
+
if (params.isLoading) {
|
|
52
|
+
loadingToastEl = toastEl;
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
setTimeout(() => {
|
|
56
|
+
// toastEl.classList.add(animationOut);
|
|
57
|
+
setTimeout(() => {
|
|
58
|
+
document.body.removeChild(toastEl);
|
|
59
|
+
}, 5e2)
|
|
60
|
+
}, duration);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const toast = params => open(params);
|
|
64
|
+
toast.success = params => {
|
|
65
|
+
if (deviceType !== 'pc' && Vue && Vue.prototype.$toast) {
|
|
66
|
+
Vue.prototype.$toast.success(params)
|
|
67
|
+
} else {
|
|
68
|
+
if (typeof params === 'string') params = { message: params };
|
|
69
|
+
if (!params) params = { message: '处理成功' };
|
|
70
|
+
params.type = 'success'
|
|
71
|
+
open(params);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
toast.clear = () => {
|
|
75
|
+
if (Vue && Vue.prototype.$toast) {
|
|
76
|
+
Vue.prototype.$toast.clear()
|
|
77
|
+
} else {
|
|
78
|
+
removeLoading();
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
let alertWin;
|
|
82
|
+
toast.error = params => {
|
|
83
|
+
// if (params) {
|
|
84
|
+
// const message = params.message || params;
|
|
85
|
+
// if (Vue && Vue.prototype.$toast) {
|
|
86
|
+
// Vue.prototype.$toast.fail(message)
|
|
87
|
+
// } else {
|
|
88
|
+
// if (typeof params === 'string') params = { message };
|
|
89
|
+
// params.type = 'error'
|
|
90
|
+
// // open(params);
|
|
91
|
+
// }
|
|
92
|
+
// } else {
|
|
93
|
+
// open({
|
|
94
|
+
// type: 'error',
|
|
95
|
+
// message: '未知错误'
|
|
96
|
+
// });
|
|
97
|
+
// throw new Error('toast.error未传入params')
|
|
98
|
+
// }
|
|
99
|
+
toast.clear();
|
|
100
|
+
let message = params.message || params
|
|
101
|
+
if (message && message.replace) {
|
|
102
|
+
message = message.replace(/error/ig, '')
|
|
103
|
+
}
|
|
104
|
+
if (deviceType === 'mobile') {
|
|
105
|
+
Vue.prototype.$dialog.alert({
|
|
106
|
+
message,
|
|
107
|
+
});
|
|
108
|
+
} else {
|
|
109
|
+
if (alertWin && alertWin.id) Vue.prototype.$$closePage({ winId: alertWin.id });
|
|
110
|
+
alertWin = {
|
|
111
|
+
pageKey: config.alertViewKey || 'vs-alert',
|
|
112
|
+
title: '错误提示',
|
|
113
|
+
width: 360,
|
|
114
|
+
height: 280,
|
|
115
|
+
hasMask: true,
|
|
116
|
+
canCloseOnMask: true,
|
|
117
|
+
props: {
|
|
118
|
+
message
|
|
119
|
+
},
|
|
120
|
+
}
|
|
121
|
+
Vue.prototype.$$openPage(alertWin);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
toast.loading = function (msg) {
|
|
125
|
+
console.log('deviceType', deviceType)
|
|
126
|
+
if (!msg) msg = '正在处理...';
|
|
127
|
+
if (deviceType !== 'pc' && Vue && Vue.prototype.$toast) {
|
|
128
|
+
Vue.prototype.$toast.loading({
|
|
129
|
+
message: msg,
|
|
130
|
+
duration: 0
|
|
131
|
+
})
|
|
132
|
+
} else {
|
|
133
|
+
open({
|
|
134
|
+
isLoading: true,
|
|
135
|
+
type: 'success',
|
|
136
|
+
message: msg,
|
|
137
|
+
})
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
toast.confirm = function (config) {
|
|
141
|
+
const maskEl = createConfirmMask();
|
|
142
|
+
const confirmEl = createConfirmEl();
|
|
143
|
+
const close = function () {
|
|
144
|
+
confirmEl.parentNode.removeChild(confirmEl);
|
|
145
|
+
maskEl.parentNode.removeChild(maskEl);
|
|
146
|
+
}
|
|
147
|
+
const {
|
|
148
|
+
btns = [
|
|
149
|
+
{
|
|
150
|
+
name: '确定',
|
|
151
|
+
onClick(close) {
|
|
152
|
+
config.onConfirm(close);
|
|
153
|
+
}
|
|
154
|
+
},
|
|
155
|
+
{
|
|
156
|
+
name: '取消',
|
|
157
|
+
onClick(close) {
|
|
158
|
+
onCancel(close);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
],
|
|
162
|
+
onCancel = function (close) {
|
|
163
|
+
close();
|
|
164
|
+
},
|
|
165
|
+
title,
|
|
166
|
+
content = '无警告内容'
|
|
167
|
+
} = config;
|
|
168
|
+
const titleEl = createTitleEl(title);
|
|
169
|
+
const contentEl = createContentEl(content);
|
|
170
|
+
const btnsEl = createBtnsEl();
|
|
171
|
+
btns.forEach(btn => {
|
|
172
|
+
btnsEl.appendChild(createBtnEl(btn, close));
|
|
173
|
+
});
|
|
174
|
+
if (titleEl) confirmEl.appendChild(titleEl);
|
|
175
|
+
confirmEl.appendChild(contentEl);
|
|
176
|
+
confirmEl.appendChild(btnsEl);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
function createConfirmMask() {
|
|
180
|
+
const el = document.createElement('div');
|
|
181
|
+
el.className = 'mask active full z-10';
|
|
182
|
+
el.style.zIndex = 10000;
|
|
183
|
+
document.body.appendChild(el);
|
|
184
|
+
return el;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
function createConfirmEl() {
|
|
188
|
+
const el = document.createElement('div');
|
|
189
|
+
el.className = 'studio-mobile-dialog center z-10';
|
|
190
|
+
el.style.zIndex = 10000;
|
|
191
|
+
document.body.appendChild(el);
|
|
192
|
+
return el;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function createTitleEl(title) {
|
|
196
|
+
if (title) {
|
|
197
|
+
const el = document.createElement('div');
|
|
198
|
+
el.className = 'studio-mobile-dialog-title';
|
|
199
|
+
el.innerHTML = title;
|
|
200
|
+
return el;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function createContentEl(title) {
|
|
205
|
+
const el = document.createElement('div');
|
|
206
|
+
el.className = 'studio-mobile-dialog-content';
|
|
207
|
+
el.innerHTML = title;
|
|
208
|
+
return el;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
function createBtnsEl() {
|
|
212
|
+
const el = document.createElement('div');
|
|
213
|
+
el.className = 'studio-mobile-dialog-btns bottom';
|
|
214
|
+
return el;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function createBtnEl(btn, close) {
|
|
218
|
+
const el = document.createElement('div');
|
|
219
|
+
el.className = 'studio-mobile-dialog-btn';
|
|
220
|
+
el.innerHTML = btn.name || '未命名';
|
|
221
|
+
el.onclick = el.ontouchstart = function () {
|
|
222
|
+
btn.onClick(close);
|
|
223
|
+
}
|
|
224
|
+
return el;
|
|
225
|
+
}
|
|
226
|
+
if (deviceType === 'mobile') {
|
|
227
|
+
Vue.prototype.$confirm = toast.confirm;
|
|
228
|
+
}
|
|
229
|
+
return toast;
|
|
230
|
+
}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import loadLib from './methods/loadLib'
|
|
2
|
+
import { nanoid } from 'nanoid'
|
|
3
|
+
import axios from 'axios'
|
|
4
|
+
import initToast from './methods/toast'
|
|
5
|
+
import {
|
|
6
|
+
watchSizeChange,
|
|
7
|
+
unwatchSizeChange,
|
|
8
|
+
} from './methods/dom'
|
|
9
|
+
|
|
10
|
+
import {
|
|
11
|
+
emitParentIframe
|
|
12
|
+
} from './methods/message'
|
|
13
|
+
|
|
14
|
+
import {
|
|
15
|
+
debounce
|
|
16
|
+
} from './methods/perfomance'
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
export default function (Vue, config = {}) {
|
|
20
|
+
Vue.prototype.$util = {
|
|
21
|
+
load: loadLib,
|
|
22
|
+
watchSizeChange,
|
|
23
|
+
unwatchSizeChange,
|
|
24
|
+
createId: nanoid,
|
|
25
|
+
emitParentIframe,
|
|
26
|
+
hasVueCompo,
|
|
27
|
+
consoleLog,
|
|
28
|
+
debounce,
|
|
29
|
+
}
|
|
30
|
+
const { getUserInfo, clientType, isDev } = config
|
|
31
|
+
const $api = {
|
|
32
|
+
runtimeBaseURL: '/studio-server',
|
|
33
|
+
getUserInfo,
|
|
34
|
+
axios,
|
|
35
|
+
userInfo: getUserInfo(),
|
|
36
|
+
setUserInfo(userInfo) {
|
|
37
|
+
this.userInfo = userInfo
|
|
38
|
+
},
|
|
39
|
+
post(url, data) {
|
|
40
|
+
return axios.post(url, data)
|
|
41
|
+
},
|
|
42
|
+
fields() {
|
|
43
|
+
return {
|
|
44
|
+
fields: []
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
const actions = [
|
|
49
|
+
'list',
|
|
50
|
+
'item',
|
|
51
|
+
'save',
|
|
52
|
+
'add',
|
|
53
|
+
'edit',
|
|
54
|
+
'remove',
|
|
55
|
+
]
|
|
56
|
+
actions.forEach(action => {
|
|
57
|
+
$api[action] = function ({
|
|
58
|
+
modelKey,
|
|
59
|
+
databaseKey,
|
|
60
|
+
filter,
|
|
61
|
+
data,
|
|
62
|
+
page,
|
|
63
|
+
size,
|
|
64
|
+
}) {
|
|
65
|
+
return axios({
|
|
66
|
+
method: 'POST',
|
|
67
|
+
url: `/api/${action}?${modelKey}`,
|
|
68
|
+
data: {
|
|
69
|
+
modelKey,
|
|
70
|
+
dataSourceKey: databaseKey,
|
|
71
|
+
filter,
|
|
72
|
+
data,
|
|
73
|
+
page,
|
|
74
|
+
size,
|
|
75
|
+
},
|
|
76
|
+
baseURL: $api.runtimeBaseURL
|
|
77
|
+
})
|
|
78
|
+
}
|
|
79
|
+
})
|
|
80
|
+
Vue.prototype.$api = $api
|
|
81
|
+
Vue.prototype.$app = {
|
|
82
|
+
baseUrl: '/studio-server',
|
|
83
|
+
$compoPrefix: 'studio-',
|
|
84
|
+
$isDev: isDev,
|
|
85
|
+
}
|
|
86
|
+
const $bus = new Vue();
|
|
87
|
+
Vue.prototype.$bus = $bus
|
|
88
|
+
Vue.prototype.$$close = close
|
|
89
|
+
Vue.prototype.$$handleAction = handleAction
|
|
90
|
+
Vue.prototype.$$openWin =
|
|
91
|
+
Vue.prototype.$$openPage = function (params) {
|
|
92
|
+
console.log('params', params)
|
|
93
|
+
this.$bus.$emit("openStdWin", params)
|
|
94
|
+
}
|
|
95
|
+
Vue.prototype.$message = initToast({
|
|
96
|
+
alertViewKey: 'std-alert'
|
|
97
|
+
}, Vue)
|
|
98
|
+
|
|
99
|
+
function hasVueCompo(compoKey) {
|
|
100
|
+
console.log('compoKey, Vue.component(compoKey)', compoKey, Vue.component(compoKey))
|
|
101
|
+
if (compoKey) return Vue.component(compoKey);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function close() {
|
|
105
|
+
this.$util.emitParentIframe({ data: { action: 'close' } })
|
|
106
|
+
if (clientType === "mobile") {
|
|
107
|
+
const router = this.$router;
|
|
108
|
+
return router.back();
|
|
109
|
+
}
|
|
110
|
+
const compo = this._self;
|
|
111
|
+
console.log('compo', compo)
|
|
112
|
+
findParentCompo(compo, function (parentCompo) {
|
|
113
|
+
if (parentCompo.$isVsWin || parentCompo.$isChromeView) {
|
|
114
|
+
if (parentCompo.close) {
|
|
115
|
+
parentCompo.close();
|
|
116
|
+
return true;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function consoleLog(...params) {
|
|
124
|
+
console.log(...params);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function findParentCompo(compo, cb) {
|
|
128
|
+
if (!compo) return;
|
|
129
|
+
const { $parent } = compo;
|
|
130
|
+
if (!$parent) return;
|
|
131
|
+
const needBreak = cb($parent);
|
|
132
|
+
if (needBreak) return;
|
|
133
|
+
findParentCompo($parent, cb);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function handleAction(params) {
|
|
137
|
+
const {
|
|
138
|
+
$event,
|
|
139
|
+
$params,
|
|
140
|
+
methodMap,
|
|
141
|
+
formData,
|
|
142
|
+
evenType,
|
|
143
|
+
context,
|
|
144
|
+
pathKey = "",
|
|
145
|
+
fieldKey = "",
|
|
146
|
+
fieldName = "",
|
|
147
|
+
} = params;
|
|
148
|
+
let { method, action } = params;
|
|
149
|
+
if (!action) {
|
|
150
|
+
action = {
|
|
151
|
+
config: {
|
|
152
|
+
methodKey: params.methodKey,
|
|
153
|
+
},
|
|
154
|
+
type: "callMethod",
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
const { config } = action;
|
|
158
|
+
try {
|
|
159
|
+
switch (action.type) {
|
|
160
|
+
case "callMethod": {
|
|
161
|
+
let { methodKey } = config;
|
|
162
|
+
if (!method) method = methodMap[methodKey];
|
|
163
|
+
const { actionConfig } = params;
|
|
164
|
+
const methodParams = config.methodParams || actionConfig || {}
|
|
165
|
+
if (!methodParams.formData) methodParams.formData = formData
|
|
166
|
+
if (method)
|
|
167
|
+
method(methodParams, {
|
|
168
|
+
$event,
|
|
169
|
+
formData,
|
|
170
|
+
actionConfig,
|
|
171
|
+
});
|
|
172
|
+
else {
|
|
173
|
+
if (methodKey === "runCode") {
|
|
174
|
+
const { $set } = Vue.prototype;
|
|
175
|
+
const { code } = actionConfig;
|
|
176
|
+
if (!code) throw new Error(`未配置运行代码`);
|
|
177
|
+
try {
|
|
178
|
+
eval(code);
|
|
179
|
+
} catch (err) {
|
|
180
|
+
let message = "";
|
|
181
|
+
if (fieldKey) {
|
|
182
|
+
message += `字段"${fieldName || "未命名字段"}(${fieldKey})"`;
|
|
183
|
+
}
|
|
184
|
+
if (evenType) {
|
|
185
|
+
message += `事件类型"${evenType}"`;
|
|
186
|
+
}
|
|
187
|
+
throw new Error(
|
|
188
|
+
`${message}自定义代码错误: "${err.message}"(code: ${code})`
|
|
189
|
+
);
|
|
190
|
+
}
|
|
191
|
+
} else if (methodKey === "close") close.call(context);
|
|
192
|
+
else throw new Error(`对应方法不存在(${methodKey})`);
|
|
193
|
+
}
|
|
194
|
+
break;
|
|
195
|
+
}
|
|
196
|
+
case "close": {
|
|
197
|
+
close.call(context);
|
|
198
|
+
break;
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
} catch (err) {
|
|
202
|
+
console.error(err);
|
|
203
|
+
$toast.error(err);
|
|
204
|
+
}
|
|
205
|
+
}
|