mirror of
https://github.com/skyle1995/NetworkAuth.git
synced 2026-05-25 02:24:05 +08:00
New warehouse
This commit is contained in:
83
web/static/lib/README.md
Normal file
83
web/static/lib/README.md
Normal file
@@ -0,0 +1,83 @@
|
||||
# ColorMode 模块(WIP)
|
||||
|
||||
开箱即用的主题切换(深色/浅色/自定义)模块,具有自动数据持久性。
|
||||
|
||||
**基本使用**
|
||||
|
||||
```js
|
||||
layui.use(['colorMode'], function () {
|
||||
var colorMode = layui.colorMode
|
||||
|
||||
var theme = colorMode.init()
|
||||
|
||||
}
|
||||
);
|
||||
```
|
||||
|
||||
**配置**
|
||||
|
||||
模块仅处理 DOM 属性更改,以便在 CSS 中应用正确的选择器,不会处理实际的样式,主题或 CSS。
|
||||
默认情况下,使用 auto 模式(与用户的浏览器首选项匹配),将类 dark 应用于 html 标签时启用深色模式,返回一个对象,用来获取和改变主题。
|
||||
|
||||
```js
|
||||
var theme = colorMode.init()
|
||||
|
||||
theme.mode() // 'dark' | 'light'
|
||||
|
||||
theme.setMode('dark') // 设置为深色模式并持久化到 localstorage
|
||||
|
||||
theme.setMode('auto') // 设置为 auto 模式
|
||||
```
|
||||
|
||||
也可以自定义以使其适用于大多数场景
|
||||
|
||||
```js
|
||||
var theme = colorMode.init({
|
||||
selector: 'body',
|
||||
attribute: 'theme-mode',
|
||||
initialValue: 'light',
|
||||
modes: {
|
||||
auto: '',
|
||||
light: 'light',
|
||||
dark: 'dark',
|
||||
contrast: 'dark contrast',
|
||||
},
|
||||
storage: localStorage,
|
||||
storageKey: 'xxx-theme-mode',
|
||||
disableTransition: true,
|
||||
})
|
||||
```
|
||||
|
||||
如果上述配置仍不能满足您的需求,可以使用 onChanged 选项完全控制处理更新的方式
|
||||
|
||||
```js
|
||||
var theme = colorMode.init({
|
||||
onChanged: function(mode, defaultHandler){
|
||||
// 自定义更新方式
|
||||
}
|
||||
})
|
||||
```
|
||||
|
||||
**API**
|
||||
|
||||
```ts
|
||||
/**
|
||||
* @typedef {object} initOptions
|
||||
* @prop {string} [selector='html'] - 应用于目标元素的 CSS 选择器
|
||||
* @prop {string} [attribute='class'] - 应用于目标元素的 HTML 属性
|
||||
* @prop {string} [initialValue='auto'] - 初始颜色模式
|
||||
* @prop {Object.<string, string>} [modes]- 颜色模式。value 为添加到 HTML 属性上的值
|
||||
* @prop {(mode: string, defaultHandler: () => void) => void} [onChanged] - 用于处理更新的自定义处理程序,指定时,默认行为将被覆盖。mode 为颜色模式,defaultHandler 为默认处理程序
|
||||
* @prop {Storage} [storage=localStorage] - 将数据持久化到 localStorage/sessionStorage 的键。传递 `null` 以禁用持久性
|
||||
* @prop {string | null} [storageKey='color-scheme'] - 持久化使用的 key
|
||||
* @prop {boolean} [disableTransition=true] - 禁用切换时的过渡 {@link https://paco.me/writing/disable-theme-transitions}
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {initOptions} options
|
||||
* @returns {{ mode: () => string; setMode: (mode: string) => void;}}
|
||||
*/
|
||||
colorMode.init(options)
|
||||
```
|
||||
191
web/static/lib/colorMode.js
Executable file
191
web/static/lib/colorMode.js
Executable file
@@ -0,0 +1,191 @@
|
||||
/**
|
||||
* WIP
|
||||
* 移植自 https://github.com/vueuse/vueuse/tree/main/packages/core/useColorMode
|
||||
*/
|
||||
// @ts-ignore
|
||||
layui.define(['jquery'], function (exports) {
|
||||
'use strict';
|
||||
|
||||
/** @type {jQuery}*/
|
||||
var $ = layui.jquery;
|
||||
|
||||
var MOD_NAME = 'colorMode';
|
||||
var defaultWindow = window;
|
||||
var document = defaultWindow.document;
|
||||
|
||||
var colorMode = {
|
||||
/**
|
||||
* @typedef {object} initOptions
|
||||
* @prop {string} [selector="html"] - 应用于目标元素的 CSS 选择器
|
||||
* @prop {string} [attribute="class"] - 应用于目标元素的 HTML 属性
|
||||
* @prop {string} [initialValue='auto'] - 初始颜色模式
|
||||
* @prop {Object.<string, string>} [modes]- 颜色模式。value 为添加到 HTML 属性上的值
|
||||
* @prop {(mode: string, defaultHandler: (window?: Window) => void) => void} [onChanged] - 用于处理更新的自定义处理程序,指定时,默认行为将被覆盖。
|
||||
* @prop {Storage} [storage=localStorage] - 将数据持久化到 localStorage/sessionStorage 的键。传递 `null` 以禁用持久性
|
||||
* @prop {string | null} [storageKey='color-scheme'] - 持久化使用的 key
|
||||
* @prop {boolean} [disableTransition=true] - 禁用切换时的过渡 {@link https://paco.me/writing/disable-theme-transitions}
|
||||
*
|
||||
*/
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {initOptions} options
|
||||
* @returns {{mode: () => string; setMode: (mode: string, window?: Window) => void; }}
|
||||
*/
|
||||
init: function (options) {
|
||||
var defaults = {
|
||||
selector: 'html',
|
||||
attribute: 'class',
|
||||
initialValue: 'auto',
|
||||
modes: {
|
||||
auto: '',
|
||||
light: 'light',
|
||||
dark: 'dark',
|
||||
},
|
||||
storage: localStorage,
|
||||
storageKey: 'color-scheme',
|
||||
disableTransition: true,
|
||||
};
|
||||
|
||||
var opts = $.extend(true, {}, defaults, options);
|
||||
|
||||
// 当前颜色模式
|
||||
var state;
|
||||
// 系统颜色模式
|
||||
var system;
|
||||
// 初始化 storage
|
||||
var store =
|
||||
opts.storageKey == null
|
||||
? opts.initialValue
|
||||
: (function () {
|
||||
var v = opts.storage.getItem(opts.storageKey);
|
||||
if (!v) {
|
||||
opts.storage.setItem(opts.storageKey, opts.initialValue);
|
||||
return opts.initialValue;
|
||||
}
|
||||
return v;
|
||||
})();
|
||||
|
||||
/**
|
||||
* 更新 HTML 属性值
|
||||
* @param {String} selector
|
||||
* @param {String} attribute
|
||||
* @param {String} value
|
||||
* @param {Window} win
|
||||
*/
|
||||
var updateHTMLAttrs = function (selector, attribute, value, win) {
|
||||
win = win || defaultWindow;
|
||||
var document = win.document;
|
||||
var el = typeof selector === 'string' ? document.querySelector(selector) : undefined;
|
||||
if (!el) return;
|
||||
|
||||
/**@type HTMLStyleElement */
|
||||
var style;
|
||||
|
||||
if (opts.disableTransition) {
|
||||
style = document.createElement('style');
|
||||
style.appendChild(
|
||||
document.createTextNode(
|
||||
'*,*::before,*::after{-webkit-transition:none!important;-moz-transition:none!important;-o-transition:none!important;-ms-transition:none!important;transition:none!important}'
|
||||
)
|
||||
);
|
||||
document.head.appendChild(style);
|
||||
}
|
||||
|
||||
if (attribute === 'class') {
|
||||
var current = value.split(/\s/g);
|
||||
$.each(opts.modes, function (_, modeval) {
|
||||
$.each((modeval || '').split(/\s/g), function (_, v) {
|
||||
if (!v) return;
|
||||
if (current.indexOf(v) !== -1) {
|
||||
el.classList.add(v);
|
||||
} else {
|
||||
el.classList.remove(v);
|
||||
}
|
||||
});
|
||||
});
|
||||
} else {
|
||||
el.setAttribute(attribute, value);
|
||||
}
|
||||
|
||||
if (opts.disableTransition) {
|
||||
// 调用 getComputedStyle 强制浏览器重绘
|
||||
// @ts-expect-error 未使用的变量
|
||||
var _ = window.getComputedStyle(style).opacity;
|
||||
document.head.removeChild(style);
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* 更新状态
|
||||
* @param {String} mode - 颜色模式
|
||||
*/
|
||||
var updateState = function (mode) {
|
||||
store = opts.storageKey == null ? mode : opts.storage.getItem(opts.storageKey);
|
||||
|
||||
state = store === 'auto' ? system : store;
|
||||
};
|
||||
|
||||
var prefersColorScheme = function () {
|
||||
var isSupported = window && 'matchMedia' in window && typeof window.matchMedia === 'function';
|
||||
if (!isSupported) {
|
||||
system = 'light';
|
||||
onChanged(system);
|
||||
return;
|
||||
}
|
||||
|
||||
var darkThemeMediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
|
||||
var update = function () {
|
||||
var preferredDark = darkThemeMediaQuery.matches;
|
||||
system = preferredDark ? 'dark' : 'light';
|
||||
onChanged(system);
|
||||
};
|
||||
update();
|
||||
if ('addEventListener' in darkThemeMediaQuery) {
|
||||
darkThemeMediaQuery.addEventListener('change', update);
|
||||
} else {
|
||||
// @ts-ignore 已弃用
|
||||
darkThemeMediaQuery.addListener(update);
|
||||
}
|
||||
};
|
||||
|
||||
prefersColorScheme();
|
||||
|
||||
function defaultOnChanged(win) {
|
||||
updateHTMLAttrs(opts.selector, opts.attribute, opts.modes[state], win);
|
||||
}
|
||||
|
||||
function onChanged(mode, win) {
|
||||
updateState(mode);
|
||||
if (opts.onChanged) {
|
||||
opts.onChanged(state, defaultOnChanged);
|
||||
} else {
|
||||
defaultOnChanged(win);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
setMode: function (mode, win) {
|
||||
if (opts.storageKey) {
|
||||
opts.storage.setItem(opts.storageKey, mode);
|
||||
}
|
||||
onChanged(mode, win);
|
||||
},
|
||||
mode: function () {
|
||||
return state;
|
||||
},
|
||||
};
|
||||
},
|
||||
addStyle: function (id, cssStr) {
|
||||
var el = /** @type {HTMLStyleElement} */ (document.getElementById(id) || document.createElement('style'));
|
||||
if (!el.isConnected) {
|
||||
el.type = 'text/css';
|
||||
el.id = id;
|
||||
document.head.appendChild(el);
|
||||
}
|
||||
el.textContent = cssStr;
|
||||
},
|
||||
};
|
||||
|
||||
exports(MOD_NAME, colorMode);
|
||||
});
|
||||
317
web/static/lib/drawer/drawer.css
Normal file
317
web/static/lib/drawer/drawer.css
Normal file
@@ -0,0 +1,317 @@
|
||||
.layer-drawer.layui-layer {
|
||||
border-radius: 0 !important;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.layer-drawer.layui-layer.position-absolute {
|
||||
position: absolute !important;
|
||||
}
|
||||
|
||||
.layer-drawer-anim,
|
||||
.layer-drawer-anim.layui-anim {
|
||||
-webkit-animation-duration: .3s;
|
||||
animation-duration: .3s;
|
||||
-webkit-animation-timing-function: cubic-bezier(0.7, 0.3, 0.1, 1);
|
||||
animation-timing-function: cubic-bezier(0.7, 0.3, 0.1, 1);
|
||||
}
|
||||
|
||||
/* right to left */
|
||||
@keyframes layer-rl {
|
||||
from {
|
||||
-webkit-transform: translate3d(100%, 0, 0);
|
||||
-ms-transform: translate3d(100%, 0, 0);
|
||||
transform: translate3d(100%, 0, 0);
|
||||
opacity: 1;
|
||||
|
||||
}
|
||||
|
||||
to {
|
||||
-webkit-transform: translate3d(0, 0, 0);
|
||||
-ms-transform: translate3d(0, 0, 0);
|
||||
transform: translate3d(0, 0, 0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes layer-rl {
|
||||
from {
|
||||
-webkit-transform: translate3d(100%, 0, 0);
|
||||
-ms-transform: translate3d(100%, 0, 0);
|
||||
transform: translate3d(100%, 0, 0);
|
||||
opacity: 1;
|
||||
|
||||
}
|
||||
|
||||
to {
|
||||
-webkit-transform: translate3d(0, 0, 0);
|
||||
-ms-transform: translate3d(0, 0, 0);
|
||||
transform: translate3d(0, 0, 0);
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
|
||||
.layer-anim-rl {
|
||||
-webkit-animation-name: layer-rl;
|
||||
animation-name: layer-rl;
|
||||
}
|
||||
|
||||
/* right to left close */
|
||||
@keyframes layer-rl-close {
|
||||
from {
|
||||
-webkit-transform: translate3d(0, 0, 0);
|
||||
-ms-transform: translate3d(0, 0, 0);
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
|
||||
to {
|
||||
-webkit-transform: translate3d(100%, 0, 0);
|
||||
-ms-transform: translate3d(100%, 0, 0);
|
||||
transform: translate3d(100%, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@-webkit-keyframes layer-rl-close {
|
||||
from {
|
||||
-webkit-transform: translate3d(0, 0, 0);
|
||||
-ms-transform: translate3d(0, 0, 0);
|
||||
transform: translate3d(0, 0, 0);
|
||||
|
||||
}
|
||||
|
||||
to {
|
||||
-webkit-transform: translate3d(100%, 0, 0);
|
||||
-ms-transform: translate3d(100%, 0, 0);
|
||||
transform: translate3d(100%, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.layer-anim-rl-close,
|
||||
.layer-anim-rl.layer-anim-close {
|
||||
-webkit-animation-name: layer-rl-close;
|
||||
animation-name: layer-rl-close;
|
||||
}
|
||||
|
||||
/* left to right */
|
||||
@-webkit-keyframes layer-lr {
|
||||
from {
|
||||
-webkit-transform: translate3d(0, 0, 0);
|
||||
-ms-transform: translate3d(0, 0, 0);
|
||||
transform: translate3d(0, 0, 0);
|
||||
opacity: 1
|
||||
}
|
||||
|
||||
to {
|
||||
-webkit-transform: translate3d(-100%, 0, 0);
|
||||
-ms-transform: translate3d(-100%, 0, 0);
|
||||
transform: translate3d(-100%, 0, 0);
|
||||
opacity: 1
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes layer-lr {
|
||||
from {
|
||||
-webkit-transform: translate3d(-100%, 0, 0);
|
||||
-ms-transform: translate3d(-100%, 0, 0);
|
||||
transform: translate3d(-100%, 0, 0);
|
||||
opacity: 1
|
||||
}
|
||||
|
||||
to {
|
||||
-webkit-transform: translate3d(0, 0, 0);
|
||||
-ms-transform: translate3d(0, 0, 0);
|
||||
transform: translate3d(0, 0, 0);
|
||||
opacity: 1
|
||||
}
|
||||
}
|
||||
|
||||
.layer-anim-lr {
|
||||
-webkit-animation-name: layer-lr;
|
||||
animation-name: layer-lr
|
||||
}
|
||||
|
||||
/* left to right close */
|
||||
@-webkit-keyframes layer-lr-close {
|
||||
from {
|
||||
-webkit-transform: translate3d(0, 0, 0);
|
||||
-ms-transform: translate3d(0, 0, 0);
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
|
||||
to {
|
||||
-webkit-transform: translate3d(-100%, 0, 0);
|
||||
-ms-transform: translate3d(-100%, 0, 0);
|
||||
transform: translate3d(-100%, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes layer-lr-close {
|
||||
from {
|
||||
-webkit-transform: translate3d(0, 0, 0);
|
||||
-ms-transform: translate3d(0, 0, 0);
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
|
||||
to {
|
||||
-webkit-transform: translate3d(-100%, 0, 0);
|
||||
-ms-transform: translate3d(-100%, 0, 0);
|
||||
transform: translate3d(-100%, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.layer-anim-lr-close,
|
||||
.layer-anim-lr.layer-anim-close {
|
||||
-webkit-animation-name: layer-lr-close;
|
||||
animation-name: layer-lr-close
|
||||
}
|
||||
|
||||
/* top to bottom */
|
||||
@-webkit-keyframes layer-tb {
|
||||
from {
|
||||
-webkit-transform: translate3d(0, -100%, 0);
|
||||
-ms-transform: translate3d(0, -100%, 0);
|
||||
transform: translate3d(0, -100%, 0);
|
||||
opacity: 1;
|
||||
animation-timing-function: cubic-bezier(0.7, 0.3, 0.1, 1);
|
||||
}
|
||||
|
||||
to {
|
||||
-webkit-transform: translate3d(0, 0, 0);
|
||||
-ms-transform: translate3d(0, 0, 0);
|
||||
transform: translate3d(0, 0, 0);
|
||||
opacity: 1;
|
||||
animation-timing-function: cubic-bezier(0.7, 0.3, 0.1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes layer-tb {
|
||||
from {
|
||||
-webkit-transform: translate3d(0, -100%, 0);
|
||||
-ms-transform: translate3d(0, -100%, 0);
|
||||
transform: translate3d(0, -100%, 0);
|
||||
opacity: 1;
|
||||
animation-timing-function: cubic-bezier(0.7, 0.3, 0.1, 1);
|
||||
}
|
||||
|
||||
to {
|
||||
-webkit-transform: translate3d(0, 0, 0);
|
||||
-ms-transform: translate3d(0, 0, 0);
|
||||
transform: translate3d(0, 0, 0);
|
||||
opacity: 1;
|
||||
animation-timing-function: cubic-bezier(0.7, 0.3, 0.1, 1);
|
||||
}
|
||||
}
|
||||
|
||||
.layer-anim-tb {
|
||||
-webkit-animation-name: layer-tb;
|
||||
animation-name: layer-tb
|
||||
}
|
||||
|
||||
/* top to bottom close */
|
||||
@-webkit-keyframes layer-tb-close {
|
||||
from {
|
||||
-webkit-transform: translate3d(0, 0, 0);
|
||||
-ms-transform: translate3d(0, 0, 0);
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
|
||||
to {
|
||||
-webkit-transform: translate3d(0, -100%, 0);
|
||||
-ms-transform: translate3d(0, -100%, 0);
|
||||
transform: translate3d(0, -100%, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes layer-tb-close {
|
||||
from {
|
||||
-webkit-transform: translate3d(0, 0, 0);
|
||||
-ms-transform: translate3d(0, 0, 0);
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
|
||||
to {
|
||||
-webkit-transform: translate3d(0, -100%, 0);
|
||||
-ms-transform: translate3d(0, -100%, 0);
|
||||
transform: translate3d(0, -100%, 0);
|
||||
}
|
||||
}
|
||||
|
||||
.layer-anim-tb-close,
|
||||
.layer-anim-tb.layer-anim-close {
|
||||
-webkit-animation-name: layer-tb-close;
|
||||
animation-name: layer-tb-close
|
||||
}
|
||||
|
||||
/* bottom to top */
|
||||
@-webkit-keyframes layer-bt {
|
||||
from {
|
||||
-webkit-transform: translate3d(0, 100%, 0);
|
||||
-ms-transform: translate3d(0, 100%, 0);
|
||||
transform: translate3d(0, 100%, 0);
|
||||
opacity: 1
|
||||
}
|
||||
|
||||
to {
|
||||
-webkit-transform: translate3d(0, 0, 0);
|
||||
-ms-transform: translate3d(0, 0, 0);
|
||||
transform: translate3d(0, 0, 0);
|
||||
opacity: 1
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes layer-bt {
|
||||
from {
|
||||
-webkit-transform: translate3d(0, 100%, 0);
|
||||
-ms-transform: translate3d(0, 100%, 0);
|
||||
transform: translate3d(0, 100%, 0);
|
||||
opacity: 1
|
||||
}
|
||||
|
||||
to {
|
||||
-webkit-transform: translate3d(0, 0, 0);
|
||||
-ms-transform: translate3d(0, 0, 0);
|
||||
transform: translate3d(0, 0, 0);
|
||||
opacity: 1
|
||||
}
|
||||
}
|
||||
|
||||
.layer-anim-bt {
|
||||
-webkit-animation-name: layer-bt;
|
||||
animation-name: layer-bt
|
||||
}
|
||||
|
||||
/* bottom to top close */
|
||||
@-webkit-keyframes layer-bt-close {
|
||||
from {
|
||||
-webkit-transform: translate3d(0, 0, 0);
|
||||
-ms-transform: translate3d(0, 0, 0);
|
||||
transform: translate3d(0, 0, 0);
|
||||
}
|
||||
|
||||
to {
|
||||
-webkit-transform: translate3d(0, 100%, 0);
|
||||
-ms-transform: translate3d(0, 100%, 0);
|
||||
transform: translate3d(0, 100%, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes layer-bt-close {
|
||||
from {
|
||||
-webkit-transform: translate3d(0, 0, 0);
|
||||
-ms-transform: translate3d(0, 0, 0);
|
||||
transform: translate3d(0, 0, 0);
|
||||
|
||||
}
|
||||
|
||||
to {
|
||||
-webkit-transform: translate3d(0, 100%, 0);
|
||||
-ms-transform: translate3d(0, 100%, 0);
|
||||
transform: translate3d(0, 100%, 0);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
.layer-anim-bt-close,
|
||||
.layer-anim-bt.layer-anim-close {
|
||||
-webkit-animation-name: layer-bt-close;
|
||||
animation-name: layer-bt-close
|
||||
}
|
||||
200
web/static/lib/drawer/drawer.js
Executable file
200
web/static/lib/drawer/drawer.js
Executable file
@@ -0,0 +1,200 @@
|
||||
/**
|
||||
* 抽屉模块
|
||||
*/
|
||||
layui.define(['jquery', 'layer'], function (exports) {
|
||||
('use strict');
|
||||
|
||||
var MOD_NAME = 'drawer';
|
||||
var $ = layui.jquery;
|
||||
var layer = layui.layer;
|
||||
|
||||
layui.link(layui.cache.base + 'drawer/drawer.css');
|
||||
var drawer = new (function () {
|
||||
this.open = function (option) {
|
||||
return layerDrawer(option);
|
||||
};
|
||||
this.title = layer.title;
|
||||
this.style = layer.style;
|
||||
this.close = layer.close;
|
||||
this.closeAll = layer.closeAll;
|
||||
})();
|
||||
|
||||
/**
|
||||
*
|
||||
* 封装 layer.open
|
||||
*
|
||||
* @param {object} option, `type`, `anim`, `move`, `fixed`, `skin`,`maxWidth`, `maxHeight`, `moveOut`, `moveEnd` 不可用,其它参数和 layer.open 一致, 新增 `iframe`和 `url`参数
|
||||
* @returns {number} 原生 layer 的 index
|
||||
*/
|
||||
function layerDrawer(option) {
|
||||
var opt = normalizeOption(option);
|
||||
if (opt.target) appendToTarget(opt);
|
||||
if (opt.url) loadFragment(opt);
|
||||
if (opt.shade) {
|
||||
$('<style/>')
|
||||
.attr('id', 'layer-drawer')
|
||||
.html('.layui-layer-shade{opacity: 0;transition: opacity .35s cubic-bezier(0.34, 0.69, 0.1, 1);}') // fadeIn
|
||||
.appendTo('head');
|
||||
|
||||
option.end = Aspect(option.end, undefined, function (layero, index) {
|
||||
$('#layer-drawer').remove();
|
||||
});
|
||||
}
|
||||
return layer.open(opt);
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载 HTML 片段到 layer content
|
||||
* @param {object} option 设置选项
|
||||
*/
|
||||
function loadFragment(option) {
|
||||
option.success = Aspect(option.success, function (layero) {
|
||||
$.ajax({
|
||||
url: option.url,
|
||||
dataType: 'html',
|
||||
success: function (result) {
|
||||
layero.children('.layui-layer-content').html(result);
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
*将 layer 附加到指定节点
|
||||
* @param {object} opt 设置选项
|
||||
*/
|
||||
function appendToTarget(opt) {
|
||||
var targetDOM = $(opt.target);
|
||||
var contentDOM = opt.content;
|
||||
|
||||
contentDOM.appendTo(targetDOM);
|
||||
opt.skin = getDrawerAnimationClass(opt.offset, true);
|
||||
opt.offset = calcOffset(opt.offset, opt.area, targetDOM);
|
||||
// 处理关闭后偶现 DOM 仍显示的问题
|
||||
opt.end = Aspect(opt.end, function () {
|
||||
contentDOM.css('display', 'none');
|
||||
});
|
||||
if (opt.shade) {
|
||||
opt.success = Aspect(opt.success, function (layero, index) {
|
||||
var shadeDOM = $('#layui-layer-shade' + index);
|
||||
shadeDOM.css('position', 'absolute');
|
||||
shadeDOM.appendTo(layero.parent());
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 规范化 layer.open 选项
|
||||
* @param {object} option layer.open 的选项
|
||||
* @returns 规范化后的选项
|
||||
*/
|
||||
function normalizeOption(option) {
|
||||
option.type = option.iframe ? 2 : 1;
|
||||
option.anim = -1;
|
||||
option.move = false;
|
||||
option.fixed = true;
|
||||
option.content = option.iframe ? option.iframe : option.content;
|
||||
if (option.offset === undefined) option.offset = 'r';
|
||||
option.area = calcDrawerArea(option.offset, option.area);
|
||||
option.skin = getDrawerAnimationClass(option.offset);
|
||||
if (option.title === undefined) option.title = false;
|
||||
if (option.closeBtn === undefined) option.closeBtn = false;
|
||||
if (option.shade === undefined) option.shade = 0.3;
|
||||
if (option.shadeClose === undefined) option.shadeClose = true;
|
||||
if (option.resize === undefined) option.resize = false;
|
||||
if (option.success === undefined) option.success = function () {}; // 处理遮罩需要
|
||||
if (option.end === undefined) option.end = function () {};
|
||||
return option;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算抽屉宽高
|
||||
* @param {string} offset 抽屉方向 l = 左, r = 右, t = 上, b = 下
|
||||
* @param {string[] | string} drawerArea 抽屉大小,字符串数组格式[width, height]:["200px","100%"],字符串格式:"30%" "200px"。
|
||||
* @returns{string[]} 抽屉宽高数组
|
||||
*/
|
||||
function calcDrawerArea(offset, drawerArea) {
|
||||
if (drawerArea instanceof Array) {
|
||||
return drawerArea;
|
||||
}
|
||||
drawerArea = drawerArea === undefined || drawerArea === 'auto' ? '30%' : drawerArea;
|
||||
if (offset === 'l' || offset === 'r') {
|
||||
return [drawerArea, '100%'];
|
||||
} else if (offset === 't' || offset === 'b') {
|
||||
return ['100%', drawerArea];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取抽屉动画类,指定挂载容器时需要设置绝对定位
|
||||
* @param {string} offset 抽屉方向 l = 左, r = 右, t = 上, b = 下
|
||||
* @param {boolean} [isAbsolute] 是否是绝对定位
|
||||
* @returns {string} 抽屉入场动画类
|
||||
*/
|
||||
function getDrawerAnimationClass(offset, isAbsolute) {
|
||||
var prefixClass = 'layer-drawer layer-drawer-anim layui-anim layer-anim-';
|
||||
var suffix = 'rl';
|
||||
if (offset === 'l') {
|
||||
suffix = 'lr';
|
||||
} else if (offset === 'r') {
|
||||
suffix = 'rl';
|
||||
} else if (offset === 't') {
|
||||
suffix = 'tb';
|
||||
} else if (offset === 'b') {
|
||||
suffix = 'bt';
|
||||
}
|
||||
return prefixClass + suffix + (isAbsolute ? ' position-absolute ' : '');
|
||||
}
|
||||
|
||||
/**
|
||||
* 指定挂载容器重新计算 offset
|
||||
*
|
||||
* layer 源码中使用窗口宽高计算位置,所以此
|
||||
* @param {string} offset 位置
|
||||
* @param {string | string[]} area 范围大小
|
||||
* @param {*} targetEl 挂载节点
|
||||
* @returns 包含抽屉位置信息的数组,[top, left]
|
||||
*/
|
||||
function calcOffset(offset, area, targetEl) {
|
||||
// https://gitee.com/layui/layui/blob/main/src/modules/layer.js#L560
|
||||
if (offset === undefined || offset === 'l' || offset === 't') {
|
||||
offset = 'lt';
|
||||
} else if (offset === 'r') {
|
||||
// https://gitee.com/layui/layui/blob/main/src/modules/layer.js#L554
|
||||
area = area instanceof Array ? area[0] : area;
|
||||
var left = /%$/.test(area)
|
||||
? targetEl.innerWidth() * (1 - window.parseFloat(area) / 100)
|
||||
: targetEl.innerWidth() - window.parseFloat(area);
|
||||
offset = ['0', left];
|
||||
} else if (offset === 'b') {
|
||||
area = area instanceof Array ? area[1] : area;
|
||||
var top = /%$/.test(area)
|
||||
? targetEl.innerHeight() * (1 - window.parseFloat(area) / 100)
|
||||
: targetEl.innerHeight() - window.parseFloat(area);
|
||||
offset = [top, '0'];
|
||||
}
|
||||
return offset;
|
||||
}
|
||||
|
||||
/**
|
||||
* 简易的切面
|
||||
* @param {Function} target 被通知的对象,原函数
|
||||
* @param {Function | undefined} [before] 前置通知
|
||||
* @param {Function | undefined} [after] 后置通知
|
||||
* @returns 代理函数
|
||||
*/
|
||||
function Aspect(target, before, after) {
|
||||
function proxyFunc() {
|
||||
if (before && typeof before === 'function') {
|
||||
before.apply(this, arguments);
|
||||
}
|
||||
target.apply(this, arguments);
|
||||
if (after && typeof after === 'function') {
|
||||
after.apply(this, arguments);
|
||||
}
|
||||
}
|
||||
return proxyFunc;
|
||||
}
|
||||
|
||||
exports(MOD_NAME, drawer);
|
||||
});
|
||||
142
web/static/lib/include.js
Executable file
142
web/static/lib/include.js
Executable file
@@ -0,0 +1,142 @@
|
||||
/**
|
||||
* @typedef {object} IncludeFile
|
||||
*
|
||||
* @prop {boolean} ok
|
||||
* @prop {number} status
|
||||
* @prop {string} html
|
||||
*/
|
||||
|
||||
/** @type {Map<string,IncludeFile | Promise<IncludeFile>>} */
|
||||
const includeFiles = new Map();
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {string} src
|
||||
* @param {'cors' | 'no-cors' | 'same-origin'} [mode='cors']
|
||||
*
|
||||
* @returns {Promise<IncludeFile>}
|
||||
*/
|
||||
export function requestInclude(src, mode = 'cors'){
|
||||
const prev = includeFiles.get(src);
|
||||
if (prev !== undefined) {
|
||||
return Promise.resolve(prev);
|
||||
}
|
||||
const fileDataPromise = fetch(src, { mode: mode }).then(async response => {
|
||||
const res = {
|
||||
ok: response.ok,
|
||||
status: response.status,
|
||||
html: await response.text()
|
||||
};
|
||||
includeFiles.set(src, res);
|
||||
return res;
|
||||
});
|
||||
includeFiles.set(src, fileDataPromise);
|
||||
return fileDataPromise;
|
||||
}
|
||||
|
||||
class HtmlImport extends HTMLElement {
|
||||
constructor () {
|
||||
super();
|
||||
}
|
||||
|
||||
static get observedAttributes () {
|
||||
return ['src', 'mode', 'allow-scripts'];
|
||||
}
|
||||
|
||||
get src() {
|
||||
return this.getAttribute('src') || '';
|
||||
}
|
||||
|
||||
set src(value) {
|
||||
this.setAttribute('src', value);
|
||||
}
|
||||
|
||||
get mode() {
|
||||
return this.getAttribute('mode') || 'cors';
|
||||
}
|
||||
|
||||
set mode(value) {
|
||||
this.setAttribute('mode', value);
|
||||
}
|
||||
|
||||
get allowScripts() {
|
||||
return this.hasAttribute('allow-scripts');
|
||||
}
|
||||
|
||||
set allowScripts(value) {
|
||||
this.toggleAttribute('allow-scripts', value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 执行 innerHTML 中的 <script></script>
|
||||
* @param {HTMLScriptElement} scripts
|
||||
*/
|
||||
async executeScript(scripts) {
|
||||
const execQueue = function (script) {
|
||||
const newScript = document.createElement('script');
|
||||
[...script.attributes].forEach(attr => newScript.setAttribute(attr.name, attr.value));
|
||||
newScript.textContent = script.textContent;
|
||||
script.parentNode && script.parentNode.replaceChild(newScript, script);
|
||||
return script.src ? new Promise((resolve) => {
|
||||
newScript.async = false;
|
||||
newScript.addEventListener('load', e => resolve(e));
|
||||
newScript.addEventListener('error', e => resolve(e));
|
||||
}) : Promise.resolve();
|
||||
};
|
||||
// 按 <script> 顺序执行,确保上下文关联
|
||||
for (const script of scripts) {
|
||||
await execQueue(script);
|
||||
// console.log(`${script.src||script} loaded`, Date.now());
|
||||
}
|
||||
}
|
||||
|
||||
async handleSrcChange() {
|
||||
try {
|
||||
const src = this.src;
|
||||
const file = await requestInclude(src, this.mode);
|
||||
|
||||
if (src !== this.src) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!file.ok) {
|
||||
this.emit('error', { detail: { status: file.status } });
|
||||
return;
|
||||
}
|
||||
|
||||
this.innerHTML = file.html;
|
||||
|
||||
if (this.allowScripts) {
|
||||
await this.executeScript(this.querySelectorAll('script'));
|
||||
}
|
||||
|
||||
this.emit('load');
|
||||
} catch {
|
||||
this.emit('error', { detail: { status: -1 } });
|
||||
}
|
||||
}
|
||||
|
||||
attributeChangedCallback (name) {
|
||||
if (name == 'src') {
|
||||
this.handleSrcChange();
|
||||
}
|
||||
}
|
||||
|
||||
emit(name, options) {
|
||||
const event = new CustomEvent(name, {
|
||||
bubbles: true,
|
||||
cancelable: false,
|
||||
composed: true,
|
||||
detail: {},
|
||||
...options
|
||||
});
|
||||
|
||||
this.dispatchEvent(event);
|
||||
|
||||
return event;
|
||||
}
|
||||
}
|
||||
|
||||
if (!customElements.get('wc-include')) {
|
||||
customElements.define('wc-include', HtmlImport);
|
||||
}
|
||||
11360
web/static/lib/less.js
Executable file
11360
web/static/lib/less.js
Executable file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user