使用Promise封装XMLHttpRequest做异步请求
This commit is contained in:
@@ -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<unknown>}
|
||||
*/
|
||||
#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封装
|
||||
* <pre>
|
||||
* 使用方式:
|
||||
* const options = {
|
||||
* url: Fmt.ctx() + '/salt',
|
||||
* data: {username: username},
|
||||
* method: 'post'
|
||||
* };
|
||||
* Fmt.axios(options).then((res) => {
|
||||
* console.warn(res)
|
||||
* }).catch((err) => {console.warn(err)});
|
||||
* </pre>
|
||||
* @param {{url, data, method, responseType, contentType, timeout, headers}} options
|
||||
* @returns {Promise<unknown>}
|
||||
*/
|
||||
static axios(options){
|
||||
let fmt = new Fmt();
|
||||
return fmt.#axiosPrivate(options);
|
||||
}
|
||||
}
|
||||
@@ -88,20 +88,21 @@
|
||||
<script th:src="@{/axui-v2.1.1/js/ax.js}" type="text/javascript" charset="utf-8"></script>
|
||||
<script th:src="@{/common/js/basic.js}" type="text/javascript" charset="utf-8"></script>
|
||||
<script type="text/javascript" th:inline="javascript" charset="utf-8">
|
||||
let fileDel = (obj) => {
|
||||
const fileDel = (obj) => {
|
||||
const path = obj.getAttribute("value");
|
||||
let formData = {path: path};
|
||||
axAjax({
|
||||
url: '/fmt/download/delete',
|
||||
data: formData,
|
||||
type: 'post',
|
||||
success: function (result) {
|
||||
window.location.href = '/fmt/download/index';
|
||||
}
|
||||
const options = {
|
||||
url: Fmt.ctx() + '/download/delete',
|
||||
data: {path: path},
|
||||
method: 'post'
|
||||
};
|
||||
Fmt.axios(options).then((result) => {
|
||||
window.location.href = Fmt.ctx() + '/download/index';
|
||||
}).catch((err) => {
|
||||
new axMessage({content: err, result: 'error', iconShow: true}).show();
|
||||
});
|
||||
}
|
||||
|
||||
let fileDown = (obj) => {
|
||||
const fileDown = (obj) => {
|
||||
const path = obj.getAttribute("value");
|
||||
window.open(Fmt.ctx() + '/download/execute?path=' + encodeURIComponent(path) + '&temp=' + false);
|
||||
};
|
||||
@@ -109,7 +110,7 @@
|
||||
window.onload = () => {
|
||||
document.getElementById("search").onclick = () => {
|
||||
const keyboard = document.getElementById("keyboard").value;
|
||||
window.location.href = '/fmt/download/index?keyboard=' + keyboard;
|
||||
window.location.href = Fmt.ctx() + '/download/index?keyboard=' + keyboard;
|
||||
}
|
||||
|
||||
document.getElementById("zipDownBtn").onclick = () => {
|
||||
@@ -125,18 +126,19 @@
|
||||
new axMessage({content: '请选择要下载的文件',result: 'warning',iconShow: true}).show();
|
||||
return false;
|
||||
}
|
||||
let formData = {filenames: filenames.join(",")};
|
||||
let message = new axMessage({content: '',result: 'success',iconShow: true});
|
||||
message.update({content: '正在根据所选文件创建压缩包......', result: 'success'}).show();
|
||||
axAjax({
|
||||
url: '/fmt/download/packZip',
|
||||
data: formData,
|
||||
type: 'post',
|
||||
success: function (result) {
|
||||
const data = result.content;
|
||||
message.update({content: '压缩包' + data.filename + "创建完成,准备下载文件......", result: 'success'}).show();
|
||||
window.open(Fmt.ctx() + '/download/execute?path=' + encodeURIComponent(data.path) + '&temp=' + true);
|
||||
}
|
||||
const options = {
|
||||
url: Fmt.ctx() + '/download/packZip',
|
||||
data: {filenames: filenames.join(",")},
|
||||
method: 'post'
|
||||
};
|
||||
Fmt.axios(options).then((result) => {
|
||||
message.update({content: '压缩包' + result.filename + "创建完成,准备下载文件......", result: 'success'}).show();
|
||||
window.open(Fmt.ctx() + '/download/execute?path=' + encodeURIComponent(result.path) + '&temp=' + true);
|
||||
}).catch((err) => {
|
||||
console.log(err);
|
||||
new axMessage({content: err, result: 'error', iconShow: true}).show();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -153,14 +155,15 @@
|
||||
new axMessage({content: '请选择要删除的文件',result: 'warning',iconShow: true}).show();
|
||||
return false;
|
||||
}
|
||||
let formData = {filenames: filenames.join(",")};
|
||||
axAjax({
|
||||
url: '/fmt/download/batchDel',
|
||||
data: formData,
|
||||
type: 'post',
|
||||
success: function (result) {
|
||||
window.location.href = '/fmt/download/index';
|
||||
}
|
||||
const options = {
|
||||
url: Fmt.ctx() + '/download/batchDel',
|
||||
data: {filenames: filenames.join(",")},
|
||||
method: 'post'
|
||||
};
|
||||
Fmt.axios(options).then((result) => {
|
||||
window.location.href = Fmt.ctx() + '/download/index';
|
||||
}).catch((err) => {
|
||||
new axMessage({content: err, result: 'error', iconShow: true}).show();
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
> v1.0.0
|
||||
```
|
||||
使用SpringBoot3.1.1 + Jdk17搭建项目
|
||||
集成AXUIv2.0.14实现文件上传、文件下载
|
||||
集成AxUIv2.0.14实现文件上传、文件下载
|
||||
集成RocksDBv8.3.2,使用RocksDB做嵌入式缓存中间件
|
||||
```
|
||||
> v1.1.0
|
||||
@@ -11,5 +11,6 @@
|
||||
```
|
||||
> v1.2.0
|
||||
```
|
||||
AXUIv2.0.14升级到AXUIv2.1.1
|
||||
AxUI从v2.0.14升级到v2.1.1
|
||||
使用Promise封装XMLHttpRequest做异步请求
|
||||
```
|
||||
Reference in New Issue
Block a user