diff --git a/src/main/resources/static/common/js/basic.js b/src/main/resources/static/common/js/basic.js index f8f932b..883dad2 100644 --- a/src/main/resources/static/common/js/basic.js +++ b/src/main/resources/static/common/js/basic.js @@ -1,4 +1,10 @@ class Fmt{ + static _fmt_token_key = "_fmt_token_value"; + /** + * 缓存配置 session:sessionStorage local:localStorage + * @type {string} + */ + static cache_config = "session"; constructor() { } /** @@ -11,5 +17,442 @@ class Fmt{ return window.location.protocol + '//' + window.location.host; } return window.location.protocol + '//' + window.location.host + '/' + webName; + }; + /** + * 缓存封装 + * @type {{ + * set: function(String, *): void, + * get: function(String): string, + * clear: function(): void, + * remove: function(String): void, + * key: function(Number): string + * }} + */ + static cache = { + /** + * 设置缓存 + * @param {String} key + * @param value + */ + set: (key, value) => { + const config = Fmt.cache_config; + const storage = config === 'session' ? + window.sessionStorage : window.localStorage; + storage.setItem(key, value); + }, + /** + * 获取缓存 + * @param {String} key + * @returns {string} + */ + get: (key) => { + const config = Fmt.cache_config; + const storage = config === 'session' ? + window.sessionStorage : window.localStorage; + return storage.getItem(key); + }, + /** + * 清除缓存 + * @param {String} key + */ + remove: (key) => { + const config = Fmt.cache_config; + const storage = config === 'session' ? + window.sessionStorage : window.localStorage; + storage.removeItem(key); + }, + /** + * 获取缓存key + * @param {Number} num + * @returns {string} + */ + key: (num) => { + const config = Fmt.cache_config; + const storage = config === 'session' ? + window.sessionStorage : window.localStorage; + return storage.key(num); + }, + /** + * 此方法会清空整个浏览器的LocalStorage缓存 + */ + clear: () => { + const config = Fmt.cache_config; + const storage = config === 'session' ? + window.sessionStorage : window.localStorage; + storage.clear(); + } + }; + /** + * 常用工具方法封装 + * @type {{isEmpty: function(*): boolean, typeOf: function(*): string}} + */ + static utils = { + /** + * 获得对象真正的类型 + * 可检测出Array,Object,Function,String,Number,Boolean,Date,Null,Undefined,HTMLDivElement(Dom节点都包含HTML)等 + * @param obj + * @returns {string} + */ + typeOf: (obj) => Object.prototype.toString.call(obj).slice(8, -1), + /** + * 定义判断为空函数,可判断function(){},0,'',[],{},undefined,null + * @param data + * @returns {boolean} + */ + isEmpty: (data) => { + let type = this.utils.typeOf(data); + if (!data) { + return true; + } else if (type === 'Function') { + return (data.toString().replace(/\s+/g, '').match(/{.*}/g)[0] === '{}'); + } else if (type === 'Array') { + return data.join('') === ''; + } else if (type === 'Object') { + return (Object.keys(data).length === 0) + } else { + return false; + } + }, + /** + * 该函数是深度拷贝函数,返回一个一模一样的对象 + * @param data + * @returns {*|Date|{}} + */ + clone: (data) => { + let cloneObj = (obj) => { + if (obj === null) { + return null; + } else if (typeof obj !== 'object') { + return obj; + } else if (obj.constructor === Date) { + return new Date(obj); + } else { + let newObj = {}; + for (let k in obj) { + if (obj[k] == null) { + newObj[k] = null; + } else if ((typeof obj[k]) == 'object' && !obj[k].nodeType) { + newObj[k] = axClone(obj[k]); + if (obj[k] instanceof Array) { + let newArray = []; + for (let i of obj[k]) { + newArray.push(axClone(i)); + } + newObj[k] = newArray; + } + } else { + newObj[k] = obj[k]; + } + } + return newObj; + } + } + if (Array.isArray(data)) { + return data.map(k => cloneObj(k)); + } else { + return cloneObj(data); + } + }, + /** + * 扩展,source相同属性的值去覆盖target, target如果没有这个属性就新增 + * @param {Object} target + * @param {Object} source + * @returns {Object} + */ + extend: (target, source) => { + if(this.utils.typeOf(target) !== 'Object') return null; + if(this.utils.typeOf(source) !== 'Object') return null; + for (const key in source) { + // 使用for in会遍历数组所有的可枚举属性,包括原型。 + if (Object.prototype.hasOwnProperty.call(source, key)) { + target[key] = source[key]; + } + } + return target; + }, + /** + * 判断选择器类型 + * @param str + * @returns {string|boolean} + */ + strType: (str) => { + if (typeof str !== 'string') { + console.error('Argument must be a string!') + return false; + } + str = str.trim(); + let isUpperCase = (letter) => { + if (letter >= 'A' && letter <= 'Z') { + return true; + } + }, + type = ''; + if (str) { + if (str.includes(' ')) { + type = 'level'; + } else { + if (str.startsWith('#')) { + type = 'id'; + } else if (str.startsWith('.')) { + type = 'class'; + } else if (str.startsWith('[') && str.endsWith(']')) { + type = 'name'; + } else if ([...str].every(i => isUpperCase(i))) { + type = 'node'; + } + } + } + return type; + }, + /** + * 将#id转成DOM + * @param elem + * @returns {*|boolean} + */ + idToDom: (elem) => { + let type = this.utils.typeOf(elem); + if (!elem) { + console.warn('Node selector is empty!'); + return false; + } else if (type === 'String') { + let trim = elem.trim(); + if (this.utils.strType(trim)) { + if (this.utils.strType(trim) === 'name' && !trim.includes('=')) { + trim = trim.slice(0, 1) + 'name=' + trim.slice(1); + } + return document.querySelector(trim); + } else { + console.warn('No node is found with this string!'); + return false; + } + } else if (type.includes('HTML')) { + return elem; + } else { + return false; + } + }, + /** + * 从表单控件取值,用于校验判断,适合的控件:checkbox、radio、select-multiple和file + * @param name + * @param type + * @param format + * @param form + * @param separator + * @returns {*[]|boolean} + */ + multiValues: (name, type = 'checkbox', format = 'array', form, separator = ',') => { + let inputs, + values = [], + parent = form ? this.utils.idToDom(form) : ''; + if (this.utils.strType(name) === 'NodeList' || this.utils.strType(name) === 'Array') { + if (type.includes('select') || type === 'file') { + console.warn('Can not be an node array in this type!'); + return false; + } + inputs = name; + } else if (typeof name == 'object' || (this.utils.strType(name) && this.utils.strType(name) !== 'name')) { + let dom = this.utils.idToDom(name); + if (type.includes('select') || name.nodeName === 'SELECT') { + type = 'select'; + inputs = dom.options; + } else if (type === 'file' || dom.type === 'file') { + type = 'file'; + inputs = [dom]; + } else { + inputs = [...dom.querySelectorAll('input')]; + } + } else { + let selector = ''; + if (this.utils.strType(name) === 'name') { + selector = name.replace('[', '[name='); + } else { + selector = `[name=${name}]`; + } + if (type.includes('select')) { + inputs = parent ? parent.querySelector(selector).options : document.querySelector(selector).options; + } else if (type === 'file') { + inputs = parent ? [parent.querySelector(selector)] : [document.querySelector(selector)]; + } else { + inputs = parent ? [...parent.querySelectorAll(selector)] : [...document.querySelectorAll(selector)]; + } + } + for (let i = 0; i < inputs.length; i++) { + let condition, + item = inputs[i]; + if (type.includes('select')) { + condition = item.selected; + } else { + condition = item.checked; + } + if (type === 'file') { + values = [...item.files]; + } else { + if (condition) { + values.push(item.value); + } + } + } + if (format === 'string') { + values = values.join(separator) + } + return values; + }, + /** + * 表单序列化,支持将表单值序列化为以“&”连接的字符串,和对象数组[{name:'',value:''},...] + * @param element + * @param type + * @param separator + * @returns {{}} + */ + serialize: (element, type = "string", separator = ',') => { + let inputs, + items = [], + output; + if (Array.isArray(element)) { + inputs = element; + } else { + inputs = [...this.utils.idToDom(element).querySelectorAll('[name]')].filter(i => ['INPUT', 'SELECT', 'TEXTAREA'].includes(i.nodeName) && i.name && !i.name.includes('_ax_alt')); + } + inputs.forEach(i => { + let have = items.find(k => k.name === i.name); + if (have) { + if (i.type === 'checkbox' || i.type === 'radio') { + have.dom.push(i); + } + } else { + items.push({ name: i.name, type: i.type, dom: [i] }) + } + }); + items.forEach(i => { + if (i.type === 'file') { + i.value = this.utils.multiValues(i.dom[0], 'file', 'array'); + } else if (i.type === 'checkbox') { + i.value = this.utils.multiValues(i.dom, 'checkbox', 'string', '', separator); + } else if (i.type === 'radio') { + i.value = this.utils.multiValues(i.dom, 'radio', 'string'); + } else if (i.type.includes('select')) { + i.value = this.utils.multiValues(i.dom[0], 'select', 'string', '', separator); + } else { + i.value = i.dom[0].value; + } + }); + if (type === 'string') { + let str = ''; + items.forEach(k => { + str += '&' + k.name + '=' + k.value; + }); + output = str; + } else if (type === 'json') { + let obj = {} + items.forEach(k => { + obj[k.name] = k.value; + }); + output = obj; + } else if (type === 'array') { + items.forEach(k => { + delete k.type; + delete k.dom; + }) + output = items; + } + return output; + } + }; + /** + * XMLHttpRequest封装 + * @param {{url, data, method, responseType, contentType, timeout, headers}} options + * @returns {Promise} + */ + #axiosPrivate(options){ + let data = options.data; + let url = options.url; + let method = options.method || 'GET'; + let responseType = options.responseType || 'json'; + let contentType = options.contentType || 'application/x-www-form-urlencoded'; + let timeout = options.timeout || 0; + /** + * 序列化参数 + * @param data + * @returns {string} + */ + let serialize = (data) => { + if (Fmt.utils.isEmpty(data)) return ''; + let pairs = []; + for (let name in data) { + if (!data.hasOwnProperty(name)) continue; + if (Fmt.utils.typeOf(data[name]) === 'Function') continue; + let value = data[name].toString(); + name = encodeURIComponent(name); + value = encodeURIComponent(value); + pairs.push(name + '=' + value); + } + return pairs.join('&'); + } + /** + * 创建xhr请求对象 + * @returns {XMLHttpRequest || ActiveXObject} + */ + let xhrObj = () => { + let xhr = ''; + if (window.XMLHttpRequest) { + xhr = new XMLHttpRequest(); + }else { + xhr = new ActiveXObject("Microsoft.XMLHTTP"); + } + return xhr; + } + return new Promise((resolve, reject) => { // promise封装 + let xhr = xhrObj(); + let params = null; + xhr.onreadystatechange = handler; + if (method.toUpperCase() === 'GET') { + url = url + '?now=' + new Date().getTime() + '&' + serialize(data); + } else if (method.toUpperCase() === 'POST') { + params = serialize(data); + } + xhr.responseType = responseType; + xhr.timeout = timeout; + xhr.open(method, url); + xhr.setRequestHeader('Content-Type', contentType); + xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest'); + xhr.setRequestHeader("Access-Control-Allow-Origin","*"); + if(!Fmt.utils.isEmpty(options.headers) && Fmt.utils.typeOf(options.headers) === 'Object'){ + for (let key in options.headers) { + xhr.setRequestHeader(key, options.headers[key]); + } + } + xhr.send(params); + + function handler() { + if (this.readyState !== 4) { + return; + } + if (this.status === 200) { + resolve(this.response);//正确回调 + } else { + reject(new Error(this.statusText));//错误回调 + } + } + }) + } + /** + * XMLHttpRequest封装 + *
+     *     使用方式:
+     *     const options = {
+     *         url: Fmt.ctx() + '/salt',
+     *         data: {username: username},
+     *         method: 'post'
+     *     };
+     *     Fmt.axios(options).then((res) => {
+     *         console.warn(res)
+     *     }).catch((err) => {console.warn(err)});
+     * 
+ * @param {{url, data, method, responseType, contentType, timeout, headers}} options + * @returns {Promise} + */ + static axios(options){ + let fmt = new Fmt(); + return fmt.#axiosPrivate(options); } } \ No newline at end of file diff --git a/src/main/resources/templates/download.html b/src/main/resources/templates/download.html index 15e030f..f53b755 100644 --- a/src/main/resources/templates/download.html +++ b/src/main/resources/templates/download.html @@ -88,20 +88,21 @@