Compare commits

...

17 Commits

Author SHA1 Message Date
Anyon
b952e07980 Update 2022.03.06.01.md 2022-07-12 15:00:33 +08:00
Anyon
89c6bd2367 Update 2022.07.11.01.md 2022-07-12 14:58:28 +08:00
Anyon
05eab359b5 移动文件 2022-07-12 14:49:59 +08:00
Anyon
f84b2e7f2c 移动插件 2022-07-12 14:44:06 +08:00
Anyon
17deae634e 修改样式 2022-07-12 11:48:16 +08:00
Anyon
d1798b8d34 修改样式 2022-07-12 11:45:12 +08:00
Anyon
e9c49b27bc 修改样式 2022-07-12 11:43:34 +08:00
Anyon
af69c61d11 修改文件选择器 2022-07-12 11:40:15 +08:00
Anyon
24b17f900a Update Upload.php 2022-07-12 11:30:20 +08:00
Anyon
5a0b7d4ef3 Update index.html 2022-07-12 11:26:27 +08:00
Anyon
8a1742521e Update index.html 2022-07-12 11:25:38 +08:00
Anyon
300c40f560 Update File.php 2022-07-12 11:23:22 +08:00
Anyon
022fe90134 Update File.php 2022-07-12 11:22:56 +08:00
Anyon
525834a40d 增加文件清理操作 2022-07-12 11:22:16 +08:00
Anyon
7526721b4a 修改应用调用 2022-07-12 10:53:27 +08:00
Anyon
a12f7599a2 静态调用app 2022-07-12 10:51:41 +08:00
Anyon
708d6bc3be 修改文件管理 2022-07-12 10:48:42 +08:00
17 changed files with 161 additions and 95 deletions

View File

@ -80,4 +80,18 @@ class File extends Controller
{
SystemFile::mDelete();
}
/**
* 清理重复文件
* @auth true
* @return void
*/
public function distinct()
{
$map = ['uuid' => AdminService::getUserId()];
$db1 = SystemFile::mk()->fieldRaw('max(id) id')->where($map)->group('type,hash');
$db2 = $this->app->db->table($db1->buildSql())->alias('dt')->field('id');
SystemFile::mk()->whereRaw("id not in {$db2->buildSql()}")->delete();
$this->success('清理重复文件成功!');
}
}

View File

@ -153,8 +153,8 @@ class Upload extends Controller
SystemFile::mQuery()->layTable(function () {
$this->title = '文件选择器';
}, function (QueryHelper $query) {
$query->like('name,hash')->dateBetween('create_at')->order('id desc');
$query->where(['status' => 2, 'issafe' => 0, 'uuid' => AdminService::getUserId()]);
$query->like('name,hash')->in('xext#type')->dateBetween('create_at')->order('id desc');
});
}

View File

@ -1,10 +1,10 @@
* 升级 layui-2.7 版本并增加了 flex 样式,细节如下:
1. 增加 .flex 及相关样式 class。
2. 增加 .help-images 图片上传容器,支持单图或多图。
3. 增加 .help-checks 多项选择器容器,支持 raido,checkbox。
4. 修复 layui.form.checkbox 特殊情况下的异常
5. 修改 ui 主布局的阴影生成方式,修改 NotSelect 使用
6. 修改内容主容器(.think-page-body>.layui-body) 样式
7. 其他细节调整见 _config.less 文件及 _layout.less 文件。。。。
8. 修改系统任务管理界面,简化数据统计及状态检测数据展示
9. 优化 admin.js 加载 loding 显示,首次加载等待成功后再显示界面
* 增加 .flex 及相关样式 class
* 升级 layui-2.7 测试版本并增加了 flex 样式
* 增加 .help-images 图片上传容器,支持单图或多图
* 增加 .help-checks 多项选择器容器,支持 raido,checkbox
* 修复 layui.form.checkbox 特殊情况下的异常
* 修改 ui 主布局的阴影生成方式,修改 NotSelect 使用
* 修改内容主容器(.think-page-body>.layui-body) 样式
* 其他细节调整见 _config.less 文件及 _layout.less 文件
* 修改系统任务管理界面,简化数据统计及状态检测数据展示
* 优化 admin.js 加载 loding 显示,首次加载等待成功后再显示界面

View File

@ -1,13 +1,12 @@
* 增加 CORS 跨域规则配置,配置参数置放于 config/app.php需要更新 ThinkLibrary。
* 修复 layui.table 导致基于 ThinkPHP 模板输出自动转义 XSS 过滤机制失效,需要更新 ThinkLibrary。
* 修复在模板中使用 {:input(NAME)} 取值而产生的 XSS 问题,模板取值更换为 {$get.NAME|default=''}。
* 修复 CKEDITOR4 配置文件禁用所有标签的on事件阻止 xss 脚本注入,需要更新 ckeditor/config.js。
* 修复上传入口的后缀验证,读取真实文件后缀与配置对比,阻止不合法的文件上传并存储到本地服务器。
* 修改 JsonRpc 接口异常处理机制,当服务端绑定 Exception 时,客户端将能收到 error 消息及异常数据。
* 修改 location.hash 访问机制禁止直接访问外部URL资源链接防止外部XSS攻击读取本地缓存数据。
* 增加后台主题样式配置,支持全局默认+用户个性配置,需要更新 ThinkLibrary, static, admin 组件及模块。
* 升级 layui 版本到 2.7.4,引入 jszip 插件
* 框架核心服务层静态化重构,增加后台皮肤样式选择与配置
* 升级 layui 版本到 2.7.4,引入 jszip 插件,引入图片压缩插件
* 重构文件上传机制,增加文件记录,增加进度 tips 提示,增加图片压缩
* 引入 ckeditor5 富文本编辑器,同时保存原 ckeditor4 版本,可后台切换
* 全局改写 layTable 动态数据表格,支持静态刷新,同时兼容原生 table 方式
* 全局改写 layTable 动态数据表格,支持静态刷新,同时兼容原生 table 方式
* 增加 CORS 跨域规则配置,配置参数置放于 config/app.php需要更新 ThinkLibrary
* 修复 layui.table 导致基于 ThinkPHP 模板输出自动转义 XSS 过滤机制失效,需要更新 ThinkLibrary
* 修复在模板中使用 {:input(NAME)} 取值而产生的 XSS 问题,模板取值更换为 {$get.NAME|default=''}
* 修复 ckeditor4 配置文件,禁用所有标签的 on 事件,阻止 XSS 脚本注入,需要更新 ckeditor4/config.js
* 修复上传入口的后缀验证,读取真实文件后缀与配置对比,阻止不合法的文件上传并存储到本地服务器
* 修改 JsonRpc 接口异常处理机制,当服务端绑定 Exception 时,客户端将能收到 error 消息及异常数据
* 修改 location.hash 访问机制,禁止直接访问外部 URL 资源链接,防止外部 XSS 攻击读取本地缓存数据

View File

@ -14,48 +14,46 @@
// | github 代码仓库https://github.com/zoujingli/ThinkAdmin
// +----------------------------------------------------------------------
use think\admin\Library;
use think\admin\service\SystemService;
use think\App;
invoke(function (App $app) {
/*! 非开发环境,清理限制文件 */
if ($app->request->isGet() && !SystemService::checkRunMode()) {
@unlink("{$app->getBasePath()}admin/controller/api/Update.php");
@unlink("{$app->getBasePath()}admin/route/demo.php");
@rmdir("{$app->getBasePath()}admin/route");
return;
}
/*! 演示环境禁止操作路由绑定 */
if (SystemService::checkRunMode('demo')) {
$app->route->post('index/pass', function () {
return json(['code' => 0, 'info' => '演示环境禁止修改用户密码!']);
});
$app->route->post('config/system', function () {
return json(['code' => 0, 'info' => '演示环境禁止修改系统配置!']);
});
$app->route->post('config/storage', function () {
return json(['code' => 0, 'info' => '演示环境禁止修改系统配置!']);
});
$app->route->post('menu', function () {
return json(['code' => 0, 'info' => '演示环境禁止给菜单排序!']);
});
$app->route->post('menu/index', function () {
return json(['code' => 0, 'info' => '演示环境禁止给菜单排序!']);
});
$app->route->post('menu/add', function () {
return json(['code' => 0, 'info' => '演示环境禁止添加菜单!']);
});
$app->route->post('menu/edit', function () {
return json(['code' => 0, 'info' => '演示环境禁止编辑菜单!']);
});
$app->route->post('menu/state', function () {
return json(['code' => 0, 'info' => '演示环境禁止禁用菜单!']);
});
$app->route->post('menu/remove', function () {
return json(['code' => 0, 'info' => '演示环境禁止删除菜单!']);
});
$app->route->post('user/pass', function () {
return json(['code' => 0, 'info' => '演示环境禁止修改用户密码!']);
});
}
});
/*! 非开发环境,清理限制文件 */
if (Library::$sapp->request->isGet() && !SystemService::checkRunMode()) {
@unlink(Library::$sapp->getBasePath() . 'admin/controller/api/Update.php');
@unlink(Library::$sapp->getBasePath() . 'admin/route/demo.php');
@rmdir(Library::$sapp->getBasePath() . 'admin/route');
}
/*! 演示环境禁止操作路由绑定 */
if (SystemService::checkRunMode('demo')) {
Library::$sapp->route->post('index/pass', function () {
return json(['code' => 0, 'info' => '演示环境禁止修改用户密码!']);
});
Library::$sapp->route->post('config/system', function () {
return json(['code' => 0, 'info' => '演示环境禁止修改系统配置!']);
});
Library::$sapp->route->post('config/storage', function () {
return json(['code' => 0, 'info' => '演示环境禁止修改系统配置!']);
});
Library::$sapp->route->post('menu', function () {
return json(['code' => 0, 'info' => '演示环境禁止给菜单排序!']);
});
Library::$sapp->route->post('menu/index', function () {
return json(['code' => 0, 'info' => '演示环境禁止给菜单排序!']);
});
Library::$sapp->route->post('menu/add', function () {
return json(['code' => 0, 'info' => '演示环境禁止添加菜单!']);
});
Library::$sapp->route->post('menu/edit', function () {
return json(['code' => 0, 'info' => '演示环境禁止编辑菜单!']);
});
Library::$sapp->route->post('menu/state', function () {
return json(['code' => 0, 'info' => '演示环境禁止禁用菜单!']);
});
Library::$sapp->route->post('menu/remove', function () {
return json(['code' => 0, 'info' => '演示环境禁止删除菜单!']);
});
Library::$sapp->route->post('user/pass', function () {
return json(['code' => 0, 'info' => '演示环境禁止修改用户密码!']);
});
}

View File

@ -12,11 +12,13 @@
<div class="image-dialog-item" v-for="x in list" @click="setItem(x)" style="display:none" v-show="show" :class="{'image-dialog-checked':x.checked}">
<div class="uploadimage" :style="x.style"></div>
<p class="image-dialog-item-name layui-elip" v-text="x.name"></p>
<span class="image-dialog-item-size">{{formatSize(x.size)}}</span>
<span class="image-dialog-item-type">{{x.xext.toUpperCase()}}</span>
</div>
</div>
<div class="image-dialog-foot">
<div id="ImageDialogPage" class="image-dialog-page"></div>
<div id="ImageDialogButton" class="image-dialog-button" v-if="data.length>0" @click="confirm">{{ data.length }}</div>
<div id="ImageDialogButton layui-hide" class="image-dialog-button" v-if="data.length>0" @click="confirm">{{ data.length }}</div>
</div>
</div>
@ -34,7 +36,9 @@
this.$btn = $('#{$get.id|default=""}');
this.$ups = $('#ImageDialogUploadLayout [data-file]');
this.mult = "{$get.file|default='image'}" === 'images';
this.loadPage();
this.loadPage(), setTimeout(function () {
$('#ImageDialogButton').removeClass('layui-hide');
}, 1000);
},
methods: {
// 搜索刷新数据
@ -49,6 +53,10 @@
app.setValue(file.xurl);
}), this.setInput();
},
// 格式文件大小
formatSize: function (size) {
return $.formatFileSize(size);
},
// 设置单项数据
setItem: function (item) {
if (!this.mult) {
@ -96,6 +104,7 @@
// 加载页面数据
loadPage: function () {
this.params = {page: this.page, limit: this.limit, output: 'layui.table', name: this.keys || ''};
this.params.type = '{$get.type|default="png,jpg,jpeg"}';
$.form.load('{:url("image")}', this.params, 'get', function (ret) {
return app.setList(ret.data, ret.count), false;
});

View File

@ -1,6 +1,9 @@
{extend name='table'}
{block name="button"}
<!--{if auth("distinct")}-->
<a data-table-id="FileTable" data-load='{:url("distinct")}' class='layui-btn layui-btn-sm layui-btn-primary'>清理重复</a>
<!--{/if}-->
<!--{if auth("remove")}-->
<a data-confirm="确定永久删除这些账号吗?" data-table-id="FileTable" data-action='{:url("remove")}' data-rule="id#{id}" class='layui-btn layui-btn-sm layui-btn-primary'>批量删除</a>
<!--{/if}-->
@ -23,7 +26,7 @@
{field: 'hash', title: '文件哈希', width: '15%', align: 'center', templet: '<div><code>{{d.hash}}</code></div>'},
{
field: 'size', title: '文件大小', align: 'center', width: '8%', sort: true, templet: function (d) {
return formatSize(d.size)
return $.formatFileSize(d.size)
}
},
{field: 'xext', title: '文件后缀', align: 'center', width: '8%', sort: true},
@ -31,6 +34,8 @@
field: 'xurl', title: '查看文件', width: '8%', align: 'center', templet: function (d) {
if (typeof d.mime === 'string' && /^image\//.test(d.mime)) {
return laytpl('<div class="headimg headimg-no headimg-ss margin-0" data-tips-hover data-tips-image="{{d.xurl}}" style="background-image:url({{d.xurl}})"></div>').render(d)
} else if (typeof d.mime === 'string' && /^(video|audio)\//.test(d.mime)) {
return laytpl('<div><a target="_blank" data-iframe="{{d.xurl}}" data-title="查看媒体">查看</a></div>').render(d);
} else {
return laytpl('<div><a target="_blank" href="{{d.xurl}}">查看</a></div>').render(d);
}
@ -47,13 +52,6 @@
]]
});
});
function formatSize(size, fixed, units) {
var unit;
units = units || ['B', 'K', 'M', 'G', 'TB'];
while ((unit = units.shift()) && size > 1024) size = size / 1024;
return (unit === 'B' ? size : size.toFixed(fixed === undefined ? 2 : fixed)) + unit;
}
</script>
<script type="text/html" id="toolbar">

View File

@ -23,11 +23,22 @@
<h3 class="layui-timeline-title">
版本号 {$version} <span class="margin-left-10 font-s13 color-desc">{$change.version|default=''}</span>
</h3>
{$change.content|default=''|raw}
<div data-marked>
{$change.content|default=''|raw}
</div>
</div>
</li>
{/foreach}
<li class="layui-timeline-item"></li>
</ul>
</div>
<script>
require(['marked'], function (marked) {
$('[data-marked]').map(function () {
if (this.marked) return; else this.marked = true;
this.innerHTML = marked.parse(this.innerHTML);
});
});
</script>
</form>

View File

@ -12,6 +12,7 @@ use app\data\service\payment\JoinpayPaymentService;
use app\data\service\payment\VoucherPaymentService;
use app\data\service\payment\WechatPaymentService;
use think\admin\Exception;
use think\admin\Library;
use think\App;
/**
@ -174,22 +175,22 @@ abstract class PaymentService
{
if ($code === 'empty') {
$vars = ['code' => 'empty', 'type' => 'empty', 'params' => []];
return static::$driver[$code] = app()->make(EmptyPaymentService::class, $vars);
return static::$driver[$code] = Library::$sapp->make(EmptyPaymentService::class, $vars);
}
[, $type, $params] = self::config($code);
if (isset(static::$driver[$code])) return static::$driver[$code];
$vars = ['code' => $code, 'type' => $type, 'params' => $params];
// 实例化具体支付参数类型
if (stripos($type, 'balance') === 0) {
return static::$driver[$code] = app()->make(BalancePyamentService::class, $vars);
return static::$driver[$code] = Library::$sapp->make(BalancePyamentService::class, $vars);
} elseif (stripos($type, 'voucher') === 0) {
return static::$driver[$code] = app()->make(VoucherPaymentService::class, $vars);
return static::$driver[$code] = Library::$sapp->make(VoucherPaymentService::class, $vars);
} elseif (stripos($type, 'alipay_') === 0) {
return static::$driver[$code] = app()->make(AlipayPaymentService::class, $vars);
return static::$driver[$code] = Library::$sapp->make(AlipayPaymentService::class, $vars);
} elseif (stripos($type, 'wechat_') === 0) {
return static::$driver[$code] = app()->make(WechatPaymentService::class, $vars);
return static::$driver[$code] = Library::$sapp->make(WechatPaymentService::class, $vars);
} elseif (stripos($type, 'joinpay_') === 0) {
return static::$driver[$code] = app()->make(JoinpayPaymentService::class, $vars);
return static::$driver[$code] = Library::$sapp->make(JoinpayPaymentService::class, $vars);
} else {
throw new Exception(sprintf('支付驱动[%s]未定义', $type));
}

View File

@ -54,7 +54,7 @@ class UserTokenService extends Service
private static function buildVerify(): string
{
return md5('-');
// return md5(app()->request->server('HTTP_USER_AGENT', '-'));
//return md5(Library::$sapp->request->server('HTTP_USER_AGENT', '-'));
}
/**

View File

@ -9,11 +9,10 @@ use app\data\service\OrderService;
use app\data\service\RebateService;
use app\data\service\UserBalanceService;
use app\data\service\UserRebateService;
use think\admin\Library;
use think\Console;
$app = app();
if ($app->request->isCli()) {
if (Library::$sapp->request->isCli()) {
// 动态注册操作指令
Console::starting(function (Console $console) {
$console->addCommand(OrderClean::class);
@ -24,21 +23,21 @@ if ($app->request->isCli()) {
});
} else {
// 注册订单支付处理事件
$app->event->listen('ShopOrderPayment', function ($orderNo) use ($app) {
Library::$sapp->event->listen('ShopOrderPayment', function ($orderNo) {
$app->log->notice("订单 {$orderNo} 支付事件,执行用户返利行为");
Library::$sapp->log->notice("订单 {$orderNo} 支付事件,执行用户返利行为");
RebateService::instance()->execute($orderNo);
$app->log->notice("订单 {$orderNo} 支付事件,执行发放余额行为");
Library::$sapp->log->notice("订单 {$orderNo} 支付事件,执行发放余额行为");
UserBalanceService::confirm($orderNo);
$app->log->notice("订单 {$orderNo} 支付事件,执行用户升级行为");
Library::$sapp->log->notice("订单 {$orderNo} 支付事件,执行用户升级行为");
OrderService::upgrade($orderNo);
});
// 注册订单确认支付事件
$app->event->listen('ShopOrderConfirm', function ($orderNo) use ($app) {
$app->log->notice("订单 {$orderNo} 确认事件,执行返利确认行为");
Library::$sapp->event->listen('ShopOrderConfirm', function ($orderNo) {
Library::$sapp->log->notice("订单 {$orderNo} 确认事件,执行返利确认行为");
UserRebateService::confirm($orderNo);
});
}

View File

@ -17,14 +17,15 @@
use app\wechat\command\Auto;
use app\wechat\command\Fans;
use app\wechat\service\AutoService;
use think\admin\Library;
use think\Console;
if (app()->request->isCli()) {
if (Library::$sapp->request->isCli()) {
Console::starting(function (Console $console) {
$console->addCommands([Fans::class, Auto::class]);
});
} else {
app()->event->listen('WechatFansSubscribe', function ($openid) {
Library::$sapp->event->listen('WechatFansSubscribe', function ($openid) {
AutoService::register($openid);
});
}

View File

@ -59,6 +59,7 @@ require.config({
'xlsx': ['plugs/jquery/xlsx.min'],
'jszip': ['plugs/jquery/jszip.min'],
'excel': ['plugs/jquery/excel.xlsx'],
'marked': ['plugs/jquery/marked.min'],
'base64': ['plugs/jquery/base64.min'],
'upload': [tapiRoot + '/api.upload/index?'],
'notify': ['plugs/notify/notify.min'],
@ -785,6 +786,14 @@ $(function () {
};
};
/*!格式化文件大小 */
$.formatFileSize = function (size, fixed, units) {
var unit;
units = units || ['B', 'K', 'M', 'G', 'TB'];
while ((unit = units.shift()) && size > 1024) size = size / 1024;
return (unit === 'B' ? size : size.toFixed(fixed === undefined ? 2 : fixed)) + unit;
}
/*! 弹出图片层 */
$.previewImage = function (src, area) {
var img = new Image(), defer = $.Deferred(), loaded = $.msg.loading();

File diff suppressed because one or more lines are too long

View File

@ -986,6 +986,27 @@ input:not(.layui-hide,[type=hidden]) {
margin: 0;
width: 145px;
height: 145px;
background-color: #fff;
}
&-type, &-size {
top: 2px;
color: #FFF;
padding: 0 6px;
position: absolute;
font-size: 12px;
transform: scale(0.9);
background: rgba(0, 0, 0, 0.6);
line-height: 18px;
border-radius: 18px;
}
&-type {
left: 1px;
}
&-size {
right: 1px;
}
&-name {

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long