Commit baa78b83 authored by wolfcode's avatar wolfcode

init

parent fb3bf521

Too many changes to show.

To preserve performance only 1000 of 1000+ files are displayed.

/.idea
/.vscode
*.log
.env
/public/upload/
/tests/.phpunit.result.cache
This diff is collapsed.
<h1 align="center">
<a href="">
<code>wolfcode-blog</code> 基于 <code>webman</code> 开源博客程序
</a>
</h1>
### 程序简介
- ###### 依赖`webman`搭建【 需要 php>=7.4,建议 `php8.1` | MySQL>=5.7,建议 `MySQL8.0`
- ###### `webman` 官方文档地址:[https://www.workerman.net/doc/webman/](https://www.workerman.net/doc/webman/)
- ###### 程序默认关闭 `app_debug` ,如需调试,请自行前往根目录中的 `.env` 文件配置
- ###### 程序默认静默安装数据库,默认数据库名 `my_blog_2022`
### 配置说明
- ###### 请先配置根目录下 `.env` 文件里以 `[DB_]` 开头的数据库连接信息
- ###### 主要修改的是 `DB_USERNAME``DB_PASSWORD` 两个参数值
- ###### 如需外网访问,搭配 `Nginx` 进行反向代理即可
### 如何运行
- ###### 将代码 `下载` 或者 `git clone` 到本地,放入到预设路径(例如 `/www/wwwroot/yourpath`下)
- ###### 在 `/www/wwwroot/yourpath` 下运行 `php start.php start -d`
- ###### 相关命令的区别
- ###### 运行前请先确认php版本,可以运行 `php -v` 查看
- ###### <font color="#eee">特别提醒
- ###### 尽量不要在 `Windows` 系统下运行该程序,建议使用 `Linux` 环境运行
- ###### 程序底层已做好 `php8+` 兼容,请谨慎使用全局的 `composer update`
- ###### 不做向下版本兼容,如需要其他版本,请自行兼容开发
- ###### 如果出现 PHP Fatal error: Uncaught Error: Call to undefined function pcntl_signal(),请到 `php.ini` 配置文件中删除对应禁用函数即可,或者宝塔用户可在 php版本管理的 `禁用函数` 中自行删除 </font>
> ###### `debug` 方式运行 【 调试模式 | 用于开发调试 】
>> ```php start.php start```
> ###### `daemon` 方式运行 【 守护模式 | 用于正式环境 】
>> ```php start.php start -d```
> ###### `restart` 服务重启 【 该命令相当于先 stop 后 start 】
> > ```php start.php restart``` # 重启后进入调试模式
>
>> ```php start.php restart -d``` # 重启后进入守护模式
> ###### `reload` 代码重载 【 该命令不会中断当前服务 】
>> ```php start.php rload```
## LICENSE
- ###### GNU General Public License v3.0
- ###### 个人使用免费,商用运营请遵循协议
<?php
namespace app\admin\controller;
use app\common\controller\AdminBase;
use app\common\service\UploadService;
use app\model\SystemAdmin;
use Respect\Validation\Exceptions\ValidationException;
use support\Request;
use support\Response;
use Respect\Validation\Validator;
use think\facade\Cache;
class Ajax extends AdminBase
{
public function beforeAction()
{
$this->checkLogin = false;
parent::beforeAction();
if ('POST' !== \request()->method()) return $this->apiError('错误的请求方式');
}
public function init(): Response
{
if (!$this->checkLogin()) return $this->apiError('请先登录');
$json = file_get_contents(app_path() . DIRECTORY_SEPARATOR . 'admin' . DIRECTORY_SEPARATOR . 'extend' . DIRECTORY_SEPARATOR . 'init.json');
$result = json_decode($json, true);
return $this->apiSuccess($result);
}
public function login(Request $request): Response
{
$post = $request->post();
try {
Validator::input($post, [
'username' => Validator::alnum()->length(3, 64)->setName('用户名'),
'password' => Validator::length(6, 64)->setName('密码'),
'captcha' => Validator::length(4, 4)->setName('验证码'),
]);
} catch (ValidationException $e) {
return $this->apiError($e->getMessage());
}
$username = $post['username'];
$password = $post['password'];
$captcha = $post['captcha'];
if ($captcha != Cache::instance()->get('captcha')) return $this->apiError('验证码错误');
$where[] = ['username', '=', $username];
$admin = SystemAdmin::where($where)->findOrEmpty()->toArray();
if (empty($admin)) return $this->apiError('账号不存在');
$checkPwd = password_verify($password . $admin['salt'], $admin['password']);
if (!$checkPwd) return $this->apiError('账号/密码错误');
Cache::instance()->delete('captcha');
if ($admin['status'] != 1) return $this->apiError('当前用户状态异常');
Cache::instance()->set('admin', $admin, 3600 * 3); // 默认缓存3小时 有需要的话可以自己需改值
return $this->apiSuccess();
}
public function upload(Request $request): Response
{
$type = $request->get('type', 'default');
$file = $request->file();
$file = array_values($file)[0] ?? '';
if (empty($file)) return $this->apiError('上传失败');
$upload_config = sysConfig('upload');
$upload_type = $upload_config['upload_type'] ?? 'local';
$check = ['ext' => $file->getUploadExtension(), 'size' => $file->getSize(),];
try {
Validator::input($check, [
'ext' => Validator::in(explode(',', $upload_config['upload_allow_ext']))->setName('文件后缀'),
'size' => Validator::max($upload_config['upload_allow_size'])->setName('文件大小'),
]);
} catch (ValidationException $e) {
return $this->apiError($e->getMessage());
}
$_rs = UploadService::instance()->setConfig($upload_config)->$upload_type($file, $type);
$code = $_rs['code'] ?? 0;
if ($code == 0) {
return $this->apiError($_rs['data'] ?? '');
} else {
return $type == 'editor' ? json($_rs['data'] ?? []) : $this->apiSuccess($_rs['data'] ?? '');
}
}
}
<?php
namespace app\admin\controller;
use app\common\controller\AdminBase;
use support\Request;
use support\Response;
use think\db\exception\PDOException;
use app\model\Article as ArticleModel;
use app\model\Category as CategoryModel;
class Article extends AdminBase
{
protected array $notes = [];
protected array $category = [];
public function beforeAction()
{
parent::beforeAction();
$this->notes = ArticleModel::$notes;
$this->category = CategoryModel::getColumn([], 'title', 'id', 'id asc');
if (!$this->isLogin) {
return redirect('/admin/login/');
}
}
public function index(Request $request): Response
{
$category = $this->category;
if (empty($request->post())) return $this->admin_tpl(['notes' => $this->notes, 'category' => $this->category]);
list($page, $limit, $where) = $this->argsWhere($request);
$data = ArticleModel::getListByWith($where, '*', 'id desc', ['categoryInfo'], $page, $limit);
$count = ArticleModel::getCount($where);
$res = compact('data', 'category', 'count') + ['code' => 0];
return json($res);
}
public function edit(Request $request): Response
{
$post = $request->post();
$id = $request->get('id', 0);
$where[] = ['id', '=', $id];
if (empty($post)) {
$info = [];
if ($id) $info = ArticleModel::getOne($where);
$notes = $this->notes;
$category = $this->category;
return $this->admin_tpl(compact('id', 'info', 'notes', 'category'));
}
$post['article_date'] = $post['article_date'] ?: date('Y-m-d');
$post['desc'] = $post['desc'] ?: mb_substr(strip_tags($post['content']), 0, 100, 'utf-8');
$post['desc'] = str_replace(PHP_EOL, '', $post['desc']);
try {
if (!$id) {
$save = ArticleModel::insertDataCache($post);
} else {
$save = ArticleModel::updateDataCache($where, $post);
}
return $save ? $this->apiSuccess() : $this->apiError('操作失败或者数据没有改变');
} catch (PDOException $exception) {
return $this->apiError($exception->getMessage());
}
}
public function delete(Request $request): Response
{
$id = $request->post('id', 0);
if ($id) {
$ids = (array)$id;
} else {
$ids = $request->post('ids', []);
}
if (empty($ids)) return $this->apiError();
try {
ArticleModel::where('id', 'IN', $ids)->delete();
return $this->apiSuccess();
} catch (PDOException $exception) {
return $this->apiError($exception->getMessage());
}
}
}
<?php
namespace app\admin\controller;
use app\common\controller\AdminBase;
use Respect\Validation\Exceptions\ValidationException;
use Respect\Validation\Validator;
use support\Request;
use support\Response;
use think\db\exception\PDOException;
use app\model\Banner as BannerModel;
class Banner extends AdminBase
{
protected array $notes = [];
public function beforeAction()
{
parent::beforeAction();
$this->notes = BannerModel::$notes;
if (!$this->isLogin) {
if ('POST' == \request()->method()) return $this->apiError('当前登录状态已失效,请刷新页面重新登录');
return redirect('/admin/login/');
}
}
public function index(Request $request): Response
{
if (empty($request->post())) return $this->admin_tpl(['notes' => $this->notes]);
list($page, $limit, $where) = $this->argsWhere($request);
$data = BannerModel::getList($where, '*', 'id desc', $page, $limit);
$count = BannerModel::getCount($where);
$res = compact('data', 'count') + ['code' => 0];
return json($res);
}
public function edit(Request $request): Response
{
$post = $request->post();
$id = $request->get('id', 0);
$where[] = ['id', '=', $id];
if (empty($post)) {
$info = [];
if ($id) $info = BannerModel::getOne($where);
$notes = $this->notes;
return $this->admin_tpl(compact('id', 'info', 'notes'));
}
try {
Validator::input($post, [
'link' => Validator::url()->length(0, 255)->setName('链接'),
]);
} catch (ValidationException $e) {
return $this->apiError($e->getMessage());
}
try {
if (!$id) {
$save = BannerModel::insertDataCache($post);
} else {
$save = BannerModel::updateDataCache($where, $post);
}
return $save ? $this->apiSuccess() : $this->apiError('操作失败或者数据没有改变');
} catch (PDOException $exception) {
return $this->apiError($exception->getMessage());
}
}
public function delete(Request $request): Response
{
$id = $request->post('id', 0);
if ($id) {
$ids = (array)$id;
} else {
$ids = $request->post('ids', []);
}
if (empty($ids)) return $this->apiError();
try {
BannerModel::where('id', 'IN', $ids)->delete();
return $this->apiSuccess();
} catch (PDOException $exception) {
return $this->apiError($exception->getMessage());
}
}
}
<?php
namespace app\admin\controller;
use app\common\controller\AdminBase;
use support\Request;
use support\Response;
use think\db\exception\PDOException;
use app\model\Category as CategoryModel;
class Category extends AdminBase
{
protected array $notes = [];
public function beforeAction()
{
parent::beforeAction();
$this->notes = CategoryModel::$notes;
if (!$this->isLogin) {
if ('POST' == \request()->method()) return $this->apiError('当前登录状态已失效,请刷新页面重新登录');
return redirect('/admin/login/');
}
}
public function index(Request $request): Response
{
if (empty($request->post())) return $this->admin_tpl(['notes' => $this->notes]);
list($page, $limit, $where) = $this->argsWhere($request);
$data = CategoryModel::getList($where, '*', 'id desc', $page, $limit);
$count = CategoryModel::getCount($where);
$res = compact('data', 'count') + ['code' => 0];
return json($res);
}
public function edit(Request $request): Response
{
$post = $request->post();
$id = $request->get('id', 0);
$where[] = ['id', '=', $id];
if (empty($post)) {
$info = [];
if ($id) $info = CategoryModel::getOne($where);
$notes = $this->notes;
return $this->admin_tpl(compact('id', 'info', 'notes'));
}
try {
if (!$id) {
$save = CategoryModel::insertDataCache($post);
} else {
$save = CategoryModel::updateDataCache($where, $post);
}
return $save ? $this->apiSuccess() : $this->apiError('操作失败或者数据没有改变');
} catch (PDOException $exception) {
return $this->apiError($exception->getMessage());
}
}
public function delete(Request $request): Response
{
$id = $request->post('id', 0);
if ($id) {
$ids = (array)$id;
} else {
$ids = $request->post('ids', []);
}
if (empty($ids)) return $this->apiError();
try {
CategoryModel::where('id', 'IN', $ids)->delete();
return $this->apiSuccess();
} catch (PDOException $exception) {
return $this->apiError($exception->getMessage());
}
}
}
<?php
namespace app\admin\controller;
use app\common\controller\AdminBase;
use app\model\SystemAdmin;
use Respect\Validation\Exceptions\ValidationException;
use Respect\Validation\Validator;
use support\Request;
use support\Response;
use think\facade\Cache;
use think\helper\Str;
class Index extends AdminBase
{
public function beforeAction()
{
parent::beforeAction();
if (!$this->isLogin) {
if ('POST' == \request()->method()) return $this->apiError('当前登录状态已失效,请刷新页面重新登录');
return redirect('/admin/login/');
}
}
public function index(Request $request): Response
{
return $this->admin_tpl();
}
public function welcome(Request $request): Response
{
return $this->admin_tpl();
}
public function changePwd(Request $request): Response
{
$post = $request->post();
if (empty($post)) return $this->admin_tpl();
try {
Validator::input($post, [
'password' => Validator::length(6, 64)->setName('新密码'),
'password2' => Validator::length(6, 64)->setName('确认密码'),
]);
} catch (ValidationException $e) {
return $this->apiError($e->getMessage());
}
if ($post['password2'] !== $post['password']) return $this->apiError('两次密码不一致');
$admin = Cache::instance()->get('admin');
$salt = Str::random();
$password = adminPasswordAuth($post['password2'], $salt);
SystemAdmin::updateDataCache(['id' => $admin['id']], compact('password', 'salt'));
Cache::instance()->delete('admin');
$this->isLogin = false;
return $this->apiSuccess();
}
}
<?php
namespace app\admin\controller;
use app\common\controller\AdminBase;
use support\Request;
use support\Response;
use think\facade\Cache;
class Login extends AdminBase
{
public function beforeAction()
{
$this->checkLogin = false;
parent::beforeAction();
}
public function index(Request $request): Response
{
if ($this->checkLogin()) return redirect('/admin/index');
return $this->admin_tpl();
}
public function out(Request $request): Response
{
Cache::instance()->delete('admin');
$this->isLogin = false;
return redirect('/admin/login');
}
}
<?php
namespace app\admin\controller;
use app\common\controller\AdminBase;
use Gregwar\Captcha\CaptchaBuilder;
use Gregwar\Captcha\PhraseBuilder;
use support\Request;
use support\Response;
use think\facade\Cache;
class Out extends AdminBase
{
public function beforeAction()
{
$this->checkLogin = false;
parent::beforeAction();
}
public function captcha(Request $request): Response
{
$phraseBuilder = new PhraseBuilder(4, '0123456789');
// 初始化验证码类
$builder = new CaptchaBuilder(null, $phraseBuilder);
// 生成验证码
$builder->build();
Cache::instance()->set('captcha', strtolower($builder->getPhrase()));
// 获得验证码图片二进制数据
$img_content = $builder->get();
// 输出验证码二进制数据
return response($img_content, 200, ['Content-Type' => 'image/jpeg']);
}
}
<?php
namespace app\admin\controller;
use app\common\controller\AdminBase;
use app\model\Options;
use support\Request;
use support\Response;
use think\db\exception\PDOException;
use think\facade\Cache;
class System extends AdminBase
{
public function beforeAction()
{
parent::beforeAction();
if (!$this->isLogin) {
if ('POST' == \request()->method()) return $this->apiError('当前登录状态已失效,请刷新页面重新登录');
return redirect('/admin/login/');
}
}
public function index(Request $request): Response
{
return $this->admin_tpl();
}
public function edit(Request $request): Response
{
$type = $request->get('type', '');
if (empty($type)) return $this->apiError();
$post = $request->post();
$where[] = ['type', '=', $type];
$data = ['type' => $type, 'content' => json_encode($post, JSON_UNESCAPED_UNICODE | JSON_PRESERVE_ZERO_FRACTION | JSON_NUMERIC_CHECK),];
$info = Options::getOne($where);
try {
if ($info) Options::updateDataCache($where, $data); else Options::insertDataCache($data);
Cache::instance()->tag('sysConfig')->clear();
} catch (PDOException $exception) {
return $this->apiError($exception->getMessage());
}
return $this->apiSuccess();
}
}
{
"homeInfo": {
"title": "首页",
"href": "admin/index/welcome"
},
"menuInfo": [
{
"title": "常规管理",
"icon": "fa fa-address-book",
"href": "",
"target": "_self",
"child": [
{
"title": "博客配置",
"href": "admin/system/index",
"icon": "fa fa-cogs",
"target": "_self"
},
{
"title": "分类管理",
"href": "admin/category/index",
"icon": "fa fa-list",
"target": "_self"
},
{
"title": "文章管理",
"href": "admin/article/index",
"icon": "fa fa-book",
"target": "_self"
},
{
"title": "轮播图管理",
"href": "admin/banner/index",
"icon": "fa fa-book",
"target": "_self"
}
]
}
]
}
\ No newline at end of file
<div class="layuimini-main">
<div class="layui-form layuimini-form">
<div class="layui-form-item">
<label class="layui-form-label required">分类</label>
<div class="layui-input-block">
<select class="layui-select" name="c_id">
{volist name='category' id='vo'}
<option value="{$key}">{$vo}</option>
{/volist}
</select>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label required">标题</label>
<div class="layui-input-block">
<input type="text" name="title" lay-verify="required" value="{$info.title|default=''}" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">简介</label>
<div class="layui-input-block">
<textarea class="layui-textarea" placeholder="不填默认取内容前100字" name="desc">{$info.desc|default=''}</textarea>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">文章日期</label>
<div class="layui-input-block">
<input type="text" name="article_date" class="layui-input" id="article_date" placeholder="不选则默认当天" value="{$info.article_date|default=''}" readonly>
</div>
</div>
<!-- <div class="layui-form-item">-->
<!-- <label class="layui-form-label required">排序</label>-->
<!-- <div class="layui-input-block">-->
<!-- <input type="number" name="sort" lay-verify="required" placeholder="越大靠前" value="{$info.sort|default=1}" class="layui-input">-->
<!-- </div>-->
<!-- </div>-->
<div class="layui-form-item">
<label class="layui-form-label ">图片</label>
<div class="layui-input-block">
<div class="layui-upload">
<button type="button" class="layui-btn" id="uploadImg">上传图片</button>
<div class="layui-upload-list">
<img class="layui-upload-img" src="{$info.img|default=''}">
<input type="hidden" name="img" value="{$info.img|default=''}">
</div>
<div style="width: 95px;">
<div class="layui-progress layui-progress-big" lay-showpercent="yes" lay-filter="uploadImg-progress">
<div class="layui-progress-bar" lay-percent=""></div>
</div>
</div>
</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label required">内容</label>
<div class="layui-input-block">
<div id="content">{$info.content|default=''|raw}</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label required">状态</label>
<div class="layui-input-block">
{volist name='notes.status' id='vo'}
<input type="radio" name="status" value="{$key}" title="{$vo}" {if !empty($info) && $info.status==$key}checked=""{elseif $key==1}checked{/if}>
{/volist}
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn layui-btn-normal" lay-submit lay-filter="saveBtn">提交</button>
</div>
</div>
</div>
</div>
<script>
let index_url = (location.hash).replace('#', '');
let edit_url = index_url.replace('/index', '/edit');
let upload_url = '/admin/ajax/upload';
let id = '{$id|default=0}'
layui.use(['form', 'upload', 'element', 'laydate'], function () {
var form = layui.form, upload = layui.upload, element = layui.element, layer = layui.layer, laydate = layui.laydate, $ = layui.$;
laydate.render({
elem: '#article_date', format: 'yyyy年MM月dd日', trigger: 'click'
});
form.render();
let editor = $("#content").editor({
upload: upload_url + '?type=editor'
});
form.on('submit(saveBtn)', function (data) {
let url = edit_url + '?id=' + id
data.field.content = editor.getHtml()
fetchApi(url, data.field, true, true)
return false;
});
var uploadInst = upload.render({
elem: '#uploadImg', url: upload_url, before: function (obj) {
//预读本地文件示例,不支持ie8
obj.preview(function (index, file, result) {
$('#uploadImg').parent('div').find('.layui-upload-img').attr('src', result);
});
element.progress('uploadImg-progress', '0%'); //进度条复位
layer.msg('上传中', {icon: 16, time: 0});
}, done: function (res) {
if (res.code != '1') {
layer.msg('上传失败', {icon: 2, time: 1200});
return
}
$('#uploadImg').parent('div').find('input').attr('value', res.data.url);
}, error: function () {
layer.msg('上传失败', {icon: 2, time: 1200});
}, progress: function (n, elem, e) {
element.progress('uploadImg-progress', n + '%'); //可配合 layui 进度条元素使用
if (n == 100) layer.msg('上传完毕', {icon: 1});
}
});
});
</script>
\ No newline at end of file
<div class="layuimini-container layuimini-page-anim">
<div class="layuimini-main">
<fieldset class="table-search-fieldset">
<legend>搜索信息</legend>
<div style="margin: 10px">
<form class="layui-form layui-form-pane" action="">
<div class="layui-form-item">
<div class="layui-inline">
<label class="layui-form-label">ID</label>
<div class="layui-input-inline">
<input type="text" name="id" autocomplete="off" class="layui-input" data-search-op="=">
</div>
</div>
<div class="layui-inline">
<label class="layui-form-label">标题</label>
<div class="layui-input-inline">
<input type="text" name="title" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-inline">
<label class="layui-form-label">分类</label>
<div class="layui-input-inline">
<select class="layui-select" name="c_id" data-search-op="=">
<option value="">- 全部 -</option>
{volist name='category' id='vo'}
<option value="{$key}">{$vo}</option>
{/volist}
</select>
</div>
</div>
<div class="layui-inline">
<label class="layui-form-label">状态</label>
<div class="layui-input-inline">
<select class="layui-select" name="status" data-search-op="=">
<option value="">- 全部 -</option>
{volist name='notes.status' id='vo'}
<option value="{$key}">{$vo}</option>
{/volist}
</select>
</div>
</div>
<div class=" layui-inline">
<button type="submit" class="layui-btn layui-btn-normal " lay-submit lay-filter="data-search-btn"><i class="layui-icon"></i> 搜 索</button>
</div>
</div>
</form>
</div>
</fieldset>
<script type="text/html" id="toolbarDemo">
<div class="layui-btn-container">
<button class="layui-btn layui-btn-normal layui-btn-sm data-add-btn" lay-event="add"> 添加</button>
<button class="layui-btn layui-btn-sm layui-btn-danger data-delete-btn" lay-event="delete"> 删除</button>
</div>
</script>
<table class="layui-hide" id="currentTableId" lay-filter="currentTableFilter"></table>
<script type="text/html" id="currentTableBar">
<a class="layui-btn layui-btn-normal layui-btn-xs data-count-edit" lay-event="edit">编辑</a>
<a class="layui-btn layui-btn-xs layui-btn-danger data-count-delete" lay-event="delete">删除</a>
</script>
</div>
</div>
<script>
let index_url = (location.hash).replace('#', '');
let edit_url = index_url.replace('/index', '/edit');
let delete_url = index_url.replace('/index', '/delete');
let status_json = '{$notes.status|json_encode|raw}';
let category_json = '{$category|json_encode|raw}';
layui.use(['form', 'table', 'miniPage', 'element'], function () {
let $ = layui.jquery, form = layui.form, table = layui.table, miniPage = layui.miniPage;
form.render()
table.render({
elem: '#currentTableId',
url: index_url,
method: 'post',
toolbar: '#toolbarDemo',
defaultToolbar: ['filter', 'exports', 'print', {
title: '提示',
layEvent: 'LAYTABLE_TIPS',
icon: 'layui-icon-tips'
}],
cols: [[
{type: "checkbox", width: 50},
{field: 'id', width: 80, title: 'ID'},
{field: 'title', minWidth: 200, title: '标题'},
{
field: 'c_id', minWidth: 100, title: '分类', templet: function (res) {
let category = JSON.parse(category_json)
return category[res.c_id]
}
},
{
field: 'img', minWidth: 150, title: '图片', templet: function (res) {
return res.img ? '<img src="' + res.img + '">' : ''
}
},
{field: 'article_date', minWidth: 80, title: '文章日期'},
{
field: 'status', minWidth: 80, title: '状态', templet: function (res) {
let status = JSON.parse(status_json)
return status[res.status]
}
},
{field: 'c_time', minWidth: 135, title: '创建时间'},
{title: '操作', minWidth: 150, toolbar: '#currentTableBar', align: "center"}
]],
limits: [10, 15, 20, 25, 50, 100],
limit: 15,
page: true,
skin: 'line'
});
// 监听搜索操作
form.on('submit(data-search-btn)', function (data) {
let dataField = data.field;
let formatFilter = {}, formatOp = {};
$.each(dataField, function (key, val) {
if (val !== '') {
formatFilter[key] = val;
let op = $('input[name=' + key + ']').attr('data-search-op');
op = op || '%*%';
formatOp[key] = op;
}
});
table.reload('currentTableId', {
page: {curr: 1}, where: {
filter: JSON.stringify(formatFilter),
op: JSON.stringify(formatOp)
}
}, 'data');
return false;
});
/**
* toolbar事件监听
*/
table.on('toolbar(currentTableFilter)', function (obj) {
if (obj.event === 'add') { // 监听添加操作
let content = miniPage.getHrefContent(edit_url);
let index = layer.open({
title: '新增',
type: 1,
shade: 0.2,
maxmin: true,
shadeClose: true,
area: getArea(),
content: content,
});
$(window).on("resize", function () {
layer.full(index);
});
} else if (obj.event === 'delete') { // 监听删除操作
let checkStatus = table.checkStatus('currentTableId'), data = checkStatus.data;
let ids = []
$.each(data, function (key, val) {
ids[key] = val.id;
});
if (ids.length < 1) return false
layer.confirm('真的删除么', function (index) {
fetchApi(delete_url, {ids: ids}, true, true)
});
}
});
//监听表格复选框选择
table.on('checkbox(currentTableFilter)', function (obj) {
console.log(obj)
});
table.on('tool(currentTableFilter)', function (obj) {
let data = obj.data;
if (obj.event === 'edit') {
let content = miniPage.getHrefContent(edit_url + '?id=' + data.id);
let index = layer.open({
title: '编辑',
type: 1,
shade: 0.2,
maxmin: true,
shadeClose: true,
area: getArea(),
content: content,
});
$(window).on("resize", function () {
layer.full(index);
});
return false;
} else if (obj.event === 'delete') {
layer.confirm('真的删除行么', function (index) {
obj.del();
fetchApi(delete_url, obj.data)
layer.close(index);
});
}
});
});
</script>
\ No newline at end of file
<div class="layuimini-main">
<div class="layui-form layuimini-form">
<div class="layui-form-item">
<label class="layui-form-label required">标题</label>
<div class="layui-input-block">
<input type="text" name="title" lay-verify="required" value="{$info.title|default=''}" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">简介</label>
<div class="layui-input-block">
<textarea class="layui-textarea" placeholder="可以为空" name="desc">{$info.desc|default=''}</textarea>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label required">排序</label>
<div class="layui-input-block">
<input type="number" name="sort" lay-verify="required" placeholder="越大靠前" value="{$info.sort|default=1}" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label ">图片</label>
<div class="layui-input-block">
<div class="layui-upload">
<button type="button" class="layui-btn" id="uploadImg">上传图片</button>
<div class="layui-upload-list">
<img class="layui-upload-img" src="{$info.img|default=''}">
<input type="hidden" name="img" value="{$info.img|default=''}">
</div>
<div style="width: 95px;">
<div class="layui-progress layui-progress-big" lay-showpercent="yes" lay-filter="uploadImg-progress">
<div class="layui-progress-bar" lay-percent=""></div>
</div>
</div>
</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label required">链接</label>
<div class="layui-input-block">
<input type="text" name="link" lay-verify="required" value="{$info.link|default=''}" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label required">跳转方式</label>
<div class="layui-input-block">
{volist name='notes.target' id='vo'}
<input type="radio" name="target" value="{$key}" title="{$vo}" {if !empty($info) && $info.target==$key}checked=""{elseif $key==1}checked{/if}>
{/volist}
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label required">状态</label>
<div class="layui-input-block">
{volist name='notes.status' id='vo'}
<input type="radio" name="status" value="{$key}" title="{$vo}" {if !empty($info) && $info.status==$key}checked=""{elseif $key==1}checked{/if}>
{/volist}
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn layui-btn-normal" lay-submit lay-filter="saveBtn">提交</button>
</div>
</div>
</div>
</div>
<script>
let index_url = (location.hash).replace('#', '');
let edit_url = index_url.replace('/index', '/edit');
let upload_url = '/admin/ajax/upload';
let id = '{$id|default=0}'
layui.use(['form', 'upload', 'element', 'laydate'], function () {
var form = layui.form, upload = layui.upload, element = layui.element, layer = layui.layer, laydate = layui.laydate, $ = layui.$;
laydate.render({
elem: '#article_date', format: 'yyyy年MM月dd日', trigger: 'click'
});
form.render();
form.on('submit(saveBtn)', function (data) {
let url = edit_url + '?id=' + id
fetchApi(url, data.field, true, true)
return false;
});
var uploadInst = upload.render({
elem: '#uploadImg', url: upload_url, before: function (obj) {
//预读本地文件示例,不支持ie8
obj.preview(function (index, file, result) {
$('#uploadImg').parent('div').find('.layui-upload-img').attr('src', result);
});
element.progress('uploadImg-progress', '0%'); //进度条复位
layer.msg('上传中', {icon: 16, time: 0});
}, done: function (res) {
if (res.code != '1') {
layer.msg('上传失败', {icon: 2, time: 1200});
return
}
$('#uploadImg').parent('div').find('input').attr('value', res.data.url);
}, error: function () {
layer.msg('上传失败', {icon: 2, time: 1200});
}, progress: function (n, elem, e) {
element.progress('uploadImg-progress', n + '%'); //可配合 layui 进度条元素使用
if (n == 100) layer.msg('上传完毕', {icon: 1});
}
});
});
</script>
\ No newline at end of file
<div class="layuimini-container layuimini-page-anim">
<div class="layuimini-main">
<fieldset class="table-search-fieldset">
<legend>搜索信息</legend>
<div style="margin: 10px">
<form class="layui-form layui-form-pane" action="">
<div class="layui-form-item">
<div class="layui-inline">
<label class="layui-form-label">ID</label>
<div class="layui-input-inline">
<input type="text" name="id" autocomplete="off" class="layui-input" data-search-op="=">
</div>
</div>
<div class="layui-inline">
<label class="layui-form-label">标题</label>
<div class="layui-input-inline">
<input type="text" name="title" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-inline">
<label class="layui-form-label">状态</label>
<div class="layui-input-inline">
<select class="layui-select" name="status" data-search-op="=">
<option value="">- 全部 -</option>
{volist name='notes.status' id='vo'}
<option value="{$key}">{$vo}</option>
{/volist}
</select>
</div>
</div>
<div class=" layui-inline">
<button type="submit" class="layui-btn layui-btn-normal " lay-submit lay-filter="data-search-btn"><i class="layui-icon"></i> 搜 索</button>
</div>
</div>
</form>
</div>
</fieldset>
<script type="text/html" id="toolbarDemo">
<div class="layui-btn-container">
<button class="layui-btn layui-btn-normal layui-btn-sm data-add-btn" lay-event="add"> 添加</button>
<button class="layui-btn layui-btn-sm layui-btn-danger data-delete-btn" lay-event="delete"> 删除</button>
</div>
</script>
<table class="layui-hide" id="currentTableId" lay-filter="currentTableFilter"></table>
<script type="text/html" id="currentTableBar">
<a class="layui-btn layui-btn-normal layui-btn-xs data-count-edit" lay-event="edit">编辑</a>
<a class="layui-btn layui-btn-xs layui-btn-danger data-count-delete" lay-event="delete">删除</a>
</script>
</div>
</div>
<script>
let index_url = (location.hash).replace('#', '');
let edit_url = index_url.replace('/index', '/edit');
let delete_url = index_url.replace('/index', '/delete');
let status_json = '{$notes.status|json_encode|raw}';
layui.use(['form', 'table', 'miniPage', 'element'], function () {
let $ = layui.jquery, form = layui.form, table = layui.table, miniPage = layui.miniPage;
form.render()
table.render({
elem: '#currentTableId',
url: index_url,
method: 'post',
toolbar: '#toolbarDemo',
defaultToolbar: ['filter', 'exports', 'print', {
title: '提示',
layEvent: 'LAYTABLE_TIPS',
icon: 'layui-icon-tips'
}],
cols: [[
{type: "checkbox", width: 50},
{field: 'id', width: 80, title: 'ID'},
{field: 'title', minWidth: 80, title: '标题'},
{
field: 'img', minWidth: 150, title: '图片', templet: function (res) {
return res.img ? '<img src="' + res.img + '">' : ''
}
},
{field: 'link', minWidth: 80, title: '链接'},
{field: 'sort', minWidth: 80, title: '排序'},
{
field: 'status', minWidth: 80, title: '状态', templet: function (res) {
let status = JSON.parse(status_json)
return status[res.status]
}
},
{field: 'c_time', minWidth: 135, title: '创建时间'},
{title: '操作', minWidth: 150, toolbar: '#currentTableBar', align: "center"}
]],
limits: [10, 15, 20, 25, 50, 100],
limit: 15,
page: true,
skin: 'line'
});
// 监听搜索操作
form.on('submit(data-search-btn)', function (data) {
let dataField = data.field;
let formatFilter = {}, formatOp = {};
$.each(dataField, function (key, val) {
if (val !== '') {
formatFilter[key] = val;
let op = $('input[name=' + key + ']').attr('data-search-op');
op = op || '%*%';
formatOp[key] = op;
}
});
table.reload('currentTableId', {
page: {curr: 1}, where: {
filter: JSON.stringify(formatFilter),
op: JSON.stringify(formatOp)
}
}, 'data');
return false;
});
/**
* toolbar事件监听
*/
table.on('toolbar(currentTableFilter)', function (obj) {
if (obj.event === 'add') { // 监听添加操作
let content = miniPage.getHrefContent(edit_url);
let index = layer.open({
title: '新增',
type: 1,
shade: 0.2,
maxmin: true,
shadeClose: true,
area: getArea(),
content: content,
});
$(window).on("resize", function () {
layer.full(index);
});
} else if (obj.event === 'delete') { // 监听删除操作
let checkStatus = table.checkStatus('currentTableId'), data = checkStatus.data;
let ids = []
$.each(data, function (key, val) {
ids[key] = val.id;
});
if (ids.length < 1) return false
layer.confirm('真的删除么', function (index) {
fetchApi(delete_url, {ids: ids}, true, true)
});
}
});
//监听表格复选框选择
table.on('checkbox(currentTableFilter)', function (obj) {
console.log(obj)
});
table.on('tool(currentTableFilter)', function (obj) {
let data = obj.data;
if (obj.event === 'edit') {
let content = miniPage.getHrefContent(edit_url + '?id=' + data.id);
let index = layer.open({
title: '编辑',
type: 1,
shade: 0.2,
maxmin: true,
shadeClose: true,
area: getArea(),
content: content,
});
$(window).on("resize", function () {
layer.full(index);
});
return false;
} else if (obj.event === 'delete') {
layer.confirm('真的删除行么', function (index) {
obj.del();
fetchApi(delete_url, obj.data)
layer.close(index);
});
}
});
});
</script>
\ No newline at end of file
<div class="layuimini-main">
<div class="layui-form layuimini-form">
<div class="layui-form-item">
<label class="layui-form-label required">标题</label>
<div class="layui-input-block">
<input type="text" name="title" lay-verify="required" value="{$info.title|default=''}" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label required">排序</label>
<div class="layui-input-block">
<input type="number" name="sort" lay-verify="required" placeholder="越大靠前" value="{$info.sort|default=1}" class="layui-input">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label required">状态</label>
<div class="layui-input-block">
{volist name='notes.status' id='vo'}
<input type="radio" name="status" value="{$key}" title="{$vo}" {if !empty($info) && $info.status==$key}checked=""{elseif $key==1}checked{/if}>
{/volist}
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn layui-btn-normal" lay-submit lay-filter="saveBtn">提交</button>
</div>
</div>
</div>
</div>
<script>
let index_url = (location.hash).replace('#', '');
let edit_url = index_url.replace('/index', '/edit');
let id = '{$id|default=0}'
layui.use(['form', 'table'], function () {
var form = layui.form, layer = layui.layer, table = layui.table, $ = layui.$;
form.render();
form.on('submit(saveBtn)', function (data) {
let url = edit_url + '?id=' + id
fetchApi(url, data.field, true, true)
return false;
});
});
</script>
\ No newline at end of file
<div class="layuimini-container layuimini-page-anim">
<div class="layuimini-main">
<fieldset class="table-search-fieldset">
<legend>搜索信息</legend>
<div style="margin: 10px">
<form class="layui-form layui-form-pane" action="">
<div class="layui-form-item">
<div class="layui-inline">
<label class="layui-form-label">ID</label>
<div class="layui-input-inline">
<input type="text" name="id" autocomplete="off" class="layui-input" data-search-op="=">
</div>
</div>
<div class="layui-inline">
<label class="layui-form-label">标题</label>
<div class="layui-input-inline">
<input type="text" name="title" autocomplete="off" class="layui-input">
</div>
</div>
<div class="layui-inline">
<label class="layui-form-label">状态</label>
<div class="layui-input-inline">
<select class="layui-select" name="status" data-search-op="=">
<option value="">- 全部 -</option>
{volist name='notes.status' id='vo'}
<option value="{$key}">{$vo}</option>
{/volist}
</select>
</div>
</div>
<div class=" layui-inline">
<button type="submit" class="layui-btn layui-btn-normal " lay-submit lay-filter="data-search-btn"><i class="layui-icon"></i> 搜 索</button>
</div>
</div>
</form>
</div>
</fieldset>
<script type="text/html" id="toolbarDemo">
<div class="layui-btn-container">
<button class="layui-btn layui-btn-normal layui-btn-sm data-add-btn" lay-event="add"> 添加</button>
<button class="layui-btn layui-btn-sm layui-btn-danger data-delete-btn" lay-event="delete"> 删除</button>
</div>
</script>
<table class="layui-hide" id="currentTableId" lay-filter="currentTableFilter"></table>
<script type="text/html" id="currentTableBar">
<a class="layui-btn layui-btn-normal layui-btn-xs data-count-edit" lay-event="edit">编辑</a>
<a class="layui-btn layui-btn-xs layui-btn-danger data-count-delete" lay-event="delete">删除</a>
</script>
</div>
</div>
<script>
let index_url = (location.hash).replace('#', '');
let edit_url = index_url.replace('/index', '/edit');
let delete_url = index_url.replace('/index', '/delete');
let status_json = '{$notes.status|json_encode|raw}';
layui.use(['form', 'table', 'miniPage', 'element'], function () {
let $ = layui.jquery, form = layui.form, table = layui.table, miniPage = layui.miniPage;
form.render()
table.render({
elem: '#currentTableId',
url: index_url,
method: 'post',
toolbar: '#toolbarDemo',
defaultToolbar: ['filter', 'exports', 'print', {
title: '提示',
layEvent: 'LAYTABLE_TIPS',
icon: 'layui-icon-tips'
}],
cols: [[
{type: "checkbox", width: 50},
{field: 'id', width: 80, title: 'ID'},
{field: 'title', minWidth: 80, title: '标题'},
{field: 'sort', minWidth: 80, title: '排序'},
{
field: 'status', minWidth: 80, title: '状态', templet: function (res) {
let status = JSON.parse(status_json)
return status[res.status]
}
},
{field: 'c_time', minWidth: 135, title: '创建时间'},
{title: '操作', minWidth: 150, toolbar: '#currentTableBar', align: "center"}
]],
limits: [10, 15, 20, 25, 50, 100],
limit: 15,
page: true,
skin: 'line'
});
// 监听搜索操作
form.on('submit(data-search-btn)', function (data) {
let dataField = data.field;
let formatFilter = {}, formatOp = {};
$.each(dataField, function (key, val) {
if (val !== '') {
formatFilter[key] = val;
let op = $('input[name=' + key + ']').attr('data-search-op');
op = op || '%*%';
formatOp[key] = op;
}
});
table.reload('currentTableId', {
page: {curr: 1}, where: {
filter: JSON.stringify(formatFilter),
op: JSON.stringify(formatOp)
}
}, 'data');
return false;
});
/**
* toolbar事件监听
*/
table.on('toolbar(currentTableFilter)', function (obj) {
if (obj.event === 'add') { // 监听添加操作
let content = miniPage.getHrefContent(edit_url);
let index = layer.open({
title: '新增',
type: 1,
shade: 0.2,
maxmin: true,
shadeClose: true,
area: getArea(),
content: content,
});
$(window).on("resize", function () {
layer.full(index);
});
} else if (obj.event === 'delete') { // 监听删除操作
let checkStatus = table.checkStatus('currentTableId'), data = checkStatus.data;
let ids = []
$.each(data, function (key, val) {
ids[key] = val.id;
});
if (ids.length < 1) return false
layer.confirm('真的删除么', function (index) {
fetchApi(delete_url, {ids: ids}, true, true)
});
}
});
//监听表格复选框选择
table.on('checkbox(currentTableFilter)', function (obj) {
console.log(obj)
});
table.on('tool(currentTableFilter)', function (obj) {
let data = obj.data;
if (obj.event === 'edit') {
let content = miniPage.getHrefContent(edit_url + '?id=' + data.id);
let index = layer.open({
title: '编辑',
type: 1,
shade: 0.2,
maxmin: true,
shadeClose: true,
area: getArea(),
content: content,
});
$(window).on("resize", function () {
layer.full(index);
});
return false;
} else if (obj.event === 'delete') {
layer.confirm('真的删除行么', function (index) {
obj.del();
fetchApi(delete_url, obj.data)
layer.close(index);
});
}
});
});
</script>
\ No newline at end of file
<div class="layuimini-container">
<div class="layuimini-main" id="app">
<div class="layui-form layuimini-form">
<div class="layui-form-item">
<label class="layui-form-label required">新密码</label>
<div class="layui-input-block">
<input type="text" name="password" class="layui-input" placeholder="请输入新密码" value="" lay-verify="required">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label required">确认新密码</label>
<div class="layui-input-block">
<input type="text" name="password2" class="layui-input" placeholder="请确认新密码" value="" lay-verify="required">
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn layui-btn-normal" lay-submit lay-filter="saveBtn">提交</button>
</div>
</div>
</div>
</div>
</div>
<script>
let edit_url = (location.hash).replace('#', '');
layui.use(['form', 'upload', 'element', 'laydate'], function () {
var form = layui.form, upload = layui.upload, element = layui.element, layer = layui.layer, laydate = layui.laydate, $ = layui.$;
form.on('submit(saveBtn)', function (data) {
let url = edit_url
fetchApi(url, data.field, true, true)
return false;
});
});
</script>
{layout name="layout" /}
\ No newline at end of file
<div class="layuimini-container">
<div class="layuimini-main">
<div class="layui-row layui-col-space15">
<div class="layui-col-md6">
<div class="layui-card">
<div class="layui-card-header"><i class="layui-icon-speaker layui-icon"></i> 特别说明</div>
<div class="layui-card-body layui-text">
<div>请勿使用IE浏览器或者IE内核浏览器,推荐</div>
<br/>
<div class="layui-row">
<a href="https://www.microsoft.com/zh-cn/edge" target="_blank">EDGE浏览器</a>
<a href="https://www.google.cn/intl/zh-CN/chrome/" target="_blank">谷歌浏览器</a>
<a href="http://www.firefox.com.cn/" target="_blank">火狐浏览器</a>
<a href="https://browser.360.cn/ee/" target="_blank">360极速浏览器</a>
<a href="https://browser.qq.com/" target="_blank">QQ浏览器</a>
</div>
<br/>
<div class="layui-red">请保管好自己的帐号密码,不要将后台地址随便分享到其他平台或者聊天工具上</div>
</div>
</div>
</div>
</div>
</div>
<div class="layuimini-main">
<div class="layui-row layui-col-space15">
<div class="layui-col-md12">
<div class="layui-card">
<div class="layui-card-header"><i class="layui-icon-about layui-icon"></i> 关于后台</div>
<div class="layui-card-body">
<p>后台采用开源 layuimini后台模板(单页版V2),尊重原作者劳动成果(MIT协议),博客系统只是在此基础上简单的修改</p>
</div>
<div class="layui-card-body">
<div class="layui-inline">
<a class="layui-btn layui-btn-normal layui-btn-danger" href="https://layui.js.cn" target="_blank">layui开发文档地址</a>
</div>
<div class="layui-inline">
<a class="layui-btn layui-btn-normal layui-bg-cyan" href="https://github.com/zhongshaofa/layuimini" target="_blank">layuimini GitHub</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
\ No newline at end of file
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>{:sysConfig('site','site_name')}-后台管理</title>
<meta name="renderer" content="webkit">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="format-detection" content="telephone=no">
<link rel="icon" href="{:sysConfig('site','site_ico')}">
<link rel="stylesheet" href="/layui/dist/css/layui.css" media="all">
<link rel="stylesheet" href="/static/css/admin/font-awesome-4.7.0/css/font-awesome.min.css" media="all">
<link rel="stylesheet" href="/static/css/admin/layuimini.css?v={$static_version}" media="all">
<link rel="stylesheet" href="/static/css/admin/default.css?v={$static_version}" media="all">
<link rel="stylesheet" href="/static/css/admin/public.css?v={$static_version}" media="all">
</head>
<body class="layui-layout-body layuimini-all">
<div class="layui-layout layui-layout-admin">
<div class="layui-header header">
<div class="layui-logo layuimini-logo layuimini-back-home">
<a href="/admin">
<img src="{:sysConfig('logo','site_admin_logo')}" alt="logo">
<h1>{:sysConfig('site','site_name')}</h1>
</a>
</div>
<div class="layuimini-header-content">
<a>
<div class="layuimini-tool"><i title="展开" class="fa fa-outdent" data-side-fold="1"></i></div>
</a>
<!--电脑端头部菜单-->
<ul class="layui-nav layui-layout-left layuimini-header-menu layuimini-menu-header-pc layuimini-pc-show">
</ul>
<!--手机端头部菜单-->
<ul class="layui-nav layui-layout-left layuimini-header-menu layuimini-mobile-show">
<li class="layui-nav-item">
<a href="javascript:;"><i class="fa fa-list-ul"></i> 选择模块</a>
<dl class="layui-nav-child layuimini-menu-header-mobile">
</dl>
</li>
</ul>
<ul class="layui-nav layui-layout-right">
<li class="layui-nav-item" lay-unselect>
<a href="/" target="_blank" data-refresh="首页"><i class="fa fa-home"></i></a>
</li>
<li class="layui-nav-item" lay-unselect>
<a href="javascript:;" data-refresh="刷新"><i class="fa fa-refresh"></i></a>
</li>
<li class="layui-nav-item mobile layui-hide-xs" lay-unselect>
<a href="javascript:;" data-check-screen="full"><i class="fa fa-arrows-alt"></i></a>
</li>
<li class="layui-nav-item layuimini-setting">
<a href="javascript:;">admin</a>
<dl class="layui-nav-child">
<dd>
<a href="javascript:;" layuimini-content-href="admin/index/changePwd" data-title="修改密码" data-icon="fa fa-gears">修改密码</a>
</dd>
<dd>
<hr>
</dd>
<dd>
<a href="/admin/login/out" class="login-out">退出登录</a>
</dd>
</dl>
</li>
</ul>
</div>
</div>
<!--无限极左侧菜单-->
<div class="layui-side layui-bg-black layuimini-menu-left">
</div>
<!--初始化加载层-->
<div class="layuimini-loader">
<div class="layuimini-loader-inner"></div>
</div>
<!--手机端遮罩层-->
<div class="layuimini-make"></div>
<!-- 移动导航 -->
<div class="layuimini-site-mobile"><i class="layui-icon"></i></div>
<div class="layui-body">
<div class="layui-card layuimini-page-header layui-hide">
<div class="layui-breadcrumb layuimini-page-title">
<a lay-href="" href="/">主页</a><span lay-separator="">/</span>
<a><cite>常规管理</cite></a><span lay-separator="">/</span>
<a><cite>系统设置</cite></a>
</div>
</div>
<div class="layuimini-content-page">
</div>
</div>
</div>
<script src="/layui/dist/layui.js" charset="utf-8"></script>
<script src="/static/js/admin/lay-config.js?v={$static_version}" charset="utf-8"></script>
<script src="/static/js/jquery.min.js" charset="utf-8"></script>
<script src="/sdeditor/editor.js?v={$static_version}" charset="utf-8"></script>
<script src="/static/js/admin/common.js?v={$static_version}" charset="utf-8"></script>
<script src="/static/js/vue.min.js" charset="utf-8"></script>
<script>
layui.use(['jquery', 'layer', 'miniAdmin'], function () {
var $ = layui.jquery, layer = layui.layer, miniAdmin = layui.miniAdmin
let options = {
iniUrl: "/admin/ajax/init", // 初始化接口
clearUrl: "/admin/ajax/clear", // 缓存清理接口
renderPageVersion: true, // 初始化页面是否加版本号
bgColorDefault: false, // 主题默认配置
multiModule: true, // 是否开启多模块
menuChildOpen: false, // 是否默认展开菜单
loadingTime: 0.3, // 初始化加载时间
pageAnim: true, // 切换菜单动画
};
miniAdmin.render(options);
});
</script>
{__CONTENT__}
</body>
</html>
\ No newline at end of file
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>后台管理-登陆</title>
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta http-equiv="Access-Control-Allow-Origin" content="*">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="format-detection" content="telephone=no">
<link rel="stylesheet" href="/layui/dist/css/layui.css" media="all">
<link rel="stylesheet" href="/static/css/admin/admin-login.css" media="all">
</head>
<body>
<div class="layui-container">
<div class="admin-login-background">
<div class="layui-form login-form">
<form class="layui-form" action="">
<div class="layui-form-item logo-title">
<h1>{:sysConfig('site','site_name')} 后台登录</h1>
</div>
<div class="layui-form-item">
<label class="layui-icon layui-icon-username"></label>
<input type="text" name="username" lay-verify="required|account" placeholder="用户名或者邮箱" autocomplete="off" class="layui-input" value="">
</div>
<div class="layui-form-item">
<label class="layui-icon layui-icon-password"></label>
<input type="password" name="password" lay-verify="required|password" placeholder="密码" autocomplete="off" class="layui-input" value="">
</div>
<div class="layui-form-item">
<label class="layui-icon layui-icon-vercode"></label>
<input type="text" name="captcha" maxlength="4" lay-verify="required|captcha" placeholder="图形验证码" autocomplete="off" class="layui-input verification captcha" value="">
<div class="captcha-img">
<img id="captchaPic" src="/admin/out/captcha" onclick="this.src='/admin/out/captcha?v='+Math.random();$(this).parents().find('input[name=captcha]').val('')">
</div>
</div>
<div class="layui-form-item">
<button class="layui-btn layui-btn layui-btn-normal layui-btn-fluid" lay-submit="" lay-filter="login">登 入</button>
</div>
</form>
</div>
</div>
</div>
<script src="/static/lib/jquery-3.4.1/jquery-3.4.1.min.js" charset="utf-8"></script>
<script src="/layui/dist/layui.js" charset="utf-8"></script>
<script>
layui.use(['form'], function () {
let form = layui.form, layer = layui.layer;
if (top.location != self.location) top.location = self.location;
form.on('submit(login)', function (data) {
let _data = data.field;
let _url = '/admin/ajax/login'
fetch(_url, {
method: 'post',
body: JSON.stringify(_data),
headers: {
'Content-Type': 'application/json'
}
}).then(function (response) {
return response.json()
}).then(function (res) {
let code = res.code || 0
if (code == '1') {
layer.msg('登录成功', {icon: 1, time: 700}, function () {
window.location = '/admin';
})
return
}
$('#captchaPic').click()
layer.msg(res.msg, {icon: 2, shade: 0.3, shadeClose: true})
})
return false;
})
})
</script>
</body>
</html>
\ No newline at end of file
<div class="layui-form layuimini-form">
<div class="layui-form-item">
<label class="layui-form-label">简介</label>
<div class="layui-input-block">
<textarea class="layui-textarea" placeholder="不填默认取内容前100字" name="about_me_desc">{:sysConfig('about','about_me_desc')}</textarea>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label required">详情</label>
<div class="layui-input-block">
<div id="content">{:sysConfig('about','about_me')}</div>
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn layui-btn-normal" lay-submit lay-filter="saveBtnAbout">提交</button>
</div>
</div>
</div>
<div class="layuimini-container">
<div class="layuimini-main" id="app">
<div class="layui-tab layui-tab-card">
<ul class="layui-tab-title">
<li class="layui-this">网站设置</li>
<li>LOGO配置</li>
<li>上传配置</li>
<li>关于我</li>
</ul>
<div class="layui-tab-content ">
<div class="layui-tab-item layui-show">
{include file="system/site" /}
</div>
<div class="layui-tab-item">
{include file="system/logo" /}
</div>
<div class="layui-tab-item">
{include file="system/upload" /}
</div>
<div class="layui-tab-item">
{include file="system/about" /}
</div>
</div>
</div>
</div>
</div>
<script>
let index_url = (location.hash).replace('#', '');
let edit_url = index_url.replace('/index', '/edit');
let upload_url = '/admin/ajax/upload';
layui.use(['form', 'upload', 'element', 'laydate', 'table'], function () {
let form = layui.form, upload = layui.upload, element = layui.element, $ = layui.$;
form.on('submit(saveBtnSite)', function (data) {
let url = edit_url + '?type=site'
fetchApi(url, data.field, true, true)
return false;
});
upload.render({
elem: '#site_uploadImg', url: upload_url, exts: 'ico', before: function (obj) {
//预读本地文件示例,不支持ie8
obj.preview(function (index, file, result) {
$('#site_uploadImg').parent('div').find('.layui-upload-img').attr('src', result);
});
element.progress('site_uploadImg-progress', '0%'); //进度条复位
layer.msg('上传中', {icon: 16, time: 0});
}, done: function (res) {
if (res.code != '1') {
layer.msg(res.msg, {icon: 2, time: 1200});
return
}
$('#site_uploadImg').parent('div').find('input').attr('value', res.data.url);
}, error: function () {
layer.msg('上传失败', {icon: 2, time: 1200});
}, progress: function (n, elem, e) {
element.progress('site_uploadImg-progress', n + '%'); //可配合 layui 进度条元素使用
if (n == 100) layer.msg('上传完毕', {icon: 1});
}
});
form.on('submit(saveBtnLogo)', function (data) {
let url = edit_url + '?type=logo'
fetchApi(url, data.field, true, true)
return false;
});
upload.render({
elem: '#logo_uploadImg', url: upload_url, before: function (obj) {
//预读本地文件示例,不支持ie8
obj.preview(function (index, file, result) {
$('#logo_uploadImg').parent('div').find('.layui-upload-img').attr('src', result);
});
element.progress('logo_uploadImg-progress', '0%'); //进度条复位
layer.msg('上传中', {icon: 16, time: 0});
}, done: function (res) {
if (res.code != '1') {
layer.msg(res.msg, {icon: 2, time: 1200});
return
}
$('#logo_uploadImg').parent('div').find('input').attr('value', res.data.url);
}, error: function () {
layer.msg('上传失败', {icon: 2, time: 1200});
}, progress: function (n, elem, e) {
element.progress('logo_uploadImg-progress', n + '%'); //可配合 layui 进度条元素使用
if (n == 100) layer.msg('上传完毕', {icon: 1});
}
});
upload.render({
elem: '#logo_uploadImg2', url: upload_url, before: function (obj) {
//预读本地文件示例,不支持ie8
obj.preview(function (index, file, result) {
$('#logo_uploadImg2').parent('div').find('.layui-upload-img').attr('src', result);
});
element.progress('logo_uploadImg2-progress', '0%'); //进度条复位
layer.msg('上传中', {icon: 16, time: 0});
}, done: function (res) {
if (res.code != '1') {
layer.msg(res.msg, {icon: 2, time: 1200});
return
}
$('#logo_uploadImg2').parent('div').find('input').attr('value', res.data.url);
}, error: function () {
layer.msg('上传失败', {icon: 2, time: 1200});
}, progress: function (n, elem, e) {
element.progress('logo_uploadImg2-progress', n + '%'); //可配合 layui 进度条元素使用
if (n == 100) layer.msg('上传完毕', {icon: 1});
}
});
let editor = $("#content").editor({
upload: upload_url + '?type=editor', height: 500
});
form.on('submit(saveBtnAbout)', function (data) {
let url = edit_url + '?type=about'
data.field.about_me = editor.getHtml()
fetchApi(url, data.field, true, true)
return false;
});
form.render();
});
</script>
\ No newline at end of file
<div class="layui-form layuimini-form">
<div class="layui-form-item">
<label class="layui-form-label">博客LOGO</label>
<div class="layui-input-block">
<div class="layui-input-block">
<div class="layuimini-upload-btn ">
<button type="button" class="layui-btn" id="logo_uploadImg">上传博客LOGO</button>
<div class="layui-upload-list">
<img class="layui-upload-img" src="{:sysConfig('logo','site_blog_logo')}">
<input type="hidden" name="site_blog_logo" value="">
</div>
<div style="width: 95px;">
<div class="layui-progress layui-progress-big" lay-showpercent="yes" lay-filter="logo_uploadImg-progress">
<div class="layui-progress-bar" lay-percent=""></div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">后台LOGO</label>
<div class="layui-input-block">
<div class="layui-input-block">
<div class="layuimini-upload-btn ">
<button type="button" class="layui-btn" id="logo_uploadImg2">上传后台LOGO</button>
<div class="layui-upload-list">
<img class="layui-upload-img" src="{:sysConfig('logo','site_admin_logo')}">
<input type="hidden" name="site_admin_logo" value="">
</div>
<div style="width: 95px;">
<div class="layui-progress layui-progress-big" lay-showpercent="yes" lay-filter="logo_uploadImg2-progress">
<div class="layui-progress-bar" lay-percent=""></div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn layui-btn-normal" lay-submit lay-filter="saveBtnLogo">提交</button>
</div>
</div>
</div>
\ No newline at end of file
<div class="layui-form layuimini-form">
<div class="layui-form-item">
<label class="layui-form-label required">博客title</label>
<div class="layui-input-block">
<input type="text" name="site_name" class="layui-input" lay-verify="required" placeholder="请输入站点名称" value="{:sysConfig('site','site_name','我的博客')}">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label required">博客keywords</label>
<div class="layui-input-block">
<input type="text" name="site_keywords" class="layui-input" lay-verify="required" placeholder="请输入站点关键词" value="{:sysConfig('site','site_keywords','博客,blog')}">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label required">博客description</label>
<div class="layui-input-block">
<input type="text" name="site_description" class="layui-input" lay-verify="required" placeholder="请输入站点描述" value="{:sysConfig('site','site_description','这是一个简单的Blog程序,欢迎你来到这里。')}">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">浏览器图标</label>
<div class="layui-input-block">
<div class="layui-upload">
<button type="button" class="layui-btn" id="site_uploadImg">上传浏览器图标,ico类型</button>
<div class="layui-upload-list">
<img class="layui-upload-img" src="{:sysConfig('site','site_ico')}">
<input type="hidden" name="site_ico" value="{:sysConfig('site','site_ico')}">
</div>
<div style="width: 95px;">
<div class="layui-progress layui-progress-big" lay-showpercent="yes" lay-filter="site_uploadImg-progress">
<div class="layui-progress-bar" lay-percent=""></div>
</div>
</div>
</div>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">版本信息</label>
<div class="layui-input-block">
<input type="text" name="site_version" class="layui-input" placeholder="请输入备案信息" value="{:sysConfig('site','site_version','1.0.0')}">
<tip>静态资源版本号,修改可以获取最新的样式</tip>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">备案信息</label>
<div class="layui-input-block">
<input type="text" name="site_icp" class="layui-input" placeholder="请输入备案信息" value="{:sysConfig('site','site_icp','闽ICP-XXXXXX')}">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">统计代码</label>
<div class="layui-input-block">
<textarea class="layui-textarea" placeholder="请输入统计代码,cnzz或者百度统计" name="site_tongji">{:sysConfig('site','site_tongji')}</textarea>
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn layui-btn-normal" lay-submit lay-filter="saveBtnSite">提交</button>
</div>
</div>
</div>
<div class="layui-form layuimini-form">
<div class="layui-form-item">
<label class="layui-form-label required">存储方式</label>
<div class="layui-input-block">
{foreach ['local'=>'本地存储','oss'=>'阿里云oss','cos'=>'腾讯云cos'] as $key=>$val}
<input type="radio" v-model="upload_type" name="upload_type" lay-filter="upload_type" value="{$key}" title="{$val}" {if $key==sysConfig('upload','upload_type')}checked=""{elseif $key=='local'}checked=""{/if}>
{/foreach}
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label required">允许类型</label>
<div class="layui-input-block">
<input type="text" name="upload_allow_ext" class="layui-input" lay-verify="required" lay-reqtext="请输入允许类型" placeholder="请输入允许类型" value="{:sysConfig('upload','upload_allow_ext','xbm,tif,pjp,svgz,jpg,jpeg,ico,tiff,gif,svg,jfif,webp,png,bmp,pjpeg,avif')}">
<tip>英文逗号做分隔符。</tip>
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label required">允许大小</label>
<div class="layui-input-block">
<input type="text" name="upload_allow_size" class="layui-input" lay-verify="required" lay-reqtext="请输入允许上传大小" placeholder="请输入允许上传大小" value="{:sysConfig('upload','upload_allow_size',10485760)}">
<tip>设置允许上传大小,10485760 等于 10M</tip>
</div>
</div>
<div class="layui-form-item" v-if="upload_type == 'oss'" v-cloak>
<label class="layui-form-label required">accessKeyId</label>
<div class="layui-input-block">
<input type="text" name="oss_access_key_id" class="layui-input" lay-verify="required" lay-reqtext="请输入公钥信息" placeholder="请输入公钥信息" value="{:sysConfig('upload','oss_access_key_id')}">
<tip>例子:XXXXshu64642THSk</tip>
</div>
</div>
<div class="layui-form-item" v-if="upload_type == 'oss'" v-cloak>
<label class="layui-form-label required">accessKeySecret</label>
<div class="layui-input-block">
<input type="text" name="oss_access_key_secret" class="layui-input" lay-verify="required" lay-reqtext="请输入私钥信息" placeholder="请输入私钥信息" value="{:sysConfig('upload','oss_access_key_secret')}">
<tip>例子:xxxxxYKkFSGGshu64642THSkmTInaIm</tip>
</div>
</div>
<div class="layui-form-item" v-if="upload_type == 'oss'" v-cloak>
<label class="layui-form-label required">endpoint</label>
<div class="layui-input-block">
<input type="text" name="oss_endpoint" class="layui-input" lay-verify="required" lay-reqtext="请输入数据中心" placeholder="请输入数据中心" value="{:sysConfig('upload','oss_endpoint')}">
<tip>例子:http://oss-cn-shenzhen.aliyuncs.com [此处请以 http:// 开头,切记]</tip>
</div>
</div>
<div class="layui-form-item" v-if="upload_type == 'oss'" v-cloak>
<label class="layui-form-label required">bucket</label>
<div class="layui-input-block">
<input type="text" name="oss_bucket" class="layui-input" lay-verify="required" lay-reqtext="请输入空间名称" placeholder="请输入空间名称" value="{:sysConfig('upload','oss_bucket')}">
<tip>例子:my-blog</tip>
</div>
</div>
<div class="layui-form-item" v-if="upload_type == 'oss'" v-cloak>
<label class="layui-form-label required">访问域名</label>
<div class="layui-input-block">
<input type="text" name="oss_domain" class="layui-input" lay-verify="required" lay-reqtext="请输入访问域名" placeholder="请输入访问域名" value="{:sysConfig('upload','oss_domain')}">
<tip>例子:my-blog.oss-cn-shenzhen.aliyuncs.com</tip>
</div>
</div>
<div class="layui-form-item" v-if="upload_type == 'cos'" v-cloak>
<label class="layui-form-label required">SecretId</label>
<div class="layui-input-block">
<input type="text" name="cos_secret_id" class="layui-input" lay-verify="required" lay-reqtext="请输入公钥信息" placeholder="请输入公钥信息" value="{:sysConfig('upload','cos_secret_id')}">
<tip>例子:AKIDta6OQCbALQGrCI6ngKwQffR3dfsfrwrfs</tip>
</div>
</div>
<div class="layui-form-item" v-if="upload_type == 'cos'" v-cloak>
<label class="layui-form-label required">SecretKey</label>
<div class="layui-input-block">
<input type="text" name="cos_secret_key" class="layui-input" lay-verify="required" lay-reqtext="请输入私钥信息" placeholder="请输入私钥信息" value="{:sysConfig('upload','cos_secret_key')}">
<tip>例子:VllEWYKtClAbpqfFdTqysXxGQM6dsfs</tip>
</div>
</div>
<div class="layui-form-item" v-if="upload_type == 'cos'" v-cloak>
<label class="layui-form-label required">region</label>
<div class="layui-input-block">
<input type="text" name="cos_region" class="layui-input" lay-verify="required" lay-reqtext="请输入存储桶地域" placeholder="请输入存储桶地域" value="{:sysConfig('upload','cos_region')}">
<tip>例子:ap-guangzhou</tip>
</div>
</div>
<div class="layui-form-item" v-if="upload_type == 'cos'" v-cloak>
<label class="layui-form-label required">bucket</label>
<div class="layui-input-block">
<input type="text" name="cos_bucket" class="layui-input" lay-verify="required" lay-reqtext="请输入存储桶名称" placeholder="请输入存储桶名称" value="{:sysConfig('upload','cos_bucket')}">
<tip>例子:xxx-1251997243</tip>
</div>
</div>
<div class="layui-form-item">
<div class="layui-input-block">
<button class="layui-btn layui-btn-normal" lay-submit lay-filter="saveBtnUpload">提交</button>
</div>
</div>
</div>
<script>
let index_url = (location.hash).replace('#', '');
let edit_url = index_url.replace('/index', '/edit');
let upload_type = "{:sysConfig('upload','upload_type')}";
if (!upload_type) upload_type = 'local'
layui.use(['form', 'upload', 'element', 'laydate', 'table'], function () {
let form = layui.form;
let app = new Vue({
el: '#app',
data: {
upload_type: upload_type
}
});
form.on("radio(upload_type)", function (data) {
app.upload_type = this.value;
});
form.on('submit(saveBtnUpload)', function (data) {
let url = edit_url + '?type=upload'
fetchApi(url, data.field, true, true)
return false;
});
});
</script>
\ No newline at end of file
<?php
namespace app\common\controller;
use support\Request;
use support\Response;
use think\facade\Cache;
class AdminBase
{
public bool $checkLogin = true;
public bool $isLogin = false;
public function beforeAction()
{
if ($this->checkLogin) return $this->checkLogin();
}
/**
* @param array $assign
* @return Response
*/
public function admin_tpl(array $assign = []): Response
{
$controllerClass = request()->controller;
$controller = strtolower(substr($controllerClass, strrpos($controllerClass, '\\') + 1));
$action_name = request()->action;
$template = $controller . '/' . $action_name;
$base_assign = [
'static_version' => sysConfig('site', 'site_version', '1.0.0'),
];
$assign = $assign + $base_assign;
return view($template, $assign);
}
protected function checkLogin(): bool
{
$admin = Cache::instance()->get('admin');
$this->isLogin = (bool)$admin;
return $this->isLogin;
}
public function apiSuccess(array $data = [], string $msg = '操作成功', int $code = 1): Response
{
$arr = compact('msg', 'code', 'data');
return json($arr);
}
public function apiError(string $msg = '操作失败', int $code = 0): Response
{
$arr = compact('msg', 'code');
return json($arr);
}
public function argsWhere(Request $request): array
{
$post = $request->post();
$page = isset($post['page']) && !empty($post['page']) ? $post['page'] : 1;
$limit = isset($post['limit']) && !empty($post['limit']) ? $post['limit'] : 15;
$filters = isset($post['filter']) && !empty($post['filter']) ? $post['filter'] : '{}';
$ops = isset($post['op']) && !empty($post['op']) ? $post['op'] : '{}';
$filters = json_decode($filters, true);
$ops = json_decode($ops, true);
$where = [];
foreach ($filters as $key => $val) {
if (in_array($key, ['page', 'limit'])) continue;
$op = isset($ops[$key]) && !empty($ops[$key]) ? $ops[$key] : '%*%';
switch (strtolower($op)) {
case '=':
$where[] = [$key, '=', $val];
break;
case '%*%':
$where[] = [$key, 'LIKE', "%{$val}%"];
break;
case '*%':
$where[] = [$key, 'LIKE', "{$val}%"];
break;
case '%*':
$where[] = [$key, 'LIKE', "%{$val}"];
break;
case 'range':
$where[] = [$key, 'BETWEEN', explode(' - ', $val)];
break;
default:
$where[] = [$key, $op, "%{$val}"];
break;
}
}
$where = array_values($where);
return [$page, $limit, $where];
}
}
\ No newline at end of file
<?php
namespace app\common\controller;
use app\model\Article;
use app\model\Category;
use support\Response;
class BlogBase
{
/**
* @param array $assign
* @return Response
*/
public function blog_tpl(array $assign = []): Response
{
$controllerClass = request()->controller;
$controller = strtolower(substr($controllerClass, strrpos($controllerClass, '\\') + 1));
$action = request()->action;
$template = $controller . '/' . $action;
$category = Category::getColumn(['status' => 1], 'title', 'id', 'sort desc,id asc');
$map[] = ['status', '=', 1];
$popular_list = Article::getList($map, 'id,title,img,desc,c_time,article_date,c_id', 'click desc,id desc', 1, 10);
$assign = $assign + compact('category', 'controller', 'action', 'popular_list');
return view($template, $assign);
}
public function jump404(): Response
{
return view('public/404');
}
}
\ No newline at end of file
<?php
namespace app\common\service;
use OSS\Core\OssException;
use OSS\OssClient;
use Qcloud\Cos\Client;
use think\helper\Str;
use Webman\File;
class UploadService
{
public static $_instance = null;
protected array $options = [];
public static function instance(): ?UploadService
{
if (!static::$_instance) static::$_instance = new static();
return static::$_instance;
}
/**
* @param array $options
* @return $this
*/
public function setConfig(array $options = []): UploadService
{
$this->options = $options;
return $this;
}
/**
* @return array
*/
public function getConfig(): array
{
return $this->options;
}
/**
* @param File $file
* @param string $base_path
* @return string
*/
protected function setFilePath(File $file, string $base_path = ''): string
{
$path = date('Ymd') . '/' . Str::random(3) . time() . Str::random() . '.' . $file->getUploadExtension();
return $base_path . $path;
}
/**
* 阿里云OSS
*
* @param File $file
* @param string $type
* @return array
*/
public function oss(File $file, string $type = ''): array
{
$config = $this->getConfig();
$accessKeyId = $config['oss_access_key_id'];
$accessKeySecret = $config['oss_access_key_secret'];
$endpoint = $config['oss_endpoint'];
$bucket = $config['oss_bucket'];
if ($file->isValid()) {
$object = $this->setFilePath($file, 'blog -static/');
try {
$ossClient = new OssClient($accessKeyId, $accessKeySecret, $endpoint);
$_rs = $ossClient->putObject($bucket, $object, file_get_contents($file->getRealPath()));
$oss_request_url = $_rs['oss - request - url'] ?? '';
if (empty($oss_request_url)) return ['code' => 0, 'data' => '上传至OSS失败'];
$oss_request_url = str_replace('http://', 'https://', $oss_request_url);
} catch (OssException | OssException $e) {
return ['code' => 0, 'data' => $e->getMessage()];
}
$data = $type == 'editor' ? ['state' => 'success', 'msg' => $oss_request_url, 'name' => ''] : ['url' => $oss_request_url];
return ['code' => 1, 'data' => $data];
}
$data = '上传失败';
return ['code' => 0, 'data' => $data];
}
/**
* 腾讯云COS
*
* @param File $file
* @param string $type
* @return array
*/
public function cos(File $file, string $type = ''): array
{
$config = $this->getConfig();
$secretId = $config['cos_secret_id']; //替换为用户的 secretId,请登录访问管理控制台进行查看和管理,https://console.cloud.tencent.com/cam/capi
$secretKey = $config['cos_secret_key']; //替换为用户的 secretKey,请登录访问管理控制台进行查看和管理,https://console.cloud.tencent.com/cam/capi
$region = $config['cos_region']; //替换为用户的 region,已创建桶归属的region可以在控制台查看,https://console.cloud.tencent.com/cos5/bucket
if ($file->isValid()) {
$cosClient = new Client(
[
'region' => $region,
'schema' => 'http',
'credentials' => ['secretId' => $secretId, 'secretKey' => $secretKey,
],
]);
try {
$object = $this->setFilePath($file, 'blog-static/');
$result = $cosClient->upload(
$config['cos_bucket'], //存储桶名称,由BucketName-Appid 组成,可以在COS控制台查看 https://console.cloud.tencent.com/cos5/bucket
$object, //此处的 key 为对象键
file_get_contents($file->getRealPath())
);
$location = $result['Location'] ?? '';
if (empty($location)) return ['code' => 0, 'data' => '上传至COS失败'];
$location = 'https://' . $location;
} catch (\Exception $e) {
return ['code' => 0, 'data' => $e->getMessage()];
}
$data = $type == 'editor' ? ['state' => 'success', 'msg' => $location, 'name' => ''] : ['url' => $location];
return ['code' => 1, 'data' => $data];
}
$data = '上传失败';
return ['code' => 0, 'data' => $data];
}
/**
* 本地存储
*
* @param File $file
* @param string $type
* @return array
*/
public function local(File $file, string $type = ''): array
{
if ($file->isValid()) {
$base_path = '/upload/';
$file_path = $this->setFilePath($file);
$file_url = $base_path . $file_path;
$file->move(public_path() . $file_url);
$data = $type == 'editor' ? ['state' => 'success', 'msg' => $file_url, 'name' => ''] : ['url' => $file_url];
return ['code' => 1, 'data' => $data];
}
$data = '上传失败';
return ['code' => 0, 'data' => $data];
}
}
\ No newline at end of file
<?php
namespace app\controller;
use app\common\controller\BlogBase;
use app\model\Article;
use app\model\Banner;
use JasonGrimes\Paginator;
use support\Request;
use support\Response;
class Category extends BlogBase
{
public function index(Request $request, $id = 0, $page = 1): Response
{
if (empty($id)) return $this->jump404();
if ($id == 3) return redirect('https://www.wolfcode.net');
$map[] = ['status', '=', 1];
$map[] = ['id', '=', $id];
$categoryInfo = \app\model\Category::getOne($map);
if (empty($categoryInfo)) return $this->jump404();
$limit = 10;
$where[] = ['status', '=', 1];
$where[] = ['c_id', '=', $id];
$list = Article::getList($where, 'id,title,img,desc,c_time,article_date,c_id', 'id desc', $page, $limit);
if (empty($list)) return redirect("/category/{$id}/");
$paginator = new Paginator(Article::getCount($where), $limit, $page, "/category/{$id}/page/(:num)/");
$paginator->setPreviousText('上页');
$paginator->setNextText('下页');
$paginator->setMaxPagesToShow(5);
return $this->blog_tpl(compact('list', 'paginator', 'categoryInfo'));
}
}
<?php
namespace app\controller;
use app\common\controller\BlogBase;
use app\model\Article;
use app\model\Banner;
use JasonGrimes\Paginator;
use support\Request;
use support\Response;
class Index extends BlogBase
{
public function index(Request $request, $page = 1): Response
{
$limit = 10;
$where[] = ['status', '=', 1];
$list = Article::getList($where, 'id,title,img,desc,c_time,article_date,c_id', 'article_date desc,id desc', $page, $limit);
if (empty($list)) return redirect('/');
$banner = Banner::getList($where, 'id,title,img,link,target,desc', 'id desc', 1, 10);
$paginator = new Paginator(Article::getCount($where), $limit, $page, '/page/(:num)/');
$paginator->setPreviousText('上页');
$paginator->setNextText('下页');
$paginator->setMaxPagesToShow(5);
return $this->blog_tpl(compact('list', 'banner', 'paginator'));
}
}
<?php
namespace app\controller;
use app\common\controller\BlogBase;
use app\model\Article;
use support\Request;
use support\Response;
class Info extends BlogBase
{
public function index(Request $request, $id = 0): Response
{
if (empty($id)) return $this->jump404();
$where[] = ['status', '=', 1];
$where[] = ['id', '=', $id];
$info = Article::getOne($where, 'id,title,img,desc,c_time,article_date,c_id,content,click');
if (empty($info)) return $this->jump404();
Article::incClick($id); // 增加 点击 次数
$map[] = ['status', '=', 1];
$map[] = ['id', '<>', $id];
$map[] = ['c_id', '=', $info['c_id']];
$more = Article::getList($map, 'id,title,img,desc,c_time,article_date,c_id', 'article_date desc,id desc', 1, 6);
return $this->blog_tpl(compact('info', 'more'));
}
public function about_me(Request $request, $id = 0): Response
{
$info = sysConfig('about');
return $this->blog_tpl(compact('info'));
}
}
<?php
/**
* Here is your custom functions.
*/
function adminPasswordAuth(string $password, string $salt): string
{
return password_hash($password . $salt, PASSWORD_BCRYPT);
}
if (!function_exists('sysConfig')) {
function sysConfig($type, $name = null, $default = '')
{
$where[] = ['type', '=', $type];
$value = empty($name) ? \think\facade\Cache::instance()->get("sysConfig_{$type}") : \think\facade\Cache::instance()->get("sysConfig_{$type}_{$name}");
if (empty($value)) {
$content = \app\model\Options::where($where)->value('content');
$value = json_decode($content, true);
if (empty($value)) return $default;
if (!empty($name)) {
$value = $value[$name] ?? '';
if (empty($value)) return $default;
\think\facade\Cache::instance()->tag('sysConfig')->set("sysConfig_{$type}_{$name}", $value, 3600);
} else {
\think\facade\Cache::instance()->tag('sysConfig')->set("sysConfig_{$type}", $value, 3600);
}
}
return $value;
}
}
\ No newline at end of file
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
namespace app\middleware;
use Webman\MiddlewareInterface;
use Webman\Http\Response;
use Webman\Http\Request;
/**
* Class StaticFile
* @package app\middleware
*/
class StaticFile implements MiddlewareInterface
{
public function process(Request $request, callable $next): Response
{
// Access to files beginning with. Is prohibited
if (strpos($request->path(), '/.') !== false) {
return response('<h1>403 forbidden</h1>', 403);
}
/** @var Response $response */
$response = $next($request);
// Add cross domain HTTP header
/*$response->withHeaders([
'Access-Control-Allow-Origin' => '*',
'Access-Control-Allow-Credentials' => 'true',
]);*/
return $response;
}
}
<?php
namespace app\model;
use app\traits\ModelTrait;
use think\Model;
use think\model\relation\HasOne;
class Article extends Model
{
use ModelTrait;
public static array $notes = [
'status' => [
1 => '正常', 2 => '禁用',
],
];
public function categoryInfo(): HasOne
{
return $this->hasOne(Category::class, 'id', 'c_id')->field('id,title');
}
public static function incClick(int $id = 0, int $i = 1)
{
return self::where('id', $id)->inc('click', $i)->update();
}
}
\ No newline at end of file
<?php
namespace app\model;
use app\traits\ModelTrait;
use think\Model;
class Banner extends Model
{
use ModelTrait;
public static array $notes = [
'status' => [
1 => '正常', 2 => '禁用',
],
'target' => [
1 => '_self', 2 => '_blank',
],
];
}
\ No newline at end of file
<?php
namespace app\model;
use app\traits\ModelTrait;
use think\Model;
class Category extends Model
{
use ModelTrait;
public static array $notes = [
'status' => [
1 => '正常', 2 => '禁用',
],
];
}
\ No newline at end of file
<?php
namespace app\model;
use app\traits\ModelTrait;
use think\Model;
class Options extends Model
{
use ModelTrait;
public static array $notes = [
'status' => [
1 => '正常', 2 => '禁用',
],
];
}
\ No newline at end of file
<?php
namespace app\model;
use app\traits\ModelTrait;
use think\Model;
class SystemAdmin extends Model
{
use ModelTrait;
}
\ No newline at end of file
<?php
namespace app\model;
use support\Model;
class Test extends Model
{
/**
* The table associated with the model.
*
* @var string
*/
protected $table = 'test';
/**
* The primary key associated with the table.
*
* @var string
*/
protected $primaryKey = 'id';
/**
* Indicates if the model should be timestamped.
*
* @var bool
*/
public $timestamps = false;
}
\ No newline at end of file
<?php
declare (strict_types = 1);
namespace app\traits;
use think\facade\Cache;
trait ModelTrait
{
/**
* @return string
*/
final public static function getTableName(): string
{
return (new self())->getName();
}
/**
* @param array $where
* @param string $field
* @param int $expire
* @return array
*/
final public static function getOne(array $where, string $field = '', int $expire = 1800): array
{
return self::where($where)->field($field)->cache(true, $expire, 'model_' . self::getTableName())->findOrEmpty()->toArray();
}
/**
* @param array $where
* @param string $field
* @param int $expire
* @param null $default
* @return mixed
*/
final public static function getField(array $where, string $field = '*', int $expire = 1800, $default = null)
{
$value = self::where($where)->cache(true, $expire, 'model_' . self::getTableName())->value($field, $default);
return $value ?: 0;
}
/**
* @param array $where
* @param string $field
* @param mixed $order
* @param int $expire
* @param null $default
* @return mixed
*/
final public static function getFieldByOrder(array $where, string $field = '*', $order = '', int $expire = 1800, $default = null)
{
$value = self::where($where)->order($order)->cache(true, $expire, 'model_' . self::getTableName())->value($field, $default);
return $value ?: 0;
}
/**
* @param array $where
* @param int $expire
* @return int
*/
public static function getCount(array $where, int $expire = 1800): int
{
return self::where($where)->cache(true, $expire, 'model_' . self::getTableName())->count();
}
/**
* @param array $where
* @param string $field
* @param mixed $order
* @param int $page
* @param int $listRows
* @param int $expire
* @return array
*/
final public static function getList(array $where, string $field = '*', $order = '', int $page = 1, int $listRows = 20, int $expire = 1800)
{
return self::where($where)->field($field)->order($order)->page($page, $listRows)->cache(true, $expire, 'model_' . self::getTableName())->select()->toArray();
}
/**
* @param array $where
* @param string $field
* @param mixed $order
* @param mixed $with
* @param int $page
* @param int $listRows
* @param int $expire
* @return array
*/
final public static function getListByWith(array $where, string $field = '*', $order = '', $with = '', int $page = 1, int $listRows = 20, int $expire = 1800): array
{
return self::where($where)->with($with)->field($field)->order($order)->page($page, $listRows)->cache(true, $expire, 'model_' . self::getTableName())->select()->toArray();
}
/**
* @param array $where
* @param string $field
* @param string $key
* @param mixed $order
* @param int $page
* @param int $listRows
* @param int $expire
* @return array
*/
final public static function getColumn(array $where, string $field = '*', string $key = '', $order = '', int $page = 1, int $listRows = 20, int $expire = 120): array
{
return self::where($where)->order($order)->page($page, $listRows)->cache(true, $expire, 'model_' . self::getTableName())->column($field, $key);
}
/**
* @param array $where
* @param array $data
* @return mixed
*/
final public static function updateDataCache(array $where, array $data)
{
Cache::instance()->tag('model_' . self::getTableName())->clear();
$data['u_time'] = date('Y-m-d H:i:s');
return self::strict(false)->where($where)->update($data);
}
/**
* @param array $data
* @return int|string
*/
final public static function insertDataCache(array $data)
{
Cache::instance()->tag('model_' . self::getTableName())->clear();
$data['c_time'] = date('Y-m-d H:i:s');
return (new self())->strict(false)->insertGetId($data);
}
/**
* @return string
*/
final public static function getSql(): string
{
return (new self())->getLastSql();
}
}
\ No newline at end of file
{layout name="default" /}
<div class="akea-page-wrapper" id="akea-page-wrapper">
<div class="gdlr-core-page-builder-body">
<div class="gdlr-core-pbf-sidebar-wrapper ">
<div class="gdlr-core-pbf-sidebar-container gdlr-core-line-height-0 clearfix gdlr-core-js gdlr-core-container">
<div class="sitemap">当前位置:<a href="/">首页</a> &gt; <a href="/category/{$categoryInfo.id}/">{$categoryInfo.title}</a></div>
<div class="gdlr-core-pbf-sidebar-content gdlr-core-column-40 gdlr-core-pbf-sidebar-padding gdlr-core-line-height gdlr-core-column-extend-left">
<div class="gdlr-core-pbf-sidebar-content-inner" data-skin="Blog List">
<div class="gdlr-core-pbf-element">
<div class="gdlr-core-blog-item gdlr-core-item-pdb clearfix gdlr-core-style-blog-full">
<div class="gdlr-core-blog-item-holder gdlr-core-js-2 clearfix" data-layout="fitrows">
{volist name='list' id='vo'}
<div class="gdlr-core-item-list gdlr-core-blog-full gdlr-core-item-mglr gdlr-core-style-left">
{if $vo.img}
<div class="gdlr-core-blog-thumbnail-wrap clearfix">
<div class="gdlr-core-blog-thumbnail gdlr-core-media-image gdlr-core-opacity-on-hover gdlr-core-zoom-on-hover">
<a href="/info/{$vo.id}/">
<img src="/static/images/loading.gif" data-src="{$vo.img}" width="1000" height="486">
</a>
</div>
</div>
{/if}
<div class="gdlr-core-blog-full-content-wrap">
<div class="gdlr-core-blog-full-head clearfix">
<div class="gdlr-core-blog-full-head-right">
<h3 class="gdlr-core-blog-title gdlr-core-skin-title">
<a href="/info/{$vo.id}/">{$vo.title}</a>
</h3>
<div class="gdlr-core-blog-info-wrapper gdlr-core-skin-divider">
<div class="gdlr-core-blog-info gdlr-core-blog-info-font gdlr-core-skin-caption gdlr-core-blog-info-date">
<a href="/info/{$vo.id}/">{$vo.article_date}</a>
</div>
<div class="gdlr-core-blog-info gdlr-core-blog-info-font gdlr-core-skin-caption gdlr-core-blog-info-category">
<a href="/category/{$vo.c_id}/" rel="tag">
{$category[$vo['c_id']]|default='-'}
</a>
</div>
</div>
</div>
</div>
<div class="gdlr-core-blog-content"><a href="/info/{$vo.id}/">{$vo.desc}...</a></div>
</div>
</div>
{/volist}
</div>
<div class="gdlr-core-pagination gdlr-core-style-circle gdlr-core-right-align gdlr-core-item-pdlr">
{$paginator|default=''|raw}
</div>
</div>
</div>
</div>
</div>
<div class="gdlr-core-pbf-sidebar-right gdlr-core-column-extend-right akea-sidebar-area gdlr-core-column-20 gdlr-core-pbf-sidebar-padding gdlr-core-line-height">
<div class="gdlr-core-sidebar-item gdlr-core-item-pdlr">
{include file='public/about_me' /}
{include file='public/popular_article' /}
</div>
</div>
</div>
</div>
</div>
</div>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="{:sysConfig('site','site_ico')}">
<title>{:sysConfig('site','site_name','My Blog')}</title>
<meta name="keywords" content="{:sysConfig('site','site_name','My Blog')}"/>
<meta name="description" content="{:sysConfig('site','site_name','My Blog')}"/>
<link rel="stylesheet" href="/static/css/page-builder.css" type="text/css" media="all">
<link rel="stylesheet" href="/static/css/style-core.css" type="text/css" media="all">
<link rel="stylesheet" href="/static/css/akea-style-custom.css" type="text/css" media="all">
<link rel="stylesheet" href="/static/css/swiper-bundle.min.css" type="text/css" media="all">
<link rel="stylesheet" href="/static/css/web/public.css" type="text/css" media="all">
<link rel="stylesheet" href="/static/lib/font-awesome-4.7.0/css/font-awesome.min.css">
<script type="text/javascript" src="/static/js/jquery.min.js"></script>
</head>
<body class="home page-template-default page page-id-2039 gdlr-core-body woocommerce-no-js akea-body akea-body-front akea-full akea-with-sticky-navigation akea-blockquote-style-1 gdlr-core-link-to-lightbox">
<div class="akea-mobile-header-wrap">
<div class="akea-mobile-header akea-header-background akea-style-slide akea-sticky-mobile-navigation "
id="akea-mobile-header">
<div class="akea-mobile-header-container akea-container clearfix">
<div class="akea-logo akea-item-pdlr">
<div class="akea-logo-inner">
<a class href="/">
<img src="{:sysConfig('logo','site_blog_logo','/static/images/logo.jpg')}" width="50"/>
</a>
</div>
</div>
<div class="akea-mobile-menu-right">
<div class="akea-overlay-menu akea-mobile-menu" id="akea-mobile-menu">
<a class="akea-overlay-menu-icon akea-mobile-menu-button akea-mobile-button-hamburger" href="javascript:;">
<span></span>
</a>
<div class="akea-overlay-menu-content akea-navigation-font">
<div class="akea-overlay-menu-close">
<i class="fa fa-times fa-lg"></i>
</div>
<div class="akea-overlay-menu-row">
<div class="akea-overlay-menu-cell">
<ul id="menu-main-navigation" class="menu">
<li class="menu-item menu-item-home current-menu-item" style="transition-delay: 0ms;">
<a href="/" aria-current="page">首页</a>
</li>
<li class="menu-item menu-item-has-children" style="transition-delay: 300ms;">
<a href="javascript:;" class="akea-no-preload">分类</a>
<ul class="sub-menu" style="">
{volist name='category' id='vo'}
<li class="menu-item">
<a href="/category/{$key}/">{$vo}</a>
</li>
{/volist}
</ul>
</li>
<li class="menu-item" style="transition-delay: 600ms;"><a href="/about/me/">关于我</a></li>
</ul>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="akea-body-outer-wrapper ">
<div class="akea-body-wrapper clearfix akea-with-frame">
<header class="akea-header-wrap akea-header-style-plain akea-style-splitted-menu akea-sticky-navigation akea-style-slide">
<div class="akea-header-container akea-container">
<div class="akea-header-container-inner clearfix">
<div class="akea-navigation akea-item-pdlr clearfix ">
<div class="akea-main-menu" id="akea-main-menu">
<ul id="menu-main-navigation-1" class="sf-menu">
<li class="menu-item menu-item-home akea-normal-menu">
<a href="/" class="sf-with-ul-pre">
首页
</a>
</li>
<li class="menu-item {if $controller=='category'}current-menu-item{/if} menu-item-has-children akea-normal-menu ">
<a href="javascript:;" class="sf-with-ul-pre">
分类
</a>
<ul class="sub-menu">
{volist name='category' id='vo'}
<li class="menu-item" data-size="60">
<a href="/category/{$key}/">{$vo}</a>
</li>
{/volist}
</ul>
</li>
<li class="akea-center-nav-menu-item">
<div class="akea-above-logo">
<a href="/">
<img src="{:sysConfig('logo','site_blog_logo','/static/images/logo.jpg')}">
</a>
</div>
<div class="akea-logo akea-item-pdlr">
<div class="akea-logo-inner">
<a class href="/">{:sysConfig('site','site_name','My Blog')}</a>
</div>
</div>
</li>
<li class="menu-item akea-normal-menu {if $action=='about_me'}current-menu-item{/if}">
<a href="/about/me/">关于我</a>
</li>
</ul>
</div>
</div>
</div>
</div>
</header>
{__CONTENT__}
<footer>
<div class="akea-copyright-wrapper">
<div class="akea-copyright-container akea-container clearfix">
<div class="akea-copyright-left akea-item-pdlr">
Copyright © {:date('Y')}&nbsp;&nbsp;&nbsp;
<a href="/">{:sysConfig('site','site_name','My Blog')}</a>&nbsp;&nbsp;&nbsp;
<a href="https://beian.miit.gov.cn" target="_blank" rel="nofollow">{:sysConfig('site','site_icp')}</a>
</div>
<div class="akea-copyright-right akea-item-pdlr">
<a href="/">首页</a>&nbsp;&nbsp;&nbsp;
<a href="/about/me/">关于我</a>
</div>
</div>
</div>
</footer>
</div>
<script type="text/javascript" src="/static/js/lazyload.min.js"></script>
<script type="text/javascript" src="/static/js/web.js"></script>
{:sysConfig('site','site_tongji')}
</body>
</html>
\ No newline at end of file
{layout name="default" /}
{if !empty($banner)}
<div class="swiper mySwiper">
<div class="swiper-wrapper">
{volist name='banner' id='vo'}
<div class="swiper-slide">
<a href="{$vo.link|default='/'}" target="{$vo.target==1 ? '_self' : '_blank'}">
<div class="slide-inner" style="background-image:url({$vo.img})">
<div class="text">
<div class="title">{$vo.title|default=''}</div>
<div class="desc">{$vo.desc|default=''}</div>
</div>
</div>
</a>
</div>
{/volist}
</div>
<!-- 如果需要导航按钮 -->
<div class="swiper-button-prev"></div>
<div class="swiper-button-next"></div>
</div>
{/if}
<div class="akea-page-wrapper" id="akea-page-wrapper">
<div class="gdlr-core-page-builder-body">
<div class="gdlr-core-pbf-wrapper" id="gdlr-core-wrapper-1">
<div class="gdlr-core-pbf-background-wrap">
</div>
</div>
<div class="gdlr-core-pbf-sidebar-wrapper ">
<div class="gdlr-core-pbf-sidebar-container gdlr-core-line-height-0 clearfix gdlr-core-js gdlr-core-container">
<div class="gdlr-core-pbf-sidebar-content gdlr-core-column-40 gdlr-core-pbf-sidebar-padding gdlr-core-line-height gdlr-core-column-extend-left">
<div class="gdlr-core-pbf-sidebar-content-inner" data-skin="Blog List">
<div class="gdlr-core-pbf-element">
<div class="gdlr-core-blog-item gdlr-core-item-pdb clearfix gdlr-core-style-blog-full">
<div class="gdlr-core-blog-item-holder gdlr-core-js-2 clearfix" data-layout="fitrows">
{volist name='list' id='vo'}
<div class="gdlr-core-item-list gdlr-core-blog-full gdlr-core-item-mglr gdlr-core-style-left">
{if $vo.img}
<div class="gdlr-core-blog-thumbnail-wrap clearfix">
<div class="gdlr-core-blog-thumbnail gdlr-core-media-image gdlr-core-opacity-on-hover gdlr-core-zoom-on-hover">
<a href="/info/{$vo.id}/">
<img src="/static/images/loading.gif" data-src="{$vo.img}" width="1000" height="486">
</a>
</div>
</div>
{/if}
<div class="gdlr-core-blog-full-content-wrap">
<div class="gdlr-core-blog-full-head clearfix">
<div class="gdlr-core-blog-full-head-right">
<h3 class="gdlr-core-blog-title gdlr-core-skin-title">
<a href="/info/{$vo.id}/">{$vo.title}</a>
</h3>
<div class="gdlr-core-blog-info-wrapper gdlr-core-skin-divider">
<div class="gdlr-core-blog-info gdlr-core-blog-info-font gdlr-core-skin-caption gdlr-core-blog-info-date">
<a href="/info/{$vo.id}/">{$vo.article_date}</a>
</div>
<div class="gdlr-core-blog-info gdlr-core-blog-info-font gdlr-core-skin-caption gdlr-core-blog-info-category">
<a href="/category/{$vo.c_id}/" rel="tag">
{$category[$vo['c_id']]|default='-'}
</a>
</div>
</div>
</div>
</div>
<div class="gdlr-core-blog-content"><a href="/info/{$vo.id}/">{$vo.desc}...</a></div>
</div>
</div>
{/volist}
</div>
<div class="gdlr-core-pagination gdlr-core-style-circle gdlr-core-right-align gdlr-core-item-pdlr">
{$paginator|default=''|raw}
</div>
</div>
</div>
</div>
</div>
<div class="gdlr-core-pbf-sidebar-right gdlr-core-column-extend-right akea-sidebar-area gdlr-core-column-20 gdlr-core-pbf-sidebar-padding gdlr-core-line-height">
<div class="gdlr-core-sidebar-item gdlr-core-item-pdlr">
{include file='public/about_me' /}
{include file='public/popular_article' /}
</div>
</div>
</div>
</div>
</div>
</div>
<script type="text/javascript" src="/static/js/swiper-bundle.min.js"></script>
<script>
$(function () {
var swiper = new Swiper(".mySwiper", {
loop: true,
navigation: {
nextEl: ".swiper-button-next",
prevEl: ".swiper-button-prev",
},
});
})
</script>
\ No newline at end of file
{layout name="default" /}
<div class="akea-page-wrapper" id="akea-page-wrapper">
<div class="gdlr-core-page-builder-body">
<div class="gdlr-core-pbf-sidebar-wrapper ">
<div class="gdlr-core-pbf-sidebar-container gdlr-core-line-height-0 clearfix gdlr-core-js gdlr-core-container">
<div class="sitemap">当前位置:<a href="/">首页</a> &gt;关于我</div>
<div class="gdlr-core-pbf-sidebar-content gdlr-core-column-40 gdlr-core-pbf-sidebar-padding gdlr-core-line-height gdlr-core-column-extend-left">
<div class="gdlr-core-pbf-sidebar-content-inner" data-skin="Blog List">
<div class="gdlr-core-pbf-element">
<div class="gdlr-core-blog-item gdlr-core-item-pdb clearfix gdlr-core-style-blog-full">
<div class="gdlr-core-blog-item-holder gdlr-core-js-2 clearfix" data-layout="fitrows">
<div class="gdlr-core-item-list gdlr-core-blog-full gdlr-core-item-mglr gdlr-core-style-left">
<div class="akea-single-article-title-wrap akea-align-center">
<header class="akea-single-article-head clearfix">
<div class="akea-single-article-head-right">
<h1 class="akea-single-article-title">关于我</h1>
</div>
</header>
</div>
<div class="akea-single-article-content">{$info.about_me|raw}</div>
</div>
</div>
</div>
</div>
</div>
</div>
<div class="gdlr-core-pbf-sidebar-right gdlr-core-column-extend-right akea-sidebar-area gdlr-core-column-20 gdlr-core-pbf-sidebar-padding gdlr-core-line-height">
<div class="gdlr-core-sidebar-item gdlr-core-item-pdlr">
{include file='public/popular_article' /}
</div>
</div>
</div>
</div>
</div>
</div>
\ No newline at end of file
{layout name="default" /}
<div class="akea-page-wrapper" id="akea-page-wrapper">
<div class="gdlr-core-page-builder-body">
<div class="gdlr-core-pbf-sidebar-wrapper ">
<div class="gdlr-core-pbf-sidebar-container gdlr-core-line-height-0 clearfix gdlr-core-js gdlr-core-container">
<div class="sitemap">当前位置:<a href="/">首页</a> &gt;<a href="/category/{$info.c_id}/">{$category[$info.c_id]|default='-'}</a> &gt; {$info.title}</div>
<div class="gdlr-core-pbf-sidebar-content gdlr-core-column-40 gdlr-core-pbf-sidebar-padding gdlr-core-line-height gdlr-core-column-extend-left">
<div class="gdlr-core-pbf-sidebar-content-inner" data-skin="Blog List">
<div class="gdlr-core-pbf-element">
<div class="gdlr-core-blog-item gdlr-core-item-pdb clearfix gdlr-core-style-blog-full">
<div class="gdlr-core-blog-item-holder gdlr-core-js-2 clearfix" data-layout="fitrows">
<div class="gdlr-core-item-list gdlr-core-blog-full gdlr-core-item-mglr gdlr-core-style-left">
<div class="akea-single-article-title-wrap akea-align-center">
<header class="akea-single-article-head clearfix">
<div class="akea-single-article-head-right">
<h1 class="akea-single-article-title">{$info.title}</h1>
<div class="akea-blog-info-wrapper">
<div class="akea-blog-info akea-blog-info-font akea-blog-info-tag ">
<a href="javascript:;">{$info.article_date}</a>
</div>
<div class="akea-blog-info akea-blog-info-font akea-blog-info-tag ">
<a href="/category/{$info.c_id}">{$category[$info.c_id]}</a>
</div>
<div class="akea-blog-info akea-blog-info-font akea-blog-info-comment-number">
<span class="akea-head"><i class="fa fa-clipboard"></i></span>{$info.click}
</div>
</div>
</div>
</header>
</div>
<div class="akea-single-article-content">{$info.content|raw}</div>
</div>
</div>
</div>
</div>
</div>
<div class="akea-single-related-post-wrap akea-item-rvpdlr"><h3 class="akea-single-related-post-title akea-item-mglr">相关文章</h3>
<div class="gdlr-core-blog-item-holder clearfix">
{volist name='more' id='vo'}
<div class="gdlr-core-item-list gdlr-core-item-pdlr gdlr-core-column-30">
<div class="gdlr-core-blog-grid ">
<div class="gdlr-core-blog-thumbnail-wrap clearfix">
<div class="gdlr-core-blog-thumbnail gdlr-core-media-image gdlr-core-opacity-on-hover gdlr-core-zoom-on-hover">
<a href="/info/{$vo.id}/"><img src="/static/images/loading.gif" data-src="{$vo.img}"></a>
</div>
</div>
<div class="gdlr-core-blog-grid-content-wrap">
<h3 class="gdlr-core-blog-title gdlr-core-skin-title" id="h3_a1c3_0">
<a href="/info/{$vo.id}/">{$vo.title}</a>
</h3>
<div class="gdlr-core-blog-info-wrapper gdlr-core-skin-divider">
<span class="gdlr-core-blog-info gdlr-core-blog-info-font gdlr-core-skin-caption gdlr-core-blog-info-date">
<a href="/info/{$vo.id}/">{$vo.article_date}</a>
</span>
</div>
</div>
</div>
</div>
{/volist}
</div>
</div>
</div>
<div class="gdlr-core-pbf-sidebar-right gdlr-core-column-extend-right akea-sidebar-area gdlr-core-column-20 gdlr-core-pbf-sidebar-padding gdlr-core-line-height" data-skin="Blog List">
<div class="gdlr-core-sidebar-item gdlr-core-item-pdlr">
{include file='public/about_me' /}
{include file='public/popular_article' /}
</div>
</div>
</div>
</div>
</div>
</div>
\ No newline at end of file
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
<title>404 NotFound</title>
<link rel="stylesheet" href="/static/css/404.css">
</head>
<body>
<div class="moon"></div>
<div class="moon__crater moon__crater1"></div>
<div class="moon__crater moon__crater2"></div>
<div class="moon__crater moon__crater3"></div>
<div class="star star1"></div>
<div class="star star2"></div>
<div class="star star3"></div>
<div class="star star4"></div>
<div class="star star5"></div>
<div class="error">
<div class="error__title">404</div>
<div class="error__subtitle">Hmm...</div>
<div class="error__description">这个网页还未开发好请稍等....</div>
<a href="/">
<button class="error__button error__button--active">返回首页</button>
</a>
</div>
<div class="astronaut">
<div class="astronaut__backpack"></div>
<div class="astronaut__body"></div>
<div class="astronaut__body__chest"></div>
<div class="astronaut__arm-left1"></div>
<div class="astronaut__arm-left2"></div>
<div class="astronaut__arm-right1"></div>
<div class="astronaut__arm-right2"></div>
<div class="astronaut__arm-thumb-left"></div>
<div class="astronaut__arm-thumb-right"></div>
<div class="astronaut__leg-left"></div>
<div class="astronaut__leg-right"></div>
<div class="astronaut__foot-left"></div>
<div class="astronaut__foot-right"></div>
<div class="astronaut__wrist-left"></div>
<div class="astronaut__wrist-right"></div>
<div class="astronaut__cord">
<canvas id="cord" height="500px" width="500px"></canvas>
</div>
<div class="astronaut__head">
<canvas id="visor" width="60px" height="60px"></canvas>
<div class="astronaut__head-visor-flare1"></div>
<div class="astronaut__head-visor-flare2"></div>
</div>
</div>
<script src="/static/js/404.js"></script>
</body>
</html>
<div id="text-4" class="widget widget_text akea-widget">
<h3 class="akea-widget-title">
<span class="akea-widget-head-text">关于我</span>
</h3>
<span class="clear"></span>
<div class="textwidget">
<p>{:sysConfig('about','about_me_desc','这个人很赖,什么都没留下')}</p>
<a class="gdlr-core-button gdlr-core-button-shortcode gdlr-core-button-transparent gdlr-core-button-with-border" href="/about/me/" target="_blank" id="a_2207_10">
<span class="gdlr-core-content">了解更多</span>
</a>
</div>
</div>
\ No newline at end of file
<div id="gdlr-core-recent-post-widget-2" class="widget widget_gdlr-core-recent-post-widget akea-widget">
<h3 class="akea-widget-title">
<span class="akea-widget-head-text">热门点击</span>
</h3>
<span class="clear"></span>
<div class="gdlr-core-recent-post-widget-wrap gdlr-core-style-1">
{volist name='popular_list' id='vo'}
<div class="gdlr-core-recent-post-widget clearfix">
<a href="/info/{$vo.id}/">
<div class="gdlr-core-recent-post-widget-thumbnail gdlr-core-media-image">
<img src="{$vo.img|default='/static/images/default-popular.webp'}" alt width="150" height="150">
</div>
<div class="gdlr-core-recent-post-widget-content">
<div class="gdlr-core-recent-post-widget-title">{$vo.title}</div>
<div class="gdlr-core-recent-post-widget-info">{$vo.desc}</div>
<div class="gdlr-core-blog-info gdlr-core-blog-info-font gdlr-core-skin-caption gdlr-core-blog-info-date">
{$vo.article_date}
</div>
</div>
</a>
</div>
{/volist}
</div>
</div>
</div>
{
"name": "workerman/webman",
"type": "project",
"keywords": [
"high performance",
"http service"
],
"homepage": "http://www.workerman.net",
"license": "MIT",
"description": "High performance HTTP Service Framework.",
"authors": [
{
"name": "walkor",
"email": "walkor@workerman.net",
"homepage": "http://www.workerman.net",
"role": "Developer"
}
],
"support": {
"email": "walkor@workerman.net",
"issues": "https://github.com/walkor/webman/issues",
"forum": "http://wenda.workerman.net/",
"wiki": "http://workerman.net/doc/webman",
"source": "https://github.com/walkor/webman"
},
"require": {
"php": ">=7.2",
"workerman/webman-framework": "^1.3.0",
"monolog/monolog": "^2.0",
"webman/auto-route": "^1.0",
"webman/think-orm": "1.0.2",
"topthink/think-template": "^2.0",
"vlucas/phpdotenv": "^5.4",
"phpoption/phpoption": "^1.8",
"psr/container": "1.1.2",
"illuminate/database": "^8.83",
"webman/action-hook": "^1.0",
"gregwar/captcha": "^1.1",
"workerman/validation": "^3.0",
"webman/think-cache": "^1.0",
"aliyuncs/oss-sdk-php": "^2.4",
"qcloud/cos-sdk-v5": "^2.5",
"jasongrimes/paginator": "^1.0"
},
"suggest": {
"ext-event": "For better performance. "
},
"autoload": {
"psr-4": {
"": "./",
"App\\": "./app"
},
"files": [
"./support/helpers.php"
]
},
"scripts": {
"post-package-install": [
"support\\Plugin::install"
],
"post-package-update": [
"support\\Plugin::install"
],
"pre-package-uninstall": [
"support\\Plugin::uninstall"
]
}
}
This diff is collapsed.
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
use support\Request;
return [
'debug' => env('APP_DEBUG', true),
'default_timezone' => 'Asia/Shanghai',
'request_class' => Request::class,
'public_path' => base_path() . DIRECTORY_SEPARATOR . 'public',
'runtime_path' => base_path(false) . DIRECTORY_SEPARATOR . 'runtime',
'controller_suffix' => '',
];
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
return [
'files' => [
base_path() . '/app/functions.php',
base_path() . '/support/Request.php',
base_path() . '/support/Response.php',
]
];
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
return [
support\bootstrap\Session::class,
support\bootstrap\LaravelDb::class,
Webman\ThinkOrm\ThinkOrm::class,
Webman\ThinkCache\ThinkCache::class,
];
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
return new Webman\Container;
\ No newline at end of file
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
return [];
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
return [];
\ No newline at end of file
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
return [
'' => support\exception\Handler::class,
];
\ No newline at end of file
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
return [
'default' => [
'handlers' => [
[
'class' => Monolog\Handler\RotatingFileHandler::class,
'constructor' => [
runtime_path() . '/logs/webman.log',
7, //$maxFiles
Monolog\Logger::DEBUG,
],
'formatter' => [
'class' => Monolog\Formatter\LineFormatter::class,
'constructor' => [null, 'Y-m-d H:i:s', true],
],
]
],
],
];
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
return [];
\ No newline at end of file
<?php
return [
'enable' => true,
];
\ No newline at end of file
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
use Webman\ActionHook\ActionHook;
return [
'' => [
ActionHook::class
]
];
\ No newline at end of file
<?php
return [
'enable' => true,
];
\ No newline at end of file
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
use Webman\Route;
// 已经设置过路由的uri则忽略
$routes = Route::getRoutes();
$ignore_list = [];
foreach ($routes as $tmp_route) {
$ignore_list[$tmp_route->getPath()] = 0;
}
$suffix = config('app.controller_suffix', '');
$suffix_length = strlen($suffix);
// 递归遍历目录查找控制器自动设置路由
$dir_iterator = new \RecursiveDirectoryIterator(app_path());
$iterator = new \RecursiveIteratorIterator($dir_iterator);
foreach ($iterator as $file) {
// 忽略目录和非php文件
if (is_dir($file) || $file->getExtension() != 'php') {
continue;
}
$file_path = str_replace('\\', '/',$file->getPathname());
// 文件路径里不带controller的文件忽略
if (strpos(strtolower($file_path), '/controller/') === false) {
continue;
}
// 只处理带 controller_suffix 后缀的
if ($suffix_length && substr($file->getBaseName('.php'), -$suffix_length) !== $suffix) {
continue;
}
// 根据文件路径计算uri
$uri_path = str_replace(['/controller/', '/Controller/'], '/', substr(substr($file_path, strlen(app_path())), 0, - (4 + $suffix_length)));
// 根据文件路径是被类名
$class_name = str_replace('/', '\\',substr(substr($file_path, strlen(base_path())), 0, -4));
if (!class_exists($class_name)) {
echo "Class $class_name not found, skip route for it\n";
continue;
}
// 通过反射找到这个类的所有共有方法作为action
$class = new ReflectionClass($class_name);
$class_name = $class->name;
$methods = $class->getMethods(ReflectionMethod::IS_PUBLIC);
$route = function ($uri, $cb) use ($ignore_list) {
if (isset($ignore_list[$uri])) {
return;
}
Route::any($uri, $cb);
if ($uri !== '') {
Route::any($uri . '/', $cb);
}
$lower_uri = strtolower($uri);
if ($lower_uri !== $uri) {
Route::any($lower_uri, $cb);
Route::any($lower_uri . '/', $cb);
}
};
// 设置路由
foreach ($methods as $item) {
$action = $item->name;
if (in_array($action, ['__construct', '__destruct'])) {
continue;
}
// action为index时uri里末尾/index可以省略
if ($action === 'index') {
// controller也为index时可以uri里可以省略/index/index
if (substr($uri_path, -6) === '/index') {
$route(substr($uri_path, 0, -6), [$class_name, $action]);
}
$route($uri_path, [$class_name, $action]);
}
$route($uri_path.'/'.$action, [$class_name, $action]);
}
}
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
return [
// File update detection and automatic reload
'monitor' => [
'handler' => process\Monitor::class,
'reloadable' => false,
'constructor' => [
// Monitor these directories
'monitor_dir' => [
app_path(),
config_path(),
base_path() . '/process',
base_path() . '/support',
base_path() . '/resource',
base_path() . '/.env',
],
// Files with these suffixes will be monitored
'monitor_extensions' => [
'php', 'html', 'htm', 'env'
]
]
]
];
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
return [
'default' => [
'host' => '127.0.0.1',
'password' => null,
'port' => 6379,
'database' => 0,
],
];
<?php
/**
* This file is part of webman.
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
use Webman\Route;
Route::get('/page/1', function() {
return redirect('/');
});
Route::get('/page/1/', function() {
return redirect('/');
});
Route::get('/category/{id:\d+}/page/1', function($request, $id) {
return redirect("/category/{$id}/");
});
Route::get('/category/{id:\d+}/page/1/', function($request, $id) {
return redirect("/category/{$id}/");
});
Route::get('/page/{page:\d+}/', [app\controller\Index::class, 'index']);
Route::get('/category/{id:\d+}/', [app\controller\Category::class, 'index']);
Route::get('/category/{id:\d+}/page/{page:\d+}/', [app\controller\Category::class, 'index']);
Route::get('/info/{id:\d+}/', [app\controller\Info::class, 'index']);
Route::get('/about/me/', [app\controller\Info::class, 'about_me']);
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
return [
'listen' => 'http://0.0.0.0:8787',
'transport' => 'tcp',
'context' => [],
'name' => 'webman',
'count' => cpu_count() * 2,
'user' => '',
'group' => '',
'reusePort' => false,
'event_loop' => '',
'pid_file' => runtime_path() . '/webman.pid',
'status_file' => runtime_path() . '/webman.status',
'stdout_file' => runtime_path() . '/logs/stdout.log',
'log_file' => runtime_path() . '/logs/workerman.log',
'max_package_size' => 10 * 1024 * 1024
];
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
return [
'type' => 'file', // or redis or redis_cluster
'handler' => Webman\FileSessionHandler::class,
'config' => [
'file' => [
'save_path' => runtime_path() . '/sessions',
],
'redis' => [
'host' => '127.0.0.1',
'port' => 6379,
'auth' => '',
'timeout' => 2,
'database' => '',
'prefix' => 'redis_session_',
],
'redis_cluster' => [
'host' => ['127.0.0.1:7000', '127.0.0.1:7001', '127.0.0.1:7001'],
'timeout' => 2,
'auth' => '',
'prefix' => 'redis_session_',
]
],
'session_name' => 'PHPSID',
];
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
/**
* Static file settings
*/
return [
'enable' => true,
'middleware' => [ // Static file Middleware
//app\middleware\StaticFile::class,
],
];
\ No newline at end of file
<?php
return [
'default' => 'file',
'stores' => [
'file' => [
'type' => 'File',
// 缓存保存目录
'path' => runtime_path().'/cache/',
// 缓存前缀
'prefix' => '',
// 缓存有效期 0表示永久缓存
'expire' => 0,
],
'redis' => [
'type' => 'redis',
'host' => '127.0.0.1',
'port' => 6379,
'prefix' => '',
'expire' => 0,
],
],
];
\ No newline at end of file
<?php
use think\facade\Cache;
use think\facade\Db;
Cache::instance()->config(config('thinkcache'));
Db::setCache(Cache::instance()->store());
return [
'default' => 'mysql',
'connections' => [
'mysql' => [
// 数据库类型
'type' => 'mysql',
// 服务器地址
'hostname' => env('DB_HOST', ''),
// 数据库名
'database' => env('DB_DATABASE', ''),
// 数据库用户名
'username' => env('DB_USERNAME', ''),
// 数据库密码
'password' => env('DB_PASSWORD', ''),
// 数据库连接端口
'hostport' => env('DB_PORT', ''),
// 数据库连接参数
'params' => [],
// 数据库编码默认采用utf8
'charset' => env('DB_CHARSET', 'utf8mb4'),
// 数据库表前缀
'prefix' => '',
// 断线重连
'break_reconnect' => true,
// 关闭SQL监听日志
'trigger_sql' => true,
],
],
];
\ No newline at end of file
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
/**
* Multilingual configuration
*/
return [
// Default language
'locale' => 'zh_CN',
// Fallback language
'fallback_locale' => ['zh_CN', 'en'],
// Folder where language files are stored
'path' => base_path() . '/resource/translations',
];
\ No newline at end of file
<?php
/**
* This file is part of webman.
*
* Licensed under The MIT License
* For full copyright and license information, please see the MIT-LICENSE.txt
* Redistributions of files must retain the above copyright notice.
*
* @author walkor<walkor@workerman.net>
* @copyright walkor<walkor@workerman.net>
* @link http://www.workerman.net/
* @license http://www.opensource.org/licenses/mit-license.php MIT License
*/
use support\view\ThinkPHP;
return [
'handler' => ThinkPHP::class,
];
This diff is collapsed.
This diff is collapsed.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,minimum-scale=1,initial-scale=1">
<title>404 NotFound</title>
<link rel="stylesheet" href="/static/css/404.css">
</head>
<body>
<div class="moon"></div>
<div class="moon__crater moon__crater1"></div>
<div class="moon__crater moon__crater2"></div>
<div class="moon__crater moon__crater3"></div>
<div class="star star1"></div>
<div class="star star2"></div>
<div class="star star3"></div>
<div class="star star4"></div>
<div class="star star5"></div>
<div class="error">
<div class="error__title">404</div>
<div class="error__subtitle">Hmm...</div>
<div class="error__description">这个网页还未开发好请稍等....</div>
<a href="/">
<button class="error__button error__button--active">返回首页</button>
</a>
</div>
<div class="astronaut">
<div class="astronaut__backpack"></div>
<div class="astronaut__body"></div>
<div class="astronaut__body__chest"></div>
<div class="astronaut__arm-left1"></div>
<div class="astronaut__arm-left2"></div>
<div class="astronaut__arm-right1"></div>
<div class="astronaut__arm-right2"></div>
<div class="astronaut__arm-thumb-left"></div>
<div class="astronaut__arm-thumb-right"></div>
<div class="astronaut__leg-left"></div>
<div class="astronaut__leg-right"></div>
<div class="astronaut__foot-left"></div>
<div class="astronaut__foot-right"></div>
<div class="astronaut__wrist-left"></div>
<div class="astronaut__wrist-right"></div>
<div class="astronaut__cord">
<canvas id="cord" height="500px" width="500px"></canvas>
</div>
<div class="astronaut__head">
<canvas id="visor" width="60px" height="60px"></canvas>
<div class="astronaut__head-visor-flare1"></div>
<div class="astronaut__head-visor-flare2"></div>
</div>
</div>
<script src="/static/js/404.js"></script>
</body>
</html>
This diff is collapsed.
This diff is collapsed.
CKEditor 4
==========
Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
https://ckeditor.com - See LICENSE.md for license information.
CKEditor 4 is a text editor to be used inside web pages. It's not a replacement
for desktop text editors like Word or OpenOffice, but a component to be used as
part of web applications and websites.
## Documentation
The full editor documentation is available online at the following address:
https://ckeditor.com/docs/
## Installation
Installing CKEditor is an easy task. Just follow these simple steps:
1. **Download** the latest version from the CKEditor website:
https://ckeditor.com. You should have already completed this step, but be
sure you have the very latest version.
2. **Extract** (decompress) the downloaded file into the root of your website.
**Note:** CKEditor is by default installed in the `ckeditor` folder. You can
place the files in whichever you want though.
## Checking Your Installation
The editor comes with a few sample pages that can be used to verify that
installation proceeded properly. Take a look at the `samples` directory.
To test your installation, just call the following page at your website:
http://<your site>/<CKEditor installation path>/samples/index.html
For example:
http://www.example.com/ckeditor/samples/index.html
# Reporting a security issues
If you believe you have found a security issue in the CKEditor 4 software, please contact us immediately.
When reporting a potential security problem, please bear this in mind:
* Make sure to provide as many details as possible about the vulnerability.
* Please do not disclose publicly any security issues until we fix them and publish security releases.
Contact the security team at security@cksource.com. As soon as we receive the security report, we will work promptly to confirm the issue and then to provide a security fix.
/*
Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/
(function(a){if("undefined"==typeof a)throw Error("jQuery should be loaded before CKEditor jQuery adapter.");if("undefined"==typeof CKEDITOR)throw Error("CKEditor should be loaded before CKEditor jQuery adapter.");CKEDITOR.config.jqueryOverrideVal="undefined"==typeof CKEDITOR.config.jqueryOverrideVal?!0:CKEDITOR.config.jqueryOverrideVal;a.extend(a.fn,{ckeditorGet:function(){var a=this.eq(0).data("ckeditorInstance");if(!a)throw"CKEditor is not initialized yet, use ckeditor() with a callback.";return a},
ckeditor:function(g,e){if(!CKEDITOR.env.isCompatible)throw Error("The environment is incompatible.");if("function"!==typeof g){var m=e;e=g;g=m}var k=[];e=e||{};this.each(function(){var b=a(this),c=b.data("ckeditorInstance"),f=b.data("_ckeditorInstanceLock"),h=this,l=new a.Deferred;k.push(l.promise());if(c&&!f)g&&g.apply(c,[this]),l.resolve();else if(f)c.once("instanceReady",function(){setTimeout(function d(){c.element?(c.element.$==h&&g&&g.apply(c,[h]),l.resolve()):setTimeout(d,100)},0)},null,null,
9999);else{if(e.autoUpdateElement||"undefined"==typeof e.autoUpdateElement&&CKEDITOR.config.autoUpdateElement)e.autoUpdateElementJquery=!0;e.autoUpdateElement=!1;b.data("_ckeditorInstanceLock",!0);c=a(this).is("textarea")?CKEDITOR.replace(h,e):CKEDITOR.inline(h,e);b.data("ckeditorInstance",c);c.on("instanceReady",function(e){var d=e.editor;setTimeout(function n(){if(d.element){e.removeListener();d.on("dataReady",function(){b.trigger("dataReady.ckeditor",[d])});d.on("setData",function(a){b.trigger("setData.ckeditor",
[d,a.data])});d.on("getData",function(a){b.trigger("getData.ckeditor",[d,a.data])},999);d.on("destroy",function(){b.trigger("destroy.ckeditor",[d])});d.on("save",function(){a(h.form).trigger("submit");return!1},null,null,20);if(d.config.autoUpdateElementJquery&&b.is("textarea")&&a(h.form).length){var c=function(){b.ckeditor(function(){d.updateElement()})};a(h.form).on("submit",c);a(h.form).on("form-pre-serialize",c);b.on("destroy.ckeditor",function(){a(h.form).off("submit",c);a(h.form).off("form-pre-serialize",
c)})}d.on("destroy",function(){b.removeData("ckeditorInstance")});b.removeData("_ckeditorInstanceLock");b.trigger("instanceReady.ckeditor",[d]);g&&g.apply(d,[h]);l.resolve()}else setTimeout(n,100)},0)},null,null,9999)}});var f=new a.Deferred;this.promise=f.promise();a.when.apply(this,k).then(function(){f.resolve()});this.editor=this.eq(0).data("ckeditorInstance");return this}});CKEDITOR.config.jqueryOverrideVal&&(a.fn.val=CKEDITOR.tools.override(a.fn.val,function(g){return function(e){if(arguments.length){var m=
this,k=[],f=this.each(function(){var b=a(this),c=b.data("ckeditorInstance");if(b.is("textarea")&&c){var f=new a.Deferred;c.setData(e,function(){f.resolve()});k.push(f.promise());return!0}return g.call(b,e)});if(k.length){var b=new a.Deferred;a.when.apply(this,k).done(function(){b.resolveWith(m)});return b.promise()}return f}var f=a(this).eq(0),c=f.data("ckeditorInstance");return f.is("textarea")&&c?c.getData():g.call(f)}}))})(window.jQuery);
\ No newline at end of file
{
"bender": {
"port": 9001
},
"server": {
"port": 9002
},
"paths": {
"ckeditor4": "../ckeditor4/",
"runner": "./src/runner.html"
},
"browsers": {
"linux": [ "chrome", "firefox" ],
"macos": [ "safari" ]
}
}
This diff is collapsed.
This diff is collapsed.
/**
* @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
CKEDITOR.editorConfig = function (config) {
// Define changes to default configuration here. For example:
config.language = 'zh-cn';
config.uiColor = '#daeef5';
config.height = 350;
config.image_previewText = '';
config.filebrowserImageUploadUrl = "/admin/ajax/upload?type=editor";
};
/**
* @license Copyright (c) 2003-2022, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see https://ckeditor.com/legal/ckeditor-oss-license
*/
CKEDITOR.editorConfig = function (config) {
// Define changes to default configuration here. For example:
config.language = 'zh-cn';
config.uiColor = '#f1f1f1';
config.height = 350;
config.image_previewText = ' ';
config.filebrowserImageUploadUrl = "/admin/ajax/upload?type=editor";
};
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment