mirror of
https://github.com/xinliangnote/go-gin-api.git
synced 2024-04-21 12:31:46 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
77e8d58298 | ||
|
|
f47f57ef3c | ||
|
|
f17f6a74d2 | ||
|
|
382c7d3c8f | ||
|
|
b382919cb5 | ||
|
|
22bfb9ad10 | ||
|
|
4254184098 | ||
|
|
bb09c70a60 | ||
|
|
65873cbfaa | ||
|
|
6af343d0b1 | ||
|
|
62367a041a | ||
|
|
1b013e2023 | ||
|
|
eba8929b4d | ||
|
|
44ca2698cd | ||
|
|
2c5683cd14 | ||
|
|
8ca47f743d | ||
|
|
a3607106d3 | ||
|
|
6d9a07f5dd | ||
|
|
a6947b59ac | ||
|
|
ec3c9f0ccf | ||
|
|
8fbcb5d717 | ||
|
|
6f71852686 | ||
|
|
9cace3337b | ||
|
|
0dbd4b0935 | ||
|
|
1f85d8df61 | ||
|
|
3e1ef9c658 | ||
|
|
8a6a34348f | ||
|
|
66a5e29c9c | ||
|
|
84cc0c9cbc |
+16
-5
@@ -1,5 +1,16 @@
|
||||
/.vscode
|
||||
.vscode
|
||||
/.idea
|
||||
.idea
|
||||
mfmt
|
||||
# Binaries for programs and plugins
|
||||
*.exe
|
||||
*.exe~
|
||||
*.dll
|
||||
*.so
|
||||
*.dylib
|
||||
*.idea
|
||||
|
||||
# Test binary, built with `go test -c`
|
||||
*.test
|
||||
|
||||
# Output of the go coverage tool, specifically when used with LiteIDE
|
||||
*.out
|
||||
|
||||
# Dependency directories (remove the comment below to include it)
|
||||
# vendor/
|
||||
|
||||
+36
@@ -0,0 +1,36 @@
|
||||
# FROM 基于 golang:1.15-alpine
|
||||
FROM golang:1.15-alpine AS builder
|
||||
|
||||
# ENV 设置环境变量
|
||||
ENV GOPATH=/opt/repo
|
||||
ENV GO111MODULE=on
|
||||
ENV GOPROXY=https://goproxy.io,direct
|
||||
|
||||
# ADD 源路径 目标路径
|
||||
COPY . $GOPATH/src/github.com/xinliangnote/go-gin-api
|
||||
|
||||
# RUN 执行 go build .
|
||||
RUN cd $GOPATH/src/github.com/xinliangnote/go-gin-api && go build .
|
||||
|
||||
# FROM 基于 alpine:latest
|
||||
FROM alpine:latest
|
||||
|
||||
# RUN 设置代理镜像
|
||||
RUN echo -e http://mirrors.ustc.edu.cn/alpine/v3.13/main/ > /etc/apk/repositories
|
||||
|
||||
# RUN 设置 Asia/Shanghai 时区
|
||||
RUN apk --no-cache add tzdata && \
|
||||
ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
|
||||
echo "Asia/Shanghai" > /etc/timezone
|
||||
|
||||
# COPY 源路径 目标路径 从镜像中 COPY
|
||||
COPY --from=builder /opt/repo/src/github.com/xinliangnote/go-gin-api /opt
|
||||
|
||||
# EXPOSE 设置端口映射
|
||||
EXPOSE 9999/tcp
|
||||
|
||||
# WORKDIR 设置工作目录
|
||||
WORKDIR /opt
|
||||
|
||||
# CMD 设置启动命令
|
||||
CMD ["./go-gin-api", "-env", "fat"]
|
||||
@@ -21,32 +21,20 @@
|
||||
1. 支持 [gorm](https://gorm.io/gorm) 数据库组件
|
||||
1. 支持 [go-redis](https://github.com/go-redis/redis/v7) 组件
|
||||
1. 支持 RESTful API 返回值规范
|
||||
1. 支持 gormgen、handlergen 代码生成工具
|
||||
1. 支持 生成数据表 CURD、控制器方法 等代码生成器
|
||||
1. 支持 web 界面,使用的 [Light Year Admin 模板](https://gitee.com/yinqi/Light-Year-Admin-Using-Iframe)
|
||||
|
||||
|
||||
## 文档索引
|
||||
|
||||
go-gin-api 文档由以下几个主要部分组成:
|
||||
|
||||
- 准备工作
|
||||
- 快速开始
|
||||
- 目录接口
|
||||
- 核心封装
|
||||
- 组件指南
|
||||
- 工具包
|
||||
|
||||
**地址:[https://www.yuque.com/xinliangnote/go-gin-api/ngc3x5](https://www.yuque.com/xinliangnote/go-gin-api/ngc3x5)**
|
||||
- 中文文档:[go-gin-api - 语雀](https://www.yuque.com/xinliangnote/go-gin-api/ngc3x5)
|
||||
- English Document:[en.md](https://github.com/xinliangnote/go-gin-api/blob/master/en.md)
|
||||
|
||||
## 其他
|
||||
|
||||
查看 Jaeger 链路追踪代码,请查看 [v1.0版](https://github.com/xinliangnote/go-gin-api/releases/tag/v1.0),文档点这里 [jaeger.md](https://github.com/xinliangnote/go-gin-api/blob/master/docs/jaeger.md) 。
|
||||
|
||||
## Special Thanks
|
||||
## 联系作者
|
||||
|
||||
[@koketama](https://github.com/koketama)
|
||||
|
||||
## Learning together
|
||||
|
||||

|
||||

|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 4.1 MiB |
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
||||
!function(r,e){"object"==typeof exports?module.exports=exports=e(require("./core")):"function"==typeof define&&define.amd?define(["./core"],e):e(r.CryptoJS)}(this,function(r){var s;return s=r.lib.WordArray,r.enc.Base64={stringify:function(r){var e=r.words,t=r.sigBytes,a=this._map;r.clamp();for(var n=[],o=0;o<t;o+=3)for(var i=(e[o>>>2]>>>24-o%4*8&255)<<16|(e[o+1>>>2]>>>24-(o+1)%4*8&255)<<8|e[o+2>>>2]>>>24-(o+2)%4*8&255,f=0;f<4&&o+.75*f<t;f++)n.push(a.charAt(i>>>6*(3-f)&63));var c=a.charAt(64);if(c)for(;n.length%4;)n.push(c);return n.join("")},parse:function(r){var e=r.length,t=this._map,a=this._reverseMap;if(!a){a=this._reverseMap=[];for(var n=0;n<t.length;n++)a[t.charCodeAt(n)]=n}var o=t.charAt(64);if(o){var i=r.indexOf(o);-1!==i&&(e=i)}return function(r,e,t){for(var a=[],n=0,o=0;o<e;o++)if(o%4){var i=t[r.charCodeAt(o-1)]<<o%4*2,f=t[r.charCodeAt(o)]>>>6-o%4*2,c=i|f;a[n>>>2]|=c<<24-n%4*8,n++}return s.create(a,n)}(r,e,a)},_map:"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="},r.enc.Base64});
|
||||
@@ -0,0 +1,18 @@
|
||||
;(function (root, factory, undef) {
|
||||
if (typeof exports === "object") {
|
||||
// CommonJS
|
||||
module.exports = exports = factory(require("./core"), require("./sha256"), require("./hmac"));
|
||||
}
|
||||
else if (typeof define === "function" && define.amd) {
|
||||
// AMD
|
||||
define(["./core", "./sha256", "./hmac"], factory);
|
||||
}
|
||||
else {
|
||||
// Global (browser)
|
||||
factory(root.CryptoJS);
|
||||
}
|
||||
}(this, function (CryptoJS) {
|
||||
|
||||
return CryptoJS.HmacSHA256;
|
||||
|
||||
}));
|
||||
@@ -0,0 +1,78 @@
|
||||
// original:https://locutus.io/php/array/ksort/
|
||||
|
||||
function ksort(inputArr, sort_flags) {
|
||||
var tmp_arr = {},
|
||||
keys = [],
|
||||
sorter, i, k, that = this,
|
||||
strictForIn = false,
|
||||
populateArr = {};
|
||||
|
||||
switch (sort_flags) {
|
||||
case 'SORT_STRING':
|
||||
// compare items as strings
|
||||
sorter = function (a, b) {
|
||||
return that.strnatcmp(a, b);
|
||||
};
|
||||
break;
|
||||
case 'SORT_LOCALE_STRING':
|
||||
// compare items as strings, original by the current locale (set with i18n_loc_set_default() as of PHP6)
|
||||
var loc = this.i18n_loc_get_default();
|
||||
sorter = this.php_js.i18nLocales[loc].sorting;
|
||||
break;
|
||||
case 'SORT_NUMERIC':
|
||||
// compare items numerically
|
||||
sorter = function (a, b) {
|
||||
return ((a + 0) - (b + 0));
|
||||
};
|
||||
break;
|
||||
// case 'SORT_REGULAR': // compare items normally (don't change types)
|
||||
default:
|
||||
sorter = function (a, b) {
|
||||
var aFloat = parseFloat(a),
|
||||
bFloat = parseFloat(b),
|
||||
aNumeric = aFloat + '' === a,
|
||||
bNumeric = bFloat + '' === b;
|
||||
if (aNumeric && bNumeric) {
|
||||
return aFloat > bFloat ? 1 : aFloat < bFloat ? -1 : 0;
|
||||
} else if (aNumeric && !bNumeric) {
|
||||
return 1;
|
||||
} else if (!aNumeric && bNumeric) {
|
||||
return -1;
|
||||
}
|
||||
return a > b ? 1 : a < b ? -1 : 0;
|
||||
};
|
||||
break;
|
||||
}
|
||||
|
||||
// Make a list of key names
|
||||
for (k in inputArr) {
|
||||
if (inputArr.hasOwnProperty(k)) {
|
||||
keys.push(k);
|
||||
}
|
||||
}
|
||||
keys.sort(sorter);
|
||||
|
||||
// BEGIN REDUNDANT
|
||||
this.php_js = this.php_js || {};
|
||||
this.php_js.ini = this.php_js.ini || {};
|
||||
// END REDUNDANT
|
||||
strictForIn = this.php_js.ini['phpjs.strictForIn'] && this.php_js.ini['phpjs.strictForIn'].local_value && this.php_js
|
||||
.ini['phpjs.strictForIn'].local_value !== 'off';
|
||||
populateArr = strictForIn ? inputArr : populateArr;
|
||||
|
||||
// Rebuild array with sorted key names
|
||||
for (i = 0; i < keys.length; i++) {
|
||||
k = keys[i];
|
||||
tmp_arr[k] = inputArr[k];
|
||||
if (strictForIn) {
|
||||
delete inputArr[k];
|
||||
}
|
||||
}
|
||||
for (i in tmp_arr) {
|
||||
if (tmp_arr.hasOwnProperty(i)) {
|
||||
populateArr[i] = tmp_arr[i];
|
||||
}
|
||||
}
|
||||
|
||||
return strictForIn || populateArr;
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
!function(n){"use strict";function d(n,t){var r=(65535&n)+(65535&t);return(n>>16)+(t>>16)+(r>>16)<<16|65535&r}function f(n,t,r,e,o,u){return d((c=d(d(t,n),d(e,u)))<<(f=o)|c>>>32-f,r);var c,f}function l(n,t,r,e,o,u,c){return f(t&r|~t&e,n,t,o,u,c)}function v(n,t,r,e,o,u,c){return f(t&e|r&~e,n,t,o,u,c)}function g(n,t,r,e,o,u,c){return f(t^r^e,n,t,o,u,c)}function m(n,t,r,e,o,u,c){return f(r^(t|~e),n,t,o,u,c)}function i(n,t){var r,e,o,u;n[t>>5]|=128<<t%32,n[14+(t+64>>>9<<4)]=t;for(var c=1732584193,f=-271733879,i=-1732584194,a=271733878,h=0;h<n.length;h+=16)c=l(r=c,e=f,o=i,u=a,n[h],7,-680876936),a=l(a,c,f,i,n[h+1],12,-389564586),i=l(i,a,c,f,n[h+2],17,606105819),f=l(f,i,a,c,n[h+3],22,-1044525330),c=l(c,f,i,a,n[h+4],7,-176418897),a=l(a,c,f,i,n[h+5],12,1200080426),i=l(i,a,c,f,n[h+6],17,-1473231341),f=l(f,i,a,c,n[h+7],22,-45705983),c=l(c,f,i,a,n[h+8],7,1770035416),a=l(a,c,f,i,n[h+9],12,-1958414417),i=l(i,a,c,f,n[h+10],17,-42063),f=l(f,i,a,c,n[h+11],22,-1990404162),c=l(c,f,i,a,n[h+12],7,1804603682),a=l(a,c,f,i,n[h+13],12,-40341101),i=l(i,a,c,f,n[h+14],17,-1502002290),c=v(c,f=l(f,i,a,c,n[h+15],22,1236535329),i,a,n[h+1],5,-165796510),a=v(a,c,f,i,n[h+6],9,-1069501632),i=v(i,a,c,f,n[h+11],14,643717713),f=v(f,i,a,c,n[h],20,-373897302),c=v(c,f,i,a,n[h+5],5,-701558691),a=v(a,c,f,i,n[h+10],9,38016083),i=v(i,a,c,f,n[h+15],14,-660478335),f=v(f,i,a,c,n[h+4],20,-405537848),c=v(c,f,i,a,n[h+9],5,568446438),a=v(a,c,f,i,n[h+14],9,-1019803690),i=v(i,a,c,f,n[h+3],14,-187363961),f=v(f,i,a,c,n[h+8],20,1163531501),c=v(c,f,i,a,n[h+13],5,-1444681467),a=v(a,c,f,i,n[h+2],9,-51403784),i=v(i,a,c,f,n[h+7],14,1735328473),c=g(c,f=v(f,i,a,c,n[h+12],20,-1926607734),i,a,n[h+5],4,-378558),a=g(a,c,f,i,n[h+8],11,-2022574463),i=g(i,a,c,f,n[h+11],16,1839030562),f=g(f,i,a,c,n[h+14],23,-35309556),c=g(c,f,i,a,n[h+1],4,-1530992060),a=g(a,c,f,i,n[h+4],11,1272893353),i=g(i,a,c,f,n[h+7],16,-155497632),f=g(f,i,a,c,n[h+10],23,-1094730640),c=g(c,f,i,a,n[h+13],4,681279174),a=g(a,c,f,i,n[h],11,-358537222),i=g(i,a,c,f,n[h+3],16,-722521979),f=g(f,i,a,c,n[h+6],23,76029189),c=g(c,f,i,a,n[h+9],4,-640364487),a=g(a,c,f,i,n[h+12],11,-421815835),i=g(i,a,c,f,n[h+15],16,530742520),c=m(c,f=g(f,i,a,c,n[h+2],23,-995338651),i,a,n[h],6,-198630844),a=m(a,c,f,i,n[h+7],10,1126891415),i=m(i,a,c,f,n[h+14],15,-1416354905),f=m(f,i,a,c,n[h+5],21,-57434055),c=m(c,f,i,a,n[h+12],6,1700485571),a=m(a,c,f,i,n[h+3],10,-1894986606),i=m(i,a,c,f,n[h+10],15,-1051523),f=m(f,i,a,c,n[h+1],21,-2054922799),c=m(c,f,i,a,n[h+8],6,1873313359),a=m(a,c,f,i,n[h+15],10,-30611744),i=m(i,a,c,f,n[h+6],15,-1560198380),f=m(f,i,a,c,n[h+13],21,1309151649),c=m(c,f,i,a,n[h+4],6,-145523070),a=m(a,c,f,i,n[h+11],10,-1120210379),i=m(i,a,c,f,n[h+2],15,718787259),f=m(f,i,a,c,n[h+9],21,-343485551),c=d(c,r),f=d(f,e),i=d(i,o),a=d(a,u);return[c,f,i,a]}function a(n){for(var t="",r=32*n.length,e=0;e<r;e+=8)t+=String.fromCharCode(n[e>>5]>>>e%32&255);return t}function h(n){var t=[];for(t[(n.length>>2)-1]=void 0,e=0;e<t.length;e+=1)t[e]=0;for(var r=8*n.length,e=0;e<r;e+=8)t[e>>5]|=(255&n.charCodeAt(e/8))<<e%32;return t}function e(n){for(var t,r="0123456789abcdef",e="",o=0;o<n.length;o+=1)t=n.charCodeAt(o),e+=r.charAt(t>>>4&15)+r.charAt(15&t);return e}function r(n){return unescape(encodeURIComponent(n))}function o(n){return a(i(h(t=r(n)),8*t.length));var t}function u(n,t){return function(n,t){var r,e,o=h(n),u=[],c=[];for(u[15]=c[15]=void 0,16<o.length&&(o=i(o,8*n.length)),r=0;r<16;r+=1)u[r]=909522486^o[r],c[r]=1549556828^o[r];return e=i(u.concat(h(t)),512+8*t.length),a(i(c.concat(e),640))}(r(n),r(t))}function t(n,t,r){return t?r?u(t,n):e(u(t,n)):r?o(n):e(o(n))}"function"==typeof define&&define.amd?define(function(){return t}):"object"==typeof module&&module.exports?module.exports=t:n.md5=t}(this);
|
||||
//# sourceMappingURL=md5.min.js.map
|
||||
@@ -0,0 +1,589 @@
|
||||
/* ==========================================================
|
||||
* bootstrap-maxlength.js v1.9.0
|
||||
*
|
||||
* Copyright (c) 2013-2020 Maurizio Napoleoni;
|
||||
*
|
||||
* Licensed under the terms of the MIT license.
|
||||
* See: https://github.com/mimo84/bootstrap-maxlength/blob/master/LICENSE
|
||||
* ========================================================== */
|
||||
/*global jQuery*/
|
||||
|
||||
(function ($) {
|
||||
'use strict';
|
||||
/**
|
||||
* We need an event when the elements are destroyed
|
||||
* because if an input is removed, we have to remove the
|
||||
* maxlength object associated (if any).
|
||||
* From:
|
||||
* http://stackoverflow.com/questions/2200494/jquery-trigger-event-when-an-element-is-removed-from-the-dom
|
||||
*/
|
||||
if (!$.event.special.destroyed) {
|
||||
$.event.special.destroyed = {
|
||||
remove: function (o) {
|
||||
if (o.handler) {
|
||||
o.handler();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
$.fn.extend({
|
||||
maxlength: function (options, callback) {
|
||||
var documentBody = $('body'),
|
||||
defaults = {
|
||||
showOnReady: false, // true to always show when indicator is ready
|
||||
alwaysShow: true, // if true the indicator it's always shown.
|
||||
threshold: 0, // Represents how many chars left are needed to show up the counter
|
||||
warningClass: 'small form-text text-muted',
|
||||
limitReachedClass: 'small form-text text-danger',
|
||||
separator: ' / ',
|
||||
preText: '',
|
||||
postText: '',
|
||||
showMaxLength: true,
|
||||
placement: 'bottom-right-inside',
|
||||
message: null, // an alternative way to provide the message text
|
||||
showCharsTyped: true, // show the number of characters typed and not the number of characters remaining
|
||||
validate: false, // if the browser doesn't support the maxlength attribute, attempt to type more than the indicated chars, will be prevented.
|
||||
utf8: false, // counts using bytesize rather than length. eg: '£' is counted as 2 characters.
|
||||
appendToParent: false, // append the indicator to the input field's parent instead of body
|
||||
twoCharLinebreak: true, // count linebreak as 2 characters to match IE/Chrome textarea validation. As well as DB storage.
|
||||
customMaxAttribute: null, // null = use maxlength attribute and browser functionality, string = use specified attribute instead.
|
||||
allowOverMax: false, // Form submit validation is handled on your own. when maxlength has been exceeded 'overmax' class added to element
|
||||
zIndex: 1099
|
||||
};
|
||||
|
||||
if ($.isFunction(options) && !callback) {
|
||||
callback = options;
|
||||
options = {};
|
||||
}
|
||||
options = $.extend(defaults, options);
|
||||
|
||||
|
||||
/**
|
||||
* Return the byte count of the specified character in UTF8 encoding.
|
||||
* Note: This won't cover UTF-8 characters that are 4 bytes long.
|
||||
*
|
||||
* @param input
|
||||
* @return {number}
|
||||
*/
|
||||
function utf8CharByteCount(character) {
|
||||
var c = character.charCodeAt();
|
||||
// Not c then 0, else c < 128 then 1, else c < 2048 then 2, else 3
|
||||
return !c ? 0 : c < 128 ? 1 : c < 2048 ? 2 : 3;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the length of the specified input in UTF8 encoding.
|
||||
*
|
||||
* @param input
|
||||
* @return {number}
|
||||
*/
|
||||
function utf8Length(string) {
|
||||
return string.split("")
|
||||
.map(utf8CharByteCount)
|
||||
// Prevent reduce from throwing an error if the string is empty.
|
||||
.concat(0)
|
||||
.reduce(function (sum, val) {
|
||||
return sum + val;
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the length of the specified input.
|
||||
*
|
||||
* @param input
|
||||
* @return {number}
|
||||
*/
|
||||
function inputLength(input) {
|
||||
var text = input.val();
|
||||
|
||||
if (options.twoCharLinebreak) {
|
||||
// Count all line breaks as 2 characters
|
||||
text = text.replace(/\r(?!\n)|\n(?!\r)/g, '\r\n');
|
||||
} else {
|
||||
// Remove all double-character (\r\n) linebreaks, so they're counted only once.
|
||||
text = text.replace(/(?:\r\n|\r|\n)/g, '\n');
|
||||
}
|
||||
|
||||
var currentLength = 0;
|
||||
|
||||
if (options.utf8) {
|
||||
currentLength = utf8Length(text);
|
||||
} else {
|
||||
currentLength = text.length;
|
||||
}
|
||||
|
||||
// Remove "C:\fakepath\" from counter when using file input
|
||||
// Fix https://github.com/mimo84/bootstrap-maxlength/issues/146
|
||||
if (input.prop("type") === "file" && input.val() !== "") {
|
||||
currentLength -= 12;
|
||||
}
|
||||
|
||||
return currentLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Truncate the text of the specified input.
|
||||
*
|
||||
* @param input
|
||||
* @param limit
|
||||
*/
|
||||
function truncateChars(input, maxlength) {
|
||||
var text = input.val();
|
||||
|
||||
if (options.twoCharLinebreak) {
|
||||
text = text.replace(/\r(?!\n)|\n(?!\r)/g, '\r\n');
|
||||
|
||||
if (text[text.length - 1] === '\n') {
|
||||
maxlength -= text.length % 2;
|
||||
}
|
||||
}
|
||||
|
||||
if (options.utf8) {
|
||||
var indexedSize = text.split("").map(utf8CharByteCount);
|
||||
for (
|
||||
var removedBytes = 0,
|
||||
bytesPastMax = utf8Length(text) - maxlength; removedBytes < bytesPastMax; removedBytes += indexedSize.pop()
|
||||
);
|
||||
maxlength -= (maxlength - indexedSize.length);
|
||||
}
|
||||
|
||||
input.val(text.substr(0, maxlength));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if the indicator should be showing up.
|
||||
*
|
||||
* @param input
|
||||
* @param threshold
|
||||
* @param maxlength
|
||||
* @return {number}
|
||||
*/
|
||||
function charsLeftThreshold(input, threshold, maxlength) {
|
||||
var output = true;
|
||||
if (!options.alwaysShow && (maxlength - inputLength(input) > threshold)) {
|
||||
output = false;
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns how many chars are left to complete the fill up of the form.
|
||||
*
|
||||
* @param input
|
||||
* @param maxlength
|
||||
* @return {number}
|
||||
*/
|
||||
function remainingChars(input, maxlength) {
|
||||
var length = maxlength - inputLength(input);
|
||||
return length;
|
||||
}
|
||||
|
||||
/**
|
||||
* When called displays the indicator.
|
||||
*
|
||||
* @param indicator
|
||||
*/
|
||||
function showRemaining(currentInput, indicator) {
|
||||
indicator.css({
|
||||
display: 'block'
|
||||
});
|
||||
currentInput.trigger('maxlength.shown');
|
||||
}
|
||||
|
||||
/**
|
||||
* When called shows the indicator.
|
||||
*
|
||||
* @param indicator
|
||||
*/
|
||||
function hideRemaining(currentInput, indicator) {
|
||||
|
||||
if (options.alwaysShow) {
|
||||
return;
|
||||
}
|
||||
|
||||
indicator.css({
|
||||
display: 'none'
|
||||
});
|
||||
currentInput.trigger('maxlength.hidden');
|
||||
}
|
||||
|
||||
/**
|
||||
* This function updates the value in the indicator
|
||||
*
|
||||
* @param maxLengthThisInput
|
||||
* @param typedChars
|
||||
* @return String
|
||||
*/
|
||||
function updateMaxLengthHTML(currentInputText, maxLengthThisInput, typedChars) {
|
||||
var output = '';
|
||||
if (options.message) {
|
||||
if (typeof options.message === 'function') {
|
||||
output = options.message(currentInputText, maxLengthThisInput);
|
||||
} else {
|
||||
output = options.message.replace('%charsTyped%', typedChars)
|
||||
.replace('%charsRemaining%', maxLengthThisInput - typedChars)
|
||||
.replace('%charsTotal%', maxLengthThisInput);
|
||||
}
|
||||
} else {
|
||||
if (options.preText) {
|
||||
output += options.preText;
|
||||
}
|
||||
if (!options.showCharsTyped) {
|
||||
output += maxLengthThisInput - typedChars;
|
||||
} else {
|
||||
output += typedChars;
|
||||
}
|
||||
if (options.showMaxLength) {
|
||||
output += options.separator + maxLengthThisInput;
|
||||
}
|
||||
if (options.postText) {
|
||||
output += options.postText;
|
||||
}
|
||||
}
|
||||
return output;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function updates the value of the counter in the indicator.
|
||||
* Wants as parameters: the number of remaining chars, the element currently managed,
|
||||
* the maxLength for the current input and the indicator generated for it.
|
||||
*
|
||||
* @param remaining
|
||||
* @param currentInput
|
||||
* @param maxLengthCurrentInput
|
||||
* @param maxLengthIndicator
|
||||
*/
|
||||
function manageRemainingVisibility(remaining, currentInput, maxLengthCurrentInput, maxLengthIndicator) {
|
||||
if (maxLengthIndicator) {
|
||||
maxLengthIndicator.html(updateMaxLengthHTML(currentInput.val(), maxLengthCurrentInput, (maxLengthCurrentInput - remaining)));
|
||||
|
||||
if (remaining > 0) {
|
||||
if (charsLeftThreshold(currentInput, options.threshold, maxLengthCurrentInput)) {
|
||||
showRemaining(currentInput, maxLengthIndicator.removeClass(options.limitReachedClass).addClass(options.warningClass));
|
||||
} else {
|
||||
hideRemaining(currentInput, maxLengthIndicator);
|
||||
}
|
||||
} else {
|
||||
showRemaining(currentInput, maxLengthIndicator.removeClass(options.warningClass).addClass(options.limitReachedClass));
|
||||
}
|
||||
}
|
||||
|
||||
if (options.customMaxAttribute) {
|
||||
// class to use for form validation on custom maxlength attribute
|
||||
if (remaining < 0) {
|
||||
currentInput.addClass('overmax');
|
||||
} else {
|
||||
currentInput.removeClass('overmax');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function returns an object containing all the
|
||||
* informations about the position of the current input
|
||||
*
|
||||
* @param currentInput
|
||||
* @return object {bottom height left right top width}
|
||||
*
|
||||
*/
|
||||
function getPosition(currentInput) {
|
||||
var el = currentInput[0];
|
||||
return $.extend({}, (typeof el.getBoundingClientRect === 'function') ? el.getBoundingClientRect() : {
|
||||
width: el.offsetWidth,
|
||||
height: el.offsetHeight
|
||||
}, currentInput.offset());
|
||||
}
|
||||
|
||||
/**
|
||||
* This function places the maxLengthIndicator based on placement config object.
|
||||
*
|
||||
* @param {object} placement
|
||||
* @param {$} maxLengthIndicator
|
||||
* @return null
|
||||
*
|
||||
*/
|
||||
function placeWithCSS(placement, maxLengthIndicator) {
|
||||
if (!placement || !maxLengthIndicator) {
|
||||
return;
|
||||
}
|
||||
|
||||
var POSITION_KEYS = [
|
||||
'top',
|
||||
'bottom',
|
||||
'left',
|
||||
'right',
|
||||
'position'
|
||||
];
|
||||
|
||||
var cssPos = {};
|
||||
|
||||
// filter css properties to position
|
||||
$.each(POSITION_KEYS, function (i, key) {
|
||||
var val = options.placement[key];
|
||||
if (typeof val !== 'undefined') {
|
||||
cssPos[key] = val;
|
||||
}
|
||||
});
|
||||
|
||||
maxLengthIndicator.css(cssPos);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* This function places the maxLengthIndicator at the
|
||||
* top / bottom / left / right of the currentInput
|
||||
*
|
||||
* @param currentInput
|
||||
* @param maxLengthIndicator
|
||||
* @return null
|
||||
*
|
||||
*/
|
||||
function place(currentInput, maxLengthIndicator) {
|
||||
var pos = getPosition(currentInput);
|
||||
|
||||
// Supports custom placement handler
|
||||
if ($.type(options.placement) === 'function') {
|
||||
options.placement(currentInput, maxLengthIndicator, pos);
|
||||
return;
|
||||
}
|
||||
|
||||
// Supports custom placement via css positional properties
|
||||
if ($.isPlainObject(options.placement)) {
|
||||
placeWithCSS(options.placement, maxLengthIndicator);
|
||||
return;
|
||||
}
|
||||
|
||||
var inputOuter = currentInput.outerWidth(),
|
||||
outerWidth = maxLengthIndicator.outerWidth(),
|
||||
actualWidth = maxLengthIndicator.width(),
|
||||
actualHeight = maxLengthIndicator.height();
|
||||
|
||||
// get the right position if the indicator is appended to the input's parent
|
||||
if (options.appendToParent) {
|
||||
pos.top -= currentInput.parent().offset().top;
|
||||
pos.left -= currentInput.parent().offset().left;
|
||||
}
|
||||
|
||||
switch (options.placement) {
|
||||
case 'bottom':
|
||||
maxLengthIndicator.css({
|
||||
top: pos.top + pos.height,
|
||||
left: pos.left + pos.width / 2 - actualWidth / 2
|
||||
});
|
||||
break;
|
||||
case 'top':
|
||||
maxLengthIndicator.css({
|
||||
top: pos.top - actualHeight,
|
||||
left: pos.left + pos.width / 2 - actualWidth / 2
|
||||
});
|
||||
break;
|
||||
case 'left':
|
||||
maxLengthIndicator.css({
|
||||
top: pos.top + pos.height / 2 - actualHeight / 2,
|
||||
left: pos.left - actualWidth
|
||||
});
|
||||
break;
|
||||
case 'right':
|
||||
maxLengthIndicator.css({
|
||||
top: pos.top + pos.height / 2 - actualHeight / 2,
|
||||
left: pos.left + pos.width
|
||||
});
|
||||
break;
|
||||
case 'bottom-right':
|
||||
maxLengthIndicator.css({
|
||||
top: pos.top + pos.height,
|
||||
left: pos.left + pos.width
|
||||
});
|
||||
break;
|
||||
case 'top-right':
|
||||
maxLengthIndicator.css({
|
||||
top: pos.top - actualHeight,
|
||||
left: pos.left + inputOuter
|
||||
});
|
||||
break;
|
||||
case 'top-left':
|
||||
maxLengthIndicator.css({
|
||||
top: pos.top - actualHeight,
|
||||
left: pos.left - outerWidth
|
||||
});
|
||||
break;
|
||||
case 'bottom-left':
|
||||
maxLengthIndicator.css({
|
||||
top: pos.top + currentInput.outerHeight(),
|
||||
left: pos.left - outerWidth
|
||||
});
|
||||
break;
|
||||
case 'centered-right':
|
||||
maxLengthIndicator.css({
|
||||
top: pos.top + (actualHeight / 2),
|
||||
left: pos.left + inputOuter - outerWidth - 3
|
||||
});
|
||||
break;
|
||||
|
||||
// Some more options for placements
|
||||
case 'bottom-right-inside':
|
||||
maxLengthIndicator.css({
|
||||
top: pos.top + pos.height,
|
||||
left: pos.left + pos.width - outerWidth
|
||||
});
|
||||
break;
|
||||
case 'top-right-inside':
|
||||
maxLengthIndicator.css({
|
||||
top: pos.top - actualHeight,
|
||||
left: pos.left + inputOuter - outerWidth
|
||||
});
|
||||
break;
|
||||
case 'top-left-inside':
|
||||
maxLengthIndicator.css({
|
||||
top: pos.top - actualHeight,
|
||||
left: pos.left
|
||||
});
|
||||
break;
|
||||
case 'bottom-left-inside':
|
||||
maxLengthIndicator.css({
|
||||
top: pos.top + currentInput.outerHeight(),
|
||||
left: pos.left
|
||||
});
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This function returns true if the indicator position needs to
|
||||
* be recalculated when the currentInput changes
|
||||
*
|
||||
* @return {boolean}
|
||||
*
|
||||
*/
|
||||
function isPlacementMutable() {
|
||||
return options.placement === 'bottom-right-inside' || options.placement === 'top-right-inside' || typeof options.placement === 'function' || (options.message && typeof options.message === 'function');
|
||||
}
|
||||
|
||||
/**
|
||||
* This function retrieves the maximum length of currentInput
|
||||
*
|
||||
* @param currentInput
|
||||
* @return {number}
|
||||
*
|
||||
*/
|
||||
function getMaxLength(currentInput) {
|
||||
var max = currentInput.attr('maxlength') || options.customMaxAttribute;
|
||||
|
||||
if (options.customMaxAttribute && !options.allowOverMax) {
|
||||
var custom = currentInput.attr(options.customMaxAttribute);
|
||||
if (!max || custom < max) {
|
||||
max = custom;
|
||||
}
|
||||
}
|
||||
|
||||
if (!max) {
|
||||
max = currentInput.attr('size');
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
return this.each(function () {
|
||||
|
||||
var currentInput = $(this),
|
||||
maxLengthCurrentInput,
|
||||
maxLengthIndicator;
|
||||
|
||||
$(window).resize(function () {
|
||||
if (maxLengthIndicator) {
|
||||
place(currentInput, maxLengthIndicator);
|
||||
}
|
||||
});
|
||||
|
||||
function firstInit() {
|
||||
var maxlengthContent = updateMaxLengthHTML(currentInput.val(), maxLengthCurrentInput, '0');
|
||||
maxLengthCurrentInput = getMaxLength(currentInput);
|
||||
|
||||
if (!maxLengthIndicator) {
|
||||
maxLengthIndicator = $('<span class="bootstrap-maxlength"></span>').css({
|
||||
display: 'none',
|
||||
position: 'absolute',
|
||||
whiteSpace: 'nowrap',
|
||||
zIndex: options.zIndex
|
||||
}).html(maxlengthContent);
|
||||
}
|
||||
|
||||
// We need to detect resizes if we are dealing with a textarea:
|
||||
if (currentInput.is('textarea')) {
|
||||
currentInput.data('maxlenghtsizex', currentInput.outerWidth());
|
||||
currentInput.data('maxlenghtsizey', currentInput.outerHeight());
|
||||
|
||||
currentInput.mouseup(function () {
|
||||
if (currentInput.outerWidth() !== currentInput.data('maxlenghtsizex') || currentInput.outerHeight() !== currentInput.data('maxlenghtsizey')) {
|
||||
place(currentInput, maxLengthIndicator);
|
||||
}
|
||||
|
||||
currentInput.data('maxlenghtsizex', currentInput.outerWidth());
|
||||
currentInput.data('maxlenghtsizey', currentInput.outerHeight());
|
||||
});
|
||||
}
|
||||
|
||||
if (options.appendToParent) {
|
||||
currentInput.parent().append(maxLengthIndicator);
|
||||
currentInput.parent().css('position', 'relative');
|
||||
} else {
|
||||
documentBody.append(maxLengthIndicator);
|
||||
}
|
||||
|
||||
var remaining = remainingChars(currentInput, getMaxLength(currentInput));
|
||||
manageRemainingVisibility(remaining, currentInput, maxLengthCurrentInput, maxLengthIndicator);
|
||||
place(currentInput, maxLengthIndicator);
|
||||
}
|
||||
|
||||
if (options.showOnReady) {
|
||||
currentInput.ready(function () {
|
||||
firstInit();
|
||||
});
|
||||
} else {
|
||||
currentInput.focus(function () {
|
||||
firstInit();
|
||||
});
|
||||
}
|
||||
|
||||
currentInput.on('maxlength.reposition', function () {
|
||||
place(currentInput, maxLengthIndicator);
|
||||
});
|
||||
|
||||
|
||||
currentInput.on('destroyed', function () {
|
||||
if (maxLengthIndicator) {
|
||||
maxLengthIndicator.remove();
|
||||
}
|
||||
});
|
||||
|
||||
currentInput.on('blur', function () {
|
||||
if (maxLengthIndicator && !options.showOnReady) {
|
||||
maxLengthIndicator.remove();
|
||||
}
|
||||
});
|
||||
|
||||
currentInput.on('input', function () {
|
||||
var maxlength = getMaxLength(currentInput),
|
||||
remaining = remainingChars(currentInput, maxlength),
|
||||
output = true;
|
||||
|
||||
if (options.validate && remaining < 0) {
|
||||
truncateChars(currentInput, maxlength);
|
||||
output = false;
|
||||
} else {
|
||||
manageRemainingVisibility(remaining, currentInput, maxLengthCurrentInput, maxLengthIndicator);
|
||||
}
|
||||
|
||||
if (isPlacementMutable()) {
|
||||
place(currentInput, maxLengthIndicator);
|
||||
}
|
||||
|
||||
return output;
|
||||
});
|
||||
});
|
||||
}
|
||||
});
|
||||
}(jQuery));
|
||||
@@ -0,0 +1,453 @@
|
||||
/*!
|
||||
* Bootstrap-select v1.13.17 (https://developer.snapappointments.com/bootstrap-select)
|
||||
*
|
||||
* Copyright 2012-2020 SnapAppointments, LLC
|
||||
* Licensed under MIT (https://github.com/snapappointments/bootstrap-select/blob/master/LICENSE)
|
||||
*/
|
||||
|
||||
@-webkit-keyframes bs-notify-fadeOut {
|
||||
0% {
|
||||
opacity: 0.9;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
@-o-keyframes bs-notify-fadeOut {
|
||||
0% {
|
||||
opacity: 0.9;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
@keyframes bs-notify-fadeOut {
|
||||
0% {
|
||||
opacity: 0.9;
|
||||
}
|
||||
100% {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
select.bs-select-hidden,
|
||||
.bootstrap-select > select.bs-select-hidden,
|
||||
select.selectpicker {
|
||||
display: none !important;
|
||||
}
|
||||
.bootstrap-select {
|
||||
width: 220px \0;
|
||||
/*IE9 and below*/
|
||||
vertical-align: middle;
|
||||
}
|
||||
.bootstrap-select > .dropdown-toggle {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
text-align: right;
|
||||
white-space: nowrap;
|
||||
display: -webkit-inline-box;
|
||||
display: -webkit-inline-flex;
|
||||
display: -ms-inline-flexbox;
|
||||
display: inline-flex;
|
||||
-webkit-box-align: center;
|
||||
-webkit-align-items: center;
|
||||
-ms-flex-align: center;
|
||||
align-items: center;
|
||||
-webkit-box-pack: justify;
|
||||
-webkit-justify-content: space-between;
|
||||
-ms-flex-pack: justify;
|
||||
justify-content: space-between;
|
||||
}
|
||||
.bootstrap-select > .dropdown-toggle:after {
|
||||
margin-top: -1px;
|
||||
}
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder:hover,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder:focus,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder:active {
|
||||
color: #999;
|
||||
}
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-primary,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-secondary,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-success,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-danger,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-info,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-dark,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-primary:hover,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-secondary:hover,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-success:hover,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-danger:hover,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-info:hover,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-dark:hover,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-primary:focus,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-secondary:focus,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-success:focus,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-danger:focus,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-info:focus,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-dark:focus,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-primary:active,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-secondary:active,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-success:active,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-danger:active,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-info:active,
|
||||
.bootstrap-select > .dropdown-toggle.bs-placeholder.btn-dark:active {
|
||||
color: rgba(255, 255, 255, 0.5);
|
||||
}
|
||||
.bootstrap-select > select {
|
||||
position: absolute !important;
|
||||
bottom: 0;
|
||||
left: 50%;
|
||||
display: block !important;
|
||||
width: 0.5px !important;
|
||||
height: 100% !important;
|
||||
padding: 0 !important;
|
||||
opacity: 0 !important;
|
||||
border: none;
|
||||
z-index: 0 !important;
|
||||
}
|
||||
.bootstrap-select > select.mobile-device {
|
||||
top: 0;
|
||||
left: 0;
|
||||
display: block !important;
|
||||
width: 100% !important;
|
||||
z-index: 2 !important;
|
||||
}
|
||||
.has-error .bootstrap-select .dropdown-toggle,
|
||||
.error .bootstrap-select .dropdown-toggle,
|
||||
.bootstrap-select.is-invalid .dropdown-toggle,
|
||||
.was-validated .bootstrap-select select:invalid + .dropdown-toggle {
|
||||
border-color: #b94a48;
|
||||
}
|
||||
.bootstrap-select.is-valid .dropdown-toggle,
|
||||
.was-validated .bootstrap-select select:valid + .dropdown-toggle {
|
||||
border-color: #28a745;
|
||||
}
|
||||
.bootstrap-select.fit-width {
|
||||
width: auto !important;
|
||||
}
|
||||
.bootstrap-select:not([class*="col-"]):not([class*="form-control"]):not(.input-group-btn) {
|
||||
width: 220px;
|
||||
}
|
||||
.bootstrap-select.form-control {
|
||||
margin-bottom: 0;
|
||||
padding: 0;
|
||||
border: none;
|
||||
height: auto;
|
||||
}
|
||||
:not(.input-group) > .bootstrap-select.form-control:not([class*="col-"]) {
|
||||
width: 100%;
|
||||
}
|
||||
.bootstrap-select.form-control.input-group-btn {
|
||||
float: none;
|
||||
z-index: auto;
|
||||
}
|
||||
.form-inline .bootstrap-select,
|
||||
.form-inline .bootstrap-select.form-control:not([class*="col-"]) {
|
||||
width: auto;
|
||||
}
|
||||
.bootstrap-select:not(.input-group-btn),
|
||||
.bootstrap-select[class*="col-"] {
|
||||
float: none;
|
||||
display: inline-block;
|
||||
margin-left: 0;
|
||||
}
|
||||
.bootstrap-select.dropdown-menu-right,
|
||||
.bootstrap-select[class*="col-"].dropdown-menu-right,
|
||||
.row .bootstrap-select[class*="col-"].dropdown-menu-right {
|
||||
float: right;
|
||||
}
|
||||
.form-inline .bootstrap-select,
|
||||
.form-horizontal .bootstrap-select,
|
||||
.form-group .bootstrap-select {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.form-group-lg .bootstrap-select.form-control,
|
||||
.form-group-sm .bootstrap-select.form-control {
|
||||
padding: 0;
|
||||
}
|
||||
.form-group-lg .bootstrap-select.form-control .dropdown-toggle,
|
||||
.form-group-sm .bootstrap-select.form-control .dropdown-toggle {
|
||||
height: 100%;
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
border-radius: inherit;
|
||||
}
|
||||
.bootstrap-select.form-control-sm .dropdown-toggle,
|
||||
.bootstrap-select.form-control-lg .dropdown-toggle {
|
||||
font-size: inherit;
|
||||
line-height: inherit;
|
||||
border-radius: inherit;
|
||||
}
|
||||
.bootstrap-select.form-control-sm .dropdown-toggle {
|
||||
padding: 0.25rem 0.5rem;
|
||||
}
|
||||
.bootstrap-select.form-control-lg .dropdown-toggle {
|
||||
padding: 0.5rem 1rem;
|
||||
}
|
||||
.form-inline .bootstrap-select .form-control {
|
||||
width: 100%;
|
||||
}
|
||||
.bootstrap-select.disabled,
|
||||
.bootstrap-select > .disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.bootstrap-select.disabled:focus,
|
||||
.bootstrap-select > .disabled:focus {
|
||||
outline: none !important;
|
||||
}
|
||||
.bootstrap-select.bs-container {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
height: 0 !important;
|
||||
padding: 0 !important;
|
||||
}
|
||||
.bootstrap-select.bs-container .dropdown-menu {
|
||||
z-index: 1060;
|
||||
}
|
||||
.bootstrap-select .dropdown-toggle .filter-option {
|
||||
position: static;
|
||||
top: 0;
|
||||
left: 0;
|
||||
float: left;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
overflow: hidden;
|
||||
-webkit-box-flex: 0;
|
||||
-webkit-flex: 0 1 auto;
|
||||
-ms-flex: 0 1 auto;
|
||||
flex: 0 1 auto;
|
||||
}
|
||||
.bs3.bootstrap-select .dropdown-toggle .filter-option {
|
||||
padding-right: inherit;
|
||||
}
|
||||
.input-group .bs3-has-addon.bootstrap-select .dropdown-toggle .filter-option {
|
||||
position: absolute;
|
||||
padding-top: inherit;
|
||||
padding-bottom: inherit;
|
||||
padding-left: inherit;
|
||||
float: none;
|
||||
}
|
||||
.input-group .bs3-has-addon.bootstrap-select .dropdown-toggle .filter-option .filter-option-inner {
|
||||
padding-right: inherit;
|
||||
}
|
||||
.bootstrap-select .dropdown-toggle .filter-option-inner-inner {
|
||||
overflow: hidden;
|
||||
}
|
||||
.bootstrap-select .dropdown-toggle .filter-expand {
|
||||
width: 0 !important;
|
||||
float: left;
|
||||
opacity: 0 !important;
|
||||
overflow: hidden;
|
||||
}
|
||||
.bootstrap-select .dropdown-toggle .caret {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
right: 12px;
|
||||
margin-top: -2px;
|
||||
vertical-align: middle;
|
||||
}
|
||||
.input-group .bootstrap-select.form-control .dropdown-toggle {
|
||||
border-radius: inherit;
|
||||
}
|
||||
.bootstrap-select[class*="col-"] .dropdown-toggle {
|
||||
width: 100%;
|
||||
}
|
||||
.bootstrap-select .dropdown-menu {
|
||||
min-width: 100%;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.bootstrap-select .dropdown-menu > .inner:focus {
|
||||
outline: none !important;
|
||||
}
|
||||
.bootstrap-select .dropdown-menu.inner {
|
||||
position: static;
|
||||
float: none;
|
||||
border: 0;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
border-radius: 0;
|
||||
-webkit-box-shadow: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
.bootstrap-select .dropdown-menu li {
|
||||
position: relative;
|
||||
}
|
||||
.bootstrap-select .dropdown-menu li.active small {
|
||||
color: rgba(255, 255, 255, 0.5) !important;
|
||||
}
|
||||
.bootstrap-select .dropdown-menu li.disabled a {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
.bootstrap-select .dropdown-menu li a {
|
||||
cursor: pointer;
|
||||
-webkit-user-select: none;
|
||||
-moz-user-select: none;
|
||||
-ms-user-select: none;
|
||||
user-select: none;
|
||||
}
|
||||
.bootstrap-select .dropdown-menu li a.opt {
|
||||
position: relative;
|
||||
padding-left: 2.25em;
|
||||
}
|
||||
.bootstrap-select .dropdown-menu li a span.check-mark {
|
||||
display: none;
|
||||
}
|
||||
.bootstrap-select .dropdown-menu li a span.text {
|
||||
display: inline-block;
|
||||
}
|
||||
.bootstrap-select .dropdown-menu li small {
|
||||
padding-left: 0.5em;
|
||||
}
|
||||
.bootstrap-select .dropdown-menu .notify {
|
||||
position: absolute;
|
||||
bottom: 5px;
|
||||
width: 96%;
|
||||
margin: 0 2%;
|
||||
min-height: 26px;
|
||||
padding: 3px 5px;
|
||||
background: #f5f5f5;
|
||||
border: 1px solid #f2f3f3;
|
||||
pointer-events: none;
|
||||
opacity: 0.9;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.bootstrap-select .dropdown-menu .notify.fadeOut {
|
||||
-webkit-animation: 300ms linear 750ms forwards bs-notify-fadeOut;
|
||||
-o-animation: 300ms linear 750ms forwards bs-notify-fadeOut;
|
||||
animation: 300ms linear 750ms forwards bs-notify-fadeOut;
|
||||
}
|
||||
.bootstrap-select .no-results {
|
||||
padding: 3px;
|
||||
background: #f5f5f5;
|
||||
margin: 0 5px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
.bootstrap-select.fit-width .dropdown-toggle .filter-option {
|
||||
position: static;
|
||||
display: inline;
|
||||
padding: 0;
|
||||
}
|
||||
.bootstrap-select.fit-width .dropdown-toggle .filter-option-inner,
|
||||
.bootstrap-select.fit-width .dropdown-toggle .filter-option-inner-inner {
|
||||
display: inline;
|
||||
}
|
||||
.bootstrap-select.fit-width .dropdown-toggle .bs-caret:before {
|
||||
content: '\00a0';
|
||||
}
|
||||
.bootstrap-select.fit-width .dropdown-toggle .caret {
|
||||
position: static;
|
||||
top: auto;
|
||||
margin-top: -1px;
|
||||
}
|
||||
.bootstrap-select.show-tick .dropdown-menu .selected span.check-mark {
|
||||
position: absolute;
|
||||
display: inline-block;
|
||||
right: 15px;
|
||||
top: 10px;
|
||||
}
|
||||
.bootstrap-select.show-tick .dropdown-menu li a span.text {
|
||||
margin-right: 34px;
|
||||
}
|
||||
.bootstrap-select .bs-ok-default:after {
|
||||
content: '';
|
||||
display: block;
|
||||
width: 0.5em;
|
||||
height: 1em;
|
||||
border-style: solid;
|
||||
border-width: 0 0.13em 0.13em 0;
|
||||
border-color: #4d5259;
|
||||
-webkit-transform-style: preserve-3d;
|
||||
transform-style: preserve-3d;
|
||||
-webkit-transform: rotate(45deg);
|
||||
-ms-transform: rotate(45deg);
|
||||
-o-transform: rotate(45deg);
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
.bootstrap-select.show-menu-arrow.open > .dropdown-toggle,
|
||||
.bootstrap-select.show-menu-arrow.show > .dropdown-toggle {
|
||||
z-index: 1061;
|
||||
}
|
||||
.bootstrap-select.show-menu-arrow .dropdown-toggle .filter-option:before {
|
||||
content: '';
|
||||
border-left: 7px solid transparent;
|
||||
border-right: 7px solid transparent;
|
||||
border-bottom: 7px solid rgba(204, 204, 204, 0.2);
|
||||
position: absolute;
|
||||
bottom: -4px;
|
||||
left: 9px;
|
||||
display: none;
|
||||
}
|
||||
.bootstrap-select.show-menu-arrow .dropdown-toggle .filter-option:after {
|
||||
content: '';
|
||||
border-left: 6px solid transparent;
|
||||
border-right: 6px solid transparent;
|
||||
border-bottom: 6px solid white;
|
||||
position: absolute;
|
||||
bottom: -4px;
|
||||
left: 10px;
|
||||
display: none;
|
||||
}
|
||||
.bootstrap-select.show-menu-arrow.dropup .dropdown-toggle .filter-option:before {
|
||||
bottom: auto;
|
||||
top: -4px;
|
||||
border-top: 7px solid rgba(204, 204, 204, 0.2);
|
||||
border-bottom: 0;
|
||||
}
|
||||
.bootstrap-select.show-menu-arrow.dropup .dropdown-toggle .filter-option:after {
|
||||
bottom: auto;
|
||||
top: -4px;
|
||||
border-top: 6px solid white;
|
||||
border-bottom: 0;
|
||||
}
|
||||
.bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle .filter-option:before {
|
||||
right: 12px;
|
||||
left: auto;
|
||||
}
|
||||
.bootstrap-select.show-menu-arrow.pull-right .dropdown-toggle .filter-option:after {
|
||||
right: 13px;
|
||||
left: auto;
|
||||
}
|
||||
.bootstrap-select.show-menu-arrow.open > .dropdown-toggle .filter-option:before,
|
||||
.bootstrap-select.show-menu-arrow.show > .dropdown-toggle .filter-option:before,
|
||||
.bootstrap-select.show-menu-arrow.open > .dropdown-toggle .filter-option:after,
|
||||
.bootstrap-select.show-menu-arrow.show > .dropdown-toggle .filter-option:after {
|
||||
display: block;
|
||||
}
|
||||
.bs-searchbox,
|
||||
.bs-actionsbox,
|
||||
.bs-donebutton {
|
||||
padding: 4px 8px;
|
||||
}
|
||||
.bs-actionsbox {
|
||||
width: 100%;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.bs-actionsbox .btn-group button {
|
||||
width: 50%;
|
||||
}
|
||||
.bs-donebutton {
|
||||
float: left;
|
||||
width: 100%;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
.bs-donebutton .btn-group button {
|
||||
width: 100%;
|
||||
}
|
||||
.bs-searchbox + .bs-actionsbox {
|
||||
padding: 0 8px 4px;
|
||||
}
|
||||
.bs-searchbox .form-control {
|
||||
margin-bottom: 0;
|
||||
width: 100%;
|
||||
float: none;
|
||||
}
|
||||
/*# sourceMappingURL=bootstrap-select.css.map */
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,8 @@
|
||||
/*!
|
||||
* Bootstrap-select v1.13.17 (https://developer.snapappointments.com/bootstrap-select)
|
||||
*
|
||||
* Copyright 2012-2020 SnapAppointments, LLC
|
||||
* Licensed under MIT (https://github.com/snapappointments/bootstrap-select/blob/master/LICENSE)
|
||||
*/
|
||||
|
||||
!function(e,t){void 0===e&&void 0!==window&&(e=window),"function"==typeof define&&define.amd?define(["jquery"],function(e){return t(e)}):"object"==typeof module&&module.exports?module.exports=t(require("jquery")):t(e.jQuery)}(this,function(e){e.fn.selectpicker.defaults={noneSelectedText:"\u6ca1\u6709\u9009\u4e2d\u4efb\u4f55\u9879",noneResultsText:"\u6ca1\u6709\u627e\u5230\u5339\u914d\u9879",countSelectedText:"\u9009\u4e2d{1}\u4e2d\u7684{0}\u9879",maxOptionsText:["\u8d85\u51fa\u9650\u5236 (\u6700\u591a\u9009\u62e9{n}\u9879)","\u7ec4\u9009\u62e9\u8d85\u51fa\u9650\u5236(\u6700\u591a\u9009\u62e9{n}\u7ec4)"],multipleSeparator:", ",selectAllText:"\u5168\u9009",deselectAllText:"\u53d6\u6d88\u5168\u9009"}});
|
||||
@@ -0,0 +1,8 @@
|
||||
/*!
|
||||
* Bootstrap-select v1.13.17 (https://developer.snapappointments.com/bootstrap-select)
|
||||
*
|
||||
* Copyright 2012-2020 SnapAppointments, LLC
|
||||
* Licensed under MIT (https://github.com/snapappointments/bootstrap-select/blob/master/LICENSE)
|
||||
*/
|
||||
|
||||
!function(e,t){void 0===e&&void 0!==window&&(e=window),"function"==typeof define&&define.amd?define(["jquery"],function(e){return t(e)}):"object"==typeof module&&module.exports?module.exports=t(require("jquery")):t(e.jQuery)}(this,function(e){e.fn.selectpicker.defaults={noneSelectedText:"\u6c92\u6709\u9078\u53d6\u4efb\u4f55\u9805\u76ee",noneResultsText:"\u6c92\u6709\u627e\u5230\u7b26\u5408\u7684\u7d50\u679c",countSelectedText:"\u5df2\u7d93\u9078\u53d6{0}\u500b\u9805\u76ee",maxOptionsText:["\u8d85\u904e\u9650\u5236 (\u6700\u591a\u9078\u64c7{n}\u9805)","\u8d85\u904e\u9650\u5236(\u6700\u591a\u9078\u64c7{n}\u7d44)"],selectAllText:"\u9078\u53d6\u5168\u90e8",deselectAllText:"\u5168\u90e8\u53d6\u6d88",multipleSeparator:", "}});
|
||||
@@ -0,0 +1,361 @@
|
||||
/**
|
||||
* @author zhixin wen <wenzhixin2010@gmail.com>
|
||||
* version: 1.16.0
|
||||
* https://github.com/wenzhixin/bootstrap-table/
|
||||
*/
|
||||
.bootstrap-table .fixed-table-toolbar::after {
|
||||
content: "";
|
||||
display: block;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .bs-bars,
|
||||
.bootstrap-table .fixed-table-toolbar .search,
|
||||
.bootstrap-table .fixed-table-toolbar .columns {
|
||||
position: relative;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .columns .btn-group > .btn-group {
|
||||
display: inline-block;
|
||||
margin-left: -1px !important;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .columns .btn-group > .btn-group > .btn {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .columns .btn-group > .btn-group:first-child > .btn {
|
||||
border-top-left-radius: 4px;
|
||||
border-bottom-left-radius: 4px;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .columns .btn-group > .btn-group:last-child > .btn {
|
||||
border-top-right-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .columns .dropdown-menu {
|
||||
text-align: left;
|
||||
max-height: 300px;
|
||||
overflow: auto;
|
||||
-ms-overflow-style: scrollbar;
|
||||
z-index: 1001;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .columns label {
|
||||
display: block;
|
||||
padding: 3px 20px;
|
||||
clear: both;
|
||||
font-weight: normal;
|
||||
line-height: 1.428571429;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .columns-left {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .columns-right {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .pull-right .dropdown-menu {
|
||||
right: 0;
|
||||
left: auto;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container {
|
||||
position: relative;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table {
|
||||
width: 100%;
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table th,
|
||||
.bootstrap-table .fixed-table-container .table td {
|
||||
vertical-align: middle;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table thead th {
|
||||
vertical-align: bottom;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table thead th:focus {
|
||||
outline: 0 solid transparent;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table thead th.detail {
|
||||
width: 30px;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table thead th .th-inner {
|
||||
padding: 0.75rem;
|
||||
vertical-align: bottom;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table thead th .sortable {
|
||||
cursor: pointer;
|
||||
background-position: right;
|
||||
background-repeat: no-repeat;
|
||||
padding-right: 30px !important;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table thead th .both {
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAQAAADYWf5HAAAAkElEQVQoz7X QMQ5AQBCF4dWQSJxC5wwax1Cq1e7BAdxD5SL+Tq/QCM1oNiJidwox0355mXnG/DrEtIQ6azioNZQxI0ykPhTQIwhCR+BmBYtlK7kLJYwWCcJA9M4qdrZrd8pPjZWPtOqdRQy320YSV17OatFC4euts6z39GYMKRPCTKY9UnPQ6P+GtMRfGtPnBCiqhAeJPmkqAAAAAElFTkSuQmCC");
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table thead th .asc {
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAZ0lEQVQ4y2NgGLKgquEuFxBPAGI2ahhWCsS/gDibUoO0gPgxEP8H4ttArEyuQYxAPBdqEAxPBImTY5gjEL9DM+wTENuQahAvEO9DMwiGdwAxOymGJQLxTyD+jgWDxCMZRsEoGAVoAADeemwtPcZI2wAAAABJRU5ErkJggg==");
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table thead th .desc {
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAZUlEQVQ4y2NgGAWjYBSggaqGu5FA/BOIv2PBIPFEUgxjB+IdQPwfC94HxLykus4GiD+hGfQOiB3J8SojEE9EM2wuSJzcsFMG4ttQgx4DsRalkZENxL+AuJQaMcsGxBOAmGvopk8AVz1sLZgg0bsAAAAASUVORK5CYII= ");
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table tbody tr.selected td {
|
||||
background-color: rgba(0, 0, 0, 0.035);
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table tbody tr.no-records-found td {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table tbody tr .card-view {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table tbody tr .card-view .card-view-title {
|
||||
font-weight: bold;
|
||||
display: inline-block;
|
||||
min-width: 30%;
|
||||
text-align: left !important;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table .bs-checkbox {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table .bs-checkbox label {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table .bs-checkbox label input[type="radio"],
|
||||
.bootstrap-table .fixed-table-container .table .bs-checkbox label input[type="checkbox"] {
|
||||
margin: 0 auto !important;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table.table-sm .th-inner {
|
||||
padding: 0.3rem;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container.fixed-height:not(.has-footer) {
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container.fixed-height.has-card-view {
|
||||
border-top: 1px solid #dee2e6;
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container.fixed-height .fixed-table-border {
|
||||
border-left: 1px solid #dee2e6;
|
||||
border-right: 1px solid #dee2e6;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container.fixed-height .table thead th {
|
||||
border-bottom: 1px solid #dee2e6;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container.fixed-height .table-dark thead th {
|
||||
border-bottom: 1px solid #32383e;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-header {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body {
|
||||
overflow-x: auto;
|
||||
overflow-y: auto;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading {
|
||||
align-items: center;
|
||||
background: #fff;
|
||||
display: none;
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap {
|
||||
align-items: baseline;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .loading-text {
|
||||
font-size: 2rem;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-dot,
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap::after,
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap::before {
|
||||
content: "";
|
||||
animation-duration: 1.5s;
|
||||
animation-iteration-count: infinite;
|
||||
animation-name: LOADING;
|
||||
background: #212529;
|
||||
border-radius: 50%;
|
||||
display: block;
|
||||
height: 5px;
|
||||
margin: 0 4px;
|
||||
opacity: 0;
|
||||
width: 5px;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-dot {
|
||||
animation-delay: 0.3s;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap::after {
|
||||
animation-delay: 0.6s;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark {
|
||||
background: #212529;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark .animation-dot,
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark .animation-wrap::after,
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark .animation-wrap::before {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-footer {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination::after {
|
||||
content: "";
|
||||
display: block;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination > .pagination-detail,
|
||||
.bootstrap-table .fixed-table-pagination > .pagination {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination > .pagination-detail .pagination-info {
|
||||
line-height: 34px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination > .pagination-detail .page-list {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination > .pagination-detail .page-list .btn-group {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination > .pagination-detail .page-list .btn-group .dropdown-menu {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination > .pagination ul.pagination {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination > .pagination ul.pagination a {
|
||||
/*padding: 6px 12px;
|
||||
line-height: 1.428571429;*/
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination > .pagination ul.pagination li.page-intermediate a {
|
||||
color: #c8c8c8;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination > .pagination ul.pagination li.page-intermediate a::before {
|
||||
content: '\2B05';
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination > .pagination ul.pagination li.page-intermediate a::after {
|
||||
content: '\27A1';
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination > .pagination ul.pagination li.disabled a {
|
||||
pointer-events: none;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.bootstrap-table.fullscreen {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1050;
|
||||
width: 100% !important;
|
||||
background: #fff;
|
||||
height: calc(100vh);
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.dropdown-item.dropdown-item-marker {
|
||||
padding: .25rem 1.5rem;
|
||||
}
|
||||
.dropdown-item.dropdown-item-marker:focus,
|
||||
.dropdown-item.dropdown-item:active {
|
||||
background-color: #fff;
|
||||
color: #4d5259;
|
||||
}
|
||||
|
||||
/* calculate scrollbar width */
|
||||
div.fixed-table-scroll-inner {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
div.fixed-table-scroll-outer {
|
||||
top: 0;
|
||||
left: 0;
|
||||
visibility: hidden;
|
||||
width: 200px;
|
||||
height: 150px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@keyframes LOADING {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
+7217
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
+10
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
Vendored
+10
File diff suppressed because one or more lines are too long
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* bootstrap-table - An extended table to integration with some of the most widely used CSS frameworks. (Supports Bootstrap, Semantic UI, Bulma, Material Design, Foundation)
|
||||
*
|
||||
* @version v1.16.0
|
||||
* @homepage https://bootstrap-table.com
|
||||
* @author wenzhixin <wenzhixin2010@gmail.com> (http://wenzhixin.net.cn/)
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
.table-cell-input{display:block!important;padding:5px!important;margin:0!important;border:0!important;width:100%!important;box-sizing:border-box!important;-moz-box-sizing:border-box!important;border-radius:0!important;line-height:1!important;white-space:nowrap}
|
||||
Vendored
+10
File diff suppressed because one or more lines are too long
+10
File diff suppressed because one or more lines are too long
+10
File diff suppressed because one or more lines are too long
+10
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
+1
File diff suppressed because one or more lines are too long
assets/bootstrap/js/bootstrap-table/extensions/filter-control/bootstrap-table-filter-control.min.css
Vendored
+10
@@ -0,0 +1,10 @@
|
||||
/**
|
||||
* bootstrap-table - An extended table to integration with some of the most widely used CSS frameworks. (Supports Bootstrap, Semantic UI, Bulma, Material Design, Foundation)
|
||||
*
|
||||
* @version v1.16.0
|
||||
* @homepage https://bootstrap-table.com
|
||||
* @author wenzhixin <wenzhixin2010@gmail.com> (http://wenzhixin.net.cn/)
|
||||
* @license MIT
|
||||
*/
|
||||
|
||||
@charset "UTF-8";.no-filter-control{height:34px}.filter-control{margin:0 2px 2px 2px}
|
||||
Vendored
+10
File diff suppressed because one or more lines are too long
Vendored
+395
@@ -0,0 +1,395 @@
|
||||
/**
|
||||
* @author zhixin wen <wenzhixin2010@gmail.com>
|
||||
*/
|
||||
|
||||
const Utils = $.fn.bootstrapTable.utils
|
||||
|
||||
// Reasonable defaults
|
||||
const PIXEL_STEP = 10
|
||||
const LINE_HEIGHT = 40
|
||||
const PAGE_HEIGHT = 800
|
||||
|
||||
function normalizeWheel (event) {
|
||||
let sX = 0 // spinX
|
||||
let sY = 0 // spinY
|
||||
let pX = 0 // pixelX
|
||||
let pY = 0 // pixelY
|
||||
|
||||
// Legacy
|
||||
if ('detail' in event) { sY = event.detail }
|
||||
if ('wheelDelta' in event) { sY = -event.wheelDelta / 120 }
|
||||
if ('wheelDeltaY' in event) { sY = -event.wheelDeltaY / 120 }
|
||||
if ('wheelDeltaX' in event) { sX = -event.wheelDeltaX / 120 }
|
||||
|
||||
// side scrolling on FF with DOMMouseScroll
|
||||
if ( 'axis' in event && event.axis === event.HORIZONTAL_AXIS ) {
|
||||
sX = sY
|
||||
sY = 0
|
||||
}
|
||||
|
||||
pX = sX * PIXEL_STEP
|
||||
pY = sY * PIXEL_STEP
|
||||
|
||||
if ('deltaY' in event) { pY = event.deltaY }
|
||||
if ('deltaX' in event) { pX = event.deltaX }
|
||||
|
||||
if ((pX || pY) && event.deltaMode) {
|
||||
if (event.deltaMode === 1) { // delta in LINE units
|
||||
pX *= LINE_HEIGHT
|
||||
pY *= LINE_HEIGHT
|
||||
} else { // delta in PAGE units
|
||||
pX *= PAGE_HEIGHT
|
||||
pY *= PAGE_HEIGHT
|
||||
}
|
||||
}
|
||||
|
||||
// Fall-back if spin cannot be determined
|
||||
if (pX && !sX) { sX = (pX < 1) ? -1 : 1 }
|
||||
if (pY && !sY) { sY = (pY < 1) ? -1 : 1 }
|
||||
|
||||
return {
|
||||
spinX: sX,
|
||||
spinY: sY,
|
||||
pixelX: pX,
|
||||
pixelY: pY
|
||||
}
|
||||
}
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, {
|
||||
fixedColumns: false,
|
||||
fixedNumber: 0,
|
||||
fixedRightNumber: 0
|
||||
})
|
||||
|
||||
$.BootstrapTable = class extends $.BootstrapTable {
|
||||
|
||||
fixedColumnsSupported () {
|
||||
return this.options.fixedColumns &&
|
||||
!this.options.detailView &&
|
||||
!this.options.cardView
|
||||
}
|
||||
|
||||
initContainer () {
|
||||
super.initContainer()
|
||||
|
||||
if (!this.fixedColumnsSupported()) {
|
||||
return
|
||||
}
|
||||
|
||||
if (this.options.fixedNumber) {
|
||||
this.$tableContainer.append('<div class="fixed-columns"></div>')
|
||||
this.$fixedColumns = this.$tableContainer.find('.fixed-columns')
|
||||
}
|
||||
|
||||
if (this.options.fixedRightNumber) {
|
||||
this.$tableContainer.append('<div class="fixed-columns-right"></div>')
|
||||
this.$fixedColumnsRight = this.$tableContainer.find('.fixed-columns-right')
|
||||
}
|
||||
}
|
||||
|
||||
initBody (...args) {
|
||||
super.initBody(...args)
|
||||
|
||||
if (!this.fixedColumnsSupported()) {
|
||||
return
|
||||
}
|
||||
|
||||
if (this.options.showHeader && this.options.height) {
|
||||
return
|
||||
}
|
||||
|
||||
this.initFixedColumnsBody()
|
||||
this.initFixedColumnsEvents()
|
||||
}
|
||||
|
||||
trigger (...args) {
|
||||
super.trigger(...args)
|
||||
|
||||
if (!this.fixedColumnsSupported()) {
|
||||
return
|
||||
}
|
||||
|
||||
if (args[0] === 'post-header') {
|
||||
this.initFixedColumnsHeader()
|
||||
} else if (args[0] === 'scroll-body') {
|
||||
if (this.needFixedColumns && this.options.fixedNumber) {
|
||||
this.$fixedBody.scrollTop(this.$tableBody.scrollTop())
|
||||
}
|
||||
|
||||
if (this.needFixedColumns && this.options.fixedRightNumber) {
|
||||
this.$fixedBodyRight.scrollTop(this.$tableBody.scrollTop())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
updateSelected () {
|
||||
super.updateSelected()
|
||||
|
||||
if (!this.fixedColumnsSupported()) {
|
||||
return
|
||||
}
|
||||
|
||||
this.$tableBody.find('tr').each((i, el) => {
|
||||
const $el = $(el)
|
||||
const index = $el.data('index')
|
||||
const classes = $el.attr('class')
|
||||
|
||||
const inputSelector = `[name="${this.options.selectItemName}"]`
|
||||
const $input = $el.find(inputSelector)
|
||||
|
||||
if (typeof index === undefined) {
|
||||
return
|
||||
}
|
||||
|
||||
const updateFixedBody = ($fixedHeader, $fixedBody) => {
|
||||
const $tr = $fixedBody.find(`tr[data-index="${index}"]`)
|
||||
$tr.attr('class', classes)
|
||||
|
||||
if ($input.length) {
|
||||
$tr.find(inputSelector).prop('checked', $input.prop('checked'))
|
||||
}
|
||||
|
||||
if (this.$selectAll.length) {
|
||||
$fixedHeader.add($fixedBody)
|
||||
.find('[name="btSelectAll"]')
|
||||
.prop('checked', this.$selectAll.prop('checked'))
|
||||
}
|
||||
}
|
||||
|
||||
if (this.$fixedBody && this.options.fixedNumber) {
|
||||
updateFixedBody(this.$fixedHeader, this.$fixedBody)
|
||||
}
|
||||
|
||||
if (this.$fixedBodyRight && this.options.fixedRightNumber) {
|
||||
updateFixedBody(this.$fixedHeaderRight, this.$fixedBodyRight)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
hideLoading () {
|
||||
super.hideLoading()
|
||||
|
||||
if (this.needFixedColumns && this.options.fixedNumber) {
|
||||
this.$fixedColumns.find('.fixed-table-loading').hide()
|
||||
}
|
||||
|
||||
if (this.needFixedColumns && this.options.fixedRightNumber) {
|
||||
this.$fixedColumnsRight.find('.fixed-table-loading').hide()
|
||||
}
|
||||
}
|
||||
|
||||
initFixedColumnsHeader () {
|
||||
if (this.options.height) {
|
||||
this.needFixedColumns = this.$tableHeader.outerWidth(true) < this.$tableHeader.find('table').outerWidth(true)
|
||||
} else {
|
||||
this.needFixedColumns = this.$tableBody.outerWidth(true) < this.$tableBody.find('table').outerWidth(true)
|
||||
}
|
||||
|
||||
const initFixedHeader = ($fixedColumns, isRight) => {
|
||||
$fixedColumns.find('.fixed-table-header').remove()
|
||||
$fixedColumns.append(this.$tableHeader.clone(true))
|
||||
|
||||
$fixedColumns.css({
|
||||
width: this.getFixedColumnsWidth(isRight)
|
||||
})
|
||||
return $fixedColumns.find('.fixed-table-header')
|
||||
}
|
||||
|
||||
if (this.needFixedColumns && this.options.fixedNumber) {
|
||||
this.$fixedHeader = initFixedHeader(this.$fixedColumns)
|
||||
this.$fixedHeader.css('margin-right', '')
|
||||
} else if (this.$fixedColumns) {
|
||||
this.$fixedColumns.html('').css('width', '')
|
||||
}
|
||||
|
||||
if (this.needFixedColumns && this.options.fixedRightNumber) {
|
||||
this.$fixedHeaderRight = initFixedHeader(this.$fixedColumnsRight, true)
|
||||
this.$fixedHeaderRight.scrollLeft(this.$fixedHeaderRight.find('table').width())
|
||||
} else if (this.$fixedColumnsRight) {
|
||||
this.$fixedColumnsRight.html('').css('width', '')
|
||||
}
|
||||
|
||||
this.initFixedColumnsBody()
|
||||
this.initFixedColumnsEvents()
|
||||
}
|
||||
|
||||
initFixedColumnsBody () {
|
||||
const initFixedBody = ($fixedColumns, $fixedHeader) => {
|
||||
$fixedColumns.find('.fixed-table-body').remove()
|
||||
$fixedColumns.append(this.$tableBody.clone(true))
|
||||
|
||||
const $fixedBody = $fixedColumns.find('.fixed-table-body')
|
||||
|
||||
const tableBody = this.$tableBody.get(0)
|
||||
const scrollHeight = tableBody.scrollWidth > tableBody.clientWidth
|
||||
? Utils.getScrollBarWidth() : 0
|
||||
const height = this.$tableContainer.outerHeight(true) - scrollHeight - 1
|
||||
|
||||
$fixedColumns.css({
|
||||
height
|
||||
})
|
||||
|
||||
$fixedBody.css({
|
||||
height: height - $fixedHeader.height()
|
||||
})
|
||||
|
||||
return $fixedBody
|
||||
}
|
||||
|
||||
if (this.needFixedColumns && this.options.fixedNumber) {
|
||||
this.$fixedBody = initFixedBody(this.$fixedColumns, this.$fixedHeader)
|
||||
}
|
||||
|
||||
if (this.needFixedColumns && this.options.fixedRightNumber) {
|
||||
this.$fixedBodyRight = initFixedBody(this.$fixedColumnsRight, this.$fixedHeaderRight)
|
||||
this.$fixedBodyRight.scrollLeft(this.$fixedBodyRight.find('table').width())
|
||||
this.$fixedBodyRight.css('overflow-y', this.options.height ? 'auto' : 'hidden')
|
||||
}
|
||||
}
|
||||
|
||||
getFixedColumnsWidth (isRight) {
|
||||
let visibleFields = this.getVisibleFields()
|
||||
let width = 0
|
||||
let fixedNumber = this.options.fixedNumber
|
||||
let marginRight = 0
|
||||
|
||||
if (isRight) {
|
||||
visibleFields = visibleFields.reverse()
|
||||
fixedNumber = this.options.fixedRightNumber
|
||||
marginRight = parseInt(this.$tableHeader.css('margin-right'), 10)
|
||||
}
|
||||
|
||||
for (let i = 0; i < fixedNumber; i++) {
|
||||
width += this.$header.find(`th[data-field="${visibleFields[i]}"]`).outerWidth(true)
|
||||
}
|
||||
|
||||
return width + marginRight + 1
|
||||
}
|
||||
|
||||
initFixedColumnsEvents () {
|
||||
const toggleHover = (e, toggle) => {
|
||||
const tr = `tr[data-index="${$(e.currentTarget).data('index')}"]`
|
||||
let $trs = this.$tableBody.find(tr)
|
||||
|
||||
if (this.$fixedBody) {
|
||||
$trs = $trs.add(this.$fixedBody.find(tr))
|
||||
}
|
||||
if (this.$fixedBodyRight) {
|
||||
$trs = $trs.add(this.$fixedBodyRight.find(tr))
|
||||
}
|
||||
|
||||
$trs.css('background-color', toggle ? $(e.currentTarget).css('background-color') : '')
|
||||
}
|
||||
|
||||
this.$tableBody.find('tr').hover(e => {
|
||||
toggleHover(e, true)
|
||||
}, e => {
|
||||
toggleHover(e, false)
|
||||
})
|
||||
|
||||
const isFirefox = typeof navigator !== 'undefined' &&
|
||||
navigator.userAgent.toLowerCase().indexOf('firefox') > -1
|
||||
const mousewheel = isFirefox ? 'DOMMouseScroll' : 'mousewheel'
|
||||
const updateScroll = (e, fixedBody) => {
|
||||
const normalized = normalizeWheel(e)
|
||||
const deltaY = Math.ceil(normalized.pixelY)
|
||||
const top = this.$tableBody.scrollTop() + deltaY
|
||||
|
||||
if (
|
||||
deltaY < 0 && top > 0 ||
|
||||
deltaY > 0 && top < fixedBody.scrollHeight - fixedBody.clientHeight
|
||||
) {
|
||||
e.preventDefault()
|
||||
}
|
||||
|
||||
this.$tableBody.scrollTop(top)
|
||||
if (this.$fixedBody) {
|
||||
this.$fixedBody.scrollTop(top)
|
||||
}
|
||||
if (this.$fixedBodyRight) {
|
||||
this.$fixedBodyRight.scrollTop(top)
|
||||
}
|
||||
}
|
||||
|
||||
if (this.needFixedColumns && this.options.fixedNumber) {
|
||||
this.$fixedBody.find('tr').hover(e => {
|
||||
toggleHover(e, true)
|
||||
}, e => {
|
||||
toggleHover(e, false)
|
||||
})
|
||||
|
||||
this.$fixedBody[0].addEventListener(mousewheel, e => {
|
||||
updateScroll(e, this.$fixedBody[0])
|
||||
})
|
||||
}
|
||||
|
||||
if (this.needFixedColumns && this.options.fixedRightNumber) {
|
||||
this.$fixedBodyRight.find('tr').hover(e => {
|
||||
toggleHover(e, true)
|
||||
}, e => {
|
||||
toggleHover(e, false)
|
||||
})
|
||||
|
||||
this.$fixedBodyRight.off('scroll').on('scroll', () => {
|
||||
const top = this.$fixedBodyRight.scrollTop()
|
||||
|
||||
this.$tableBody.scrollTop(top)
|
||||
if (this.$fixedBody) {
|
||||
this.$fixedBody.scrollTop(top)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
if (this.options.filterControl) {
|
||||
$(this.$fixedColumns).off('keyup change').on('keyup change', e => {
|
||||
const $target = $(e.target)
|
||||
const value = $target.val()
|
||||
const field = $target.parents('th').data('field')
|
||||
const $coreTh = this.$header.find(`th[data-field="${field}"]`)
|
||||
|
||||
if ($target.is('input')) {
|
||||
$coreTh.find('input').val(value)
|
||||
} else if ($target.is('select')) {
|
||||
const $select = $coreTh.find('select')
|
||||
$select.find('option[selected]').removeAttr('selected')
|
||||
$select.find(`option[value="${value}"]`).attr('selected', true)
|
||||
}
|
||||
|
||||
this.triggerSearch()
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
renderStickyHeader () {
|
||||
if (!this.options.stickyHeader) {
|
||||
return
|
||||
}
|
||||
|
||||
this.$stickyContainer = this.$container.find('.sticky-header-container')
|
||||
super.renderStickyHeader()
|
||||
|
||||
if (this.needFixedColumns && this.options.fixedNumber) {
|
||||
this.$fixedColumns.css('z-index', 101)
|
||||
.find('.sticky-header-container')
|
||||
.css('right', '')
|
||||
.width(this.$fixedColumns.outerWidth())
|
||||
}
|
||||
|
||||
if (this.needFixedColumns && this.options.fixedRightNumber) {
|
||||
const $stickyHeaderContainerRight = this.$fixedColumnsRight.find('.sticky-header-container')
|
||||
|
||||
this.$fixedColumnsRight.css('z-index', 101)
|
||||
$stickyHeaderContainerRight.css('left', '')
|
||||
.scrollLeft($stickyHeaderContainerRight.find('.table').outerWidth())
|
||||
.width(this.$fixedColumnsRight.outerWidth())
|
||||
}
|
||||
}
|
||||
|
||||
matchPositionX () {
|
||||
if (!this.options.stickyHeader) {
|
||||
return
|
||||
}
|
||||
|
||||
this.$stickyContainer.eq(0).scrollLeft(this.$tableBody.scrollLeft())
|
||||
}
|
||||
}
|
||||
Vendored
+25
@@ -0,0 +1,25 @@
|
||||
.fixed-columns,
|
||||
.fixed-columns-right {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
height: 100%;
|
||||
background-color: #fff;
|
||||
box-sizing: border-box;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.fixed-columns {
|
||||
left: 0;
|
||||
|
||||
.fixed-table-body {
|
||||
overflow: hidden!important;
|
||||
}
|
||||
}
|
||||
|
||||
.fixed-columns-right {
|
||||
right: 0;
|
||||
|
||||
.fixed-table-body {
|
||||
overflow-x: hidden!important;
|
||||
}
|
||||
}
|
||||
+277
@@ -0,0 +1,277 @@
|
||||
/**
|
||||
* @author: Yura Knoxville
|
||||
* @version: v1.1.0
|
||||
*/
|
||||
|
||||
let initBodyCaller
|
||||
|
||||
// it only does '%s', and return '' when arguments are undefined
|
||||
const sprintf = function (str) {
|
||||
const args = arguments
|
||||
let flag = true
|
||||
let i = 1
|
||||
|
||||
str = str.replace(/%s/g, () => {
|
||||
const arg = args[i++]
|
||||
|
||||
if (typeof arg === 'undefined') {
|
||||
flag = false
|
||||
return ''
|
||||
}
|
||||
return arg
|
||||
})
|
||||
return flag ? str : ''
|
||||
}
|
||||
|
||||
const groupBy = (array, f) => {
|
||||
const tmpGroups = {}
|
||||
array.forEach(o => {
|
||||
const groups = f(o)
|
||||
tmpGroups[groups] = tmpGroups[groups] || []
|
||||
tmpGroups[groups].push(o)
|
||||
})
|
||||
|
||||
return tmpGroups
|
||||
}
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, {
|
||||
groupBy: false,
|
||||
groupByField: '',
|
||||
groupByFormatter: undefined
|
||||
})
|
||||
|
||||
const Utils = $.fn.bootstrapTable.utils
|
||||
const BootstrapTable = $.fn.bootstrapTable.Constructor
|
||||
const _initSort = BootstrapTable.prototype.initSort
|
||||
const _initBody = BootstrapTable.prototype.initBody
|
||||
const _updateSelected = BootstrapTable.prototype.updateSelected
|
||||
|
||||
BootstrapTable.prototype.initSort = function (...args) {
|
||||
_initSort.apply(this, Array.prototype.slice.apply(args))
|
||||
|
||||
const that = this
|
||||
this.tableGroups = []
|
||||
|
||||
if ((this.options.groupBy) && (this.options.groupByField !== '')) {
|
||||
|
||||
if ((this.options.sortName !== this.options.groupByField)) {
|
||||
if (this.options.customSort) {
|
||||
Utils.calculateObjectValue(this.options, this.options.customSort, [
|
||||
this.options.sortName,
|
||||
this.options.sortOrder,
|
||||
this.data
|
||||
])
|
||||
} else {
|
||||
this.data.sort((a, b) => {
|
||||
const groupByFields = this.getGroupByFields()
|
||||
const fieldValuesA = []
|
||||
const fieldValuesB = []
|
||||
|
||||
$.each(groupByFields, (i, field) => {
|
||||
fieldValuesA.push(a[field])
|
||||
fieldValuesB.push(b[field])
|
||||
})
|
||||
|
||||
a = fieldValuesA.join()
|
||||
b = fieldValuesB.join()
|
||||
return a.localeCompare(b, undefined, {numeric: true})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const groups = groupBy(that.data, (item) => {
|
||||
const groupByFields = this.getGroupByFields()
|
||||
const groupValues = []
|
||||
$.each(groupByFields, (i, field) => {
|
||||
groupValues.push(item[field])
|
||||
})
|
||||
|
||||
return groupValues.join(', ')
|
||||
})
|
||||
|
||||
let index = 0
|
||||
$.each(groups, (key, value) => {
|
||||
this.tableGroups.push({
|
||||
id: index,
|
||||
name: key,
|
||||
data: value
|
||||
})
|
||||
|
||||
value.forEach(item => {
|
||||
if (!item._data) {
|
||||
item._data = {}
|
||||
}
|
||||
|
||||
item._data['parent-index'] = index
|
||||
})
|
||||
|
||||
index++
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
BootstrapTable.prototype.initBody = function (...args) {
|
||||
initBodyCaller = true
|
||||
|
||||
_initBody.apply(this, Array.prototype.slice.apply(args))
|
||||
|
||||
if ((this.options.groupBy) && (this.options.groupByField !== '')) {
|
||||
const that = this
|
||||
let checkBox = false
|
||||
let visibleColumns = 0
|
||||
|
||||
this.columns.forEach(column => {
|
||||
if (column.checkbox) {
|
||||
checkBox = true
|
||||
} else {
|
||||
if (column.visible) {
|
||||
visibleColumns += 1
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
if (this.options.detailView && !this.options.cardView) {
|
||||
visibleColumns += 1
|
||||
}
|
||||
|
||||
this.tableGroups.forEach(item => {
|
||||
const html = []
|
||||
|
||||
html.push(sprintf('<tr class="info groupBy expanded" data-group-index="%s">', item.id))
|
||||
|
||||
if (that.options.detailView && !that.options.cardView) {
|
||||
html.push('<td class="detail"></td>')
|
||||
}
|
||||
|
||||
if (checkBox) {
|
||||
html.push('<td class="bs-checkbox">',
|
||||
'<input name="btSelectGroup" type="checkbox" />',
|
||||
'</td>'
|
||||
)
|
||||
}
|
||||
let formattedValue = item.name
|
||||
if (typeof (that.options.groupByFormatter) === 'function') {
|
||||
formattedValue = that.options.groupByFormatter(item.name, item.id, item.data)
|
||||
}
|
||||
html.push('<td',
|
||||
sprintf(' colspan="%s"', visibleColumns),
|
||||
'>', formattedValue, '</td>'
|
||||
)
|
||||
|
||||
html.push('</tr>')
|
||||
|
||||
that.$body.find(`tr[data-parent-index=${item.id}]:first`).before($(html.join('')))
|
||||
})
|
||||
|
||||
this.$selectGroup = []
|
||||
this.$body.find('[name="btSelectGroup"]').each(function () {
|
||||
const self = $(this)
|
||||
|
||||
that.$selectGroup.push({
|
||||
group: self,
|
||||
item: that.$selectItem.filter(function () {
|
||||
return ($(this).closest('tr').data('parent-index') ===
|
||||
self.closest('tr').data('group-index'))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
this.$container.off('click', '.groupBy')
|
||||
.on('click', '.groupBy', function () {
|
||||
$(this).toggleClass('expanded')
|
||||
that.$body.find(`tr[data-parent-index=${$(this).closest('tr').data('group-index')}]`).toggleClass('hidden')
|
||||
})
|
||||
|
||||
this.$container.off('click', '[name="btSelectGroup"]')
|
||||
.on('click', '[name="btSelectGroup"]', function (event) {
|
||||
event.stopImmediatePropagation()
|
||||
|
||||
const self = $(this)
|
||||
const checked = self.prop('checked')
|
||||
that[checked ? 'checkGroup' : 'uncheckGroup']($(this).closest('tr').data('group-index'))
|
||||
})
|
||||
}
|
||||
|
||||
initBodyCaller = false
|
||||
this.updateSelected()
|
||||
}
|
||||
|
||||
BootstrapTable.prototype.updateSelected = function (...args) {
|
||||
if (!initBodyCaller) {
|
||||
_updateSelected.apply(this, Array.prototype.slice.apply(args))
|
||||
|
||||
if ((this.options.groupBy) && (this.options.groupByField !== '')) {
|
||||
this.$selectGroup.forEach(item => {
|
||||
const checkGroup = item.item.filter(':enabled').length ===
|
||||
item.item.filter(':enabled').filter(':checked').length
|
||||
|
||||
item.group.prop('checked', checkGroup)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BootstrapTable.prototype.checkGroup = function (index) {
|
||||
this.checkGroup_(index, true)
|
||||
}
|
||||
|
||||
BootstrapTable.prototype.uncheckGroup = function (index) {
|
||||
this.checkGroup_(index, false)
|
||||
}
|
||||
|
||||
BootstrapTable.prototype.checkGroup_ = function (index, checked) {
|
||||
const rowsBefore = this.getSelections()
|
||||
let rows
|
||||
const filter = function () {
|
||||
return ($(this).closest('tr').data('parent-index') === index)
|
||||
}
|
||||
|
||||
this.$selectItem.filter(filter).prop('checked', checked)
|
||||
|
||||
this.updateRows()
|
||||
this.updateSelected()
|
||||
const rowsAfter = this.getSelections()
|
||||
if (checked) {
|
||||
this.trigger('check-all', rowsAfter, rowsBefore)
|
||||
return
|
||||
}
|
||||
|
||||
this.trigger('uncheck-all', rowsAfter, rowsBefore)
|
||||
}
|
||||
|
||||
BootstrapTable.prototype.getGroupByFields = function () {
|
||||
let groupByFields = this.options.groupByField
|
||||
if (!$.isArray(this.options.groupByField)) {
|
||||
groupByFields = [this.options.groupByField]
|
||||
}
|
||||
|
||||
return groupByFields
|
||||
}
|
||||
|
||||
$.BootstrapTable = class extends $.BootstrapTable {
|
||||
scrollTo (params) {
|
||||
if (this.options.groupBy) {
|
||||
let options = {unit: 'px', value: 0}
|
||||
if (typeof params === 'object') {
|
||||
options = Object.assign(options, params)
|
||||
}
|
||||
|
||||
if (options.unit === 'rows') {
|
||||
let scrollTo = 0
|
||||
this.$body.find(`> tr:lt(${options.value})`).each((i, el) => {
|
||||
scrollTo += $(el).outerHeight(true)
|
||||
})
|
||||
|
||||
const $targetColumn = this.$body.find(`> tr:not(.groupBy):eq(${options.value})`)
|
||||
$targetColumn.prevAll('.groupBy').each((i, el) => {
|
||||
scrollTo += $(el).outerHeight(true)
|
||||
})
|
||||
|
||||
this.$tableBody.scrollTop(scrollTo)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
super.scrollTo(params)
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
.bootstrap-table .table > tbody > tr.groupBy {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.bootstrap-table .table > tbody > tr.hidden + tr.detail-view {
|
||||
display: none;
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "Group By V2",
|
||||
"version": "1.0.0",
|
||||
"description": "Group the data by field",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/group-by-v2",
|
||||
"example": "",
|
||||
"plugins": [],
|
||||
"author": {
|
||||
"name": "Knoxvillekm",
|
||||
"image": "https://avatars3.githubusercontent.com/u/11072464"
|
||||
}
|
||||
}
|
||||
Vendored
+31
@@ -0,0 +1,31 @@
|
||||
/**
|
||||
* @author: Jewway
|
||||
* @update zhixin wen <wenzhixin2010@gmail.com>
|
||||
*/
|
||||
|
||||
$.fn.bootstrapTable.methods.push('changeTitle')
|
||||
$.fn.bootstrapTable.methods.push('changeLocale')
|
||||
|
||||
$.BootstrapTable = class extends $.BootstrapTable {
|
||||
|
||||
changeTitle (locale) {
|
||||
$.each(this.options.columns, (idx, columnList) => {
|
||||
$.each(columnList, (idx, column) => {
|
||||
if (column.field) {
|
||||
column.title = locale[column.field]
|
||||
}
|
||||
})
|
||||
})
|
||||
this.initHeader()
|
||||
this.initBody()
|
||||
this.initToolbar()
|
||||
}
|
||||
|
||||
changeLocale (localeId) {
|
||||
this.options.locale = localeId
|
||||
this.initLocale()
|
||||
this.initPagination()
|
||||
this.initBody()
|
||||
this.initToolbar()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "i18n Enhance",
|
||||
"version": "1.0.0",
|
||||
"description": "Plugin to add i18n API in order to change column's title and table locale.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/i18n-enhance",
|
||||
"example": "http://issues.wenzhixin.net.cn/bootstrap-table/#extensions/i18n-enhance.html",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-i18n-enhance",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/i18n-enhance"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "Jewway",
|
||||
"image": "https://avatars0.githubusercontent.com/u/3501899"
|
||||
}
|
||||
}
|
||||
+74
@@ -0,0 +1,74 @@
|
||||
/**
|
||||
* @author: Dennis Hernández
|
||||
* @webSite: http://djhvscf.github.io/Blog
|
||||
* @update zhixin wen <wenzhixin2010@gmail.com>
|
||||
*/
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, {
|
||||
keyEvents: false
|
||||
})
|
||||
|
||||
$.BootstrapTable = class extends $.BootstrapTable {
|
||||
|
||||
init (...args) {
|
||||
super.init(...args)
|
||||
|
||||
if (this.options.keyEvents) {
|
||||
this.initKeyEvents()
|
||||
}
|
||||
}
|
||||
|
||||
initKeyEvents () {
|
||||
$(document).off('keydown').on('keydown', e => {
|
||||
const $search = this.$toolbar.find('.search input')
|
||||
const $refresh = this.$toolbar.find('button[name="refresh"]')
|
||||
const $toggle = this.$toolbar.find('button[name="toggle"]')
|
||||
const $paginationSwitch = this.$toolbar.find('button[name="paginationSwitch"]')
|
||||
|
||||
if (document.activeElement === $search.get(0) || !$.contains(document.activeElement ,this.$toolbar.get(0))) {
|
||||
return true
|
||||
}
|
||||
|
||||
switch (e.keyCode) {
|
||||
case 83: // s
|
||||
if (!this.options.search) {
|
||||
return
|
||||
}
|
||||
$search.focus()
|
||||
return false
|
||||
case 82: // r
|
||||
if (!this.options.showRefresh) {
|
||||
return
|
||||
}
|
||||
$refresh.click()
|
||||
return false
|
||||
case 84: // t
|
||||
if (!this.options.showToggle) {
|
||||
return
|
||||
}
|
||||
$toggle.click()
|
||||
return false
|
||||
case 80: // p
|
||||
if (!this.options.showPaginationSwitch) {
|
||||
return
|
||||
}
|
||||
$paginationSwitch.click()
|
||||
return false
|
||||
case 37: // left
|
||||
if (!this.options.pagination) {
|
||||
return
|
||||
}
|
||||
this.prevPage()
|
||||
return false
|
||||
case 39: // right
|
||||
if (!this.options.pagination) {
|
||||
return
|
||||
}
|
||||
this.nextPage()
|
||||
return
|
||||
default:
|
||||
break
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "Key Events",
|
||||
"version": "1.0.0",
|
||||
"description": "Plugin to support the key events in the bootstrap table.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/key-events",
|
||||
"example": "http://issues.wenzhixin.net.cn/bootstrap-table/#extensions/key-events.html",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-key-events",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/key-events"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "djhvscf",
|
||||
"image": "https://avatars1.githubusercontent.com/u/4496763"
|
||||
}
|
||||
}
|
||||
+123
@@ -0,0 +1,123 @@
|
||||
/**
|
||||
* @author: Dennis Hernández
|
||||
* @webSite: http://djhvscf.github.io/Blog
|
||||
* @update zhixin wen <wenzhixin2010@gmail.com>
|
||||
*/
|
||||
|
||||
const debounce = (func, wait) => {
|
||||
let timeout = 0
|
||||
return (...args) => {
|
||||
const later = () => {
|
||||
timeout = 0
|
||||
func(...args)
|
||||
}
|
||||
clearTimeout(timeout)
|
||||
timeout = setTimeout(later, wait)
|
||||
}
|
||||
}
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, {
|
||||
mobileResponsive: false,
|
||||
minWidth: 562,
|
||||
minHeight: undefined,
|
||||
heightThreshold: 100, // just slightly larger than mobile chrome's auto-hiding toolbar
|
||||
checkOnInit: true,
|
||||
columnsHidden: []
|
||||
})
|
||||
|
||||
$.BootstrapTable = class extends $.BootstrapTable {
|
||||
init (...args) {
|
||||
super.init(...args)
|
||||
|
||||
if (!this.options.mobileResponsive || !this.options.minWidth) {
|
||||
return
|
||||
}
|
||||
|
||||
if (this.options.minWidth < 100 && this.options.resizable) {
|
||||
console.info('The minWidth when the resizable extension is active should be greater or equal than 100')
|
||||
this.options.minWidth = 100
|
||||
}
|
||||
|
||||
let old = {
|
||||
width: $(window).width(),
|
||||
height: $(window).height()
|
||||
}
|
||||
|
||||
$(window).on('resize orientationchange', debounce(() => {
|
||||
// reset view if height has only changed by at least the threshold.
|
||||
const width = $(window).width()
|
||||
const height = $(window).height()
|
||||
const $activeElement = $(document.activeElement)
|
||||
|
||||
if ($activeElement.length && ['INPUT', 'SELECT', 'TEXTAREA'].includes($activeElement.prop('nodeName'))) {
|
||||
return
|
||||
}
|
||||
|
||||
if (
|
||||
Math.abs(old.height - height) > this.options.heightThreshold ||
|
||||
old.width !== width
|
||||
) {
|
||||
this.changeView(width, height)
|
||||
old = {
|
||||
width,
|
||||
height
|
||||
}
|
||||
}
|
||||
}, 200))
|
||||
|
||||
if (this.options.checkOnInit) {
|
||||
const width = $(window).width()
|
||||
const height = $(window).height()
|
||||
this.changeView(width, height)
|
||||
old = {
|
||||
width,
|
||||
height
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
conditionCardView () {
|
||||
this.changeTableView(false)
|
||||
this.showHideColumns(false)
|
||||
}
|
||||
|
||||
conditionFullView () {
|
||||
this.changeTableView(true)
|
||||
this.showHideColumns(true)
|
||||
}
|
||||
|
||||
changeTableView (cardViewState) {
|
||||
this.options.cardView = cardViewState
|
||||
this.toggleView()
|
||||
}
|
||||
|
||||
showHideColumns (checked) {
|
||||
if (this.options.columnsHidden.length > 0) {
|
||||
this.columns.forEach(column => {
|
||||
if (this.options.columnsHidden.includes(column.field)) {
|
||||
if (column.visible !== checked) {
|
||||
this._toggleColumn(this.fieldsColumnsIndex[column.field], checked, true)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
changeView (width, height) {
|
||||
if (this.options.minHeight) {
|
||||
if ((width <= this.options.minWidth) && (height <= this.options.minHeight)) {
|
||||
this.conditionCardView()
|
||||
} else if ((width > this.options.minWidth) && (height > this.options.minHeight)) {
|
||||
this.conditionFullView()
|
||||
}
|
||||
} else {
|
||||
if (width <= this.options.minWidth) {
|
||||
this.conditionCardView()
|
||||
} else if (width > this.options.minWidth) {
|
||||
this.conditionFullView()
|
||||
}
|
||||
}
|
||||
|
||||
this.resetView()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "Mobile",
|
||||
"version": "1.1.0",
|
||||
"description": "Plugin to support the responsive feature.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/mobile",
|
||||
"example": "http://issues.wenzhixin.net.cn/bootstrap-table/#extensions/mobile.html",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-mobile",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/mobile"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "djhvscf",
|
||||
"image": "https://avatars1.githubusercontent.com/u/4496763"
|
||||
}
|
||||
}
|
||||
Vendored
+728
@@ -0,0 +1,728 @@
|
||||
/**
|
||||
* @author Nadim Basalamah <dimbslmh@gmail.com>
|
||||
* @version: v1.1.0
|
||||
* https://github.com/dimbslmh/bootstrap-table/tree/master/src/extensions/multiple-sort/bootstrap-table-multiple-sort.js
|
||||
* Modification: ErwannNevou <https://github.com/ErwannNevou>
|
||||
*/
|
||||
|
||||
let isSingleSort = false
|
||||
const Utils = $.fn.bootstrapTable.utils
|
||||
const bootstrap = {
|
||||
bootstrap3: {
|
||||
icons: {
|
||||
plus: 'glyphicon-plus',
|
||||
minus: 'glyphicon-minus',
|
||||
sort: 'glyphicon-sort'
|
||||
},
|
||||
html: {
|
||||
multipleSortModal: `
|
||||
<div class="modal fade" id="%s" tabindex="-1" role="dialog" aria-labelledby="%sLabel" aria-hidden="true">
|
||||
<div class="modal-dialog">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
|
||||
<h4 class="modal-title" id="%sLabel">%s</h4>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="bootstrap-table">
|
||||
<div class="fixed-table-toolbar">
|
||||
<div class="bars">
|
||||
<div id="toolbar">
|
||||
<button id="add" type="button" class="btn btn-default">%s %s</button>
|
||||
<button id="delete" type="button" class="btn btn-default" disabled>%s %s</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fixed-table-container">
|
||||
<table id="multi-sort" class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th><div class="th-inner">%s</div></th>
|
||||
<th><div class="th-inner">%s</div></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-default" data-dismiss="modal">%s</button>
|
||||
<button type="button" class="btn btn-primary multi-sort-order-button">%s</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
multipleSortButton: '<button class="multi-sort btn btn-default" type="button" data-toggle="modal" data-target="#%s" title="%s">%s</button>',
|
||||
multipleSortSelect: '<select class="%s %s form-control">'
|
||||
}
|
||||
},
|
||||
bootstrap4: {
|
||||
icons: {
|
||||
'plus': 'fa-plus',
|
||||
'minus': 'fa-minus',
|
||||
'sort': 'fa-sort'
|
||||
},
|
||||
html: {
|
||||
multipleSortModal: `
|
||||
<div class="modal fade" id="%s" tabindex="-1" role="dialog" aria-labelledby="%sLabel" aria-hidden="true">
|
||||
<div class="modal-dialog" role="document">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="%sLabel">%s</h5>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="bootstrap-table">
|
||||
<div class="fixed-table-toolbar">
|
||||
<div class="bars">
|
||||
<div id="toolbar" class="pb-3">
|
||||
<button id="add" type="button" class="btn btn-secondary">%s %s</button>
|
||||
<button id="delete" type="button" class="btn btn-secondary" disabled>%s %s</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fixed-table-container">
|
||||
<table id="multi-sort" class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th><div class="th-inner">%s</div></th>
|
||||
<th><div class="th-inner">%s</div></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" class="btn btn-secondary" data-dismiss="modal">%s</button>
|
||||
<button type="button" class="btn btn-primary multi-sort-order-button">%s</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
multipleSortButton: '<button class="multi-sort btn btn-secondary" type="button" data-toggle="modal" data-target="#%s" title="%s">%s</button>',
|
||||
multipleSortSelect: '<select class="%s %s form-control">'
|
||||
}
|
||||
},
|
||||
semantic: {
|
||||
icons: {
|
||||
'plus': 'fa-plus',
|
||||
'minus': 'fa-minus',
|
||||
'sort': 'fa-sort'
|
||||
},
|
||||
html: {
|
||||
multipleSortModal: `
|
||||
<div class="ui modal tiny" id="%s" aria-labelledby="%sLabel" aria-hidden="true">
|
||||
<i class="close icon"></i>
|
||||
<div class="header" id="%sLabel">
|
||||
%s
|
||||
</div>
|
||||
<div class="image content">
|
||||
<div class="bootstrap-table">
|
||||
<div class="fixed-table-toolbar">
|
||||
<div class="bars">
|
||||
<div id="toolbar" class="pb-3">
|
||||
<button id="add" type="button" class="ui button">%s %s</button>
|
||||
<button id="delete" type="button" class="ui button" disabled>%s %s</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fixed-table-container">
|
||||
<table id="multi-sort" class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th><div class="th-inner">%s</div></th>
|
||||
<th><div class="th-inner">%s</div></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<div class="ui button deny">%s</div>
|
||||
<div class="ui button approve multi-sort-order-button">%s</div>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
multipleSortButton: '<button class="multi-sort ui button" type="button" data-toggle="modal" data-target="#%s" title="%s">%s</button>',
|
||||
multipleSortSelect: '<select class="%s %s">'
|
||||
}
|
||||
},
|
||||
materialize: {
|
||||
icons: {
|
||||
'plus': 'plus',
|
||||
'minus': 'minus',
|
||||
'sort': 'sort'
|
||||
},
|
||||
html: {
|
||||
multipleSortModal: `
|
||||
<div id="%s" class="modal" aria-labelledby="%sLabel" aria-hidden="true">
|
||||
<div class="modal-content" id="%sLabel">
|
||||
<h4>%s</h4>
|
||||
<div class="bootstrap-table">
|
||||
<div class="fixed-table-toolbar">
|
||||
<div class="bars">
|
||||
<div id="toolbar" class="pb-3">
|
||||
<button id="add" type="button" class="waves-effect waves-light btn">%s %s</button>
|
||||
<button id="delete" type="button" class="waves-effect waves-light btn" disabled>%s %s</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fixed-table-container">
|
||||
<table id="multi-sort" class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th><div class="th-inner">%s</div></th>
|
||||
<th><div class="th-inner">%s</div></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a href="javascript:void(0)" class="modal-close waves-effect waves-light btn">%s</a>
|
||||
<a href="javascript:void(0)" class="modal-close waves-effect waves-light btn multi-sort-order-button">%s</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
multipleSortButton: '<a href="#%s" class="multi-sort waves-effect waves-light btn modal-trigger" type="button" data-toggle="modal" title="%s">%s</a>',
|
||||
multipleSortSelect: '<select class="%s %s browser-default">'
|
||||
}
|
||||
},
|
||||
foundation: {
|
||||
icons: {
|
||||
'plus': 'fa-plus',
|
||||
'minus': 'fa-minus',
|
||||
'sort': 'fa-sort'
|
||||
},
|
||||
html: {
|
||||
multipleSortModal: `
|
||||
<div class="reveal" id="%s" data-reveal aria-labelledby="%sLabel" aria-hidden="true">
|
||||
<div id="%sLabel">
|
||||
<h1>%s</h1>
|
||||
<div class="bootstrap-table">
|
||||
<div class="fixed-table-toolbar">
|
||||
<div class="bars">
|
||||
<div id="toolbar" class="padding-bottom-2">
|
||||
<button id="add" type="button" class="waves-effect waves-light button">%s %s</button>
|
||||
<button id="delete" type="button" class="waves-effect waves-light button" disabled>%s %s</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fixed-table-container">
|
||||
<table id="multi-sort" class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th><div class="th-inner">%s</div></th>
|
||||
<th><div class="th-inner">%s</div></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="waves-effect waves-light button" data-close aria-label="Close modal" type="button">
|
||||
<span aria-hidden="true">%s</span>
|
||||
</button>
|
||||
<button class="waves-effect waves-light button multi-sort-order-button" data-close aria-label="Order" type="button">
|
||||
<span aria-hidden="true">%s</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
multipleSortButton: '<button class="button multi-sort" data-open="%s" title="%s">%s</button>',
|
||||
multipleSortSelect: '<select class="%s %s browser-default">'
|
||||
}
|
||||
},
|
||||
bulma: {
|
||||
icons: {
|
||||
'plus': 'fa-plus',
|
||||
'minus': 'fa-minus',
|
||||
'sort': 'fa-sort'
|
||||
},
|
||||
html: {
|
||||
multipleSortModal: `
|
||||
<div class="modal" id="%s" aria-labelledby="%sLabel" aria-hidden="true">
|
||||
<div class="modal-background"></div>
|
||||
<div class="modal-content" id="%sLabel">
|
||||
<div class="box">
|
||||
<h2>%s</h2>
|
||||
<div class="bootstrap-table">
|
||||
<div class="fixed-table-toolbar">
|
||||
<div class="bars">
|
||||
<div id="toolbar" class="padding-bottom-2">
|
||||
<button id="add" type="button" class="waves-effect waves-light button">%s %s</button>
|
||||
<button id="delete" type="button" class="waves-effect waves-light button" disabled>%s %s</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="fixed-table-container">
|
||||
<table id="multi-sort" class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th></th>
|
||||
<th><div class="th-inner">%s</div></th>
|
||||
<th><div class="th-inner">%s</div></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<button type="button" class="waves-effect waves-light button" data-close>%s</button>
|
||||
<button type="button" class="waves-effect waves-light button multi-sort-order-button" data-close>%s</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`,
|
||||
multipleSortButton: '<button class="button multi-sort" data-target="%s" title="%s">%s</button>',
|
||||
multipleSortSelect: '<select class="%s %s browser-default">'
|
||||
}
|
||||
}
|
||||
}[$.fn.bootstrapTable.theme]
|
||||
$.extend($.fn.bootstrapTable.defaults.icons, bootstrap.icons)
|
||||
$.extend($.fn.bootstrapTable.defaults.html, bootstrap.html)
|
||||
|
||||
const showSortModal = that => {
|
||||
const _selector = that.sortModalSelector
|
||||
const _id = `#${_selector}`
|
||||
const o = that.options
|
||||
|
||||
if (!$(_id).hasClass('modal')) {
|
||||
const sModal = Utils.sprintf(
|
||||
that.constants.html.multipleSortModal,
|
||||
_selector, _selector, _selector,
|
||||
that.options.formatMultipleSort(),
|
||||
Utils.sprintf(that.constants.html.icon, o.iconsPrefix, that.constants.icons.plus),
|
||||
that.options.formatAddLevel(),
|
||||
Utils.sprintf(that.constants.html.icon, o.iconsPrefix, that.constants.icons.minus),
|
||||
that.options.formatDeleteLevel(),
|
||||
that.options.formatColumn(),
|
||||
that.options.formatOrder(),
|
||||
that.options.formatCancel(),
|
||||
that.options.formatSort()
|
||||
)
|
||||
|
||||
$('body').append($(sModal))
|
||||
|
||||
that.$sortModal = $(_id)
|
||||
const $rows = that.$sortModal.find('tbody > tr')
|
||||
|
||||
that.$sortModal.off('click', '#add').on('click', '#add', () => {
|
||||
const total = that.$sortModal.find('.multi-sort-name:first option').length
|
||||
let current = that.$sortModal.find('tbody tr').length
|
||||
|
||||
if (current < total) {
|
||||
current++
|
||||
that.addLevel()
|
||||
that.setButtonStates()
|
||||
}
|
||||
})
|
||||
|
||||
that.$sortModal.off('click', '#delete').on('click', '#delete', () => {
|
||||
const total = that.$sortModal.find('.multi-sort-name:first option').length
|
||||
let current = that.$sortModal.find('tbody tr').length
|
||||
|
||||
if (current > 1 && current <= total) {
|
||||
current--
|
||||
that.$sortModal.find('tbody tr:last').remove()
|
||||
that.setButtonStates()
|
||||
}
|
||||
})
|
||||
|
||||
that.$sortModal.off('click', '.multi-sort-order-button').on('click', '.multi-sort-order-button', () => {
|
||||
const $rows = that.$sortModal.find('tbody > tr')
|
||||
let $alert = that.$sortModal.find('div.alert')
|
||||
const fields = []
|
||||
const results = []
|
||||
|
||||
const sortPriority = $.map($rows, row => {
|
||||
const $row = $(row)
|
||||
const name = $row.find('.multi-sort-name').val()
|
||||
const order = $row.find('.multi-sort-order').val()
|
||||
|
||||
fields.push(name)
|
||||
|
||||
return {
|
||||
sortName: name,
|
||||
sortOrder: order
|
||||
}
|
||||
})
|
||||
|
||||
const sorted_fields = fields.sort()
|
||||
|
||||
for (let i = 0; i < fields.length - 1; i++) {
|
||||
if (sorted_fields[i + 1] === sorted_fields[i]) {
|
||||
results.push(sorted_fields[i])
|
||||
}
|
||||
}
|
||||
|
||||
if (results.length > 0) {
|
||||
if ($alert.length === 0) {
|
||||
$alert = `<div class="alert alert-danger" role="alert"><strong>${that.options.formatDuplicateAlertTitle()}</strong> ${that.options.formatDuplicateAlertDescription()}</div>`
|
||||
$($alert).insertBefore(that.$sortModal.find('.bars'))
|
||||
}
|
||||
} else {
|
||||
if ($alert.length === 1) {
|
||||
$($alert).remove()
|
||||
}
|
||||
|
||||
if ($.inArray($.fn.bootstrapTable.theme, ['bootstrap3', 'bootstrap4']) !== -1) {
|
||||
that.$sortModal.modal('hide')
|
||||
}
|
||||
|
||||
that.multiSort(sortPriority)
|
||||
}
|
||||
})
|
||||
|
||||
if (that.options.sortPriority === null || that.options.sortPriority.length === 0) {
|
||||
if (that.options.sortName) {
|
||||
that.options.sortPriority = [{
|
||||
sortName: that.options.sortName,
|
||||
sortOrder: that.options.sortOrder
|
||||
}]
|
||||
}
|
||||
}
|
||||
|
||||
if (that.options.sortPriority !== null && that.options.sortPriority.length > 0) {
|
||||
if ($rows.length < that.options.sortPriority.length && typeof that.options.sortPriority === 'object') {
|
||||
for (let i = 0; i < that.options.sortPriority.length; i++) {
|
||||
that.addLevel(i, that.options.sortPriority[i])
|
||||
}
|
||||
}
|
||||
} else {
|
||||
that.addLevel(0)
|
||||
}
|
||||
|
||||
that.setButtonStates()
|
||||
}
|
||||
}
|
||||
|
||||
$.fn.bootstrapTable.methods.push('multipleSort')
|
||||
$.fn.bootstrapTable.methods.push('multiSort')
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, {
|
||||
showMultiSort: false,
|
||||
showMultiSortButton: true,
|
||||
multiSortStrictSort: false,
|
||||
sortPriority: null,
|
||||
onMultipleSort () {
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
$.extend($.fn.bootstrapTable.Constructor.EVENTS, {
|
||||
'multiple-sort.bs.table': 'onMultipleSort'
|
||||
})
|
||||
|
||||
$.extend($.fn.bootstrapTable.locales, {
|
||||
formatMultipleSort () {
|
||||
return 'Multiple Sort'
|
||||
},
|
||||
formatAddLevel () {
|
||||
return 'Add Level'
|
||||
},
|
||||
formatDeleteLevel () {
|
||||
return 'Delete Level'
|
||||
},
|
||||
formatColumn () {
|
||||
return 'Column'
|
||||
},
|
||||
formatOrder () {
|
||||
return 'Order'
|
||||
},
|
||||
formatSortBy () {
|
||||
return 'Sort by'
|
||||
},
|
||||
formatThenBy () {
|
||||
return 'Then by'
|
||||
},
|
||||
formatSort () {
|
||||
return 'Sort'
|
||||
},
|
||||
formatCancel () {
|
||||
return 'Cancel'
|
||||
},
|
||||
formatDuplicateAlertTitle () {
|
||||
return 'Duplicate(s) detected!'
|
||||
},
|
||||
formatDuplicateAlertDescription () {
|
||||
return 'Please remove or change any duplicate column.'
|
||||
},
|
||||
formatSortOrders () {
|
||||
return {
|
||||
asc: 'Ascending',
|
||||
desc: 'Descending'
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, $.fn.bootstrapTable.locales)
|
||||
|
||||
const BootstrapTable = $.fn.bootstrapTable.Constructor
|
||||
const _initToolbar = BootstrapTable.prototype.initToolbar
|
||||
const _destroy = BootstrapTable.prototype.destroy
|
||||
|
||||
BootstrapTable.prototype.initToolbar = function (...args) {
|
||||
this.showToolbar = this.showToolbar || this.options.showMultiSort
|
||||
const that = this
|
||||
const sortModalSelector = `sortModal_${this.$el.attr('id')}`
|
||||
const sortModalId = `#${sortModalSelector}`
|
||||
this.$sortModal = $(sortModalId)
|
||||
this.sortModalSelector = sortModalSelector
|
||||
|
||||
if (that.options.sortPriority !== null) {
|
||||
that.onMultipleSort()
|
||||
}
|
||||
|
||||
_initToolbar.apply(this, Array.prototype.slice.apply(args))
|
||||
|
||||
if (that.options.sidePagination === 'server' && !isSingleSort && that.options.sortPriority !== null) {
|
||||
const t = that.options.queryParams
|
||||
that.options.queryParams = params => {
|
||||
params.multiSort = that.options.sortPriority
|
||||
return t(params)
|
||||
}
|
||||
}
|
||||
|
||||
if (this.options.showMultiSort) {
|
||||
const $btnGroup = this.$toolbar.find('>.' + that.constants.classes.buttonsGroup.split(' ').join('.')).first()
|
||||
let $multiSortBtn = this.$toolbar.find('div.multi-sort')
|
||||
const o = that.options
|
||||
|
||||
if (!$multiSortBtn.length && this.options.showMultiSortButton) {
|
||||
$multiSortBtn = Utils.sprintf(that.constants.html.multipleSortButton, that.sortModalSelector, this.options.formatMultipleSort(), Utils.sprintf(that.constants.html.icon, o.iconsPrefix, o.icons.sort))
|
||||
$btnGroup.append($multiSortBtn)
|
||||
|
||||
if ($.fn.bootstrapTable.theme === 'semantic') {
|
||||
this.$toolbar.find('.multi-sort').on('click', () => {
|
||||
$(sortModalId).modal('show')
|
||||
})
|
||||
} else if ($.fn.bootstrapTable.theme === 'materialize') {
|
||||
this.$toolbar.find('.multi-sort').on('click', () => {
|
||||
$(sortModalId).modal()
|
||||
})
|
||||
} else if ($.fn.bootstrapTable.theme === 'foundation') {
|
||||
this.$toolbar.find('.multi-sort').on('click', () => {
|
||||
if (!this.foundationModal) {
|
||||
// eslint-disable-next-line no-undef
|
||||
this.foundationModal = new Foundation.Reveal($(sortModalId))
|
||||
}
|
||||
this.foundationModal.open()
|
||||
})
|
||||
} else if ($.fn.bootstrapTable.theme === 'bulma') {
|
||||
this.$toolbar.find('.multi-sort').on('click', () => {
|
||||
$('html').toggleClass('is-clipped')
|
||||
$(sortModalId).toggleClass('is-active')
|
||||
$('button[data-close]').one('click', () => {
|
||||
$('html').toggleClass('is-clipped')
|
||||
$(sortModalId).toggleClass('is-active')
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
showSortModal(that)
|
||||
}
|
||||
|
||||
this.$el.on('sort.bs.table', () => {
|
||||
isSingleSort = true
|
||||
})
|
||||
|
||||
this.$el.on('multiple-sort.bs.table', () => {
|
||||
isSingleSort = false
|
||||
})
|
||||
|
||||
this.$el.on('load-success.bs.table', () => {
|
||||
if (!isSingleSort && that.options.sortPriority !== null && typeof that.options.sortPriority === 'object' && that.options.sidePagination !== 'server') {
|
||||
that.onMultipleSort()
|
||||
}
|
||||
})
|
||||
|
||||
this.$el.on('column-switch.bs.table', (field, checked) => {
|
||||
for (let i = 0; i < that.options.sortPriority.length; i++) {
|
||||
if (that.options.sortPriority[i].sortName === checked) {
|
||||
that.options.sortPriority.splice(i, 1)
|
||||
}
|
||||
}
|
||||
|
||||
that.assignSortableArrows()
|
||||
that.$sortModal.remove()
|
||||
showSortModal(that)
|
||||
})
|
||||
|
||||
this.$el.on('reset-view.bs.table', () => {
|
||||
if (!isSingleSort && that.options.sortPriority !== null && typeof that.options.sortPriority === 'object') {
|
||||
that.assignSortableArrows()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
BootstrapTable.prototype.destroy = function (...args) {
|
||||
_destroy.apply(this, Array.prototype.slice.apply(args))
|
||||
|
||||
if (this.options.showMultiSort) {
|
||||
this.$sortModal.remove()
|
||||
}
|
||||
}
|
||||
|
||||
BootstrapTable.prototype.multipleSort = function () {
|
||||
const that = this
|
||||
if (!isSingleSort && that.options.sortPriority !== null && typeof that.options.sortPriority === 'object' && that.options.sidePagination !== 'server') {
|
||||
that.onMultipleSort()
|
||||
}
|
||||
}
|
||||
|
||||
BootstrapTable.prototype.onMultipleSort = function () {
|
||||
const that = this
|
||||
|
||||
const cmp = (x, y) => x > y ? 1 : x < y ? -1 : 0
|
||||
|
||||
const arrayCmp = (a, b) => {
|
||||
const arr1 = []
|
||||
const arr2 = []
|
||||
|
||||
for (let i = 0; i < that.options.sortPriority.length; i++) {
|
||||
let fieldName = that.options.sortPriority[i].sortName
|
||||
const fieldIndex = that.header.fields.indexOf(fieldName)
|
||||
const sorterName = that.header.sorters[that.header.fields.indexOf(fieldName)]
|
||||
|
||||
if (that.header.sortNames[fieldIndex]) {
|
||||
fieldName = that.header.sortNames[fieldIndex]
|
||||
}
|
||||
|
||||
const order = that.options.sortPriority[i].sortOrder === 'desc' ? -1 : 1
|
||||
let aa = Utils.getItemField(a, fieldName)
|
||||
let bb = Utils.getItemField(b, fieldName)
|
||||
const value1 = $.fn.bootstrapTable.utils.calculateObjectValue(that.header, sorterName, [aa, bb])
|
||||
const value2 = $.fn.bootstrapTable.utils.calculateObjectValue(that.header, sorterName, [bb, aa])
|
||||
|
||||
if (value1 !== undefined && value2 !== undefined) {
|
||||
arr1.push(order * value1)
|
||||
arr2.push(order * value2)
|
||||
continue
|
||||
}
|
||||
|
||||
if (aa === undefined || aa === null) aa = ''
|
||||
if (bb === undefined || bb === null) bb = ''
|
||||
|
||||
if ($.isNumeric(aa) && $.isNumeric(bb)) {
|
||||
aa = parseFloat(aa)
|
||||
bb = parseFloat(bb)
|
||||
} else {
|
||||
aa = aa.toString()
|
||||
bb = bb.toString()
|
||||
|
||||
if (that.options.multiSortStrictSort) {
|
||||
aa = aa.toLowerCase()
|
||||
bb = bb.toLowerCase()
|
||||
}
|
||||
}
|
||||
|
||||
arr1.push(order * cmp(aa, bb))
|
||||
arr2.push(order * cmp(bb, aa))
|
||||
}
|
||||
|
||||
return cmp(arr1, arr2)
|
||||
}
|
||||
|
||||
this.data.sort((a, b) => arrayCmp(a, b))
|
||||
|
||||
this.initBody()
|
||||
this.assignSortableArrows()
|
||||
this.trigger('multiple-sort')
|
||||
}
|
||||
|
||||
BootstrapTable.prototype.addLevel = function (index, sortPriority) {
|
||||
const text = index === 0 ? this.options.formatSortBy() : this.options.formatThenBy()
|
||||
|
||||
this.$sortModal.find('tbody')
|
||||
.append($('<tr>')
|
||||
.append($('<td>').text(text))
|
||||
.append($('<td>').append($(Utils.sprintf(this.constants.html.multipleSortSelect, this.constants.classes.paginationDropdown, 'multi-sort-name'))))
|
||||
.append($('<td>').append($(Utils.sprintf(this.constants.html.multipleSortSelect, this.constants.classes.paginationDropdown, 'multi-sort-order'))))
|
||||
)
|
||||
|
||||
const $multiSortName = this.$sortModal.find('.multi-sort-name').last()
|
||||
const $multiSortOrder = this.$sortModal.find('.multi-sort-order').last()
|
||||
|
||||
$.each(this.columns, (i, column) => {
|
||||
if (column.sortable === false || column.visible === false) {
|
||||
return true
|
||||
}
|
||||
$multiSortName.append(`<option value="${column.field}">${column.title}</option>`)
|
||||
})
|
||||
|
||||
$.each(this.options.formatSortOrders(), (value, order) => {
|
||||
$multiSortOrder.append(`<option value="${value}">${order}</option>`)
|
||||
})
|
||||
|
||||
if (sortPriority !== undefined) {
|
||||
$multiSortName.find(`option[value="${sortPriority.sortName}"]`).attr('selected', true)
|
||||
$multiSortOrder.find(`option[value="${sortPriority.sortOrder}"]`).attr('selected', true)
|
||||
}
|
||||
}
|
||||
|
||||
BootstrapTable.prototype.assignSortableArrows = function () {
|
||||
const that = this
|
||||
const headers = that.$header.find('th')
|
||||
|
||||
for (let i = 0; i < headers.length; i++) {
|
||||
for (let c = 0; c < that.options.sortPriority.length; c++) {
|
||||
if ($(headers[i]).data('field') === that.options.sortPriority[c].sortName) {
|
||||
$(headers[i]).find('.sortable').removeClass('desc asc').addClass(that.options.sortPriority[c].sortOrder)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BootstrapTable.prototype.setButtonStates = function () {
|
||||
const total = this.$sortModal.find('.multi-sort-name:first option').length
|
||||
const current = this.$sortModal.find('tbody tr').length
|
||||
|
||||
if (current === total) {
|
||||
this.$sortModal.find('#add').attr('disabled', 'disabled')
|
||||
}
|
||||
if (current > 1) {
|
||||
this.$sortModal.find('#delete').removeAttr('disabled')
|
||||
}
|
||||
if (current < total) {
|
||||
this.$sortModal.find('#add').removeAttr('disabled')
|
||||
}
|
||||
if (current === 1) {
|
||||
this.$sortModal.find('#delete').attr('disabled', 'disabled')
|
||||
}
|
||||
}
|
||||
|
||||
BootstrapTable.prototype.multiSort = function (sortPriority) {
|
||||
this.options.sortPriority = sortPriority
|
||||
this.options.sortName = ''
|
||||
|
||||
if (this.options.sidePagination === 'server') {
|
||||
this.options.queryParams = params => {
|
||||
params.multiSort = this.options.sortPriority
|
||||
return $.fn.bootstrapTable.utils.calculateObjectValue(this.options, this.options.queryParams, [params])
|
||||
}
|
||||
isSingleSort = false
|
||||
this.initServer(this.options.silentSort)
|
||||
return
|
||||
}
|
||||
|
||||
this.onMultipleSort()
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "Multiple Sort",
|
||||
"version": "1.1.0",
|
||||
"description": "Plugin to support the multiple sort.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/multiple-sort",
|
||||
"example": "#",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-multiple-sort",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/multiple-sort"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "dimbslmh",
|
||||
"image": "https://avatars1.githubusercontent.com/u/745635"
|
||||
}
|
||||
}
|
||||
Vendored
+43
@@ -0,0 +1,43 @@
|
||||
/**
|
||||
* @author Jay <jwang@dizsoft.com>
|
||||
* @update zhixin wen <wenzhixin2010@gmail.com>
|
||||
*/
|
||||
|
||||
const Utils = $.fn.bootstrapTable.utils
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, {
|
||||
showJumpTo: false
|
||||
})
|
||||
|
||||
$.extend($.fn.bootstrapTable.locales, {
|
||||
formatJumpTo () {
|
||||
return 'GO'
|
||||
}
|
||||
})
|
||||
$.extend($.fn.bootstrapTable.defaults, $.fn.bootstrapTable.locales)
|
||||
|
||||
$.BootstrapTable = class extends $.BootstrapTable {
|
||||
initPagination (...args) {
|
||||
super.initPagination(...args)
|
||||
|
||||
if (this.options.showJumpTo) {
|
||||
const $pageGroup = this.$pagination.find('> .pagination')
|
||||
let $jumpTo = $pageGroup.find('.page-jump-to')
|
||||
|
||||
if (!$jumpTo.length) {
|
||||
$jumpTo = $(`
|
||||
<div class="page-jump-to ${this.constants.classes.inputGroup}">
|
||||
<input type="number" class="${this.constants.classes.input}${Utils.sprintf(' input-%s', this.options.iconSize)}" value="${this.options.pageNumber}">
|
||||
<button class="${this.constants.buttonsClass}" type="button">
|
||||
${this.options.formatJumpTo()}
|
||||
</button>
|
||||
</div>
|
||||
`).appendTo($pageGroup)
|
||||
|
||||
$jumpTo.on('click', 'button', (e) => {
|
||||
this.selectPage(+$(e.target).parent('.page-jump-to').find('input').val())
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Vendored
+11
@@ -0,0 +1,11 @@
|
||||
.bootstrap-table.bootstrap3 .fixed-table-pagination > .pagination ul.pagination,
|
||||
.bootstrap-table.bootstrap3 .fixed-table-pagination > .pagination .page-jump-to {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination > .pagination .page-jump-to input {
|
||||
width: 70px;
|
||||
margin-left: 5px;
|
||||
text-align: center;
|
||||
float: left;
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2019 doug-the-guy <badlydrawnsun@yahoo.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
@@ -0,0 +1,92 @@
|
||||
# Bootstrap Table Pipelining
|
||||
|
||||
Use Plugin: [bootstrap-table-pipeline]
|
||||
|
||||
This plugin enables client side data caching for server side requests which will
|
||||
eliminate the need to issue a new request every page change. This will allow
|
||||
for a performance balance for a large data set between returning all data at once
|
||||
(client side paging) and a new server side request (server side paging).
|
||||
|
||||
There are two new options:
|
||||
- usePipeline: enables this feature
|
||||
- pipelineSize: the size of each cache window
|
||||
|
||||
The size of the pipeline must be evenly divisible by the current page size. This is
|
||||
assured by rounding up to the nearest evenly divisible value. For example, if
|
||||
the pipeline size is 4990 and the current page size is 25, then pipeline size will
|
||||
be dynamically set to 5000.
|
||||
|
||||
The cache windows are computed based on the pipeline size and the total number of rows
|
||||
returned by the server side query. For example, with pipeline size 500 and total rows
|
||||
1300, the cache windows will be:
|
||||
|
||||
[{'lower': 0, 'upper': 499}, {'lower': 500, 'upper': 999}, {'lower': 1000, 'upper': 1499}]
|
||||
|
||||
Using the limit (i.e. the pipelineSize) and offset parameters, the server side request
|
||||
**MUST** return only the data in the requested cache window **AND** the total number of rows.
|
||||
To wit, the server side code must use the offset and limit parameters to prepare the response
|
||||
data.
|
||||
|
||||
On a page change, the new offset is checked if it is within the current cache window. If so,
|
||||
the requested page data is returned from the cached data set. Otherwise, a new server side
|
||||
request will be issued for the new cache window.
|
||||
|
||||
The current cached data is only invalidated on these events:
|
||||
- sorting
|
||||
- searching
|
||||
- page size change
|
||||
- page change moves into a new cache window
|
||||
|
||||
There are two new events:
|
||||
- cached-data-hit.bs.table: issued when cached data is used on a page change
|
||||
- cached-data-reset.bs.table: issued when the cached data is invalidated and new server side request is issued
|
||||
|
||||
## Features
|
||||
|
||||
* Created with Bootstrap 4
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
# assumed import of bootstrap and bootstrap-table assets
|
||||
<script src="/path/to/bootstrap-table-pipeline.js"></script>
|
||||
...
|
||||
<table id="pipeline_table"
|
||||
class="table table-striped"
|
||||
data-method='post'
|
||||
data-use-pipeline="true"
|
||||
data-pipeline-size="5000"
|
||||
data-pagination="true"
|
||||
data-side-pagination="server"
|
||||
data-page-size="50">
|
||||
<thead><tr>
|
||||
<th data-field="type" data-sortable="true">Type</th>
|
||||
<th data-field="value" data-sortable="true">Value</th>
|
||||
<th data-field="date" data-sortable="true">Date</th>
|
||||
</tr></thead>
|
||||
</table>
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
### usePipeline
|
||||
|
||||
* type: Boolean
|
||||
* description: Set true to enable pipelining
|
||||
* default: `false`
|
||||
|
||||
## pipelineSize
|
||||
|
||||
* type: Integer
|
||||
* description: Size of each cache window. Must be greater than 0
|
||||
* default: `1000`
|
||||
|
||||
## Events
|
||||
|
||||
### onCachedDataHit(cached-data-hit.bs.table)
|
||||
|
||||
* Fires when paging was able to use the locally cached data.
|
||||
|
||||
### onCachedDataReset(cached-data-reset.bs.table)
|
||||
|
||||
* Fires when the locally cached data needed to be reset (i.e. on sorting, searching, page size change or paged out of current cache window)
|
||||
+320
@@ -0,0 +1,320 @@
|
||||
/**
|
||||
* @author doug-the-guy
|
||||
* @version v1.0.0
|
||||
*
|
||||
* Bootstrap Table Pipeline
|
||||
* -----------------------
|
||||
*
|
||||
* This plugin enables client side data caching for server side requests which will
|
||||
* eliminate the need to issue a new request every page change. This will allow
|
||||
* for a performance balance for a large data set between returning all data at once
|
||||
* (client side paging) and a new server side request (server side paging).
|
||||
*
|
||||
* There are two new options:
|
||||
* - usePipeline: enables this feature
|
||||
* - pipelineSize: the size of each cache window
|
||||
*
|
||||
* The size of the pipeline must be evenly divisible by the current page size. This is
|
||||
* assured by rounding up to the nearest evenly divisible value. For example, if
|
||||
* the pipeline size is 4990 and the current page size is 25, then pipeline size will
|
||||
* be dynamically set to 5000.
|
||||
*
|
||||
* The cache windows are computed based on the pipeline size and the total number of rows
|
||||
* returned by the server side query. For example, with pipeline size 500 and total rows
|
||||
* 1300, the cache windows will be:
|
||||
*
|
||||
* [{'lower': 0, 'upper': 499}, {'lower': 500, 'upper': 999}, {'lower': 1000, 'upper': 1499}]
|
||||
*
|
||||
* Using the limit (i.e. the pipelineSize) and offset parameters, the server side request
|
||||
* **MUST** return only the data in the requested cache window **AND** the total number of rows.
|
||||
* To wit, the server side code must use the offset and limit parameters to prepare the response
|
||||
* data.
|
||||
*
|
||||
* On a page change, the new offset is checked if it is within the current cache window. If so,
|
||||
* the requested page data is returned from the cached data set. Otherwise, a new server side
|
||||
* request will be issued for the new cache window.
|
||||
*
|
||||
* The current cached data is only invalidated on these events:
|
||||
* * sorting
|
||||
* * searching
|
||||
* * page size change
|
||||
* * page change moves into a new cache window
|
||||
*
|
||||
* There are two new events:
|
||||
* - cached-data-hit.bs.table: issued when cached data is used on a page change
|
||||
* - cached-data-reset.bs.table: issued when the cached data is invalidated and a
|
||||
* new server side request is issued
|
||||
*
|
||||
**/
|
||||
|
||||
const Utils = $.fn.bootstrapTable.utils
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, {
|
||||
usePipeline: false,
|
||||
pipelineSize: 1000,
|
||||
onCachedDataHit (data) {
|
||||
return false
|
||||
},
|
||||
onCachedDataReset (data) {
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
$.extend($.fn.bootstrapTable.Constructor.EVENTS, {
|
||||
'cached-data-hit.bs.table': 'onCachedDataHit',
|
||||
'cached-data-reset.bs.table': 'onCachedDataReset'
|
||||
})
|
||||
|
||||
const BootstrapTable = $.fn.bootstrapTable.Constructor
|
||||
const _init = BootstrapTable.prototype.init
|
||||
const _initServer = BootstrapTable.prototype.initServer
|
||||
const _onSearch = BootstrapTable.prototype.onSearch
|
||||
const _onSort = BootstrapTable.prototype.onSort
|
||||
const _onPageListChange = BootstrapTable.prototype.onPageListChange
|
||||
|
||||
BootstrapTable.prototype.init = function (...args) {
|
||||
// needs to be called before initServer()
|
||||
this.initPipeline()
|
||||
_init.apply(this, Array.prototype.slice.apply(args))
|
||||
}
|
||||
|
||||
BootstrapTable.prototype.initPipeline = function () {
|
||||
this.cacheRequestJSON = {}
|
||||
this.cacheWindows = []
|
||||
this.currWindow = 0
|
||||
this.resetCache = true
|
||||
}
|
||||
|
||||
BootstrapTable.prototype.onSearch = function (event) {
|
||||
/* force a cache reset on search */
|
||||
if (this.options.usePipeline) {
|
||||
this.resetCache = true
|
||||
}
|
||||
_onSearch.apply(this, Array.prototype.slice.apply(arguments))
|
||||
}
|
||||
|
||||
BootstrapTable.prototype.onSort = function (event) {
|
||||
/* force a cache reset on sort */
|
||||
if (this.options.usePipeline) {
|
||||
this.resetCache = true
|
||||
}
|
||||
_onSort.apply(this, Array.prototype.slice.apply(arguments))
|
||||
}
|
||||
|
||||
BootstrapTable.prototype.onPageListChange = function (event) {
|
||||
/* rebuild cache window on page size change */
|
||||
const target = $(event.currentTarget)
|
||||
const newPageSize = parseInt(target.text())
|
||||
this.options.pipelineSize = this.calculatePipelineSize(this.options.pipelineSize, newPageSize)
|
||||
this.resetCache = true
|
||||
_onPageListChange.apply(this, Array.prototype.slice.apply(arguments))
|
||||
}
|
||||
|
||||
BootstrapTable.prototype.calculatePipelineSize = (pipelineSize, pageSize) => {
|
||||
/* calculate pipeline size by rounding up to the nearest value evenly divisible
|
||||
* by the pageSize */
|
||||
if (pageSize === 0) return 0
|
||||
return Math.ceil(pipelineSize / pageSize) * pageSize
|
||||
}
|
||||
|
||||
BootstrapTable.prototype.setCacheWindows = function () {
|
||||
/* set cache windows based on the total number of rows returned by server side
|
||||
* request and the pipelineSize */
|
||||
this.cacheWindows = []
|
||||
const numWindows = this.options.totalRows / this.options.pipelineSize
|
||||
for (let i = 0; i <= numWindows; i++) {
|
||||
const b = i * this.options.pipelineSize
|
||||
this.cacheWindows[i] = {'lower': b, 'upper': b + this.options.pipelineSize - 1}
|
||||
}
|
||||
}
|
||||
|
||||
BootstrapTable.prototype.setCurrWindow = function (offset) {
|
||||
/* set the current cache window index, based on where the current offset falls */
|
||||
this.currWindow = 0
|
||||
for (let i = 0; i < this.cacheWindows.length; i++) {
|
||||
if (this.cacheWindows[i].lower <= offset && offset <= this.cacheWindows[i].upper) {
|
||||
this.currWindow = i
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
BootstrapTable.prototype.drawFromCache = function (offset, limit) {
|
||||
/* draw rows from the cache using offset and limit */
|
||||
const res = $.extend(true, {}, this.cacheRequestJSON)
|
||||
const drawStart = offset - this.cacheWindows[this.currWindow].lower
|
||||
const drawEnd = drawStart + limit
|
||||
res.rows = res.rows.slice(drawStart, drawEnd)
|
||||
return res
|
||||
}
|
||||
|
||||
BootstrapTable.prototype.initServer = function (silent, query, url) {
|
||||
/* determine if requested data is in cache (on paging) or if
|
||||
* a new ajax request needs to be issued (sorting, searching, paging
|
||||
* moving outside of cached data, page size change)
|
||||
* initial version of this extension will entirely override base initServer
|
||||
**/
|
||||
|
||||
let data = {}
|
||||
const index = this.header.fields.indexOf(this.options.sortName)
|
||||
|
||||
let params = {
|
||||
searchText: this.searchText,
|
||||
sortName: this.options.sortName,
|
||||
sortOrder: this.options.sortOrder
|
||||
}
|
||||
|
||||
let request = null
|
||||
|
||||
if (this.header.sortNames[index]) {
|
||||
params.sortName = this.header.sortNames[index]
|
||||
}
|
||||
|
||||
if (this.options.pagination && this.options.sidePagination === 'server') {
|
||||
params.pageSize = this.options.pageSize === this.options.formatAllRows()
|
||||
? this.options.totalRows : this.options.pageSize
|
||||
params.pageNumber = this.options.pageNumber
|
||||
}
|
||||
|
||||
if (!(url || this.options.url) && !this.options.ajax) {
|
||||
return
|
||||
}
|
||||
|
||||
let useAjax = true
|
||||
if (this.options.queryParamsType === 'limit') {
|
||||
params = {
|
||||
searchText: params.searchText,
|
||||
sortName: params.sortName,
|
||||
sortOrder: params.sortOrder
|
||||
}
|
||||
if (this.options.pagination && this.options.sidePagination === 'server') {
|
||||
params.limit = this.options.pageSize === this.options.formatAllRows() ? this.options.totalRows : this.options.pageSize
|
||||
params.offset = (this.options.pageSize === this.options.formatAllRows() ? this.options.totalRows : this.options.pageSize) * (this.options.pageNumber - 1)
|
||||
if (this.options.usePipeline) {
|
||||
// if cacheWindows is empty, this is the initial request
|
||||
if (!this.cacheWindows.length) {
|
||||
useAjax = true
|
||||
params.drawOffset = params.offset
|
||||
// cache exists: determine if the page request is entirely within the current cached window
|
||||
} else {
|
||||
const w = this.cacheWindows[this.currWindow]
|
||||
// case 1: reset cache but stay within current window (e.g. column sort)
|
||||
// case 2: move outside of the current window (e.g. search or paging)
|
||||
// since each cache window is aligned with the current page size
|
||||
// checking if params.offset is outside the current window is sufficient.
|
||||
// need to requery for preceding or succeeding cache window
|
||||
// also handle case
|
||||
if (this.resetCache || (params.offset < w.lower || params.offset > w.upper)) {
|
||||
useAjax = true
|
||||
this.setCurrWindow(params.offset)
|
||||
// store the relative offset for drawing the page data afterwards
|
||||
params.drawOffset = params.offset
|
||||
// now set params.offset to the lower bound of the new cache window
|
||||
// the server will return that whole cache window
|
||||
params.offset = this.cacheWindows[this.currWindow].lower
|
||||
// within current cache window
|
||||
} else {
|
||||
useAjax = false
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (params.limit === 0) {
|
||||
delete params.limit
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// force an ajax call - this is on search, sort or page size change
|
||||
if (this.resetCache) {
|
||||
useAjax = true
|
||||
this.resetCache = false
|
||||
}
|
||||
|
||||
if (this.options.usePipeline && useAjax) {
|
||||
/* in this scenario limit is used on the server to get the cache window
|
||||
* and drawLimit is used to get the page data afterwards */
|
||||
params.drawLimit = params.limit
|
||||
params.limit = this.options.pipelineSize
|
||||
}
|
||||
|
||||
// cached results can be used
|
||||
if (!useAjax) {
|
||||
const res = this.drawFromCache(params.offset, params.limit)
|
||||
this.load(res)
|
||||
this.trigger('load-success', res)
|
||||
this.trigger('cached-data-hit', res)
|
||||
return
|
||||
}
|
||||
// cached results can't be used
|
||||
// continue base initServer code
|
||||
if (!($.isEmptyObject(this.filterColumnsPartial))) {
|
||||
params.filter = JSON.stringify(this.filterColumnsPartial, null)
|
||||
}
|
||||
|
||||
data = Utils.calculateObjectValue(this.options, this.options.queryParams, [params], data)
|
||||
|
||||
$.extend(data, query || {})
|
||||
|
||||
// false to stop request
|
||||
if (data === false) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!silent) {
|
||||
this.$tableLoading.show()
|
||||
}
|
||||
const self = this
|
||||
|
||||
request = $.extend({}, Utils.calculateObjectValue(null, this.options.ajaxOptions), {
|
||||
type: this.options.method,
|
||||
url: url || this.options.url,
|
||||
data: this.options.contentType === 'application/json' && this.options.method === 'post'
|
||||
? JSON.stringify(data) : data,
|
||||
cache: this.options.cache,
|
||||
contentType: this.options.contentType,
|
||||
dataType: this.options.dataType,
|
||||
success (res) {
|
||||
res = Utils.calculateObjectValue(self.options, self.options.responseHandler, [res], res)
|
||||
// cache results if using pipelining
|
||||
if (self.options.usePipeline) {
|
||||
// store entire request in cache
|
||||
self.cacheRequestJSON = $.extend(true, {}, res)
|
||||
// this gets set in load() also but needs to be set before
|
||||
// setting cacheWindows
|
||||
self.options.totalRows = res[self.options.totalField]
|
||||
// if this is a search, potentially less results will be returned
|
||||
// so cache windows need to be rebuilt. Otherwise it
|
||||
// will come out the same
|
||||
self.setCacheWindows()
|
||||
self.setCurrWindow(params.drawOffset)
|
||||
// just load data for the page
|
||||
res = self.drawFromCache(params.drawOffset, params.drawLimit)
|
||||
self.trigger('cached-data-reset', res)
|
||||
}
|
||||
self.load(res)
|
||||
self.trigger('load-success', res)
|
||||
if (!silent) self.$tableLoading.hide()
|
||||
},
|
||||
error (res) {
|
||||
let data = []
|
||||
if (self.options.sidePagination === 'server') {
|
||||
data = {}
|
||||
data[self.options.totalField] = 0
|
||||
data[self.options.dataField] = []
|
||||
}
|
||||
self.load(data)
|
||||
self.trigger('load-error', res.status, res)
|
||||
if (!silent) self.$tableLoading.hide()
|
||||
}
|
||||
})
|
||||
|
||||
if (this.options.ajax) {
|
||||
Utils.calculateObjectValue(this, this.options.ajax, [request], null)
|
||||
} else {
|
||||
if (this._xhr && this._xhr.readyState !== 4) {
|
||||
this._xhr.abort()
|
||||
}
|
||||
this._xhr = $.ajax(request)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "Pipeline",
|
||||
"version": "1.0.0",
|
||||
"description": "Plugin to support a hybrid approach to server/client side paging.",
|
||||
"url": "",
|
||||
"example": "#",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-pipeline",
|
||||
"url": ""
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "doug-the-guy",
|
||||
"image": ""
|
||||
}
|
||||
}
|
||||
|
||||
+181
@@ -0,0 +1,181 @@
|
||||
/**
|
||||
* @update zhixin wen <wenzhixin2010@gmail.com>
|
||||
*/
|
||||
|
||||
const Utils = $.fn.bootstrapTable.utils
|
||||
|
||||
function printPageBuilderDefault (table) {
|
||||
return `
|
||||
<html>
|
||||
<head>
|
||||
<style type="text/css" media="print">
|
||||
@page {
|
||||
size: auto;
|
||||
margin: 25px 0 25px 0;
|
||||
}
|
||||
</style>
|
||||
<style type="text/css" media="all">
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
font-size: 12px;
|
||||
}
|
||||
table, th, td {
|
||||
border: 1px solid grey;
|
||||
}
|
||||
th, td {
|
||||
text-align: center;
|
||||
vertical-align: middle;
|
||||
}
|
||||
p {
|
||||
font-weight: bold;
|
||||
margin-left:20px;
|
||||
}
|
||||
table {
|
||||
width:94%;
|
||||
margin-left:3%;
|
||||
margin-right:3%;
|
||||
}
|
||||
div.bs-table-print {
|
||||
text-align:center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<title>Print Table</title>
|
||||
<body>
|
||||
<p>Printed on: ${new Date} </p>
|
||||
<div class="bs-table-print">${table}</div>
|
||||
</body>
|
||||
</html>`
|
||||
}
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, {
|
||||
showPrint: false,
|
||||
printAsFilteredAndSortedOnUI: true,
|
||||
printSortColumn: undefined,
|
||||
printSortOrder: 'asc',
|
||||
printPageBuilder (table) {
|
||||
return printPageBuilderDefault(table)
|
||||
}
|
||||
})
|
||||
|
||||
$.extend($.fn.bootstrapTable.COLUMN_DEFAULTS, {
|
||||
printFilter: undefined,
|
||||
printIgnore: false,
|
||||
printFormatter: undefined
|
||||
})
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults.icons, {
|
||||
print: {
|
||||
bootstrap3: 'glyphicon-print icon-share'
|
||||
}[$.fn.bootstrapTable.theme] || 'fa-print'
|
||||
})
|
||||
|
||||
$.BootstrapTable = class extends $.BootstrapTable {
|
||||
initToolbar (...args) {
|
||||
this.showToolbar = this.showToolbar || this.options.showPrint
|
||||
|
||||
super.initToolbar(...args)
|
||||
|
||||
if (!this.options.showPrint) {
|
||||
return
|
||||
}
|
||||
|
||||
const $btnGroup = this.$toolbar.find('>.columns')
|
||||
let $print = $btnGroup.find('button.bs-print')
|
||||
|
||||
if (!$print.length) {
|
||||
$print = $(`
|
||||
<button class="${this.constants.buttonsClass} bs-print" type="button">
|
||||
<i class="${this.options.iconsPrefix} ${this.options.icons.print}"></i>
|
||||
</button>`
|
||||
).appendTo($btnGroup)
|
||||
}
|
||||
|
||||
$print.off('click').on('click', () => {
|
||||
this.doPrint(this.options.printAsFilteredAndSortedOnUI ?
|
||||
this.getData() : this.options.data.slice(0))
|
||||
})
|
||||
}
|
||||
|
||||
doPrint (data) {
|
||||
const formatValue = (row, i, column ) => {
|
||||
const value = Utils.calculateObjectValue(column, column.printFormatter,
|
||||
[row[column.field], row, i], row[column.field])
|
||||
|
||||
return typeof value === 'undefined' || value === null
|
||||
? this.options.undefinedText : value
|
||||
}
|
||||
|
||||
const buildTable = (data, columnsArray) => {
|
||||
const dir = this.$el.attr('dir') || 'ltr'
|
||||
const html = [`<table dir="${dir}"><thead>`]
|
||||
|
||||
for (const columns of columnsArray) {
|
||||
html.push('<tr>')
|
||||
for (let h = 0; h < columns.length; h++) {
|
||||
if (!columns[h].printIgnore) {
|
||||
html.push(
|
||||
`<th
|
||||
${Utils.sprintf(' rowspan="%s"', columns[h].rowspan)}
|
||||
${Utils.sprintf(' colspan="%s"', columns[h].colspan)}
|
||||
>${columns[h].title}</th>`)
|
||||
}
|
||||
}
|
||||
html.push('</tr>')
|
||||
}
|
||||
|
||||
html.push('</thead><tbody>')
|
||||
|
||||
for (let i = 0; i < data.length; i++) {
|
||||
html.push('<tr>')
|
||||
|
||||
for (const columns of columnsArray) {
|
||||
for (let j = 0; j < columns.length; j++) {
|
||||
if (!columns[j].printIgnore && columns[j].field) {
|
||||
html.push('<td>', formatValue(data[i], i, columns[j]), '</td>')
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
html.push('</tr>')
|
||||
}
|
||||
html.push('</tbody></table>')
|
||||
return html.join('')
|
||||
}
|
||||
|
||||
const sortRows = (data, colName, sortOrder) => {
|
||||
if (!colName) {
|
||||
return data
|
||||
}
|
||||
let reverse = sortOrder !== 'asc'
|
||||
reverse = -((+reverse) || -1)
|
||||
return data.sort((a, b) => reverse * (a[colName].localeCompare(b[colName])))
|
||||
}
|
||||
|
||||
const filterRow = (row, filters) => {
|
||||
for (let index = 0; index < filters.length; ++index) {
|
||||
if (row[filters[index].colName] !== filters[index].value) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
const filterRows = (data, filters) => data.filter(row => filterRow(row,filters))
|
||||
|
||||
const getColumnFilters = columns => !columns || !columns[0] ? [] : columns[0].filter(col => col.printFilter).map(col => ({
|
||||
colName: col.field,
|
||||
value: col.printFilter
|
||||
}))
|
||||
|
||||
data = filterRows(data,getColumnFilters(this.options.columns))
|
||||
data = sortRows(data, this.options.printSortColumn, this.options.printSortOrder)
|
||||
const table = buildTable(data, this.options.columns)
|
||||
const newWin = window.open('')
|
||||
newWin.document.write(this.options.printPageBuilder.call(this, table))
|
||||
newWin.document.close()
|
||||
newWin.focus()
|
||||
newWin.print()
|
||||
newWin.close()
|
||||
}
|
||||
}
|
||||
Vendored
+199
@@ -0,0 +1,199 @@
|
||||
/**
|
||||
* @author: Dennis Hernández
|
||||
* @webSite: http://djhvscf.github.io/Blog
|
||||
* @update: https://github.com/wenzhixin
|
||||
* @version: v1.2.0
|
||||
*/
|
||||
|
||||
$.akottr.dragtable.prototype._restoreState = function (persistObj) {
|
||||
for (const [field, value] of Object.entries(persistObj)) {
|
||||
var $th = this.originalTable.el.find(`th[data-field="${field}"]`)
|
||||
this.originalTable.startIndex = $th.prevAll().length + 1
|
||||
this.originalTable.endIndex = parseInt(value, 10) + 1
|
||||
this._bubbleCols()
|
||||
}
|
||||
}
|
||||
|
||||
// From MDN site, https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter
|
||||
const filterFn = () => {
|
||||
if (!Array.prototype.filter) {
|
||||
Array.prototype.filter = function (fun/* , thisArg*/) {
|
||||
if (this === undefined || this === null) {
|
||||
throw new TypeError()
|
||||
}
|
||||
|
||||
const t = Object(this)
|
||||
const len = t.length >>> 0
|
||||
if (typeof fun !== 'function') {
|
||||
throw new TypeError()
|
||||
}
|
||||
|
||||
const res = []
|
||||
const thisArg = arguments.length >= 2 ? arguments[1] : undefined
|
||||
for (let i = 0; i < len; i++) {
|
||||
if (i in t) {
|
||||
const val = t[i]
|
||||
|
||||
// NOTE: Technically this should Object.defineProperty at
|
||||
// the next index, as push can be affected by
|
||||
// properties on Object.prototype and Array.prototype.
|
||||
// But this method's new, and collisions should be
|
||||
// rare, so use the more-compatible alternative.
|
||||
if (fun.call(thisArg, val, i, t)) {
|
||||
res.push(val)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return res
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, {
|
||||
reorderableColumns: false,
|
||||
maxMovingRows: 10,
|
||||
onReorderColumn (headerFields) {
|
||||
return false
|
||||
},
|
||||
dragaccept: null
|
||||
})
|
||||
|
||||
$.extend($.fn.bootstrapTable.Constructor.EVENTS, {
|
||||
'reorder-column.bs.table': 'onReorderColumn'
|
||||
})
|
||||
|
||||
$.fn.bootstrapTable.methods.push('orderColumns')
|
||||
|
||||
$.BootstrapTable = class extends $.BootstrapTable {
|
||||
initHeader (...args) {
|
||||
super.initHeader(...args)
|
||||
|
||||
if (!this.options.reorderableColumns) {
|
||||
return
|
||||
}
|
||||
|
||||
this.makeRowsReorderable()
|
||||
}
|
||||
|
||||
_toggleColumn (...args) {
|
||||
super._toggleColumn(...args)
|
||||
|
||||
if (!this.options.reorderableColumns) {
|
||||
return
|
||||
}
|
||||
|
||||
this.makeRowsReorderable()
|
||||
}
|
||||
|
||||
toggleView (...args) {
|
||||
super.toggleView(...args)
|
||||
|
||||
if (!this.options.reorderableColumns) {
|
||||
return
|
||||
}
|
||||
|
||||
if (this.options.cardView) {
|
||||
return
|
||||
}
|
||||
|
||||
this.makeRowsReorderable()
|
||||
}
|
||||
|
||||
resetView (...args) {
|
||||
super.resetView(...args)
|
||||
|
||||
if (!this.options.reorderableColumns) {
|
||||
return
|
||||
}
|
||||
|
||||
this.makeRowsReorderable()
|
||||
}
|
||||
|
||||
makeRowsReorderable (order = null) {
|
||||
try {
|
||||
$(this.$el).dragtable('destroy')
|
||||
} catch (e) {
|
||||
// do nothing
|
||||
}
|
||||
$(this.$el).dragtable({
|
||||
maxMovingRows: this.options.maxMovingRows,
|
||||
dragaccept: this.options.dragaccept,
|
||||
clickDelay: 200,
|
||||
dragHandle: '.th-inner',
|
||||
restoreState: order ? order : this.columnsSortOrder,
|
||||
beforeStop: (table) => {
|
||||
const sortOrder = {}
|
||||
table.el.find('th').each((i, el) => {
|
||||
sortOrder[$(el).data('field')] = i
|
||||
})
|
||||
|
||||
this.columnsSortOrder = sortOrder
|
||||
if (this.options.cookie) {
|
||||
this.persistReorderColumnsState(this)
|
||||
}
|
||||
|
||||
const ths = []
|
||||
const formatters = []
|
||||
const columns = []
|
||||
let columnsHidden = []
|
||||
let columnIndex = -1
|
||||
const optionsColumns = []
|
||||
this.$header.find('th:not(.detail)').each(function (i) {
|
||||
ths.push($(this).data('field'))
|
||||
formatters.push($(this).data('formatter'))
|
||||
})
|
||||
|
||||
// Exist columns not shown
|
||||
if (ths.length < this.columns.length) {
|
||||
columnsHidden = this.columns.filter(column => !column.visible)
|
||||
for (var i = 0; i < columnsHidden.length; i++) {
|
||||
ths.push(columnsHidden[i].field)
|
||||
formatters.push(columnsHidden[i].formatter)
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < ths.length; i++) {
|
||||
columnIndex = this.fieldsColumnsIndex[ths[i]]
|
||||
if (columnIndex !== -1) {
|
||||
this.fieldsColumnsIndex[ths[i]] = i
|
||||
this.columns[columnIndex].fieldIndex = i
|
||||
columns.push(this.columns[columnIndex])
|
||||
}
|
||||
}
|
||||
|
||||
this.columns = columns
|
||||
|
||||
filterFn() // Support <IE9
|
||||
$.each(this.columns, (i, column) => {
|
||||
let found = false
|
||||
const field = column.field
|
||||
this.options.columns[0].filter(item => {
|
||||
if (!found && item['field'] === field) {
|
||||
optionsColumns.push(item)
|
||||
found = true
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
})
|
||||
|
||||
this.options.columns[0] = optionsColumns
|
||||
|
||||
this.header.fields = ths
|
||||
this.header.formatters = formatters
|
||||
this.initHeader()
|
||||
this.initToolbar()
|
||||
this.initSearchText()
|
||||
this.initBody()
|
||||
this.resetView()
|
||||
this.trigger('reorder-column', ths)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
orderColumns (order) {
|
||||
this.columnsSortOrder = order
|
||||
this.makeRowsReorderable()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "Reorder Columns",
|
||||
"version": "1.1.0",
|
||||
"description": "Plugin to support the reordering columns feature.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/reorder-columns",
|
||||
"example": "http://issues.wenzhixin.net.cn/bootstrap-table/#extensions/reorder-columns.html",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-reorder-columns",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/reorder-columns"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "djhvscf",
|
||||
"image": "https://avatars1.githubusercontent.com/u/4496763"
|
||||
}
|
||||
}
|
||||
Vendored
+95
@@ -0,0 +1,95 @@
|
||||
/**
|
||||
* @author: Dennis Hernández
|
||||
* @webSite: http://djhvscf.github.io/Blog
|
||||
* @update zhixin wen <wenzhixin2010@gmail.com>
|
||||
*/
|
||||
|
||||
const rowAttr = (row, index) => ({
|
||||
id: `customId_${index}`
|
||||
})
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, {
|
||||
reorderableRows: false,
|
||||
onDragStyle: null,
|
||||
onDropStyle: null,
|
||||
onDragClass: 'reorder_rows_onDragClass',
|
||||
dragHandle: '>tbody>tr>td',
|
||||
useRowAttrFunc: false,
|
||||
onReorderRowsDrag (row) {
|
||||
return false
|
||||
},
|
||||
onReorderRowsDrop (row) {
|
||||
return false
|
||||
},
|
||||
onReorderRow (newData) {
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
$.extend($.fn.bootstrapTable.Constructor.EVENTS, {
|
||||
'reorder-row.bs.table': 'onReorderRow'
|
||||
})
|
||||
|
||||
$.BootstrapTable = class extends $.BootstrapTable {
|
||||
init (...args) {
|
||||
if (!this.options.reorderableRows) {
|
||||
super.init(...args)
|
||||
return
|
||||
}
|
||||
|
||||
if (this.options.useRowAttrFunc) {
|
||||
this.options.rowAttributes = rowAttr
|
||||
}
|
||||
|
||||
const onPostBody = this.options.onPostBody
|
||||
this.options.onPostBody = () => {
|
||||
setTimeout(() => {
|
||||
this.makeRowsReorderable()
|
||||
onPostBody.call(this.options, this.options.data)
|
||||
}, 1)
|
||||
}
|
||||
|
||||
super.init(...args)
|
||||
}
|
||||
|
||||
makeRowsReorderable () {
|
||||
this.$el.tableDnD({
|
||||
onDragStyle: this.options.onDragStyle,
|
||||
onDropStyle: this.options.onDropStyle,
|
||||
onDragClass: this.options.onDragClass,
|
||||
onDragStart: (table, droppedRow) => this.onDropStart(table, droppedRow),
|
||||
onDrop: (table, droppedRow) => this.onDrop(table, droppedRow),
|
||||
dragHandle: this.options.dragHandle
|
||||
})
|
||||
}
|
||||
|
||||
onDropStart (table, draggingTd) {
|
||||
this.$draggingTd = $(draggingTd).css('cursor', 'move')
|
||||
this.draggingIndex = $(this.$draggingTd.parent()).data('index')
|
||||
// Call the user defined function
|
||||
this.options.onReorderRowsDrag(this.data[this.draggingIndex])
|
||||
}
|
||||
|
||||
onDrop (table) {
|
||||
this.$draggingTd.css('cursor', '')
|
||||
const newData = []
|
||||
for (let i = 0; i < table.tBodies[0].rows.length; i++) {
|
||||
const $tr = $(table.tBodies[0].rows[i])
|
||||
newData.push(this.data[$tr.data('index')])
|
||||
$tr.data('index', i)
|
||||
}
|
||||
|
||||
const draggingRow = this.data[this.draggingIndex]
|
||||
const droppedIndex = newData.indexOf(this.data[this.draggingIndex])
|
||||
const droppedRow = this.data[droppedIndex]
|
||||
const index = this.options.data.indexOf(this.data[droppedIndex])
|
||||
this.options.data.splice(this.options.data.indexOf(draggingRow), 1)
|
||||
this.options.data.splice(index, 0, draggingRow)
|
||||
|
||||
// Call the user defined function
|
||||
this.options.onReorderRowsDrop(droppedRow)
|
||||
|
||||
// Call the event reorder-row
|
||||
this.trigger('reorder-row', newData)
|
||||
}
|
||||
}
|
||||
Vendored
+14
@@ -0,0 +1,14 @@
|
||||
.reorder_rows_onDragClass td {
|
||||
background-color: #eee;
|
||||
-webkit-box-shadow: 11px 5px 12px 2px #333, 0 1px 0 #ccc inset, 0 -1px 0 #ccc inset;
|
||||
-webkit-box-shadow: 6px 3px 5px #555, 0 1px 0 #ccc inset, 0 -1px 0 #ccc inset;
|
||||
-moz-box-shadow: 6px 4px 5px 1px #555, 0 1px 0 #ccc inset, 0 -1px 0 #ccc inset;
|
||||
-box-shadow: 6px 4px 5px 1px #555, 0 1px 0 #ccc inset, 0 -1px 0 #ccc inset;
|
||||
}
|
||||
|
||||
.reorder_rows_onDragClass td:last-child {
|
||||
-webkit-box-shadow: 8px 7px 12px 0 #333, 0 1px 0 #ccc inset, 0 -1px 0 #ccc inset;
|
||||
-webkit-box-shadow: 1px 8px 6px -4px #555, 0 1px 0 #ccc inset, 0 -1px 0 #ccc inset;
|
||||
-moz-box-shadow: 0 9px 4px -4px #555, 0 1px 0 #ccc inset, 0 -1px 0 #ccc inset, -1px 0 0 #ccc inset;
|
||||
-box-shadow: 0 9px 4px -4px #555, 0 1px 0 #ccc inset, 0 -1px 0 #ccc inset, -1px 0 0 #ccc inset;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "Reorder Rows",
|
||||
"version": "1.0.0",
|
||||
"description": "Plugin to support the reordering rows feature.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/reorder-rows",
|
||||
"example": "http://issues.wenzhixin.net.cn/bootstrap-table/#extensions/reorder-rows.html",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-reorder-rows",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/reorder-rows"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "djhvscf",
|
||||
"image": "https://avatars1.githubusercontent.com/u/4496763"
|
||||
}
|
||||
}
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
/**
|
||||
* @author: Dennis Hernández
|
||||
* @webSite: http://djhvscf.github.io/Blog
|
||||
* @version: v2.0.0
|
||||
*/
|
||||
|
||||
const isInit = that => that.$el.data('resizableColumns') !== undefined
|
||||
|
||||
const initResizable = that => {
|
||||
if (that.options.resizable && !that.options.cardView && !isInit(that)) {
|
||||
that.$el.resizableColumns({
|
||||
store: window.store
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
const destroy = that => {
|
||||
if (isInit(that)) {
|
||||
that.$el.data('resizableColumns').destroy()
|
||||
}
|
||||
}
|
||||
|
||||
const reInitResizable = that => {
|
||||
destroy(that)
|
||||
initResizable(that)
|
||||
}
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, {
|
||||
resizable: false
|
||||
})
|
||||
|
||||
const BootstrapTable = $.fn.bootstrapTable.Constructor
|
||||
const _initBody = BootstrapTable.prototype.initBody
|
||||
const _toggleView = BootstrapTable.prototype.toggleView
|
||||
const _resetView = BootstrapTable.prototype.resetView
|
||||
|
||||
BootstrapTable.prototype.initBody = function (...args) {
|
||||
const that = this
|
||||
_initBody.apply(this, Array.prototype.slice.apply(args))
|
||||
|
||||
that.$el
|
||||
.off('column-switch.bs.table page-change.bs.table')
|
||||
.on('column-switch.bs.table page-change.bs.table', () => {
|
||||
reInitResizable(that)
|
||||
})
|
||||
}
|
||||
|
||||
BootstrapTable.prototype.toggleView = function (...args) {
|
||||
_toggleView.apply(this, Array.prototype.slice.apply(args))
|
||||
|
||||
if (this.options.resizable && this.options.cardView) {
|
||||
// Destroy the plugin
|
||||
destroy(this)
|
||||
}
|
||||
}
|
||||
|
||||
BootstrapTable.prototype.resetView = function (...args) {
|
||||
const that = this
|
||||
|
||||
_resetView.apply(this, Array.prototype.slice.apply(args))
|
||||
|
||||
if (this.options.resizable) {
|
||||
// because in fitHeader function, we use setTimeout(func, 100);
|
||||
setTimeout(() => {
|
||||
initResizable(that)
|
||||
}, 100)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "Resizable",
|
||||
"version": "1.1.0",
|
||||
"description": "Plugin to support the resizable feature.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/resizable",
|
||||
"example": "http://issues.wenzhixin.net.cn/bootstrap-table/#extensions/resizable.html",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-resizable",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/resizable"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "djhvscf",
|
||||
"image": "https://avatars1.githubusercontent.com/u/4496763"
|
||||
}
|
||||
}
|
||||
Vendored
+119
@@ -0,0 +1,119 @@
|
||||
/**
|
||||
* @author vincent loh <vincent.ml@gmail.com>
|
||||
* @update J Manuel Corona <jmcg92@gmail.com>
|
||||
* @update zhixin wen <wenzhixin2010@gmail.com>
|
||||
*/
|
||||
|
||||
const Utils = $.fn.bootstrapTable.utils
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, {
|
||||
stickyHeader: false,
|
||||
stickyHeaderOffsetY: 0,
|
||||
stickyHeaderOffsetLeft: 0,
|
||||
stickyHeaderOffsetRight: 0
|
||||
})
|
||||
|
||||
$.BootstrapTable = class extends $.BootstrapTable {
|
||||
initHeader (...args) {
|
||||
super.initHeader(...args)
|
||||
|
||||
if (!this.options.stickyHeader) {
|
||||
return
|
||||
}
|
||||
|
||||
this.$tableBody.find('.sticky-header-container,.sticky_anchor_begin,.sticky_anchor_end').remove()
|
||||
|
||||
this.$el.before('<div class="sticky-header-container"></div>')
|
||||
this.$el.before('<div class="sticky_anchor_begin"></div>')
|
||||
this.$el.after('<div class="sticky_anchor_end"></div>')
|
||||
this.$header.addClass('sticky-header')
|
||||
|
||||
// clone header just once, to be used as sticky header
|
||||
// deep clone header, using source header affects tbody>td width
|
||||
this.$stickyContainer = this.$tableBody.find('.sticky-header-container')
|
||||
this.$stickyBegin = this.$tableBody.find('.sticky_anchor_begin')
|
||||
this.$stickyEnd = this.$tableBody.find('.sticky_anchor_end')
|
||||
this.$stickyHeader = this.$header.clone(true, true)
|
||||
|
||||
// render sticky on window scroll or resize
|
||||
$(window).off('resize.sticky-header-table')
|
||||
.on('resize.sticky-header-table', () => this.renderStickyHeader())
|
||||
$(window).off('scroll.sticky-header-table')
|
||||
.on('scroll.sticky-header-table', () => this.renderStickyHeader())
|
||||
this.$tableBody.off('scroll').on('scroll', () => this.matchPositionX())
|
||||
}
|
||||
|
||||
onColumnSearch ({currentTarget, keyCode}) {
|
||||
super.onColumnSearch({currentTarget, keyCode})
|
||||
this.renderStickyHeader()
|
||||
}
|
||||
|
||||
resetView (...args) {
|
||||
super.resetView(...args)
|
||||
|
||||
$('.bootstrap-table.fullscreen').off('scroll')
|
||||
.on('scroll', () => this.renderStickyHeader())
|
||||
}
|
||||
|
||||
renderStickyHeader () {
|
||||
const that = this
|
||||
this.$stickyHeader = this.$header.clone(true, true)
|
||||
|
||||
if (this.options.filterControl) {
|
||||
$(this.$stickyHeader).off('keyup change mouseup').on('keyup change mouse', function (e) {
|
||||
const $target = $(e.target)
|
||||
const value = $target.val()
|
||||
const field = $target.parents('th').data('field')
|
||||
const $coreTh = that.$header.find('th[data-field="' + field + '"]')
|
||||
|
||||
if ($target.is('input')) {
|
||||
$coreTh.find('input').val(value)
|
||||
} else if ($target.is('select')) {
|
||||
const $select = $coreTh.find('select')
|
||||
$select.find('option[selected]').removeAttr('selected')
|
||||
$select.find('option[value="' + value + '"]').attr('selected', true)
|
||||
}
|
||||
|
||||
that.triggerSearch()
|
||||
})
|
||||
}
|
||||
|
||||
const top = $(window).scrollTop()
|
||||
// top anchor scroll position, minus header height
|
||||
const start = this.$stickyBegin.offset().top - this.options.stickyHeaderOffsetY
|
||||
// bottom anchor scroll position, minus header height, minus sticky height
|
||||
const end = this.$stickyEnd.offset().top - this.options.stickyHeaderOffsetY - this.$header.height()
|
||||
// show sticky when top anchor touches header, and when bottom anchor not exceeded
|
||||
if (top > start && top <= end) {
|
||||
// ensure clone and source column widths are the same
|
||||
this.$stickyHeader.find('tr:eq(0)').find('th').each((index, el) => {
|
||||
$(el).css('min-width', this.$header.find('tr:eq(0)').find('th').eq(index).css('width'))
|
||||
})
|
||||
// match bootstrap table style
|
||||
this.$stickyContainer.show().addClass('fix-sticky fixed-table-container')
|
||||
// stick it in position
|
||||
let stickyHeaderOffsetLeft = this.options.stickyHeaderOffsetLeft
|
||||
let stickyHeaderOffsetRight = this.options.stickyHeaderOffsetRight
|
||||
if (this.$el.closest('.bootstrap-table').hasClass('fullscreen')) {
|
||||
stickyHeaderOffsetLeft = 0
|
||||
stickyHeaderOffsetRight = 0
|
||||
}
|
||||
this.$stickyContainer.css('top', `${this.options.stickyHeaderOffsetY}`)
|
||||
this.$stickyContainer.css('left', `${stickyHeaderOffsetLeft}`)
|
||||
this.$stickyContainer.css('right', `${stickyHeaderOffsetRight}`)
|
||||
// create scrollable container for header
|
||||
this.$stickyTable = $('<table/>')
|
||||
this.$stickyTable.addClass(this.options.classes)
|
||||
// append cloned header to dom
|
||||
this.$stickyContainer.html(this.$stickyTable.append(this.$stickyHeader))
|
||||
// match clone and source header positions when left-right scroll
|
||||
this.matchPositionX()
|
||||
} else {
|
||||
this.$stickyContainer.removeClass('fix-sticky').hide()
|
||||
}
|
||||
}
|
||||
|
||||
matchPositionX () {
|
||||
this.$stickyContainer.scrollLeft(this.$tableBody.scrollLeft())
|
||||
}
|
||||
}
|
||||
Vendored
+22
@@ -0,0 +1,22 @@
|
||||
/**
|
||||
* @author vincent loh <vincent.ml@gmail.com>
|
||||
* @update zhixin wen <wenzhixin2010@gmail.com>
|
||||
*/
|
||||
|
||||
.fix-sticky {
|
||||
position: fixed !important;
|
||||
overflow: hidden;
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
.fix-sticky table thead {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.fix-sticky table thead.thead-light {
|
||||
background: #e9ecef;
|
||||
}
|
||||
|
||||
.fix-sticky table thead.thead-dark {
|
||||
background: #212529;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "Sticky Header",
|
||||
"version": "1.0.0",
|
||||
"description": "An extension which provides a sticky header for table columns when scrolling on a long page and / or table. Works for tables with many columns and narrow width with horizontal scrollbars too.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/sticky-header",
|
||||
"example": "http://issues.wenzhixin.net.cn/bootstrap-table/#extensions/sticky-header.html",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-sticky-header",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/sticky-header"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "vinzloh",
|
||||
"image": "https://avatars0.githubusercontent.com/u/5501845"
|
||||
}
|
||||
}
|
||||
+360
@@ -0,0 +1,360 @@
|
||||
/**
|
||||
* @author: aperez <aperez@datadec.es>
|
||||
* @version: v2.0.0
|
||||
*
|
||||
* @update Dennis Hernández <http://djhvscf.github.io/Blog>
|
||||
* @update zhixin wen <wenzhixin2010@gmail.com>
|
||||
*/
|
||||
|
||||
const Utils = $.fn.bootstrapTable.utils
|
||||
|
||||
const bootstrap = {
|
||||
bootstrap3: {
|
||||
icons: {
|
||||
advancedSearchIcon: 'glyphicon-chevron-down'
|
||||
},
|
||||
html: {
|
||||
modal: `
|
||||
<div id="avdSearchModal_%s" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-xs">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">%s</h4>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body modal-body-custom">
|
||||
<div class="container-fluid" id="avdSearchModalContent_%s"
|
||||
style="padding-right: 0px; padding-left: 0px;" >
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" id="btnCloseAvd_%s" class="btn btn-%s">%s</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
},
|
||||
bootstrap4: {
|
||||
icons: {
|
||||
advancedSearchIcon: 'fa-chevron-down'
|
||||
},
|
||||
html: {
|
||||
modal: `
|
||||
<div id="avdSearchModal_%s" class="modal fade" tabindex="-1" role="dialog" aria-labelledby="mySmallModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-xs">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h4 class="modal-title">%s</h4>
|
||||
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
</div>
|
||||
<div class="modal-body modal-body-custom">
|
||||
<div class="container-fluid" id="avdSearchModalContent_%s"
|
||||
style="padding-right: 0px; padding-left: 0px;" >
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button type="button" id="btnCloseAvd_%s" class="btn btn-%s">%s</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
},
|
||||
bulma: {
|
||||
icons: {
|
||||
advancedSearchIcon: 'fa-chevron-down'
|
||||
},
|
||||
html: {
|
||||
modal: `
|
||||
<div class="modal" id="avdSearchModal_%s">
|
||||
<div class="modal-background"></div>
|
||||
<div class="modal-card">
|
||||
<header class="modal-card-head">
|
||||
<p class="modal-card-title">%s</p>
|
||||
<button class="delete" aria-label="close"></button>
|
||||
</header>
|
||||
<section class="modal-card-body" id="avdSearchModalContent_%s"></section>
|
||||
<footer class="modal-card-foot">
|
||||
<button class="button" id="btnCloseAvd_%s" data-close="btn btn-%s">%s</button>
|
||||
</footer>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
},
|
||||
foundation: {
|
||||
icons: {
|
||||
advancedSearchIcon: 'fa-chevron-down'
|
||||
},
|
||||
html: {
|
||||
modal: `
|
||||
<div class="reveal" id="avdSearchModal_%s" data-reveal>
|
||||
<h1>%s</h1>
|
||||
<div id="avdSearchModalContent_%s">
|
||||
|
||||
</div>
|
||||
<button class="close-button" data-close aria-label="Close modal" type="button">
|
||||
<span aria-hidden="true">×</span>
|
||||
</button>
|
||||
|
||||
<button id="btnCloseAvd_%s" class="%s" type="button">%s</button>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
},
|
||||
materialize: {
|
||||
icons: {
|
||||
advancedSearchIcon: 'expand_more'
|
||||
},
|
||||
html: {
|
||||
modal: `
|
||||
<div id="avdSearchModal_%s" class="modal">
|
||||
<div class="modal-content">
|
||||
<h4>%s</h4>
|
||||
<div id="avdSearchModalContent_%s">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<a href="javascript:void(0)"" id="btnCloseAvd_%s" class="modal-close waves-effect waves-green btn-flat %s">%s</a>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
},
|
||||
semantic: {
|
||||
icons: {
|
||||
advancedSearchIcon: 'fa-chevron-down'
|
||||
},
|
||||
html: {
|
||||
modal: `
|
||||
<div class="ui modal" id="avdSearchModal_%s">
|
||||
<i class="close icon"></i>
|
||||
<div class="header">
|
||||
%s
|
||||
</div>
|
||||
<div class="image content ui form" id="avdSearchModalContent_%s"></div>
|
||||
<div class="actions">
|
||||
<div id="btnCloseAvd_%s" class="ui black deny button %s">%s</div>
|
||||
</div>
|
||||
</div>
|
||||
`
|
||||
}
|
||||
}
|
||||
}[$.fn.bootstrapTable.theme]
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, {
|
||||
advancedSearch: false,
|
||||
idForm: 'advancedSearch',
|
||||
actionForm: '',
|
||||
idTable: undefined,
|
||||
onColumnAdvancedSearch (field, text) {
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults.icons, {
|
||||
advancedSearchIcon: bootstrap.icons.advancedSearchIcon
|
||||
})
|
||||
|
||||
$.extend($.fn.bootstrapTable.Constructor.EVENTS, {
|
||||
'column-advanced-search.bs.table': 'onColumnAdvancedSearch'
|
||||
})
|
||||
|
||||
$.extend($.fn.bootstrapTable.locales, {
|
||||
formatAdvancedSearch () {
|
||||
return 'Advanced search'
|
||||
},
|
||||
formatAdvancedCloseButton () {
|
||||
return 'Close'
|
||||
}
|
||||
})
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, $.fn.bootstrapTable.locales)
|
||||
|
||||
$.BootstrapTable = class extends $.BootstrapTable {
|
||||
initToolbar () {
|
||||
const o = this.options
|
||||
|
||||
this.showToolbar = this.showToolbar ||
|
||||
(o.search &&
|
||||
o.advancedSearch &&
|
||||
o.idTable)
|
||||
|
||||
super.initToolbar()
|
||||
|
||||
if (!o.search || !o.advancedSearch || !o.idTable) {
|
||||
return
|
||||
}
|
||||
|
||||
this.$toolbar.find('>.columns').append(`
|
||||
<button class="${this.constants.buttonsClass} "
|
||||
type="button"
|
||||
name="advancedSearch"
|
||||
aria-label="advanced search"
|
||||
title="${o.formatAdvancedSearch()}">
|
||||
${ this.options.showButtonIcons ? Utils.sprintf(this.constants.html.icon, o.iconsPrefix, o.icons.advancedSearchIcon) : ''}
|
||||
${ this.options.showButtonText ? this.options.formatAdvancedSearch() : ''}
|
||||
</button>
|
||||
`)
|
||||
|
||||
this.$toolbar.find('button[name="advancedSearch"]').off('click').on('click', () => this.showAvdSearch())
|
||||
}
|
||||
|
||||
showAvdSearch () {
|
||||
const o = this.options
|
||||
const modalSelector = '#avdSearchModal_' + o.idTable
|
||||
if ($(modalSelector).length <= 0) {
|
||||
$('body').append(Utils.sprintf(bootstrap.html.modal, o.idTable, o.formatAdvancedSearch(), o.idTable, o.idTable, o.buttonsClass, o.formatAdvancedCloseButton()))
|
||||
|
||||
let timeoutId = 0
|
||||
|
||||
$(`#avdSearchModalContent_${o.idTable}`).append(this.createFormAvd().join(''))
|
||||
|
||||
$(`#${o.idForm}`).off('keyup blur', 'input').on('keyup blur', 'input', e => {
|
||||
if (o.sidePagination === 'server') {
|
||||
this.onColumnAdvancedSearch(e)
|
||||
} else {
|
||||
clearTimeout(timeoutId)
|
||||
timeoutId = setTimeout(() => {
|
||||
this.onColumnAdvancedSearch(e)
|
||||
}, o.searchTimeOut)
|
||||
}
|
||||
})
|
||||
|
||||
$(`#btnCloseAvd_${o.idTable}`).click(() => this.hideModal())
|
||||
|
||||
if ($.fn.bootstrapTable.theme === 'bulma') {
|
||||
$(modalSelector).find('.delete').off('click').on('click', () => this.hideModal())
|
||||
}
|
||||
|
||||
this.showModal()
|
||||
} else {
|
||||
this.showModal()
|
||||
}
|
||||
}
|
||||
|
||||
showModal () {
|
||||
const modalSelector = '#avdSearchModal_' + this.options.idTable
|
||||
if ($.inArray($.fn.bootstrapTable.theme, ['bootstrap3', 'bootstrap4']) !== -1) {
|
||||
$(modalSelector).modal()
|
||||
} else if ($.fn.bootstrapTable.theme === 'bulma') {
|
||||
$(modalSelector).toggleClass('is-active')
|
||||
} else if ($.fn.bootstrapTable.theme === 'foundation') {
|
||||
if (!this.toolbarModal) {
|
||||
// eslint-disable-next-line no-undef
|
||||
this.toolbarModal = new Foundation.Reveal($(modalSelector))
|
||||
}
|
||||
this.toolbarModal.open()
|
||||
} else if ($.fn.bootstrapTable.theme === 'materialize') {
|
||||
$(modalSelector).modal()
|
||||
$(modalSelector).modal('open')
|
||||
} else if ($.fn.bootstrapTable.theme === 'semantic') {
|
||||
$(modalSelector).modal('show')
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
hideModal () {
|
||||
const $closeModalButton = $(`#avdSearchModal_${this.options.idTable}`)
|
||||
const modalSelector = '#avdSearchModal_' + this.options.idTable
|
||||
if ($.inArray($.fn.bootstrapTable.theme, ['bootstrap3', 'bootstrap4']) !== -1) {
|
||||
$closeModalButton.modal('hide')
|
||||
} else if ($.fn.bootstrapTable.theme === 'bulma') {
|
||||
$('html').toggleClass('is-clipped')
|
||||
$(modalSelector).toggleClass('is-active')
|
||||
} else if ($.fn.bootstrapTable.theme === 'foundation') {
|
||||
this.toolbarModal.close()
|
||||
} else if ($.fn.bootstrapTable.theme === 'materialize') {
|
||||
$(modalSelector).modal('open')
|
||||
} else if ($.fn.bootstrapTable.theme === 'semantic') {
|
||||
$(modalSelector).modal('close')
|
||||
}
|
||||
|
||||
if (this.options.sidePagination === 'server') {
|
||||
this.options.pageNumber = 1
|
||||
this.updatePagination()
|
||||
this.trigger('column-advanced-search', this.filterColumnsPartial)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
createFormAvd () {
|
||||
const o = this.options
|
||||
const html = [`<form class="form-horizontal" id="${o.idForm}" action="${o.actionForm}">`]
|
||||
|
||||
for (const column of this.columns) {
|
||||
if (!column.checkbox && column.visible && column.searchable) {
|
||||
html.push(`
|
||||
<div class="form-group row">
|
||||
<label class="col-sm-4 control-label">${column.title}</label>
|
||||
<div class="col-sm-6">
|
||||
<input type="text" class="form-control ${this.constants.classes.input}" name="${column.field}" placeholder="${column.title}" id="${column.field}">
|
||||
</div>
|
||||
</div>
|
||||
`)
|
||||
}
|
||||
}
|
||||
|
||||
html.push('</form>')
|
||||
|
||||
return html
|
||||
}
|
||||
|
||||
initSearch () {
|
||||
super.initSearch()
|
||||
|
||||
if (!this.options.advancedSearch || this.options.sidePagination === 'server') {
|
||||
return
|
||||
}
|
||||
|
||||
const fp = $.isEmptyObject(this.filterColumnsPartial) ? null : this.filterColumnsPartial
|
||||
|
||||
this.data = fp ? this.data.filter((item, i) => {
|
||||
for (const [key, v] of Object.entries(fp)) {
|
||||
const fval = v.toLowerCase()
|
||||
let value = item[key]
|
||||
const index = this.header.fields.indexOf(key)
|
||||
value = Utils.calculateObjectValue(this.header,
|
||||
this.header.formatters[index], [value, item, i], value)
|
||||
|
||||
if (
|
||||
!(index !== -1 &&
|
||||
(typeof value === 'string' || typeof value === 'number') &&
|
||||
(`${value}`).toLowerCase().includes(fval))
|
||||
) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}) : this.data
|
||||
}
|
||||
|
||||
onColumnAdvancedSearch (e) {
|
||||
const text = $.trim($(e.currentTarget).val())
|
||||
const $field = $(e.currentTarget)[0].id
|
||||
|
||||
if ($.isEmptyObject(this.filterColumnsPartial)) {
|
||||
this.filterColumnsPartial = {}
|
||||
}
|
||||
if (text) {
|
||||
this.filterColumnsPartial[$field] = text
|
||||
} else {
|
||||
delete this.filterColumnsPartial[$field]
|
||||
}
|
||||
|
||||
if (this.options.sidePagination !== 'server') {
|
||||
this.options.pageNumber = 1
|
||||
this.onSearch(e)
|
||||
this.updatePagination()
|
||||
this.trigger('column-advanced-search', $field, text)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"name": "Toolbar",
|
||||
"version": "2.0.0",
|
||||
"description": "Plugin to support the advanced search.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/toolbar",
|
||||
"example": "http://issues.wenzhixin.net.cn/bootstrap-table/#extensions/toolbar.html",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-toolbar",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/toolbar"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "djhvscf",
|
||||
"image": "https://avatars1.githubusercontent.com/u/4496763"
|
||||
}
|
||||
}
|
||||
+10
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,124 @@
|
||||
document.write('<script type="text/javascript" src="../../bootstrap/js/authorization/ksort.js"></script>');
|
||||
document.write('<script type="text/javascript" src="../../bootstrap/js/authorization/crypto-js.min.js"></script>');
|
||||
document.write('<script type="text/javascript" src="../../bootstrap/js/authorization/hmac-sha256.js"></script>');
|
||||
document.write('<script type="text/javascript" src="../../bootstrap/js/authorization/enc-base64.min.js"></script>');
|
||||
document.write('<script type="text/javascript" src="../../bootstrap/js/jquery.cookie.min.js"></script>');
|
||||
document.write('<div style="display:none"><script type="text/javascript">document.write(unescape("%3Cspan id=\'cnzz_stat_icon_1279911342\'%3E%3C/span%3E%3Cscript src=\'https://v1.cnzz.com/z_stat.php%3Fid%3D1279911342%26\' type=\'text/javascript\'%3E%3C/script%3E"));</script></div>');
|
||||
|
||||
function GenerateAuthorization(path, method, params) {
|
||||
let key = "admin";
|
||||
let secret = "12878dd962115106db6d";
|
||||
|
||||
let date = new Date();
|
||||
let datetime = date.getFullYear() + "-" // "年"
|
||||
+ ((date.getMonth() + 1) > 10 ? (date.getMonth() + 1) : "0" + (date.getMonth() + 1)) + "-" // "月"
|
||||
+ (date.getDate() < 10 ? "0" + date.getDate() : date.getDate()) + " " // "日"
|
||||
+ (date.getHours() < 10 ? "0" + date.getHours() : date.getHours()) + ":" // "小时"
|
||||
+ (date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes()) + ":" // "分钟"
|
||||
+ (date.getSeconds() < 10 ? "0" + date.getSeconds() : date.getSeconds()); // "秒"
|
||||
|
||||
let sortParamsEncode = decodeURIComponent(jQuery.param(ksort(params)));
|
||||
let encryptStr = path + "|" + method.toUpperCase() + "|" + sortParamsEncode + "|" + datetime;
|
||||
let digest = CryptoJS.enc.Base64.stringify(CryptoJS.HmacSHA256(encryptStr, secret));
|
||||
return {authorization: key + " " + digest, date: datetime};
|
||||
}
|
||||
|
||||
function IsJson(str) {
|
||||
if (typeof str == 'string') {
|
||||
try {
|
||||
let obj = JSON.parse(str);
|
||||
if (typeof obj == 'object' && obj) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('error:' + str + '!!!' + e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
console.log('It is not a string!')
|
||||
}
|
||||
|
||||
function AjaxError(response) {
|
||||
let errCode = response.status;
|
||||
let errMsg = response.responseText;
|
||||
|
||||
if (errCode === 401) { // 跳转到登录页
|
||||
// 关闭当前界面
|
||||
parent.window.close();
|
||||
window.open("/login");
|
||||
return;
|
||||
}
|
||||
|
||||
if (IsJson(response.responseText)) {
|
||||
const errInfo = JSON.parse(response.responseText);
|
||||
errCode = errInfo.code;
|
||||
errMsg = errInfo.message;
|
||||
}
|
||||
|
||||
$.alert({
|
||||
title: '错误提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'red',
|
||||
content: '错误码:' + errCode + '<br/>' + '错误信息:' + errMsg,
|
||||
});
|
||||
}
|
||||
|
||||
function AjaxForm(method, url, params, beforeSendFunction, successFunction, errorFunction) {
|
||||
let authorizationData = GenerateAuthorization(url, method, params);
|
||||
|
||||
$.ajax({
|
||||
url: url,
|
||||
type: method,
|
||||
data: params,
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
|
||||
'Authorization': authorizationData.authorization,
|
||||
'Authorization-Date': authorizationData.date,
|
||||
'Token': $.cookie("_login_token_"),
|
||||
},
|
||||
beforeSend: beforeSendFunction,
|
||||
success: successFunction,
|
||||
error: errorFunction,
|
||||
});
|
||||
}
|
||||
|
||||
function AjaxFormNoAsync(method, url, params, beforeSendFunction, successFunction, errorFunction) {
|
||||
let authorizationData = GenerateAuthorization(url, method, params);
|
||||
|
||||
$.ajax({
|
||||
url: url,
|
||||
type: method,
|
||||
data: params,
|
||||
async: false,
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8',
|
||||
'Authorization': authorizationData.authorization,
|
||||
'Authorization-Date': authorizationData.date,
|
||||
'Token': $.cookie("_login_token_"),
|
||||
},
|
||||
beforeSend: beforeSendFunction,
|
||||
success: successFunction,
|
||||
error: errorFunction,
|
||||
});
|
||||
}
|
||||
|
||||
function AjaxPostJson(url, params, beforeSendFunction, successFunction, errorFunction) {
|
||||
let authorizationData = GenerateAuthorization(url, "POST", params);
|
||||
|
||||
$.ajax({
|
||||
url: url,
|
||||
type: "POST",
|
||||
data: JSON.stringify(params),
|
||||
headers: {
|
||||
'Content-Type': 'application/json; charset=utf-8',
|
||||
'Authorization': authorizationData.authorization,
|
||||
'Authorization-Date': authorizationData.date,
|
||||
'Token': $.cookie("_login_token_"),
|
||||
},
|
||||
beforeSend: beforeSendFunction,
|
||||
success: successFunction,
|
||||
error: errorFunction,
|
||||
});
|
||||
}
|
||||
Vendored
+9
-4
@@ -113,22 +113,27 @@
|
||||
// 选项卡
|
||||
$('#iframe-content').multitabs({
|
||||
iframe : true,
|
||||
refresh : 'no', // iframe中页面是否刷新,'no':'从不刷新','nav':'点击菜单刷新','all':'菜单和tab点击都刷新'
|
||||
refresh : 'nav', // iframe中页面是否刷新,'no':'从不刷新','nav':'点击菜单刷新','all':'菜单和tab点击都刷新'
|
||||
nav: {
|
||||
backgroundColor: '#ffffff',
|
||||
maxTabs : 35, // 选项卡最大值
|
||||
},
|
||||
init : [{
|
||||
type : 'main',
|
||||
title : '仪表盘',
|
||||
url : '/dashboard'
|
||||
title : $.cookie('_nav_title_') ? $.cookie('_nav_title_') : '仪表盘',
|
||||
url : $.cookie('_nav_url_') ? $.cookie('_nav_url_') : '/dashboard',
|
||||
}]
|
||||
});
|
||||
|
||||
|
||||
$(document).on('click', '.nav-item .multitabs', function() {
|
||||
$('.nav-item').removeClass('active');
|
||||
$('.nav-subnav li').removeClass('active');
|
||||
$(this).parent('li').addClass('active');
|
||||
$(this).parents('.nav-item-has-subnav').addClass('open').first().addClass('active');
|
||||
|
||||
var date = new Date();
|
||||
date.setTime(date.getTime() + 24 * 60 * 60 * 1000); // 24 * 60 * 60 * 1000 表示 24 小时
|
||||
$.cookie('_nav_url_', $(this).attr('href'), {expires: date});
|
||||
$.cookie('_nav_title_', $(this).text(), {expires: date});
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1 @@
|
||||
.treegrid-indent{width:16px;height:16px;display:inline-block;position:relative}.treegrid-expander{width:16px;height:16px;display:inline-block;position:relative;cursor:pointer}
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
jQuery twitter bootstrap wizard plugin
|
||||
Examples and documentation at: http://github.com/VinceG/twitter-bootstrap-wizard
|
||||
version 1.4.2
|
||||
Requires jQuery v1.3.2 or later
|
||||
Supports Bootstrap 2.2.x, 2.3.x, 3.0
|
||||
Dual licensed under the MIT and GPL licenses:
|
||||
http://www.opensource.org/licenses/mit-license.php
|
||||
http://www.gnu.org/licenses/gpl.html
|
||||
Authors: Vadim Vincent Gabriel (http://vadimg.com), Jason Gill (www.gilluminate.com)
|
||||
*/
|
||||
(function(c){var n=function(d,k){d=c(d);var a=this,h=[],b=c.extend({},c.fn.bootstrapWizard.defaults,k),f=null,e=null;this.rebindClick=function(b,a){b.unbind("click",a).bind("click",a)};this.fixNavigationButtons=function(){f.length||(e.find("a:first").tab("show"),f=e.find('li:has([data-toggle="tab"]):first'));c(b.previousSelector,d).toggleClass("disabled",a.firstIndex()>=a.currentIndex());c(b.nextSelector,d).toggleClass("disabled",a.currentIndex()>=a.navigationLength());c(b.nextSelector,d).toggleClass("d-none",
|
||||
a.currentIndex()>=a.navigationLength()&&0<c(b.finishSelector,d).length);c(b.lastSelector,d).toggleClass("d-none",a.currentIndex()>=a.navigationLength()&&0<c(b.finishSelector,d).length);c(b.finishSelector,d).toggleClass("d-none",a.currentIndex()<a.navigationLength());c(b.backSelector,d).toggleClass("disabled",0==h.length);c(b.backSelector,d).toggleClass("d-none",a.currentIndex()>=a.navigationLength()&&0<c(b.finishSelector,d).length);a.rebindClick(c(b.nextSelector,d),a.next);a.rebindClick(c(b.previousSelector,
|
||||
d),a.previous);a.rebindClick(c(b.lastSelector,d),a.last);a.rebindClick(c(b.firstSelector,d),a.first);a.rebindClick(c(b.finishSelector,d),a.finish);a.rebindClick(c(b.backSelector,d),a.back);if(b.onTabShow&&"function"===typeof b.onTabShow&&!1===b.onTabShow(f,e,a.currentIndex()))return!1};this.next=function(g){if(d.hasClass("last")||b.onNext&&"function"===typeof b.onNext&&!1===b.onNext(f,e,a.nextIndex()))return!1;g=a.currentIndex();var c=a.nextIndex();c>a.navigationLength()||(h.push(g),e.find('li:has([data-toggle="tab"])'+
|
||||
(b.withVisible?":visible":"")+":eq("+c+") a").tab("show"))};this.previous=function(g){if(d.hasClass("first")||b.onPrevious&&"function"===typeof b.onPrevious&&!1===b.onPrevious(f,e,a.previousIndex()))return!1;g=a.currentIndex();var c=a.previousIndex();0>c||(h.push(g),e.find('li:has([data-toggle="tab"])'+(b.withVisible?":visible":"")+":eq("+c+") a").tab("show"))};this.first=function(g){if(b.onFirst&&"function"===typeof b.onFirst&&!1===b.onFirst(f,e,a.firstIndex())||d.hasClass("disabled"))return!1;h.push(a.currentIndex());
|
||||
e.find('li:has([data-toggle="tab"]):eq(0) a').tab("show")};this.last=function(g){if(b.onLast&&"function"===typeof b.onLast&&!1===b.onLast(f,e,a.lastIndex())||d.hasClass("disabled"))return!1;h.push(a.currentIndex());e.find('li:has([data-toggle="tab"]):eq('+a.navigationLength()+") a").tab("show")};this.finish=function(g){if(b.onFinish&&"function"===typeof b.onFinish)b.onFinish(f,e,a.lastIndex())};this.back=function(){if(0==h.length)return null;var a=h.pop();if(b.onBack&&"function"===typeof b.onBack&&
|
||||
!1===b.onBack(f,e,a))return h.push(a),!1;d.find('li:has([data-toggle="tab"]):eq('+a+") a").tab("show")};this.currentIndex=function(){return e.find('li:has([data-toggle="tab"])'+(b.withVisible?":visible":"")).index(f)};this.firstIndex=function(){return 0};this.lastIndex=function(){return a.navigationLength()};this.getIndex=function(a){return e.find('li:has([data-toggle="tab"])'+(b.withVisible?":visible":"")).index(a)};this.nextIndex=function(){var a=this.currentIndex(),c;do a++,c=e.find('li:has([data-toggle="tab"])'+
|
||||
(b.withVisible?":visible":"")+":eq("+a+")");while(c&&c.hasClass("disabled"));return a};this.previousIndex=function(){var a=this.currentIndex(),c;do a--,c=e.find('li:has([data-toggle="tab"])'+(b.withVisible?":visible":"")+":eq("+a+")");while(c&&c.hasClass("disabled"));return a};this.navigationLength=function(){return e.find('li:has([data-toggle="tab"])'+(b.withVisible?":visible":"")).length-1};this.activeTab=function(){return f};this.nextTab=function(){return e.find('li:has([data-toggle="tab"]):eq('+
|
||||
(a.currentIndex()+1)+")").length?e.find('li:has([data-toggle="tab"]):eq('+(a.currentIndex()+1)+")"):null};this.previousTab=function(){return 0>=a.currentIndex()?null:e.find('li:has([data-toggle="tab"]):eq('+parseInt(a.currentIndex()-1)+")")};this.show=function(b){b=isNaN(b)?d.find('li:has([data-toggle="tab"]) a[href="#'+b+'"]'):d.find('li:has([data-toggle="tab"]):eq('+b+") a");0<b.length&&(h.push(a.currentIndex()),b.tab("show"))};this.disable=function(a){e.find('li:has([data-toggle="tab"]):eq('+a+
|
||||
")").addClass("disabled")};this.enable=function(a){e.find('li:has([data-toggle="tab"]):eq('+a+")").removeClass("disabled")};this.hide=function(a){e.find('li:has([data-toggle="tab"]):eq('+a+")").hide()};this.display=function(a){e.find('li:has([data-toggle="tab"]):eq('+a+")").show()};this.remove=function(a){var b="undefined"!=typeof a[1]?a[1]:!1;a=e.find('li:has([data-toggle="tab"]):eq('+a[0]+")");b&&(b=a.find("a").attr("href"),c(b).remove());a.remove()};var l=function(d){var g=e.find('li:has([data-toggle="tab"])');
|
||||
d=g.index(c(d.currentTarget).parent('li:has([data-toggle="tab"])'));g=c(g[d]);if(b.onTabClick&&"function"===typeof b.onTabClick&&!1===b.onTabClick(f,e,a.currentIndex(),d,g))return!1},m=function(d){d=c(d.target).parent();var g=e.find('li:has([data-toggle="tab"])').index(d);if(d.hasClass("disabled")||b.onTabChange&&"function"===typeof b.onTabChange&&!1===b.onTabChange(f,e,a.currentIndex(),g))return!1;f=d;a.fixNavigationButtons()};this.resetWizard=function(){c('a[data-toggle="tab"]',e).off("click",l);
|
||||
c('a[data-toggle="tab"]',e).off("show show.bs.tab",m);e=d.find("ul:first",d);f=e.find('li:has([data-toggle="tab"]).active',d);c('a[data-toggle="tab"]',e).on("click",l);c('a[data-toggle="tab"]',e).on("show show.bs.tab",m);a.fixNavigationButtons()};e=d.find("ul:first",d);f=e.find('li:has([data-toggle="tab"]).active',d);e.hasClass(b.tabClass)||e.addClass(b.tabClass);if(b.onInit&&"function"===typeof b.onInit)b.onInit(f,e,0);if(b.onShow&&"function"===typeof b.onShow)b.onShow(f,e,a.nextIndex());c('a[data-toggle="tab"]',
|
||||
e).on("click",l);c('a[data-toggle="tab"]',e).on("show show.bs.tab",m)};c.fn.bootstrapWizard=function(d){if("string"==typeof d){var k=Array.prototype.slice.call(arguments,1);1===k.length&&k.toString();return this.data("bootstrapWizard")[d](k)}return this.each(function(a){a=c(this);if(!a.data("bootstrapWizard")){var h=new n(a,d);a.data("bootstrapWizard",h);h.fixNavigationButtons()}})};c.fn.bootstrapWizard.defaults={withVisible:!0,tabClass:"nav nav-pills",nextSelector:".wizard li.next",previousSelector:".wizard li.previous",
|
||||
firstSelector:".wizard li.first",lastSelector:".wizard li.last",finishSelector:".wizard li.finish",backSelector:".wizard li.back",onShow:null,onInit:null,onNext:null,onPrevious:null,onLast:null,onFirst:null,onFinish:null,onBack:null,onTabChange:null,onTabClick:null,onTabShow:null}})(jQuery);
|
||||
@@ -0,0 +1,292 @@
|
||||
/**
|
||||
* pagination.js 1.5.1
|
||||
* A jQuery plugin to provide simple yet fully customisable pagination.
|
||||
* @version 1.5.1
|
||||
* @author mss
|
||||
* @url https://github.com/Maxiaoxiang/jQuery-plugins
|
||||
*
|
||||
* @调用方法
|
||||
* $(selector).pagination(option, callback);
|
||||
* -此处callback是初始化调用,option里的callback是点击页码后调用
|
||||
*
|
||||
* -- example --
|
||||
* $(selector).pagination({
|
||||
* ... // 配置参数
|
||||
* callback: function(api) {
|
||||
* console.log('点击页码调用该回调'); //切换页码时执行一次回调
|
||||
* }
|
||||
* }, function(){
|
||||
* console.log('初始化'); //插件初始化时调用该方法,比如请求第一次接口来初始化分页配置
|
||||
* });
|
||||
*/
|
||||
;
|
||||
(function (factory) {
|
||||
if (typeof define === "function" && (define.amd || define.cmd) && !jQuery) {
|
||||
// AMD或CMD
|
||||
define(["jquery"], factory);
|
||||
} else if (typeof module === 'object' && module.exports) {
|
||||
// Node/CommonJS
|
||||
module.exports = function (root, jQuery) {
|
||||
if (jQuery === undefined) {
|
||||
if (typeof window !== 'undefined') {
|
||||
jQuery = require('jquery');
|
||||
} else {
|
||||
jQuery = require('jquery')(root);
|
||||
}
|
||||
}
|
||||
factory(jQuery);
|
||||
return jQuery;
|
||||
};
|
||||
} else {
|
||||
//Browser globals
|
||||
factory(jQuery);
|
||||
}
|
||||
}(function ($) {
|
||||
|
||||
//配置参数
|
||||
var defaults = {
|
||||
totalData: 0, //数据总条数
|
||||
showData: 0, //每页显示的条数
|
||||
pageCount: 9, //总页数,默认为9
|
||||
current: 1, //当前第几页
|
||||
prevCls: 'prev', //上一页class
|
||||
nextCls: 'next', //下一页class
|
||||
prevContent: '<', //上一页内容
|
||||
nextContent: '>', //下一页内容
|
||||
activeCls: 'active', //当前页选中状态
|
||||
coping: false, //首页和尾页
|
||||
isHide: false, //当前页数为0页或者1页时不显示分页
|
||||
homePage: '', //首页节点内容
|
||||
endPage: '', //尾页节点内容
|
||||
keepShowPN: false, //是否一直显示上一页下一页
|
||||
mode: 'unfixed', //分页模式,unfixed:不固定页码数量,fixed:固定页码数量
|
||||
count: 4, //mode为unfixed时显示当前选中页前后页数,mode为fixed显示页码总数
|
||||
jump: false, //跳转到指定页数
|
||||
jumpIptCls: 'jump-ipt', //文本框内容
|
||||
jumpBtnCls: 'jump-btn', //跳转按钮
|
||||
jumpBtn: '跳转', //跳转按钮文本
|
||||
callback: function () {} //回调
|
||||
};
|
||||
|
||||
var Pagination = function (element, options) {
|
||||
//全局变量
|
||||
var opts = options, //配置
|
||||
current, //当前页
|
||||
$document = $(document),
|
||||
$obj = $(element); //容器
|
||||
|
||||
/**
|
||||
* 设置总页数
|
||||
* @param {int} page 页码
|
||||
* @return opts.pageCount 总页数配置
|
||||
*/
|
||||
this.setPageCount = function (page) {
|
||||
return opts.pageCount = page;
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取总页数
|
||||
* 如果配置了总条数和每页显示条数,将会自动计算总页数并略过总页数配置,反之
|
||||
* @return {int} 总页数
|
||||
*/
|
||||
this.getPageCount = function () {
|
||||
return opts.totalData && opts.showData ? Math.ceil(parseInt(opts.totalData) / opts.showData) : opts.pageCount;
|
||||
};
|
||||
|
||||
/**
|
||||
* 获取当前页
|
||||
* @return {int} 当前页码
|
||||
*/
|
||||
this.getCurrent = function () {
|
||||
return current;
|
||||
};
|
||||
|
||||
/**
|
||||
* 填充数据
|
||||
* @param {int} 页码
|
||||
*/
|
||||
this.filling = function (index) {
|
||||
var html = '';
|
||||
current = parseInt(index) || parseInt(opts.current); //当前页码
|
||||
var pageCount = this.getPageCount(); //获取的总页数
|
||||
switch (opts.mode) { //配置模式
|
||||
case 'fixed': //固定按钮模式
|
||||
html += '<li class="page-item"><a href="javascript:;" class="page-link ' + opts.prevCls + '">' + opts.prevContent + '</a></li>';
|
||||
if (opts.coping) {
|
||||
var home = opts.coping && opts.homePage ? opts.homePage : '1';
|
||||
html += '<li class="page-item"><a class="page-link" href="javascript:;" data-page="1">' + home + '</a></li>';
|
||||
}
|
||||
var start = current > opts.count - 1 ? current + opts.count - 1 > pageCount ? current - (opts.count - (pageCount - current)) : current - 2 : 1;
|
||||
var end = current + opts.count - 1 > pageCount ? pageCount : start + opts.count;
|
||||
for (; start <= end; start++) {
|
||||
if (start != current) {
|
||||
html += '<li class="page-item"><a class="page-link" href="javascript:;" data-page="' + start + '">' + start + '</a></li>';
|
||||
} else {
|
||||
html += '<li class="page-item active"><span class="page-link ' + opts.activeCls + '">' + start + '</span></li>';
|
||||
}
|
||||
}
|
||||
if (opts.coping) {
|
||||
var _end = opts.coping && opts.endPage ? opts.endPage : pageCount;
|
||||
html += '<li class="page-item"><a class="page-link" href="javascript:;" data-page="' + pageCount + '">' + _end + '</a></li>';
|
||||
}
|
||||
html += '<li class="page-item"><a href="javascript:;" class="page-link ' + opts.nextCls + '">' + opts.nextContent + '</a></li>';
|
||||
break;
|
||||
|
||||
// if (opts.keepShowPN || current > 1) { //上一页
|
||||
// html += '<a href="javascript:;" class="' + opts.prevCls + '">' + opts.prevContent + '</a>';
|
||||
// } else {
|
||||
// if (opts.keepShowPN == false) {
|
||||
// $obj.find('.' + opts.prevCls) && $obj.find('.' + opts.prevCls).remove();
|
||||
// }
|
||||
// }
|
||||
// if (current >= opts.count + 2 && current != 1 && pageCount != opts.count) {
|
||||
// var home = opts.coping && opts.homePage ? opts.homePage : '1';
|
||||
// html += opts.coping ? '<a href="javascript:;" data-page="1">' + home + '</a><span>...</span>' : '';
|
||||
// }
|
||||
// var start = (current - opts.count) <= 1 ? 1 : (current - opts.count);
|
||||
// var end = (current + opts.count) >= pageCount ? pageCount : (current + opts.count);
|
||||
// for (; start <= end; start++) {
|
||||
// if (start <= pageCount && start >= 1) {
|
||||
// if (start != current) {
|
||||
// html += '<a href="javascript:;" data-page="' + start + '">' + start + '</a>';
|
||||
// } else {
|
||||
// html += '<span class="' + opts.activeCls + '">' + start + '</span>';
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// if (current + opts.count < pageCount && current >= 1 && pageCount > opts.count) {
|
||||
// var end = opts.coping && opts.endPage ? opts.endPage : pageCount;
|
||||
// html += opts.coping ? '<span>...</span><a href="javascript:;" data-page="' + pageCount + '">' + end + '</a>' : '';
|
||||
// }
|
||||
// if (opts.keepShowPN || current < pageCount) { //下一页
|
||||
// html += '<a href="javascript:;" class="' + opts.nextCls + '">' + opts.nextContent + '</a>';
|
||||
// } else {
|
||||
// if (opts.keepShowPN == false) {
|
||||
// $obj.find('.' + opts.nextCls) && $obj.find('.' + opts.nextCls).remove();
|
||||
// }
|
||||
// }
|
||||
// break;
|
||||
|
||||
case 'unfixed': //不固定按钮模式
|
||||
if (opts.keepShowPN || current > 1) { //上一页
|
||||
html += '<a href="javascript:;" class="' + opts.prevCls + '">' + opts.prevContent + '</a>';
|
||||
} else {
|
||||
if (opts.keepShowPN == false) {
|
||||
$obj.find('.' + opts.prevCls) && $obj.find('.' + opts.prevCls).remove();
|
||||
}
|
||||
}
|
||||
if (current >= opts.count + 2 && current != 1 && pageCount != opts.count) {
|
||||
var home = opts.coping && opts.homePage ? opts.homePage : '1';
|
||||
html += opts.coping ? '<a href="javascript:;" data-page="1">' + home + '</a><span>...</span>' : '';
|
||||
}
|
||||
var start = (current - opts.count) <= 1 ? 1 : (current - opts.count);
|
||||
var end = (current + opts.count) >= pageCount ? pageCount : (current + opts.count);
|
||||
for (; start <= end; start++) {
|
||||
if (start <= pageCount && start >= 1) {
|
||||
if (start != current) {
|
||||
html += '<a href="javascript:;" data-page="' + start + '">' + start + '</a>';
|
||||
} else {
|
||||
html += '<span class="' + opts.activeCls + '">' + start + '</span>';
|
||||
}
|
||||
}
|
||||
}
|
||||
if (current + opts.count < pageCount && current >= 1 && pageCount > opts.count) {
|
||||
var end = opts.coping && opts.endPage ? opts.endPage : pageCount;
|
||||
html += opts.coping ? '<span>...</span><a href="javascript:;" data-page="' + pageCount + '">' + end + '</a>' : '';
|
||||
}
|
||||
if (opts.keepShowPN || current < pageCount) { //下一页
|
||||
html += '<a href="javascript:;" class="' + opts.nextCls + '">' + opts.nextContent + '</a>';
|
||||
} else {
|
||||
if (opts.keepShowPN == false) {
|
||||
$obj.find('.' + opts.nextCls) && $obj.find('.' + opts.nextCls).remove();
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'easy': //简单模式
|
||||
break;
|
||||
default:
|
||||
}
|
||||
html += opts.jump ? '<input type="text" class="' + opts.jumpIptCls + '"><a href="javascript:;" class="' + opts.jumpBtnCls + '">' + opts.jumpBtn + '</a>' : '';
|
||||
$obj.empty().html(html);
|
||||
};
|
||||
|
||||
//绑定事件
|
||||
this.eventBind = function () {
|
||||
var that = this;
|
||||
var pageCount = that.getPageCount(); //总页数
|
||||
var index = 1;
|
||||
$obj.off().on('click', 'a', function () {
|
||||
if ($(this).hasClass(opts.nextCls)) {
|
||||
if (parseInt($obj.find('.' + opts.activeCls).text()) >= pageCount) {
|
||||
$(this).addClass('disabled');
|
||||
return false;
|
||||
} else {
|
||||
index = parseInt($obj.find('.' + opts.activeCls).text()) + 1;
|
||||
}
|
||||
} else if ($(this).hasClass(opts.prevCls)) {
|
||||
if (parseInt($obj.find('.' + opts.activeCls).text()) <= 1) {
|
||||
$(this).addClass('disabled');
|
||||
return false;
|
||||
} else {
|
||||
index = parseInt($obj.find('.' + opts.activeCls).text()) - 1;
|
||||
}
|
||||
} else if ($(this).hasClass(opts.jumpBtnCls)) {
|
||||
if ($obj.find('.' + opts.jumpIptCls).val() !== '') {
|
||||
index = parseInt($obj.find('.' + opts.jumpIptCls).val());
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
index = parseInt($(this).data('page'));
|
||||
}
|
||||
that.filling(index);
|
||||
typeof opts.callback === 'function' && opts.callback(that);
|
||||
});
|
||||
//输入跳转的页码
|
||||
$obj.on('input propertychange', '.' + opts.jumpIptCls, function () {
|
||||
var $this = $(this);
|
||||
var val = $this.val();
|
||||
var reg = /[^\d]/g;
|
||||
if (reg.test(val)) $this.val(val.replace(reg, ''));
|
||||
(parseInt(val) > pageCount) && $this.val(pageCount);
|
||||
if (parseInt(val) === 0) $this.val(1); //最小值为1
|
||||
});
|
||||
//回车跳转指定页码
|
||||
$document.keydown(function (e) {
|
||||
if (e.keyCode == 13 && $obj.find('.' + opts.jumpIptCls).val()) {
|
||||
var index = parseInt($obj.find('.' + opts.jumpIptCls).val());
|
||||
that.filling(index);
|
||||
typeof opts.callback === 'function' && opts.callback(that);
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
//初始化
|
||||
this.init = function () {
|
||||
this.filling(opts.current);
|
||||
this.eventBind();
|
||||
if (opts.isHide && this.getPageCount() == '1' || this.getPageCount() == '0') {
|
||||
$obj.hide();
|
||||
} else {
|
||||
$obj.show();
|
||||
}
|
||||
};
|
||||
this.init();
|
||||
};
|
||||
|
||||
$.fn.pagination = function (parameter, callback) {
|
||||
if (typeof parameter == 'function') { //重载
|
||||
callback = parameter;
|
||||
parameter = {};
|
||||
} else {
|
||||
parameter = parameter || {};
|
||||
callback = callback || function () {};
|
||||
}
|
||||
var options = $.extend({}, defaults, parameter);
|
||||
return this.each(function () {
|
||||
var pagination = new Pagination(this, options);
|
||||
callback(pagination);
|
||||
});
|
||||
};
|
||||
|
||||
}));
|
||||
@@ -0,0 +1,357 @@
|
||||
/**
|
||||
* vkBeautify - javascript plugin to pretty-print or minify text in XML, JSON, CSS and SQL formats.
|
||||
*
|
||||
* Version - 0.99.00.beta
|
||||
* Copyright (c) 2012 Vadim Kiryukhin
|
||||
* vkiryukhin @ gmail.com
|
||||
* http://www.eslinstructor.net/vkbeautify/
|
||||
*
|
||||
* MIT license:
|
||||
* http://www.opensource.org/licenses/mit-license.php
|
||||
*
|
||||
* Pretty print
|
||||
*
|
||||
* vkbeautify.xml(text [,indent_pattern]);
|
||||
* vkbeautify.json(text [,indent_pattern]);
|
||||
* vkbeautify.css(text [,indent_pattern]);
|
||||
* vkbeautify.sql(text [,indent_pattern]);
|
||||
*
|
||||
* @text - String; text to beatufy;
|
||||
* @indent_pattern - Integer | String;
|
||||
* Integer: number of white spaces;
|
||||
* String: character string to visualize indentation ( can also be a set of white spaces )
|
||||
* Minify
|
||||
*
|
||||
* vkbeautify.xmlmin(text [,preserve_comments]);
|
||||
* vkbeautify.jsonmin(text);
|
||||
* vkbeautify.cssmin(text [,preserve_comments]);
|
||||
* vkbeautify.sqlmin(text);
|
||||
*
|
||||
* @text - String; text to minify;
|
||||
* @preserve_comments - Bool; [optional];
|
||||
* Set this flag to true to prevent removing comments from @text ( minxml and mincss functions only. )
|
||||
*
|
||||
* Examples:
|
||||
* vkbeautify.xml(text); // pretty print XML
|
||||
* vkbeautify.json(text, 4 ); // pretty print JSON
|
||||
* vkbeautify.css(text, '. . . .'); // pretty print CSS
|
||||
* vkbeautify.sql(text, '----'); // pretty print SQL
|
||||
*
|
||||
* vkbeautify.xmlmin(text, true);// minify XML, preserve comments
|
||||
* vkbeautify.jsonmin(text);// minify JSON
|
||||
* vkbeautify.cssmin(text);// minify CSS, remove comments ( default )
|
||||
* vkbeautify.sqlmin(text);// minify SQL
|
||||
*
|
||||
*/
|
||||
|
||||
(function() {
|
||||
|
||||
function createShiftArr(step) {
|
||||
|
||||
var space = ' ';
|
||||
|
||||
if ( isNaN(parseInt(step)) ) { // argument is string
|
||||
space = step;
|
||||
} else { // argument is integer
|
||||
switch(step) {
|
||||
case 1: space = ' '; break;
|
||||
case 2: space = ' '; break;
|
||||
case 3: space = ' '; break;
|
||||
case 4: space = ' '; break;
|
||||
case 5: space = ' '; break;
|
||||
case 6: space = ' '; break;
|
||||
case 7: space = ' '; break;
|
||||
case 8: space = ' '; break;
|
||||
case 9: space = ' '; break;
|
||||
case 10: space = ' '; break;
|
||||
case 11: space = ' '; break;
|
||||
case 12: space = ' '; break;
|
||||
}
|
||||
}
|
||||
|
||||
var shift = ['\n']; // array of shifts
|
||||
for(ix=0;ix<100;ix++){
|
||||
shift.push(shift[ix]+space);
|
||||
}
|
||||
return shift;
|
||||
}
|
||||
|
||||
function vkbeautify(){
|
||||
this.step = '\t'; // 4 spaces
|
||||
this.shift = createShiftArr(this.step);
|
||||
};
|
||||
|
||||
vkbeautify.prototype.xml = function(text,step) {
|
||||
|
||||
var ar = text.replace(/>\s{0,}</g,"><")
|
||||
.replace(/</g,"~::~<")
|
||||
.replace(/\s*xmlns\:/g,"~::~xmlns:")
|
||||
.replace(/\s*xmlns\=/g,"~::~xmlns=")
|
||||
.split('~::~'),
|
||||
len = ar.length,
|
||||
inComment = false,
|
||||
deep = 0,
|
||||
str = '',
|
||||
ix = 0,
|
||||
shift = step ? createShiftArr(step) : this.shift;
|
||||
|
||||
for(ix=0;ix<len;ix++) {
|
||||
// start comment or <![CDATA[...]]> or <!DOCTYPE //
|
||||
if(ar[ix].search(/<!/) > -1) {
|
||||
str += shift[deep]+ar[ix];
|
||||
inComment = true;
|
||||
// end comment or <![CDATA[...]]> //
|
||||
if(ar[ix].search(/-->/) > -1 || ar[ix].search(/\]>/) > -1 || ar[ix].search(/!DOCTYPE/) > -1 ) {
|
||||
inComment = false;
|
||||
}
|
||||
} else
|
||||
// end comment or <![CDATA[...]]> //
|
||||
if(ar[ix].search(/-->/) > -1 || ar[ix].search(/\]>/) > -1) {
|
||||
str += ar[ix];
|
||||
inComment = false;
|
||||
} else
|
||||
// <elm></elm> //
|
||||
if( /^<\w/.exec(ar[ix-1]) && /^<\/\w/.exec(ar[ix]) &&
|
||||
/^<[\w:\-\.\,]+/.exec(ar[ix-1]) == /^<\/[\w:\-\.\,]+/.exec(ar[ix])[0].replace('/','')) {
|
||||
str += ar[ix];
|
||||
if(!inComment) deep--;
|
||||
} else
|
||||
// <elm> //
|
||||
if(ar[ix].search(/<\w/) > -1 && ar[ix].search(/<\//) == -1 && ar[ix].search(/\/>/) == -1 ) {
|
||||
str = !inComment ? str += shift[deep++]+ar[ix] : str += ar[ix];
|
||||
} else
|
||||
// <elm>...</elm> //
|
||||
if(ar[ix].search(/<\w/) > -1 && ar[ix].search(/<\//) > -1) {
|
||||
str = !inComment ? str += shift[deep]+ar[ix] : str += ar[ix];
|
||||
} else
|
||||
// </elm> //
|
||||
if(ar[ix].search(/<\//) > -1) {
|
||||
str = !inComment ? str += shift[--deep]+ar[ix] : str += ar[ix];
|
||||
} else
|
||||
// <elm/> //
|
||||
if(ar[ix].search(/\/>/) > -1 ) {
|
||||
str = !inComment ? str += shift[deep]+ar[ix] : str += ar[ix];
|
||||
} else
|
||||
// <? xml ... ?> //
|
||||
if(ar[ix].search(/<\?/) > -1) {
|
||||
str += shift[deep]+ar[ix];
|
||||
} else
|
||||
// xmlns //
|
||||
if( ar[ix].search(/xmlns\:/) > -1 || ar[ix].search(/xmlns\=/) > -1) {
|
||||
str += shift[deep]+ar[ix];
|
||||
}
|
||||
|
||||
else {
|
||||
str += ar[ix];
|
||||
}
|
||||
}
|
||||
|
||||
return (str[0] == '\n') ? str.slice(1) : str;
|
||||
}
|
||||
|
||||
vkbeautify.prototype.json = function(text,step) {
|
||||
|
||||
var step = step ? step : this.step;
|
||||
|
||||
if (typeof JSON === 'undefined' ) return text;
|
||||
|
||||
if ( typeof text === "string" ) return JSON.stringify(JSON.parse(text), null, step);
|
||||
if ( typeof text === "object" ) return JSON.stringify(text, null, step);
|
||||
|
||||
return text; // text is not string nor object
|
||||
}
|
||||
|
||||
vkbeautify.prototype.css = function(text, step) {
|
||||
|
||||
var ar = text.replace(/\s{1,}/g,' ')
|
||||
.replace(/\{/g,"{~::~")
|
||||
.replace(/\}/g,"~::~}~::~")
|
||||
.replace(/\;/g,";~::~")
|
||||
.replace(/\/\*/g,"~::~/*")
|
||||
.replace(/\*\//g,"*/~::~")
|
||||
.replace(/~::~\s{0,}~::~/g,"~::~")
|
||||
.split('~::~'),
|
||||
len = ar.length,
|
||||
deep = 0,
|
||||
str = '',
|
||||
ix = 0,
|
||||
shift = step ? createShiftArr(step) : this.shift;
|
||||
|
||||
for(ix=0;ix<len;ix++) {
|
||||
|
||||
if( /\{/.exec(ar[ix])) {
|
||||
str += shift[deep++]+ar[ix];
|
||||
} else
|
||||
if( /\}/.exec(ar[ix])) {
|
||||
str += shift[--deep]+ar[ix];
|
||||
} else
|
||||
if( /\*\\/.exec(ar[ix])) {
|
||||
str += shift[deep]+ar[ix];
|
||||
}
|
||||
else {
|
||||
str += shift[deep]+ar[ix];
|
||||
}
|
||||
}
|
||||
return str.replace(/^\n{1,}/,'');
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
function isSubquery(str, parenthesisLevel) {
|
||||
return parenthesisLevel - (str.replace(/\(/g,'').length - str.replace(/\)/g,'').length )
|
||||
}
|
||||
|
||||
function split_sql(str, tab) {
|
||||
|
||||
return str.replace(/\s{1,}/g," ")
|
||||
|
||||
.replace(/ AND /ig,"~::~"+tab+tab+"AND ")
|
||||
.replace(/ BETWEEN /ig,"~::~"+tab+"BETWEEN ")
|
||||
.replace(/ CASE /ig,"~::~"+tab+"CASE ")
|
||||
.replace(/ ELSE /ig,"~::~"+tab+"ELSE ")
|
||||
.replace(/ END /ig,"~::~"+tab+"END ")
|
||||
.replace(/ FROM /ig,"~::~FROM ")
|
||||
.replace(/ GROUP\s{1,}BY/ig,"~::~GROUP BY ")
|
||||
.replace(/ HAVING /ig,"~::~HAVING ")
|
||||
//.replace(/ SET /ig," SET~::~")
|
||||
.replace(/ IN /ig," IN ")
|
||||
|
||||
.replace(/ JOIN /ig,"~::~JOIN ")
|
||||
.replace(/ CROSS~::~{1,}JOIN /ig,"~::~CROSS JOIN ")
|
||||
.replace(/ INNER~::~{1,}JOIN /ig,"~::~INNER JOIN ")
|
||||
.replace(/ LEFT~::~{1,}JOIN /ig,"~::~LEFT JOIN ")
|
||||
.replace(/ RIGHT~::~{1,}JOIN /ig,"~::~RIGHT JOIN ")
|
||||
|
||||
.replace(/ ON /ig,"~::~"+tab+"ON ")
|
||||
.replace(/ OR /ig,"~::~"+tab+tab+"OR ")
|
||||
.replace(/ ORDER\s{1,}BY/ig,"~::~ORDER BY ")
|
||||
.replace(/ OVER /ig,"~::~"+tab+"OVER ")
|
||||
|
||||
.replace(/\(\s{0,}SELECT /ig,"~::~(SELECT ")
|
||||
.replace(/\)\s{0,}SELECT /ig,")~::~SELECT ")
|
||||
|
||||
.replace(/ THEN /ig," THEN~::~"+tab+"")
|
||||
.replace(/ UNION /ig,"~::~UNION~::~")
|
||||
.replace(/ USING /ig,"~::~USING ")
|
||||
.replace(/ WHEN /ig,"~::~"+tab+"WHEN ")
|
||||
.replace(/ WHERE /ig,"~::~WHERE ")
|
||||
.replace(/ WITH /ig,"~::~WITH ")
|
||||
|
||||
//.replace(/\,\s{0,}\(/ig,",~::~( ")
|
||||
//.replace(/\,/ig,",~::~"+tab+tab+"")
|
||||
|
||||
.replace(/ ALL /ig," ALL ")
|
||||
.replace(/ AS /ig," AS ")
|
||||
.replace(/ ASC /ig," ASC ")
|
||||
.replace(/ DESC /ig," DESC ")
|
||||
.replace(/ DISTINCT /ig," DISTINCT ")
|
||||
.replace(/ EXISTS /ig," EXISTS ")
|
||||
.replace(/ NOT /ig," NOT ")
|
||||
.replace(/ NULL /ig," NULL ")
|
||||
.replace(/ LIKE /ig," LIKE ")
|
||||
.replace(/\s{0,}SELECT /ig,"SELECT ")
|
||||
.replace(/\s{0,}UPDATE /ig,"UPDATE ")
|
||||
.replace(/ SET /ig," SET ")
|
||||
|
||||
.replace(/~::~{1,}/g,"~::~")
|
||||
.split('~::~');
|
||||
}
|
||||
|
||||
vkbeautify.prototype.sql = function(text,step) {
|
||||
|
||||
var ar_by_quote = text.replace(/\s{1,}/g," ")
|
||||
.replace(/\'/ig,"~::~\'")
|
||||
.split('~::~'),
|
||||
len = ar_by_quote.length,
|
||||
ar = [],
|
||||
deep = 0,
|
||||
tab = this.step,//+this.step,
|
||||
inComment = true,
|
||||
inQuote = false,
|
||||
parenthesisLevel = 0,
|
||||
str = '',
|
||||
ix = 0,
|
||||
shift = step ? createShiftArr(step) : this.shift;;
|
||||
|
||||
for(ix=0;ix<len;ix++) {
|
||||
if(ix%2) {
|
||||
ar = ar.concat(ar_by_quote[ix]);
|
||||
} else {
|
||||
ar = ar.concat(split_sql(ar_by_quote[ix], tab) );
|
||||
}
|
||||
}
|
||||
|
||||
len = ar.length;
|
||||
for(ix=0;ix<len;ix++) {
|
||||
|
||||
parenthesisLevel = isSubquery(ar[ix], parenthesisLevel);
|
||||
|
||||
if( /\s{0,}\s{0,}SELECT\s{0,}/.exec(ar[ix])) {
|
||||
ar[ix] = ar[ix].replace(/\,/g,",\n"+tab+tab+"")
|
||||
}
|
||||
|
||||
if( /\s{0,}\s{0,}SET\s{0,}/.exec(ar[ix])) {
|
||||
ar[ix] = ar[ix].replace(/\,/g,",\n"+tab+tab+"")
|
||||
}
|
||||
|
||||
if( /\s{0,}\(\s{0,}SELECT\s{0,}/.exec(ar[ix])) {
|
||||
deep++;
|
||||
str += shift[deep]+ar[ix];
|
||||
} else
|
||||
if( /\'/.exec(ar[ix]) ) {
|
||||
if(parenthesisLevel<1 && deep) {
|
||||
deep--;
|
||||
}
|
||||
str += ar[ix];
|
||||
}
|
||||
else {
|
||||
str += shift[deep]+ar[ix];
|
||||
if(parenthesisLevel<1 && deep) {
|
||||
deep--;
|
||||
}
|
||||
}
|
||||
var junk = 0;
|
||||
}
|
||||
|
||||
str = str.replace(/^\n{1,}/,'').replace(/\n{1,}/g,"\n");
|
||||
return str;
|
||||
}
|
||||
|
||||
|
||||
vkbeautify.prototype.xmlmin = function(text, preserveComments) {
|
||||
|
||||
var str = preserveComments ? text
|
||||
: text.replace(/\<![ \r\n\t]*(--([^\-]|[\r\n]|-[^\-])*--[ \r\n\t]*)\>/g,"")
|
||||
.replace(/[ \r\n\t]{1,}xmlns/g, ' xmlns');
|
||||
return str.replace(/>\s{0,}</g,"><");
|
||||
}
|
||||
|
||||
vkbeautify.prototype.jsonmin = function(text) {
|
||||
|
||||
if (typeof JSON === 'undefined' ) return text;
|
||||
|
||||
return JSON.stringify(JSON.parse(text), null, 0);
|
||||
|
||||
}
|
||||
|
||||
vkbeautify.prototype.cssmin = function(text, preserveComments) {
|
||||
|
||||
var str = preserveComments ? text
|
||||
: text.replace(/\/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+\//g,"") ;
|
||||
|
||||
return str.replace(/\s{1,}/g,' ')
|
||||
.replace(/\{\s{1,}/g,"{")
|
||||
.replace(/\}\s{1,}/g,"}")
|
||||
.replace(/\;\s{1,}/g,";")
|
||||
.replace(/\/\*\s{1,}/g,"/*")
|
||||
.replace(/\*\/\s{1,}/g,"*/");
|
||||
}
|
||||
|
||||
vkbeautify.prototype.sqlmin = function(text) {
|
||||
return text.replace(/\s{1,}/g," ").replace(/\s{1,}\(/,"(").replace(/\s{1,}\)/,")");
|
||||
}
|
||||
|
||||
window.vkbeautify = new vkbeautify();
|
||||
|
||||
})();
|
||||
|
||||
@@ -0,0 +1,164 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
|
||||
<link href="../../bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/js/bootstrap-multitabs/multitabs.min.css" rel="stylesheet" type="text/css">
|
||||
<link href="../../bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container-fluid p-t-15">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-title">新增管理员</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form>
|
||||
<div class="form-group">
|
||||
<label>用户名</label>
|
||||
<input type="text" class="form-control" maxlength="10" id="username"
|
||||
placeholder="请输入用户名">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>昵称</label>
|
||||
<input type="text" class="form-control" maxlength="10" id="nickname"
|
||||
placeholder="请输入昵称">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>手机号</label>
|
||||
<input type="text" class="form-control" maxlength="11" id="mobile"
|
||||
placeholder="请输入手机号">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>密码</label>
|
||||
<input type="password" class="form-control" maxlength="20" id="password"
|
||||
placeholder="请输入密码">
|
||||
</div>
|
||||
<button type="button" id="btnOk" class="btn btn-primary">确认</button>
|
||||
<button type="button" id="btnLoading" class="btn btn-primary" disabled style="display: none">
|
||||
<span class="spinner-grow spinner-grow-sm" role="status" aria-hidden="true"></span>
|
||||
执行中...
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap-maxlength/bootstrap-maxlength.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap-multitabs/multitabs.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/authorization/md5.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
|
||||
$("input#username").maxlength({
|
||||
warningClass: "badge badge-info",
|
||||
limitReachedClass: "badge badge-warning"
|
||||
});
|
||||
|
||||
$("input#nickname").maxlength({
|
||||
warningClass: "badge badge-info",
|
||||
limitReachedClass: "badge badge-warning"
|
||||
});
|
||||
|
||||
$("input#mobile").maxlength({
|
||||
warningClass: "badge badge-info",
|
||||
limitReachedClass: "badge badge-warning"
|
||||
});
|
||||
|
||||
$("input#password").maxlength({
|
||||
warningClass: "badge badge-info",
|
||||
limitReachedClass: "badge badge-warning"
|
||||
});
|
||||
|
||||
$('#btnOk').on('click', function () {
|
||||
const username = $("#username").val();
|
||||
if (username === "") {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请输入用户名。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
const nickname = $("#nickname").val();
|
||||
if (nickname === "") {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请输入昵称。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
const password = $("#password").val();
|
||||
if (password === "") {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请输入密码。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
const postData = {
|
||||
username: username,
|
||||
nickname: nickname,
|
||||
mobile: $("#mobile").val(),
|
||||
password: md5(password),
|
||||
};
|
||||
|
||||
|
||||
AjaxForm(
|
||||
"POST",
|
||||
"/api/admin",
|
||||
postData,
|
||||
function () {
|
||||
$(this).hide();
|
||||
$("#btnLoading").show();
|
||||
},
|
||||
function (data) {
|
||||
$("#btnLoading").hide();
|
||||
$("#btnOk").show();
|
||||
|
||||
$.alert({
|
||||
title: '操作成功',
|
||||
icon: 'mdi mdi-check-decagram',
|
||||
type: 'green',
|
||||
content: '编号:' + data.id + ' 创建完成。',
|
||||
buttons: {
|
||||
okay: {
|
||||
text: '关闭',
|
||||
action: function () {
|
||||
location.href = "/admin/list";
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
function (response) {
|
||||
$("#btnLoading").hide();
|
||||
$("#btnOk").show();
|
||||
|
||||
AjaxError(response);
|
||||
}
|
||||
);
|
||||
});
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,401 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
|
||||
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container-fluid p-t-15">
|
||||
<div class="row">
|
||||
|
||||
<div class="col-lg-12">
|
||||
<div class="card">
|
||||
<div class="card-toolbar d-flex flex-column flex-md-row">
|
||||
<div class="toolbar-btn-action">
|
||||
<a class="btn btn-primary m-r-5" href="/admin/add"><i class="mdi mdi-plus"></i> 新增</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>编号</th>
|
||||
<th>用户名</th>
|
||||
<th>昵称</th>
|
||||
<th>手机号</th>
|
||||
<th>创建日期</th>
|
||||
<th>更新日期</th>
|
||||
<th style="text-align: center; ">可用状态</th>
|
||||
<th style="text-align: center; ">在线状态</th>
|
||||
<th style="text-align: center; ">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="tbody">
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<ul class="pagination">
|
||||
<ul class="pagination" id="paginationDiv">
|
||||
|
||||
</ul>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery.pagination.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
// 加载列表页数据
|
||||
getPageListData();
|
||||
|
||||
function getPageListData(page = 0, page_size = 0) {
|
||||
|
||||
if (parseInt(page) < 1) {
|
||||
page = 1;
|
||||
}
|
||||
|
||||
if (parseInt(page_size) < 1) {
|
||||
page_size = 10;
|
||||
}
|
||||
|
||||
AjaxForm(
|
||||
"GET",
|
||||
"/api/admin",
|
||||
{page: page, page_size: page_size},
|
||||
function () {},
|
||||
function (data) {
|
||||
if (data.list.length > 0) {
|
||||
var totalNum = data.pagination.total; //总条数
|
||||
var pageNum = Math.ceil(totalNum / data.pagination.per_page_count); //分页的总页数
|
||||
|
||||
$("#paginationDiv").pagination({
|
||||
current: data.pagination.current_page,
|
||||
pageCount: pageNum,
|
||||
coping: true,
|
||||
homePage: '首页',
|
||||
endPage: '末页',
|
||||
mode: 'fixed',
|
||||
prevContent: '上一页',
|
||||
nextContent: '下一页',
|
||||
activeCls: 'pageActive',
|
||||
prevCls: 'pagePrev',
|
||||
nextCls: 'pageNext',
|
||||
callback: function (api) {
|
||||
$(".tbody").html("");
|
||||
getPageListData(api.getCurrent());
|
||||
}
|
||||
});
|
||||
|
||||
$.each(data.list, function (index, value) {
|
||||
var showUsedBadge = "";
|
||||
var showOnlineBadge = "";
|
||||
var optionUsedName = "";
|
||||
|
||||
if (value.is_used === 1) {
|
||||
optionUsedName = '禁用';
|
||||
showUsedBadge = '<span class="badge badge-success">启用</span></td>'
|
||||
}
|
||||
|
||||
if (value.is_used === -1) {
|
||||
optionUsedName = '启用';
|
||||
showUsedBadge = '<span class="badge badge-danger">禁用</span></td>'
|
||||
}
|
||||
|
||||
if (value.is_online === 1) {
|
||||
showOnlineBadge = '<span class="badge btn-primary">在线</span></td>'
|
||||
}
|
||||
|
||||
if (value.is_online === -1) {
|
||||
showOnlineBadge = '<span class="badge btn-secondary">离线</span></td>'
|
||||
}
|
||||
|
||||
const tr = '<tr>\n' +
|
||||
'<td>' + value.id + '</td>\n' +
|
||||
'<td>' + value.username + '</td>\n' +
|
||||
'<td>' + value.nickname + '</td>\n' +
|
||||
'<td>' + value.mobile + '</td>\n' +
|
||||
'<td>' + value.created_at + '</td>\n' +
|
||||
'<td>' + value.updated_at + '</td>\n' +
|
||||
'<td style="text-align: center; ">' + showUsedBadge + '</td>\n' +
|
||||
'<td style="text-align: center; ">' + showOnlineBadge + '</td>\n' +
|
||||
'<td style="text-align: center; ">\n' +
|
||||
'<div class="btn-group">\n' +
|
||||
' <a class="btn btn-xs btn-default btn-option" href="#!" title=""\n' +
|
||||
' data-id="' + value.hashid + '"' +
|
||||
' data-is-used="' + value.is_used + '"' +
|
||||
' data-toggle="tooltip" data-original-title="' + optionUsedName + '">' + optionUsedName + '</a>\n' +
|
||||
' <a class="btn btn-xs btn-default btn-resetPassword" href="#!" title=""\n' +
|
||||
' data-id="' + value.hashid + '"' +
|
||||
' data-toggle="tooltip" data-original-title="重置密码">重置密码</a>\n' +
|
||||
' <a class="btn btn-xs btn-default btn-menu" href="#!" title=""\n' +
|
||||
' data-id="' + value.hashid + '"' +
|
||||
' data-toggle="tooltip" data-original-title="菜单授权">菜单授权</a>\n' +
|
||||
' <a class="btn btn-xs btn-default btn-offline" href="#!" title=""\n' +
|
||||
' data-id="' + value.hashid + '"' +
|
||||
' data-toggle="tooltip" data-original-title="下线">下线</a>\n' +
|
||||
' <a class="btn btn-xs btn-default btn-confirm" href="#!" title=""\n' +
|
||||
' data-id="' + value.hashid + '"' +
|
||||
' data-toggle="tooltip" data-original-title="删除">删除</a>\n' +
|
||||
'</div>\n' +
|
||||
'</td>\n' +
|
||||
'</tr>';
|
||||
|
||||
$(".tbody").append(tr);
|
||||
})
|
||||
} else {
|
||||
// 数据为空
|
||||
const tr = '<tr><td colspan="8" style="text-align: center">暂无数据</td></tr>';
|
||||
$(".tbody").append(tr);
|
||||
}
|
||||
},
|
||||
function (response) {
|
||||
AjaxError(response);
|
||||
}
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
// 启用/禁用
|
||||
$(document).on('click', '.btn-option', function () {
|
||||
const id = $(this).attr('data-id');
|
||||
const isUsed = $(this).attr('data-is-used');
|
||||
|
||||
var tipMessage = "";
|
||||
var wantUsed = 0;
|
||||
if (isUsed === "1") { // 1=当前为启用状态,需要改成禁用
|
||||
tipMessage = "禁用";
|
||||
wantUsed = -1;
|
||||
}
|
||||
if (isUsed === "-1") { // -1=当前为禁用状态,需要改成启用
|
||||
tipMessage = "启用";
|
||||
wantUsed = 1;
|
||||
}
|
||||
|
||||
const patchData = {
|
||||
id: id,
|
||||
used: wantUsed,
|
||||
};
|
||||
|
||||
$.confirm({
|
||||
title: '谨慎操作',
|
||||
content: '确认要 <strong style="color: red">' + tipMessage + '</strong> 吗?',
|
||||
icon: 'mdi mdi-alert',
|
||||
animation: 'scale',
|
||||
closeAnimation: 'zoom',
|
||||
buttons: {
|
||||
okay: {
|
||||
text: '确认',
|
||||
keys: ['enter'],
|
||||
btnClass: 'btn-orange',
|
||||
action: function () {
|
||||
AjaxForm(
|
||||
"PATCH",
|
||||
"/api/admin/used",
|
||||
patchData,
|
||||
function () {},
|
||||
function (data) {
|
||||
$.alert({
|
||||
title: '操作成功',
|
||||
icon: 'mdi mdi-check-decagram',
|
||||
type: 'green',
|
||||
content: '编号:' + data.id + ' 已' + tipMessage + '。',
|
||||
buttons: {
|
||||
okay: {
|
||||
text: '关闭',
|
||||
action: function () {
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
function (response) {
|
||||
AjaxError(response);
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
cancel: {
|
||||
text: '取消',
|
||||
keys: ['ctrl', 'shift'],
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 重置密码
|
||||
$(document).on('click', '.btn-resetPassword', function () {
|
||||
const id = $(this).attr('data-id');
|
||||
|
||||
$.confirm({
|
||||
title: '谨慎操作',
|
||||
content: '确认要 <strong style="color: red">重置密码</strong> 吗?' + '<hr>' + '重置后密码为:123456',
|
||||
icon: 'mdi mdi-alert',
|
||||
animation: 'scale',
|
||||
closeAnimation: 'zoom',
|
||||
buttons: {
|
||||
okay: {
|
||||
text: '确认',
|
||||
keys: ['enter'],
|
||||
btnClass: 'btn-orange',
|
||||
action: function () {
|
||||
AjaxForm(
|
||||
"PATCH",
|
||||
'/api/admin/reset_password/' + id,
|
||||
"",
|
||||
function () {},
|
||||
function (data) {
|
||||
$.alert({
|
||||
title: '操作成功',
|
||||
icon: 'mdi mdi-check-decagram',
|
||||
type: 'green',
|
||||
content: '编号:' + data.id + ' 密码重置完成。',
|
||||
buttons: {
|
||||
okay: {
|
||||
text: '关闭',
|
||||
action: function () {
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
function (response) {
|
||||
AjaxError(response);
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
cancel: {
|
||||
text: '取消',
|
||||
keys: ['ctrl', 'shift'],
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 菜单授权
|
||||
$(document).on('click', '.btn-menu', function () {
|
||||
location.href = "/admin/action/" + $(this).attr('data-id');
|
||||
});
|
||||
|
||||
// 下线
|
||||
$(document).on('click', '.btn-offline', function () {
|
||||
const id = $(this).attr('data-id');
|
||||
|
||||
$.confirm({
|
||||
title: '谨慎操作',
|
||||
content: '确认要 <strong style="color: red">下线</strong> 吗?',
|
||||
icon: 'mdi mdi-alert',
|
||||
animation: 'scale',
|
||||
closeAnimation: 'zoom',
|
||||
buttons: {
|
||||
okay: {
|
||||
text: '确认',
|
||||
keys: ['enter'],
|
||||
btnClass: 'btn-orange',
|
||||
action: function () {
|
||||
AjaxForm(
|
||||
"PATCH",
|
||||
'/api/admin/offline',
|
||||
{id: id},
|
||||
function () {},
|
||||
function (data) {
|
||||
$.alert({
|
||||
title: '操作成功',
|
||||
icon: 'mdi mdi-check-decagram',
|
||||
type: 'green',
|
||||
content: '编号:' + data.id + ' 已下线。',
|
||||
buttons: {
|
||||
okay: {
|
||||
text: '关闭',
|
||||
action: function () {
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
function (response) {
|
||||
AjaxError(response);
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
cancel: {
|
||||
text: '取消',
|
||||
keys: ['ctrl', 'shift'],
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 删除
|
||||
$(document).on('click', '.btn-confirm', function () {
|
||||
const id = $(this).attr('data-id');
|
||||
|
||||
$.confirm({
|
||||
title: '谨慎操作',
|
||||
content: '确认要 <strong style="color: red">删除</strong> 吗?',
|
||||
icon: 'mdi mdi-alert',
|
||||
animation: 'scale',
|
||||
closeAnimation: 'zoom',
|
||||
buttons: {
|
||||
okay: {
|
||||
text: '确认',
|
||||
keys: ['enter'],
|
||||
btnClass: 'btn-orange',
|
||||
action: function () {
|
||||
AjaxForm(
|
||||
"DELETE",
|
||||
'/api/admin/' + id,
|
||||
"",
|
||||
function () {},
|
||||
function (data) {
|
||||
$.alert({
|
||||
title: '操作成功',
|
||||
icon: 'mdi mdi-check-decagram',
|
||||
type: 'green',
|
||||
content: '编号:' + data.id + ' 已删除。',
|
||||
buttons: {
|
||||
okay: {
|
||||
text: '关闭',
|
||||
action: function () {
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
function (response) {
|
||||
AjaxError(response);
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
cancel: {
|
||||
text: '取消',
|
||||
keys: ['ctrl', 'shift'],
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,137 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0, minimal-ui">
|
||||
<title>登录页面</title>
|
||||
<link rel="shortcut icon" type="image/x-icon" href="bootstrap/favicon.ico">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-touch-fullscreen" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="default">
|
||||
<link rel="stylesheet" type="text/css" href="../../bootstrap/js/jquery-confirm/jquery-confirm.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="../../bootstrap/css/materialdesignicons.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="../../bootstrap/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="../../bootstrap/css/style.min.css">
|
||||
|
||||
<style>
|
||||
.login-form .has-feedback {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.login-form .has-feedback .form-control {
|
||||
padding-left: 36px;
|
||||
}
|
||||
|
||||
.login-form .has-feedback .mdi {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: auto;
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
line-height: 36px;
|
||||
z-index: 4;
|
||||
color: #dcdcdc;
|
||||
display: block;
|
||||
text-align: center;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.login-form .has-feedback.row .mdi {
|
||||
left: 15px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body class="center-vh">
|
||||
<div class="card card-shadowed p-5 w-420 mb-0 mr-2 ml-2">
|
||||
<div class="text-center mb-3">
|
||||
<img src="../../bootstrap/images/logo-sidebar.png">
|
||||
</div>
|
||||
<form class="login-form">
|
||||
<div class="form-group has-feedback">
|
||||
<span class="mdi mdi-account" aria-hidden="true"></span>
|
||||
<input type="text" class="form-control" id="username" placeholder="用户名">
|
||||
</div>
|
||||
|
||||
<div class="form-group has-feedback">
|
||||
<span class="mdi mdi-lock" aria-hidden="true"></span>
|
||||
<input type="password" class="form-control" id="password" placeholder="密码">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<button class="btn btn-block btn-primary" id="btnOk" type="button">立即登录</button>
|
||||
</div>
|
||||
<div class="alert alert-primary" style="text-align: center;" role="alert">
|
||||
默认
|
||||
<span class="mdi mdi-account" aria-hidden="true"></span> admin
|
||||
<span class="mdi mdi-lock" aria-hidden="true"></span> admin
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/authorization/md5.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
|
||||
// 回车触发按钮事件
|
||||
$(document).keyup(function (event) {
|
||||
if (event.keyCode === 13) {
|
||||
$("#btnOk").trigger("click");
|
||||
}
|
||||
});
|
||||
|
||||
$('#btnOk').on('click', function () {
|
||||
const username = $("#username").val();
|
||||
if (username === "") {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请输入用户名。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
const password = $("#password").val();
|
||||
if (password === "") {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请输入密码。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
const postData = {
|
||||
username: username,
|
||||
password: md5(password),
|
||||
};
|
||||
|
||||
AjaxForm(
|
||||
"POST",
|
||||
"/api/login",
|
||||
postData,
|
||||
function () {
|
||||
},
|
||||
function (data) {
|
||||
let date = new Date();
|
||||
date.setTime(date.getTime() + 24 * 60 * 60 * 1000); // 24 * 60 * 60 * 1000 表示 24 小时
|
||||
$.cookie('_login_token_', data.token, {expires: date});
|
||||
|
||||
location.href = "/";
|
||||
},
|
||||
function (response) {
|
||||
AjaxError(response);
|
||||
}
|
||||
);
|
||||
});
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,213 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
|
||||
<link href="../../bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/js/bootstrap-multitabs/multitabs.min.css" rel="stylesheet" type="text/css">
|
||||
<link href="../../bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container-fluid p-t-15">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-title">配置菜单
|
||||
<small id="adminName"></small>
|
||||
</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
||||
<form action="#!" method="post" id="form">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<div class="custom-control custom-checkbox">
|
||||
<input type="checkbox" class="custom-control-input" id="check-all">
|
||||
<label class="custom-control-label" for="check-all">全选/取消全选</label>
|
||||
</div>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
|
||||
<tbody class="tbody">
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<button type="button" id="btnOk" class="btn btn-primary">确认</button>
|
||||
<button type="button" id="btnLoading" class="btn btn-primary" disabled style="display: none">
|
||||
<span class="spinner-grow spinner-grow-sm" role="status" aria-hidden="true"></span>
|
||||
执行中...
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap-maxlength/bootstrap-maxlength.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap-multitabs/multitabs.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
const hash_id = {{ .HashID }}
|
||||
|
||||
AjaxForm(
|
||||
"GET",
|
||||
"/api/admin/menu/" + hash_id,
|
||||
'',
|
||||
function () {
|
||||
},
|
||||
function (data) {
|
||||
$("#adminName").html("(管理员:" + data.username + ")");
|
||||
|
||||
if (data.list.length > 0) {
|
||||
let newArr = [];
|
||||
data.list.forEach(function (v) {
|
||||
if (v.pid === 0) {
|
||||
v.children = [];
|
||||
newArr.push(v)
|
||||
}
|
||||
});
|
||||
|
||||
data.list.forEach(function (v) {
|
||||
newArr.forEach(function (item) {
|
||||
if (v.pid === item.id) {
|
||||
item.children.push(v)
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
|
||||
$.each(newArr, function (index, value) {
|
||||
let checked = "";
|
||||
if (value.is_have == 1) {
|
||||
checked = "checked";
|
||||
}
|
||||
|
||||
let tr = '<tr><td><div class="custom-control custom-checkbox custom-parent">';
|
||||
tr += '<input type="checkbox" ' + checked + ' class="custom-control-input checkbox-parent" id="' + value.id + '" value="' + value.id + '">';
|
||||
tr += '<label class="custom-control-label" for="' + value.id + '">' + value.name + '</label>';
|
||||
tr += '</div></td></tr>';
|
||||
|
||||
tr += '<tr><td class="p-l-40">';
|
||||
|
||||
value.children.forEach(function (item) {
|
||||
let itemChecked = "";
|
||||
if (item.is_have == 1) {
|
||||
itemChecked = "checked";
|
||||
}
|
||||
|
||||
tr += '<div class="custom-control custom-checkbox custom-control-inline">';
|
||||
tr += '<input type="checkbox" ' + itemChecked + ' class="custom-control-input checkbox-child c-' + item.pid + '" id="' + item.id + '" value="' + item.id + '">';
|
||||
tr += '<label class="custom-control-label" for="' + item.id + '">' + item.name + '</label>';
|
||||
tr += '</div>';
|
||||
});
|
||||
|
||||
tr += '</td></tr>';
|
||||
|
||||
$(".tbody").append(tr);
|
||||
|
||||
});
|
||||
}
|
||||
},
|
||||
function (response) {
|
||||
AjaxError(response);
|
||||
}
|
||||
);
|
||||
|
||||
$(document).on('click', '.checkbox-parent', function () {
|
||||
const id = $(this).attr('id');
|
||||
if ($(this).prop('checked') === true) {
|
||||
$(this).prop('checked', true);
|
||||
$('.c-' + id).prop('checked', true);
|
||||
} else {
|
||||
$(this).prop('checked', false);
|
||||
$('.c-' + id).prop('checked', false);
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on('click', '.checkbox-child', function () {
|
||||
if ($(this).prop('checked') === true) {
|
||||
$(this).prop('checked', true);
|
||||
} else {
|
||||
$(this).prop('checked', false);
|
||||
}
|
||||
});
|
||||
|
||||
$(document).on('click', '#check-all', function () {
|
||||
if ($(this).prop('checked') === true) {
|
||||
$('.checkbox-parent').prop('checked', true);
|
||||
$('.checkbox-child').prop('checked', true);
|
||||
} else {
|
||||
$('.checkbox-parent').prop('checked', false);
|
||||
$('.checkbox-child').prop('checked', false);
|
||||
}
|
||||
});
|
||||
|
||||
$('#btnOk').on('click', function () {
|
||||
let vals = [];
|
||||
$.each($('.tbody').find('input:checkbox:checked'), function () {
|
||||
vals.push($(this).val());
|
||||
});
|
||||
|
||||
if (vals.length === 0) {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请至少选择 1 个功能权限。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
AjaxForm(
|
||||
"POST",
|
||||
"/api/admin/menu",
|
||||
{id: hash_id, actions: vals.join(',')},
|
||||
function () {
|
||||
$(this).hide();
|
||||
$("#btnLoading").show();
|
||||
},
|
||||
function () {
|
||||
$("#btnLoading").hide();
|
||||
$("#btnOk").show();
|
||||
|
||||
$.alert({
|
||||
title: '操作成功',
|
||||
icon: 'mdi mdi-check-decagram',
|
||||
type: 'green',
|
||||
content: $("#adminName").html() + ' 菜单授权完成。',
|
||||
buttons: {
|
||||
okay: {
|
||||
text: '关闭',
|
||||
action: function () {
|
||||
location.href = "/admin/list";
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
function (response) {
|
||||
$("#btnLoading").hide();
|
||||
$("#btnOk").show();
|
||||
|
||||
AjaxError(response);
|
||||
}
|
||||
);
|
||||
});
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,131 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
|
||||
<link href="../../bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/js/bootstrap-multitabs/multitabs.min.css" rel="stylesheet" type="text/css">
|
||||
<link href="../../bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container-fluid p-t-15">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-title">个人信息</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
||||
<form class="site-form">
|
||||
<div class="form-group">
|
||||
<label>用户名</label>
|
||||
<input type="text" class="form-control" id="username" disabled="disabled">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>昵称</label>
|
||||
<input type="text" class="form-control" maxlength="10" id="nickname" placeholder="输入您的昵称">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>手机号</label>
|
||||
<input type="text" class="form-control" maxlength="11" id="mobile" placeholder="请输入您的手机号">
|
||||
</div>
|
||||
<button type="button" id="btnOk" class="btn btn-primary">保存</button>
|
||||
<button type="button" id="btnLoading" class="btn btn-primary" disabled style="display: none">
|
||||
<span class="spinner-grow spinner-grow-sm" role="status" aria-hidden="true"></span>
|
||||
执行中...
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap-maxlength/bootstrap-maxlength.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap-multitabs/multitabs.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
|
||||
$("input#nickname, input#mobile").maxlength({
|
||||
warningClass: "badge badge-info",
|
||||
limitReachedClass: "badge badge-warning"
|
||||
});
|
||||
|
||||
AjaxForm(
|
||||
"GET",
|
||||
"/api/admin/info",
|
||||
"",
|
||||
function () {},
|
||||
function (data) {
|
||||
$("#username").val(data.username);
|
||||
$("#nickname").val(data.nickname);
|
||||
$("#mobile").val(data.mobile);
|
||||
},
|
||||
function (response) {
|
||||
AjaxError(response);
|
||||
}
|
||||
);
|
||||
|
||||
$('#btnOk').on('click', function () {
|
||||
const nickname = $("#nickname").val();
|
||||
if (nickname === "") {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请输入您的昵称。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
const postData = {
|
||||
nickname: nickname,
|
||||
mobile: $("#mobile").val(),
|
||||
};
|
||||
|
||||
AjaxForm(
|
||||
"PATCH",
|
||||
"/api/admin/modify_personal_info",
|
||||
postData,
|
||||
function () {
|
||||
$(this).hide();
|
||||
$("#btnLoading").show();
|
||||
},
|
||||
function () {
|
||||
$("#btnLoading").hide();
|
||||
$("#btnOk").show();
|
||||
|
||||
$.alert({
|
||||
title: '操作成功',
|
||||
icon: 'mdi mdi-check-decagram',
|
||||
type: 'green',
|
||||
content: '信息修改成功。',
|
||||
buttons: {
|
||||
okay: {
|
||||
text: '关闭',
|
||||
action: function () {
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
function (response) {
|
||||
$("#btnLoading").hide();
|
||||
$("#btnOk").show();
|
||||
|
||||
AjaxError(response);
|
||||
}
|
||||
);
|
||||
});
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,153 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
|
||||
<link href="../../bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/js/bootstrap-multitabs/multitabs.min.css" rel="stylesheet" type="text/css">
|
||||
<link href="../../bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container-fluid p-t-15">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-title">新增管理员</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form>
|
||||
<div class="form-group">
|
||||
<label>旧密码</label>
|
||||
<input type="password" class="form-control" maxlength="20" id="old_password"
|
||||
placeholder="输入账号的原登录密码">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>新密码</label>
|
||||
<input type="password" class="form-control" maxlength="20" id="new_password"
|
||||
placeholder="输入新的密码">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>确认新密码</label>
|
||||
<input type="password" class="form-control" maxlength="20" id="confirm_password"
|
||||
placeholder="再次输入新的密码">
|
||||
</div>
|
||||
<button type="button" id="btnOk" class="btn btn-primary">确认</button>
|
||||
<button type="button" id="btnLoading" class="btn btn-primary" disabled style="display: none">
|
||||
<span class="spinner-grow spinner-grow-sm" role="status" aria-hidden="true"></span>
|
||||
执行中...
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap-maxlength/bootstrap-maxlength.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap-multitabs/multitabs.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/authorization/md5.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
|
||||
$("input#old_password, input#new_password, input#confirm_password").maxlength({
|
||||
warningClass: "badge badge-info",
|
||||
limitReachedClass: "badge badge-warning"
|
||||
});
|
||||
|
||||
$('#btnOk').on('click', function () {
|
||||
const old_password = $("#old_password").val();
|
||||
if (old_password === "") {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请输入账号的原登录密码。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
const new_password = $("#new_password").val();
|
||||
if (new_password === "") {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请输入新的密码。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
const confirm_password = $("#confirm_password").val();
|
||||
if (confirm_password === "") {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请再次输入新的密码。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
if (new_password !== confirm_password) {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请确认两次密码一致。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
const postData = {
|
||||
old_password: md5(old_password),
|
||||
new_password: md5(new_password),
|
||||
};
|
||||
|
||||
|
||||
AjaxForm(
|
||||
"PATCH",
|
||||
"/api/admin/modify_password",
|
||||
postData,
|
||||
function () {
|
||||
$(this).hide();
|
||||
$("#btnLoading").show();
|
||||
},
|
||||
function () {
|
||||
$("#btnLoading").hide();
|
||||
$("#btnOk").show();
|
||||
|
||||
$.alert({
|
||||
title: '操作成功',
|
||||
icon: 'mdi mdi-check-decagram',
|
||||
type: 'green',
|
||||
content: '密码修改成功。',
|
||||
buttons: {
|
||||
okay: {
|
||||
text: '关闭',
|
||||
action: function () {
|
||||
parent.window.close();
|
||||
window.open("/login");
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
function (response) {
|
||||
$("#btnLoading").hide();
|
||||
$("#btnOk").show();
|
||||
|
||||
AjaxError(response);
|
||||
}
|
||||
);
|
||||
});
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,142 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
|
||||
<link href="../../bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/js/bootstrap-multitabs/multitabs.min.css" rel="stylesheet" type="text/css">
|
||||
<link href="../../bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container-fluid p-t-15">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-title">新增调用方</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form>
|
||||
<div class="form-group">
|
||||
<label for="business_key">调用方</label>
|
||||
<input type="text" class="form-control" maxlength="25" id="business_key"
|
||||
placeholder="请输入调用方标识">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="business_developer">调用方对接人</label>
|
||||
<input type="text" class="form-control" maxlength="50" id="business_developer"
|
||||
placeholder="请输入调用方对接人">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="remark">备注</label>
|
||||
<textarea class="form-control" maxlength="225" rows="3" id="remark"
|
||||
placeholder="备注"></textarea>
|
||||
</div>
|
||||
<button type="button" id="btnOk" class="btn btn-primary">确认</button>
|
||||
<button type="button" id="btnLoading" class="btn btn-primary" disabled style="display: none">
|
||||
<span class="spinner-grow spinner-grow-sm" role="status" aria-hidden="true"></span>
|
||||
执行中...
|
||||
</button>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap-maxlength/bootstrap-maxlength.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap-multitabs/multitabs.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
|
||||
$("input#business_key").maxlength({
|
||||
warningClass: "badge badge-info",
|
||||
limitReachedClass: "badge badge-warning"
|
||||
});
|
||||
|
||||
$("input#business_developer").maxlength({
|
||||
warningClass: "badge badge-info",
|
||||
limitReachedClass: "badge badge-warning"
|
||||
});
|
||||
|
||||
$("textarea#remark").maxlength({
|
||||
threshold: 255,
|
||||
warningClass: "badge badge-info",
|
||||
limitReachedClass: "badge badge-warning"
|
||||
});
|
||||
|
||||
$('#btnOk').on('click', function () {
|
||||
const businessKey = $("#business_key").val();
|
||||
if (businessKey === "") {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请输入调用方标识。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
const businessDeveloper = $("#business_developer").val();
|
||||
if (businessDeveloper === "") {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请输入调用方对接人。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
const postData = {
|
||||
business_key: businessKey,
|
||||
business_developer: businessDeveloper,
|
||||
remark: $("#remark").val(),
|
||||
};
|
||||
|
||||
|
||||
AjaxForm(
|
||||
"POST",
|
||||
"/api/authorized",
|
||||
postData,
|
||||
function () {
|
||||
$(this).hide();
|
||||
$("#btnLoading").show();
|
||||
},
|
||||
function (data) {
|
||||
$("#btnLoading").hide();
|
||||
$("#btnOk").show();
|
||||
|
||||
$.alert({
|
||||
title: '操作成功',
|
||||
icon: 'mdi mdi-check-decagram',
|
||||
type: 'green',
|
||||
content: '编号:' + data.id + ' 创建完成。',
|
||||
buttons: {
|
||||
okay: {
|
||||
text: '关闭',
|
||||
action: function () {
|
||||
location.href = "/authorized/list";
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
function (response) {
|
||||
$("#btnLoading").hide();
|
||||
$("#btnOk").show();
|
||||
|
||||
AjaxError(response);
|
||||
}
|
||||
);
|
||||
});
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,260 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
|
||||
<link href="../../bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/js/bootstrap-multitabs/multitabs.min.css" rel="stylesheet" type="text/css">
|
||||
<link href="../../bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container-fluid p-t-15">
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-title">接口授权</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
||||
<div class="alert alert-warning" role="alert">
|
||||
接口地址支持通配符(*),其中 * 表示 1 级,** 表示 n 级。
|
||||
</div>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-prepend">
|
||||
<label class="input-group-text" for="request_method">选择请求方式</label>
|
||||
</div>
|
||||
<select class="custom-select" id="request_method">
|
||||
<option value="GET">GET</option>
|
||||
<option value="POST">POST</option>
|
||||
<option value="PUT">PUT</option>
|
||||
<option value="DELETE">DELETE</option>
|
||||
<option value="PATCH">PATCH</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text" id="inputGroup-sizing-default">输入接口地址</span>
|
||||
</div>
|
||||
<input type="text" class="form-control" maxlength="60" id="request_api"
|
||||
placeholder="接口地址">
|
||||
</div>
|
||||
|
||||
<button type="button" id="btnOk" class="btn btn-primary">确认</button>
|
||||
<button type="button" id="btnLoading" class="btn btn-primary" disabled style="display: none">
|
||||
<span class="spinner-grow spinner-grow-sm" role="status" aria-hidden="true"></span>
|
||||
执行中...
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-6">
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
<div class="card-title">已授权接口<small id="businessKey"></small></div>
|
||||
</header>
|
||||
<div class="card-body apis">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/popper.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap-maxlength/bootstrap-maxlength.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap-multitabs/multitabs.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
|
||||
const hash_id = {{ .HashID }}
|
||||
|
||||
$("input#request_api").maxlength({
|
||||
warningClass: "badge badge-info",
|
||||
limitReachedClass: "badge badge-warning"
|
||||
});
|
||||
|
||||
// 加载列表页数据
|
||||
getListData();
|
||||
|
||||
function getListData() {
|
||||
AjaxForm(
|
||||
"GET",
|
||||
"/api/authorized_api",
|
||||
{id: hash_id},
|
||||
function () {
|
||||
|
||||
},
|
||||
function (data) {
|
||||
$("#businessKey").html("(授权方:" + data.business_key + ")");
|
||||
if (data.list.length > 0) {
|
||||
var badgeMethodClass = "";
|
||||
|
||||
$.each(data.list, function (index, value) {
|
||||
if (value.method === "GET") {
|
||||
badgeMethodClass = "badge-primary";
|
||||
} else if (value.method === "POST") {
|
||||
badgeMethodClass = "badge-success";
|
||||
} else if (value.method === "DELETE") {
|
||||
badgeMethodClass = "badge-danger";
|
||||
} else if (value.method === "PUT") {
|
||||
badgeMethodClass = "badge-yellow";
|
||||
} else if (value.method === "PATCH") {
|
||||
badgeMethodClass = "badge-cyan";
|
||||
} else {
|
||||
badgeMethodClass = "badge-dark";
|
||||
}
|
||||
|
||||
const p = '<p>\n' +
|
||||
'<a href="#!" data-id="' + value.hash_id + '" data-api="' + value.api + '" class="del">' +
|
||||
'<span class="badge badge-dark"><i class="mdi mdi-window-close"></i></span>\n' +
|
||||
'</a>\n' +
|
||||
'<span class="badge ' + badgeMethodClass + '">' + value.method + '</span>\n' + value.api
|
||||
;
|
||||
|
||||
$(".apis").append(p);
|
||||
})
|
||||
} else {
|
||||
// 数据为空
|
||||
const p = '<p>暂无授权接口</p>';
|
||||
$(".apis").append(p);
|
||||
}
|
||||
},
|
||||
function (response) {
|
||||
AjaxError(response);
|
||||
}
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
$('#btnOk').on('click', function () {
|
||||
const requestMethod = $("#request_method").val();
|
||||
const requestApi = $("#request_api").val();
|
||||
if (requestMethod === "") {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请选择请求方式。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
if (requestApi === "") {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请输入请求地址。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
const postData = {
|
||||
method: requestMethod,
|
||||
api: requestApi,
|
||||
id: hash_id,
|
||||
};
|
||||
|
||||
AjaxForm(
|
||||
"POST",
|
||||
"/api/authorized_api",
|
||||
postData,
|
||||
function () {
|
||||
$(this).hide();
|
||||
$("#btnLoading").show();
|
||||
},
|
||||
function () {
|
||||
$("#btnLoading").hide();
|
||||
$("#btnOk").show();
|
||||
|
||||
$.alert({
|
||||
title: '操作成功',
|
||||
icon: 'mdi mdi-check-decagram',
|
||||
type: 'green',
|
||||
content: '接口:' + requestApi + ' 授权完成。',
|
||||
buttons: {
|
||||
okay: {
|
||||
text: '关闭',
|
||||
action: function () {
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
function (response) {
|
||||
$("#btnLoading").hide();
|
||||
$("#btnOk").show();
|
||||
|
||||
AjaxError(response);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
$(document).on('click', '.del', function () {
|
||||
const id = $(this).attr('data-id');
|
||||
const api = $(this).attr('data-api');
|
||||
|
||||
$.confirm({
|
||||
title: '谨慎操作',
|
||||
content: '确认要 <strong style="color: red">取消授权</strong> 吗?',
|
||||
icon: 'mdi mdi-alert',
|
||||
animation: 'scale',
|
||||
closeAnimation: 'zoom',
|
||||
buttons: {
|
||||
okay: {
|
||||
text: '确认',
|
||||
keys: ['enter'],
|
||||
btnClass: 'btn-orange',
|
||||
action: function () {
|
||||
AjaxForm(
|
||||
"DELETE",
|
||||
'/api/authorized_api/' + id,
|
||||
"",
|
||||
function () {
|
||||
|
||||
},
|
||||
function () {
|
||||
$.alert({
|
||||
title: '操作成功',
|
||||
icon: 'mdi mdi-check-decagram',
|
||||
type: 'green',
|
||||
content: '接口:' + api + ' 已取消授权。',
|
||||
buttons: {
|
||||
okay: {
|
||||
text: '关闭',
|
||||
action: function () {
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
function (response) {
|
||||
AjaxError(response);
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
cancel: {
|
||||
text: '取消',
|
||||
keys: ['ctrl', 'shift'],
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,215 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
|
||||
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container-fluid p-t-15">
|
||||
<div class="row">
|
||||
|
||||
<div class="col-lg-12">
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
<div class="card-title">如何给调用方开通 KEY 和 SECRET?</div>
|
||||
</header>
|
||||
|
||||
<div class="card-body">
|
||||
<p>1. 新增调用方,输入调用方标识、调用方对接人、备注等信息;</p>
|
||||
<p>2. 授权调用方可调用的接口;</p>
|
||||
<p>3. 查看详情,将调用方的 <code>KEY</code>、<code>SECRET</code> 发给调用方;</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-12">
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
<div class="card-title">调用方如何传递 Token?</div>
|
||||
</header>
|
||||
|
||||
<div class="card-body">
|
||||
<p>基于 HTTP Header 中的两个参数 <code>Authorization</code>、<code>Authorization-Date</code> 存储签名信息。</p>
|
||||
<p>1. Authorization 存储签名信息,格式:调用方 KEY + 空格分隔符 + 摘要(加密串),例如:</p>
|
||||
<pre>Authorization:blog MjJjMDE1MWFkZjMwOWFmYjFlNzViNDFjYjYwMWFlMmM=</pre>
|
||||
<p>2. Authorization-Date 存储时间信息,格式:0000-00-00 00:00:00,使用 <code>Asia/Shanghai</code> 时区,例如;</p>
|
||||
<pre>Authorization-Date:2021-04-03 21:12:36</pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-12">
|
||||
<div class="card">
|
||||
<header class="card-header"><div class="card-title">不同语言生成签名的方法,供参考</div></header>
|
||||
<div class="card-body">
|
||||
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" data-toggle="tab" href="#go" aria-selected="true">Go 语言</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-toggle="tab" href="#php" aria-selected="false">PHP 语言</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" data-toggle="tab" href="#js" aria-selected="false">JS 语言</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane fade active show" id="go">
|
||||
<pre>
|
||||
func New(key, secret string, ttl time.Duration) Signature {
|
||||
return &signature{
|
||||
key: key,
|
||||
secret: secret,
|
||||
ttl: ttl,
|
||||
}
|
||||
}
|
||||
|
||||
// Generate
|
||||
// path 请求的路径 (不附带 querystring)
|
||||
func (s *signature) Generate(path string, method string, params url.Values) (authorization, date string, err error) {
|
||||
if path == "" {
|
||||
err = errors.New("path required")
|
||||
return
|
||||
}
|
||||
|
||||
if method == "" {
|
||||
err = errors.New("method required")
|
||||
return
|
||||
}
|
||||
|
||||
methodName := strings.ToUpper(method)
|
||||
if !methods[methodName] {
|
||||
err = errors.New("method param error")
|
||||
return
|
||||
}
|
||||
|
||||
// Date
|
||||
date = time_parse.CSTLayoutString()
|
||||
|
||||
// Encode() 方法中自带 sorted by key
|
||||
sortParamsEncode, err := url.QueryUnescape(params.Encode())
|
||||
if err != nil {
|
||||
err = errors.Errorf("url QueryUnescape %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
// 加密字符串规则
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
buffer.WriteString(path)
|
||||
buffer.WriteString(delimiter)
|
||||
buffer.WriteString(methodName)
|
||||
buffer.WriteString(delimiter)
|
||||
buffer.WriteString(sortParamsEncode)
|
||||
buffer.WriteString(delimiter)
|
||||
buffer.WriteString(date)
|
||||
|
||||
// 对数据进行 sha256 加密,并进行 base64 encode
|
||||
hash := hmac.New(sha256.New, []byte(s.secret))
|
||||
hash.Write(buffer.Bytes())
|
||||
digest := base64.StdEncoding.EncodeToString(hash.Sum(nil))
|
||||
|
||||
authorization = fmt.Sprintf("%s %s", s.key, digest)
|
||||
return
|
||||
}
|
||||
|
||||
// 模拟数据
|
||||
const (
|
||||
key = "blog"
|
||||
secret = "i1ydX9RtHyuJTrw7frcu"
|
||||
ttl = time.Minute * 10
|
||||
)
|
||||
|
||||
func TestSignature_Generate(t *testing.T) {
|
||||
path := "/echo"
|
||||
method := "POST"
|
||||
|
||||
params := url.Values{}
|
||||
params.Add("a", "a1")
|
||||
params.Add("d", "d1")
|
||||
params.Add("c", "c1 c2*")
|
||||
|
||||
authorization, date, err := New(key, secret, ttl).Generate(path, method, params)
|
||||
t.Log("authorization:", authorization)
|
||||
t.Log("authorization-date:", date)
|
||||
t.Log("err:", err)
|
||||
}
|
||||
</pre>
|
||||
</div>
|
||||
<div class="tab-pane fade" id="php">
|
||||
<pre>
|
||||
// 模拟数据
|
||||
$key = "blog";
|
||||
$secret = "i1ydX9RtHyuJTrw7frcu";
|
||||
|
||||
$path = "/echo";
|
||||
$method = "POST";
|
||||
|
||||
$params['a'] = "a1";
|
||||
$params['d'] = "d1";
|
||||
$params['c'] = "c1 c2*";
|
||||
|
||||
// 对 params key 进行排序
|
||||
ksort($params);
|
||||
|
||||
// 对 sortParams 进行操作
|
||||
$sortParamsEncode = rawurldecode(http_build_query($params, "", "&", PHP_QUERY_RFC3986));
|
||||
|
||||
// 时间 使用 Asia/Shanghai 时区
|
||||
$date = date("Y-m-d H:i:s", time());
|
||||
|
||||
// 加密字符串规则
|
||||
$encryptStr = $path."|".strtoupper($method)."|".$sortParamsEncode."|".$date;
|
||||
|
||||
// 对数据进行 sha256 加密,并进行 base64 encode
|
||||
$digest = base64_encode(hash_hmac("sha256", $encryptStr, $secret, true));
|
||||
|
||||
$authorization = $key." ".$digest;
|
||||
|
||||
echo "authorization:{$authorization}";
|
||||
echo "---";
|
||||
echo "authorization-date:{$date}";
|
||||
</pre>
|
||||
</div>
|
||||
<div class="tab-pane fade" id="js">
|
||||
<pre>
|
||||
let key = "blog";
|
||||
let secret = "i1ydX9RtHyuJTrw7frcu";
|
||||
|
||||
let date = new Date();
|
||||
let datetime = date.getFullYear() + "-" // "年"
|
||||
+ ((date.getMonth() + 1) > 10 ? (date.getMonth() + 1) : "0" + (date.getMonth() + 1)) + "-" // "月"
|
||||
+ (date.getDate() < 10 ? "0" + date.getDate() : date.getDate()) + " " // "日"
|
||||
+ (date.getHours() < 10 ? "0" + date.getHours() : date.getHours()) + ":" // "小时"
|
||||
+ (date.getMinutes() < 10 ? "0" + date.getMinutes() : date.getMinutes()) + ":" // "分钟"
|
||||
+ (date.getSeconds() < 10 ? "0" + date.getSeconds() : date.getSeconds()); // "秒"
|
||||
|
||||
let path = "/echo";
|
||||
let method = "POST";
|
||||
let params = {a:'a1', d:'d1', c: 'c1 c2*'};
|
||||
|
||||
let sortParamsEncode = decodeURIComponent(jQuery.param(ksort(params)));
|
||||
let encryptStr = path + "|" + method.toUpperCase() + "|" + sortParamsEncode + "|" + datetime;
|
||||
let digest = CryptoJS.enc.Base64.stringify(CryptoJS.HmacSHA256(encryptStr, secret));
|
||||
console.log({authorization: key + " " + digest, date: datetime});
|
||||
</pre>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/popper.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/main.min.js"></script>
|
||||
<script type="text/javascript">document.write(unescape("%3Cspan id='cnzz_stat_icon_1279911342'%3E%3C/span%3E%3Cscript src='https://v1.cnzz.com/z_stat.php%3Fid%3D1279911342%26' type='text/javascript'%3E%3C/script%3E"));</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,294 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
|
||||
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container-fluid p-t-15">
|
||||
<div class="row">
|
||||
|
||||
<div class="col-lg-12">
|
||||
<div class="card">
|
||||
<div class="card-toolbar d-flex flex-column flex-md-row">
|
||||
<div class="toolbar-btn-action">
|
||||
<a class="btn btn-primary m-r-5" href="/authorized/add"><i class="mdi mdi-plus"></i> 新增</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>编号</th>
|
||||
<th>调用方</th>
|
||||
<th>对接人</th>
|
||||
<th>创建日期</th>
|
||||
<th>更新日期</th>
|
||||
<th style="text-align: center; ">状态</th>
|
||||
<th style="text-align: center; ">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="tbody">
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<ul class="pagination">
|
||||
<ul class="pagination" id="paginationDiv">
|
||||
|
||||
</ul>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery.pagination.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
// 加载列表页数据
|
||||
getPageListData();
|
||||
|
||||
function getPageListData(page = 0, page_size = 0) {
|
||||
|
||||
if (parseInt(page) < 1) {
|
||||
page = 1;
|
||||
}
|
||||
|
||||
if (parseInt(page_size) < 1) {
|
||||
page_size = 10;
|
||||
}
|
||||
|
||||
AjaxForm(
|
||||
"GET",
|
||||
"/api/authorized",
|
||||
{page: page, page_size: page_size},
|
||||
function () {},
|
||||
function (data) {
|
||||
if (data.list.length > 0) {
|
||||
var totalNum = data.pagination.total; //总条数
|
||||
var pageNum = Math.ceil(totalNum / data.pagination.per_page_count); //分页的总页数
|
||||
|
||||
$("#paginationDiv").pagination({
|
||||
current: data.pagination.current_page,
|
||||
pageCount: pageNum,
|
||||
coping: true,
|
||||
homePage: '首页',
|
||||
endPage: '末页',
|
||||
mode: 'fixed',
|
||||
prevContent: '上一页',
|
||||
nextContent: '下一页',
|
||||
activeCls: 'pageActive',
|
||||
prevCls: 'pagePrev',
|
||||
nextCls: 'pageNext',
|
||||
callback: function (api) {
|
||||
$(".tbody").html("");
|
||||
getPageListData(api.getCurrent());
|
||||
}
|
||||
});
|
||||
|
||||
$.each(data.list, function (index, value) {
|
||||
var showUsedBadge = "";
|
||||
var optionUsedName = "";
|
||||
|
||||
if (value.is_used === 1) {
|
||||
optionUsedName = '禁用';
|
||||
showUsedBadge = '<span class="badge badge-success">启用</span></td>'
|
||||
}
|
||||
|
||||
if (value.is_used === -1) {
|
||||
optionUsedName = '启用';
|
||||
showUsedBadge = '<span class="badge badge-danger">禁用</span></td>'
|
||||
}
|
||||
|
||||
const tr = '<tr>\n' +
|
||||
'<td>' + value.id + '</td>\n' +
|
||||
'<td>' + value.business_key + '</td>\n' +
|
||||
'<td>' + value.business_developer + '</td>\n' +
|
||||
'<td>' + value.created_at + '</td>\n' +
|
||||
'<td>' + value.updated_at + '</td>\n' +
|
||||
'<td style="text-align: center; ">' + showUsedBadge + '</td>\n' +
|
||||
'<td style="text-align: center; ">\n' +
|
||||
'<div class="btn-group">\n' +
|
||||
' <a class="btn btn-xs btn-default btn-detail" href="#!" title=""\n' +
|
||||
' data-business-key="' + value.business_key + '"' +
|
||||
' data-business-secret="' + value.business_secret + '"' +
|
||||
' data-remark="' + value.remark + '"' +
|
||||
' data-toggle="tooltip" data-original-title="详情">详情</a>\n' +
|
||||
' <a class="btn btn-xs btn-default btn-option" href="#!" title=""\n' +
|
||||
' data-id="' + value.hashid + '"' +
|
||||
' data-is-used="' + value.is_used + '"' +
|
||||
' data-toggle="tooltip" data-original-title="' + optionUsedName + '">' + optionUsedName + '</a>\n' +
|
||||
' <a class="btn btn-xs btn-default" href="/authorized/api/'+value.hashid+'" title=""\n' +
|
||||
' data-toggle="tooltip" data-original-title="接口">接口</a>\n' +
|
||||
' <a class="btn btn-xs btn-default btn-confirm" href="#!" title=""\n' +
|
||||
' data-id="' + value.hashid + '"' +
|
||||
' data-toggle="tooltip" data-original-title="删除">删除</a>\n' +
|
||||
'</div>\n' +
|
||||
'</td>\n' +
|
||||
'</tr>';
|
||||
|
||||
$(".tbody").append(tr);
|
||||
})
|
||||
} else {
|
||||
// 数据为空
|
||||
const tr = '<tr><td colspan="7" style="text-align: center">暂无数据</td></tr>';
|
||||
$(".tbody").append(tr);
|
||||
}
|
||||
},
|
||||
function (response) {
|
||||
AjaxError(response);
|
||||
}
|
||||
|
||||
);
|
||||
}
|
||||
|
||||
// 详情
|
||||
$(document).on('click', '.btn-detail', function () {
|
||||
const business_key = $(this).attr('data-business-key');
|
||||
const business_secret = $(this).attr('data-business-secret');
|
||||
const business_remark = $(this).attr('data-remark');
|
||||
|
||||
$.alert({
|
||||
title: '详情',
|
||||
content: '调用方 KEY :' + business_key + '<br/>调用方 SECRET :' + business_secret + '<br/>备注:' + business_remark,
|
||||
type: 'green',
|
||||
animation: 'scale',
|
||||
draggable: true,
|
||||
});
|
||||
});
|
||||
|
||||
// 启用/禁用
|
||||
$(document).on('click', '.btn-option', function () {
|
||||
const id = $(this).attr('data-id');
|
||||
const isUsed = $(this).attr('data-is-used');
|
||||
|
||||
var tipMessage = "";
|
||||
var wantUsed = 0;
|
||||
if (isUsed === "1") { // 1=当前为启用状态,需要改成禁用
|
||||
tipMessage = "禁用";
|
||||
wantUsed = -1;
|
||||
}
|
||||
if (isUsed === "-1") { // -1=当前为禁用状态,需要改成启用
|
||||
tipMessage = "启用";
|
||||
wantUsed = 1;
|
||||
}
|
||||
|
||||
const patchData = {
|
||||
id: id,
|
||||
used: wantUsed,
|
||||
};
|
||||
|
||||
$.confirm({
|
||||
title: '谨慎操作',
|
||||
content: '确认要 <strong style="color: red">' + tipMessage + '</strong> 吗?',
|
||||
icon: 'mdi mdi-alert',
|
||||
animation: 'scale',
|
||||
closeAnimation: 'zoom',
|
||||
buttons: {
|
||||
okay: {
|
||||
text: '确认',
|
||||
keys: ['enter'],
|
||||
btnClass: 'btn-orange',
|
||||
action: function () {
|
||||
AjaxForm(
|
||||
"PATCH",
|
||||
"/api/authorized/used",
|
||||
patchData,
|
||||
function () {},
|
||||
function (data) {
|
||||
$.alert({
|
||||
title: '操作成功',
|
||||
icon: 'mdi mdi-check-decagram',
|
||||
type: 'green',
|
||||
content: '编号:' + data.id + ' 已' + tipMessage + '。',
|
||||
buttons: {
|
||||
okay: {
|
||||
text: '关闭',
|
||||
action: function () {
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
function (response) {
|
||||
AjaxError(response);
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
cancel: {
|
||||
text: '取消',
|
||||
keys: ['ctrl', 'shift'],
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 删除
|
||||
$(document).on('click', '.btn-confirm', function () {
|
||||
const id = $(this).attr('data-id');
|
||||
|
||||
$.confirm({
|
||||
title: '谨慎操作',
|
||||
content: '确认要 <strong style="color: red">删除</strong> 吗?',
|
||||
icon: 'mdi mdi-alert',
|
||||
animation: 'scale',
|
||||
closeAnimation: 'zoom',
|
||||
buttons: {
|
||||
okay: {
|
||||
text: '确认',
|
||||
keys: ['enter'],
|
||||
btnClass: 'btn-orange',
|
||||
action: function () {
|
||||
AjaxForm(
|
||||
"DELETE",
|
||||
'/api/authorized/' + id,
|
||||
"",
|
||||
function () {},
|
||||
function (data) {
|
||||
$.alert({
|
||||
title: '操作成功',
|
||||
icon: 'mdi mdi-check-decagram',
|
||||
type: 'green',
|
||||
content: '编号:' + data.id + ' 已删除。',
|
||||
buttons: {
|
||||
okay: {
|
||||
text: '关闭',
|
||||
action: function () {
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
function (response) {
|
||||
AjaxError(response);
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
cancel: {
|
||||
text: '取消',
|
||||
keys: ['ctrl', 'shift'],
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,49 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
|
||||
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container-fluid p-t-15">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
<div class="card-title"> 错误码</div>
|
||||
</header>
|
||||
<div class="card-body">
|
||||
<div class="row">
|
||||
<div class="col-sm-4 col-xs-12">
|
||||
<p><code>服务级错误码</code></p>
|
||||
<ul class="list-unstyled">
|
||||
{{range $key, $value := .SystemCodes}}
|
||||
<li><code>{{$value.Code}}</code> <small>{{$value.Message}}</small></li>
|
||||
{{end}}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="col-sm-4 col-xs-12">
|
||||
<p><code>模块级错误码</code></p>
|
||||
<ul class="list-unstyled">
|
||||
{{range $key, $value := .BusinessCodes}}
|
||||
<li><code>{{$value.Code}}</code> <small>{{$value.Message}}</small></li>
|
||||
{{end}}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div style="display:none">
|
||||
<script type="text/javascript">document.write(unescape("%3Cspan id='cnzz_stat_icon_1279911342'%3E%3C/span%3E%3Cscript src='https://v1.cnzz.com/z_stat.php%3Fid%3D1279911342%26' type='text/javascript'%3E%3C/script%3E"));</script>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,169 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
|
||||
<link href="../../bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container-fluid p-t-15">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
<div class="card-title">邮箱告警配置</div>
|
||||
</header>
|
||||
<div class="card-body">
|
||||
<form class="site-form">
|
||||
<div class="form-group">
|
||||
<label>邮箱服务器:</label>
|
||||
<input type="text" class="form-control" id="host" value="{{ .Mail.Host }}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>端口:</label>
|
||||
<input type="text" class="form-control" id="port" value="{{ .Mail.Port }}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>发件人邮箱</label>
|
||||
<input type="text" class="form-control" id="user" value="{{ .Mail.User }}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>发件人密码</label> <small>(发件人邮箱密码或授权码,根据邮箱服务器而定)</small>
|
||||
<input type="password" class="form-control" id="pass" value="{{ .Mail.Pass }}">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label>收件人</label> <small>(添加多个收件人邮箱,用英文,分割)</small>
|
||||
<input type="text" class="form-control" id="to" value="{{ .Mail.To }}"
|
||||
placeholder="请输入收件人邮箱,多个用,分割">
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<small>为了验证邮箱配置的准确性,点击保存后会给收件人发送邮件以确保配置可用。</small>
|
||||
</div>
|
||||
<button type="button" id="btnOk" class="btn btn-primary">保存</button>
|
||||
<button type="button" id="btnLoading" class="btn btn-primary" disabled style="display: none">
|
||||
<span class="spinner-grow spinner-grow-sm" role="status" aria-hidden="true"></span>
|
||||
执行中...
|
||||
</button>
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
|
||||
$('#btnOk').on('click', function () {
|
||||
const host = $("#host").val();
|
||||
if (host === "") {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请输入邮箱服务器。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
const port = $("#port").val();
|
||||
if (port === "") {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请输入端口。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
const user = $("#user").val();
|
||||
if (user === "") {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请输入发件人邮箱。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
const pass = $("#pass").val();
|
||||
if (pass === "") {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请输入发件人密码。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
const to = $("#to").val();
|
||||
if (to === "") {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请输入收件人邮箱。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
const patchData = {
|
||||
host: host,
|
||||
port: port,
|
||||
user: user,
|
||||
pass: pass,
|
||||
to: to,
|
||||
};
|
||||
|
||||
|
||||
AjaxForm(
|
||||
"PATCH",
|
||||
"/api/config/email",
|
||||
patchData,
|
||||
function () {
|
||||
$(this).hide();
|
||||
$("#btnLoading").show();
|
||||
},
|
||||
function () {
|
||||
$("#btnLoading").hide();
|
||||
$("#btnOk").show();
|
||||
|
||||
$.alert({
|
||||
title: '操作成功',
|
||||
icon: 'mdi mdi-check-decagram',
|
||||
type: 'green',
|
||||
content: '配置修改完成',
|
||||
buttons: {
|
||||
okay: {
|
||||
text: '关闭',
|
||||
action: function () {
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
function (response) {
|
||||
$("#btnLoading").hide();
|
||||
$("#btnOk").show();
|
||||
|
||||
AjaxError(response);
|
||||
}
|
||||
);
|
||||
});
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,45 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
|
||||
<link href="bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container-fluid p-t-15">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="card">
|
||||
<header class="card-header"><div class="card-title"> 配置信息</div></header>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">MySQL</h5>
|
||||
<p>主库信息:{{.MySQL.Write.Addr}},账号:{{.MySQL.Write.User}},数据库:{{.MySQL.Write.Name}}</p>
|
||||
<p>从库信息:{{.MySQL.Read.Addr}},账号:{{.MySQL.Read.User}},数据库:{{.MySQL.Read.Name}}</p>
|
||||
<p>最大连接数:{{ .MySQL.Base.MaxOpenConn }}</p>
|
||||
<p>空闲连接数:{{ .MySQL.Base.MaxIdleConn }}</p>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Redis</h5>
|
||||
<p>地址:{{ .Redis.Addr }} </p>
|
||||
<p>最大连接数:{{ .Redis.PoolSize }} </p>
|
||||
<p>空闲连接数:{{ .Redis.MinIdleConns }} </p>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<h5 class="card-title">Mail</h5>
|
||||
<p>邮箱服务器:{{ .Mail.Host }} </p>
|
||||
<p>发件人邮箱地址:{{ .Mail.User }} </p>
|
||||
<p>收件人邮箱地址:{{ .Mail.To }} </p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,299 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
|
||||
<link href="../../bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/js/bootstrap-multitabs/multitabs.min.css" rel="stylesheet" type="text/css">
|
||||
<link href="../../bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container-fluid p-t-15">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-title">新增后台任务</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form>
|
||||
<div class="form-group">
|
||||
<label for="name">任务名称</label>
|
||||
<input type="text" class="form-control maxlength" maxlength="60" id="name"
|
||||
placeholder="请输入任务名称">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="protocol">选择任务执行方式</label>
|
||||
<select class="form-control" id="protocol">
|
||||
<option value="1">SHELL</option>
|
||||
<option value="2">HTTP</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group http_method">
|
||||
<label for="http_method">选择 HTTP 请求方式</label>
|
||||
<select class="form-control" id="http_method">
|
||||
<option value="0">请选择</option>
|
||||
<option value="1">GET</option>
|
||||
<option value="2">POST</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="spec">任务表达式</label>
|
||||
<input type="text" class="form-control maxlength" maxlength="60" id="spec"
|
||||
placeholder="请输入任务表达式,例如:*/5 * * * *">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="command">命令</label>
|
||||
<textarea class="form-control maxlength" maxlength="250" rows="2" id="command"
|
||||
placeholder="请输入任务表达式,例如:/usr/local/bin/php /data/web/artisan consoleCommand"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="timeout">超时时间(单位:秒)</label>
|
||||
<input type="text" class="form-control" id="timeout"
|
||||
placeholder="请输入超时时间,例如:180">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="retry_times">重试次数</label>
|
||||
<input type="text" class="form-control" id="retry_times"
|
||||
placeholder="请输入重试次数,例如:3">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="retry_interval">重试间隔(单位:秒)</label>
|
||||
<input type="text" class="form-control" id="retry_interval"
|
||||
placeholder="请输入重试间隔,例如:60">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="notify_status">执行结束是否通知</label>
|
||||
<select class="form-control" id="notify_status">
|
||||
<option value="1">不通知</option>
|
||||
<option value="2">失败通知</option>
|
||||
<option value="3">结束通知</option>
|
||||
<option value="4">结果关键字匹配通知</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group notify_type">
|
||||
<label for="notify_type">通知方式</label>
|
||||
<select class="form-control" id="notify_type">
|
||||
<option value="0">请选择</option>
|
||||
<option value="1">邮件</option>
|
||||
<option value="2">webhook</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group notify_receiver_email">
|
||||
<label for="notify_receiver_email">通知者邮箱地址(多个用,分割)</label>
|
||||
<input type="text" class="form-control maxlength" maxlength="255" id="notify_receiver_email"
|
||||
placeholder="请输入邮箱地址">
|
||||
</div>
|
||||
|
||||
<div class="form-group notify_keyword">
|
||||
<label for="notify_keyword">匹配关键字(多个用,分割)</label>
|
||||
<input type="text" class="form-control maxlength" maxlength="255" id="notify_keyword"
|
||||
placeholder="请输入匹配关键字">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>状态</label>
|
||||
<div class="clearfix">
|
||||
<div class="custom-control custom-radio custom-control-inline">
|
||||
<input type="radio" id="statusOne" value="1" name="is_used"
|
||||
class="custom-control-input"
|
||||
checked="">
|
||||
<label class="custom-control-label" for="statusOne">启用</label>
|
||||
</div>
|
||||
<div class="custom-control custom-radio custom-control-inline">
|
||||
<input type="radio" id="statusTwo" value="-1" name="is_used"
|
||||
class="custom-control-input">
|
||||
<label class="custom-control-label" for="statusTwo">禁用</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="remark">备注</label>
|
||||
<textarea class="form-control maxlength" maxlength="100" rows="3" id="remark"
|
||||
placeholder="备注"></textarea>
|
||||
</div>
|
||||
<button type="button" id="btnOk" class="btn btn-primary">确认</button>
|
||||
<button type="button" id="btnLoading" class="btn btn-primary" disabled style="display: none">
|
||||
<span class="spinner-grow spinner-grow-sm" role="status" aria-hidden="true"></span>
|
||||
执行中...
|
||||
</button>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap-maxlength/bootstrap-maxlength.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap-multitabs/multitabs.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
|
||||
$(".maxlength").maxlength({
|
||||
warningClass: "badge badge-info",
|
||||
limitReachedClass: "badge badge-warning"
|
||||
});
|
||||
|
||||
$(".http_method").hide();
|
||||
|
||||
$("#protocol").change(function () {
|
||||
if ($(this).val() === "2") {
|
||||
$(".http_method").show();
|
||||
} else {
|
||||
$("#http_method").val(0);
|
||||
$(".http_method").hide();
|
||||
}
|
||||
})
|
||||
|
||||
$(".notify_type").hide();
|
||||
$(".notify_receiver_email").hide();
|
||||
$(".notify_keyword").hide();
|
||||
|
||||
$("#notify_status").change(function () {
|
||||
if ($(this).val() === "1") {
|
||||
$(".notify_type").hide();
|
||||
$("#notify_type").val(0)
|
||||
$(".notify_receiver_email").hide();
|
||||
$("#notify_receiver_email").val("");
|
||||
$(".notify_keyword").hide();
|
||||
$("#notify_keyword").val("");
|
||||
} else if($(this).val() === "2" || $(this).val() === "3") {
|
||||
$(".notify_type").show();
|
||||
$(".notify_keyword").hide();
|
||||
$("#notify_keyword").val("");
|
||||
|
||||
if (("#notify_type").val() === "1") {
|
||||
$(".notify_receiver_email").show();
|
||||
} else {
|
||||
$(".notify_receiver_email").hide();
|
||||
$("#notify_receiver_email").val("");
|
||||
}
|
||||
} else if($(this).val() === "4") {
|
||||
$(".notify_type").show();
|
||||
$(".notify_keyword").show();
|
||||
|
||||
if (("#notify_type").val() === "1") {
|
||||
$(".notify_receiver_email").show();
|
||||
} else {
|
||||
$(".notify_receiver_email").hide();
|
||||
$("#notify_receiver_email").val("");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$("#notify_type").change(function (){
|
||||
if ($(this).val() === "1") {
|
||||
$(".notify_receiver_email").show();
|
||||
} else {
|
||||
$(".notify_receiver_email").hide();
|
||||
}
|
||||
});
|
||||
|
||||
$('#btnOk').on('click', function () {
|
||||
const name = $("#name").val();
|
||||
if (name === "") {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请输入任务名称。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
const spec = $("#spec").val();
|
||||
if (spec === "") {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请输入任务表达式。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
const command = $("#command").val();
|
||||
if (spec === "") {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请输入命令。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
const postData = {
|
||||
name: name,
|
||||
spec: spec,
|
||||
command: command,
|
||||
protocol: $("#protocol").val(),
|
||||
http_method: $("#http_method").val(),
|
||||
timeout: $("#timeout").val(),
|
||||
retry_times: $("#retry_times").val(),
|
||||
retry_interval: $("#retry_interval").val(),
|
||||
notify_status: $("#notify_status").val(),
|
||||
notify_type: $("#notify_type").val(),
|
||||
notify_receiver_email: $("#notify_receiver_email").val(),
|
||||
notify_keyword: $("#notify_keyword").val(),
|
||||
is_used: $("input[name='is_used']:checked").val(),
|
||||
remark: $("#remark").val(),
|
||||
};
|
||||
|
||||
AjaxForm(
|
||||
"POST",
|
||||
"/api/cron",
|
||||
postData,
|
||||
function () {
|
||||
$(this).hide();
|
||||
$("#btnLoading").show();
|
||||
},
|
||||
function (data) {
|
||||
$("#btnLoading").hide();
|
||||
$("#btnOk").show();
|
||||
|
||||
$.alert({
|
||||
title: '操作成功',
|
||||
icon: 'mdi mdi-check-decagram',
|
||||
type: 'green',
|
||||
content: '编号:' + data.id + ' 创建完成。',
|
||||
buttons: {
|
||||
okay: {
|
||||
text: '关闭',
|
||||
action: function () {
|
||||
location.href = "/cron/list";
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
function (response) {
|
||||
$("#btnLoading").hide();
|
||||
$("#btnOk").show();
|
||||
|
||||
AjaxError(response);
|
||||
}
|
||||
);
|
||||
});
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,349 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
|
||||
<link href="../../bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/js/bootstrap-multitabs/multitabs.min.css" rel="stylesheet" type="text/css">
|
||||
<link href="../../bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container-fluid p-t-15">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-title">编辑后台任务</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form>
|
||||
<div class="form-group">
|
||||
<label for="name">任务名称</label>
|
||||
<input type="text" class="form-control maxlength" maxlength="60" id="name"
|
||||
placeholder="请输入任务名称">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="protocol">选择任务执行方式</label>
|
||||
<select class="form-control" id="protocol">
|
||||
<option value="1">SHELL</option>
|
||||
<option value="2">HTTP</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group http_method">
|
||||
<label for="http_method">选择 HTTP 请求方式</label>
|
||||
<select class="form-control" id="http_method">
|
||||
<option value="0">请选择</option>
|
||||
<option value="1">GET</option>
|
||||
<option value="2">POST</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="spec">任务表达式</label>
|
||||
<input type="text" class="form-control maxlength" maxlength="60" id="spec"
|
||||
placeholder="请输入任务表达式,例如:*/5 * * * *">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="command">命令</label>
|
||||
<textarea class="form-control maxlength" maxlength="250" rows="2" id="command"
|
||||
placeholder="请输入任务表达式,例如:/usr/local/bin/php /data/web/artisan consoleCommand"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="timeout">超时时间(单位:秒)</label>
|
||||
<input type="text" class="form-control" id="timeout"
|
||||
placeholder="请输入超时时间,例如:180">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="retry_times">重试次数</label>
|
||||
<input type="text" class="form-control" id="retry_times"
|
||||
placeholder="请输入重试次数,例如:3">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="retry_interval">重试间隔(单位:秒)</label>
|
||||
<input type="text" class="form-control" id="retry_interval"
|
||||
placeholder="请输入重试间隔,例如:60">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="notify_status">执行结束是否通知</label>
|
||||
<select class="form-control" id="notify_status">
|
||||
<option value="1">不通知</option>
|
||||
<option value="2">失败通知</option>
|
||||
<option value="3">结束通知</option>
|
||||
<option value="4">结果关键字匹配通知</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group notify_type">
|
||||
<label for="notify_type">通知方式</label>
|
||||
<select class="form-control" id="notify_type">
|
||||
<option value="0">请选择</option>
|
||||
<option value="1">邮件</option>
|
||||
<option value="2">webhook</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group notify_receiver_email">
|
||||
<label for="notify_receiver_email">通知者邮箱地址(多个用,分割)</label>
|
||||
<input type="text" class="form-control maxlength" maxlength="255" id="notify_receiver_email"
|
||||
placeholder="请输入邮箱地址">
|
||||
</div>
|
||||
|
||||
<div class="form-group notify_keyword">
|
||||
<label for="notify_keyword">匹配关键字(多个用,分割)</label>
|
||||
<input type="text" class="form-control maxlength" maxlength="255" id="notify_keyword"
|
||||
placeholder="请输入匹配关键字">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label>状态</label>
|
||||
<div class="clearfix">
|
||||
<div class="custom-control custom-radio custom-control-inline">
|
||||
<input type="radio" id="statusOne" value="1" name="is_used"
|
||||
class="custom-control-input">
|
||||
<label class="custom-control-label" for="statusOne">启用</label>
|
||||
</div>
|
||||
<div class="custom-control custom-radio custom-control-inline">
|
||||
<input type="radio" id="statusTwo" value="-1" name="is_used"
|
||||
class="custom-control-input">
|
||||
<label class="custom-control-label" for="statusTwo">禁用</label>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="remark">备注</label>
|
||||
<textarea class="form-control maxlength" maxlength="100" rows="3" id="remark"
|
||||
placeholder="备注"></textarea>
|
||||
</div>
|
||||
<button type="button" id="btnOk" class="btn btn-primary">确认</button>
|
||||
<button type="button" id="btnLoading" class="btn btn-primary" disabled style="display: none">
|
||||
<span class="spinner-grow spinner-grow-sm" role="status" aria-hidden="true"></span>
|
||||
执行中...
|
||||
</button>
|
||||
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap-maxlength/bootstrap-maxlength.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap-multitabs/multitabs.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
|
||||
const hash_id = {{ .HashID }}
|
||||
|
||||
$(".maxlength").maxlength({
|
||||
warningClass: "badge badge-info",
|
||||
limitReachedClass: "badge badge-warning"
|
||||
});
|
||||
|
||||
$("#protocol").change(function () {
|
||||
if ($(this).val() === "2") {
|
||||
$(".http_method").show();
|
||||
} else {
|
||||
$("#http_method").val(0);
|
||||
$(".http_method").hide();
|
||||
}
|
||||
})
|
||||
|
||||
$("#notify_status").change(function () {
|
||||
if ($(this).val() === "1") {
|
||||
$(".notify_type").hide();
|
||||
$("#notify_type").val(0)
|
||||
$(".notify_receiver_email").hide();
|
||||
$("#notify_receiver_email").val("");
|
||||
$(".notify_keyword").hide();
|
||||
$("#notify_keyword").val("");
|
||||
} else if($(this).val() === "2" || $(this).val() === "3") {
|
||||
$(".notify_type").show();
|
||||
$(".notify_keyword").hide();
|
||||
$("#notify_keyword").val("");
|
||||
|
||||
if (("#notify_type").val() === "1") {
|
||||
$(".notify_receiver_email").show();
|
||||
} else {
|
||||
$(".notify_receiver_email").hide();
|
||||
$("#notify_receiver_email").val("");
|
||||
}
|
||||
} else if($(this).val() === "4") {
|
||||
$(".notify_type").show();
|
||||
$(".notify_keyword").show();
|
||||
|
||||
if (("#notify_type").val() === "1") {
|
||||
$(".notify_receiver_email").show();
|
||||
} else {
|
||||
$(".notify_receiver_email").hide();
|
||||
$("#notify_receiver_email").val("");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
$("#notify_type").change(function (){
|
||||
if ($(this).val() === "1") {
|
||||
$(".notify_receiver_email").show();
|
||||
} else {
|
||||
$(".notify_receiver_email").hide();
|
||||
}
|
||||
});
|
||||
|
||||
AjaxForm(
|
||||
"GET",
|
||||
"/api/cron/" + hash_id,
|
||||
"",
|
||||
function () {},
|
||||
function (data) {
|
||||
$("#name").val(data.name);
|
||||
$("#protocol").val(data.protocol);
|
||||
if (data.protocol === 1) {
|
||||
$(".http_method").hide();
|
||||
}
|
||||
|
||||
$("#http_method").val(data.http_method);
|
||||
$("#spec").val(data.spec);
|
||||
$("#command").val(data.command);
|
||||
$("#timeout").val(data.timeout);
|
||||
$("#retry_times").val(data.retry_times);
|
||||
$("#retry_interval").val(data.retry_interval);
|
||||
$("#notify_status").val(data.notify_status);
|
||||
if (data.notify_status === 1) {
|
||||
$(".notify_type").hide();
|
||||
$(".notify_receiver_email").hide();
|
||||
$(".notify_keyword").hide();
|
||||
} else if (data.notify_status === 2 || data.notify_status === 3) {
|
||||
$(".notify_type").show();
|
||||
$(".notify_keyword").hide();
|
||||
|
||||
if (data.notify_type === 1) {
|
||||
$(".notify_receiver_email").show();
|
||||
} else {
|
||||
$(".notify_receiver_email").hide();
|
||||
}
|
||||
} else if (data.notify_status === 4) {
|
||||
$(".notify_type").show();
|
||||
$(".notify_keyword").show();
|
||||
|
||||
if (data.notify_type === 1) {
|
||||
$(".notify_receiver_email").show();
|
||||
} else {
|
||||
$(".notify_receiver_email").hide();
|
||||
}
|
||||
}
|
||||
|
||||
$("#notify_type").val(data.notify_type);
|
||||
$("#notify_receiver_email").val(data.notify_receiver_email);
|
||||
$("#notify_keyword").val(data.notify_keyword);
|
||||
$("input[name='is_used'][value='"+data.is_used+"']").attr("checked", true);
|
||||
$("#remark").val(data.remark);
|
||||
},
|
||||
function (response) {
|
||||
AjaxError(response);
|
||||
}
|
||||
);
|
||||
|
||||
$('#btnOk').on('click', function () {
|
||||
const name = $("#name").val();
|
||||
if (name === "") {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请输入任务名称。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
const spec = $("#spec").val();
|
||||
if (spec === "") {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请输入任务表达式。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
const command = $("#command").val();
|
||||
if (spec === "") {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请输入命令。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
const postData = {
|
||||
id:hash_id,
|
||||
name: name,
|
||||
spec: spec,
|
||||
command: command,
|
||||
protocol: $("#protocol").val(),
|
||||
http_method: $("#http_method").val(),
|
||||
timeout: $("#timeout").val(),
|
||||
retry_times: $("#retry_times").val(),
|
||||
retry_interval: $("#retry_interval").val(),
|
||||
notify_status: $("#notify_status").val(),
|
||||
notify_type: $("#notify_type").val(),
|
||||
notify_receiver_email: $("#notify_receiver_email").val(),
|
||||
notify_keyword: $("#notify_keyword").val(),
|
||||
is_used: $("input[name='is_used']:checked").val(),
|
||||
remark: $("#remark").val(),
|
||||
};
|
||||
|
||||
AjaxForm(
|
||||
"POST",
|
||||
"/api/cron/" + hash_id,
|
||||
postData,
|
||||
function () {
|
||||
$(this).hide();
|
||||
$("#btnLoading").show();
|
||||
},
|
||||
function (data) {
|
||||
$("#btnLoading").hide();
|
||||
$("#btnOk").show();
|
||||
|
||||
$.alert({
|
||||
title: '操作成功',
|
||||
icon: 'mdi mdi-check-decagram',
|
||||
type: 'green',
|
||||
content: '编号:' + data.id + ' 信息修改成功。',
|
||||
buttons: {
|
||||
okay: {
|
||||
text: '关闭',
|
||||
action: function () {
|
||||
location.href = "/cron/list";
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
function (response) {
|
||||
$("#btnLoading").hide();
|
||||
$("#btnOk").show();
|
||||
|
||||
AjaxError(response);
|
||||
}
|
||||
);
|
||||
});
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,325 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
|
||||
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container-fluid p-t-15">
|
||||
<div class="row">
|
||||
|
||||
<div class="col-lg-12">
|
||||
<div class="card">
|
||||
<div class="card-toolbar d-flex flex-column flex-md-row">
|
||||
<div class="toolbar-btn-action">
|
||||
<a class="btn btn-primary m-r-5" href="/cron/add"><i class="mdi mdi-plus"></i> 新增</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>编号</th>
|
||||
<th>任务名称</th>
|
||||
<th>cron 表达式</th>
|
||||
<th>执行方式</th>
|
||||
<th>超时限制(秒)</th>
|
||||
<th>重试次数</th>
|
||||
<th>重试间隔(秒)</th>
|
||||
<th>通知方式</th>
|
||||
<th>创建人</th>
|
||||
<th>创建日期</th>
|
||||
<th style="text-align: center; ">可用状态</th>
|
||||
<th style="text-align: center; ">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody class="tbody">
|
||||
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<ul class="pagination">
|
||||
<ul class="pagination" id="paginationDiv">
|
||||
|
||||
</ul>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/popper.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery.pagination.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
|
||||
// 加载列表页数据
|
||||
getPageListData();
|
||||
|
||||
// 提示工具
|
||||
$('[data-toggle="popover"]').popover();
|
||||
|
||||
//$('[data-toggle="tooltip"]').tooltip();
|
||||
|
||||
function getPageListData(page = 0, page_size = 0) {
|
||||
|
||||
if (parseInt(page) < 1) {
|
||||
page = 1;
|
||||
}
|
||||
|
||||
if (parseInt(page_size) < 1) {
|
||||
page_size = 10;
|
||||
}
|
||||
|
||||
AjaxFormNoAsync(
|
||||
"GET",
|
||||
"/api/cron",
|
||||
{page: page, page_size: page_size},
|
||||
function () {
|
||||
},
|
||||
function (data) {
|
||||
if (data.list.length > 0) {
|
||||
var totalNum = data.pagination.total; //总条数
|
||||
var pageNum = Math.ceil(totalNum / data.pagination.per_page_count); //分页的总页数
|
||||
|
||||
$("#paginationDiv").pagination({
|
||||
current: data.pagination.current_page,
|
||||
pageCount: pageNum,
|
||||
coping: true,
|
||||
homePage: '首页',
|
||||
endPage: '末页',
|
||||
mode: 'fixed',
|
||||
prevContent: '上一页',
|
||||
nextContent: '下一页',
|
||||
activeCls: 'pageActive',
|
||||
prevCls: 'pagePrev',
|
||||
nextCls: 'pageNext',
|
||||
callback: function (api) {
|
||||
$(".tbody").html("");
|
||||
getPageListData(api.getCurrent());
|
||||
}
|
||||
});
|
||||
|
||||
$.each(data.list, function (index, value) {
|
||||
var showUsedBadge = "";
|
||||
var showProtocolBadge = "";
|
||||
var optionUsedName = "";
|
||||
var notifyBadge = "";
|
||||
|
||||
if (value.is_used === 1) {
|
||||
optionUsedName = '禁用';
|
||||
showUsedBadge = '<span class="badge badge-success">启用</span>';
|
||||
}
|
||||
|
||||
if (value.is_used === -1) {
|
||||
optionUsedName = '启用';
|
||||
showUsedBadge = '<span class="badge badge-danger">禁用</span>';
|
||||
}
|
||||
|
||||
if (value.protocol === 1) {
|
||||
showProtocolBadge = '<span class="badge btn-brown">SHELL</span>';
|
||||
}
|
||||
|
||||
if (value.protocol === 2) {
|
||||
showProtocolBadge = '<span class="badge btn-warning">HTTP</span> ';
|
||||
showProtocolBadge += '<span class="badge btn-secondary">' + value.http_method_text + '</span>';
|
||||
}
|
||||
|
||||
notifyBadge = '<span class="badge btn-cyan">' + value.notify_status_text + '</span>';
|
||||
|
||||
const tr = '<tr>\n' +
|
||||
'<td>' + value.id + '</td>\n' +
|
||||
'<td>' + value.name + '</td>\n' +
|
||||
'<td>' + value.spec + ' <a tabindex="' + value.id + '" role="button" class="badge btn-secondary" data-toggle="popover" data-trigger="focus" title="' + value.spec + '" data-content="' + value.command + '">命令</a></td>\n' +
|
||||
'<td>' + showProtocolBadge + '</td>\n' +
|
||||
'<td>' + value.timeout + '</td>\n' +
|
||||
'<td>' + value.retry_times + '</td>\n' +
|
||||
'<td>' + value.retry_interval + '</td>\n' +
|
||||
'<td>' + notifyBadge + '</td>\n' +
|
||||
'<td>' + value.created_user + '</td>\n' +
|
||||
'<td>' + value.created_at + '</td>\n' +
|
||||
'<td style="text-align: center; ">' + showUsedBadge + '</td>\n' +
|
||||
'<td style="text-align: center; ">\n' +
|
||||
'<div class="btn-group">\n' +
|
||||
' <a class="btn btn-xs btn-default btn-option" href="#!" title=""\n' +
|
||||
' data-id="' + value.hashid + '"' +
|
||||
' data-is-used="' + value.is_used + '"' +
|
||||
' data-toggle="tooltip" data-original-title="' + optionUsedName + '">' + optionUsedName + '</a>\n' +
|
||||
' <a class="btn btn-xs btn-default btn-edit" href="#!" title=""\n' +
|
||||
' data-id="' + value.hashid + '"' +
|
||||
' data-toggle="tooltip" data-original-title="编辑">编辑</a>\n' +
|
||||
' <a class="btn btn-xs btn-default btn-log" href="#!" title=""\n' +
|
||||
' data-id="' + value.hashid + '"' +
|
||||
' data-toggle="tooltip" data-original-title="日志">日志</a>\n' +
|
||||
' <a class="btn btn-xs btn-default btn-exec" href="#!" title=""\n' +
|
||||
' data-id="' + value.hashid + '"' +
|
||||
' data-toggle="tooltip" data-original-title="手动执行">手动执行</a>\n' +
|
||||
'</div>\n' +
|
||||
'</td>\n' +
|
||||
'</tr>';
|
||||
|
||||
$(".tbody").append(tr);
|
||||
})
|
||||
} else {
|
||||
// 数据为空
|
||||
const tr = '<tr><td colspan="12" style="text-align: center">暂无数据</td></tr>';
|
||||
$(".tbody").append(tr);
|
||||
}
|
||||
},
|
||||
function (response) {
|
||||
AjaxError(response);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
// 启用/禁用
|
||||
$(document).on('click', '.btn-option', function () {
|
||||
const id = $(this).attr('data-id');
|
||||
const isUsed = $(this).attr('data-is-used');
|
||||
|
||||
var tipMessage = "";
|
||||
var wantUsed = 0;
|
||||
if (isUsed === "1") { // 1=当前为启用状态,需要改成禁用
|
||||
tipMessage = "禁用";
|
||||
wantUsed = -1;
|
||||
}
|
||||
if (isUsed === "-1") { // -1=当前为禁用状态,需要改成启用
|
||||
tipMessage = "启用";
|
||||
wantUsed = 1;
|
||||
}
|
||||
|
||||
const patchData = {
|
||||
id: id,
|
||||
used: wantUsed,
|
||||
};
|
||||
|
||||
$.confirm({
|
||||
title: '谨慎操作',
|
||||
content: '确认要 <strong style="color: red">' + tipMessage + '</strong> 吗?',
|
||||
icon: 'mdi mdi-alert',
|
||||
animation: 'scale',
|
||||
closeAnimation: 'zoom',
|
||||
buttons: {
|
||||
okay: {
|
||||
text: '确认',
|
||||
keys: ['enter'],
|
||||
btnClass: 'btn-orange',
|
||||
action: function () {
|
||||
AjaxForm(
|
||||
"PATCH",
|
||||
"/api/cron/used",
|
||||
patchData,
|
||||
function () {
|
||||
},
|
||||
function (data) {
|
||||
$.alert({
|
||||
title: '操作成功',
|
||||
icon: 'mdi mdi-check-decagram',
|
||||
type: 'green',
|
||||
content: '编号:' + data.id + ' 已' + tipMessage + '。',
|
||||
buttons: {
|
||||
okay: {
|
||||
text: '关闭',
|
||||
action: function () {
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
function (response) {
|
||||
AjaxError(response);
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
cancel: {
|
||||
text: '取消',
|
||||
keys: ['ctrl', 'shift'],
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
// 编辑
|
||||
$(document).on('click', '.btn-edit', function () {
|
||||
location.href = "/cron/edit/" + $(this).attr('data-id');
|
||||
});
|
||||
|
||||
// 日志
|
||||
$(document).on('click', '.btn-log', function () {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '功能开发中...',
|
||||
});
|
||||
})
|
||||
|
||||
// 手动执行
|
||||
$(document).on('click', '.btn-exec', function () {
|
||||
const id = $(this).attr('data-id');
|
||||
$.confirm({
|
||||
title: '谨慎操作',
|
||||
content: '确认要 <strong style="color: red">手动执行</strong> 吗?',
|
||||
icon: 'mdi mdi-alert',
|
||||
animation: 'scale',
|
||||
closeAnimation: 'zoom',
|
||||
buttons: {
|
||||
okay: {
|
||||
text: '确认',
|
||||
keys: ['enter'],
|
||||
btnClass: 'btn-orange',
|
||||
action: function () {
|
||||
AjaxForm(
|
||||
"PATCH",
|
||||
"/api/cron/exec/" + id,
|
||||
{},
|
||||
function () {
|
||||
},
|
||||
function (data) {
|
||||
$.alert({
|
||||
title: '操作成功',
|
||||
icon: 'mdi mdi-check-decagram',
|
||||
type: 'green',
|
||||
content: '编号:' + data.id + ' 任务已开始运行。',
|
||||
buttons: {
|
||||
okay: {
|
||||
text: '关闭',
|
||||
action: function () {
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
function (response) {
|
||||
AjaxError(response);
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
cancel: {
|
||||
text: '取消',
|
||||
keys: ['ctrl', 'shift'],
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -3,8 +3,8 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
|
||||
<link href="bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="bootstrap/css/style.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -12,6 +12,19 @@
|
||||
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md-6 col-lg-4">
|
||||
<div class="card border-secondary">
|
||||
<header class="card-header">
|
||||
<div class="card-title">项目信息 <span class="badge badge-pill badge-warning">{{ .ProjectVersion }}</span> </div>
|
||||
</header>
|
||||
<div class="card-body">
|
||||
<p>操作系统:{{ .GoOS }} <span class="badge badge-brown"> {{ .GoArch }} </span> <span class="badge badge-info"> {{ .GoVersion }} </span></p>
|
||||
<p>项目地址:{{ .ProjectPath }}</p>
|
||||
<p>项目域名:{{ .Host }} <span class="badge badge-secondary"> {{ .Env }} </span></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-lg-4">
|
||||
<div class="card border-secondary">
|
||||
<header class="card-header">
|
||||
@@ -66,21 +79,30 @@
|
||||
<div class="col-md-6 col-lg-4">
|
||||
<div class="card border-secondary">
|
||||
<header class="card-header">
|
||||
<div class="card-title">项目信息</div>
|
||||
<div class="card-title">开源信息</div>
|
||||
</header>
|
||||
<div class="card-body">
|
||||
<p>操作系统:{{ .GoOS }} <span class="badge badge-brown"> {{ .GoArch }} </span></p>
|
||||
<p>项目地址:{{ .ProjectPath }}</p>
|
||||
<p>项目域名:{{ .Host }} <span class="badge badge-secondary"> {{ .Env }} </span></p>
|
||||
<p>GOPATH:{{ .GoPath }}</p>
|
||||
<p>Version:{{ .GoVersion }}</p>
|
||||
<p>Goroutine:{{ .Goroutine }}</p>
|
||||
|
||||
|
||||
<p>GitHub 地址:<a target="_blank" href="https://github.com/xinliangnote/go-gin-api">xinliangnote/go-gin-api</a></p>
|
||||
<p>GitHub Stars:<a href="https://github.com/xinliangnote/go-gin-api/stargazers"><img alt="GitHub stars" src="https://img.shields.io/github/stars/xinliangnote/go-gin-api?style=flat-square"></a></p>
|
||||
<p>GitHub Forks:<a href="https://github.com/xinliangnote/go-gin-api/network"><img alt="GitHub forks" src="https://img.shields.io/github/forks/xinliangnote/go-gin-api?style=flat-square"></a></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-md-6 col-lg-4">
|
||||
<div class="card border-secondary">
|
||||
<header class="card-header">
|
||||
<div class="card-title">欢迎交流</div>
|
||||
</header>
|
||||
<div class="card-body text-center">
|
||||
<p style="height: 105px;">
|
||||
<img style="height: 100px;" src="../../bootstrap/images/qr-code.png"/>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@@ -1,65 +0,0 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
|
||||
<link href="bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container-fluid p-t-15">
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<div class="card">
|
||||
<header class="card-header"><div class="card-title">初始化</div></header>
|
||||
<div class="card-body">
|
||||
<div class="callout callout-warning mb-3">注意:请在 <code>Mac</code> 或 <code>Linux</code> 环境执行。</div>
|
||||
<p>初始化项目所需信息:</p>
|
||||
|
||||
<p><i class="mdi mdi-checkbox-marked-circle"></i> <span>MySQL 数据表:<code>user_demo</code> ;</span></p>
|
||||
|
||||
<p>
|
||||
<button type="button" id="btnOk" class="btn btn-primary">确认</button>
|
||||
<button type="button" id="btnLoading" class="btn btn-primary" disabled style="display: none">
|
||||
<span class="spinner-grow spinner-grow-sm" role="status" aria-hidden="true"></span>
|
||||
执行中...
|
||||
</button>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-6">
|
||||
<div class="card">
|
||||
<header class="card-header"><div class="card-title">执行结果</div></header>
|
||||
<div class="card-body">
|
||||
<pre id="resultDiv"></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
$("#btnOk").click(function(){
|
||||
|
||||
$("#resultDiv").text("");
|
||||
$(this).hide();
|
||||
$("#btnLoading").show();
|
||||
|
||||
$.post("/init_exec","",function (data) {
|
||||
$("#resultDiv").text(data);
|
||||
$("#btnLoading").hide();
|
||||
$("#btnOk").show();
|
||||
})
|
||||
|
||||
})
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
+11
-10
@@ -3,9 +3,9 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
|
||||
<link href="bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="bootstrap/css/style.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -13,10 +13,8 @@
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<div class="card">
|
||||
<header class="card-header"><div class="card-title"> <code>gormgen</code> 代码生成工具</div></header>
|
||||
<header class="card-header"><div class="card-title"> 生成数据表 CURD </div></header>
|
||||
<div class="card-body">
|
||||
<div class="callout callout-warning mb-3">注意:请在 <code>Mac</code> 或 <code>Linux</code> 环境执行。</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="tableSelect">选择数据表,可进行多选:</label>
|
||||
<select multiple="" class="form-control" id="tableSelect" style="height: 260px;">
|
||||
@@ -50,9 +48,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
$("#btnOk").click(function(){
|
||||
@@ -77,7 +75,7 @@
|
||||
tables.push(options.eq(i).val()); // 将所有的值赋给数组
|
||||
}
|
||||
|
||||
$.post("/gormgen_exec",{tables:tables.join(',')},function (data) {
|
||||
$.post("/generator/gorm/execute",{tables:tables.join(',')},function (data) {
|
||||
$("#resultDiv").text(data);
|
||||
$("#btnLoading").hide();
|
||||
$("#btnOk").show();
|
||||
@@ -86,5 +84,8 @@
|
||||
})
|
||||
})
|
||||
</script>
|
||||
<div style="display:none">
|
||||
<script type="text/javascript">document.write(unescape("%3Cspan id='cnzz_stat_icon_1279911342'%3E%3C/span%3E%3Cscript src='https://v1.cnzz.com/z_stat.php%3Fid%3D1279911342%26' type='text/javascript'%3E%3C/script%3E"));</script>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
+12
-11
@@ -3,9 +3,9 @@
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no" />
|
||||
<link href="bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="bootstrap/css/style.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
@@ -13,12 +13,10 @@
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<div class="card">
|
||||
<header class="card-header"><div class="card-title"> <code>handlergen</code> 代码生成工具</div></header>
|
||||
<header class="card-header"><div class="card-title"> 生成控制器方法 </div></header>
|
||||
<div class="card-body">
|
||||
<div class="callout callout-warning mb-3">注意:请在 <code>Mac</code> 或 <code>Linux</code> 环境执行。</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="exampleFormControlInput1">handler 名称,例如:<code>user_handler</code></label>
|
||||
<label for="handlerName">handler 名称,例如:<code>user_handler</code></label>
|
||||
<input type="text" class="form-control" id="handlerName" placeholder="请输入 handler 名称">
|
||||
</div>
|
||||
|
||||
@@ -46,9 +44,9 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function() {
|
||||
$("#btnOk").click(function(){
|
||||
@@ -67,7 +65,7 @@
|
||||
$(this).hide();
|
||||
$("#btnLoading").show();
|
||||
|
||||
$.post("/handlergen_exec",{name:handlerName},function (data) {
|
||||
$.post("/generator/handler/execute",{name:handlerName},function (data) {
|
||||
$("#resultDiv").text(data);
|
||||
$("#btnLoading").hide();
|
||||
$("#btnOk").show();
|
||||
@@ -76,5 +74,8 @@
|
||||
})
|
||||
})
|
||||
</script>
|
||||
<div style="display:none">
|
||||
<script type="text/javascript">document.write(unescape("%3Cspan id='cnzz_stat_icon_1279911342'%3E%3C/span%3E%3Cscript src='https://v1.cnzz.com/z_stat.php%3Fid%3D1279911342%26' type='text/javascript'%3E%3C/script%3E"));</script>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -9,6 +9,7 @@
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-touch-fullscreen" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="default">
|
||||
<link rel="stylesheet" type="text/css" href="bootstrap/js/jquery-confirm/jquery-confirm.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="bootstrap/css/materialdesignicons.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="bootstrap/css/bootstrap.min.css">
|
||||
<link rel="stylesheet" type="text/css" href="bootstrap/js/bootstrap-multitabs/multitabs.min.css">
|
||||
@@ -21,45 +22,29 @@
|
||||
<div class="lyear-layout-container">
|
||||
<!--左侧导航-->
|
||||
<aside class="lyear-layout-sidebar">
|
||||
|
||||
|
||||
<!-- logo -->
|
||||
<div id="logo" class="sidebar-header">
|
||||
<a href="/">
|
||||
<img src="bootstrap/images/logo-sidebar.png"/>
|
||||
</a>
|
||||
</div>
|
||||
<div class="lyear-layout-sidebar-info lyear-scroll">
|
||||
|
||||
<div class="lyear-layout-sidebar-info lyear-scroll">
|
||||
|
||||
<nav class="sidebar-main">
|
||||
<ul class="nav-drawer">
|
||||
<li class="nav-item active"> <a class="multitabs" href="/dashboard"><i class="mdi mdi-home"></i> <span>仪表盘</span></a> </li>
|
||||
<li class="nav-item"> <a class="multitabs" href="/configinfo"><i class="mdi mdi-settings-box"></i> <span>配置信息</span></a> </li>
|
||||
|
||||
<li class="nav-item nav-item-has-subnav">
|
||||
<a href="javascript:void(0)"><i class="mdi mdi-code-not-equal-variant"></i> <span>代码生成</span></a>
|
||||
<ul class="nav nav-subnav">
|
||||
<li> <a class="multitabs" href="/init">初始化</a> </li>
|
||||
<li> <a class="multitabs" href="/gormgen">gormgen</a> </li>
|
||||
<li> <a class="multitabs" href="/handlergen">handlergen</a> </li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="nav-item"> <a href="/swagger/index.html" target="_blank" ><i class="mdi mdi-file-document-box"></i> <span>接口文档</span></a> </li>
|
||||
<li class="nav-item"> <a href="/graphql" target="_blank" ><i class="mdi mdi-file-document-box-search"></i> <span>GraphQL</span></a> </li>
|
||||
<li class="nav-item"> <a href="/metrics" target="_blank" ><i class="mdi mdi-speedometer"></i> <span>接口指标</span></a> </li>
|
||||
|
||||
</ul>
|
||||
</nav>
|
||||
</div>
|
||||
|
||||
|
||||
</aside>
|
||||
<!--End 左侧导航-->
|
||||
|
||||
|
||||
<!--头部信息-->
|
||||
<header class="lyear-layout-header">
|
||||
|
||||
|
||||
<nav class="navbar">
|
||||
|
||||
|
||||
<div class="navbar-left">
|
||||
<div class="lyear-aside-toggler">
|
||||
<span class="lyear-toggler-bar"></span>
|
||||
@@ -67,7 +52,7 @@
|
||||
<span class="lyear-toggler-bar"></span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<ul class="navbar-right d-flex align-items-center">
|
||||
|
||||
<!--切换主题配色-->
|
||||
@@ -114,28 +99,28 @@
|
||||
<span class="inverse">
|
||||
<input type="radio" name="header_bg" value="default" id="header_bg_1" checked>
|
||||
<label for="header_bg_1"></label>
|
||||
</span>
|
||||
<span>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="header_bg" value="color_2" id="header_bg_2">
|
||||
<label for="header_bg_2"></label>
|
||||
</span>
|
||||
<span>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="header_bg" value="color_3" id="header_bg_3">
|
||||
<label for="header_bg_3"></label>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="header_bg" value="color_4" id="header_bg_4">
|
||||
<label for="header_bg_4"></label>
|
||||
</span>
|
||||
<span>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="header_bg" value="color_5" id="header_bg_5">
|
||||
<label for="header_bg_5"></label>
|
||||
</span>
|
||||
<span>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="header_bg" value="color_6" id="header_bg_6">
|
||||
<label for="header_bg_6"></label>
|
||||
</span>
|
||||
<span>
|
||||
</span>
|
||||
<span>
|
||||
<input type="radio" name="header_bg" value="color_7" id="header_bg_7">
|
||||
<label for="header_bg_7"></label>
|
||||
</span>
|
||||
@@ -183,18 +168,43 @@
|
||||
</li>
|
||||
<!--切换主题配色-->
|
||||
|
||||
<li class="dropdown dropdown-profile">
|
||||
<a href="javascript:void(0)" data-toggle="dropdown" class="dropdown-toggle">
|
||||
<img class="img-avatar img-avatar-48 m-r-10" src="bootstrap/images/users/avatar.png">
|
||||
<span id="nickname"></span>
|
||||
</a>
|
||||
<ul class="dropdown-menu dropdown-menu-right">
|
||||
<li>
|
||||
<a class="multitabs dropdown-item" data-url="/admin/modify_info" href="javascript:void(0)">
|
||||
<i class="mdi mdi-account"></i> 个人信息
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a class="multitabs dropdown-item" data-url="/admin/modify_password" href="javascript:void(0)">
|
||||
<i class="mdi mdi-lock-outline"></i> 修改密码
|
||||
</a>
|
||||
</li>
|
||||
<li class="dropdown-divider"></li>
|
||||
<li>
|
||||
<a class="dropdown-item" href="javascript:void(0)" id="logout">
|
||||
<i class="mdi mdi-logout-variant"></i> 退出登录
|
||||
</a>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
|
||||
|
||||
</nav>
|
||||
|
||||
|
||||
</header>
|
||||
<!--End 头部信息-->
|
||||
|
||||
|
||||
<!--页面主要内容-->
|
||||
<main class="lyear-layout-content">
|
||||
|
||||
|
||||
<div id="iframe-content"></div>
|
||||
|
||||
|
||||
</main>
|
||||
<!--End 页面主要内容-->
|
||||
</div>
|
||||
@@ -206,6 +216,80 @@
|
||||
<script type="text/javascript" src="bootstrap/js/perfect-scrollbar.min.js"></script>
|
||||
<script type="text/javascript" src="bootstrap/js/bootstrap-multitabs/multitabs.min.js"></script>
|
||||
<script type="text/javascript" src="bootstrap/js/jquery.cookie.min.js"></script>
|
||||
<script type="text/javascript" src="bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="bootstrap/js/index.min.js"></script>
|
||||
<script type="text/javascript" src="bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
AjaxForm(
|
||||
"GET",
|
||||
"/api/admin/info",
|
||||
"",
|
||||
function () {},
|
||||
function (data) {
|
||||
$("#nickname").html(data.nickname);
|
||||
$(".nav-drawer").html("");
|
||||
let li = '<li class="nav-item active"><a class="multitabs" href="/dashboard"><i class="mdi mdi-home"></i> <span>仪表盘</span></a></li>';
|
||||
|
||||
if (data.menu.length > 0) {
|
||||
let newArr = [];
|
||||
data.menu.forEach(function (v) {
|
||||
if (v.pid === 0) {
|
||||
v.children = [];
|
||||
newArr.push(v)
|
||||
}
|
||||
});
|
||||
|
||||
data.menu.forEach(function (v) {
|
||||
newArr.forEach(function (item) {
|
||||
if (v.pid === item.id) {
|
||||
item.children.push(v)
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
$.each(newArr, function (index, value) {
|
||||
li += '<li class="nav-item nav-item-has-subnav">';
|
||||
li += '<a href="javascript:void(0)"><i class="mdi '+ value.icon +'"></i> <span>'+ value.name +'</span></a>';
|
||||
li += '<ul class="nav nav-subnav">';
|
||||
|
||||
|
||||
value.children.forEach(function (item) {
|
||||
li += '<li> <a class="multitabs" href="'+ item.link +'"> '+ item.name +' </a> </li>';
|
||||
});
|
||||
|
||||
li += '</ul></li>';
|
||||
});
|
||||
|
||||
$(".nav-drawer").html(li);
|
||||
}
|
||||
},
|
||||
function (response) {
|
||||
AjaxError(response);
|
||||
}
|
||||
);
|
||||
|
||||
$("#logout").on('click', function () {
|
||||
AjaxForm(
|
||||
"POST",
|
||||
"/api/admin/logout",
|
||||
"",
|
||||
function () {},
|
||||
function () {
|
||||
// 清空 cookie
|
||||
$.cookie('_nav_url_', '');
|
||||
$.cookie('_nav_title_', '');
|
||||
$.cookie('_login_token_', '');
|
||||
|
||||
parent.window.close();
|
||||
window.open("/login");
|
||||
},
|
||||
function (response) {
|
||||
AjaxError(response);
|
||||
}
|
||||
);
|
||||
})
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,263 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0, minimal-ui">
|
||||
<title>项目初始化</title>
|
||||
<link rel="shortcut icon" type="image/x-icon" href="../../bootstrap/favicon.ico">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-touch-fullscreen" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="default">
|
||||
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/js/bootstrap-select/bootstrap-select.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container-fluid p-t-15">
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
<div class="card-title">项目初始化</div>
|
||||
</header>
|
||||
<div class="card-body">
|
||||
<h5 class="card-title"><span class="badge badge-info">检测 · 环境</span></h5>
|
||||
<div class="input-group mb-3">
|
||||
<p class="text-left">所需版本:<code>go1.15+</code> (<small>当前版本 </small><code>{{.GoVersion}}</code>)</p>
|
||||
</div>
|
||||
|
||||
<h5 class="card-title"><span class="badge badge-info">配置 · 语言</span></h5>
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">语言</span>
|
||||
</div>
|
||||
<select class="form-control select-picker col-lg-3" data-width="auto"
|
||||
data-live-search="true" id="language">
|
||||
<option value="zh-cn">简体中文</option>
|
||||
<option value="en-us">English</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<h5 class="card-title"><span class="badge badge-info">配置 · Redis</span></h5>
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">地址</span>
|
||||
</div>
|
||||
<input type="text" class="form-control" id="redis_addr" value="{{ .Config.Redis.Addr }}"
|
||||
placeholder="请输入地址,例如:127.0.0.1:6379">
|
||||
</div>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">密码</span>
|
||||
</div>
|
||||
<input type="password" class="form-control" id="redis_pass" value="{{ .Config.Redis.Pass }}"
|
||||
placeholder="请输入密码">
|
||||
</div>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">DB </span>
|
||||
</div>
|
||||
<input type="text" class="form-control" id="redis_db" value="{{ .Config.Redis.Db }}"
|
||||
placeholder="请输入 DB ,序号从 0 开始,默认是 0">
|
||||
</div>
|
||||
|
||||
<h5 class="card-title"><span class="badge badge-info">配置 · MySQL</span></h5>
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">地址</span>
|
||||
</div>
|
||||
<input type="text" class="form-control" id="mysql_addr" value="{{ .Config.MySQL.Write.Addr }}"
|
||||
placeholder="请输入服务器地址,例如:127.0.0.1:3306">
|
||||
</div>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">用户</span>
|
||||
</div>
|
||||
<input type="text" class="form-control" id="mysql_user" value="{{ .Config.MySQL.Write.User }}"
|
||||
placeholder="请输入用户名">
|
||||
</div>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">密码</span>
|
||||
</div>
|
||||
<input type="password" class="form-control" id="mysql_pass" value="{{ .Config.MySQL.Write.Pass }}"
|
||||
placeholder="请输入密码">
|
||||
</div>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">DB </span>
|
||||
</div>
|
||||
<input type="text" class="form-control" id="mysql_name" value="{{ .Config.MySQL.Write.Name }}"
|
||||
placeholder="请输入数据库名">
|
||||
</div>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<small>
|
||||
<i class="mdi mdi-checkbox-marked-circle"></i>
|
||||
初始化 MySQL 数据表:<code>authorized</code>、
|
||||
<code>authorized_api</code> 、
|
||||
<code>admin</code>、
|
||||
<code>menu</code>、
|
||||
<code>menu_action</code>、
|
||||
<code>admin_menu</code>、
|
||||
<code>cron_task</code>;
|
||||
</small>
|
||||
</div>
|
||||
|
||||
<button type="button" id="btnOk" class="btn btn-primary">初始化项目</button>
|
||||
<button type="button" id="btnLoading" class="btn btn-primary" disabled style="display: none">
|
||||
<span class="spinner-grow spinner-grow-sm" role="status" aria-hidden="true"></span>
|
||||
执行中...
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-6">
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
<div class="card-title">执行结果</div>
|
||||
</header>
|
||||
<div class="card-body">
|
||||
<pre id="resultDiv"></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/popper.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap-select/bootstrap-select.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap-select/i18n/defaults-zh_CN.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
$('.select-picker').selectpicker();
|
||||
|
||||
$("#btnOk").click(function () {
|
||||
|
||||
const redis_addr = $("#redis_addr").val();
|
||||
if (redis_addr === "") {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请输入 Redis 服务器地址。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
const redis_db = $("#redis_db").val();
|
||||
if (redis_db === "") {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请输入 Redis DB。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
const mysql_addr = $("#mysql_addr").val();
|
||||
if (mysql_addr === "") {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请输入 MySQL 服务器地址。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
const mysql_user = $("#mysql_user").val();
|
||||
if (mysql_user === "") {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请输入 MySQL 用户名。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
const mysql_pass = $("#mysql_pass").val();
|
||||
if (mysql_pass === "") {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请输入 MySQL 密码。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
const mysql_name = $("#mysql_name").val();
|
||||
if (mysql_name === "") {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请输入 MySQL 数据库名。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
const postData = {
|
||||
language: $("#language").val(),
|
||||
|
||||
redis_addr: redis_addr,
|
||||
redis_pass: $("#redis_pass").val(),
|
||||
redis_db: redis_db,
|
||||
|
||||
mysql_addr: mysql_addr,
|
||||
mysql_user: mysql_user,
|
||||
mysql_pass: mysql_pass,
|
||||
mysql_name: mysql_name,
|
||||
};
|
||||
|
||||
AjaxForm(
|
||||
"POST",
|
||||
"/install/execute",
|
||||
postData,
|
||||
function () {
|
||||
$("#resultDiv").text("");
|
||||
$(this).hide();
|
||||
$("#btnLoading").show();
|
||||
},
|
||||
function (data) {
|
||||
$("#resultDiv").text(data);
|
||||
$("#btnLoading").hide();
|
||||
$("#btnOk").show();
|
||||
|
||||
$.alert({
|
||||
title: '操作成功',
|
||||
icon: 'mdi mdi-check-decagram',
|
||||
type: 'green',
|
||||
content: '服务初始化成功,<strong style="color: red">请重新启动服务!</strong>',
|
||||
});
|
||||
},
|
||||
function (response) {
|
||||
$("#btnLoading").hide();
|
||||
$("#btnOk").show();
|
||||
|
||||
AjaxError(response);
|
||||
}
|
||||
);
|
||||
|
||||
})
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,172 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0, minimal-ui">
|
||||
<title>服务升级</title>
|
||||
<link rel="shortcut icon" type="image/x-icon" href="../../bootstrap/favicon.ico">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-touch-fullscreen" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="default">
|
||||
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container-fluid p-t-15">
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
<div class="card-title">无版本号情况,服务升级</div>
|
||||
</header>
|
||||
<div class="card-body">
|
||||
<p class="h6">发现代码运行报错或缺少表字段,如何进行服务初始化?</p>
|
||||
<p>1、删除根目录文件:
|
||||
<mark>{{ .LockFile }}</mark>
|
||||
。
|
||||
</p>
|
||||
<p>2、重新启动服务。</p>
|
||||
|
||||
<hr/>
|
||||
<p class="h6">发现 GitHub 仓库更新了新代码,如何进行服务升级?</p>
|
||||
<p>1、源代码升级:
|
||||
<mark>拉取最新代码,覆盖旧版本代码即可。</mark>
|
||||
</p>
|
||||
<p>2、数据表升级:</p>
|
||||
|
||||
<table style="margin-left: 24px;" class="table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>数据表</th>
|
||||
<th>是否存在</th>
|
||||
<th>操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{{range $key, $value := .List}}
|
||||
<tr>
|
||||
<td>{{$value.TableName}}</td>
|
||||
<td>
|
||||
{{if eq $value.IsHave 1}} <font class="text-success">已存在</font>
|
||||
{{else}} <font class="text-danger">不存在</font>
|
||||
{{end}}
|
||||
</td>
|
||||
<td>
|
||||
<button class="btn btn-xs btn-info upgrade" data-op="table"
|
||||
data-table="{{$value.TableName}}">创建表结构
|
||||
</button>
|
||||
<button class="btn btn-xs btn-cyan upgrade" data-op="table_data"
|
||||
data-table="{{$value.TableName}}">初始化数据
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
{{end}}
|
||||
</tbody>
|
||||
</table>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-6">
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
<div class="card-title">有版本号情况,服务升级</div>
|
||||
</header>
|
||||
<div class="card-body">
|
||||
<p class="h6">
|
||||
<span class="badge badge-pill badge-warning">v1.2.6</span>
|
||||
->
|
||||
<span class="badge badge-pill badge-warning">v1.2.7</span>
|
||||
</p>
|
||||
|
||||
<p>1、源代码升级:
|
||||
<mark>拉取最新代码,覆盖旧版本代码即可。</mark>
|
||||
</p>
|
||||
<p>2、数据表升级:
|
||||
<mark class="btn btn-xs btn-info upgrade" data-op="table" data-table="cron_task">创建 cron_task 表结构</mark>
|
||||
</p>
|
||||
<p>3、系统管理员->菜单管理,新增侧边栏:后台任务模块,同时添加菜单栏的功能权限。</p>
|
||||
<p>4、系统管理员->管理员,对管理员进行菜单授权。</p>
|
||||
<p>5、退出重新登录,即可以看到自己新增的模块。</p>
|
||||
|
||||
<hr/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
$(document).on('click', '.upgrade', function () {
|
||||
const op = $(this).attr('data-op');
|
||||
const table = $(this).attr('data-table');
|
||||
|
||||
let tipMsg;
|
||||
if (op === "table") {
|
||||
tipMsg = '请先确保数据表不存在,确定要创建表结构吗?';
|
||||
}
|
||||
|
||||
if (op === "table_data") {
|
||||
tipMsg = '请先确保表中数据不存在,确定要初始化表数据吗?';
|
||||
}
|
||||
|
||||
$.confirm({
|
||||
title: '谨慎操作',
|
||||
content: tipMsg,
|
||||
icon: 'mdi mdi-alert',
|
||||
animation: 'scale',
|
||||
closeAnimation: 'zoom',
|
||||
buttons: {
|
||||
okay: {
|
||||
text: '确认',
|
||||
keys: ['enter'],
|
||||
btnClass: 'btn-orange',
|
||||
action: function () {
|
||||
AjaxForm(
|
||||
"POST",
|
||||
'/upgrade/execute',
|
||||
{table_name: table, op: op},
|
||||
function () {
|
||||
},
|
||||
function (data) {
|
||||
$.alert({
|
||||
title: '操作成功',
|
||||
icon: 'mdi mdi-check-decagram',
|
||||
type: 'green',
|
||||
content: data,
|
||||
buttons: {
|
||||
okay: {
|
||||
text: '关闭',
|
||||
action: function () {
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
function (response) {
|
||||
AjaxError(response);
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
cancel: {
|
||||
text: '取消',
|
||||
keys: ['ctrl', 'shift'],
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,261 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
|
||||
<link href="../../bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/js/bootstrap-multitabs/multitabs.min.css" rel="stylesheet" type="text/css">
|
||||
<link href="../../bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container-fluid p-t-15">
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-title">功能权限</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
||||
<div class="alert alert-warning" role="alert">
|
||||
接口地址支持通配符(*),其中 * 表示 1 级,** 表示 n 级。
|
||||
</div>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-prepend">
|
||||
<label class="input-group-text" for="request_method">选择请求方式</label>
|
||||
</div>
|
||||
<select class="custom-select" id="request_method">
|
||||
<option value="GET">GET</option>
|
||||
<option value="POST">POST</option>
|
||||
<option value="PUT">PUT</option>
|
||||
<option value="DELETE">DELETE</option>
|
||||
<option value="PATCH">PATCH</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text" id="inputGroup-sizing-default">输入接口地址</span>
|
||||
</div>
|
||||
<input type="text" class="form-control" maxlength="60" id="request_api"
|
||||
placeholder="接口地址">
|
||||
</div>
|
||||
|
||||
<button type="button" id="btnOk" class="btn btn-primary">确认</button>
|
||||
<button type="button" id="btnLoading" class="btn btn-primary" disabled style="display: none">
|
||||
<span class="spinner-grow spinner-grow-sm" role="status" aria-hidden="true"></span>
|
||||
执行中...
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-6">
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
<div class="card-title">已配置功能权限
|
||||
<small id="menuName"></small>
|
||||
</div>
|
||||
</header>
|
||||
<div class="card-body apis">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/popper.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap-maxlength/bootstrap-maxlength.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap-multitabs/multitabs.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
|
||||
const hash_id = {{ .HashID }}
|
||||
|
||||
$("input#request_api").maxlength({
|
||||
warningClass: "badge badge-info",
|
||||
limitReachedClass: "badge badge-warning"
|
||||
});
|
||||
|
||||
// 加载列表页数据
|
||||
getListData();
|
||||
|
||||
function getListData() {
|
||||
AjaxForm(
|
||||
"GET",
|
||||
"/api/menu_action",
|
||||
{id: hash_id},
|
||||
function () {
|
||||
|
||||
},
|
||||
function (data) {
|
||||
$("#menuName").html("(菜单栏:" + data.menu_name + ")");
|
||||
if (data.list.length > 0) {
|
||||
var badgeMethodClass = "";
|
||||
|
||||
$.each(data.list, function (index, value) {
|
||||
if (value.method === "GET") {
|
||||
badgeMethodClass = "badge-primary";
|
||||
} else if (value.method === "POST") {
|
||||
badgeMethodClass = "badge-success";
|
||||
} else if (value.method === "DELETE") {
|
||||
badgeMethodClass = "badge-danger";
|
||||
} else if (value.method === "PUT") {
|
||||
badgeMethodClass = "badge-yellow";
|
||||
} else if (value.method === "PATCH") {
|
||||
badgeMethodClass = "badge-cyan";
|
||||
} else {
|
||||
badgeMethodClass = "badge-dark";
|
||||
}
|
||||
|
||||
const p = '<p>\n' +
|
||||
'<a href="#!" data-id="' + value.hash_id + '" data-api="' + value.api + '" class="del">' +
|
||||
'<span class="badge badge-dark"><i class="mdi mdi-window-close"></i></span>\n' +
|
||||
'</a>\n' +
|
||||
'<span class="badge ' + badgeMethodClass + '">' + value.method + '</span>\n' + value.api
|
||||
;
|
||||
|
||||
$(".apis").append(p);
|
||||
})
|
||||
} else {
|
||||
// 数据为空
|
||||
const p = '<p>暂无功能权限</p>';
|
||||
$(".apis").append(p);
|
||||
}
|
||||
},
|
||||
function (response) {
|
||||
AjaxError(response);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
$('#btnOk').on('click', function () {
|
||||
const requestMethod = $("#request_method").val();
|
||||
const requestApi = $("#request_api").val();
|
||||
if (requestMethod === "") {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请选择请求方式。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
if (requestApi === "") {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请输入请求地址。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
const postData = {
|
||||
method: requestMethod,
|
||||
api: requestApi,
|
||||
id: hash_id,
|
||||
};
|
||||
|
||||
AjaxForm(
|
||||
"POST",
|
||||
"/api/menu_action",
|
||||
postData,
|
||||
function () {
|
||||
$(this).hide();
|
||||
$("#btnLoading").show();
|
||||
},
|
||||
function () {
|
||||
$("#btnLoading").hide();
|
||||
$("#btnOk").show();
|
||||
|
||||
$.alert({
|
||||
title: '操作成功',
|
||||
icon: 'mdi mdi-check-decagram',
|
||||
type: 'green',
|
||||
content: '接口:' + requestApi + ' 配置完成。',
|
||||
buttons: {
|
||||
okay: {
|
||||
text: '关闭',
|
||||
action: function () {
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
function (response) {
|
||||
$("#btnLoading").hide();
|
||||
$("#btnOk").show();
|
||||
|
||||
AjaxError(response);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
$(document).on('click', '.del', function () {
|
||||
const id = $(this).attr('data-id');
|
||||
const api = $(this).attr('data-api');
|
||||
|
||||
$.confirm({
|
||||
title: '谨慎操作',
|
||||
content: '确认要 <strong style="color: red">取消配置</strong> 吗?',
|
||||
icon: 'mdi mdi-alert',
|
||||
animation: 'scale',
|
||||
closeAnimation: 'zoom',
|
||||
buttons: {
|
||||
okay: {
|
||||
text: '确认',
|
||||
keys: ['enter'],
|
||||
btnClass: 'btn-orange',
|
||||
action: function () {
|
||||
AjaxForm(
|
||||
"DELETE",
|
||||
'/api/menu_action/' + id,
|
||||
"",
|
||||
function () {
|
||||
|
||||
},
|
||||
function () {
|
||||
$.alert({
|
||||
title: '操作成功',
|
||||
icon: 'mdi mdi-check-decagram',
|
||||
type: 'green',
|
||||
content: '接口:' + api + ' 已取消配置。',
|
||||
buttons: {
|
||||
okay: {
|
||||
text: '关闭',
|
||||
action: function () {
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
function (response) {
|
||||
AjaxError(response);
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
cancel: {
|
||||
text: '取消',
|
||||
keys: ['ctrl', 'shift'],
|
||||
}
|
||||
}
|
||||
});
|
||||
})
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,493 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
|
||||
<link href="../../bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/js/jquery-treegrid/jquery.treegrid.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/js/bootstrap-select/bootstrap-select.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/js/bootstrap-table/bootstrap-table.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container-fluid p-t-15">
|
||||
<div class="row">
|
||||
<div class="col-lg-5">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-title">配置菜单栏</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">上级菜单</span>
|
||||
</div>
|
||||
<select class="form-control select-picker col-lg-3" data-width="auto"
|
||||
data-live-search="true" id="level">
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">菜单名称</span>
|
||||
</div>
|
||||
<input type="text" class="form-control" id="name" placeholder="请输入菜单名称">
|
||||
</div>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">菜单图标</span>
|
||||
</div>
|
||||
<input type="text" class="form-control" id="icon" placeholder="请输入菜单图标">
|
||||
</div>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">链接地址</span>
|
||||
</div>
|
||||
<input type="text" class="form-control" id="link" placeholder="请输入链接地址">
|
||||
</div>
|
||||
|
||||
<input type="hidden" id="id">
|
||||
|
||||
<button type="button" id="btnOk" class="btn btn-primary">提交</button>
|
||||
<button type="button" id="btnLoading" class="btn btn-primary" disabled style="display: none">
|
||||
<span class="spinner-grow spinner-grow-sm" role="status" aria-hidden="true"></span>
|
||||
提交中...
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-7">
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
<div class="card-title">菜单栏列表</div>
|
||||
</header>
|
||||
<div class="card-body">
|
||||
<table class="tree-table"></table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/popper.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap-table/bootstrap-table.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap-table/locale/bootstrap-table-zh-CN.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery-treegrid/jquery.treegrid.min.js"></script>
|
||||
<script type="text/javascript"
|
||||
src="../../bootstrap/js/bootstrap-table/extensions/treegrid/bootstrap-table-treegrid.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap-select/bootstrap-select.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap-select/i18n/defaults-zh_CN.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
$('.select-picker').selectpicker();
|
||||
let menuData = "";
|
||||
|
||||
AjaxFormNoAsync(
|
||||
"GET",
|
||||
"/api/menu",
|
||||
"",
|
||||
function () {
|
||||
},
|
||||
function (data) {
|
||||
$("#level").append("<option value='-1'>一级目录</option>");
|
||||
$.each(data.list, function (index, value) {
|
||||
$("#level").append("<option value='" + value.id + "'>" + value.name + "</option>");
|
||||
});
|
||||
|
||||
$("#level option:eq(0)").attr('selected', 'selected'); //选中第一个
|
||||
$("#level").selectpicker('refresh');
|
||||
|
||||
menuData = data.list;
|
||||
},
|
||||
function (response) {
|
||||
AjaxError(response);
|
||||
}
|
||||
);
|
||||
|
||||
const $treeTable = $('.tree-table');
|
||||
$treeTable.bootstrapTable({
|
||||
data: menuData,
|
||||
idField: 'id',
|
||||
uniqueId: 'id',
|
||||
dataType: 'jsonp',
|
||||
//toolbar: '#toolbar2',
|
||||
columns: [
|
||||
{
|
||||
field: 'name',
|
||||
title: '名称',
|
||||
formatter: function (value, row, index) {
|
||||
return '<i class="mdi ' + row.icon + '"></i>' + row.name;
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'sort',
|
||||
title: '排序',
|
||||
formatter: function (value, row, index) {
|
||||
return '<input type="text" value="'+ row.sort +'" data-id="' + row.hashid + '" data-name="' + row.name + '" class="form-control sort" style="width:60px;">';
|
||||
}
|
||||
},
|
||||
{
|
||||
field: 'is_used',
|
||||
title: '是否启用',
|
||||
//sortable: true,
|
||||
formatter: function (value, row, index) {
|
||||
let is_checked;
|
||||
if (value === -1) {
|
||||
is_checked = '';
|
||||
} else if (value === 1) {
|
||||
is_checked = 'checked="checked"';
|
||||
}
|
||||
return '<div class="custom-control custom-switch"><input type="checkbox" class="custom-control-input" id="customSwitch' + row.id + '" ' + is_checked + '><label class="custom-control-label customSwitch" state="' + value + '" hashid="' + row.hashid + '" for="customSwitch' + row.id + ')"></label></div>';
|
||||
},
|
||||
},
|
||||
{
|
||||
field: 'link',
|
||||
title: '链接地址'
|
||||
},
|
||||
{
|
||||
field: 'operate',
|
||||
title: '操作',
|
||||
align: 'center',
|
||||
events: {
|
||||
'click .role-add': function (e, value, row, index) {
|
||||
add(row.hashid);
|
||||
},
|
||||
'click .role-delete': function (e, value, row, index) {
|
||||
del(row.hashid);
|
||||
},
|
||||
'click .role-edit': function (e, value, row, index) {
|
||||
update(row.hashid);
|
||||
},
|
||||
'click .role-action': function (e, value, row, index) {
|
||||
if (row.link !== '') {
|
||||
location.href = "/admin/menu_action/" + row.hashid;
|
||||
} else {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '当前菜单无需设置功能权限。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
}
|
||||
},
|
||||
formatter: operateFormatter
|
||||
}
|
||||
],
|
||||
|
||||
treeShowField: 'name',
|
||||
parentIdField: 'pid',
|
||||
|
||||
onResetView: function (menuData) {
|
||||
$treeTable.treegrid({
|
||||
initialState: 'collapsed', // 所有节点都折叠
|
||||
treeColumn: 0,
|
||||
//expanderExpandedClass: 'mdi mdi-folder-open', // 可自定义图标样式
|
||||
//expanderCollapsedClass: 'mdi mdi-folder',
|
||||
});
|
||||
|
||||
// 只展开树形的第一集节点
|
||||
$treeTable.treegrid('getRootNodes').treegrid('expand');
|
||||
},
|
||||
|
||||
});
|
||||
|
||||
// 操作按钮
|
||||
function operateFormatter(value, row, index) {
|
||||
return [
|
||||
'<a type="button" class="role-add btn btn-xs btn-default m-r-5" title="编辑" data-toggle="tooltip"><i class="mdi mdi-plus"></i></a>',
|
||||
'<a type="button" class="role-edit btn btn-xs btn-default m-r-5" title="修改" data-toggle="tooltip"><i class="mdi mdi-pencil"></i></a>',
|
||||
'<a type="button" class="role-action btn btn-xs btn-default m-r-5" title="功能权限" data-toggle="tooltip"><i class="mdi mdi-playlist-plus"></i></a>',
|
||||
'<a type="button" class="role-delete btn btn-xs btn-default" title="删除" data-toggle="tooltip"><i class="mdi mdi-delete"></i></a>'
|
||||
].join('');
|
||||
}
|
||||
|
||||
function add(id) {
|
||||
$("#id").val('');
|
||||
$("#name").val('');
|
||||
$("#icon").val('');
|
||||
$("#link").val('');
|
||||
getMenuDetail(id, 'add');
|
||||
}
|
||||
|
||||
function del(id) {
|
||||
$.confirm({
|
||||
title: '谨慎操作',
|
||||
content: '确认要 <strong style="color: red">删除</strong> 吗?',
|
||||
icon: 'mdi mdi-alert',
|
||||
animation: 'scale',
|
||||
closeAnimation: 'zoom',
|
||||
buttons: {
|
||||
okay: {
|
||||
text: '确认',
|
||||
keys: ['enter'],
|
||||
btnClass: 'btn-orange',
|
||||
action: function () {
|
||||
AjaxForm(
|
||||
"DELETE",
|
||||
"/api/menu/" + id,
|
||||
"",
|
||||
function () {
|
||||
},
|
||||
function () {
|
||||
$.alert({
|
||||
title: '操作成功',
|
||||
icon: 'mdi mdi-check-decagram',
|
||||
type: 'green',
|
||||
content: '已删除成功。',
|
||||
buttons: {
|
||||
okay: {
|
||||
text: '关闭',
|
||||
action: function () {
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
function (response) {
|
||||
AjaxError(response);
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
cancel: {
|
||||
text: '取消',
|
||||
keys: ['ctrl', 'shift'],
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function update(id) {
|
||||
getMenuDetail(id, 'update');
|
||||
}
|
||||
|
||||
function getMenuDetail(id, op) {
|
||||
AjaxForm(
|
||||
"GET",
|
||||
"/api/menu/" + id,
|
||||
"",
|
||||
function () {
|
||||
},
|
||||
function (data) {
|
||||
if (op === 'add') {
|
||||
$('#level').selectpicker('val', data.id);
|
||||
$("#level").selectpicker('refresh');
|
||||
$("#level").attr("disabled", "");
|
||||
}
|
||||
|
||||
if (op === 'update') {
|
||||
if (data.pid === 0) {
|
||||
data.pid = -1
|
||||
}
|
||||
$('#level').selectpicker('val', data.pid);
|
||||
$("#level").selectpicker('refresh');
|
||||
$("#level").attr("disabled", "");
|
||||
|
||||
$("#id").val(id);
|
||||
$("#name").val(data.name);
|
||||
$("#icon").val(data.icon);
|
||||
$("#link").val(data.link);
|
||||
}
|
||||
},
|
||||
function (response) {
|
||||
AjaxError(response);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
$(document).on('blur', '.sort', function () {
|
||||
const hashid = $(this).attr("data-id");
|
||||
const name = $(this).attr("data-name");
|
||||
const val = $(this).val();
|
||||
|
||||
$.confirm({
|
||||
title: '谨慎操作',
|
||||
content: '确认要将 ' + name + ' 的排序设置为 <strong style="color: red">' + val + '</strong> 吗?',
|
||||
icon: 'mdi mdi-alert',
|
||||
animation: 'scale',
|
||||
closeAnimation: 'zoom',
|
||||
buttons: {
|
||||
okay: {
|
||||
text: '确认',
|
||||
keys: ['enter'],
|
||||
btnClass: 'btn-orange',
|
||||
action: function () {
|
||||
AjaxForm(
|
||||
"PATCH",
|
||||
"/api/menu/sort",
|
||||
{id: hashid, sort: val},
|
||||
function () {
|
||||
},
|
||||
function () {
|
||||
$.alert({
|
||||
title: '操作成功',
|
||||
icon: 'mdi mdi-check-decagram',
|
||||
type: 'green',
|
||||
content: '菜单:' + name + '排序成功。',
|
||||
buttons: {
|
||||
okay: {
|
||||
text: '关闭',
|
||||
action: function () {
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
function (response) {
|
||||
AjaxError(response);
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
cancel: {
|
||||
text: '取消',
|
||||
keys: ['ctrl', 'shift'],
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$(document).on('click', '.customSwitch', function () {
|
||||
let state = $(this).attr("state");
|
||||
const hashid = $(this).attr("hashid");
|
||||
|
||||
const is_used = (state === '1') ? -1 : 1;
|
||||
const is_used_msg = (state === '1') ? "禁用" : "启用";
|
||||
|
||||
$.confirm({
|
||||
title: '谨慎操作',
|
||||
content: '确认要 <strong style="color: red">' + is_used_msg + '</strong> 吗?',
|
||||
icon: 'mdi mdi-alert',
|
||||
animation: 'scale',
|
||||
closeAnimation: 'zoom',
|
||||
buttons: {
|
||||
okay: {
|
||||
text: '确认',
|
||||
keys: ['enter'],
|
||||
btnClass: 'btn-orange',
|
||||
action: function () {
|
||||
AjaxForm(
|
||||
"PATCH",
|
||||
"/api/menu/used",
|
||||
{id: hashid, used: is_used},
|
||||
function () {
|
||||
},
|
||||
function () {
|
||||
$.alert({
|
||||
title: '操作成功',
|
||||
icon: 'mdi mdi-check-decagram',
|
||||
type: 'green',
|
||||
content: '已' + is_used_msg + '成功。',
|
||||
buttons: {
|
||||
okay: {
|
||||
text: '关闭',
|
||||
action: function () {
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
function (response) {
|
||||
AjaxError(response);
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
cancel: {
|
||||
text: '取消',
|
||||
keys: ['ctrl', 'shift'],
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$('#btnOk').on('click', function () {
|
||||
const level = $("#level").val();
|
||||
const name = $("#name").val();
|
||||
const icon = $("#icon").val();
|
||||
const link = $("#link").val();
|
||||
const id = $("#id").val();
|
||||
|
||||
if (level === "") {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请选择上级目录。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
if (name === "") {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请输入菜单名称。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
const postData = {
|
||||
level: level,
|
||||
name: name,
|
||||
icon: icon,
|
||||
link: link,
|
||||
id: id,
|
||||
};
|
||||
|
||||
AjaxForm(
|
||||
"POST",
|
||||
"/api/menu",
|
||||
postData,
|
||||
function () {
|
||||
$(this).hide();
|
||||
$("#btnLoading").show();
|
||||
},
|
||||
function () {
|
||||
$("#btnLoading").hide();
|
||||
$("#btnOk").show();
|
||||
|
||||
$.alert({
|
||||
title: '操作成功',
|
||||
icon: 'mdi mdi-check-decagram',
|
||||
type: 'green',
|
||||
content: '菜单:' + name + ' 创建完成。',
|
||||
buttons: {
|
||||
okay: {
|
||||
text: '关闭',
|
||||
action: function () {
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
function (response) {
|
||||
$("#btnLoading").hide();
|
||||
$("#btnOk").show();
|
||||
|
||||
AjaxError(response);
|
||||
}
|
||||
);
|
||||
});
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,185 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
|
||||
<link href="../../bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container-fluid p-t-15">
|
||||
<div class="row">
|
||||
<div class="col-lg-6">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-title">查询缓存</div>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" data-toggle="tab" href="#redis" aria-selected="true">Redis</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane fade active show" id="redis">
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">KEY</span>
|
||||
</div>
|
||||
<input type="text" class="form-control" id="redis_key" placeholder="请输入 Redis Key">
|
||||
</div>
|
||||
|
||||
<button type="button" id="btnSearch" class="btn btn-primary">查询</button>
|
||||
<button type="button" id="btnSearchLoading" class="btn btn-primary" disabled
|
||||
style="display: none">
|
||||
<span class="spinner-grow spinner-grow-sm" role="status" aria-hidden="true"></span>
|
||||
查询中...
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-6">
|
||||
<div class="card">
|
||||
<header class="card-header">
|
||||
<div class="card-title">查询结果</div>
|
||||
</header>
|
||||
<div class="card-body">
|
||||
<pre id="resultDiv" style="white-space: pre-wrap;word-wrap: break-word;"></pre>
|
||||
<p><code id="ttl" style="display: none;"></code></p>
|
||||
<button class="btn btn-label btn-warning btn-clear-cache" style="display: none;"><label><i
|
||||
class="mdi mdi-delete-empty"></i></label> 清空数据
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/popper.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
|
||||
$('#btnSearch').on('click', function () {
|
||||
const redis_key = $("#redis_key").val();
|
||||
if (redis_key === "") {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请输入 Redis Key。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
AjaxForm(
|
||||
"POST",
|
||||
"/api/tool/cache/search",
|
||||
{redis_key: redis_key},
|
||||
function () {
|
||||
$("#resultDiv").text("");
|
||||
$("#ttl").hide();
|
||||
|
||||
$(".btn-clear-cache").hide();
|
||||
|
||||
$(this).hide();
|
||||
$("#btnSearchLoading").show();
|
||||
},
|
||||
function (data) {
|
||||
$("#btnSearchLoading").hide();
|
||||
$("#btnSearch").show();
|
||||
|
||||
$("#resultDiv").text(data.val);
|
||||
|
||||
$("#ttl").show();
|
||||
$("#ttl").text("剩余过期时间:" + data.ttl);
|
||||
|
||||
$(".btn-clear-cache").show();
|
||||
},
|
||||
function (response) {
|
||||
$("#btnSearchLoading").hide();
|
||||
$("#btnSearch").show();
|
||||
|
||||
AjaxError(response);
|
||||
}
|
||||
);
|
||||
});
|
||||
|
||||
$(document).on('click', '.btn-clear-cache', function () {
|
||||
const redis_key = $("#redis_key").val();
|
||||
|
||||
if (redis_key === "") {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请输入 Redis Key。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
const patchData = {
|
||||
redis_key: redis_key,
|
||||
};
|
||||
|
||||
$.confirm({
|
||||
title: '谨慎操作',
|
||||
content: '确认要清空 REDIS KEY: <strong style="color: red">' + redis_key + '</strong> 的数据吗?',
|
||||
icon: 'mdi mdi-alert',
|
||||
animation: 'scale',
|
||||
closeAnimation: 'zoom',
|
||||
buttons: {
|
||||
okay: {
|
||||
text: '确认',
|
||||
keys: ['enter'],
|
||||
btnClass: 'btn-orange',
|
||||
action: function () {
|
||||
AjaxForm(
|
||||
"PATCH",
|
||||
"/api/tool/cache/clear",
|
||||
patchData,
|
||||
function () {
|
||||
},
|
||||
function () {
|
||||
$.alert({
|
||||
title: '操作成功',
|
||||
icon: 'mdi mdi-check-decagram',
|
||||
type: 'green',
|
||||
content: 'REDIS KEY:' + redis_key + ' 数据已清空。',
|
||||
buttons: {
|
||||
okay: {
|
||||
text: '关闭',
|
||||
action: function () {
|
||||
location.reload();
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
function (response) {
|
||||
AjaxError(response);
|
||||
}
|
||||
);
|
||||
}
|
||||
},
|
||||
cancel: {
|
||||
text: '取消',
|
||||
keys: ['ctrl', 'shift'],
|
||||
}
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,292 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1, user-scalable=no"/>
|
||||
<link href="../../bootstrap/js/jquery-confirm/jquery-confirm.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/js/bootstrap-select/bootstrap-select.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/bootstrap.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/materialdesignicons.min.css" rel="stylesheet">
|
||||
<link href="../../bootstrap/css/style.min.css" rel="stylesheet">
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<div class="container-fluid p-t-15">
|
||||
<div class="row">
|
||||
<div class="col-lg-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<div class="card-title">查询数据</div>
|
||||
<ul class="card-actions">
|
||||
<li><a href="#!" class="card-btn-slide"><i class="mdi mdi-chevron-up"></i></a></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<ul class="nav nav-tabs">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" data-toggle="tab" href="#mysql" aria-selected="true">MySQL</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<div class="tab-content">
|
||||
<div class="tab-pane fade active show" id="mysql">
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">数据库</span>
|
||||
</div>
|
||||
<select class="form-control select-picker col-lg-2" data-width="auto"
|
||||
data-live-search="true" id="db">
|
||||
</select>
|
||||
|
||||
<div style="width: 50px;"></div>
|
||||
|
||||
<div class="input-group-prepend">
|
||||
<span class="input-group-text">数据表</span>
|
||||
</div>
|
||||
<select class="form-control select-picker col-lg-2" data-width="auto"
|
||||
data-live-search="true" id="table">
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<textarea rows="3" class="form-control" aria-label="With textarea" id="sql"></textarea>
|
||||
</div>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<button class="btn btn-sm btn-round btn-secondary btn-select">SELECT *</button>
|
||||
<div style="width: 20px;"></div>
|
||||
<button class="btn btn-sm btn-round btn-secondary btn-show-create">SHOW CREATE TABLE </button>
|
||||
<div style="width: 20px;"></div>
|
||||
<button class="btn btn-sm btn-round btn-secondary btn-clear">清除</button>
|
||||
<div style="width: 20px;"></div>
|
||||
<button class="btn btn-sm btn-round btn-secondary btn-format">格式</button>
|
||||
</div>
|
||||
|
||||
<div class="input-group mb-3">
|
||||
<code>仅支持查询语句,最多支持查询 100 条数据。</code>
|
||||
</div>
|
||||
|
||||
<div style="float: right">
|
||||
<button type="button" id="btnSearch" class="btn btn-primary">查询</button>
|
||||
<button type="button" id="btnExplain" class="btn btn-info">分析</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="col-lg-12">
|
||||
<div class="card">
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-hover">
|
||||
<thead>
|
||||
<tr id="thead-tr">
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="tbody">
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/vkbeautify.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/main.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/popper.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/jquery-confirm/jquery-confirm.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/httpclient/httpclient.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap-select/bootstrap-select.min.js"></script>
|
||||
<script type="text/javascript" src="../../bootstrap/js/bootstrap-select/i18n/defaults-zh_CN.min.js"></script>
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function () {
|
||||
$('.select-picker').selectpicker();
|
||||
|
||||
AjaxForm(
|
||||
"GET",
|
||||
"/api/tool/data/dbs",
|
||||
"",
|
||||
function () {
|
||||
},
|
||||
function (data) {
|
||||
$.each(data.list, function (index, value) {
|
||||
$("#db").append("<option value='" + value.db_name + "'>" + value.db_name + "</option>");
|
||||
});
|
||||
|
||||
$("#db option:eq(0)").attr('selected', 'selected');//选中第一个
|
||||
$("#db").selectpicker('refresh');
|
||||
|
||||
getTables($('#db option:selected').val());
|
||||
},
|
||||
function (response) {
|
||||
AjaxError(response);
|
||||
}
|
||||
);
|
||||
|
||||
$('.btn-select').on('click', function () {
|
||||
const table = $('#table option:selected').val();
|
||||
$("#sql").val("SELECT * FROM `" + table + "` ORDER BY 1 DESC");
|
||||
});
|
||||
|
||||
$('.btn-show-create').on('click', function () {
|
||||
const table = $('#table option:selected').val();
|
||||
$("#sql").val("SHOW CREATE TABLE `" + table + "` ");
|
||||
});
|
||||
|
||||
$('.btn-format').on('click', function () {
|
||||
const val = $("#sql").val();
|
||||
if (val !== "") {
|
||||
$("#sql").val(vkbeautify.sql(val));
|
||||
}
|
||||
});
|
||||
|
||||
$('.btn-clear').on('click', function () {
|
||||
$("#sql").val('');
|
||||
});
|
||||
|
||||
$("#db").on('change', function () {
|
||||
getTables($(this).val());
|
||||
});
|
||||
|
||||
$('#btnSearch').on('click', function () {
|
||||
const db = $('#db option:selected').val();
|
||||
if (db === "") {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请选择数据库。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
const sql = $('#sql').val();
|
||||
if (sql === "") {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请先填写 SQL 语句。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
const table = $('#table option:selected').val();
|
||||
if (table === "") {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请选择数据表。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
searchMySQL(db, table, sql);
|
||||
});
|
||||
|
||||
$('#btnExplain').on('click', function () {
|
||||
const db = $('#db option:selected').val();
|
||||
if (db === "") {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请选择数据库。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
const table = $('#table option:selected').val();
|
||||
if (table === "") {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请选择数据表。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
const sql = $('#sql').val();
|
||||
if (sql === "") {
|
||||
$.alert({
|
||||
title: '温馨提示',
|
||||
icon: 'mdi mdi-alert',
|
||||
type: 'orange',
|
||||
content: '请先填写 SQL 语句。',
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
searchMySQL(db, table, "explain " + sql);
|
||||
});
|
||||
|
||||
function searchMySQL(db_name, table_name, sql) {
|
||||
AjaxForm(
|
||||
"POST",
|
||||
"/api/tool/data/mysql",
|
||||
{db_name: db_name, table_name: table_name, sql: sql},
|
||||
function () {
|
||||
},
|
||||
function (data) {
|
||||
$("#thead-tr").html("");
|
||||
$.each(data.cols, function (index, value) {
|
||||
let thHtml = "<th>" + value;
|
||||
$.each(data.cols_info, function (info_index, info_value) {
|
||||
if (info_value.column_name === value) {
|
||||
thHtml += "<br> <small> " + info_value.column_comment + " </small>";
|
||||
}
|
||||
});
|
||||
thHtml += "</th>";
|
||||
$("#thead-tr").append(thHtml);
|
||||
});
|
||||
|
||||
$("#tbody").html("");
|
||||
$.each(data.list, function (listIndex, listValue) {
|
||||
$("#tbody").append("<tr>");
|
||||
$.each(data.cols, function (index, value) {
|
||||
$("#tbody").append("<td><pre>" + listValue[value] + "</pre></td>");
|
||||
});
|
||||
$("#tbody").append("</tr>");
|
||||
});
|
||||
|
||||
$(".mdi-chevron-up").click();
|
||||
},
|
||||
function (response) {
|
||||
AjaxError(response);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
function getTables(db_name) {
|
||||
AjaxForm(
|
||||
"POST",
|
||||
"/api/tool/data/tables",
|
||||
{db_name: db_name},
|
||||
function () {
|
||||
},
|
||||
function (data) {
|
||||
$.each(data.list, function (index, value) {
|
||||
$("#table").append("<option value='" + value.table_name + "' data-subtext='" + value.table_comment + "'>" + value.table_name + "</option>");
|
||||
});
|
||||
|
||||
$("#table option:eq(0)").attr('selected', 'selected');//选中第一个
|
||||
$("#table").selectpicker('refresh');
|
||||
},
|
||||
function (response) {
|
||||
AjaxError(response);
|
||||
}
|
||||
);
|
||||
}
|
||||
})
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user