同步升级

* 增加又拍云存储支持
* 增加ckeditor5支持
* 其他部分优化
This commit is contained in:
Anyon 2022-05-24 10:59:43 +08:00
parent 0e7beb247c
commit d33753cf39
281 changed files with 2025 additions and 598 deletions

View File

@ -73,7 +73,7 @@ class Oplog extends Controller
{
try {
SystemOplog::mQuery()->empty();
sysoplog('系统运维管理', '成功清理所有日志数据');
sysoplog('系统运维管理', '成功清理所有日志');
$this->success('日志清理成功!');
} catch (HttpResponseException $exception) {
throw $exception;

View File

@ -39,7 +39,7 @@ class Runtime extends Controller
if (AdminService::instance()->isSuper()) try {
AdminService::instance()->clearCache();
SystemService::instance()->pushRuntime();
sysoplog('系统运维管理', '刷新创建路由缓存');
sysoplog('系统运维管理', '刷新创建路由缓存');
$this->success('网站缓存加速成功!', 'javascript:location.reload()');
} catch (HttpResponseException $exception) {
throw $exception;
@ -59,8 +59,8 @@ class Runtime extends Controller
if (AdminService::instance()->isSuper()) try {
AdminService::instance()->clearCache();
SystemService::instance()->clearRuntime();
sysoplog('系统运维管理', '清理网站日志缓存数据');
$this->success('清空缓存日志成功!', 'javascript:location.reload()');
sysoplog('系统运维管理', '清理网站日志缓存');
$this->success('清空日志缓存成功!', 'javascript:location.reload()');
} catch (HttpResponseException $exception) {
throw $exception;
} catch (\Exception $exception) {
@ -89,6 +89,25 @@ class Runtime extends Controller
}
}
/**
* 修改富文本编辑器
* @return void
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function editor()
{
if (AdminService::instance()->isSuper()) {
$editor = input('editor', 'auto');
sysconf('base.editor', $editor);
sysoplog('系统运维管理', "切换编辑器为{$editor}");
$this->success('已切换后台编辑器!', 'javascript:location.reload()');
} else {
$this->error('只有超级管理员才能操作!');
}
}
/**
* 清理系统配置
* @login true

View File

@ -22,6 +22,7 @@ use think\admin\storage\AliossStorage;
use think\admin\storage\LocalStorage;
use think\admin\storage\QiniuStorage;
use think\admin\storage\TxcosStorage;
use think\admin\storage\UpyunStorage;
use think\exception\HttpResponseException;
use think\file\UploadedFile;
use think\Response;
@ -92,6 +93,12 @@ class Upload extends Controller
$data['q-signature'] = $token['q-signature'];
$data['q-sign-algorithm'] = $token['q-sign-algorithm'];
$data['server'] = TxcosStorage::instance()->upload();
} elseif ('upyun' === $data['uptype']) {
$token = UpyunStorage::instance()->buildUploadToken($data['key'], 3600, $name, input('size'), input('hash'));
$data['url'] = $token['siteurl'];
$data['policy'] = $token['policy'];
$data['authorization'] = $token['authorization'];
$data['server'] = UpyunStorage::instance()->upload();
}
$this->success('获取上传授权参数', $data, 404);
}
@ -176,7 +183,7 @@ class Upload extends Controller
private function getType(): string
{
$type = strtolower(input('uptype', ''));
if (in_array($type, ['local', 'qiniu', 'alioss', 'txcos'])) {
if (in_array($type, ['local', 'qiniu', 'alioss', 'txcos', 'uptype'])) {
return $type;
} else {
return strtolower(sysconf('storage.type'));

View File

@ -1,151 +1,310 @@
define(['md5'], function (SparkMD5, allowMime) {
define(['md5', 'notify'], function (SparkMD5, Notify, allowMime) {
allowMime = JSON.parse('{$exts|raw}');
return function (element, callable) {
/*! 初始化变量 */
var opt = {elem: $(element), exts: [], mimes: [], files: {}, cache: {}, load: 0, count: {total: 0, uploaded: 0}};
opt.size = opt.elem.data('size') || 0, opt.mult = opt.elem.data('multiple') > 0;
opt.safe = opt.elem.data('safe') ? 1 : 0, opt.hide = opt.elem.data('hide-load') ? 1 : 0;
opt.type = opt.safe ? 'local' : opt.elem.attr('data-uptype') || '';
function UploadAdapter(elem, done) {
return new (function (elem, done, that) {
/*! 查找表单元素, 如果没有找到将不会自动写值 */
if (!opt.elem.data('input') && opt.elem.data('field')) {
var $input = $('input[name="' + opt.elem.data('field') + '"]:not([type=file])');
opt.elem.data('input', $input.size() > 0 ? $input.get(0) : null);
}
/*! 初始化变量 */
that = this;
this.option = {elem: $(elem), exts: [], mimes: []};
this.option.size = this.option.elem.data('size') || 0;
this.option.safe = this.option.elem.data('safe') ? 1 : 0;
this.option.hide = this.option.elem.data('hload') ? 1 : 0;
this.option.mult = this.option.elem.data('multiple') > 0;
this.option.type = this.option.safe ? 'local' : this.option.elem.attr('data-uptype') || '';
/*! 文件选择筛选,使用 MIME 规则过滤文件列表 */
$((opt.elem.data('type') || '').split(',')).map(function (i, e) {
if (allowMime[e]) opt.exts.push(e), opt.mimes.push(allowMime[e]);
});
/*! 查找表单元素, 如果没有找到将不会自动写值 */
if (!this.option.elem.data('input') && this.option.elem.data('field')) {
this.$input = $('input[name="' + this.option.elem.data('field') + '"]:not([type=file])');
this.option.elem.data('input', this.$input.size() > 0 ? this.$input.get(0) : null);
}
/*! 初始化上传组件 */
opt.uploader = layui.upload.render({
url: '{:sysuri("admin/api.upload/file")}', auto: false, elem: element, accept: 'file', multiple: opt.mult, exts: opt.exts.join('|'), acceptMime: opt.mimes.join(','), choose: function (object) {
opt.elem.triggerHandler('upload.choose', opt.files = object.pushFile());
opt.uploader.config.elem.next().val(''), layui.each(opt.files, function (index, file) {
if (opt.size > 0 && file.size > opt.size) return delete opt.files[index], $.msg.tips('文件大小超出限制!');
opt.load = opt.hide || $.msg.loading('上传进度 <span data-upload-progress>0%</span>');
opt.count.total++, file.index = index, opt.cache[index] = file, delete opt.files[index];
md5file(file).then(function (file) {
opt.elem.triggerHandler('upload.hash', file), jQuery.ajax("{:sysuri('admin/api.upload/state')}", {
data: {key: file.xkey, uptype: opt.type, safe: opt.safe, name: file.name}, method: 'post', success: function (ret) {
file.xurl = ret.data.url, file.xsafe = ret.data.safe;
file.xpath = ret.data.key, file.xtype = ret.data.uptype;
if (parseInt(ret.code) === 404) {
opt.uploader.config.url = ret.data.server;
opt.uploader.config.data.key = ret.data.key;
opt.uploader.config.data.safe = ret.data.safe;
opt.uploader.config.data.uptype = ret.data.uptype;
if (ret.data.uptype === 'qiniu') {
opt.uploader.config.data.token = ret.data.token;
} else if (ret.data.uptype === 'alioss') {
opt.uploader.config.data['policy'] = ret.data.policy;
opt.uploader.config.data['signature'] = ret.data.signature;
opt.uploader.config.data['OSSAccessKeyId'] = ret.data.OSSAccessKeyId;
opt.uploader.config.data['success_action_status'] = 200;
opt.uploader.config.data['Content-Disposition'] = 'inline;filename=' + encodeURIComponent(file.name);
} else if (ret.data.uptype === 'txcos') {
opt.uploader.config.data['q-ak'] = ret.data['q-ak'];
opt.uploader.config.data['policy'] = ret.data['policy'];
opt.uploader.config.data['q-key-time'] = ret.data['q-key-time'];
opt.uploader.config.data['q-signature'] = ret.data['q-signature'];
opt.uploader.config.data['q-sign-algorithm'] = ret.data['q-sign-algorithm'];
opt.uploader.config.data['success_action_status'] = 200;
opt.uploader.config.data['Content-Disposition'] = 'inline;filename=' + encodeURIComponent(file.name);
}
object.upload(file.index, file);
} else if (parseInt(ret.code) === 200) {
file.xurl = ret.data.url;
opt.uploader.config.done({code: 1, url: file.xurl, info: '文件秒传成功!'}, file.index);
} else {
$.msg.tips(ret.info || ret.error.message || '文件上传出错!');
}
}
});
/*! 文件选择筛选,使用 MIME 规则过滤文件列表 */
$((this.option.elem.data('type') || '').split(',')).map(function (i, e) {
if (allowMime[e]) that.option.exts.push(e), that.option.mimes.push(allowMime[e]);
});
/*! 初始化上传组件 */
this.adapter = new Adapter(this.option, layui.upload.render({
url: '{:url("admin/api.upload/file")}', auto: false, elem: elem, accept: 'file', multiple: this.option.mult, exts: this.option.exts.join('|'), acceptMime: this.option.mimes.join(','), choose: function (object) {
object.files = object.pushFile();
that.adapter.event('upload.choose', object.files);
that.adapter.upload(object.files, done), layui.each(object.files, function (index) {
delete object.files[index];
});
});
}, progress: function (number) {
/*! 文件上传进度处理 */
opt.elem.triggerHandler('upload.progress', {number: number, event: arguments[2], file: arguments[3]});
if (opt.count.total > 1) {
$('[data-upload-progress]').html(number + '%' + ' ' + (opt.count.uploaded + 1) + '/' + opt.count.total);
} else {
$('[data-upload-progress]').html(number + '%');
}
}, done: function (ret, idx) {
// 兼容部分环境不解析 JSON 数据
if (typeof ret === 'string' && ret.length > 0) try {
ret = JSON.parse(ret) || ret;
} catch (e) {
console.log(e)
}
/*! 检查单个文件上传返回的结果 */
if (ret.code < 1) return $.msg.tips(ret.info || '文件上传失败!');
if (typeof opt.cache[idx].xurl !== 'string') return $.msg.tips('无效的文件上传对象!');
/*! 单个文件上传成功结果处理 */
if (typeof callable === 'function') {
callable.call(opt.elem, opt.cache[idx].xurl, opt.cache['id']);
} else if (opt.mult < 1 && opt.elem.data('input')) {
$(opt.elem.data('input')).val(opt.cache[idx].xurl).trigger('change', opt.cache[idx]);
}
opt.elem.html(opt.elem.data('html')).triggerHandler('upload.done', {file: opt.cache[idx], data: ret});
/*! 所有文件上传完成后结果处理 */
if (++opt.count.uploaded >= opt.count.total) {
opt.hide || $.msg.close(opt.load);
if (opt.mult > 0 && opt.elem.data('input')) {
var urls = opt.elem.data('input').value || [];
if (typeof urls === 'string') urls = urls.split('|');
for (var i in opt.cache) urls.push(opt.cache[i].xurl);
$(opt.elem.data('input')).val(urls.join('|')).trigger('change', opt.cache);
}));
})(elem, done)
}
// 创建对象
UploadAdapter.adapter = window.AdminUploadAdapter = Adapter;
// 上传文件
function Adapter(option, uploader) {
this.uploader = uploader, this.config = function (option) {
return (this.option = Object.assign({}, this.option || {}, option || {})), this;
}, this.init = function (option) {
this.uploader && this.uploader.config.elem.next().val('');
this.files = {}, this.loader = 0, this.count = {total: 0, error: 0, success: 0};
return this.config(option).config({safe: this.option.safe || 0, type: this.option.type || ''});
}, this.init(option);
}
// 文件推送
Adapter.prototype.upload = function (files, done) {
var that = this.init();
layui.each(files, function (index, file) {
that.count.total++, file.index = index, that.files[index] = file;
if (that.option.size && file.size > that.option.size) {
that.count.error++, file.xstate = -1, file.xstats = '大小超限';
return $.msg.tips('文件大小超出限制!');
}
if (!that.option.hide) {
file.notify = new NotifyExtend(file);
}
}), layui.each(files, function (index, file) {
that.hash(file).then(function (file) {
that.event('upload.hash', file).request(file, done);
});
});
};
// 文件上传
Adapter.prototype.request = function (file, done) {
var that = this, data = {key: file.xkey, safe: that.option.safe, uptype: that.option.type};
data.size = file.size, data.name = file.name, data.hash = file.xmd5;
jQuery.ajax("{:url('admin/api.upload/state')}", {
data: data, method: 'post', success: function (ret) {
file.xurl = ret.data.url, file.xsafe = ret.data.safe, file.xpath = ret.data.key, file.xtype = ret.data.uptype;
if (parseInt(ret.code) === 404) {
var uploader = {};
uploader.url = ret.data.server;
uploader.form = new FormData();
uploader.form.append('key', ret.data.key);
uploader.form.append('safe', ret.data.safe);
uploader.form.append('uptype', ret.data.uptype);
if (ret.data.uptype === 'qiniu') {
uploader.form.append('token', ret.data.token);
} else if (ret.data.uptype === 'alioss') {
uploader.form.append('policy', ret.data['policy']);
uploader.form.append('signature', ret.data['signature']);
uploader.form.append('OSSAccessKeyId', ret.data['OSSAccessKeyId']);
uploader.form.append('success_action_status', '200');
uploader.form.append('Content-Disposition', 'inline;filename=' + encodeURIComponent(file.name));
} else if (ret.data.uptype === 'txcos') {
uploader.form.append('q-ak', ret.data['q-ak']);
uploader.form.append('policy', ret.data['policy']);
uploader.form.append('q-key-time', ret.data['q-key-time']);
uploader.form.append('q-signature', ret.data['q-signature']);
uploader.form.append('q-sign-algorithm', ret.data['q-sign-algorithm']);
uploader.form.append('success_action_status', '200');
uploader.form.append('Content-Disposition', 'inline;filename=' + encodeURIComponent(file.name));
} else if (ret.data.uptype === 'upyun') {
uploader.form.delete('key');
uploader.form.delete('safe');
uploader.form.delete('uptype');
uploader.form.append('save-key', ret.data['key']);
uploader.form.append('policy', ret.data['policy']);
uploader.form.append('authorization', ret.data['authorization']);
uploader.form.append('Content-Disposition', 'inline;filename=' + encodeURIComponent(file.name));
}
opt.elem.triggerHandler('upload.complete', {file: opt.cache});
(opt.cache = [], opt.files = [], opt.count = {uploaded: 0, total: 0}), opt.uploader.reload();
uploader.form.append('file', file), jQuery.ajax({
url: uploader.url, data: uploader.form, type: 'post', xhr: function (xhr) {
xhr = new XMLHttpRequest();
return xhr.upload.addEventListener('progress', function (event) {
file.xtotal = event.total, file.xloaded = event.loaded || 0;
that.progress((file.xloaded / file.xtotal * 100).toFixed(2), file)
}), xhr;
}, contentType: false, error: function () {
that.event('upload.error', {file: file}, file, '接口异常');
}, processData: false, success: function (ret) {
// 兼容数据格式
if (typeof ret === 'string' && ret.length > 0) try {
ret = JSON.parse(ret) || ret;
} catch (e) {
console.log(e)
}
if (typeof ret !== 'object') {
ret = {code: 1, url: file.xurl, info: '上传成功'};
}
/*! 检查单个文件上传返回的结果 */
if (typeof ret === 'object' && ret.code < 1) {
that.event('upload.error', {file: file}, file, ret.info || '上传失败');
} else {
that.done(ret, file.index, file, done, '上传成功');
}
}
});
} else if (parseInt(ret.code) === 200) {
(file.xurl = ret.data.url), that.progress('100.00', file);
that.done({code: 1, url: file.xurl, info: file.xstats}, file.index, file, done, '秒传成功');
} else {
that.event('upload.error', {file: file}, file, ret.info || ret.error.message || '上传出错!');
}
}
});
};
function md5file(file) {
var deferred = jQuery.Deferred();
// 上传进度
Adapter.prototype.progress = function (number, file) {
this.event('upload.progress', {number: number, file: file});
if (file.notify) file.notify.setProgress(number);
};
// 上传结果
Adapter.prototype.done = function (ret, idx, file, done, message) {
/*! 检查单个文件上传返回的结果 */
if (ret.code < 1) return $.msg.tips(ret.info || '文件上传失败!');
if (typeof file.xurl !== 'string') return $.msg.tips('无效的文件上传对象!');
/*! 单个文件上传成功结果处理 */
if (typeof done === 'function') {
done.call(this.option.elem, file.xurl, this.files['id']);
} else if (this.option.mult < 1 && this.option.elem.data('input')) {
$(this.option.elem.data('input')).val(file.xurl).trigger('change', file);
}
// 文件上传成功事件
this.event('upload.done', {file: file, data: ret}, file, message);
/*! 所有文件上传完成后结果处理 */
if (this.count.success + this.count.error >= this.count.total) {
this.option.hide || $.msg.close(this.loader);
if (this.option.mult > 0 && this.option.elem.data('input')) {
var urls = this.option.elem.data('input').value || [];
if (typeof urls === 'string') urls = urls.split('|');
for (var i in this.files) urls.push(this.files[i].xurl);
$(this.option.elem.data('input')).val(urls.join('|')).trigger('change', files);
}
this.event('upload.complete', {file: this.files}, file).init().uploader && this.uploader.reload();
}
};
/*! 触发事件过程 */
Adapter.prototype.event = function (name, data, file, message) {
if (name === 'upload.error') {
this.count.error++, file.xstate = -1, file.xstats = message;
if (file.notify) file.notify.setError(message || file.xstats || '');
} else if (name === 'upload.done') {
this.count.success++, file.xstate = 1, file.xstats = message;
if (file.notify) file.notify.setSuccess(message || file.xstats || '')
}
if (this.option.elem) {
this.option.elem.triggerHandler(name, data);
}
return this;
};
/*! 计算文件 HASH 值 */
Adapter.prototype.hash = function (file) {
var defer = jQuery.Deferred();
file.xext = file.name.indexOf('.') > -1 ? file.name.split('.').pop() : 'tmp';
/*! 兼容不能计算文件 HASH 的情况 */
var IsDate = '{$nameType|default=""}'.indexOf('date') > -1;
if (!window.FileReader || IsDate) return jQuery.when((function (xmd5, chars) {
while (xmd5.length < 32) xmd5 += chars.charAt(Math.floor(Math.random() * chars.length));
return setFileXdata(file, xmd5, 6), deferred.resolve(file, file.xmd5, file.xkey), deferred;
return SetFileXdata(file, xmd5, 6), defer.promise();
})(layui.util.toDateString(Date.now(), 'yyyyMMddHHmmss-'), '0123456789'));
/*! 读取文件并计算 HASH 值 */
var spark = new SparkMD5.ArrayBuffer();
var slice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
file.chunkIdx = 0, file.chunkSize = 2097152, file.chunkTotal = Math.ceil(this.size / this.chunkSize);
return jQuery.when(loadNextChunk(file));
return new LoadNextChunk(file).ReadAsChunk();
function setFileXdata(file, xmd5, slice) {
file.xmd5 = xmd5, file.xkey = file.xmd5.substr(0, slice || 2) + '/' + file.xmd5.substr(slice || 2, 30) + '.' + file.xext;
return delete file.chunkIdx, delete file.chunkSize, delete file.chunkTotal, file;
function SetFileXdata(file, xmd5, slice) {
file.xmd5 = xmd5, file.xstate = 0, file.xstats = '';
file.xkey = file.xmd5.substring(0, slice || 2) + '/' + file.xmd5.substring(slice || 2) + '.' + file.xext;
return defer.resolve(file, file.xmd5, file.xkey), file;
}
function loadNextChunk(file) {
this.reader = new FileReader();
this.reader.onload = function (event) {
function LoadNextChunk(file) {
var that = this, reader = new FileReader(), spark = new SparkMD5.ArrayBuffer();
var slice = File.prototype.slice || File.prototype.mozSlice || File.prototype.webkitSlice;
this.chunkIdx = 0, this.chunkSize = 2097152, this.chunkTotal = Math.ceil(file.size / this.chunkSize);
reader.onload = function (event) {
spark.append(event.target.result);
if (++file.chunkIdx < file.chunkTotal) {
loadNextChunk(file);
} else {
setFileXdata(file, spark.end());
deferred.resolve(file, file.xmd5, file.xkey);
}
++that.chunkIdx < that.chunkTotal ? that.ReadAsChunk() : SetFileXdata(file, spark.end());
}, reader.onerror = function () {
defer.reject();
}, this.ReadAsChunk = function () {
this.start = that.chunkIdx * that.chunkSize;
this.loaded = this.start + that.chunkSize >= file.size ? file.size : this.start + that.chunkSize;
reader.readAsArrayBuffer(slice.call(file, this.start, this.loaded));
defer.notify(file, (this.loaded / file.size * 100).toFixed(2));
return defer.promise();
};
this.reader.onerror = function () {
deferred.reject();
};
this.start = file.chunkIdx * file.chunkSize;
this.loaded = (this.start + file.chunkSize >= file.size) ? file.size : this.start + file.chunkSize;
this.reader.readAsArrayBuffer(slice.call(file, this.start, this.loaded));
return deferred.notify(file, (this.loaded / file.size * 100).toFixed(2)), deferred;
}
};
return UploadAdapter;
/*! Base64 内容转 File 对象 */
function Base64ToFile(base64, filename) {
var arr = base64.split(',');
var mime = arr[0].match(/:(.*?);/)[1], suffix = mime.split('/')[1];
var bstr = atob(arr[1]), n = bstr.length, u8arr = new Uint8Array(n);
while (n--) u8arr[n] = bstr.charCodeAt(n);
return new File([u8arr], filename + '.' + suffix, {type: mime});
}
/*! File 对象转 Base64 内容 */
function FileToBase64(file) {
var defer = jQuery.Deferred(), reader = new FileReader();
reader.onload = function () {
defer.resolve(this.result);
};
return reader.readAsDataURL(file), defer.promise();
}
/*! 图片压缩处理 */
function ImageToThumb(url, quality) {
var deferred = jQuery.Deferred();
var maxWidth = 500, maxHeight = 400, image = new Image();
image.src = url, image.onload = function () {
var canvas = document.createElement('canvas');
var context = canvas.getContext('2d');
// 原始尺寸
var originWidth = this.width, originHeight = this.height;
// 目标尺寸
var targetWidth = originWidth, targetHeight = originHeight;
// 图片尺寸超过最大值的限制
if (originWidth > maxWidth || originHeight > maxHeight) {
if (originWidth / originHeight > maxWidth / maxHeight) {
targetWidth = maxWidth;
targetHeight = Math.round(maxWidth * (originHeight / originWidth));
} else {
targetHeight = maxHeight;
targetWidth = Math.round(maxHeight * (originWidth / originHeight));
}
}
canvas.width = targetWidth, canvas.height = targetHeight;
context.clearRect(0, 0, targetWidth, targetHeight);
context.drawImage(this, 0, 0, targetWidth, targetHeight);
deferred.resolve(canvas.toDataURL('image/jpeg', quality || 0.92));
};
}
/*! 上传状态提示扩展插件 */
function NotifyExtend(file) {
var that = this;
this.notify = Notify.notify({width: 260, title: file.name, showProgress: true, description: '上传进度 <span data-upload-progress>0%</span>', type: 'default', position: 'top-right', closeTimeout: 0});
this.$elem = $(this.notify.notification.nodes);
this.$elem.find('.growl-notification__progress').addClass('is-visible');
this.$elem.find('.growl-notification__progress-bar').addClass('transition');
this.setProgress = function (number) {
this.$elem.find('[data-upload-progress]').html(number + '%');
this.$elem.find('.growl-notification__progress-bar').css({width: number + '%'});
return this;
}, this.setError = function (message) {
this.$elem.find('.growl-notification__desc').html(message || '文件上传失败!');
this.$elem.removeClass('growl-notification--default').addClass('growl-notification--error')
return this.close();
}, this.setSuccess = function (message) {
this.setProgress('100.00');
this.$elem.find('.growl-notification__desc').html(message || '文件上传成功!');
this.$elem.removeClass('growl-notification--default').addClass('growl-notification--success');
return this.close();
}, this.close = function (timeout) {
return setTimeout(function () {
that.notify.close();
}, timeout || 2000), this;
};
}
});

View File

@ -32,15 +32,42 @@
</div>
</div>
</div>
<div class="layui-card padding-20 shadow">
<div class="layui-card-header notselect">
<b>富编辑器</b><span class="color-desc font-s12 padding-left-5">Rich Text Editor</span>
</div>
<div class="layui-card-body layui-clear">
<div class="layui-btn-group shadow-mini nowrap">
{if !in_array(sysconf('base.editor'),['ckeditor4','ckeditor5','auto'])}{php}sysconf('base.editor','ckeditor4');{/php}{/if}
{foreach ['ckeditor4'=>'CKEditor4','ckeditor5'=>'CKEditor5','auto'=>'自适应模式'] as $k => $v}{if sysconf('base.editor') eq $k}
{if auth('storage')}<a data-title="配置{$v}" class="layui-btn layui-btn-sm layui-btn-active">{$v}</a>{else}<a class="layui-btn layui-btn-sm layui-btn-active">{$v}</a>{/if}
{else}
{if auth('storage')}<a data-title="配置{$v}" data-action="{:url('admin/api.runtime/editor')}" data-value="editor#{$k}" class="layui-btn layui-btn-sm layui-btn-primary">{$v}</a>{else}<a class="layui-btn layui-btn-sm layui-btn-primary">{$v}</a>{/if}
{/if}{/foreach}
</div>
<div class="margin-top-20 nowrap full-width pull-left">
<p><b>CKEditor4</b>:旧版本编辑器,对浏览器兼容较好,但内容编辑体验稍有不足。</p>
<p><b>CKEditor5</b>:新版本编辑器,只支持新特性浏览器,对内容编辑体验较好,推荐使用。</p>
<p><b>自适应模式</b>:优先使用新版本编辑器,若浏览器不支持新版本时自动降级为旧版本编辑器。</p>
</div>
</div>
</div>
<!--{/notempty}-->
<div class="layui-card padding-20 shadow">
<div class="layui-card-header notselect">
<b>存储引擎</b><span class="color-desc font-s12 padding-left-5">Storage Engine</span>
</div>
<!-- 初始化存储配置 -->
{if !sysconf('storage.type')}{php}sysconf('storage.type','local');{/php}{/if}
{if !sysconf('storage.link_type')}{php}sysconf('storage.link_type','none');{/php}{/if}
{if !sysconf('storage.name_type')}{php}sysconf('storage.name_type','xmd5');{/php}{/if}
{if !sysconf('storage.allow_exts')}{php}sysconf('storage.allow_exts','doc,gif,ico,jpg,mp3,mp4,p12,pem,png,rar,xls,xlsx');{/php}{/if}
{if !sysconf('storage.local_http_protocol')}{php}sysconf('storage.local_http_protocol','http');{/php}{/if}
<div class="layui-card-body layui-clear">
<div class="layui-btn-group shadow-mini nowrap">
{foreach ['local' => '本地服务器存储','qiniu' => '七牛云对象存储','alioss' => '阿里云OSS存储','txcos' => '腾讯云COS存储'] as $k => $v} {if sysconf('storage.type') eq $k}
{foreach ['local'=>'本地服务器存储','qiniu'=>'七牛云对象存储','upyun'=>'又拍云USS存储','alioss'=>'阿里云OSS存储','txcos'=>'腾讯云COS存储'] as $k => $v}{if sysconf('storage.type') eq $k}
{if auth('storage')}<a data-title="配置{$v}" data-modal="{:url('storage')}?type={$k}" class="layui-btn layui-btn-sm layui-btn-active">{$v}</a>{else}<a class="layui-btn layui-btn-sm layui-btn-active">{$v}</a>{/if}
{else}
{if auth('storage')}<a data-title="配置{$v}" data-modal="{:url('storage')}?type={$k}" class="layui-btn layui-btn-sm layui-btn-primary">{$v}</a>{else}<a class="layui-btn layui-btn-sm layui-btn-primary">{$v}</a>{/if}
@ -49,6 +76,7 @@
<div class="margin-top-20 nowrap full-width pull-left">
<p><b>本地服务器存储</b>:文件直接上传到本地服务器的 `static/upload` 目录,不支持大文件上传,占用服务器磁盘空间,访问时消耗服务器带宽流量。</p>
<p><b>七牛云对象存储</b>:文件直接上传到七牛云存储空间,支持大文件上传,不占用服务器空间及服务器带宽流量,支持 CDN 加速访问,访问量大时推荐使用。</p>
<p><b>又拍云USS存储</b>:文件直接上传到又拍云 USS 存储空间,支持大文件上传,不占用服务器空间及服务器带宽流量,支持 CDN 加速访问,访问量大时推荐使用。</p>
<p><b>阿里云OSS存储</b>:文件直接上传到阿里云 OSS 存储空间,支持大文件上传,不占用服务器空间及服务器带宽流量,支持 CDN 加速访问,访问量大时推荐使用。</p>
<p><b>腾讯云COS存储</b>:文件直接上传到腾讯云 COS 存储空间,支持大文件上传,不占用服务器空间及服务器带宽流量,支持 CDN 加速访问,访问量大时推荐使用。</p>
</div>

View File

@ -1,10 +1,9 @@
<div class="layui-form-item">
<label class="layui-form-label label-required">
<span class="color-green font-w7">命名方式</span><br><span class="nowrap color-desc">NameType</span>
<b class="color-green">命名方式</b><br><span class="nowrap color-desc">NameType</span>
</label>
<div class="layui-input-block">
{if !sysconf('storage.name_type')}{php}sysconf('storage.name_type','xmd5');{/php}{/if}
{foreach ['xmd5'=>'文件哈希值','date'=>'日期+随机'] as $k=>$v}
{foreach ['xmd5'=>'文件哈希值 ( 支持秒传 )','date'=>'日期+随机 ( 普通上传 )'] as $k=>$v}
<label class="think-radio notselect">
{if sysconf('storage.name_type') eq $k}
<input checked type="radio" name="storage.name_type" value="{$k}" lay-ignore> {$v}
@ -16,12 +15,12 @@
<p class="help-block">类型为“文件哈希”时可以实现文件秒传功能,同一个文件只需上传一次节省存储空间,推荐使用。</p>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label label-required">
<span class="color-green font-w7">链接类型</span><br><span class="nowrap color-desc">LinkType</span>
<b class="color-green">链接类型</b><br><span class="nowrap color-desc">LinkType</span>
</label>
<div class="layui-input-block">
{if !sysconf('storage.link_type')}{php}sysconf('storage.link_type','none');{/php}{/if}
{foreach ['none'=>'简洁链接','full'=>'完整链接','none+compress'=>'简洁并压缩图片','full+compress'=>'完整并压缩图片'] as $k=>$v}
<label class="think-radio notselect">
{if sysconf('storage.link_type') eq $k}
@ -37,7 +36,7 @@
<div class="layui-form-item">
<label class="layui-form-label" for="storage.allow_exts">
<span class="color-green font-w7">允许类型</span><br><span class="nowrap color-desc">AllowExts</span>
<b class="color-green">允许类型</b><br><span class="nowrap color-desc">AllowExts</span>
</label>
<div class="layui-input-block">
<input id="storage.allow_exts" type="text" name="storage.allow_exts" required value="{:sysconf('storage.allow_exts')}" placeholder="请输入系统文件上传后缀" class="layui-input">

View File

@ -1,7 +1,7 @@
<form action="{:sysuri()}" method="post" data-auto="true" class="layui-form layui-card">
<div class="layui-card-body padding-top-20">
<div class="color-text margin-left-40 margin-bottom-20 layui-code text-center layui-bg-gray" style="border-left-width:1px">
<div class="color-text layui-code text-center layui-bg-gray" style="border-left-width:1px;margin:0 0 15px 40px">
<p class="margin-bottom-5 font-w7">文件将上传到阿里云 OSS 存储,需要配置 OSS 公开访问及跨域策略</p>
<p>需要配置跨域访问 CORS 规则,设置:来源 Origin *,允许 Methods POST允许 Headers *</p>
</div>
@ -10,7 +10,7 @@
<div class="layui-form-item">
<label class="layui-form-label label-required">
<span class="color-green font-w7">访问协议</span><br><span class="nowrap color-desc">Protocol</span>
<b class="color-green">访问协议</b><br><span class="nowrap color-desc">Protocol</span>
</label>
<div class="layui-input-block">
{if !sysconf('storage.alioss_http_protocol')}{php}sysconf('storage.alioss_http_protocol','http');{/php}{/if}
@ -29,7 +29,7 @@
<div class="layui-form-item">
<label class="layui-form-label">
<span class="color-green font-w7">存储区域</span><br><span class="nowrap color-desc label-required">Region</span>
<b class="color-green">存储区域</b><br><span class="nowrap color-desc label-required">Region</span>
</label>
<div class="layui-input-block">
<select class="layui-select" name="storage.alioss_point" lay-search>
@ -46,7 +46,7 @@
<div class="layui-form-item">
<label class="layui-form-label" for="storage.alioss_bucket">
<span class="color-green font-w7">空间名称</span><br><span class="nowrap color-desc">Bucket</span>
<b class="color-green">空间名称</b><br><span class="nowrap color-desc">Bucket</span>
</label>
<div class="layui-input-block">
<input id="storage.alioss_bucket" type="text" name="storage.alioss_bucket" required value="{:sysconf('storage.alioss_bucket')}" placeholder="请输入阿里云OSS存储 Bucket (空间名称)" class="layui-input">
@ -56,7 +56,7 @@
<div class="layui-form-item">
<label class="layui-form-label" for="storage.alioss_http_domain">
<span class="color-green font-w7">访问域名</span><br><span class="nowrap color-desc">Domain</span>
<b class="color-green">访问域名</b><br><span class="nowrap color-desc">Domain</span>
</label>
<div class="layui-input-block">
<input id="storage.alioss_http_domain" type="text" name="storage.alioss_http_domain" required value="{:sysconf('storage.alioss_http_domain')}" placeholder="请输入阿里云OSS存储 Domain (访问域名)" class="layui-input">
@ -66,7 +66,7 @@
<div class="layui-form-item">
<label class="layui-form-label" for="storage.alioss_access_key">
<span class="color-green font-w7">访问密钥</span><br><span class="nowrap color-desc">AccessKey</span>
<b class="color-green">访问密钥</b><br><span class="nowrap color-desc">AccessKey</span>
</label>
<div class="layui-input-block">
<input id="storage.alioss_access_key" type="text" name="storage.alioss_access_key" required value="{:sysconf('storage.alioss_access_key')}" placeholder="请输入阿里云OSS存储 AccessKey (访问密钥)" class="layui-input">
@ -76,7 +76,7 @@
<div class="layui-form-item">
<label class="layui-form-label" for="storage.alioss_secret_key">
<span class="color-green font-w7">安全密钥</span><br><span class="nowrap color-desc">SecretKey</span>
<b class="color-green">安全密钥</b><br><span class="nowrap color-desc">SecretKey</span>
</label>
<div class="layui-input-block">
<input id="storage.alioss_secret_key" type="text" name="storage.alioss_secret_key" required value="{:sysconf('storage.alioss_secret_key')}" maxlength="43" placeholder="请输入阿里云OSS存储 SecretKey (安全密钥)" class="layui-input">

View File

@ -1,7 +1,7 @@
<form action="{:sysuri()}" method="post" data-auto="true" class="layui-form layui-card">
<div class="layui-card-body padding-top-20">
<div class="color-text margin-left-40 margin-bottom-20 layui-code text-center layui-bg-gray" style="border-left-width:1px">
<div class="color-text layui-code text-center layui-bg-gray" style="border-left-width:1px;margin:0 0 15px 40px">
<p class="margin-bottom-5 font-w7">文件将存储在本地服务器,默认保存在 public/upload 目录,文件以 HASH 命名。</p>
<p>文件存储的目录需要有读写权限,有足够的存储空间。<span class="color-red">特别注意,本地存储暂不支持图片压缩!</span></p>
</div>
@ -10,7 +10,7 @@
<div class="layui-form-item">
<label class="layui-form-label label-required">
<span class="color-green font-w7">访问协议</span><br><span class="nowrap color-desc">Protocol</span>
<b class="color-green">访问协议</b><br><span class="nowrap color-desc">Protocol</span>
</label>
<div class="layui-input-block">
{if !sysconf('storage.local_http_protocol')}{php}sysconf('storage.local_http_protocol','http');{/php}{/if}
@ -29,7 +29,7 @@
<div class="layui-form-item">
<label class="layui-form-label" for="storage.local_http_domain">
<span class="color-green font-w7">访问域名</span><br><span class="nowrap color-desc">Domain</span>
<b class="color-green">访问域名</b><br><span class="nowrap color-desc">Domain</span>
</label>
<div class="layui-input-block">
<input id="storage.local_http_domain" type="text" name="storage.local_http_domain" value="{:sysconf('storage.local_http_domain')}" placeholder="请输入上传后的访问域名 (非必填项)" class="layui-input">

View File

@ -1,7 +1,7 @@
<form action="{:sysuri()}" method="post" data-auto="true" class="layui-form layui-card">
<div class="layui-card-body padding-top-20">
<div class="color-text margin-left-40 margin-bottom-20 layui-code text-center layui-bg-gray" style="border-left-width:1px">
<div class="color-text layui-code text-center layui-bg-gray" style="border-left-width:1px;margin:0 0 15px 40px">
<p class="margin-bottom-5 font-w7">文件将上传到七牛云存储,对象存储需要配置为公开访问的 Bucket 空间</p>
完成实名认证后可获得 10G 免费存储空间哦!<a target="_blank" href="https://portal.qiniu.com/signup?code=1hefnmobzees2">我要免费申请</a>
</div>
@ -10,7 +10,7 @@
<div class="layui-form-item">
<label class="layui-form-label label-required">
<span class="color-green font-w7">访问协议</span><br><span class="nowrap color-desc">Protocol</span>
<b class="color-green">访问协议</b><br><span class="nowrap color-desc">Protocol</span>
</label>
<div class="layui-input-block">
{if !sysconf('storage.qiniu_http_protocol')}{php}sysconf('storage.qiniu_http_protocol','http');{/php}{/if}
@ -29,7 +29,7 @@
<div class="layui-form-item">
<label class="layui-form-label">
<span class="color-green font-w7">存储区域</span><br><span class="nowrap color-desc label-required">Region</span>
<b class="color-green">存储区域</b><br><span class="nowrap color-desc label-required">Region</span>
</label>
<div class="layui-input-block">
{foreach ['华东','华北','华南','北美'] as $area}
@ -47,7 +47,7 @@
<div class="layui-form-item">
<label class="layui-form-label" for="storage.qiniu_bucket">
<span class="color-green font-w7">空间名称</span><br><span class="nowrap color-desc">Bucket</span>
<b class="color-green">空间名称</b><br><span class="nowrap color-desc">Bucket</span>
</label>
<div class="layui-input-block">
<input id="storage.qiniu_bucket" type="text" name="storage.qiniu_bucket" required value="{:sysconf('storage.qiniu_bucket')}" placeholder="请输入七牛云存储 Bucket (空间名称)" class="layui-input">
@ -57,7 +57,7 @@
<div class="layui-form-item">
<label class="layui-form-label" for="storage.qiniu_http_domain">
<span class="color-green font-w7">访问域名</span><br><span class="nowrap color-desc">Domain</span>
<b class="color-green">访问域名</b><br><span class="nowrap color-desc">Domain</span>
</label>
<div class="layui-input-block">
<input id="storage.qiniu_http_domain" type="text" name="storage.qiniu_http_domain" required value="{:sysconf('storage.qiniu_http_domain')}" placeholder="请输入七牛云存储 Domain (访问域名)" class="layui-input">
@ -67,7 +67,7 @@
<div class="layui-form-item">
<label class="layui-form-label" for="storage.qiniu_access_key">
<span class="color-green font-w7">访问密钥</span><br><span class="nowrap color-desc">AccessKey</span>
<b class="color-green">访问密钥</b><br><span class="nowrap color-desc">AccessKey</span>
</label>
<div class="layui-input-block">
<input id="storage.qiniu_access_key" type="text" name="storage.qiniu_access_key" required value="{:sysconf('storage.qiniu_access_key')}" placeholder="请输入七牛云授权 AccessKey (访问密钥)" class="layui-input">
@ -77,7 +77,7 @@
<div class="layui-form-item">
<label class="layui-form-label" for="storage.qiniu_secret_key">
<span class="color-green font-w7">安全密钥</span><br><span class="nowrap color-desc">SecretKey</span>
<b class="color-green">安全密钥</b><br><span class="nowrap color-desc">SecretKey</span>
</label>
<div class="layui-input-block">
<input id="storage.qiniu_secret_key" type="text" name="storage.qiniu_secret_key" required value="{:sysconf('storage.qiniu_secret_key')}" maxlength="43" placeholder="请输入七牛云授权 SecretKey (安全密钥)" class="layui-input">

View File

@ -1,16 +1,16 @@
<form action="{:sysuri()}" method="post" data-auto="true" class="layui-form layui-card">
<div class="layui-card-body padding-top-20">
<div class="color-text margin-left-40 margin-bottom-20 layui-code text-center layui-bg-gray" style="border-left-width:1px">
<div class="color-text layui-code text-center layui-bg-gray" style="border-left-width:1px;margin:0 0 15px 40px">
<p class="margin-bottom-5 font-w7">文件将上传到 <a target="_blank" href="https://curl.qcloud.com/4t0Mbw2K">腾讯云</a> COS 存储,需要配置 COS 公有读私有写访问权限及跨域策略</p>
<p>配置跨域访问 CORS 规则,设置来源 Origin *,允许 Methods POST允许 Headers *</p>
<p>配置跨域访问 CORS 规则,设置来源 Origin *,允许 Methods POST允许 Headers *</p>
</div>
{include file='config/storage-0'}
<div class="layui-form-item">
<label class="layui-form-label label-required">
<span class="color-green font-w7">访问协议</span><br><span class="nowrap color-desc">Protocol</span>
<b class="color-green">访问协议</b><br><span class="nowrap color-desc">Protocol</span>
</label>
<div class="layui-input-block">
{if !sysconf('storage.txcos_http_protocol')}{php}sysconf('storage.txcos_http_protocol','http');{/php}{/if}
@ -29,7 +29,7 @@
<div class="layui-form-item">
<label class="layui-form-label">
<span class="color-green font-w7">存储区域</span><br><span class="nowrap color-desc label-required">Region</span>
<b class="color-green">存储区域</b><br><span class="nowrap color-desc label-required">Region</span>
</label>
<div class="layui-input-block">
<select class="layui-select" name="storage.txcos_point" lay-search>
@ -46,7 +46,7 @@
<div class="layui-form-item">
<label class="layui-form-label" for="storage.txcos_bucket">
<span class="color-green font-w7">空间名称</span><br><span class="nowrap color-desc">Bucket</span>
<b class="color-green">空间名称</b><br><span class="nowrap color-desc">Bucket</span>
</label>
<div class="layui-input-block">
<input id="storage.txcos_bucket" type="text" name="storage.txcos_bucket" required value="{:sysconf('storage.txcos_bucket')}" placeholder="请输入腾讯云COS存储 Bucket" class="layui-input">
@ -56,7 +56,7 @@
<div class="layui-form-item">
<label class="layui-form-label" for="storage.txcos_http_domain">
<span class="color-green font-w7">访问域名</span><br><span class="nowrap color-desc">Domain</span>
<b class="color-green">访问域名</b><br><span class="nowrap color-desc">Domain</span>
</label>
<div class="layui-input-block">
<input id="storage.txcos_http_domain" type="text" name="storage.txcos_http_domain" required value="{:sysconf('storage.txcos_http_domain')}" placeholder="请输入腾讯云COS存储 Domain" class="layui-input">
@ -66,7 +66,7 @@
<div class="layui-form-item">
<label class="layui-form-label" for="storage.txcos_access_key">
<span class="color-green font-w7">访问密钥</span><br><span class="nowrap color-desc">AccessKey</span>
<b class="color-green">访问密钥</b><br><span class="nowrap color-desc">AccessKey</span>
</label>
<div class="layui-input-block">
<input id="storage.txcos_access_key" type="text" name="storage.txcos_access_key" required value="{:sysconf('storage.txcos_access_key')}" placeholder="请输入腾讯云COS存储 AccessKey" class="layui-input">
@ -76,7 +76,7 @@
<div class="layui-form-item">
<label class="layui-form-label" for="storage.txcos_secret_key">
<span class="color-green font-w7">安全密钥</span><br><span class="nowrap color-desc">SecretKey</span>
<b class="color-green">安全密钥</b><br><span class="nowrap color-desc">SecretKey</span>
</label>
<div class="layui-input-block">
<input id="storage.txcos_secret_key" type="text" name="storage.txcos_secret_key" required value="{:sysconf('storage.txcos_secret_key')}" maxlength="43" placeholder="请输入腾讯云COS存储 SecretKey" class="layui-input">

View File

@ -0,0 +1,79 @@
<form action="{:sysuri()}" method="post" data-auto="true" class="layui-form layui-card">
<div class="layui-card-body padding-top-20">
<div class="color-text layui-code text-center layui-bg-gray" style="border-left-width:1px;margin:0 0 15px 40px">
<p class="margin-bottom-5 font-w7">文件将上传到 <a target="_blank" href="https://console.upyun.com/register/?invite=PN1cRmjRb">又拍云</a> USS 存储,需要配置 USS 公开访问及跨域策略</p>
<p>需配置跨域访问 CORS 规则,设置来源 Origin *,允许 Methods POST允许 Headers *</p>
</div>
{include file='config/storage-0'}
<div class="layui-form-item">
<label class="layui-form-label label-required">
<b class="color-green">访问协议</b><br><span class="nowrap color-desc">Protocol</span>
</label>
<div class="layui-input-block">
{if !sysconf('storage.upyun_http_protocol')}{php}sysconf('storage.upyun_http_protocol','http');{/php}{/if}
{foreach ['http'=>'HTTP','https'=>'HTTPS','auto'=>"AUTO"] as $protocol=>$remark}
<label class="think-radio">
{if sysconf('storage.upyun_http_protocol') eq $protocol}
<input checked type="radio" name="storage.upyun_http_protocol" value="{$protocol}" lay-ignore> {$remark}
{else}
<input type="radio" name="storage.upyun_http_protocol" value="{$protocol}" lay-ignore> {$remark}
{/if}
</label>
{/foreach}
<p class="help-block">又拍云存储访问协议,其中 HTTPS 需要配置证书才能使用AUTO 为相对协议)</p>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label" for="storage.upyun_bucket">
<b class="color-green">空间名称</b><br><span class="nowrap color-desc">Bucket</span>
</label>
<div class="layui-input-block">
<input id="storage.upyun_bucket" name="storage.upyun_bucket" required value="{:sysconf('storage.upyun_bucket')}" placeholder="请输入又拍云存储 Bucket (空间名称)" class="layui-input">
<p class="help-block">填写又拍云存储空间名称think-admin-USS需要是全区唯一的值不存在时会自动创建</p>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label" for="storage.upyun_http_domain">
<b class="color-green">访问域名</b><br><span class="nowrap color-desc">Domain</span>
</label>
<div class="layui-input-block">
<input id="storage.upyun_http_domain" name="storage.upyun_http_domain" required value="{:sysconf('storage.upyun_http_domain')}" placeholder="请输入又拍云存储 Domain (访问域名)" class="layui-input">
<p class="help-block">填写又拍云存储外部访问域名static.thinkadmin.top</p>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label" for="storage.upyun_access_key">
<b class="color-green">操作账号</b><br><span class="nowrap color-desc">Username</span>
</label>
<div class="layui-input-block">
<input id="storage.upyun_access_key" name="storage.upyun_access_key" required value="{:sysconf('storage.upyun_access_key')}" maxlength="100" placeholder="请输入又拍云存储 Username (操作员账号)" class="layui-input">
<p class="help-block">可以在 [ 账户管理 > 操作员 ] 设置操作员账号并将空间给予授权。</p>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label" for="storage.upyun_secret_key">
<b class="color-green">操作密码</b><br><span class="nowrap color-desc">Password</span>
</label>
<div class="layui-input-block">
<input id="storage.upyun_secret_key" name="storage.upyun_secret_key" required value="{:sysconf('storage.upyun_secret_key')}" maxlength="100" placeholder="请输入又拍云存储 Password (操作员密码)" class="layui-input">
<p class="help-block">可以在 [ 账户管理 > 操作员 ] 设置操作员密码并将空间给予授权</p>
</div>
</div>
<div class="hr-line-dashed margin-left-40"></div>
<input type="hidden" name="storage.type" value="upyun">
<div class="layui-form-item text-center padding-left-40">
<button class="layui-btn" type="submit">保存配置</button>
<button class="layui-btn layui-btn-danger" type='button' data-confirm="确定要取消修改吗?" data-close>取消修改</button>
</div>
</div>
</form>

View File

@ -15,7 +15,10 @@
<link rel="stylesheet" href="__ROOT__/static/theme/css/console.css?at={:date('md')}">
<link rel="stylesheet" href="__ROOT__/static/extra/style.css?at={:date('md')}">
{block name="style"}{/block}
<script>window.tapiRoot = '{:sysuri("admin/index/index",[],false)}'</script>
<script>
window.taEditor = '{:sysconf("base.editor")?:"ckeditor4"}';
window.tapiRoot = '{:sysuri("admin/index/index",[],false)}'
</script>
<script src="__ROOT__/static/plugs/jquery/pace.min.js"></script>
</head>

View File

@ -29,7 +29,7 @@
{field: 'username', title: '操作账号', minWidth: 100, sort: true, align: 'center'},
{field: 'node', title: '操作节点', minWidth: 120},
{field: 'action', title: '操作行为', minWidth: 120},
{field: 'content', title: '操作描述', minWidth: 120},
{field: 'content', title: '操作描述', minWidth: 150},
{field: 'geoip', title: '访问地址', minWidth: 100},
{field: 'geoisp', title: '网络服务商', minWidth: 100},
{field: 'create_at', title: '操作时间', minWidth: 170, align: 'center', sort: true},

View File

@ -11,9 +11,7 @@
<div class="layui-form-item label-required-prev">
<span class="help-label"><b>页面内容</b>Page Content</span>
<label class="relative block" style="height:606px">
<textarea class="layui-hide" name="content">{$data.content|default=$base.content}</textarea>
</label>
<textarea class="layui-hide" name="content">{$data.content|default=$base.content}</textarea>
</div>
<div class="hr-line-dashed"></div>
@ -29,7 +27,7 @@
<script>
require(['ckeditor'], function () {
window.createEditor('[name=content]', {height: 533});
window.createEditor('[name=content]', {height: 565});
});
</script>
{/block}

View File

@ -25,7 +25,7 @@
even: true, height: 'full',
sort: {field: 'number', type: 'asc'},
cols: [[
{field: 'number', title: '序号', align: "center", minWidth: 80, sort: true},
{field: 'number', title: '序号', align: "center", width: 80, sort: true},
{field: 'name', title: '等级名称', align: 'center', minWidth: 100},
{
field: 'upgrade_team', title: '团队计数', align: 'center', width: 80, templet: function (d) {
@ -70,7 +70,7 @@
}
},
{
field: 'rebate_rule', title: '奖利规则', align: 'center', minWidth: 100, templet: function (d) {
field: 'rebate_rule', title: '奖利规则', align: 'left', minWidth: 100, templet: function (d) {
return (d.html = ''), layui.each(d.rebate_rule || {}, function (k, rule) {
d.html += laytpl('<span class="layui-badge layui-bg-gray">{{d.v}}</span>').render({v: rule});
}), d.html || '-';

View File

@ -49,9 +49,9 @@
</form>
<script>
$('input[name="cover"]').uploadOneImage();
require(['ckeditor'], function () {
$('input[name="cover"]').uploadOneImage();
window.createEditor('[name=content]', {height: 350});
window.createEditor('[name=content]', {height: 350})
});
</script>
{/block}

View File

@ -333,7 +333,7 @@
function getRand(length, prefix) {
return (function (time, code) {
code += parseInt(time.substr(0, 1)) + parseInt(time.substr(1, 1)) + time.substr(2, 8);
code += parseInt(time.substring(0, 1)) + parseInt(time.substring(1, 2)) + time.substring(2);
while (code.length < length) code += Math.round(Math.random() * 10);
return code;
})(Date.now().toString(), prefix || '' + '')

3
app/service.php Normal file
View File

@ -0,0 +1,3 @@
<?php
include_once __DIR__ . '/../extend/think/admin/common.php';
return [\think\admin\Library::class];

View File

@ -54,7 +54,7 @@
</div>
<div class="layui-form-item label-required-prev">
<span class="color-green">图文文章内容</span>
<textarea ng-model="item.content" name='content'></textarea>
<textarea class="layui-hide" ng-model="item.content" name='content'></textarea>
</div>
<label class="layui-form-item relative block">
<span class="help-block">摘要选填如果不填写会默认抓取正文前54个字</span>
@ -113,8 +113,14 @@
setTimeout(function () {
if (editor) editor.destroy();
editor = window.createEditor('[name="content"]');
editor.setData($rootScope.item.content);
$vali.checkAllInput();
if (typeof editor !== 'undefined') {
editor.setData($rootScope.item.content);
$vali.checkAllInput();
} else $('[name="content"]').on('editor.init', function (event, myEditor) {
myEditor.setData($rootScope.item.content);
editor = myEditor;
$vali.checkAllInput();
});
}, 100);
}

View File

@ -50,9 +50,7 @@ window.jQuery = window.$ = window.jQuery || window.$ || layui.$;
/*! 配置 require 参数 */
require.config({
baseUrl: baseRoot, waitSeconds: 60,
map: {'*': {css: baseRoot + 'plugs/require/css.js'}},
paths: {
baseUrl: baseRoot, waitSeconds: 60, map: {'*': {css: baseRoot + 'plugs/require/css.js'}}, paths: {
'vue': ['plugs/vue/vue.min'],
'md5': ['plugs/jquery/md5.min'],
'json': ['plugs/jquery/json.min'],
@ -60,10 +58,12 @@ require.config({
'excel': ['plugs/jquery/excel.xlsx'],
'base64': ['plugs/jquery/base64.min'],
'upload': [tapiRoot + '/api.upload/index?'],
'notify': ['plugs/notify/notify.min'],
'angular': ['plugs/angular/angular.min'],
'cropper': ['plugs/cropper/cropper.min'],
'echarts': ['plugs/echarts/echarts.min'],
'ckeditor': ['plugs/ckeditor/ckeditor'],
'ckeditor4': ['plugs/ckeditor4/ckeditor'],
'ckeditor5': ['plugs/ckeditor5/ckeditor'],
'websocket': ['plugs/socket/websocket'],
'pcasunzips': ['plugs/jquery/pcasunzips'],
'sortablejs': ['plugs/sortable/sortable.min'],
@ -72,11 +72,12 @@ require.config({
'jquery.masonry': ['plugs/jquery/masonry.min'],
'jquery.cropper': ['plugs/cropper/cropper.min'],
'jquery.autocompleter': ['plugs/jquery/autocompleter.min'],
},
shim: {
}, shim: {
'excel': {deps: [baseRoot + 'plugs/layui_exts/excel.js']},
'websocket': {deps: [baseRoot + 'plugs/socket/swfobject.min.js']},
'notify': {deps: ['css!' + baseRoot + 'plugs/notify/light.css']},
'cropper': {deps: ['css!' + baseRoot + 'plugs/cropper/cropper.min.css']},
'websocket': {deps: [baseRoot + 'plugs/socket/swfobject.min.js']},
'ckeditor5': {deps: ['jquery', 'upload', 'css!' + baseRoot + 'plugs/ckeditor5/ckeditor.css']},
'vue.sortable': {deps: ['vue', 'sortablejs']},
'jquery.ztree': {deps: ['jquery', 'css!' + baseRoot + 'plugs/ztree/zTreeStyle/zTreeStyle.css']},
'jquery.autocompleter': {deps: ['jquery', 'css!' + baseRoot + 'plugs/jquery/autocompleter.css']},
@ -88,6 +89,14 @@ define('jquery', [], function () {
return layui.$;
});
/*! 注册 ckeditor 组件 */
define('ckeditor', (function (type) {
if (/^ckeditor[45]$/.test(type)) return [type];
return [Object.fromEntries ? 'ckeditor5' : 'ckeditor4'];
})(window.taEditor || 'ckeditor4'), function (ckeditor) {
return ckeditor;
});
$(function () {
window.$body = $('body');
@ -240,8 +249,7 @@ $(function () {
$.vali.listen($dom = $dom || $(this.selecter)), $body.trigger('reInit', $dom);
return $dom.find('[required]').map(function () {
this.$parent = $(this).parent();
if (this.$parent.is('label')) this.$parent.addClass('label-required-prev');
else this.$parent.prevAll('label.layui-form-label').addClass('label-required-next');
if (this.$parent.is('label')) this.$parent.addClass('label-required-prev'); else this.$parent.prevAll('label.layui-form-label').addClass('label-required-next');
}), $dom.find('[data-lazy-src]:not([data-lazy-loaded])').map(function () {
if (this.dataset.lazyLoaded === 'true') return; else this.dataset.lazyLoaded = 'true';
if (this.nodeName === 'IMG') this.src = this.dataset.lazySrc; else this.style.backgroundImage = 'url(' + this.dataset.lazySrc + ')';
@ -388,16 +396,14 @@ $(function () {
(layui.data('AdminMenuType')['mini'] || $body.width() < 1000) ? layout.addClass(mclass) : layout.removeClass(mclass);
}).trigger('resize').on('hashchange', function () {
if (/^#(https?:)?(\/\/|\\\\)/.test(location.hash)) return $.msg.tips('禁止访问外部链接!');
if (location.hash.length < 1) return $body.find('[data-menu-node]:first').trigger('click');
else return that.href(location.hash);
if (location.hash.length < 1) return $body.find('[data-menu-node]:first').trigger('click'); else return that.href(location.hash);
}).trigger('hashchange');
};
/*! 同步二级菜单展示状态 */
this.sync = function (mode) {
$('[data-submenu-layout]').map(function () {
var node = this.dataset.submenuLayout;
if (mode === 1) layui.data('AdminMenuState', {key: node, value: $(this).hasClass('layui-nav-itemed') ? 2 : 1});
else if (mode === 2) (layui.data('AdminMenuState')[node] || 2) === 2 && $(this).addClass('layui-nav-itemed');
if (mode === 1) layui.data('AdminMenuState', {key: node, value: $(this).hasClass('layui-nav-itemed') ? 2 : 1}); else if (mode === 2) (layui.data('AdminMenuState')[node] || 2) === 2 && $(this).addClass('layui-nav-itemed');
});
};
/*! 页面 LOCATION-HASH 跳转 */
@ -440,8 +446,7 @@ $(function () {
this.tags = 'input,select,textarea';
/* 预设检测规则 */
this.patterns = {
phone: '^1[3-9][0-9]{9}$',
email: '^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$'
phone: '^1[3-9][0-9]{9}$', email: '^([a-zA-Z0-9_\.\-])+\@(([a-zA-Z0-9\-])+\.)+([a-zA-Z0-9]{2,4})+$'
};
/*! 检测属性是否有定义 */
this.hasProp = function (ele, prop) {
@ -550,9 +555,7 @@ $(function () {
var key, keys = this.name.match(rules.key), merge = this.value, name = this.name;
while ((key = keys.pop()) !== undefined) {
name = name.replace(new RegExp("\\[" + key + "\\]$"), '');
if (key.match(rules.push)) merge = self.build([], self.pushCounter(name), merge);
else if (key.match(rules.fixed)) merge = self.build([], key, merge);
else if (key.match(rules.named)) merge = self.build({}, key, merge);
if (key.match(rules.push)) merge = self.build([], self.pushCounter(name), merge); else if (key.match(rules.fixed)) merge = self.build([], key, merge); else if (key.match(rules.named)) merge = self.build({}, key, merge);
}
data = $.extend(true, data, merge);
});
@ -598,8 +601,7 @@ $(function () {
$image = $('<div class="uploadimage uploadimagemtl transition"><div><a class="layui-icon">&#xe603;</a><a class="layui-icon">&#x1006;</a><a class="layui-icon">&#xe602;</a></div></div>');
$image.attr('data-tips-image', encodeURI(src)).css('backgroundImage', 'url(' + encodeURI(src) + ')').on('click', 'a', function (event, index, prevs, $item) {
event.stopPropagation(), $item = $(this).parent().parent(), index = $(this).index();
if (index === 2 && $item.index() !== $bt.prevAll('div.uploadimage').length) $item.next().after($item);
else if (index === 0 && $item.index() > 1) $item.prev().before($item); else if (index === 1) $item.remove();
if (index === 2 && $item.index() !== $bt.prevAll('div.uploadimage').length) $item.next().after($item); else if (index === 0 && $item.index() > 1) $item.prev().before($item); else if (index === 1) $item.remove();
imgs = [], $bt.prevAll('.uploadimage').map(function () {
imgs.push($(this).attr('data-tips-image'));
});
@ -660,7 +662,7 @@ $(function () {
(selection.text = value), selection.select(), selection.unselect();
} else if (this.selectionStart || this.selectionStart === 0) {
var spos = this.selectionStart, apos = this.selectionEnd || spos;
this.value = this.value.substring(0, spos) + value + this.value.substring(apos, this.value.length);
this.value = this.value.substring(0, spos) + value + this.value.substring(apos);
this.selectionEnd = this.selectionStart = spos + value.length;
} else {
this.value += value;
@ -798,13 +800,7 @@ $(function () {
if (doReload && doScript) {
$.layTable.reload(((element || {}).dataset || {}).tableId || true);
}
}, content: '' +
'<div class="padding-30 padding-bottom-0" data-queue-load="' + code + '">' +
' <div class="layui-elip notselect nowrap" data-message-title></div>' +
' <div class="margin-top-15 layui-progress layui-progress-big" lay-showPercent="yes"><div class="layui-progress-bar transition" lay-percent="0.00%"></div></div>' +
' <div class="margin-top-15"><code class="layui-textarea layui-bg-black border-0" disabled style="resize:none;overflow:hidden;height:190px"></code></div>' +
'</div>',
success: function ($elem) {
}, content: '' + '<div class="padding-30 padding-bottom-0" data-queue-load="' + code + '">' + ' <div class="layui-elip notselect nowrap" data-message-title></div>' + ' <div class="margin-top-15 layui-progress layui-progress-big" lay-showPercent="yes"><div class="layui-progress-bar transition" lay-percent="0.00%"></div></div>' + ' <div class="margin-top-15"><code class="layui-textarea layui-bg-black border-0" disabled style="resize:none;overflow:hidden;height:190px"></code></div>' + '</div>', success: function ($elem) {
new function () {
var that = this;
this.$box = $elem.find('[data-queue-load=' + code + ']');
@ -839,8 +835,7 @@ $(function () {
var lines = [];
for (var idx in ret.data.history) {
var line = ret.data.history[idx], percent = '[ ' + line.progress + '% ] ';
if (line.message.indexOf('javascript:') === -1) lines.push(line.message.indexOf('>>>') > -1 ? line.message : percent + line.message);
else if (!that.SetCache(code, idx) && doScript !== false) that.SetCache(code, idx, 1), location.href = line.message;
if (line.message.indexOf('javascript:') === -1) lines.push(line.message.indexOf('>>>') > -1 ? line.message : percent + line.message); else if (!that.SetCache(code, idx) && doScript !== false) that.SetCache(code, idx, 1), location.href = line.message;
}
if (ret.data.status > 0) {
that.SetState(parseInt(ret.data.status), ret.data.message);

View File

@ -1436,7 +1436,8 @@ h&&l&&!g.data("cke-upload-id")&&!g.isReadOnly(1)&&(h=(h=d.match(/image\/([a-z]+?
// 注册创建函数
window.createEditor = function (selector, option) {
var $container = $(selector);
var $container = $(selector), editor;
$container.attr('id', $container.attr('id') || (Math.random() + '').replace('.', '_'));
return CKEDITOR.replace($container.attr('id'), option || {});
editor = CKEDITOR.replace($container.attr('id'), option || {});
return $container.triggerHandler('editor.init', editor), editor;
};

View File

Before

Width:  |  Height:  |  Size: 5.5 KiB

After

Width:  |  Height:  |  Size: 5.5 KiB

View File

Before

Width:  |  Height:  |  Size: 178 B

After

Width:  |  Height:  |  Size: 178 B

View File

Before

Width:  |  Height:  |  Size: 12 KiB

After

Width:  |  Height:  |  Size: 12 KiB

View File

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 37 KiB

View File

Before

Width:  |  Height:  |  Size: 265 B

After

Width:  |  Height:  |  Size: 265 B

View File

Before

Width:  |  Height:  |  Size: 1.6 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

Before

Width:  |  Height:  |  Size: 752 B

After

Width:  |  Height:  |  Size: 752 B

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 138 B

After

Width:  |  Height:  |  Size: 138 B

View File

Before

Width:  |  Height:  |  Size: 133 B

After

Width:  |  Height:  |  Size: 133 B

View File

Before

Width:  |  Height:  |  Size: 99 B

After

Width:  |  Height:  |  Size: 99 B

View File

Before

Width:  |  Height:  |  Size: 115 B

After

Width:  |  Height:  |  Size: 115 B

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

Before

Width:  |  Height:  |  Size: 1.2 KiB

After

Width:  |  Height:  |  Size: 1.2 KiB

Some files were not shown because too many files have changed in this diff Show More