优化流量日志中间件

This commit is contained in:
taoser 2024-04-01 10:24:38 +08:00
parent a5acd6cb6e
commit 91bd9d28c2
23 changed files with 1000 additions and 245 deletions

View File

@ -38,6 +38,9 @@ class User extends AdminController
if(Request::isAjax()){
$datas = Request::only(['id','name','email','sex','status']);
$map = array_filter($datas,[$this,'filtrArr']);
if(!empty($map['id'])) {
$map['id'] = (int) $map['id'];
}
$user = Db::name('user')->where(['delete_time'=>0])->where($map)->order('id desc')->paginate([
'list_rows' => input('limit'),

View File

@ -12,6 +12,7 @@
<meta charset="utf-8">
<title>{block name="title"}TaoLerCMS后台管理系统{/block}</title>
<link rel="stylesheet" href="/static/component/pear/css/pear.css" />
{block name="head"}内容{/block}
</head>
<body class="pear-container">
{block name="body"}内容{/block}

View File

@ -10,7 +10,7 @@
<div class="layui-fluid" id="component-tabs">
<div class="layui-card">
<div class="layui-card-body">
<div class="layui-tab layui-tab-brief" lay-filter="server-tabs-brief">
<div class="layui-tab layui-tab-brief" lay-filter="server-tabs-brief" lay-allowclose="true">
<ul class="layui-tab-title">
<li lay-id="vip" class="layui-this">用户等级</li>
</ul>
@ -68,6 +68,7 @@
</div>
{:hook('signadminhook')}
</div>
</div>
</div>

View File

@ -313,14 +313,17 @@ class Article extends Model
*/
public function getAllTags($tagId)
{
$allTags = $this::hasWhere('taglist',['tag_id'=>$tagId])
$arrId = Taglist::where('tag_id', $tagId)->column('article_id');
$allTags = $this::field('id,user_id,cate_id,title,create_time,is_hot')->where('id','in', $arrId)
->with(['user' => function($query){
$query->field('id,name,nickname,user_img,area_id,vip');
},'cate' => function($query){
$query->where('delete_time',0)->field('id,catename,ename');
}])
->where(['status'=>1])
->order('pv desc')
->order('id desc')
->limit(20)
->append(['url'])
->select()
->toArray();
@ -337,14 +340,13 @@ class Article extends Model
*/
public function getRelationTags($tagId,$id,$limit)
{
$allTags = $this::hasWhere('taglist',['tag_id'=>$tagId])
->with(['user' => function($query){
$query->field('id,name,nickname,user_img,area_id,vip');
},'cate' => function($query){
$query->where('delete_time',0)->field('id,catename,ename');
}])
$arrId = Taglist::where([
['tag_id', '=', $tagId],
['article_id','<>',$id]
])->column('article_id');
$allTags = $this::field('id,cate_id,title')->where('id', 'in', $arrId)
->where(['status'=>1])
// ->where('article.id', '<>', $id)
->order('pv desc')
->limit($limit)
->append(['url'])
@ -377,7 +379,7 @@ class Article extends Model
['id', '>', $id],
['cate_id', '=', $cid],
['status', '=',1]
])->limit(1)->append(['url'])->select()->toArray();
])->order('id asc')->limit(1)->append(['url'])->select()->toArray();
return ['previous' => $previous, 'next' => $next];
}

View File

@ -55,13 +55,13 @@ class Article extends BaseController
$assignArr = [
'ename'=>$ename,
'cateinfo'=> $cateInfo,
'type'=>$type,
'artList'=>$artList,
'artHot'=>$artHot,
'path'=>$path,
'jspage'=>'jie'
'ename' => $ename,
'cateinfo' => $cateInfo,
'type' => $type,
'artList' => $artList,
'artHot' => $artHot,
'path' => $path,
'jspage' => 'jie'
];
View::assign($assignArr);
@ -96,8 +96,8 @@ class Article extends BaseController
// 标签
$tags = [];
$relationArticle = [];
$artTags = Db::name('taglist')->where('article_id',$id)->select();
$relationArticle = []; //相关帖子
$artTags = Db::name('taglist')->where('article_id', $id)->select();
if(count($artTags)) {
foreach($artTags as $v) {
$tag = Db::name('tag')->find($v['tag_id']);
@ -179,7 +179,7 @@ class Article extends BaseController
$data = Request::only(['content','article_id','pid','to_user_id']);
$data['user_id'] = $this->uid;
$sendId = $data['user_id'];
// halt($data);
$art = Db::name('article')->field('id,status,is_reply,delete_time')->find($data['article_id']);
if($art['delete_time'] != 0 || $art['status'] != 1 || $art['is_reply'] != 1){
@ -294,15 +294,16 @@ class Article extends BaseController
Db::name('user_article_log')->where('id', $postLog['id'])->inc('user_postnum')->update();
// 获取到的最新ID
$aid = $result['data']['id'];
//写入taglist表
if(!empty($tagId)) {
$tagArr = [];
if(isset($tagId)) {
$tagIdArr = explode(',',$tagId);
$tagIdArr = explode(',', $tagId);
foreach($tagIdArr as $tid) {
$tagArr[] = ['article_id'=>$aid,'tag_id'=>$tid,'create_time'=>time()];
}
$tagArr[] = [ 'article_id' => $aid, 'tag_id' => $tid, 'create_time'=>time()];
}
Db::name('taglist')->insertAll($tagArr);
}
// 清除文章tag缓存
Cache::tag('tagArtDetail')->clear();
@ -385,11 +386,11 @@ class Article extends BaseController
$result = $article->edit($data);
if($result == 1) {
//处理标签
$artTags = Db::name('taglist')->where('article_id',$id)->column('tag_id','id');
if(isset($tagId)) {
if(!empty($tagId)) {
$tagIdArr = explode(',',$tagId);
$artTags = Db::name('taglist')->where('article_id',$id)->column('tag_id','id');
foreach($artTags as $aid => $tid) {
if(!in_array($tid,$tagIdArr)){
if(!in_array($tid, $tagIdArr)){
//删除被取消的tag
Db::name('taglist')->delete($aid);
}

View File

@ -29,6 +29,8 @@ class Index extends BaseController
*/
public function index()
{
// $ip = file_get_contents('https://myip.ipip.net');
// echo "My public IP address is: " . $ip;
$types = input('type');
//置顶文章
$artTop = Article::getArtTop(5);

View File

@ -15,7 +15,6 @@ use app\facade\Article;
use think\facade\View;
use think\facade\Request;
use think\facade\Db;
use app\common\model\Slider;
use app\common\model\Tag as TagModel;
class Tag extends BaseController
@ -31,22 +30,20 @@ class Tag extends BaseController
{
//
$tagEname = Request::param('ename');
$tag = Db::name('tag')->where('ename',$tagEname)->find();
$tag = Db::name('tag')->where('ename', $tagEname)->find();
$artList = Article::getAllTags($tag['id']);
$slider = new Slider();
//首页右栏
$ad_comm = $slider->getSliderList(2);
// 查询热议
$artHot = Article::getArtHot(10);
$assign = [
'tag'=>$tag,
'artList'=>$artList,
'ad_comm'=>$ad_comm,
'artHot'=>$artHot,
'jspage'=>''
'tag' => $tag,
'artList' => $artList,
'artHot' => $artHot,
'jspage' => ''
];
View::assign($assign);
return View::fetch('index');
}

View File

@ -19,7 +19,7 @@ class Auth
$request->uid = $data->uid;
} catch(\Exception $e) {
return $e->getMessage();
return json(['code' => -1, 'msg' => $e->getMessage()]);
}
} else {

View File

@ -1,17 +1,17 @@
<?php
namespace app\middleware;
use think\facade\Request;
use think\facade\Log;
class Browse
{
public function handle($request, \Closure $next)
{
$agent = $_SERVER['HTTP_USER_AGENT'];
$ip = $_SERVER['REMOTE_ADDR'];
$url = Request::url(true);
$agent = $request->header('user-agent');
$ip = $request->ip();
$url = $request->url(true);
Log::channel('browse')->info('browse:{agent} {ip} {url}',['agent'=>$agent,'url'=>$url,'ip'=>$ip]);
return $next($request);
}
}

26
composer.lock generated
View File

@ -859,16 +859,16 @@
},
{
"name": "laravel/serializable-closure",
"version": "v1.3.2",
"version": "v1.3.3",
"source": {
"type": "git",
"url": "https://github.com/laravel/serializable-closure.git",
"reference": "076fe2cf128bd54b4341cdc6d49b95b34e101e4c"
"reference": "3dbf8a8e914634c48d389c1234552666b3d43754"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/serializable-closure/zipball/076fe2cf128bd54b4341cdc6d49b95b34e101e4c",
"reference": "076fe2cf128bd54b4341cdc6d49b95b34e101e4c",
"url": "https://api.github.com/repos/laravel/serializable-closure/zipball/3dbf8a8e914634c48d389c1234552666b3d43754",
"reference": "3dbf8a8e914634c48d389c1234552666b3d43754",
"shasum": ""
},
"require": {
@ -915,7 +915,7 @@
"issues": "https://github.com/laravel/serializable-closure/issues",
"source": "https://github.com/laravel/serializable-closure"
},
"time": "2023-10-17T13:38:16+00:00"
"time": "2023-11-08T14:08:06+00:00"
},
{
"name": "league/flysystem",
@ -3202,16 +3202,16 @@
},
{
"name": "yzh52521/easyhttp",
"version": "v1.1.0",
"version": "v1.1.3",
"source": {
"type": "git",
"url": "https://github.com/yzh52521/easyhttp.git",
"reference": "78ec5cea1884d6da0709cac95a1e4d23fe9bfc65"
"url": "https://github.com/yuanzhihai/easyhttp.git",
"reference": "02bcf47eaf723520fa3905d0e6f1852168fe646c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/yzh52521/easyhttp/zipball/78ec5cea1884d6da0709cac95a1e4d23fe9bfc65",
"reference": "78ec5cea1884d6da0709cac95a1e4d23fe9bfc65",
"url": "https://api.github.com/repos/yuanzhihai/easyhttp/zipball/02bcf47eaf723520fa3905d0e6f1852168fe646c",
"reference": "02bcf47eaf723520fa3905d0e6f1852168fe646c",
"shasum": ""
},
"require": {
@ -3247,10 +3247,10 @@
"phphttp"
],
"support": {
"issues": "https://github.com/yzh52521/easyhttp/issues",
"source": "https://github.com/yzh52521/easyhttp/tree/v1.1.0"
"issues": "https://github.com/yuanzhihai/easyhttp/issues",
"source": "https://github.com/yuanzhihai/easyhttp/tree/v1.1.3"
},
"time": "2023-08-31T06:20:52+00:00"
"time": "2023-11-14T05:49:02+00:00"
}
],
"packages-dev": [

View File

@ -16,7 +16,7 @@ return [
// 应用名,此项不可更改
'appname' => 'TaoLer',
// 版本配置
'version' => '2.5.2',
'version' => '2.5.3',
// 加盐
'salt' => 'taoler',
// 数据库备份目录

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -910,17 +910,17 @@
},
{
"name": "laravel/serializable-closure",
"version": "v1.3.2",
"version_normalized": "1.3.2.0",
"version": "v1.3.3",
"version_normalized": "1.3.3.0",
"source": {
"type": "git",
"url": "https://github.com/laravel/serializable-closure.git",
"reference": "076fe2cf128bd54b4341cdc6d49b95b34e101e4c"
"reference": "3dbf8a8e914634c48d389c1234552666b3d43754"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/laravel/serializable-closure/zipball/076fe2cf128bd54b4341cdc6d49b95b34e101e4c",
"reference": "076fe2cf128bd54b4341cdc6d49b95b34e101e4c",
"url": "https://api.github.com/repos/laravel/serializable-closure/zipball/3dbf8a8e914634c48d389c1234552666b3d43754",
"reference": "3dbf8a8e914634c48d389c1234552666b3d43754",
"shasum": ""
},
"require": {
@ -932,7 +932,7 @@
"phpstan/phpstan": "^1.8.2",
"symfony/var-dumper": "^5.4.11"
},
"time": "2023-10-17T13:38:16+00:00",
"time": "2023-11-08T14:08:06+00:00",
"type": "library",
"extra": {
"branch-alias": {
@ -3531,17 +3531,17 @@
},
{
"name": "yzh52521/easyhttp",
"version": "v1.1.0",
"version_normalized": "1.1.0.0",
"version": "v1.1.3",
"version_normalized": "1.1.3.0",
"source": {
"type": "git",
"url": "https://github.com/yzh52521/easyhttp.git",
"reference": "78ec5cea1884d6da0709cac95a1e4d23fe9bfc65"
"url": "https://github.com/yuanzhihai/easyhttp.git",
"reference": "02bcf47eaf723520fa3905d0e6f1852168fe646c"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/yzh52521/easyhttp/zipball/78ec5cea1884d6da0709cac95a1e4d23fe9bfc65",
"reference": "78ec5cea1884d6da0709cac95a1e4d23fe9bfc65",
"url": "https://api.github.com/repos/yuanzhihai/easyhttp/zipball/02bcf47eaf723520fa3905d0e6f1852168fe646c",
"reference": "02bcf47eaf723520fa3905d0e6f1852168fe646c",
"shasum": ""
},
"require": {
@ -3549,7 +3549,7 @@
"php": ">=7.2.5",
"psr/log": "^1.0|^2.0|^3.0"
},
"time": "2023-08-31T06:20:52+00:00",
"time": "2023-11-14T05:49:02+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
@ -3579,8 +3579,8 @@
"phphttp"
],
"support": {
"issues": "https://github.com/yzh52521/easyhttp/issues",
"source": "https://github.com/yzh52521/easyhttp/tree/v1.1.0"
"issues": "https://github.com/yuanzhihai/easyhttp/issues",
"source": "https://github.com/yuanzhihai/easyhttp/tree/v1.1.3"
},
"install-path": "../yzh52521/easyhttp"
}

View File

@ -3,7 +3,7 @@
'name' => 'taoser/taoler',
'pretty_version' => '2.3.10.x-dev',
'version' => '2.3.10.9999999-dev',
'reference' => '3115f271141016645a6dadee3b74f71758c868fb',
'reference' => '1bc07dde67cd6605da4e8444bd50db23b3da2c0b',
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@ -128,9 +128,9 @@
'dev_requirement' => false,
),
'laravel/serializable-closure' => array(
'pretty_version' => 'v1.3.2',
'version' => '1.3.2.0',
'reference' => '076fe2cf128bd54b4341cdc6d49b95b34e101e4c',
'pretty_version' => 'v1.3.3',
'version' => '1.3.3.0',
'reference' => '3dbf8a8e914634c48d389c1234552666b3d43754',
'type' => 'library',
'install_path' => __DIR__ . '/../laravel/serializable-closure',
'aliases' => array(),
@ -358,7 +358,7 @@
'taoser/taoler' => array(
'pretty_version' => '2.3.10.x-dev',
'version' => '2.3.10.9999999-dev',
'reference' => '3115f271141016645a6dadee3b74f71758c868fb',
'reference' => '1bc07dde67cd6605da4e8444bd50db23b3da2c0b',
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@ -536,9 +536,9 @@
'dev_requirement' => false,
),
'yzh52521/easyhttp' => array(
'pretty_version' => 'v1.1.0',
'version' => '1.1.0.0',
'reference' => '78ec5cea1884d6da0709cac95a1e4d23fe9bfc65',
'pretty_version' => 'v1.1.3',
'version' => '1.1.3.0',
'reference' => '02bcf47eaf723520fa3905d0e6f1852168fe646c',
'type' => 'library',
'install_path' => __DIR__ . '/../yzh52521/easyhttp',
'aliases' => array(),

View File

@ -508,8 +508,7 @@ class ReflectionClosure extends ReflectionFunction
break;
case 'id_name':
switch ($token[0]) {
// named arguments...
case ':':
case $token[0] === ':' && $context !== 'instanceof':
if ($lastState === 'closure' && $context === 'root') {
$state = 'closure';
$code .= $id_start.$token;

2
vendor/services.php vendored
View File

@ -1,5 +1,5 @@
<?php
// This file is automatically generated at:2023-11-02 18:31:51
// This file is automatically generated at:2023-11-23 17:07:27
declare (strict_types = 1);
return array (
0 => 'taoser\\addons\\Service',

View File

@ -13,6 +13,11 @@ gitee:[gitee.com/yzh52521/easyhttp](https://gitee.com/yzh52521/easyhttp "gitee.c
1. 增加 retry() 重试机制。
2. 增加 debug 日志调试功能。
3. 增加 withHost 指定服务端base_url
4. 增加 withBody 发送原始数据Raw请求
5. 增加 withMiddleware/withRequestMiddleware/withResponseMiddleware Guzzle 中间件
6. 增加 connectTimeout 设置等待服务器响应超时
7. 增加 sink 响应的主体部分将要保存的位置
8. 增加 maxRedirects 请求的重定向行为最大次数
# 安装说明
@ -53,6 +58,7 @@ $response = Http::delete(...);
$response = Http::head(...);
$response = Http::options(...);
```
###### 指定服务端base_url的请求
@ -61,6 +67,12 @@ $response = Http::options(...);
// 指定服务端base_url地址,最终请求地址为 https://serv.yzh52521.com/login
$response = Http::withHost('https://serv.yzh52521.com')->post('/login');
```
##### 发送原始数据Raw请求
```php
$response = Http::withBody(
base64_encode($photo), 'image/jpeg'
)->post(...);
```
###### 发送 Content-Type 编码请求
@ -114,8 +126,12 @@ $response = Http::withRedirect([
'protocols' => ['http', 'https'],
'track_redirects' => false
])->post(...);
$response = Http::maxRedirects(5)->post(...);
```
###### 携带认证的请求
```php
@ -182,6 +198,12 @@ $response = Http::withProxy([
$response = Http::timeout(60)->post(...);
```
###### 设置等待服务器响应超时的最大值(单位秒)
```php
$response = Http::connectTimeout(60)->post(...);
```
###### 设置延迟时间(单位秒)
```php
@ -199,6 +221,42 @@ $response = Http::concurrency(10)->promise(...);
```php
$response = Http::retry(3, 100)->post(...);
```
##### 响应的主体部分将要保存的位置
```php
$response = Http::sink('/path/to/file')->post(...);
```
#### Guzzle 中间件
```php
use GuzzleHttp\Middleware;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
$response = Http::withMiddleware(
Middleware::mapRequest(function (RequestInterface $request) {
$request = $request->withHeader('X-Example', 'Value');
return $request;
})
)->get('http://example.com');
………………
$response = Http::withRequestMiddleware(
function (RequestInterface $request) {
$request = $request->withHeader('X-Example', 'Value');
return $request;
}
)->get('http://example.com');
………………
$response = Http::withResponseMiddleware(
function (RequestInterface $response) {
$response = $response->getHeader('X-Example');
return $response;
}
)->get('http://example.com');
```
#### 异步请求
@ -206,22 +264,26 @@ $response = Http::retry(3, 100)->post(...);
use yzh52521\EasyHttp\Response;
use yzh52521\EasyHttp\RequestException;
Http::getAsync('http://easyhttp.yzh52521.cn/api/sleep3.json', ['token' => TOKEN], function (Response $response) {
$promise = Http::getAsync('http://easyhttp.yzh52521.cn/api/sleep3.json', ['token' => TOKEN], function (Response $response) {
echo '异步请求成功,响应内容:' . $response->body() . PHP_EOL;
}, function (RequestException $e) {
echo '异步请求异常,错误码:' . $e->getCode() . ',错误信息:' . $e->getMessage() . PHP_EOL;
});
$promise->wait();
echo json_encode(['code' => 200, 'msg' => '请求成功'], JSON_UNESCAPED_UNICODE) . PHP_EOL;
//输出
{"code":200,"msg":"请求成功"}
异步请求成功,响应内容:{"code":200,"msg":"success","second":3}
Http::getAsync('http1://easyhttp.yzh52521.cn/api/sleep3.json', function (Response $response) {
$promise = Http::getAsync('http1://easyhttp.yzh52521.cn/api/sleep3.json', function (Response $response) {
echo '异步请求成功,响应内容:' . $response->body() . PHP_EOL;
}, function (RequestException $e) {
echo '异步请求异常,错误信息:' . $e->getMessage() . PHP_EOL;
});
$promise->wait();
echo json_encode(['code' => 200, 'msg' => '请求成功'], JSON_UNESCAPED_UNICODE) . PHP_EOL;
//输出
@ -239,6 +301,9 @@ Http::deleteAsync(...);
Http::headAsync(...);
Http::optionsAsync(...);
使用 等待异步回调处理完成
Http::wait();
```
#### 异步并发请求
@ -253,12 +318,15 @@ $promises = [
Http::postAsync('http://easyhttp.yzh52521.cn/api/sleep2.json', ['name' => 'yzh52521']),
];
Http::concurrency(10)->multiAsync($promises, function (Response $response, $index) {
$pool=Http::concurrency(10)->multiAsync($promises, function (Response $response, $index) {
echo "发起第 $index 个异步请求,请求时长:" . $response->json()->second . '秒' . PHP_EOL;
}, function (RequestException $e, $index) {
echo "发起第 $index 个请求失败,失败原因:" . $e->getMessage() . PHP_EOL;
});
$promise = $pool->promise();
$promise->wait();
//输出
发起第 1 个请求失败失败原因cURL error 1: Protocol "http1" not supported or disabled in libcurl (see https://curl.haxx.se/libcurl/c/libcurl-errors.html)
发起第 2 个异步请求请求时长2 秒
@ -306,6 +374,12 @@ Http::debug(Log::class)->post(...);
## 更新日志
### 2023-08-31
* 新增 withBody 可以发送原始数据Raw请求
* 新增 withMiddleware/withRequestMiddleware/withResponseMiddleware 支持Guzzle中间件
* 新增 connectTimeout 设置等待服务器响应超时
* 新增 sink 响应的主体部分将要保存的位置
* 新增 maxRedirects 请求的重定向行为最大次数
### 2022-05-11
* 新增removeBodyFormat() 用于withOptions 指定body时清除原由的bodyFromat
### 2022-05-10

View File

@ -11,11 +11,16 @@ class Facade
$this->facade = new $this->facade;
}
public function __call($name, $params) {
public function __call($name, $params)
{
if (method_exists($this->facade, 'removeOptions')) {
call_user_func_array([$this->facade, 'removeOptions'], []);
}
return call_user_func_array([$this->facade, $name], $params);
}
public static function __callStatic($name, $params) {
public static function __callStatic($name, $params)
{
return call_user_func_array([new static(), $name], $params);
}
}

View File

@ -17,7 +17,7 @@ use GuzzleHttp\Exception\ConnectException;
* @method \yzh52521\EasyHttp\Response array()
* @method \yzh52521\EasyHttp\Response json()
* @method \yzh52521\EasyHttp\Response headers()
* @method \yzh52521\EasyHttp\Response header( string $header )
* @method \yzh52521\EasyHttp\Response header(string $header)
* @method \yzh52521\EasyHttp\Response status()
* @method \yzh52521\EasyHttp\Response successful()
* @method \yzh52521\EasyHttp\Response ok()
@ -88,7 +88,7 @@ class Request
$this->options = [
'http_errors' => false,
];
$this->handlerStack = HandlerStack::create( new CurlHandler() );
$this->handlerStack = HandlerStack::create(new CurlHandler());
}
/**
@ -96,9 +96,7 @@ class Request
*/
public function __destruct()
{
if (!empty( $this->promises )) {
Promise\settle( $this->promises )->wait();
}
}
/**
@ -109,17 +107,28 @@ class Request
{
$name = get_called_class();
if (!isset( self::$instances[$name] )) {
if (!isset(self::$instances[$name])) {
self::$instances[$name] = new Client();
}
return self::$instances[$name];
}
public function removeOptions()
{
$this->bodyFormat = 'form_params';
$this->isRemoveBodyFormat = false;
$this->options = [
'http_errors' => false,
'verify' => false
];
return $this;
}
public function asForm()
{
$this->bodyFormat = 'form_params';
$this->withHeaders( ['Content-Type' => 'application/x-www-form-urlencoded'] );
$this->withHeaders(['Content-Type' => 'application/x-www-form-urlencoded']);
return $this;
}
@ -127,21 +136,21 @@ class Request
public function asJson()
{
$this->bodyFormat = 'json';
$this->withHeaders( ['Content-Type' => 'application/json'] );
$this->withHeaders(['Content-Type' => 'application/json']);
return $this;
}
public function asMultipart(string $name,string $contents,string $filename = null,array $headers = [])
public function asMultipart(string $name, string $contents, string $filename = null, array $headers = [])
{
$this->bodyFormat = 'multipart';
$this->options = array_filter( [
$this->options = array_filter([
'name' => $name,
'contents' => $contents,
'headers' => $headers,
'filename' => $filename,
] );
]);
return $this;
}
@ -182,25 +191,25 @@ class Request
public function withOptions(array $options)
{
unset( $this->options[$this->bodyFormat],$this->options['body'] );
unset($this->options[$this->bodyFormat], $this->options['body']);
$this->options = array_merge_recursive( $this->options,$options );
$this->options = array_merge_recursive($this->options, $options);
return $this;
}
public function withCert(string $path,string $password)
public function withCert(string $path, string $password)
{
$this->options['cert'] = [$path,$password];
$this->options['cert'] = [$path, $password];
return $this;
}
public function withHeaders(array $headers)
{
$this->options = array_merge_recursive( $this->options,[
$this->options = array_merge_recursive($this->options, [
'headers' => $headers,
] );
]);
return $this;
}
@ -216,39 +225,39 @@ class Request
return $this;
}
public function withBasicAuth(string $username,string $password)
public function withBasicAuth(string $username, string $password)
{
$this->options['auth'] = [$username,$password];
$this->options['auth'] = [$username, $password];
return $this;
}
public function withDigestAuth(string $username,string $password)
public function withDigestAuth(string $username, string $password)
{
$this->options['auth'] = [$username,$password,'digest'];
$this->options['auth'] = [$username, $password, 'digest'];
return $this;
}
public function withUA(string $ua)
{
$this->options['headers']['User-Agent'] = trim( $ua );
$this->options['headers']['User-Agent'] = trim($ua);
return $this;
}
public function withToken(string $token,string $type = 'Bearer')
public function withToken(string $token, string $type = 'Bearer')
{
$this->options['headers']['Authorization'] = trim( $type.' '.$token );
$this->options['headers']['Authorization'] = trim($type . ' ' . $token);
return $this;
}
public function withCookies(array $cookies,string $domain)
public function withCookies(array $cookies, string $domain)
{
$this->options = array_merge_recursive( $this->options,[
'cookies' => CookieJar::fromArray( $cookies,$domain ),
] );
$this->options = array_merge_recursive($this->options, [
'cookies' => CookieJar::fromArray($cookies, $domain),
]);
return $this;
}
@ -302,9 +311,9 @@ class Request
return $this;
}
public function retry(int $retries = 1,int $sleep = 0)
public function retry(int $retries = 1, int $sleep = 0)
{
$this->handlerStack->push( ( new Retry() )->handle( $retries,$sleep ) );
$this->handlerStack->push((new Retry())->handle($retries, $sleep));
$this->options['handler'] = $this->handlerStack;
@ -351,17 +360,17 @@ class Request
public function debug($class)
{
$logger = new Logger( function ($level,$message,array $context) use ($class) {
$class::log( $level,$message );
},function ($request,$response,$reason) {
$logger = new Logger(function ($level, $message, array $context) use ($class) {
$class::log($level, $message);
}, function ($request, $response, $reason) {
$requestBody = $request->getBody();
$requestBody->rewind();
//请求头
$requestHeaders = [];
foreach ( (array)$request->getHeaders() as $k => $vs ) {
foreach ( $vs as $v ) {
foreach ((array)$request->getHeaders() as $k => $vs) {
foreach ($vs as $v) {
$requestHeaders[] = "$k: $v";
}
}
@ -369,8 +378,8 @@ class Request
//响应头
$responseHeaders = [];
foreach ( (array)$response->getHeaders() as $k => $vs ) {
foreach ( $vs as $v ) {
foreach ((array)$response->getHeaders() as $k => $vs) {
foreach ($vs as $v) {
$responseHeaders[] = "$k: $v";
}
}
@ -379,7 +388,7 @@ class Request
$path = $uri->getPath();
if ($query = $uri->getQuery()) {
$path .= '?'.$query;
$path .= '?' . $query;
}
return sprintf(
@ -388,175 +397,175 @@ class Request
$request->getMethod(),
$path,
$request->getProtocolVersion(),
join( "\r\n",$requestHeaders ),
join("\r\n", $requestHeaders),
$requestBody->getContents(),
$response->getProtocolVersion(),
$response->getStatusCode(),
$response->getReasonPhrase(),
join( "\r\n",$responseHeaders ),
join("\r\n", $responseHeaders),
$response->getBody()->getContents()
);
} );
$this->handlerStack->push( $logger );
});
$this->handlerStack->push($logger);
$this->options['handler'] = $this->handlerStack;
return $this;
}
public function attach(string $name,string $contents,string $filename = null,array $headers = [])
public function attach(string $name, string $contents, string $filename = null, array $headers = [])
{
$this->options['multipart'] = array_filter( [
$this->options['multipart'] = array_filter([
'name' => $name,
'contents' => $contents,
'headers' => $headers,
'filename' => $filename,
] );
]);
return $this;
}
public function get(string $url,array $query = [])
public function get(string $url, array $query = [])
{
$params= parse_url( $url,PHP_URL_QUERY );
$params = parse_url($url, PHP_URL_QUERY);
parse_str( $params?:'',$result );
parse_str($params ?: '', $result);
$this->options['query'] = array_merge( $result,$query );
$this->options['query'] = array_merge($result, $query);
return $this->request( 'GET',$url,$query );
return $this->request('GET', $url, $query);
}
public function post(string $url,array $data = [])
public function post(string $url, array $data = [])
{
$this->options[$this->bodyFormat] = $data;
return $this->request( 'POST',$url,$data );
return $this->request('POST', $url, $data);
}
public function patch(string $url,array $data = [])
public function patch(string $url, array $data = [])
{
$this->options[$this->bodyFormat] = $data;
return $this->request( 'PATCH',$url,$data );
return $this->request('PATCH', $url, $data);
}
public function put(string $url,array $data = [])
public function put(string $url, array $data = [])
{
$this->options[$this->bodyFormat] = $data;
return $this->request( 'PUT',$url,$data );
return $this->request('PUT', $url, $data);
}
public function delete(string $url,array $data = [])
public function delete(string $url, array $data = [])
{
$this->options[$this->bodyFormat] = $data;
return $this->request( 'DELETE',$url,$data );
return $this->request('DELETE', $url, $data);
}
public function head(string $url,array $data = [])
public function head(string $url, array $data = [])
{
$this->options[$this->bodyFormat] = $data;
return $this->request( 'HEAD',$url,$data );
return $this->request('HEAD', $url, $data);
}
public function options(string $url,array $data = [])
public function options(string $url, array $data = [])
{
$this->options[$this->bodyFormat] = $data;
return $this->request( 'OPTIONS',$url,$data );
return $this->request('OPTIONS', $url, $data);
}
public function getAsync(string $url,$query = null,callable $success = null,callable $fail = null)
public function getAsync(string $url, $query = null, callable $success = null, callable $fail = null)
{
is_callable( $query ) || $this->options['query'] = $query;
is_callable($query) || $this->options['query'] = $query;
return $this->requestAsync( 'GET',$url,$query,$success,$fail );
return $this->requestAsync('GET', $url, $query, $success, $fail);
}
public function postAsync(string $url,$data = null,callable $success = null,callable $fail = null)
public function postAsync(string $url, $data = null, callable $success = null, callable $fail = null)
{
is_callable( $data ) || $this->options[$this->bodyFormat] = $data;
is_callable($data) || $this->options[$this->bodyFormat] = $data;
return $this->requestAsync( 'POST',$url,$data,$success,$fail );
return $this->requestAsync('POST', $url, $data, $success, $fail);
}
public function patchAsync(string $url,$data = null,callable $success = null,callable $fail = null)
public function patchAsync(string $url, $data = null, callable $success = null, callable $fail = null)
{
is_callable( $data ) || $this->options[$this->bodyFormat] = $data;
is_callable($data) || $this->options[$this->bodyFormat] = $data;
return $this->requestAsync( 'PATCH',$url,$data,$success,$fail );
return $this->requestAsync('PATCH', $url, $data, $success, $fail);
}
public function putAsync(string $url,$data = null,callable $success = null,callable $fail = null)
public function putAsync(string $url, $data = null, callable $success = null, callable $fail = null)
{
is_callable( $data ) || $this->options[$this->bodyFormat] = $data;
is_callable($data) || $this->options[$this->bodyFormat] = $data;
return $this->requestAsync( 'PUT',$url,$data,$success,$fail );
return $this->requestAsync('PUT', $url, $data, $success, $fail);
}
public function deleteAsync(string $url,$data = null,callable $success = null,callable $fail = null)
public function deleteAsync(string $url, $data = null, callable $success = null, callable $fail = null)
{
is_callable( $data ) || $this->options[$this->bodyFormat] = $data;
is_callable($data) || $this->options[$this->bodyFormat] = $data;
return $this->requestAsync( 'DELETE',$url,$data,$success,$fail );
return $this->requestAsync('DELETE', $url, $data, $success, $fail);
}
public function headAsync(string $url,$data = null,callable $success = null,callable $fail = null)
public function headAsync(string $url, $data = null, callable $success = null, callable $fail = null)
{
is_callable( $data ) || $this->options[$this->bodyFormat] = $data;
is_callable($data) || $this->options[$this->bodyFormat] = $data;
return $this->requestAsync( 'HEAD',$url,$data,$success,$fail );
return $this->requestAsync('HEAD', $url, $data, $success, $fail);
}
public function optionsAsync(string $url,$data = null,callable $success = null,callable $fail = null)
public function optionsAsync(string $url, $data = null, callable $success = null, callable $fail = null)
{
is_callable( $data ) || $this->options[$this->bodyFormat] = $data;
is_callable($data) || $this->options[$this->bodyFormat] = $data;
return $this->requestAsync( 'OPTIONS',$url,$data,$success,$fail );
return $this->requestAsync('OPTIONS', $url, $data, $success, $fail);
}
public function multiAsync(array $promises,callable $success = null,callable $fail = null)
public function multiAsync(array $promises, callable $success = null, callable $fail = null)
{
$count = count( $promises );
$count = count($promises);
$this->concurrency = $this->concurrency ?: $count;
$requests = function () use ($promises) {
foreach ( $promises as $promise ) {
foreach ($promises as $promise) {
yield function () use ($promise) {
return $promise;
};
}
};
$fulfilled = function ($response,$index) use ($success) {
if (!is_null( $success )) {
$response = $this->response( $response );
call_user_func_array( $success,[$response,$index] );
$fulfilled = function ($response, $index) use ($success) {
if (!is_null($success)) {
$response = $this->response($response);
call_user_func_array($success, [$response, $index]);
}
};
$rejected = function ($exception,$index) use ($fail) {
if (!is_null( $fail )) {
$exception = $this->exception( $exception );
call_user_func_array( $fail,[$exception,$index] );
$rejected = function ($exception, $index) use ($fail) {
if (!is_null($fail)) {
$exception = $this->exception($exception);
call_user_func_array($fail, [$exception, $index]);
}
};
$pool = new Pool( $this->client,$requests(),[
$pool = new Pool($this->client, $requests(), [
'concurrency' => $this->concurrency,
'fulfilled' => $fulfilled,
'rejected' => $rejected,
] );
]);
$pool->promise();
return $pool;
}
protected function request(string $method,string $url,array $options = [])
protected function request(string $method, string $url, array $options = [])
{
if (isset($this->options[$this->bodyFormat])) {
$this->options[$this->bodyFormat] = $options;
@ -564,13 +573,13 @@ class Request
$this->options[$this->bodyFormat] = $this->pendingBody;
}
if ($this->isRemoveBodyFormat) {
unset( $this->options[$this->bodyFormat] );
unset($this->options[$this->bodyFormat]);
}
try {
$response = $this->client->request( $method,$url,$this->options );
return $this->response( $response );
} catch ( ConnectException $e ) {
throw new ConnectionException( $e->getMessage(),0,$e );
$response = $this->client->request($method, $url, $this->options);
return $this->response($response);
} catch (ConnectException $e) {
throw new ConnectionException($e->getMessage(), 0, $e);
}
}
@ -583,7 +592,7 @@ class Request
* @throws ConnectionException
* @throws \GuzzleHttp\Exception\GuzzleException
*/
public function client(string $method,string $url,array $options = [])
public function client(string $method, string $url, array $options = [])
{
if (isset($this->options[$this->bodyFormat])) {
$this->options[$this->bodyFormat] = $options;
@ -591,16 +600,16 @@ class Request
$this->options[$this->bodyFormat] = $this->pendingBody;
}
if ($this->isRemoveBodyFormat) {
unset( $this->options[$this->bodyFormat] );
unset($this->options[$this->bodyFormat]);
}
try {
if (empty( $options )) {
if (empty($options)) {
$options = $this->options;
}
$response = $this->client->request( $method,$url,$options );
return $this->response( $response );
} catch ( ConnectException $e ) {
throw new ConnectionException( $e->getMessage(),0,$e );
$response = $this->client->request($method, $url, $options);
return $this->response($response);
} catch (ConnectException $e) {
throw new ConnectionException($e->getMessage(), 0, $e);
}
}
@ -612,7 +621,7 @@ class Request
* @return Response
* @throws ConnectionException
*/
public function clientAsync(string $method,string $url,array $options = [])
public function clientAsync(string $method, string $url, array $options = [])
{
if (isset($this->options[$this->bodyFormat])) {
$this->options[$this->bodyFormat] = $options;
@ -620,23 +629,23 @@ class Request
$this->options[$this->bodyFormat] = $this->pendingBody;
}
if ($this->isRemoveBodyFormat) {
unset( $this->options[$this->bodyFormat] );
unset($this->options[$this->bodyFormat]);
}
try {
if (empty( $options )) {
if (empty($options)) {
$options = $this->options;
}
$response = $this->client->requestAsync( $method,$url,$options );
return $this->response( $response );
} catch ( ConnectException $e ) {
throw new ConnectionException( $e->getMessage(),0,$e );
$response = $this->client->requestAsync($method, $url, $options);
return $this->response($response);
} catch (ConnectException $e) {
throw new ConnectionException($e->getMessage(), 0, $e);
}
}
protected function requestAsync(string $method, string $url, $options = null, callable $success = null, callable $fail = null)
{
if (is_callable( $options )) {
if (is_callable($options)) {
$successCallback = $options;
$failCallback = $success;
} else {
@ -651,52 +660,52 @@ class Request
}
if ($this->isRemoveBodyFormat) {
unset( $this->options[$this->bodyFormat] );
unset($this->options[$this->bodyFormat]);
}
try {
$promise = $this->client->requestAsync( $method,$url,$this->options );
$promise = $this->client->requestAsync($method, $url, $this->options);
$fulfilled = function ($response) use ($successCallback) {
if (!is_null( $successCallback )) {
$response = $this->response( $response );
call_user_func_array( $successCallback,[$response] );
if (!is_null($successCallback)) {
$response = $this->response($response);
call_user_func_array($successCallback, [$response]);
}
};
$rejected = function ($exception) use ($failCallback) {
if (!is_null( $failCallback )) {
$exception = $this->exception( $exception );
call_user_func_array( $failCallback,[$exception] );
if (!is_null($failCallback)) {
$exception = $this->exception($exception);
call_user_func_array($failCallback, [$exception]);
}
};
$promise->then( $fulfilled,$rejected );
$promise->then($fulfilled, $rejected);
$this->promises[] = $promise;
return $promise;
} catch ( ConnectException $e ) {
throw new ConnectionException( $e->getMessage(),0,$e );
} catch (ConnectException $e) {
throw new ConnectionException($e->getMessage(), 0, $e);
}
}
public function wait()
{
if (!empty($this->promises)) {
Promise\settle($this->promises)->wait();
\GuzzleHttp\Promise\Utils($this->promises)->wait();
}
$this->promises = [];
}
protected function response($response)
{
return new Response( $response );
return new Response($response);
}
protected function exception($exception)
{
return new RequestException( $exception );
return new RequestException($exception);
}
}

View File

@ -0,0 +1,141 @@
{extend name="public/base" /}
{block name="title"}{$cateinfo.catename} - {$sysInfo.webname}{/block}
{block name="keywords"}{$cateinfo.catename},{$sysInfo.webname}{/block}
{block name="description"}{$cateinfo.desc}{/block}
{block name="ogtitle"}<meta property="og:title" content="{$cateinfo.catename} - {$sysInfo.webname}">{/block}
{block name="ogdescription"}<meta property="og:description" content="{$cateinfo.desc}" />{/block}
{block name="meta"}
<!-- SEO优化 -->
<meta name="Copyright" content="{$sysInfo.webname}">
<meta property="og:type" content="website"/>
<meta property="og:url" content="{$Request.domain}"/>
<meta property="og:site_name" content="{$sysInfo.webname}"/>
<meta property="og:image" content=""/>
{/block}
{block name="link"}
<!-- 特效丶样式 -->
<link rel="dns-prefetch" href="//q4.qlogo.cn">
<link rel="shortcut icon" type="image/x-icon" href="{$Request.domain}/favicon.ico"/>
<link rel="apple-touch-icon" sizes="180x180" href=""/>
<link rel="apple-touch-icon-precomposed" href=""/>
<link rel="stylesheet" href="{$Request.domain}/view/common/blog/css/style.css" type="text/css" charset="utf-8"/>
<script src="/static/jquery-3.6.0.min.js"></script>
{/block}
{block name="column"}{include file="/public/column" /}{/block}
{block name="content"}
<main class="py-2 py-md-2 pb-3">
<div class="layui-container">
<div class="layui-row layui-col-space15">
<div class="layui-col-md8">
<div class="post"><h1>{$sysInfo.webname}</h1></div>
<section id="main" class="list-home list-grid list-grid-padding">
{article:list}
<article class="list-item block card-plain">
{if getOnepic($article.content)}
<figure class="media media-3x2 col-4 col-md-4 d-none d-md-block">
<a class="media-content" href="{article:link /}" title="{article:title /}">
<img src="{:getOnepic($article.content)}" width="233" height="155" alt="{article:title /}">
</a>
</figure>
{/if}
<div class="list-width list-content">
<div class="list-body">
<h3>
<a href="{article:link /}" title="{article:title /}" class="list-title fanpian">{article:title /}</a>
</h3>
<div class="list-desc d-block d-md-block text-sm text-secondary my-3">
<p class="h-3x">{:getArtContent($article.content)}</p>
</div>
</div>
<div class="list-footer">
<div class="d-flex flex-fill align-items-center text-muted text-xs">
<time class="d-inline-block" datetime="{$article.create_time|date='Y-m-d'}">{$article.create_time|date='Y-m-d'}</time>
<div class="d-inline-block mx-1 mx-md-2">
<i class="text-primary">·</i>
</div>
<div class="d-inline-block">
<a href="{article:user name='link' /}" class="text-muted" title="发布于{article:auther /}" rel="category">{article:auther /}</a>
{$article.has_img ?= '<span><i class="layui-icon layui-icon-picture" style="color: #5FB878;"></i></span>'}
{$article.has_video ?= '<span><i class="layui-icon layui-icon-play" style="color: #FF5722;"></i></span>'}
{$article.has_audio ?= '<span><i class="layui-icon layui-icon-speaker" style="color: #000000;"></i></span>'}
{$article.read_type ?= '<span><i class="layui-icon layui-icon-password" style="color: #FF5722;"></i></span>'}
{$article.upzip ?= '<span><i class="layui-icon layui-icon-file-b" style="color: #009688;" title="附件"></i></span>'}
</div>
<div class="flex-fill"></div>
<div class="mx-1">
<a href="{article:link /}" class="text-muted">阅读全文</a>
</div>
</div>
</div>
</div>
</article>
{/article:list}
</section>
<nav class="navigation pagination" role="navigation" aria-label="Posts Navigation">
<div class="nav-links" style="padding-bottom: 10px;" id="pages">
<ul class="page-navigator">
<li class='current'><span aria-current="page">1</span></li>
<li id="loadmore"><a style="margin-right: 0px;">当前第 1 页</a></li>
</ul>
</div>
</nav>
</div>
<div class="sidebar layui-col-md4 d-none d-lg-block">
<div class="theiaStickySidebar">
<!--博客列表广告赞助位-->
{:hook('ads_blog_cate_rimg')}
<dl class="function" id="rongkeji_remenwenzhang">
<dt class="function_t">热门文章</dt>
<dd class="function_c">
<ul>
{volist name="artHot" id="vo"}
<li style="display: block;">
<div class="list-body fanpian">
<a href="{$Request.domain}{$vo.url}" target="_blank">{$vo.title}</a>
</div>
<div class="list-footer">
<div class="text-muted text-xs"><time class="d-inline-block">{$vo.create_time|date='Y-m-d'}</time></div>
</div>
</li>
{/volist}
</ul>
</dd>
</dl>
<!--博客列表友情链接-->
{:hook('ads_blog_cate_flink')}
</div>
</div>
</div>
</div>
</main>
{include file="public/menu" /}
{/block}
{block name="script"}
<script type='text/javascript' src="{$Request.domain}/view/common/blog/js/theme.js"></script>
<script>
layui.use(['laypage'], function(){
var laypage = layui.laypage;
//执行一个laypage实例
laypage.render({
elem: 'pages' //注意,这里的 test1 是 ID不用加 # 号
,count: "{$artList['total']}" //数据总数,从服务端得到
,limit: "{$artList['per_page']}"
,curr : "{$artList['current_page']}"
//获取起始页
,jump: function(obj, first){
//首次不执行
if(!first){
var page = obj.curr;
location.href ="{$path}/"+page+'.html';
}
}
})
});
</script>
{/block}

View File

@ -0,0 +1,520 @@
{extend name="public/base" /}
{block name="title"}{$article.title} - {$sysInfo.webname}{/block}
{block name="keywords"}{$article.keywords ?: $article.title}{/block}
{block name="description"}{$article.title},{$article.description}{/block}
{block name="ogtitle"}<meta property="og:title" content="{article:title} - {$sysInfo.webname}">{/block}
{block name="ogdescription"}<meta property="og:description" content="{article:title},{$article.description ?? ''}" />{/block}
{block name="ogimage"}<meta property="og:image" content="{:getOnepic($article.content)}"/>{/block}
{block name="meta"}
<!-- SEO优化 -->
<meta property="og:type" content="article"/>
<meta property="bytedance:published_time" content="{$article.create_time|date='c'}" />
<meta property="bytedance:updated_time" content="{$article.update_time|date='c'}" />
<meta property="og:updated_time" content="{$article.update_time|date='c'}" />
<meta name="mobile-agent" content="format=html5;url={$Request.domain}{$Request.url}">
{/block}
{block name="link"}
<!-- 谷歌JavaScript结构化 -->
<script type="application/ld+json">{"@context": "https://schema.org","@type": "NewsArticle","headline": "{$article.title}","image" : "{:getOnepic($article.content)}","datePublished" : "{$article.create_time|date='c'}","dateModified": "{$article.update_time|date='c'}","author" : {"@type" : "Person","name" : "用户投稿","url": "{$Request.domain}"}},</script>
<!-- 百度JavaScript结构化 -->
<script type="application/ld+json">{"@context": "https://ziyuan.baidu.com/contexts/cambrian.jsonld","@id": "{$Request.domain}{$Request.url}","title": "{$article.title}","images": ["{:getOnepic($article.content)}"],"description": "{$article.description ?? ''}","pubDate": "{$article.create_time|date='c'}","upDate": "{$article.update_time|date='c'}"}</script>
<!-- 特效丶样式 -->
<link rel="stylesheet" href="{$Request.domain}/view/common/blog/css/style.css" type="text/css" charset="utf-8"/>
<script type='text/javascript' src="/static/jquery-3.6.0.min.js"></script>
<script type='text/javascript' src="{$Request.domain}/view/common/blog/js/view-image.min.js"></script>
<script>window.ViewImage && ViewImage.init('.post-content img');</script>
{/block}
{block name="column"}<div class="layui-hide-xs">{include file="/public/column" /}</div>{/block}
<!-- 内容 -->
{block name="content"}
<main class="py-2 py-md-2 pb-3">
<div class="layui-container">
<div class="layui-row layui-col-space15">
<div class="layui-col-md8" itemscope itemType="">
<div class="post card">
<section class="card-body">
<div class="post-header border-bottom mb-4 pb-4">
<h1 class="h3 mb-3" itemprop="headline">{article:title /}</h1>
<div class="meta d-flex align-items-center text-xs text-muted">
<div class="d-inline-block" itemprop="author" itemscope="" itemtype="">
<a href="{$Request.domain}" target="_blank" class="text-muted" itemprop="url"><span itemprop="name">{article:auther}</span></a>
<a href="{article:cate name='link'}" target="_blank" class="text-muted" rel="category">{article:cate name="name"}</a>
<time class="d-inline-block" datetime="{$article.create_time|date='Y-m-d h:m:s'}" itemprop="datePublished">{article:uptime}</time>
</div>
<div class="ml-auto text-sm yincang">
<span class="mx-1"><small>阅读 {article:pv /} 次</small></span>
</div>
</div>
<div class="border-theme bg-primary"></div>
</div>
{:hook('taoplayerdiv')}
<article class="post-content detail-body" itemprop="articleBody" view-image>
{article:content}
</article>
<!-- 文章末尾 -->
{notempty name="$article.upzip"}
{if (session('?user_name'))}
<button type="button" class="layui-btn layui-btn-xs" id="zip-download"><i class="layui-icon layui-icon-download-circle"></i>{:lang('download files')}: {$article.downloads}次</button>
{/if}
{/notempty}
<!-- 解密文件 -->
{empty name="passJieMi"}
{if($article.read_type == 1)}
<div id="jiemi" style="text-align:center">
<button type="button" class="layui-btn layui-btn-primary"><i class="layui-icon layui-icon-password" style="font-size: 30px; color: #FF5722;"></i> 阅读请解密 </button>
</div>
{/if}
{/empty}
<!-- 标签 -->
{notempty name="tags"}
<div style="margin-top: 15px">标签
{volist name="tags" id="vo" }
<a href="{$vo.url}"><span class="layui-btn layui-btn-xs layui-btn-normal layui-btn-radius">{$vo.name}</span></a>
{/volist}
</div>
{/notempty}
<div style="margin: 20px 0px 15px 0px; color: rgb(130, 125, 125);">
<p style="line-height:200%;">{$sysInfo.state|raw}</p>
</div>
<div style="margin-top: 20px;">
本文链接:<a href="{$Request.domain}{$Request.url}">{$Request.domain}{$Request.url}</a>
</div>
<div class="detail-zan">
<span class="jieda-zan" type="zan" id="article-zan">点赞 <i class="iconfont icon-zan"></i> <em>{:count($userZanList)}</em></span>
{volist name="userZanList" id="vo" }
<span><img src="{$vo.userImg}"></span>
{/volist}
</div>
</section>
<div class="card-footer text-center mt-4">
<a href="javascript:" style="color: #FFF !important;" data-img="/qrcode/?text={$Request.domain}{$Request.url}&size=200" data-title="手机扫一扫继续阅读" data-desc="微信或浏览器均可" class="single-popup shangzan mr-3 text-muted text-xs">手机浏览</a>
<a href="javascript:" style="color: #FFF !important;" data-img="{:hook('qqKefu','dashang')}" data-title="赞赏支持一下站长" data-desc="微信赞赏扫码" class="single-popup shangzan text-muted text-xs">赞赏支持</a>
<div class="text-center text-muted text-xs" style="margin-top: 30px;">
"作者已被打赏 0 次"
</div>
</div>
</div>
<div class="post card block d-flex p-4">
<span class="d-inline-block text-muted mr-2 fanpian pb-2 mb-2"> # 上一篇: {$previous|raw}</span>
<span class="d-inline-block text-muted mr-2 fanpian"># 下一篇: {$next|raw}</span>
</div>
<!-- 相关文章 -->
{notempty name="relationArticle"}
<section class="list-related">
<div class="content-related card">
<div class="card-body">
<div class="mb-3">相关文章</div>
<div class="list list-dots my-n2">
{volist name="relationArticle" id="vo"}
<div class="list-item py-2">
<a href="{$Request.domain}/{$vo.url}" target="_blank" class="list-title fanpian" title="{$vo.title}" rel="bookmark">
{$vo.title}
</a>
</div>
{/volist}
</div>
</div>
</div>
</section>
{/notempty}
<!-- 评论 -->
<section class="comments">
<div class="card">
<div class="card-body pt-4">
{//评论内容}
<div class="mb-3" id="flyReply">
文章评论 <small class="font-theme text-muted">({article:comment_num})</small>
<ul class="jieda" id="jieda">
{article:comment}
<li data-id="{comment:id /}" class="jieda-daan">
<div class="detail-about detail-about-reply">
<a class="fly-avatar" href="{comment:ulink /}"><img src="{comment:uimg /}" alt="{comment:uname}"></a>
<div class="fly-detail-user">
<a href="{comment:ulink /}" class="fly-link"><cite>{comment:uname /}</cite></a>
{if condition="$article.user_id eq $comment.user_id"}
<span>({:lang('poster')})</span>
{/if}
<span>{comment:usign /}</span>
</div>
<div class="detail-hits">
<span class="post-time" data="{comment:time}"></span>{:hook('ipShow',$comment.user.city)}</span>
</div>
{//加密未解密评论不可查看}
{if ($article.read_type == 0 || (($article.read_type == 1) && $passJieMi))}
<div class="detail-body jieda-body photos">{comment:content /}</div>
<div class="jieda-reply">
{eq name="comment.delete_time" value="0"}
<span class="jieda-zan {if($comment.zan != 0)}zanok{/if}" type="zan">
<i class="iconfont icon-zan"></i><em>{comment:zan /}</em>
</span>
<span type="reply" data-pid="{comment:id /}" data-tid="{comment:uid /}"><i class="iconfont icon-svgmoban53"></i>{:lang('reply')}</span>
{//评论编辑删除采纳权限}
<div class="jieda-admin">
{if ((session('user_id') == $comment.user_id) && (getLimtTime($comment.create_time) < 2)) OR ($user.auth ?? '')}
<span type="edit" class="comment-edit" data-id="{comment:id /}">{:lang('edit')}</span>
<span type="del" class="comment-del" data-id="{comment:id /}">{:lang('delete')}</span>
{/if}
</div>
{/eq}
{// 第二层回复}
{notempty name="$comment.children"}
{volist name="$comment.children" id="vo"}
<div class="layui-clear" style="margin:10px 0; padding: 10px; border: 1px solid #f0f0f0; background: #f6f6f6">
<a style="display: inline-block;width: 50px;">
<img src="{$vo.user.user_img}" style="width: 30px; height: 30px; border-radius: 15px; object-fit: cover">
</a>
<div style="float: left;width: calc(100% - 50px);">
<div>{$vo.user.name} {$vo.create_time|date='Y-m-d H:i'}</div>
<div class="detail-body jieda-body photos">{$vo.content|raw}</div>
<div class="jieda-reply">
{eq name="vo.delete_time" value="0"}
<span class="jieda-zan {if($vo.zan != 0)}zanok{/if}" type="zan">
<i class="iconfont icon-zan"></i><em>{$vo.zan}</em>
</span>
<span type="reply" data-pid="{$vo.id}" data-tid="{$vo.user.id}"><i class="iconfont icon-svgmoban53"></i>{:lang('reply')}</span>
{//评论编辑删除采纳权限}
<div class="jieda-admin">
{if ((session('user_id') == $vo.user.id) && (getLimtTime($vo.create_time) < 2)) OR ($user.auth ?? '')}
<span type="edit" class="comment-edit" data-id="{$vo.id}">{:lang('edit')}</span>
<span type="del" class="comment-del" data-id="{$vo.id}" >{:lang('delete')}</span>
{/if}
</div>
{/eq}
</div>
</div>
</div>
{// 第三层回复}
{notempty name="$vo.children"}
{volist name="$vo.children" id="voo"}
<div class="layui-clear" style="margin:10px 0; padding: 10px; border: 1px solid #f0f0f0;">
<a style="display: inline-block; width: 50px;">
<img src="{$voo.user.user_img}" style="width: 30px; height: 30px; object-fit: cover; border-radius: 15px;">
</a>
<div style="float: left;width: calc(100% - 50px);">
<div>{$voo.user.name} 回复 {$voo.touser} {$voo.create_time|date='Y-m-d H:i'}</div>
<div class="detail-body jieda-body photos">{$voo.content|raw}</div>
<div class="jieda-reply">
{eq name="voo.delete_time" value="0"}
<span class="jieda-zan {if($voo.zan != 0)}zanok{/if}" type="zan">
<i class="iconfont icon-zan"></i><em>{$voo.zan}</em>
</span>
<span type="reply" data-pid="{$vo.id}" data-tid="{$voo.user.id}"><i class="iconfont icon-svgmoban53"></i>{:lang('reply')}</span>
{//评论编辑删除采纳权限}
<div class="jieda-admin">
{if ((session('user_id') == $voo.user.id) && (getLimtTime($voo.create_time) < 2)) OR ($user.auth ?? '')}
<span type="edit" class="comment-edit" data-id="{$voo.id}">{:lang('edit')}</span>
<span type="del" class="comment-del" data-id="{$voo.id}">{:lang('delete')}</span>
{/if}
</div>
{/eq}
</div>
</div>
</div>
{/volist}
{/notempty}
{/volist}
{/notempty}
</div>
{else /}
<div class="detail-body jieda-body photos">
<i class="layui-icon layui-icon-password" style="font-size: 24px; color: #FF5722;"></i>
解密后查看内容
</div>
{/if}
</div>
</li>
{/article:comment}
</ul>
<div style="text-align: center" id="pages"></div>
</div>
{//评论区}
{if (config('taoler.config.is_reply') == 1 ) AND ( $article.is_reply == 0 )}
<div id="commentszbpmy" style="text-align: center;">
<h5>评论已关闭!</h5>
</div>
{/if}
{if session('?user_id') AND (config('taoler.config.is_reply') == 1 ) AND ( $article.is_reply == 1 )}
<div class="comment-respond">
<form class="comment-form">
<div class="layui-form">
<div class="layui-form-item layui-form-text">
<a name="comment"></a>
<div class="comment-textarea mb-3">
<textarea id="L_content" name="content" required lay-verify="required" placeholder="{:lang('please input the content')}" class="layui-textarea taonyeditor" style="height: 100px"></textarea>
</div>
</div>
<div class="layui-form-item que-comments">
<input type="hidden" name="article_id" value="{$article.id}" />
<input type="hidden" name="user_id" value="{:session('user_id')}" />
<button class="layui-btn layui-btn-danger" lay-filter="user-comment" lay-submit>{:lang('submit comments')}</button>
</div>
</div>
</form>
</div>
{/if}
</div>
</div>
</section>
</div>
<div class="sidebar layui-col-md4 d-none d-lg-block">
<div class="theiaStickySidebar">
{//广告}
<!--博客详情广告赞助位-->
{:hook('ads_blog_detail_rimg')}
<!-- 联系方式 -->
{:hook('callme_show', ['id' => $article.id, 'uid' => $article.user.id])}
{//热门文章}
<dl class="function" id="rongkeji_remenwenzhang">
<dt class="function_t">热门文章</dt>
<dd class="function_c">
<ul>
{volist name="artHot" id="vo"}
<li style="display: block;">
<div class="list-body fanpian">
<a href="{$vo.url}" target="_blank">{$vo.title}</a>
</div>
<div class="list-footer">
<div class="text-muted text-xs"><time class="d-inline-block">{$vo.create_time|date='Y-m-d'}</time></div>
</div>
</li>
{/volist}
</ul>
</dd>
</dl>
{//标签}
{if count($hotTag) > 0}
<dl class="function" id="rongkeji_retags">
<dt class="function_t">热门标签</dt>
<dd class="function_c">
<ul>
{volist name="hotTag" id="vo"}
<li><a href="{$vo.url}" rel="tag">{$vo.name}</a></li>
{/volist}
</ul>
</dd>
</dl>
{/if}
{//友情链接}
<!--博客详情友链-->
{:hook('ads_blog_detail_flink')}
</div>
</div>
</div>
</div>
{//crud管理模块}
{include file="/public/crud" /}
</main>
{include file="public/menu" /}
{/block}
{block name="script"}
<script type='text/javascript' src="{$Request.domain}/view/common/blog/js/theme.js"></script>
<script>
var collectionFind = "{:url('Collection/find')}",
collection = "{:url('collection/')}",
articleJieset = "{:url('Article/jieset')}",
articleDelete = "{:url('Article/delete')}",
commentJiedaZan = "{:url('Comment/jiedaZan')}",
commentJiedaCai = "{:url('Comment/jiedaCai')}",
commentGetDa = "{:url('Comment/getDa')}",
commentUpdateDa = "{:url('Comment/updateDa')}",
commentJiedaDelete = "{:url('Comment/jiedaDelete')}",
langCollection = "{:lang('collection')}",
langCancelCollection = "{:lang('cancel collection')}";
let taonystatus = "{:hook('taonystatus') ? 1 : 0} ";
layui.use(['laypage'], function(){
var $ = layui.jquery
,form = layui.form
,laypage = layui.laypage;
let uid = layui.cache.user.uid;
let id = "{$article.id}";
let LOGIN_URL = "{:url('login/index')}";
//文章点赞
$("#article-zan").on('click',function(){
//判断登陆
if(uid == -1){
layer.msg('请登录再点赞', {icon: 6}, function(){location.href = LOGIN_URL})
return false;
}
$.post("{:url('article/userZanArticle')}",{article_id:id,type:1},function(data){
if(data.code == 0){
layer.msg(data.msg,{icon:6,time:2000},function () {location.reload(true)});
} else {
layer.msg(data.msg,{icon:6,adim:6});
}
})
});
// 评论
form.on('submit(user-comment)',function (data){
comment(data.field);
});
// 提交回复
form.on('submit(submit-user-comment)', function(data){
comment(data.field);
return false;
})
// 编辑评论
$(".comment-edit").on('click', function () {
var id = $(this).data('id');
layer.open({
type: 2,
title: '修改',
shade: 0.1,
area: ['600px', '500px'],
content: "{:url('comment/edit')}" + '?id=' + id
});
});
// 删除评论
$(".comment-del").on('click', function () {
var id = $(this).data('id');
layer.confirm('需要删除吗?', {icon: 3}, function (){
$.post("{:url('comment/jiedaDelete')}", {id: id}, function (res) {
if (res.status === 0) {
toast.success({title: "成功消息", message: res.msg});
location.reload(true);
} else {
toast.error({title: "失败消息", message: res.msg});
}
})
})
});
// 评论接口
function comment(data){
if (uid == -1) {
layer.msg('请先登陆',{icon:5,time:2000},function(){location.href = LOGIN_URL});
return false;
}
var index = layer.load(1);
$.ajax({
type: "post",
url: "{:url('article/comment')}",
data: data,
dataType: "json",
success:function (res) {
layer.close(index);
if (res.code === 0) {
layer.msg(res.msg,{icon:6,time:2000},function () {location.reload(true)});
}else {
layer.open({title:'评论失败',content:res.msg,icon:5,anim:6});
}
}
});
}
// 回复用户
$("span[type='reply']").on('click',function (){
var pid = $(this).attr('data-pid');
var tid = $(this).data('tid');
var html =
'<form class="layui-form user-comment" style="margin-left:50px;">' +
'<div>' +
'<input type="hidden" name="article_id" value="{$article.id}">' +
'<input name="pid" value="'+ pid +'" class="layui-hide">' +
'<input name="to_user_id" value="'+ tid +'" class="layui-hide">' +
'<textarea name="content" required lay-verify="required" class="layui-textarea taonyeditor" style="height: 100px; right: 5px; margin: 10px 5px;"></textarea>' +
'<button type="submit" class="layui-btn" lay-submit lay-filter="submit-user-comment">提交</button>' +
'</div>' +
'</form>';
var forms = $(this).nextAll('form');
if(forms.length == 0) {
// 移除其它评论块
$('.user-comment').remove();
//动态添加评论块
$(this).next().after(html);
}
})
//解密
$("#jiemi").click(function (){
//判断登陆
if(uid == -1){
layer.msg('请先登录再查看', {icon: 6}, function(){location.href = LOGIN_URL})
return false;
}
var id = "{$article.id}";
layer.prompt(function(value, index, elem){
$.post("{:url('article/jiemi')}",{id:id, art_pass:value},function (res){
layer.close(index);
if(res.code === 0){
layer.msg(res.msg,{icon:6,time:2000},function () {
parent.location.reload(); //刷新父页面注意一定要在关闭当前iframe层之前执行刷新
});
} else {
layer.msg(res.msg,{icon:5,adim:6});
}
});
});
});
//下载
$('#zip-download').click(function (){
var id = "{$article.id}";
$.ajax({
type:"post",
url:"{:url('article/download')}",
data:{id:id},
success:function (data) {
location.href = "{:url('article/download',['id'=>$article.id])}";
}
});
});
// 评论分页
laypage.render({
elem: "pages", //注意,这里的 test1 是 ID不用加 # 号
count: "{$article.comments_count}", //数据总数,从服务端得到
limit: 10,
curr: "{$page}",
//获取起始页
jump: function (obj, first) {
var page = obj.curr;
var limit = obj.limit;
var url = "{:url('article_detail',['id' => $article.id ,'ename' =>$article['cate']['ename']])}";
var id = "{$article.id}";
//首次不执行
if (!first) {
$.post("{:url('article/detail')}", { id: id, page: page }, function () {
location.href = url + '?page=' + page + '#flyReply';
});
}
},
});
});
</script>
<!-- 插件hook位 -->
{:hook('addonhook_detail_js')}
{/block}

View File

@ -66,9 +66,9 @@
{include file="/public/crud" /}
</div>
<!--底部栏-->
<div class="site-tree-mobile-detail-bottom layui-hide-md">
<!-- <div class="site-tree-mobile-detail-bottom layui-hide-md">
<div id="LAY_jieAdmin1" data-id="{$article['id']}"></div>
</div>
</div> -->
</div>
{include file="public/menu" /}