@studio-kit/utils-browser 1.0.2 → 1.0.3
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 +5 -2
- package/src/APP.js +159 -0
- package/src/DOM.js +100 -13
- package/src/Data.js +18 -2
- package/src/DateConfig.js +197 -0
- package/src/DateTime.js +2 -0
- package/src/Effect.js +4 -1
- package/src/EventBus.js +2 -0
- package/src/FIO.js +183 -3
- package/src/Geo.js +2 -0
- package/src/HTTP.js +20 -0
- package/src/Img.js +482 -0
- package/src/Message.js +7 -0
- package/src/Perf.js +3 -0
- package/src/Socket.js +263 -0
- package/src/Style.js +74 -3
- package/src/UserInput.js +0 -1
- package/src/cta.js +3 -2
- package/src/methods/dom.js +6 -10
- package/src/methods/loadLib.js +24 -2
- package/src/types/app.d.ts +67 -0
- package/src/vue2-plugin.js +2 -5
- package/src/methods/message.js +0 -5
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@studio-kit/utils-browser",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.3",
|
|
4
4
|
"description": "",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -14,7 +14,10 @@
|
|
|
14
14
|
"@studio-kit/utils-common": "^2.0.1",
|
|
15
15
|
"axios": "^1.7.9",
|
|
16
16
|
"chroma-js": "^3.1.2",
|
|
17
|
-
"
|
|
17
|
+
"clipboardy": "^5.3.1",
|
|
18
|
+
"file-saver": "^2.0.5",
|
|
19
|
+
"nanoid": "^5.1.2",
|
|
20
|
+
"reconnecting-websocket": "^4.4.0"
|
|
18
21
|
},
|
|
19
22
|
"exports": {
|
|
20
23
|
".": "./src/index.js",
|
package/src/APP.js
ADDED
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import Data from "./Data";
|
|
2
|
+
import HTTP from "./HTTP.js";
|
|
3
|
+
import clipboard from 'clipboardy';
|
|
4
|
+
export default class APP {
|
|
5
|
+
static USER_INFO_KEY = '$userInfo'
|
|
6
|
+
static clipboard = clipboard
|
|
7
|
+
/**
|
|
8
|
+
* 以 Vue Router hash 模式在新标签页打开路由(#/path?query)
|
|
9
|
+
* @param {string} path 路由路径,如 /workflow-menu
|
|
10
|
+
* @param {Record<string, unknown>} [query] hash 查询参数
|
|
11
|
+
* @param {string} [target] window.open 的 target,默认 _blank
|
|
12
|
+
* @returns {Window | null}
|
|
13
|
+
*/
|
|
14
|
+
static openHashUrl({ path, query, target = '_blank', name = '新页面' } = {}) {
|
|
15
|
+
let routePath = path || '/'
|
|
16
|
+
if (routePath.startsWith('#')) {
|
|
17
|
+
routePath = routePath.slice(1)
|
|
18
|
+
}
|
|
19
|
+
if (!routePath.startsWith('/')) {
|
|
20
|
+
routePath = `/${routePath}`
|
|
21
|
+
}
|
|
22
|
+
const queryStr = query && Object.keys(query).length
|
|
23
|
+
? HTTP.stringifyUrlQuery({ query })
|
|
24
|
+
: ''
|
|
25
|
+
const hash = queryStr ? `${routePath}?${queryStr}` : routePath
|
|
26
|
+
const base = location.href.replace(/#.*$/, '')
|
|
27
|
+
const url = `${base}#${hash}`
|
|
28
|
+
const openPayload = {
|
|
29
|
+
target: "3",
|
|
30
|
+
id: new Date().getTime(),
|
|
31
|
+
openType: "iframe",
|
|
32
|
+
name: name,
|
|
33
|
+
accessPath: url
|
|
34
|
+
}
|
|
35
|
+
let opened = false
|
|
36
|
+
try {
|
|
37
|
+
if (window.parent && window.parent !== window && window.parent.openNewPage) {
|
|
38
|
+
window.parent.openNewPage(openPayload)
|
|
39
|
+
opened = true
|
|
40
|
+
}
|
|
41
|
+
} catch (_) {}
|
|
42
|
+
if (!opened) {
|
|
43
|
+
const openNewPage = APP.getTopProp('openNewPage')
|
|
44
|
+
if (openNewPage) {
|
|
45
|
+
openNewPage(openPayload)
|
|
46
|
+
} else {
|
|
47
|
+
window.open(url, "_blank");
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* 安全读取当前窗或 top 上的命名属性,跨域时返回 fallback,不抛错
|
|
53
|
+
* @param {string} key 属性名,如 'electronAPI' | 'openNewPage' | 'STD_REALTIME'
|
|
54
|
+
* @param {{ fallback?: any }} [options]
|
|
55
|
+
* @returns {any}
|
|
56
|
+
*/
|
|
57
|
+
static getTopProp(key, { fallback } = {}) {
|
|
58
|
+
if (typeof window === 'undefined' || !key) {
|
|
59
|
+
return fallback
|
|
60
|
+
}
|
|
61
|
+
try {
|
|
62
|
+
const local = window[key]
|
|
63
|
+
if (local != null) {
|
|
64
|
+
return local
|
|
65
|
+
}
|
|
66
|
+
} catch (_) {}
|
|
67
|
+
try {
|
|
68
|
+
const top = window.top
|
|
69
|
+
if (top && top !== window) {
|
|
70
|
+
const value = top[key]
|
|
71
|
+
if (value != null) {
|
|
72
|
+
return value
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
} catch (_) {
|
|
76
|
+
// Blocked a frame with origin "..." from accessing a cross-origin frame
|
|
77
|
+
}
|
|
78
|
+
return fallback
|
|
79
|
+
}
|
|
80
|
+
static openStdWin(params) {
|
|
81
|
+
APP.$bus.$emit("openStdWin", params)
|
|
82
|
+
}
|
|
83
|
+
/** 通过全局 $bus 触发 toast.loading */
|
|
84
|
+
static showLoadingToast(msg) {
|
|
85
|
+
APP.$bus?.$emit('showLoadingToast', msg)
|
|
86
|
+
}
|
|
87
|
+
/** 通过全局 $bus 触发 toast.success(字符串或参数对象) */
|
|
88
|
+
static showSuccessToast(params) {
|
|
89
|
+
APP.$bus?.$emit('showSuccessToast', params)
|
|
90
|
+
}
|
|
91
|
+
/** 通过全局 $bus 触发 toast.error(字符串或含 message 的对象) */
|
|
92
|
+
static showErrorToast(params) {
|
|
93
|
+
APP.$bus?.$emit('showErrorToast', params)
|
|
94
|
+
}
|
|
95
|
+
/** 通过全局 $bus 触发 toast.warning */
|
|
96
|
+
static showWarningToast(params) {
|
|
97
|
+
APP.$bus?.$emit('showErrorToast', params)
|
|
98
|
+
// APP.$bus?.$emit('showWarningToast', params)
|
|
99
|
+
}
|
|
100
|
+
/** 通过全局 $bus 触发 toast.confirm */
|
|
101
|
+
static showConfirm(config) {
|
|
102
|
+
APP.$bus?.$emit('showConfirm', config)
|
|
103
|
+
}
|
|
104
|
+
static async sleep(ms) {
|
|
105
|
+
return new Promise((resolve, reject) => {
|
|
106
|
+
setTimeout(resolve, ms)
|
|
107
|
+
})
|
|
108
|
+
}
|
|
109
|
+
static copyText(text) {
|
|
110
|
+
return clipboard.write(text)
|
|
111
|
+
}
|
|
112
|
+
static emitParentIframe({ data, origin = '*', topic, payload }) {
|
|
113
|
+
if (topic && payload) {
|
|
114
|
+
data = {
|
|
115
|
+
topic,
|
|
116
|
+
payload,
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
console.log('emitParentIframe', data)
|
|
120
|
+
try {
|
|
121
|
+
if (window.parent) window.parent.postMessage(data, origin);
|
|
122
|
+
else console.error('对应父级iframe不存在')
|
|
123
|
+
} catch (error) {
|
|
124
|
+
console.error(error)
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
static setUserInfo({ userInfo }) {
|
|
128
|
+
APP.userInfo = userInfo
|
|
129
|
+
Data.setCache({
|
|
130
|
+
key: APP.USER_INFO_KEY,
|
|
131
|
+
value: userInfo,
|
|
132
|
+
})
|
|
133
|
+
if (userInfo?.token) {
|
|
134
|
+
HTTP.setToken({ token: userInfo.token })
|
|
135
|
+
} else {
|
|
136
|
+
HTTP.clearToken()
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
static getUserInfo() {
|
|
140
|
+
if (APP.userInfo) {
|
|
141
|
+
return APP.userInfo
|
|
142
|
+
} else {
|
|
143
|
+
return Data.getCache({
|
|
144
|
+
key: APP.USER_INFO_KEY,
|
|
145
|
+
})
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
static getApiBaseUrl() {
|
|
149
|
+
return window.LV_API_BASE_URL || window.$std_api_base_url || '/studio-server'
|
|
150
|
+
}
|
|
151
|
+
static runCode({ code, args }) {
|
|
152
|
+
try {
|
|
153
|
+
const fn = new Function(code)
|
|
154
|
+
return fn.call(args)
|
|
155
|
+
} catch (error) {
|
|
156
|
+
console.error('run code error:', error)
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
}
|
package/src/DOM.js
CHANGED
|
@@ -1,19 +1,36 @@
|
|
|
1
|
+
import Perf from './Perf.js';
|
|
2
|
+
|
|
1
3
|
export default class DOM {
|
|
2
4
|
// 获取坐标在元素中的相对位置
|
|
3
5
|
static getOffsetPositionInElement({
|
|
4
6
|
targetElement, // 对应DOM元素
|
|
5
7
|
absolutePosition, // 相对于屏幕左上角的坐标 { x:..., y:.. }
|
|
6
8
|
}) {
|
|
7
|
-
// 获取元素在视口中的位置信息
|
|
9
|
+
// // 获取元素在视口中的位置信息
|
|
8
10
|
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
|
-
|
|
15
|
-
}
|
|
16
|
-
return { x, y, isOutside };
|
|
11
|
+
// // 计算坐标相对于元素左上角的位置
|
|
12
|
+
// const x = absolutePosition.x - rect.left;
|
|
13
|
+
// const y = absolutePosition.y - rect.top;
|
|
14
|
+
// let isOutside = false
|
|
15
|
+
// if (x < 0 || y < 0 || x > rect.right || y > rect.bottom) {
|
|
16
|
+
// isOutside = true
|
|
17
|
+
// }
|
|
18
|
+
// return { x, y, isOutside };
|
|
19
|
+
// 计算视口坐标系下的相对位置
|
|
20
|
+
const viewportX = absolutePosition.x - rect.left;
|
|
21
|
+
const viewportY = absolutePosition.y - rect.top;
|
|
22
|
+
|
|
23
|
+
// 判断是否在可视区域外(基于元素布局尺寸)
|
|
24
|
+
const isOutside = viewportX < 0 || viewportY < 0 ||
|
|
25
|
+
viewportX > rect.width ||
|
|
26
|
+
viewportY > rect.height;
|
|
27
|
+
|
|
28
|
+
// 转换为包含元素自身滚动的内容坐标系
|
|
29
|
+
return {
|
|
30
|
+
x: viewportX + targetElement.scrollLeft,
|
|
31
|
+
y: viewportY + targetElement.scrollTop,
|
|
32
|
+
isOutside
|
|
33
|
+
};
|
|
17
34
|
}
|
|
18
35
|
static getBoundingClientRect({ targetEl, wrapperEl, needAutoDetectParent }) {
|
|
19
36
|
if (needAutoDetectParent) {
|
|
@@ -24,7 +41,8 @@ export default class DOM {
|
|
|
24
41
|
return {
|
|
25
42
|
left: left - wrapperBoundingClientRect.left,
|
|
26
43
|
top: top - wrapperBoundingClientRect.top,
|
|
27
|
-
right
|
|
44
|
+
right: right - wrapperBoundingClientRect.left,
|
|
45
|
+
bottom: bottom - wrapperBoundingClientRect.top,
|
|
28
46
|
width: targetEl.offsetWidth,
|
|
29
47
|
height: targetEl.offsetHeight
|
|
30
48
|
}
|
|
@@ -48,9 +66,39 @@ export default class DOM {
|
|
|
48
66
|
const style = window.getComputedStyle(element);
|
|
49
67
|
return style.overflow === 'scroll' || style.overflow === 'auto';
|
|
50
68
|
}
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
69
|
+
/**
|
|
70
|
+
* @param {Element} element
|
|
71
|
+
* @param {(entries: ResizeObserverEntry[]) => void} onChange
|
|
72
|
+
* @param {{ debounce?: boolean, debounceDelay?: number }} [options]
|
|
73
|
+
* debounce 默认 true;为 false 时每次尺寸回调立即执行 onChange
|
|
74
|
+
*/
|
|
75
|
+
static watchSizeChange(element, onChange, options = {}) {
|
|
76
|
+
if (!window.ResizeObserver || !element || typeof onChange !== 'function') {
|
|
77
|
+
return null;
|
|
78
|
+
}
|
|
79
|
+
const debounceEnabled = options.debounce !== false;
|
|
80
|
+
const debounceDelay = options.debounceDelay ?? 100;
|
|
81
|
+
let notify = onChange;
|
|
82
|
+
if (debounceEnabled) {
|
|
83
|
+
notify = Perf.debounce({ callback: onChange, delay: debounceDelay });
|
|
84
|
+
}
|
|
85
|
+
const ro = new ResizeObserver((entries) => {
|
|
86
|
+
notify(entries);
|
|
87
|
+
});
|
|
88
|
+
if (typeof notify.cancel === 'function') {
|
|
89
|
+
const disconnectOrig = ro.disconnect.bind(ro);
|
|
90
|
+
const unobserveOrig = ro.unobserve.bind(ro);
|
|
91
|
+
ro.disconnect = () => {
|
|
92
|
+
notify.cancel();
|
|
93
|
+
disconnectOrig();
|
|
94
|
+
};
|
|
95
|
+
ro.unobserve = (target) => {
|
|
96
|
+
notify.cancel();
|
|
97
|
+
unobserveOrig(target);
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
ro.observe(element);
|
|
101
|
+
return ro;
|
|
54
102
|
}
|
|
55
103
|
static unwatchSizeChange(watcher, element) {
|
|
56
104
|
if (window.ResizeObserver) {
|
|
@@ -65,4 +113,43 @@ export default class DOM {
|
|
|
65
113
|
styleDom.innerHTML = code
|
|
66
114
|
document.head.appendChild(styleDom)
|
|
67
115
|
}
|
|
116
|
+
static getSizeUnit({ size }) {
|
|
117
|
+
if (typeof size === 'string') {
|
|
118
|
+
return size[size.length - 1]
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
static getRelativePositionedParent({ element }) {
|
|
122
|
+
let parent = element.parentElement;
|
|
123
|
+
while (parent) {
|
|
124
|
+
const style = window.getComputedStyle(parent);
|
|
125
|
+
if (style.position !== 'static') {
|
|
126
|
+
return parent;
|
|
127
|
+
}
|
|
128
|
+
if (document.body === parent) {
|
|
129
|
+
return parent
|
|
130
|
+
}
|
|
131
|
+
parent = parent.parentElement;
|
|
132
|
+
}
|
|
133
|
+
return parent
|
|
134
|
+
}
|
|
135
|
+
// 去除元素flex属性
|
|
136
|
+
static removeFlexStyle({ element }) {
|
|
137
|
+
if (!element) return;
|
|
138
|
+
if (DOM.checkHasFlexStyle({ element })) {
|
|
139
|
+
element.style.flex = '';
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
static findSiblingWithFlexStyle({ element }) {
|
|
143
|
+
const siblings = Array.from(element.parentElement.children);
|
|
144
|
+
for (const sibling of siblings) {
|
|
145
|
+
if (sibling !== element && DOM.checkHasFlexStyle({ element: sibling })) {
|
|
146
|
+
return sibling;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
static checkHasFlexStyle({ element }) {
|
|
151
|
+
const computedStyle = window.getComputedStyle(element);
|
|
152
|
+
const flexValue = computedStyle.flex;
|
|
153
|
+
return flexValue === '1 1 0%' || flexValue === '1';
|
|
154
|
+
}
|
|
68
155
|
}
|
package/src/Data.js
CHANGED
|
@@ -1,2 +1,18 @@
|
|
|
1
|
-
import
|
|
2
|
-
export default Data
|
|
1
|
+
import CommonData from '@studio-kit/utils-common/Data.js'
|
|
2
|
+
export default class Data extends CommonData {
|
|
3
|
+
static getCache({ key, defaultValue }) {
|
|
4
|
+
let value = localStorage.getItem(key)
|
|
5
|
+
value = this.maybeJSON({ value })
|
|
6
|
+
if (!value) {
|
|
7
|
+
return defaultValue
|
|
8
|
+
}
|
|
9
|
+
return value
|
|
10
|
+
}
|
|
11
|
+
static setCache({ key, value }) {
|
|
12
|
+
if (typeof value === 'object' && value) {
|
|
13
|
+
value = JSON.stringify(value)
|
|
14
|
+
}
|
|
15
|
+
localStorage.setItem(key, value)
|
|
16
|
+
return value
|
|
17
|
+
}
|
|
18
|
+
}
|
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
import DateTime from './DateTime.js'
|
|
2
|
+
|
|
3
|
+
const DATE_UNIT = {
|
|
4
|
+
YEAR: 'year',
|
|
5
|
+
MONTH: 'month',
|
|
6
|
+
DAY: 'day',
|
|
7
|
+
HOUR: 'hour',
|
|
8
|
+
MINUTE: 'minute',
|
|
9
|
+
SECOND: 'second',
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
const DATE_CONFIG_TYPE = {
|
|
13
|
+
RELATIVE: 'diff',
|
|
14
|
+
FIXED: 'fixed',
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const UNITS_ORDER = [
|
|
18
|
+
DATE_UNIT.YEAR,
|
|
19
|
+
DATE_UNIT.MONTH,
|
|
20
|
+
DATE_UNIT.DAY,
|
|
21
|
+
DATE_UNIT.HOUR,
|
|
22
|
+
DATE_UNIT.MINUTE,
|
|
23
|
+
DATE_UNIT.SECOND,
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
const MOMENT_UNIT_MAP = {
|
|
27
|
+
[DATE_UNIT.YEAR]: 'year',
|
|
28
|
+
[DATE_UNIT.MONTH]: 'month',
|
|
29
|
+
[DATE_UNIT.DAY]: 'day',
|
|
30
|
+
[DATE_UNIT.HOUR]: 'hour',
|
|
31
|
+
[DATE_UNIT.MINUTE]: 'minute',
|
|
32
|
+
[DATE_UNIT.SECOND]: 'second',
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
const DEFAULT_DATE_VALUE_FORMAT = 'YYYY-MM-DD HH:mm:ss'
|
|
36
|
+
|
|
37
|
+
/** 与 editor EdtParamTypeEnum 中带 dateFormat 的项保持一致 */
|
|
38
|
+
const DATE_PARAM_FORMAT_MAP = {
|
|
39
|
+
datetime: 'YYYY-MM-DD HH:mm:ss',
|
|
40
|
+
date: 'YYYY-MM-DD',
|
|
41
|
+
'year-month': 'YYYY-MM',
|
|
42
|
+
year: 'YYYY',
|
|
43
|
+
month: 'MM',
|
|
44
|
+
day: 'DD',
|
|
45
|
+
hour: 'HH',
|
|
46
|
+
minute: 'mm',
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function getDateParamFormatMap() {
|
|
50
|
+
return { ...DATE_PARAM_FORMAT_MAP }
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export function getDateConfigParam({ dateConfig, paramList = [] } = {}) {
|
|
54
|
+
if (dateConfig?.baseType !== 'param') return null
|
|
55
|
+
return (paramList || []).find((item) => {
|
|
56
|
+
return item && (
|
|
57
|
+
(dateConfig.paramId && item.id === dateConfig.paramId)
|
|
58
|
+
|| (dateConfig.paramKey && item.key === dateConfig.paramKey)
|
|
59
|
+
)
|
|
60
|
+
}) || null
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export function getDateConfigParamFormat({ dateConfig, paramList = [] } = {}) {
|
|
64
|
+
const param = getDateConfigParam({ dateConfig, paramList })
|
|
65
|
+
return param ? DATE_PARAM_FORMAT_MAP[param.valueType] || null : null
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function getDateConfigValueFormat({
|
|
69
|
+
dateConfig,
|
|
70
|
+
paramList = [],
|
|
71
|
+
fieldType,
|
|
72
|
+
dateType,
|
|
73
|
+
defaultFormat = DEFAULT_DATE_VALUE_FORMAT,
|
|
74
|
+
} = {}) {
|
|
75
|
+
return getDateConfigParamFormat({ dateConfig, paramList })
|
|
76
|
+
|| (fieldType === 'date' && dateType ? DATE_PARAM_FORMAT_MAP[dateType] : null)
|
|
77
|
+
|| defaultFormat
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function parseDateValueByFormat(value, format) {
|
|
81
|
+
if (value === undefined || value === null || value === '') return null
|
|
82
|
+
if (value instanceof Date) return value
|
|
83
|
+
const text = String(value).trim()
|
|
84
|
+
if (!text) return null
|
|
85
|
+
if (format === 'YYYY' && /^\d{4}$/.test(text)) {
|
|
86
|
+
return new Date(Number(text), 0, 1)
|
|
87
|
+
}
|
|
88
|
+
if (format === 'MM' && /^\d{1,2}$/.test(text)) {
|
|
89
|
+
return new Date(new Date().getFullYear(), Number(text) - 1, 1)
|
|
90
|
+
}
|
|
91
|
+
if (format === 'DD' && /^\d{1,2}$/.test(text)) {
|
|
92
|
+
return new Date(new Date().getFullYear(), new Date().getMonth(), Number(text))
|
|
93
|
+
}
|
|
94
|
+
if (format === 'HH' && /^\d{1,2}$/.test(text)) {
|
|
95
|
+
const date = new Date()
|
|
96
|
+
date.setHours(Number(text), 0, 0, 0)
|
|
97
|
+
return date
|
|
98
|
+
}
|
|
99
|
+
if (format === 'mm' && /^\d{1,2}$/.test(text)) {
|
|
100
|
+
const date = new Date()
|
|
101
|
+
date.setMinutes(Number(text), 0, 0)
|
|
102
|
+
return date
|
|
103
|
+
}
|
|
104
|
+
if (format === 'YYYY-MM' && /^(\d{4})-(\d{1,2})$/.test(text)) {
|
|
105
|
+
const matched = text.match(/^(\d{4})-(\d{1,2})$/)
|
|
106
|
+
if (matched) {
|
|
107
|
+
return new Date(Number(matched[1]), Number(matched[2]) - 1, 1)
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
const date = new Date(text.replace(/-/g, '/'))
|
|
111
|
+
return isNaN(date.getTime()) ? null : date
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
export function getDateConfigBaseDate({ dateConfig, paramList = [] } = {}) {
|
|
115
|
+
const param = getDateConfigParam({ dateConfig, paramList })
|
|
116
|
+
if (!param?.defaultDateConfig) return null
|
|
117
|
+
const paramFormat = getDateConfigParamFormat({ dateConfig, paramList })
|
|
118
|
+
const paramDate = computeDateFromConfig(param.defaultDateConfig)
|
|
119
|
+
if (!paramDate) return null
|
|
120
|
+
const paramValue = DateTime.format({
|
|
121
|
+
value: paramDate,
|
|
122
|
+
format: paramFormat || DEFAULT_DATE_VALUE_FORMAT,
|
|
123
|
+
})
|
|
124
|
+
return parseDateValueByFormat(paramValue, paramFormat)
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
export function getDateConfigValue({
|
|
128
|
+
dateConfig,
|
|
129
|
+
paramList = [],
|
|
130
|
+
fieldType,
|
|
131
|
+
dateType,
|
|
132
|
+
defaultFormat = DEFAULT_DATE_VALUE_FORMAT,
|
|
133
|
+
} = {}) {
|
|
134
|
+
if (!dateConfig) return ''
|
|
135
|
+
const baseDate = dateConfig.baseType === 'param'
|
|
136
|
+
? getDateConfigBaseDate({ dateConfig, paramList })
|
|
137
|
+
: null
|
|
138
|
+
if (dateConfig.baseType === 'param' && !baseDate) return ''
|
|
139
|
+
const date = computeDateFromConfig(dateConfig, baseDate || undefined)
|
|
140
|
+
if (!date) return ''
|
|
141
|
+
return DateTime.format({
|
|
142
|
+
value: date,
|
|
143
|
+
format: getDateConfigValueFormat({
|
|
144
|
+
dateConfig,
|
|
145
|
+
paramList,
|
|
146
|
+
fieldType,
|
|
147
|
+
dateType,
|
|
148
|
+
defaultFormat,
|
|
149
|
+
}),
|
|
150
|
+
}) || ''
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* 根据单日期配置(相对/固定)计算相对于当前时间的日期,用于展示
|
|
155
|
+
* @param {{ year?, month?, day?, hour?, minute?, second? }} config
|
|
156
|
+
* @returns {Date | null}
|
|
157
|
+
*/
|
|
158
|
+
export function computeDateFromConfig(config, baseDate) {
|
|
159
|
+
if (!config) return null
|
|
160
|
+
const m = baseDate ? DateTime.moment(baseDate) : DateTime.moment()
|
|
161
|
+
if (typeof m.isValid === 'function' && !m.isValid()) return null
|
|
162
|
+
for (const unitKey of UNITS_ORDER) {
|
|
163
|
+
const item = config[unitKey]
|
|
164
|
+
if (!item) continue
|
|
165
|
+
const momUnit = MOMENT_UNIT_MAP[unitKey]
|
|
166
|
+
if (item.type === DATE_CONFIG_TYPE.RELATIVE) {
|
|
167
|
+
const diff = item.diff ?? 0
|
|
168
|
+
if (diff !== 0) {
|
|
169
|
+
if (momUnit === 'day') {
|
|
170
|
+
const before = m.clone()
|
|
171
|
+
m.add(diff, momUnit)
|
|
172
|
+
if (m.month() !== before.month() || m.year() !== before.year()) {
|
|
173
|
+
// m.set(before).endOf('month');
|
|
174
|
+
} else {
|
|
175
|
+
const lastDay = DateTime.getLastDayOfMonth(m.toDate())
|
|
176
|
+
m.date(Math.min(m.date(), lastDay))
|
|
177
|
+
}
|
|
178
|
+
} else {
|
|
179
|
+
m.add(diff, momUnit)
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
} else if (item.type === DATE_CONFIG_TYPE.FIXED) {
|
|
183
|
+
const v = item.fixed
|
|
184
|
+
if (v != null) {
|
|
185
|
+
if (momUnit === 'month') {
|
|
186
|
+
m.month(Number(v) - 1)
|
|
187
|
+
} else if (momUnit === 'day') {
|
|
188
|
+
const lastDay = DateTime.getLastDayOfMonth(m.toDate())
|
|
189
|
+
m.date(Math.min(Number(v), lastDay))
|
|
190
|
+
} else {
|
|
191
|
+
m.set(momUnit, Number(v))
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
return m.toDate()
|
|
197
|
+
}
|
package/src/DateTime.js
ADDED
package/src/Effect.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import cta from "./cta";
|
|
2
2
|
export default class Effect {
|
|
3
|
-
static morph({ sourceEl, targetEl, duration }) {
|
|
3
|
+
static morph({ sourceEl, targetEl, duration, onFinish }) {
|
|
4
|
+
if (onFinish) {
|
|
5
|
+
setTimeout(onFinish, duration * 1e3)
|
|
6
|
+
}
|
|
4
7
|
return cta(sourceEl, targetEl, {
|
|
5
8
|
duration, // seconds
|
|
6
9
|
});
|
package/src/EventBus.js
ADDED