mirror of
https://gitee.com/koogua/course-tencent-cloud.git
synced 2025-06-25 04:07:17 +08:00
Merge branch 'koogua/v1.3.4' into demo
# Conflicts: # scheduler.php
This commit is contained in:
commit
38b2fecbe9
12
CHANGELOG.md
12
CHANGELOG.md
@ -1,3 +1,15 @@
|
|||||||
|
### [v1.3.4](https://gitee.com/koogua/course-tencent-cloud/releases/v1.3.4)(2021-05-13)
|
||||||
|
|
||||||
|
### 更新
|
||||||
|
|
||||||
|
- 前台增加问答功能
|
||||||
|
- 优化标签功能
|
||||||
|
- 优化文章功能以及全文搜索
|
||||||
|
- 优化课程评价,咨询,文章等相关统计
|
||||||
|
- 后台增加提问和回答审核功能
|
||||||
|
- 后台增加查看用户在线记录 修正后台编辑角色权限错误
|
||||||
|
- 优化前台界面
|
||||||
|
|
||||||
### [v1.3.3](https://gitee.com/koogua/course-tencent-cloud/releases/v1.3.3)(2021-04-30)
|
### [v1.3.3](https://gitee.com/koogua/course-tencent-cloud/releases/v1.3.3)(2021-04-30)
|
||||||
|
|
||||||
### 更新
|
### 更新
|
||||||
|
70
app/Builders/AnswerList.php
Normal file
70
app/Builders/AnswerList.php
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Builders;
|
||||||
|
|
||||||
|
use App\Repos\Question as QuestionRepo;
|
||||||
|
use App\Repos\User as UserRepo;
|
||||||
|
|
||||||
|
class AnswerList extends Builder
|
||||||
|
{
|
||||||
|
|
||||||
|
public function handleQuestions(array $answers)
|
||||||
|
{
|
||||||
|
$questions = $this->getQuestions($answers);
|
||||||
|
|
||||||
|
foreach ($answers as $key => $answer) {
|
||||||
|
$answers[$key]['question'] = $questions[$answer['question_id']] ?? new \stdClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $answers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handleUsers(array $answers)
|
||||||
|
{
|
||||||
|
$users = $this->getUsers($answers);
|
||||||
|
|
||||||
|
foreach ($answers as $key => $answer) {
|
||||||
|
$answers[$key]['owner'] = $users[$answer['owner_id']] ?? new \stdClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $answers;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQuestions(array $answers)
|
||||||
|
{
|
||||||
|
$ids = kg_array_column($answers, 'question_id');
|
||||||
|
|
||||||
|
$questionRepo = new QuestionRepo();
|
||||||
|
|
||||||
|
$questions = $questionRepo->findByIds($ids, ['id', 'title']);
|
||||||
|
|
||||||
|
$result = [];
|
||||||
|
|
||||||
|
foreach ($questions->toArray() as $question) {
|
||||||
|
$result[$question['id']] = $question;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUsers(array $answers)
|
||||||
|
{
|
||||||
|
$ids = kg_array_column($answers, 'owner_id');
|
||||||
|
|
||||||
|
$userRepo = new UserRepo();
|
||||||
|
|
||||||
|
$users = $userRepo->findByIds($ids, ['id', 'name', 'avatar']);
|
||||||
|
|
||||||
|
$baseUrl = kg_cos_url();
|
||||||
|
|
||||||
|
$result = [];
|
||||||
|
|
||||||
|
foreach ($users->toArray() as $user) {
|
||||||
|
$user['avatar'] = $baseUrl . $user['avatar'];
|
||||||
|
$result[$user['id']] = $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -4,6 +4,7 @@ namespace App\Builders;
|
|||||||
|
|
||||||
use App\Repos\Article as ArticleRepo;
|
use App\Repos\Article as ArticleRepo;
|
||||||
use App\Repos\User as UserRepo;
|
use App\Repos\User as UserRepo;
|
||||||
|
use Phalcon\Text;
|
||||||
|
|
||||||
class ArticleFavoriteList extends Builder
|
class ArticleFavoriteList extends Builder
|
||||||
{
|
{
|
||||||
@ -38,7 +39,8 @@ class ArticleFavoriteList extends Builder
|
|||||||
|
|
||||||
$columns = [
|
$columns = [
|
||||||
'id', 'title', 'cover',
|
'id', 'title', 'cover',
|
||||||
'view_count', 'like_count', 'comment_count', 'favorite_count',
|
'view_count', 'like_count',
|
||||||
|
'comment_count', 'favorite_count',
|
||||||
];
|
];
|
||||||
|
|
||||||
$articles = $articleRepo->findByIds($ids, $columns);
|
$articles = $articleRepo->findByIds($ids, $columns);
|
||||||
@ -48,7 +50,11 @@ class ArticleFavoriteList extends Builder
|
|||||||
$result = [];
|
$result = [];
|
||||||
|
|
||||||
foreach ($articles->toArray() as $article) {
|
foreach ($articles->toArray() as $article) {
|
||||||
$article['cover'] = $baseUrl . $article['cover'];
|
|
||||||
|
if (!empty($article['cover']) && !Text::startsWith($article['cover'], 'http')) {
|
||||||
|
$article['cover'] = $baseUrl . $article['cover'];
|
||||||
|
}
|
||||||
|
|
||||||
$result[$article['id']] = $article;
|
$result[$article['id']] = $article;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
84
app/Builders/QuestionFavoriteList.php
Normal file
84
app/Builders/QuestionFavoriteList.php
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Builders;
|
||||||
|
|
||||||
|
use App\Repos\Question as QuestionRepo;
|
||||||
|
use App\Repos\User as UserRepo;
|
||||||
|
use Phalcon\Text;
|
||||||
|
|
||||||
|
class QuestionFavoriteList extends Builder
|
||||||
|
{
|
||||||
|
|
||||||
|
public function handleQuestions(array $relations)
|
||||||
|
{
|
||||||
|
$questions = $this->getQuestions($relations);
|
||||||
|
|
||||||
|
foreach ($relations as $key => $value) {
|
||||||
|
$relations[$key]['question'] = $questions[$value['question_id']] ?? new \stdClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $relations;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handleUsers(array $relations)
|
||||||
|
{
|
||||||
|
$users = $this->getUsers($relations);
|
||||||
|
|
||||||
|
foreach ($relations as $key => $value) {
|
||||||
|
$relations[$key]['user'] = $users[$value['user_id']] ?? new \stdClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $relations;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQuestions(array $relations)
|
||||||
|
{
|
||||||
|
$ids = kg_array_column($relations, 'question_id');
|
||||||
|
|
||||||
|
$questionRepo = new QuestionRepo();
|
||||||
|
|
||||||
|
$columns = [
|
||||||
|
'id', 'title', 'cover',
|
||||||
|
'view_count', 'like_count',
|
||||||
|
'answer_count', 'favorite_count',
|
||||||
|
];
|
||||||
|
|
||||||
|
$questions = $questionRepo->findByIds($ids, $columns);
|
||||||
|
|
||||||
|
$baseUrl = kg_cos_url();
|
||||||
|
|
||||||
|
$result = [];
|
||||||
|
|
||||||
|
foreach ($questions->toArray() as $question) {
|
||||||
|
|
||||||
|
if (!empty($question['cover']) && !Text::startsWith($question['cover'], 'http')) {
|
||||||
|
$question['cover'] = $baseUrl . $question['cover'];
|
||||||
|
}
|
||||||
|
|
||||||
|
$result[$question['id']] = $question;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUsers(array $relations)
|
||||||
|
{
|
||||||
|
$ids = kg_array_column($relations, 'user_id');
|
||||||
|
|
||||||
|
$userRepo = new UserRepo();
|
||||||
|
|
||||||
|
$users = $userRepo->findByIds($ids, ['id', 'name', 'avatar']);
|
||||||
|
|
||||||
|
$baseUrl = kg_cos_url();
|
||||||
|
|
||||||
|
$result = [];
|
||||||
|
|
||||||
|
foreach ($users->toArray() as $user) {
|
||||||
|
$user['avatar'] = $baseUrl . $user['avatar'];
|
||||||
|
$result[$user['id']] = $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
86
app/Builders/QuestionList.php
Normal file
86
app/Builders/QuestionList.php
Normal file
@ -0,0 +1,86 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Builders;
|
||||||
|
|
||||||
|
use App\Caches\CategoryList as CategoryListCache;
|
||||||
|
use App\Models\Category as CategoryModel;
|
||||||
|
use App\Repos\User as UserRepo;
|
||||||
|
|
||||||
|
class QuestionList extends Builder
|
||||||
|
{
|
||||||
|
|
||||||
|
public function handleQuestions(array $questions)
|
||||||
|
{
|
||||||
|
foreach ($questions as $key => $question) {
|
||||||
|
$questions[$key]['tags'] = json_decode($question['tags'], true);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $questions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handleCategories(array $articles)
|
||||||
|
{
|
||||||
|
$categories = $this->getCategories();
|
||||||
|
|
||||||
|
foreach ($articles as $key => $article) {
|
||||||
|
$articles[$key]['category'] = $categories[$article['category_id']] ?? new \stdClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $articles;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handleUsers(array $questions)
|
||||||
|
{
|
||||||
|
$users = $this->getUsers($questions);
|
||||||
|
|
||||||
|
foreach ($questions as $key => $question) {
|
||||||
|
$questions[$key]['owner'] = $users[$question['owner_id']] ?? new \stdClass();
|
||||||
|
$questions[$key]['last_replier'] = $users[$question['last_replier_id']] ?? new \stdClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $questions;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCategories()
|
||||||
|
{
|
||||||
|
$cache = new CategoryListCache();
|
||||||
|
|
||||||
|
$items = $cache->get(CategoryModel::TYPE_QUESTION);
|
||||||
|
|
||||||
|
if (empty($items)) return [];
|
||||||
|
|
||||||
|
$result = [];
|
||||||
|
|
||||||
|
foreach ($items as $item) {
|
||||||
|
$result[$item['id']] = [
|
||||||
|
'id' => $item['id'],
|
||||||
|
'name' => $item['name'],
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUsers($questions)
|
||||||
|
{
|
||||||
|
$ownerIds = kg_array_column($questions, 'owner_id');
|
||||||
|
$lastReplierIds = kg_array_column($questions, 'last_replier_id');
|
||||||
|
$ids = array_merge($ownerIds, $lastReplierIds);
|
||||||
|
|
||||||
|
$userRepo = new UserRepo();
|
||||||
|
|
||||||
|
$users = $userRepo->findByIds($ids, ['id', 'name', 'avatar']);
|
||||||
|
|
||||||
|
$baseUrl = kg_cos_url();
|
||||||
|
|
||||||
|
$result = [];
|
||||||
|
|
||||||
|
foreach ($users->toArray() as $user) {
|
||||||
|
$user['avatar'] = $baseUrl . $user['avatar'];
|
||||||
|
$result[$user['id']] = $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
75
app/Builders/TagFollowList.php
Normal file
75
app/Builders/TagFollowList.php
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Builders;
|
||||||
|
|
||||||
|
use App\Repos\Tag as TagRepo;
|
||||||
|
use App\Repos\User as UserRepo;
|
||||||
|
|
||||||
|
class TagFollowList extends Builder
|
||||||
|
{
|
||||||
|
|
||||||
|
public function handleTags(array $relations)
|
||||||
|
{
|
||||||
|
$tags = $this->getTags($relations);
|
||||||
|
|
||||||
|
foreach ($relations as $key => $value) {
|
||||||
|
$relations[$key]['tag'] = $tags[$value['tag_id']] ?? new \stdClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $relations;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handleUsers(array $relations)
|
||||||
|
{
|
||||||
|
$users = $this->getUsers($relations);
|
||||||
|
|
||||||
|
foreach ($relations as $key => $value) {
|
||||||
|
$relations[$key]['user'] = $users[$value['user_id']] ?? new \stdClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $relations;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTags(array $relations)
|
||||||
|
{
|
||||||
|
$ids = kg_array_column($relations, 'tag_id');
|
||||||
|
|
||||||
|
$tagRepo = new TagRepo();
|
||||||
|
|
||||||
|
$columns = ['id', 'name', 'alias', 'icon', 'follow_count'];
|
||||||
|
|
||||||
|
$tags = $tagRepo->findByIds($ids, $columns);
|
||||||
|
|
||||||
|
$baseUrl = kg_cos_url();
|
||||||
|
|
||||||
|
$result = [];
|
||||||
|
|
||||||
|
foreach ($tags->toArray() as $tag) {
|
||||||
|
$tag['icon'] = $baseUrl . $tag['icon'];
|
||||||
|
$result[$tag['id']] = $tag;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getUsers(array $relations)
|
||||||
|
{
|
||||||
|
$ids = kg_array_column($relations, 'user_id');
|
||||||
|
|
||||||
|
$userRepo = new UserRepo();
|
||||||
|
|
||||||
|
$users = $userRepo->findByIds($ids, ['id', 'name', 'avatar']);
|
||||||
|
|
||||||
|
$baseUrl = kg_cos_url();
|
||||||
|
|
||||||
|
$result = [];
|
||||||
|
|
||||||
|
foreach ($users->toArray() as $user) {
|
||||||
|
$user['avatar'] = $baseUrl . $user['avatar'];
|
||||||
|
$result[$user['id']] = $user;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
114
app/Caches/HotQuestionList.php
Normal file
114
app/Caches/HotQuestionList.php
Normal file
@ -0,0 +1,114 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Caches;
|
||||||
|
|
||||||
|
use App\Models\Question as QuestionModel;
|
||||||
|
use Phalcon\Mvc\Model\Resultset;
|
||||||
|
use Phalcon\Mvc\Model\ResultsetInterface;
|
||||||
|
|
||||||
|
class HotQuestionList extends Cache
|
||||||
|
{
|
||||||
|
|
||||||
|
protected $lifetime = 1 * 86400;
|
||||||
|
|
||||||
|
public function getLifetime()
|
||||||
|
{
|
||||||
|
$tomorrow = strtotime('tomorrow');
|
||||||
|
|
||||||
|
return $tomorrow - time();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getKey($id = null)
|
||||||
|
{
|
||||||
|
return 'hot_question_list';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getContent($id = null)
|
||||||
|
{
|
||||||
|
$questions = $this->findWeeklyHotQuestions();
|
||||||
|
|
||||||
|
if ($questions->count() > 0) {
|
||||||
|
return $this->handleQuestions($questions);
|
||||||
|
}
|
||||||
|
|
||||||
|
$questions = $this->findMonthlyHotQuestions();
|
||||||
|
|
||||||
|
if ($questions->count() > 0) {
|
||||||
|
return $this->handleQuestions($questions);
|
||||||
|
}
|
||||||
|
|
||||||
|
$questions = $this->findYearlyHotQuestions();
|
||||||
|
|
||||||
|
if ($questions->count() > 0) {
|
||||||
|
return $this->handleQuestions($questions);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param QuestionModel[] $questions
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
protected function handleQuestions($questions)
|
||||||
|
{
|
||||||
|
$result = [];
|
||||||
|
|
||||||
|
foreach ($questions as $question) {
|
||||||
|
$result[] = [
|
||||||
|
'id' => $question->id,
|
||||||
|
'title' => $question->title,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $limit
|
||||||
|
* @return ResultsetInterface|Resultset|QuestionModel[]
|
||||||
|
*/
|
||||||
|
protected function findWeeklyHotQuestions($limit = 10)
|
||||||
|
{
|
||||||
|
$createTime = strtotime('monday this week');
|
||||||
|
|
||||||
|
return $this->findHotQuestions($createTime, $limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $limit
|
||||||
|
* @return ResultsetInterface|Resultset|QuestionModel[]
|
||||||
|
*/
|
||||||
|
protected function findMonthlyHotQuestions($limit = 10)
|
||||||
|
{
|
||||||
|
$createTime = strtotime(date('Y-m-01'));
|
||||||
|
|
||||||
|
return $this->findHotQuestions($createTime, $limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $limit
|
||||||
|
* @return ResultsetInterface|Resultset|QuestionModel[]
|
||||||
|
*/
|
||||||
|
protected function findYearlyHotQuestions($limit = 10)
|
||||||
|
{
|
||||||
|
$createTime = strtotime(date('Y-01-01'));
|
||||||
|
|
||||||
|
return $this->findHotQuestions($createTime, $limit);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $createTime
|
||||||
|
* @param int $limit
|
||||||
|
* @return ResultsetInterface|Resultset|QuestionModel[]
|
||||||
|
*/
|
||||||
|
protected function findHotQuestions($createTime, $limit = 10)
|
||||||
|
{
|
||||||
|
return QuestionModel::query()
|
||||||
|
->where('create_time > :create_time:', ['create_time' => $createTime])
|
||||||
|
->orderBy('score DESC')
|
||||||
|
->limit($limit)
|
||||||
|
->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
29
app/Caches/MaxAnswerId.php
Normal file
29
app/Caches/MaxAnswerId.php
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Caches;
|
||||||
|
|
||||||
|
use App\Models\Answer as AnswerModel;
|
||||||
|
|
||||||
|
class MaxAnswerId extends Cache
|
||||||
|
{
|
||||||
|
|
||||||
|
protected $lifetime = 365 * 86400;
|
||||||
|
|
||||||
|
public function getLifetime()
|
||||||
|
{
|
||||||
|
return $this->lifetime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getKey($id = null)
|
||||||
|
{
|
||||||
|
return 'max_answer_id';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getContent($id = null)
|
||||||
|
{
|
||||||
|
$answer = AnswerModel::findFirst(['order' => 'id DESC']);
|
||||||
|
|
||||||
|
return $answer->id ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
29
app/Caches/MaxQuestionId.php
Normal file
29
app/Caches/MaxQuestionId.php
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Caches;
|
||||||
|
|
||||||
|
use App\Models\Question as QuestionModel;
|
||||||
|
|
||||||
|
class MaxQuestionId extends Cache
|
||||||
|
{
|
||||||
|
|
||||||
|
protected $lifetime = 365 * 86400;
|
||||||
|
|
||||||
|
public function getLifetime()
|
||||||
|
{
|
||||||
|
return $this->lifetime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getKey($id = null)
|
||||||
|
{
|
||||||
|
return 'max_question_id';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getContent($id = null)
|
||||||
|
{
|
||||||
|
$question = QuestionModel::findFirst(['order' => 'id DESC']);
|
||||||
|
|
||||||
|
return $question->id ?? 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
31
app/Caches/Question.php
Normal file
31
app/Caches/Question.php
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Caches;
|
||||||
|
|
||||||
|
use App\Repos\Question as QuestionRepo;
|
||||||
|
|
||||||
|
class Question extends Cache
|
||||||
|
{
|
||||||
|
|
||||||
|
protected $lifetime = 1 * 86400;
|
||||||
|
|
||||||
|
public function getLifetime()
|
||||||
|
{
|
||||||
|
return $this->lifetime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getKey($id = null)
|
||||||
|
{
|
||||||
|
return "question:{$id}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getContent($id = null)
|
||||||
|
{
|
||||||
|
$questionRepo = new QuestionRepo();
|
||||||
|
|
||||||
|
$question = $questionRepo->findById($id);
|
||||||
|
|
||||||
|
return $question ?: null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -5,11 +5,9 @@ namespace App\Caches;
|
|||||||
use App\Models\Article as ArticleModel;
|
use App\Models\Article as ArticleModel;
|
||||||
use App\Repos\Article as ArticleRepo;
|
use App\Repos\Article as ArticleRepo;
|
||||||
|
|
||||||
class ArticleRelatedList extends Cache
|
class TaggedArticleList extends Cache
|
||||||
{
|
{
|
||||||
|
|
||||||
protected $articleId;
|
|
||||||
|
|
||||||
protected $limit = 5;
|
protected $limit = 5;
|
||||||
|
|
||||||
protected $lifetime = 1 * 86400;
|
protected $lifetime = 1 * 86400;
|
||||||
@ -21,25 +19,15 @@ class ArticleRelatedList extends Cache
|
|||||||
|
|
||||||
public function getKey($id = null)
|
public function getKey($id = null)
|
||||||
{
|
{
|
||||||
return "article_related_list:{$id}";
|
return "tagged_article_list:{$id}";
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getContent($id = null)
|
public function getContent($id = null)
|
||||||
{
|
{
|
||||||
$this->articleId = $id;
|
|
||||||
|
|
||||||
$articleRepo = new ArticleRepo();
|
$articleRepo = new ArticleRepo();
|
||||||
|
|
||||||
$article = $articleRepo->findById($id);
|
|
||||||
|
|
||||||
if (empty($article->tags)) return [];
|
|
||||||
|
|
||||||
$tagIds = kg_array_column($article->tags, 'id');
|
|
||||||
|
|
||||||
$randKey = array_rand($tagIds);
|
|
||||||
|
|
||||||
$where = [
|
$where = [
|
||||||
'tag_id' => $tagIds[$randKey],
|
'tag_id' => $id,
|
||||||
'published' => ArticleModel::PUBLISH_APPROVED,
|
'published' => ArticleModel::PUBLISH_APPROVED,
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -61,7 +49,7 @@ class ArticleRelatedList extends Cache
|
|||||||
$count = 0;
|
$count = 0;
|
||||||
|
|
||||||
foreach ($articles as $article) {
|
foreach ($articles as $article) {
|
||||||
if ($article->id != $this->articleId && $count < $this->limit) {
|
if ($count < $this->limit) {
|
||||||
$result[] = [
|
$result[] = [
|
||||||
'id' => $article->id,
|
'id' => $article->id,
|
||||||
'title' => $article->title,
|
'title' => $article->title,
|
69
app/Caches/TaggedQuestionList.php
Normal file
69
app/Caches/TaggedQuestionList.php
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Caches;
|
||||||
|
|
||||||
|
use App\Models\Question as QuestionModel;
|
||||||
|
use App\Repos\Question as QuestionRepo;
|
||||||
|
|
||||||
|
class TaggedQuestionList extends Cache
|
||||||
|
{
|
||||||
|
|
||||||
|
protected $limit = 5;
|
||||||
|
|
||||||
|
protected $lifetime = 1 * 86400;
|
||||||
|
|
||||||
|
public function getLifetime()
|
||||||
|
{
|
||||||
|
return $this->lifetime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getKey($id = null)
|
||||||
|
{
|
||||||
|
return "tagged_question_list:{$id}";
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getContent($id = null)
|
||||||
|
{
|
||||||
|
|
||||||
|
$questionRepo = new QuestionRepo();
|
||||||
|
|
||||||
|
$where = [
|
||||||
|
'tag_id' => $id,
|
||||||
|
'published' => QuestionModel::PUBLISH_APPROVED,
|
||||||
|
];
|
||||||
|
|
||||||
|
$pager = $questionRepo->paginate($where);
|
||||||
|
|
||||||
|
if ($pager->total_items == 0) return [];
|
||||||
|
|
||||||
|
return $this->handleContent($pager->items);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param QuestionModel[] $questions
|
||||||
|
* @return array
|
||||||
|
*/
|
||||||
|
public function handleContent($questions)
|
||||||
|
{
|
||||||
|
$result = [];
|
||||||
|
|
||||||
|
$count = 0;
|
||||||
|
|
||||||
|
foreach ($questions as $question) {
|
||||||
|
if ($count < $this->limit) {
|
||||||
|
$result[] = [
|
||||||
|
'id' => $question->id,
|
||||||
|
'title' => $question->title,
|
||||||
|
'view_count' => $question->view_count,
|
||||||
|
'like_count' => $question->like_count,
|
||||||
|
'answer_count' => $question->answer_count,
|
||||||
|
'favorite_count' => $question->favorite_count,
|
||||||
|
];
|
||||||
|
$count++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
103
app/Caches/TopAnswererList.php
Normal file
103
app/Caches/TopAnswererList.php
Normal file
@ -0,0 +1,103 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Caches;
|
||||||
|
|
||||||
|
use App\Models\Article as ArticleModel;
|
||||||
|
use App\Models\ArticleLike as ArticleLikeModel;
|
||||||
|
use App\Repos\User as UserRepo;
|
||||||
|
use Phalcon\Mvc\Model\Resultset;
|
||||||
|
use Phalcon\Mvc\Model\ResultsetInterface;
|
||||||
|
|
||||||
|
class TopAnswererList extends Cache
|
||||||
|
{
|
||||||
|
|
||||||
|
protected $lifetime = 1 * 86400;
|
||||||
|
|
||||||
|
public function getLifetime()
|
||||||
|
{
|
||||||
|
return $this->lifetime;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getKey($id = null)
|
||||||
|
{
|
||||||
|
return 'question_top_answerer_list';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getContent($id = null)
|
||||||
|
{
|
||||||
|
$rankings = $this->findWeeklyAuthorRankings();
|
||||||
|
|
||||||
|
if ($rankings->count() > 0) {
|
||||||
|
$userIds = kg_array_column($rankings->toArray(), 'author_id');
|
||||||
|
return $this->handleUsers($userIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
$randOwners = $this->findRandArticleOwners();
|
||||||
|
|
||||||
|
if ($randOwners->count() > 0) {
|
||||||
|
$userIds = kg_array_column($randOwners->toArray(), 'owner_id');
|
||||||
|
return $this->handleUsers($userIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function handleUsers($userIds)
|
||||||
|
{
|
||||||
|
$userRepo = new UserRepo();
|
||||||
|
|
||||||
|
$users = $userRepo->findByIds($userIds);
|
||||||
|
|
||||||
|
$result = [];
|
||||||
|
|
||||||
|
foreach ($users as $user) {
|
||||||
|
$result[] = [
|
||||||
|
'id' => $user->id,
|
||||||
|
'name' => $user->name,
|
||||||
|
'avatar' => $user->avatar,
|
||||||
|
'title' => $user->title,
|
||||||
|
'about' => $user->about,
|
||||||
|
'vip' => $user->vip,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $limit
|
||||||
|
* @return ResultsetInterface|Resultset
|
||||||
|
*/
|
||||||
|
protected function findRandArticleOwners($limit = 10)
|
||||||
|
{
|
||||||
|
return ArticleModel::query()
|
||||||
|
->columns(['owner_id'])
|
||||||
|
->orderBy('RAND()')
|
||||||
|
->limit($limit)
|
||||||
|
->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param int $limit
|
||||||
|
* @return ResultsetInterface|Resultset
|
||||||
|
*/
|
||||||
|
protected function findWeeklyAuthorRankings($limit = 10)
|
||||||
|
{
|
||||||
|
$createTime = strtotime('monday this week');
|
||||||
|
|
||||||
|
$columns = [
|
||||||
|
'author_id' => 'a.owner_id',
|
||||||
|
'like_count' => 'count(al.user_id)',
|
||||||
|
];
|
||||||
|
|
||||||
|
return $this->modelsManager->createBuilder()
|
||||||
|
->columns($columns)
|
||||||
|
->addFrom(ArticleLikeModel::class, 'al')
|
||||||
|
->join(ArticleModel::class, 'al.article_id = a.id', 'a')
|
||||||
|
->where('al.create_time > :create_time:', ['create_time' => $createTime])
|
||||||
|
->groupBy('author_id')
|
||||||
|
->orderBy('like_count DESC')
|
||||||
|
->limit($limit)->getQuery()->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -118,7 +118,7 @@ class ArticleIndexTask extends Task
|
|||||||
protected function findArticles()
|
protected function findArticles()
|
||||||
{
|
{
|
||||||
return ArticleModel::query()
|
return ArticleModel::query()
|
||||||
->where('published = 1')
|
->where('published = :published:', ['published' => ArticleModel::PUBLISH_APPROVED])
|
||||||
->execute();
|
->execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
125
app/Console/Tasks/QuestionIndexTask.php
Normal file
125
app/Console/Tasks/QuestionIndexTask.php
Normal file
@ -0,0 +1,125 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Tasks;
|
||||||
|
|
||||||
|
use App\Models\Question as QuestionModel;
|
||||||
|
use App\Services\Search\QuestionDocument;
|
||||||
|
use App\Services\Search\QuestionSearcher;
|
||||||
|
use Phalcon\Mvc\Model\Resultset;
|
||||||
|
use Phalcon\Mvc\Model\ResultsetInterface;
|
||||||
|
|
||||||
|
class QuestionIndexTask extends Task
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 搜索测试
|
||||||
|
*
|
||||||
|
* @command: php console.php question_index search {query}
|
||||||
|
* @param array $params
|
||||||
|
* @throws \XSException
|
||||||
|
*/
|
||||||
|
public function searchAction($params)
|
||||||
|
{
|
||||||
|
$query = $params[0] ?? null;
|
||||||
|
|
||||||
|
if (!$query) {
|
||||||
|
exit('please special a query word' . PHP_EOL);
|
||||||
|
}
|
||||||
|
|
||||||
|
$result = $this->searchQuestions($query);
|
||||||
|
|
||||||
|
var_export($result);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清空索引
|
||||||
|
*
|
||||||
|
* @command: php console.php question_index clean
|
||||||
|
*/
|
||||||
|
public function cleanAction()
|
||||||
|
{
|
||||||
|
$this->cleanQuestionIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重建索引
|
||||||
|
*
|
||||||
|
* @command: php console.php question_index rebuild
|
||||||
|
*/
|
||||||
|
public function rebuildAction()
|
||||||
|
{
|
||||||
|
$this->rebuildQuestionIndex();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清空索引
|
||||||
|
*/
|
||||||
|
protected function cleanQuestionIndex()
|
||||||
|
{
|
||||||
|
$handler = new QuestionSearcher();
|
||||||
|
|
||||||
|
$index = $handler->getXS()->getIndex();
|
||||||
|
|
||||||
|
echo '------ start clean question index ------' . PHP_EOL;
|
||||||
|
|
||||||
|
$index->clean();
|
||||||
|
|
||||||
|
echo '------ end clean question index ------' . PHP_EOL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 重建索引
|
||||||
|
*/
|
||||||
|
protected function rebuildQuestionIndex()
|
||||||
|
{
|
||||||
|
$questions = $this->findQuestions();
|
||||||
|
|
||||||
|
if ($questions->count() == 0) return;
|
||||||
|
|
||||||
|
$handler = new QuestionSearcher();
|
||||||
|
|
||||||
|
$documenter = new QuestionDocument();
|
||||||
|
|
||||||
|
$index = $handler->getXS()->getIndex();
|
||||||
|
|
||||||
|
echo '------ start rebuild question index ------' . PHP_EOL;
|
||||||
|
|
||||||
|
$index->beginRebuild();
|
||||||
|
|
||||||
|
foreach ($questions as $question) {
|
||||||
|
$document = $documenter->setDocument($question);
|
||||||
|
$index->add($document);
|
||||||
|
}
|
||||||
|
|
||||||
|
$index->endRebuild();
|
||||||
|
|
||||||
|
echo '------ end rebuild question index ------' . PHP_EOL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 搜索文章
|
||||||
|
*
|
||||||
|
* @param string $query
|
||||||
|
* @return array
|
||||||
|
* @throws \XSException
|
||||||
|
*/
|
||||||
|
protected function searchQuestions($query)
|
||||||
|
{
|
||||||
|
$handler = new QuestionSearcher();
|
||||||
|
|
||||||
|
return $handler->search($query);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查找文章
|
||||||
|
*
|
||||||
|
* @return ResultsetInterface|Resultset|QuestionModel[]
|
||||||
|
*/
|
||||||
|
protected function findQuestions()
|
||||||
|
{
|
||||||
|
return QuestionModel::query()
|
||||||
|
->where('published = :published:', ['published' => QuestionModel::PUBLISH_APPROVED])
|
||||||
|
->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -8,6 +8,7 @@ use App\Models\Course as CourseModel;
|
|||||||
use App\Models\Help as HelpModel;
|
use App\Models\Help as HelpModel;
|
||||||
use App\Models\ImGroup as ImGroupModel;
|
use App\Models\ImGroup as ImGroupModel;
|
||||||
use App\Models\Page as PageModel;
|
use App\Models\Page as PageModel;
|
||||||
|
use App\Models\Question as QuestionModel;
|
||||||
use App\Models\Topic as TopicModel;
|
use App\Models\Topic as TopicModel;
|
||||||
use App\Models\User as UserModel;
|
use App\Models\User as UserModel;
|
||||||
use App\Services\Service as AppService;
|
use App\Services\Service as AppService;
|
||||||
@ -37,6 +38,7 @@ class SitemapTask extends Task
|
|||||||
$this->addIndex();
|
$this->addIndex();
|
||||||
$this->addCourses();
|
$this->addCourses();
|
||||||
$this->addArticles();
|
$this->addArticles();
|
||||||
|
$this->addQuestions();
|
||||||
$this->addTeachers();
|
$this->addTeachers();
|
||||||
$this->addTopics();
|
$this->addTopics();
|
||||||
$this->addImGroups();
|
$this->addImGroups();
|
||||||
@ -66,7 +68,11 @@ class SitemapTask extends Task
|
|||||||
/**
|
/**
|
||||||
* @var Resultset|CourseModel[] $courses
|
* @var Resultset|CourseModel[] $courses
|
||||||
*/
|
*/
|
||||||
$courses = CourseModel::query()->where('published = 1')->execute();
|
$courses = CourseModel::query()
|
||||||
|
->where('published = 1')
|
||||||
|
->orderBy('id DESC')
|
||||||
|
->limit(500)
|
||||||
|
->execute();
|
||||||
|
|
||||||
if ($courses->count() == 0) return;
|
if ($courses->count() == 0) return;
|
||||||
|
|
||||||
@ -81,7 +87,11 @@ class SitemapTask extends Task
|
|||||||
/**
|
/**
|
||||||
* @var Resultset|ArticleModel[] $articles
|
* @var Resultset|ArticleModel[] $articles
|
||||||
*/
|
*/
|
||||||
$articles = ArticleModel::query()->where('published = 1')->execute();
|
$articles = ArticleModel::query()
|
||||||
|
->where('published = :published:', ['published' => ArticleModel::PUBLISH_APPROVED])
|
||||||
|
->orderBy('id DESC')
|
||||||
|
->limit(500)
|
||||||
|
->execute();
|
||||||
|
|
||||||
if ($articles->count() == 0) return;
|
if ($articles->count() == 0) return;
|
||||||
|
|
||||||
@ -91,6 +101,25 @@ class SitemapTask extends Task
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function addQuestions()
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* @var Resultset|QuestionModel[] $questions
|
||||||
|
*/
|
||||||
|
$questions = QuestionModel::query()
|
||||||
|
->where('published = :published:', ['published' => QuestionModel::PUBLISH_APPROVED])
|
||||||
|
->orderBy('id DESC')
|
||||||
|
->limit(500)
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
if ($questions->count() == 0) return;
|
||||||
|
|
||||||
|
foreach ($questions as $question) {
|
||||||
|
$loc = sprintf('%s/question/%s', $this->siteUrl, $question->id);
|
||||||
|
$this->sitemap->addItem($loc, 0.8);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
protected function addTeachers()
|
protected function addTeachers()
|
||||||
{
|
{
|
||||||
/**
|
/**
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
namespace App\Console\Tasks;
|
namespace App\Console\Tasks;
|
||||||
|
|
||||||
|
use App\Models\Article as ArticleModel;
|
||||||
use App\Repos\Article as ArticleRepo;
|
use App\Repos\Article as ArticleRepo;
|
||||||
use App\Services\Search\ArticleDocument;
|
use App\Services\Search\ArticleDocument;
|
||||||
use App\Services\Search\ArticleSearcher;
|
use App\Services\Search\ArticleSearcher;
|
||||||
@ -38,7 +39,7 @@ class SyncArticleIndexTask extends Task
|
|||||||
|
|
||||||
$doc = $document->setDocument($article);
|
$doc = $document->setDocument($article);
|
||||||
|
|
||||||
if ($article->published == 1) {
|
if ($article->published == ArticleModel::PUBLISH_APPROVED) {
|
||||||
$index->update($doc);
|
$index->update($doc);
|
||||||
} else {
|
} else {
|
||||||
$index->del($article->id);
|
$index->del($article->id);
|
||||||
|
44
app/Console/Tasks/SyncArticleScoreTask.php
Normal file
44
app/Console/Tasks/SyncArticleScoreTask.php
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Tasks;
|
||||||
|
|
||||||
|
use App\Repos\Article as ArticleRepo;
|
||||||
|
use App\Services\Sync\ArticleScore as ArticleScoreSync;
|
||||||
|
use App\Services\Utils\ArticleScore as ArticleScoreService;
|
||||||
|
|
||||||
|
class SyncArticleScoreTask extends Task
|
||||||
|
{
|
||||||
|
|
||||||
|
public function mainAction()
|
||||||
|
{
|
||||||
|
$redis = $this->getRedis();
|
||||||
|
|
||||||
|
$key = $this->getSyncKey();
|
||||||
|
|
||||||
|
$articleIds = $redis->sRandMember($key, 1000);
|
||||||
|
|
||||||
|
if (!$articleIds) return;
|
||||||
|
|
||||||
|
$articleRepo = new ArticleRepo();
|
||||||
|
|
||||||
|
$articles = $articleRepo->findByIds($articleIds);
|
||||||
|
|
||||||
|
if ($articles->count() == 0) return;
|
||||||
|
|
||||||
|
$service = new ArticleScoreService();
|
||||||
|
|
||||||
|
foreach ($articles as $article) {
|
||||||
|
$service->handle($article);
|
||||||
|
}
|
||||||
|
|
||||||
|
$redis->sRem($key, ...$articleIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getSyncKey()
|
||||||
|
{
|
||||||
|
$sync = new ArticleScoreSync();
|
||||||
|
|
||||||
|
return $sync->getSyncKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -3,8 +3,8 @@
|
|||||||
namespace App\Console\Tasks;
|
namespace App\Console\Tasks;
|
||||||
|
|
||||||
use App\Repos\Course as CourseRepo;
|
use App\Repos\Course as CourseRepo;
|
||||||
use App\Services\CourseStat as CourseStatService;
|
|
||||||
use App\Services\Sync\CourseScore as CourseScoreSync;
|
use App\Services\Sync\CourseScore as CourseScoreSync;
|
||||||
|
use App\Services\Utils\CourseScore as CourseScoreService;
|
||||||
|
|
||||||
class SyncCourseScoreTask extends Task
|
class SyncCourseScoreTask extends Task
|
||||||
{
|
{
|
||||||
@ -25,10 +25,10 @@ class SyncCourseScoreTask extends Task
|
|||||||
|
|
||||||
if ($courses->count() == 0) return;
|
if ($courses->count() == 0) return;
|
||||||
|
|
||||||
$statService = new CourseStatService();
|
$service = new CourseScoreService();
|
||||||
|
|
||||||
foreach ($courses as $course) {
|
foreach ($courses as $course) {
|
||||||
$statService->updateScore($course->id);
|
$service->handle($course);
|
||||||
}
|
}
|
||||||
|
|
||||||
$redis->sRem($key, ...$courseIds);
|
$redis->sRem($key, ...$courseIds);
|
||||||
|
61
app/Console/Tasks/SyncQuestionIndexTask.php
Normal file
61
app/Console/Tasks/SyncQuestionIndexTask.php
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Tasks;
|
||||||
|
|
||||||
|
use App\Models\Question as QuestionModel;
|
||||||
|
use App\Repos\Question as QuestionRepo;
|
||||||
|
use App\Services\Search\QuestionDocument;
|
||||||
|
use App\Services\Search\QuestionSearcher;
|
||||||
|
use App\Services\Sync\QuestionIndex as QuestionIndexSync;
|
||||||
|
|
||||||
|
class SyncQuestionIndexTask extends Task
|
||||||
|
{
|
||||||
|
|
||||||
|
public function mainAction()
|
||||||
|
{
|
||||||
|
$redis = $this->getRedis();
|
||||||
|
|
||||||
|
$key = $this->getSyncKey();
|
||||||
|
|
||||||
|
$questionIds = $redis->sRandMember($key, 1000);
|
||||||
|
|
||||||
|
if (!$questionIds) return;
|
||||||
|
|
||||||
|
$questionRepo = new QuestionRepo();
|
||||||
|
|
||||||
|
$questions = $questionRepo->findByIds($questionIds);
|
||||||
|
|
||||||
|
if ($questions->count() == 0) return;
|
||||||
|
|
||||||
|
$document = new QuestionDocument();
|
||||||
|
|
||||||
|
$handler = new QuestionSearcher();
|
||||||
|
|
||||||
|
$index = $handler->getXS()->getIndex();
|
||||||
|
|
||||||
|
$index->openBuffer();
|
||||||
|
|
||||||
|
foreach ($questions as $question) {
|
||||||
|
|
||||||
|
$doc = $document->setDocument($question);
|
||||||
|
|
||||||
|
if ($question->published == QuestionModel::PUBLISH_APPROVED) {
|
||||||
|
$index->update($doc);
|
||||||
|
} else {
|
||||||
|
$index->del($question->id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$index->closeBuffer();
|
||||||
|
|
||||||
|
$redis->sRem($key, ...$questionIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getSyncKey()
|
||||||
|
{
|
||||||
|
$sync = new QuestionIndexSync();
|
||||||
|
|
||||||
|
return $sync->getSyncKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
44
app/Console/Tasks/SyncQuestionScoreTask.php
Normal file
44
app/Console/Tasks/SyncQuestionScoreTask.php
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Console\Tasks;
|
||||||
|
|
||||||
|
use App\Repos\Question as QuestionRepo;
|
||||||
|
use App\Services\Sync\QuestionScore as QuestionScoreSync;
|
||||||
|
use App\Services\Utils\QuestionScore as QuestionScoreService;
|
||||||
|
|
||||||
|
class SyncQuestionScoreTask extends Task
|
||||||
|
{
|
||||||
|
|
||||||
|
public function mainAction()
|
||||||
|
{
|
||||||
|
$redis = $this->getRedis();
|
||||||
|
|
||||||
|
$key = $this->getSyncKey();
|
||||||
|
|
||||||
|
$questionIds = $redis->sRandMember($key, 1000);
|
||||||
|
|
||||||
|
if (!$questionIds) return;
|
||||||
|
|
||||||
|
$questionRepo = new QuestionRepo();
|
||||||
|
|
||||||
|
$questions = $questionRepo->findByIds($questionIds);
|
||||||
|
|
||||||
|
if ($questions->count() == 0) return;
|
||||||
|
|
||||||
|
$service = new QuestionScoreService();
|
||||||
|
|
||||||
|
foreach ($questions as $question) {
|
||||||
|
$service->handle($question);
|
||||||
|
}
|
||||||
|
|
||||||
|
$redis->sRem($key, ...$questionIds);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getSyncKey()
|
||||||
|
{
|
||||||
|
$sync = new QuestionScoreSync();
|
||||||
|
|
||||||
|
return $sync->getSyncKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
201
app/Http/Admin/Controllers/AnswerController.php
Normal file
201
app/Http/Admin/Controllers/AnswerController.php
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Admin\Controllers;
|
||||||
|
|
||||||
|
use App\Http\Admin\Services\Answer as AnswerService;
|
||||||
|
use App\Http\Admin\Services\Question as QuestionService;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @RoutePrefix("/admin/answer")
|
||||||
|
*/
|
||||||
|
class AnswerController extends Controller
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Get("/search", name="admin.answer.search")
|
||||||
|
*/
|
||||||
|
public function searchAction()
|
||||||
|
{
|
||||||
|
$answerService = new AnswerService();
|
||||||
|
|
||||||
|
$publishTypes = $answerService->getPublishTypes();
|
||||||
|
|
||||||
|
$this->view->setVar('publish_types', $publishTypes);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Get("/list", name="admin.answer.list")
|
||||||
|
*/
|
||||||
|
public function listAction()
|
||||||
|
{
|
||||||
|
$answerService = new AnswerService();
|
||||||
|
|
||||||
|
$pager = $answerService->getAnswers();
|
||||||
|
|
||||||
|
$this->view->setVar('pager', $pager);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Get("/add", name="admin.answer.add")
|
||||||
|
*/
|
||||||
|
public function addAction()
|
||||||
|
{
|
||||||
|
$id = $this->request->getQuery('question_id', 'int', 0);
|
||||||
|
|
||||||
|
$questionService = new QuestionService();
|
||||||
|
|
||||||
|
$question = $questionService->getQuestion($id);
|
||||||
|
|
||||||
|
$referer = $this->request->getHTTPReferer();
|
||||||
|
|
||||||
|
$this->view->setVar('question', $question);
|
||||||
|
$this->view->setVar('referer', $referer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Get("/{id:[0-9]+}/edit", name="admin.answer.edit")
|
||||||
|
*/
|
||||||
|
public function editAction($id)
|
||||||
|
{
|
||||||
|
$answerService = new AnswerService();
|
||||||
|
|
||||||
|
$answer = $answerService->getAnswer($id);
|
||||||
|
|
||||||
|
$questionService = new QuestionService();
|
||||||
|
|
||||||
|
$question = $questionService->getQuestion($answer->question_id);
|
||||||
|
|
||||||
|
$referer = $this->request->getHTTPReferer();
|
||||||
|
|
||||||
|
$this->view->setVar('referer', $referer);
|
||||||
|
$this->view->setVar('question', $question);
|
||||||
|
$this->view->setVar('answer', $answer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Get("/{id:[0-9]+}/show", name="admin.answer.show")
|
||||||
|
*/
|
||||||
|
public function showAction($id)
|
||||||
|
{
|
||||||
|
$answerService = new AnswerService();
|
||||||
|
|
||||||
|
$answer = $answerService->getAnswer($id);
|
||||||
|
|
||||||
|
$this->view->setVar('answer', $answer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Post("/create", name="admin.answer.create")
|
||||||
|
*/
|
||||||
|
public function createAction()
|
||||||
|
{
|
||||||
|
$answerService = new AnswerService();
|
||||||
|
|
||||||
|
$answerService->createAnswer();
|
||||||
|
|
||||||
|
$location = $this->request->getPost('referer');
|
||||||
|
|
||||||
|
if (empty($location)) {
|
||||||
|
$location = $this->url->get(['for' => 'admin.question.list']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$content = [
|
||||||
|
'location' => $location,
|
||||||
|
'msg' => '回答问题成功',
|
||||||
|
];
|
||||||
|
|
||||||
|
return $this->jsonSuccess($content);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Post("/{id:[0-9]+}/update", name="admin.answer.update")
|
||||||
|
*/
|
||||||
|
public function updateAction($id)
|
||||||
|
{
|
||||||
|
$answerService = new AnswerService();
|
||||||
|
|
||||||
|
$answerService->updateAnswer($id);
|
||||||
|
|
||||||
|
$location = $this->request->getPost('referer');
|
||||||
|
|
||||||
|
if (empty($location)) {
|
||||||
|
$location = $this->url->get(['for' => 'admin.answer.list']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$content = [
|
||||||
|
'location' => $location,
|
||||||
|
'msg' => '更新回答成功',
|
||||||
|
];
|
||||||
|
|
||||||
|
return $this->jsonSuccess($content);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Post("/{id:[0-9]+}/delete", name="admin.answer.delete")
|
||||||
|
*/
|
||||||
|
public function deleteAction($id)
|
||||||
|
{
|
||||||
|
$answerService = new AnswerService();
|
||||||
|
|
||||||
|
$answerService->deleteAnswer($id);
|
||||||
|
|
||||||
|
$content = [
|
||||||
|
'location' => $this->request->getHTTPReferer(),
|
||||||
|
'msg' => '删除回答成功',
|
||||||
|
];
|
||||||
|
|
||||||
|
return $this->jsonSuccess($content);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Post("/{id:[0-9]+}/restore", name="admin.answer.restore")
|
||||||
|
*/
|
||||||
|
public function restoreAction($id)
|
||||||
|
{
|
||||||
|
$answerService = new AnswerService();
|
||||||
|
|
||||||
|
$answerService->restoreAnswer($id);
|
||||||
|
|
||||||
|
$content = [
|
||||||
|
'location' => $this->request->getHTTPReferer(),
|
||||||
|
'msg' => '还原回答成功',
|
||||||
|
];
|
||||||
|
|
||||||
|
return $this->jsonSuccess($content);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Route("/{id:[0-9]+}/review", name="admin.answer.review")
|
||||||
|
*/
|
||||||
|
public function reviewAction($id)
|
||||||
|
{
|
||||||
|
$answerService = new AnswerService();
|
||||||
|
|
||||||
|
$answer = $answerService->getAnswer($id);
|
||||||
|
|
||||||
|
if ($this->request->isPost()) {
|
||||||
|
|
||||||
|
$answerService->reviewAnswer($id);
|
||||||
|
|
||||||
|
$location = $this->url->get(['for' => 'admin.mod.answers']);
|
||||||
|
|
||||||
|
$content = [
|
||||||
|
'location' => $location,
|
||||||
|
'msg' => '审核回答成功',
|
||||||
|
];
|
||||||
|
|
||||||
|
return $this->jsonSuccess($content);
|
||||||
|
}
|
||||||
|
|
||||||
|
$reasons = $answerService->getReasons();
|
||||||
|
|
||||||
|
$questionService = new QuestionService();
|
||||||
|
|
||||||
|
$question = $questionService->getQuestion($answer->question_id);
|
||||||
|
|
||||||
|
$this->view->setVar('reasons', $reasons);
|
||||||
|
$this->view->setVar('question', $question);
|
||||||
|
$this->view->setVar('answer', $answer);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -54,19 +54,6 @@ class ArticleController extends Controller
|
|||||||
$this->view->setVar('pager', $pager);
|
$this->view->setVar('pager', $pager);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @Get("/list/pending", name="admin.article.pending_list")
|
|
||||||
*/
|
|
||||||
public function pendingListAction()
|
|
||||||
{
|
|
||||||
$articleService = new ArticleService();
|
|
||||||
|
|
||||||
$pager = $articleService->getPendingArticles();
|
|
||||||
|
|
||||||
$this->view->pick('article/pending_list');
|
|
||||||
$this->view->setVar('pager', $pager);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Get("/add", name="admin.article.add")
|
* @Get("/add", name="admin.article.add")
|
||||||
*/
|
*/
|
||||||
@ -106,10 +93,8 @@ class ArticleController extends Controller
|
|||||||
{
|
{
|
||||||
$articleService = new ArticleService();
|
$articleService = new ArticleService();
|
||||||
|
|
||||||
$rejectOptions = $articleService->getRejectOptions();
|
|
||||||
$article = $articleService->getArticle($id);
|
$article = $articleService->getArticle($id);
|
||||||
|
|
||||||
$this->view->setVar('reject_options', $rejectOptions);
|
|
||||||
$this->view->setVar('article', $article);
|
$this->view->setVar('article', $article);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -184,22 +169,31 @@ class ArticleController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Post("/{id:[0-9]+}/review", name="admin.article.review")
|
* @Route("/{id:[0-9]+}/review", name="admin.article.review")
|
||||||
*/
|
*/
|
||||||
public function reviewAction($id)
|
public function reviewAction($id)
|
||||||
{
|
{
|
||||||
$articleService = new ArticleService();
|
$articleService = new ArticleService();
|
||||||
|
|
||||||
$articleService->reviewArticle($id);
|
if ($this->request->isPost()) {
|
||||||
|
|
||||||
$location = $this->url->get(['for' => 'admin.article.pending_list']);
|
$articleService->reviewArticle($id);
|
||||||
|
|
||||||
$content = [
|
$location = $this->url->get(['for' => 'admin.mod.articles']);
|
||||||
'location' => $location,
|
|
||||||
'msg' => '审核文章成功',
|
|
||||||
];
|
|
||||||
|
|
||||||
return $this->jsonSuccess($content);
|
$content = [
|
||||||
|
'location' => $location,
|
||||||
|
'msg' => '审核文章成功',
|
||||||
|
];
|
||||||
|
|
||||||
|
return $this->jsonSuccess($content);
|
||||||
|
}
|
||||||
|
|
||||||
|
$reasons = $articleService->getReasons();
|
||||||
|
$article = $articleService->getArticle($id);
|
||||||
|
|
||||||
|
$this->view->setVar('reasons', $reasons);
|
||||||
|
$this->view->setVar('article', $article);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -22,4 +22,28 @@ class ModerationController extends Controller
|
|||||||
$this->view->setVar('pager', $pager);
|
$this->view->setVar('pager', $pager);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Get("/questions", name="admin.mod.questions")
|
||||||
|
*/
|
||||||
|
public function questionsAction()
|
||||||
|
{
|
||||||
|
$modService = new ModerationService();
|
||||||
|
|
||||||
|
$pager = $modService->getQuestions();
|
||||||
|
|
||||||
|
$this->view->setVar('pager', $pager);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Get("/answers", name="admin.mod.answers")
|
||||||
|
*/
|
||||||
|
public function answersAction()
|
||||||
|
{
|
||||||
|
$modService = new ModerationService();
|
||||||
|
|
||||||
|
$pager = $modService->getAnswers();
|
||||||
|
|
||||||
|
$this->view->setVar('pager', $pager);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
195
app/Http/Admin/Controllers/QuestionController.php
Normal file
195
app/Http/Admin/Controllers/QuestionController.php
Normal file
@ -0,0 +1,195 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Admin\Controllers;
|
||||||
|
|
||||||
|
use App\Http\Admin\Services\Question as QuestionService;
|
||||||
|
use App\Models\Category as CategoryModel;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @RoutePrefix("/admin/question")
|
||||||
|
*/
|
||||||
|
class QuestionController extends Controller
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Get("/category", name="admin.question.category")
|
||||||
|
*/
|
||||||
|
public function categoryAction()
|
||||||
|
{
|
||||||
|
$location = $this->url->get(
|
||||||
|
['for' => 'admin.category.list'],
|
||||||
|
['type' => CategoryModel::TYPE_ARTICLE]
|
||||||
|
);
|
||||||
|
|
||||||
|
$this->response->redirect($location);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Get("/search", name="admin.question.search")
|
||||||
|
*/
|
||||||
|
public function searchAction()
|
||||||
|
{
|
||||||
|
$questionService = new QuestionService();
|
||||||
|
|
||||||
|
$publishTypes = $questionService->getPublishTypes();
|
||||||
|
$categories = $questionService->getCategories();
|
||||||
|
$xmTags = $questionService->getXmTags(0);
|
||||||
|
|
||||||
|
$this->view->setVar('publish_types', $publishTypes);
|
||||||
|
$this->view->setVar('categories', $categories);
|
||||||
|
$this->view->setVar('xm_tags', $xmTags);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Get("/list", name="admin.question.list")
|
||||||
|
*/
|
||||||
|
public function listAction()
|
||||||
|
{
|
||||||
|
$questionService = new QuestionService();
|
||||||
|
|
||||||
|
$pager = $questionService->getQuestions();
|
||||||
|
|
||||||
|
$this->view->setVar('pager', $pager);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Get("/add", name="admin.question.add")
|
||||||
|
*/
|
||||||
|
public function addAction()
|
||||||
|
{
|
||||||
|
$questionService = new QuestionService();
|
||||||
|
|
||||||
|
$categories = $questionService->getCategories();
|
||||||
|
|
||||||
|
$this->view->setVar('categories', $categories);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Get("/{id:[0-9]+}/edit", name="admin.question.edit")
|
||||||
|
*/
|
||||||
|
public function editAction($id)
|
||||||
|
{
|
||||||
|
$questionService = new QuestionService();
|
||||||
|
|
||||||
|
$publishTypes = $questionService->getPublishTypes();
|
||||||
|
$categories = $questionService->getCategories();
|
||||||
|
$question = $questionService->getQuestion($id);
|
||||||
|
$xmTags = $questionService->getXmTags($id);
|
||||||
|
|
||||||
|
$this->view->setVar('publish_types', $publishTypes);
|
||||||
|
$this->view->setVar('categories', $categories);
|
||||||
|
$this->view->setVar('question', $question);
|
||||||
|
$this->view->setVar('xm_tags', $xmTags);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Get("/{id:[0-9]+}/show", name="admin.question.show")
|
||||||
|
*/
|
||||||
|
public function showAction($id)
|
||||||
|
{
|
||||||
|
$questionService = new QuestionService();
|
||||||
|
|
||||||
|
$question = $questionService->getQuestion($id);
|
||||||
|
|
||||||
|
$this->view->setVar('question', $question);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Post("/create", name="admin.question.create")
|
||||||
|
*/
|
||||||
|
public function createAction()
|
||||||
|
{
|
||||||
|
$questionService = new QuestionService();
|
||||||
|
|
||||||
|
$question = $questionService->createQuestion();
|
||||||
|
|
||||||
|
$location = $this->url->get([
|
||||||
|
'for' => 'admin.question.edit',
|
||||||
|
'id' => $question->id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$content = [
|
||||||
|
'location' => $location,
|
||||||
|
'msg' => '创建问题成功',
|
||||||
|
];
|
||||||
|
|
||||||
|
return $this->jsonSuccess($content);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Post("/{id:[0-9]+}/update", name="admin.question.update")
|
||||||
|
*/
|
||||||
|
public function updateAction($id)
|
||||||
|
{
|
||||||
|
$questionService = new QuestionService();
|
||||||
|
|
||||||
|
$questionService->updateQuestion($id);
|
||||||
|
|
||||||
|
$content = ['msg' => '更新问题成功'];
|
||||||
|
|
||||||
|
return $this->jsonSuccess($content);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Post("/{id:[0-9]+}/delete", name="admin.question.delete")
|
||||||
|
*/
|
||||||
|
public function deleteAction($id)
|
||||||
|
{
|
||||||
|
$questionService = new QuestionService();
|
||||||
|
|
||||||
|
$questionService->deleteQuestion($id);
|
||||||
|
|
||||||
|
$content = [
|
||||||
|
'location' => $this->request->getHTTPReferer(),
|
||||||
|
'msg' => '删除问题成功',
|
||||||
|
];
|
||||||
|
|
||||||
|
return $this->jsonSuccess($content);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Post("/{id:[0-9]+}/restore", name="admin.question.restore")
|
||||||
|
*/
|
||||||
|
public function restoreAction($id)
|
||||||
|
{
|
||||||
|
$questionService = new QuestionService();
|
||||||
|
|
||||||
|
$questionService->restoreQuestion($id);
|
||||||
|
|
||||||
|
$content = [
|
||||||
|
'location' => $this->request->getHTTPReferer(),
|
||||||
|
'msg' => '还原问题成功',
|
||||||
|
];
|
||||||
|
|
||||||
|
return $this->jsonSuccess($content);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Route("/{id:[0-9]+}/review", name="admin.question.review")
|
||||||
|
*/
|
||||||
|
public function reviewAction($id)
|
||||||
|
{
|
||||||
|
$questionService = new QuestionService();
|
||||||
|
|
||||||
|
if ($this->request->isPost()) {
|
||||||
|
|
||||||
|
$questionService->reviewQuestion($id);
|
||||||
|
|
||||||
|
$location = $this->url->get(['for' => 'admin.mod.questions']);
|
||||||
|
|
||||||
|
$content = [
|
||||||
|
'location' => $location,
|
||||||
|
'msg' => '审核问题成功',
|
||||||
|
];
|
||||||
|
|
||||||
|
return $this->jsonSuccess($content);
|
||||||
|
}
|
||||||
|
|
||||||
|
$reasons = $questionService->getReasons();
|
||||||
|
$question = $questionService->getQuestion($id);
|
||||||
|
|
||||||
|
$this->view->setVar('reasons', $reasons);
|
||||||
|
$this->view->setVar('question', $question);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -53,6 +53,27 @@ class UploadController extends Controller
|
|||||||
return $this->jsonSuccess(['data' => $data]);
|
return $this->jsonSuccess(['data' => $data]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Post("/icon/img", name="admin.upload.icon_img")
|
||||||
|
*/
|
||||||
|
public function uploadIconImageAction()
|
||||||
|
{
|
||||||
|
$service = new StorageService();
|
||||||
|
|
||||||
|
$file = $service->uploadIconImage();
|
||||||
|
|
||||||
|
if (!$file) {
|
||||||
|
return $this->jsonError(['msg' => '上传文件失败']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = [
|
||||||
|
'src' => $service->getImageUrl($file->path),
|
||||||
|
'title' => $file->name,
|
||||||
|
];
|
||||||
|
|
||||||
|
return $this->jsonSuccess(['data' => $data]);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Post("/cover/img", name="admin.upload.cover_img")
|
* @Post("/cover/img", name="admin.upload.cover_img")
|
||||||
*/
|
*/
|
||||||
@ -127,7 +148,6 @@ class UploadController extends Controller
|
|||||||
|
|
||||||
$items['user_avatar'] = $service->uploadDefaultUserAvatar();
|
$items['user_avatar'] = $service->uploadDefaultUserAvatar();
|
||||||
$items['group_avatar'] = $service->uploadDefaultGroupAvatar();
|
$items['group_avatar'] = $service->uploadDefaultGroupAvatar();
|
||||||
$items['article_cover'] = $service->uploadDefaultArticleCover();
|
|
||||||
$items['course_cover'] = $service->uploadDefaultCourseCover();
|
$items['course_cover'] = $service->uploadDefaultCourseCover();
|
||||||
$items['package_cover'] = $service->uploadDefaultPackageCover();
|
$items['package_cover'] = $service->uploadDefaultPackageCover();
|
||||||
$items['gift_cover'] = $service->uploadDefaultGiftCover();
|
$items['gift_cover'] = $service->uploadDefaultGiftCover();
|
||||||
|
@ -94,6 +94,18 @@ class UserController extends Controller
|
|||||||
$this->view->setVar('admin_roles', $adminRoles);
|
$this->view->setVar('admin_roles', $adminRoles);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Get("/{id:[0-9]+}/online", name="admin.user.online")
|
||||||
|
*/
|
||||||
|
public function onlineAction($id)
|
||||||
|
{
|
||||||
|
$userService = new UserService();
|
||||||
|
|
||||||
|
$pager = $userService->getOnlineLogs($id);
|
||||||
|
|
||||||
|
$this->view->setVar('pager', $pager);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Post("/{id:[0-9]+}/update", name="admin.user.update")
|
* @Post("/{id:[0-9]+}/update", name="admin.user.update")
|
||||||
*/
|
*/
|
||||||
|
295
app/Http/Admin/Services/Answer.php
Normal file
295
app/Http/Admin/Services/Answer.php
Normal file
@ -0,0 +1,295 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Admin\Services;
|
||||||
|
|
||||||
|
use App\Builders\AnswerList as AnswerListBuilder;
|
||||||
|
use App\Library\Paginator\Query as PagerQuery;
|
||||||
|
use App\Models\Answer as AnswerModel;
|
||||||
|
use App\Models\Question as QuestionModel;
|
||||||
|
use App\Models\Reason as ReasonModel;
|
||||||
|
use App\Models\User as UserModel;
|
||||||
|
use App\Repos\Answer as AnswerRepo;
|
||||||
|
use App\Repos\Question as QuestionRepo;
|
||||||
|
use App\Repos\User as UserRepo;
|
||||||
|
use App\Services\Logic\Notice\System\AnswerApproved as AnswerApprovedNotice;
|
||||||
|
use App\Services\Logic\Notice\System\AnswerRejected as AnswerRejectedNotice;
|
||||||
|
use App\Services\Logic\Notice\System\QuestionAnswered as QuestionAnsweredNotice;
|
||||||
|
use App\Services\Logic\Point\History\AnswerPost as AnswerPostPointHistory;
|
||||||
|
use App\Validators\Answer as AnswerValidator;
|
||||||
|
|
||||||
|
class Answer extends Service
|
||||||
|
{
|
||||||
|
|
||||||
|
public function getPublishTypes()
|
||||||
|
{
|
||||||
|
return AnswerModel::publishTypes();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getReasons()
|
||||||
|
{
|
||||||
|
return ReasonModel::answerRejectOptions();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAnswers()
|
||||||
|
{
|
||||||
|
$pagerQuery = new PagerQuery();
|
||||||
|
|
||||||
|
$params = $pagerQuery->getParams();
|
||||||
|
|
||||||
|
$params['deleted'] = $params['deleted'] ?? 0;
|
||||||
|
|
||||||
|
$sort = $pagerQuery->getSort();
|
||||||
|
$page = $pagerQuery->getPage();
|
||||||
|
$limit = $pagerQuery->getLimit();
|
||||||
|
|
||||||
|
$answerRepo = new AnswerRepo();
|
||||||
|
|
||||||
|
$pager = $answerRepo->paginate($params, $sort, $page, $limit);
|
||||||
|
|
||||||
|
return $this->handleAnswers($pager);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAnswer($id)
|
||||||
|
{
|
||||||
|
return $this->findOrFail($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createAnswer()
|
||||||
|
{
|
||||||
|
$post = $this->request->getPost();
|
||||||
|
|
||||||
|
$user = $this->getLoginUser();
|
||||||
|
|
||||||
|
$validator = new AnswerValidator();
|
||||||
|
|
||||||
|
$question = $validator->checkQuestion($post['question_id']);
|
||||||
|
|
||||||
|
$answer = new AnswerModel();
|
||||||
|
|
||||||
|
$answer->owner_id = $user->id;
|
||||||
|
$answer->question_id = $question->id;
|
||||||
|
$answer->published = AnswerModel::PUBLISH_APPROVED;
|
||||||
|
$answer->content = $validator->checkContent($post['content']);
|
||||||
|
|
||||||
|
$answer->create();
|
||||||
|
|
||||||
|
$this->recountQuestionAnswers($question);
|
||||||
|
$this->recountUserAnswers($user);
|
||||||
|
$this->handleAnswerPostPoint($answer);
|
||||||
|
$this->handleQuestionAnsweredNotice($answer);
|
||||||
|
|
||||||
|
return $answer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateAnswer($id)
|
||||||
|
{
|
||||||
|
$answer = $this->findOrFail($id);
|
||||||
|
|
||||||
|
$post = $this->request->getPost();
|
||||||
|
|
||||||
|
$validator = new AnswerValidator();
|
||||||
|
|
||||||
|
$data = [];
|
||||||
|
|
||||||
|
if (isset($post['content'])) {
|
||||||
|
$data['content'] = $validator->checkContent($post['content']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($post['published'])) {
|
||||||
|
|
||||||
|
$data['published'] = $validator->checkPublishStatus($post['published']);
|
||||||
|
|
||||||
|
$question = $this->findQuestion($answer->question_id);
|
||||||
|
|
||||||
|
$this->recountQuestionAnswers($question);
|
||||||
|
|
||||||
|
$user = $this->findUser($answer->owner_id);
|
||||||
|
|
||||||
|
$this->recountUserAnswers($user);
|
||||||
|
}
|
||||||
|
|
||||||
|
$answer->update($data);
|
||||||
|
|
||||||
|
return $answer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleteAnswer($id)
|
||||||
|
{
|
||||||
|
$answer = $this->findOrFail($id);
|
||||||
|
|
||||||
|
$answer->deleted = 1;
|
||||||
|
|
||||||
|
$answer->update();
|
||||||
|
|
||||||
|
$question = $this->findQuestion($answer->question_id);
|
||||||
|
|
||||||
|
$this->recountQuestionAnswers($question);
|
||||||
|
|
||||||
|
$owner = $this->findUser($answer->owner_id);
|
||||||
|
|
||||||
|
$this->recountUserAnswers($owner);
|
||||||
|
|
||||||
|
return $answer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function restoreAnswer($id)
|
||||||
|
{
|
||||||
|
$answer = $this->findOrFail($id);
|
||||||
|
|
||||||
|
$answer->deleted = 0;
|
||||||
|
|
||||||
|
$answer->update();
|
||||||
|
|
||||||
|
$question = $this->findQuestion($answer->question_id);
|
||||||
|
|
||||||
|
$this->recountQuestionAnswers($question);
|
||||||
|
|
||||||
|
$owner = $this->findUser($answer->owner_id);
|
||||||
|
|
||||||
|
$this->recountUserAnswers($owner);
|
||||||
|
|
||||||
|
return $answer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function reviewAnswer($id)
|
||||||
|
{
|
||||||
|
$type = $this->request->getPost('type', ['trim', 'string']);
|
||||||
|
$reason = $this->request->getPost('reason', ['trim', 'string']);
|
||||||
|
|
||||||
|
$answer = $this->findOrFail($id);
|
||||||
|
|
||||||
|
$validator = new AnswerValidator();
|
||||||
|
|
||||||
|
if ($type == 'approve') {
|
||||||
|
$answer->published = AnswerModel::PUBLISH_APPROVED;
|
||||||
|
} elseif ($type == 'reject') {
|
||||||
|
$validator->checkRejectReason($reason);
|
||||||
|
$answer->published = AnswerModel::PUBLISH_REJECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
$answer->update();
|
||||||
|
|
||||||
|
$question = $this->findQuestion($answer->question_id);
|
||||||
|
|
||||||
|
$this->recountQuestionAnswers($question);
|
||||||
|
|
||||||
|
$owner = $this->findUser($answer->owner_id);
|
||||||
|
|
||||||
|
$this->recountUserAnswers($owner);
|
||||||
|
|
||||||
|
$sender = $this->getLoginUser();
|
||||||
|
|
||||||
|
if ($type == 'approve') {
|
||||||
|
|
||||||
|
$this->handleAnswerPostPoint($answer);
|
||||||
|
$this->handleAnswerApprovedNotice($answer, $sender);
|
||||||
|
|
||||||
|
$this->eventsManager->fire('Answer:afterApprove', $this, $answer);
|
||||||
|
|
||||||
|
} elseif ($type == 'reject') {
|
||||||
|
|
||||||
|
$options = ReasonModel::answerRejectOptions();
|
||||||
|
|
||||||
|
if (array_key_exists($reason, $options)) {
|
||||||
|
$reason = $options[$reason];
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->handleAnswerRejectedNotice($answer, $sender, $reason);
|
||||||
|
|
||||||
|
$this->eventsManager->fire('Answer:afterReject', $this, $answer);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $answer;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function findOrFail($id)
|
||||||
|
{
|
||||||
|
$validator = new AnswerValidator();
|
||||||
|
|
||||||
|
return $validator->checkAnswer($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function findQuestion($id)
|
||||||
|
{
|
||||||
|
$questionRepo = new QuestionRepo();
|
||||||
|
|
||||||
|
return $questionRepo->findById($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function findUser($id)
|
||||||
|
{
|
||||||
|
$userRepo = new UserRepo();
|
||||||
|
|
||||||
|
return $userRepo->findById($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function handleAnswers($pager)
|
||||||
|
{
|
||||||
|
if ($pager->total_items > 0) {
|
||||||
|
|
||||||
|
$builder = new AnswerListBuilder();
|
||||||
|
|
||||||
|
$items = $pager->items->toArray();
|
||||||
|
|
||||||
|
$pipeA = $builder->handleQuestions($items);
|
||||||
|
$pipeB = $builder->handleUsers($pipeA);
|
||||||
|
$pipeC = $builder->objects($pipeB);
|
||||||
|
|
||||||
|
$pager->items = $pipeC;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $pager;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function recountQuestionAnswers(QuestionModel $question)
|
||||||
|
{
|
||||||
|
$questionRepo = new QuestionRepo();
|
||||||
|
|
||||||
|
$answerCount = $questionRepo->countAnswers($question->id);
|
||||||
|
|
||||||
|
$question->answer_count = $answerCount;
|
||||||
|
|
||||||
|
$question->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function recountUserAnswers(UserModel $user)
|
||||||
|
{
|
||||||
|
$userRepo = new UserRepo();
|
||||||
|
|
||||||
|
$answerCount = $userRepo->countAnswers($user->id);
|
||||||
|
|
||||||
|
$user->answer_count = $answerCount;
|
||||||
|
|
||||||
|
$user->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function handleQuestionAnsweredNotice(AnswerModel $answer)
|
||||||
|
{
|
||||||
|
$notice = new QuestionAnsweredNotice();
|
||||||
|
|
||||||
|
$notice->handle($answer);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function handleAnswerApprovedNotice(AnswerModel $answer, UserModel $sender)
|
||||||
|
{
|
||||||
|
$notice = new AnswerApprovedNotice();
|
||||||
|
|
||||||
|
$notice->handle($answer, $sender);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function handleAnswerRejectedNotice(AnswerModel $answer, UserModel $sender, $reason)
|
||||||
|
{
|
||||||
|
$notice = new AnswerRejectedNotice();
|
||||||
|
|
||||||
|
$notice->handle($answer, $sender, $reason);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function handleAnswerPostPoint(AnswerModel $answer)
|
||||||
|
{
|
||||||
|
$service = new AnswerPostPointHistory();
|
||||||
|
|
||||||
|
$service->handle($answer);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -7,15 +7,14 @@ use App\Caches\Article as ArticleCache;
|
|||||||
use App\Library\Paginator\Query as PagerQuery;
|
use App\Library\Paginator\Query as PagerQuery;
|
||||||
use App\Library\Utils\Word as WordUtil;
|
use App\Library\Utils\Word as WordUtil;
|
||||||
use App\Models\Article as ArticleModel;
|
use App\Models\Article as ArticleModel;
|
||||||
use App\Models\ArticleTag as ArticleTagModel;
|
|
||||||
use App\Models\Category as CategoryModel;
|
use App\Models\Category as CategoryModel;
|
||||||
use App\Models\Reason as ReasonModel;
|
use App\Models\Reason as ReasonModel;
|
||||||
use App\Models\User as UserModel;
|
use App\Models\User as UserModel;
|
||||||
use App\Repos\Article as ArticleRepo;
|
use App\Repos\Article as ArticleRepo;
|
||||||
use App\Repos\ArticleTag as ArticleTagRepo;
|
|
||||||
use App\Repos\Category as CategoryRepo;
|
use App\Repos\Category as CategoryRepo;
|
||||||
use App\Repos\Tag as TagRepo;
|
use App\Repos\Tag as TagRepo;
|
||||||
use App\Repos\User as UserRepo;
|
use App\Repos\User as UserRepo;
|
||||||
|
use App\Services\Logic\Article\ArticleDataTrait;
|
||||||
use App\Services\Logic\Notice\System\ArticleApproved as ArticleApprovedNotice;
|
use App\Services\Logic\Notice\System\ArticleApproved as ArticleApprovedNotice;
|
||||||
use App\Services\Logic\Notice\System\ArticleRejected as ArticleRejectedNotice;
|
use App\Services\Logic\Notice\System\ArticleRejected as ArticleRejectedNotice;
|
||||||
use App\Services\Logic\Point\History\ArticlePost as ArticlePostPointHistory;
|
use App\Services\Logic\Point\History\ArticlePost as ArticlePostPointHistory;
|
||||||
@ -25,20 +24,13 @@ use App\Validators\Article as ArticleValidator;
|
|||||||
class Article extends Service
|
class Article extends Service
|
||||||
{
|
{
|
||||||
|
|
||||||
public function getArticleModel()
|
use ArticleDataTrait;
|
||||||
{
|
|
||||||
$article = new ArticleModel();
|
|
||||||
|
|
||||||
$article->afterFetch();
|
|
||||||
|
|
||||||
return $article;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getXmTags($id)
|
public function getXmTags($id)
|
||||||
{
|
{
|
||||||
$tagRepo = new TagRepo();
|
$tagRepo = new TagRepo();
|
||||||
|
|
||||||
$allTags = $tagRepo->findAll(['published' => 1], 'priority');
|
$allTags = $tagRepo->findAll(['published' => 1]);
|
||||||
|
|
||||||
if ($allTags->count() == 0) return [];
|
if ($allTags->count() == 0) return [];
|
||||||
|
|
||||||
@ -86,7 +78,7 @@ class Article extends Service
|
|||||||
return ArticleModel::sourceTypes();
|
return ArticleModel::sourceTypes();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRejectOptions()
|
public function getReasons()
|
||||||
{
|
{
|
||||||
return ReasonModel::articleRejectOptions();
|
return ReasonModel::articleRejectOptions();
|
||||||
}
|
}
|
||||||
@ -132,13 +124,14 @@ class Article extends Service
|
|||||||
|
|
||||||
$article = new ArticleModel();
|
$article = new ArticleModel();
|
||||||
|
|
||||||
|
$article->published = ArticleModel::PUBLISH_APPROVED;
|
||||||
$article->owner_id = $user->id;
|
$article->owner_id = $user->id;
|
||||||
$article->category_id = $category->id;
|
$article->category_id = $category->id;
|
||||||
$article->title = $title;
|
$article->title = $title;
|
||||||
|
|
||||||
$article->create();
|
$article->create();
|
||||||
|
|
||||||
$this->incrUserArticleCount($user);
|
$this->recountUserArticles($user);
|
||||||
|
|
||||||
$this->eventsManager->fire('Article:afterCreate', $this, $article);
|
$this->eventsManager->fire('Article:afterCreate', $this, $article);
|
||||||
|
|
||||||
@ -164,14 +157,6 @@ class Article extends Service
|
|||||||
$data['title'] = $validator->checkTitle($post['title']);
|
$data['title'] = $validator->checkTitle($post['title']);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($post['cover'])) {
|
|
||||||
$data['cover'] = $validator->checkCover($post['cover']);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($post['summary'])) {
|
|
||||||
$data['summary'] = $validator->checkSummary($post['summary']);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($post['content'])) {
|
if (isset($post['content'])) {
|
||||||
$data['content'] = $validator->checkContent($post['content']);
|
$data['content'] = $validator->checkContent($post['content']);
|
||||||
$data['word_count'] = WordUtil::getWordCount($data['content']);
|
$data['word_count'] = WordUtil::getWordCount($data['content']);
|
||||||
@ -184,8 +169,8 @@ class Article extends Service
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($post['allow_comment'])) {
|
if (isset($post['closed'])) {
|
||||||
$data['allow_comment'] = $post['allow_comment'];
|
$data['closed'] = $validator->checkCloseStatus($post['closed']);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($post['private'])) {
|
if (isset($post['private'])) {
|
||||||
@ -197,7 +182,12 @@ class Article extends Service
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (isset($post['published'])) {
|
if (isset($post['published'])) {
|
||||||
|
|
||||||
$data['published'] = $validator->checkPublishStatus($post['published']);
|
$data['published'] = $validator->checkPublishStatus($post['published']);
|
||||||
|
|
||||||
|
$owner = $this->findUser($article->owner_id);
|
||||||
|
|
||||||
|
$this->recountUserArticles($owner);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($post['xm_tag_ids'])) {
|
if (isset($post['xm_tag_ids'])) {
|
||||||
@ -225,7 +215,7 @@ class Article extends Service
|
|||||||
|
|
||||||
$owner = $userRepo->findById($article->owner_id);
|
$owner = $userRepo->findById($article->owner_id);
|
||||||
|
|
||||||
$this->decrUserArticleCount($owner);
|
$this->recountUserArticles($owner);
|
||||||
|
|
||||||
$this->rebuildArticleIndex($article);
|
$this->rebuildArticleIndex($article);
|
||||||
|
|
||||||
@ -246,7 +236,7 @@ class Article extends Service
|
|||||||
|
|
||||||
$owner = $userRepo->findById($article->owner_id);
|
$owner = $userRepo->findById($article->owner_id);
|
||||||
|
|
||||||
$this->incrUserArticleCount($owner);
|
$this->recountUserArticles($owner);
|
||||||
|
|
||||||
$this->rebuildArticleIndex($article);
|
$this->rebuildArticleIndex($article);
|
||||||
|
|
||||||
@ -262,14 +252,25 @@ class Article extends Service
|
|||||||
|
|
||||||
$article = $this->findOrFail($id);
|
$article = $this->findOrFail($id);
|
||||||
|
|
||||||
|
$validator = new ArticleValidator();
|
||||||
|
|
||||||
if ($type == 'approve') {
|
if ($type == 'approve') {
|
||||||
|
|
||||||
$article->published = ArticleModel::PUBLISH_APPROVED;
|
$article->published = ArticleModel::PUBLISH_APPROVED;
|
||||||
|
|
||||||
} elseif ($type == 'reject') {
|
} elseif ($type == 'reject') {
|
||||||
|
|
||||||
|
$validator->checkRejectReason($reason);
|
||||||
|
|
||||||
$article->published = ArticleModel::PUBLISH_REJECTED;
|
$article->published = ArticleModel::PUBLISH_REJECTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
$article->update();
|
$article->update();
|
||||||
|
|
||||||
|
$owner = $this->findUser($article->owner_id);
|
||||||
|
|
||||||
|
$this->recountUserArticles($owner);
|
||||||
|
|
||||||
$sender = $this->getLoginUser();
|
$sender = $this->getLoginUser();
|
||||||
|
|
||||||
if ($type == 'approve') {
|
if ($type == 'approve') {
|
||||||
@ -309,59 +310,11 @@ class Article extends Service
|
|||||||
return $validator->checkArticle($id);
|
return $validator->checkArticle($id);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function saveTags(ArticleModel $article, $tagIds)
|
protected function findUser($id)
|
||||||
{
|
{
|
||||||
$originTagIds = [];
|
$userRepo = new UserRepo();
|
||||||
|
|
||||||
/**
|
return $userRepo->findById($id);
|
||||||
* 修改数据后,afterFetch设置的属性会失效,重新执行
|
|
||||||
*/
|
|
||||||
$article->afterFetch();
|
|
||||||
|
|
||||||
if ($article->tags) {
|
|
||||||
$originTagIds = kg_array_column($article->tags, 'id');
|
|
||||||
}
|
|
||||||
|
|
||||||
$newTagIds = $tagIds ? explode(',', $tagIds) : [];
|
|
||||||
$addedTagIds = array_diff($newTagIds, $originTagIds);
|
|
||||||
|
|
||||||
if ($addedTagIds) {
|
|
||||||
foreach ($addedTagIds as $tagId) {
|
|
||||||
$articleTag = new ArticleTagModel();
|
|
||||||
$articleTag->article_id = $article->id;
|
|
||||||
$articleTag->tag_id = $tagId;
|
|
||||||
$articleTag->create();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$deletedTagIds = array_diff($originTagIds, $newTagIds);
|
|
||||||
|
|
||||||
if ($deletedTagIds) {
|
|
||||||
$articleTagRepo = new ArticleTagRepo();
|
|
||||||
foreach ($deletedTagIds as $tagId) {
|
|
||||||
$articleTag = $articleTagRepo->findArticleTag($article->id, $tagId);
|
|
||||||
if ($articleTag) {
|
|
||||||
$articleTag->delete();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$articleTags = [];
|
|
||||||
|
|
||||||
if ($newTagIds) {
|
|
||||||
$tagRepo = new TagRepo();
|
|
||||||
$tags = $tagRepo->findByIds($newTagIds);
|
|
||||||
if ($tags->count() > 0) {
|
|
||||||
$articleTags = [];
|
|
||||||
foreach ($tags as $tag) {
|
|
||||||
$articleTags[] = ['id' => $tag->id, 'name' => $tag->name];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$article->tags = $articleTags;
|
|
||||||
|
|
||||||
$article->update();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function handleArticles($pager)
|
protected function handleArticles($pager)
|
||||||
@ -383,21 +336,17 @@ class Article extends Service
|
|||||||
return $pager;
|
return $pager;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function incrUserArticleCount(UserModel $user)
|
protected function recountUserArticles(UserModel $user)
|
||||||
{
|
{
|
||||||
$user->article_count += 1;
|
$userRepo = new UserRepo();
|
||||||
|
|
||||||
|
$articleCount = $userRepo->countArticles($user->id);
|
||||||
|
|
||||||
|
$user->article_count = $articleCount;
|
||||||
|
|
||||||
$user->update();
|
$user->update();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function decrUserArticleCount(UserModel $user)
|
|
||||||
{
|
|
||||||
if ($user->article_count > 0) {
|
|
||||||
$user->article_count -= 1;
|
|
||||||
$user->update();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function rebuildArticleCache(ArticleModel $article)
|
protected function rebuildArticleCache(ArticleModel $article)
|
||||||
{
|
{
|
||||||
$cache = new ArticleCache();
|
$cache = new ArticleCache();
|
||||||
|
@ -272,12 +272,6 @@ class AuthNode extends Service
|
|||||||
'type' => 'button',
|
'type' => 'button',
|
||||||
'route' => 'admin.article.edit',
|
'route' => 'admin.article.edit',
|
||||||
],
|
],
|
||||||
[
|
|
||||||
'id' => '1-7-6',
|
|
||||||
'title' => '文章分类',
|
|
||||||
'type' => 'menu',
|
|
||||||
'route' => 'admin.article.category',
|
|
||||||
],
|
|
||||||
[
|
[
|
||||||
'id' => '1-7-5',
|
'id' => '1-7-5',
|
||||||
'title' => '删除文章',
|
'title' => '删除文章',
|
||||||
@ -288,7 +282,7 @@ class AuthNode extends Service
|
|||||||
'id' => '1-7-9',
|
'id' => '1-7-9',
|
||||||
'title' => '文章详情',
|
'title' => '文章详情',
|
||||||
'type' => 'button',
|
'type' => 'button',
|
||||||
'route' => 'admin.article.review',
|
'route' => 'admin.article.show',
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
'id' => '1-7-10',
|
'id' => '1-7-10',
|
||||||
@ -298,6 +292,104 @@ class AuthNode extends Service
|
|||||||
],
|
],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
'id' => '1-10',
|
||||||
|
'title' => '问题管理',
|
||||||
|
'type' => 'menu',
|
||||||
|
'children' => [
|
||||||
|
[
|
||||||
|
'id' => '1-10-1',
|
||||||
|
'title' => '问题列表',
|
||||||
|
'type' => 'menu',
|
||||||
|
'route' => 'admin.question.list',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => '1-10-2',
|
||||||
|
'title' => '搜索问题',
|
||||||
|
'type' => 'menu',
|
||||||
|
'route' => 'admin.question.search',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => '1-10-3',
|
||||||
|
'title' => '添加问题',
|
||||||
|
'type' => 'menu',
|
||||||
|
'route' => 'admin.question.add',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => '1-10-4',
|
||||||
|
'title' => '编辑问题',
|
||||||
|
'type' => 'button',
|
||||||
|
'route' => 'admin.question.edit',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => '1-10-5',
|
||||||
|
'title' => '删除问题',
|
||||||
|
'type' => 'button',
|
||||||
|
'route' => 'admin.question.delete',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => '1-10-9',
|
||||||
|
'title' => '问题详情',
|
||||||
|
'type' => 'button',
|
||||||
|
'route' => 'admin.question.show',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => '1-10-10',
|
||||||
|
'title' => '审核问题',
|
||||||
|
'type' => 'button',
|
||||||
|
'route' => 'admin.question.review',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => '1-11',
|
||||||
|
'title' => '回答管理',
|
||||||
|
'type' => 'menu',
|
||||||
|
'children' => [
|
||||||
|
[
|
||||||
|
'id' => '1-11-1',
|
||||||
|
'title' => '回答列表',
|
||||||
|
'type' => 'menu',
|
||||||
|
'route' => 'admin.answer.list',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => '1-11-2',
|
||||||
|
'title' => '搜索回答',
|
||||||
|
'type' => 'menu',
|
||||||
|
'route' => 'admin.answer.search',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => '1-11-3',
|
||||||
|
'title' => '添加答案',
|
||||||
|
'type' => 'button',
|
||||||
|
'route' => 'admin.answer.add',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => '1-11-4',
|
||||||
|
'title' => '编辑回答',
|
||||||
|
'type' => 'button',
|
||||||
|
'route' => 'admin.answer.edit',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => '1-11-5',
|
||||||
|
'title' => '删除回答',
|
||||||
|
'type' => 'button',
|
||||||
|
'route' => 'admin.answer.delete',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => '1-11-9',
|
||||||
|
'title' => '回答详情',
|
||||||
|
'type' => 'button',
|
||||||
|
'route' => 'admin.answer.show',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => '1-11-10',
|
||||||
|
'title' => '审核回答',
|
||||||
|
'type' => 'button',
|
||||||
|
'route' => 'admin.answer.review',
|
||||||
|
],
|
||||||
|
],
|
||||||
|
],
|
||||||
[
|
[
|
||||||
'id' => '1-8',
|
'id' => '1-8',
|
||||||
'title' => '标签管理',
|
'title' => '标签管理',
|
||||||
@ -387,6 +479,18 @@ class AuthNode extends Service
|
|||||||
'type' => 'menu',
|
'type' => 'menu',
|
||||||
'route' => 'admin.mod.articles',
|
'route' => 'admin.mod.articles',
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
'id' => '2-10-2',
|
||||||
|
'title' => '问题列表',
|
||||||
|
'type' => 'menu',
|
||||||
|
'route' => 'admin.mod.questions',
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'id' => '2-10-3',
|
||||||
|
'title' => '回答列表',
|
||||||
|
'type' => 'menu',
|
||||||
|
'route' => 'admin.mod.answers',
|
||||||
|
],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
@ -542,6 +646,12 @@ class AuthNode extends Service
|
|||||||
'type' => 'menu',
|
'type' => 'menu',
|
||||||
'route' => 'admin.slide.list',
|
'route' => 'admin.slide.list',
|
||||||
],
|
],
|
||||||
|
[
|
||||||
|
'id' => '2-5-5',
|
||||||
|
'title' => '搜索轮播',
|
||||||
|
'type' => 'menu',
|
||||||
|
'route' => 'admin.slide.search',
|
||||||
|
],
|
||||||
[
|
[
|
||||||
'id' => '2-5-2',
|
'id' => '2-5-2',
|
||||||
'title' => '添加轮播',
|
'title' => '添加轮播',
|
||||||
@ -560,12 +670,6 @@ class AuthNode extends Service
|
|||||||
'type' => 'button',
|
'type' => 'button',
|
||||||
'route' => 'admin.slide.delete',
|
'route' => 'admin.slide.delete',
|
||||||
],
|
],
|
||||||
[
|
|
||||||
'id' => '2-5-5',
|
|
||||||
'title' => '搜索轮播',
|
|
||||||
'type' => 'menu',
|
|
||||||
'route' => 'admin.slide.search',
|
|
||||||
],
|
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
@ -851,7 +955,13 @@ class AuthNode extends Service
|
|||||||
'title' => '编辑用户',
|
'title' => '编辑用户',
|
||||||
'type' => 'button',
|
'type' => 'button',
|
||||||
'route' => 'admin.user.edit',
|
'route' => 'admin.user.edit',
|
||||||
]
|
],
|
||||||
|
[
|
||||||
|
'id' => '4-1-5',
|
||||||
|
'title' => '在线记录',
|
||||||
|
'type' => 'button',
|
||||||
|
'route' => 'admin.user.online',
|
||||||
|
],
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
[
|
[
|
||||||
|
@ -4,7 +4,10 @@ namespace App\Http\Admin\Services;
|
|||||||
|
|
||||||
use App\Builders\ConsultList as ConsultListBuilder;
|
use App\Builders\ConsultList as ConsultListBuilder;
|
||||||
use App\Library\Paginator\Query as PagerQuery;
|
use App\Library\Paginator\Query as PagerQuery;
|
||||||
|
use App\Models\Chapter as ChapterModel;
|
||||||
use App\Models\Consult as ConsultModel;
|
use App\Models\Consult as ConsultModel;
|
||||||
|
use App\Models\Course as CourseModel;
|
||||||
|
use App\Repos\Chapter as ChapterRepo;
|
||||||
use App\Repos\Consult as ConsultRepo;
|
use App\Repos\Consult as ConsultRepo;
|
||||||
use App\Repos\Course as CourseRepo;
|
use App\Repos\Course as CourseRepo;
|
||||||
use App\Services\Logic\Notice\ConsultReply as ConsultReplyNotice;
|
use App\Services\Logic\Notice\ConsultReply as ConsultReplyNotice;
|
||||||
@ -74,6 +77,7 @@ class Consult extends Service
|
|||||||
|
|
||||||
if (isset($post['published'])) {
|
if (isset($post['published'])) {
|
||||||
$data['published'] = $validator->checkPublishStatus($post['published']);
|
$data['published'] = $validator->checkPublishStatus($post['published']);
|
||||||
|
$this->handleItemConsults($consult);
|
||||||
}
|
}
|
||||||
|
|
||||||
$consult->update($data);
|
$consult->update($data);
|
||||||
@ -93,13 +97,7 @@ class Consult extends Service
|
|||||||
|
|
||||||
$consult->update();
|
$consult->update();
|
||||||
|
|
||||||
$courseRepo = new CourseRepo();
|
$this->handleItemConsults($consult);
|
||||||
|
|
||||||
$course = $courseRepo->findById($consult->course_id);
|
|
||||||
|
|
||||||
$course->consult_count -= 1;
|
|
||||||
|
|
||||||
$course->update();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function restoreConsult($id)
|
public function restoreConsult($id)
|
||||||
@ -110,13 +108,20 @@ class Consult extends Service
|
|||||||
|
|
||||||
$consult->update();
|
$consult->update();
|
||||||
|
|
||||||
$courseRepo = new CourseRepo();
|
$this->handleItemConsults($consult);
|
||||||
|
}
|
||||||
|
|
||||||
$course = $courseRepo->findById($consult->course_id);
|
protected function handleItemConsults(ConsultModel $consult)
|
||||||
|
{
|
||||||
|
if ($consult->course_id > 0) {
|
||||||
|
$course = $this->findCourse($consult->course_id);
|
||||||
|
$this->recountCourseConsults($course);
|
||||||
|
}
|
||||||
|
|
||||||
$course->consult_count += 1;
|
if ($consult->chapter_id > 0) {
|
||||||
|
$chapter = $this->findChapter($consult->chapter_id);
|
||||||
$course->update();
|
$this->recountChapterConsults($chapter);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function handleReplyNotice(ConsultModel $consult)
|
protected function handleReplyNotice(ConsultModel $consult)
|
||||||
@ -133,6 +138,42 @@ class Consult extends Service
|
|||||||
return $validator->checkConsult($id);
|
return $validator->checkConsult($id);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function findCourse($id)
|
||||||
|
{
|
||||||
|
$courseRepo = new CourseRepo();
|
||||||
|
|
||||||
|
return $courseRepo->findById($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function findChapter($id)
|
||||||
|
{
|
||||||
|
$chapterRepo = new ChapterRepo();
|
||||||
|
|
||||||
|
return $chapterRepo->findById($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function recountCourseConsults(CourseModel $course)
|
||||||
|
{
|
||||||
|
$courseRepo = new CourseRepo();
|
||||||
|
|
||||||
|
$consultCount = $courseRepo->countConsults($course->id);
|
||||||
|
|
||||||
|
$course->consult_count = $consultCount;
|
||||||
|
|
||||||
|
$course->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function recountChapterConsults(ChapterModel $chapter)
|
||||||
|
{
|
||||||
|
$chapterRepo = new ChapterRepo();
|
||||||
|
|
||||||
|
$consultCount = $chapterRepo->countConsults($chapter->id);
|
||||||
|
|
||||||
|
$chapter->consult_count = $consultCount;
|
||||||
|
|
||||||
|
$chapter->update();
|
||||||
|
}
|
||||||
|
|
||||||
protected function handleConsults($pager)
|
protected function handleConsults($pager)
|
||||||
{
|
{
|
||||||
if ($pager->total_items > 0) {
|
if ($pager->total_items > 0) {
|
||||||
|
@ -2,10 +2,16 @@
|
|||||||
|
|
||||||
namespace App\Http\Admin\Services;
|
namespace App\Http\Admin\Services;
|
||||||
|
|
||||||
|
use App\Builders\AnswerList as AnswerListBuilder;
|
||||||
use App\Builders\ArticleList as ArticleListBuilder;
|
use App\Builders\ArticleList as ArticleListBuilder;
|
||||||
|
use App\Builders\QuestionList as QuestionListBuilder;
|
||||||
use App\Library\Paginator\Query as PagerQuery;
|
use App\Library\Paginator\Query as PagerQuery;
|
||||||
|
use App\Models\Answer as AnswerModel;
|
||||||
use App\Models\Article as ArticleModel;
|
use App\Models\Article as ArticleModel;
|
||||||
|
use App\Models\Question as QuestionModel;
|
||||||
|
use App\Repos\Answer as AnswerRepo;
|
||||||
use App\Repos\Article as ArticleRepo;
|
use App\Repos\Article as ArticleRepo;
|
||||||
|
use App\Repos\Question as QuestionRepo;
|
||||||
|
|
||||||
class Moderation extends Service
|
class Moderation extends Service
|
||||||
{
|
{
|
||||||
@ -30,6 +36,46 @@ class Moderation extends Service
|
|||||||
return $this->handleArticles($pager);
|
return $this->handleArticles($pager);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getQuestions()
|
||||||
|
{
|
||||||
|
$pagerQuery = new PagerQuery();
|
||||||
|
|
||||||
|
$params = $pagerQuery->getParams();
|
||||||
|
|
||||||
|
$params['published'] = QuestionModel::PUBLISH_PENDING;
|
||||||
|
$params['deleted'] = 0;
|
||||||
|
|
||||||
|
$sort = $pagerQuery->getSort();
|
||||||
|
$page = $pagerQuery->getPage();
|
||||||
|
$limit = $pagerQuery->getLimit();
|
||||||
|
|
||||||
|
$questionRepo = new QuestionRepo();
|
||||||
|
|
||||||
|
$pager = $questionRepo->paginate($params, $sort, $page, $limit);
|
||||||
|
|
||||||
|
return $this->handleQuestions($pager);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAnswers()
|
||||||
|
{
|
||||||
|
$pagerQuery = new PagerQuery();
|
||||||
|
|
||||||
|
$params = $pagerQuery->getParams();
|
||||||
|
|
||||||
|
$params['published'] = AnswerModel::PUBLISH_PENDING;
|
||||||
|
$params['deleted'] = 0;
|
||||||
|
|
||||||
|
$sort = $pagerQuery->getSort();
|
||||||
|
$page = $pagerQuery->getPage();
|
||||||
|
$limit = $pagerQuery->getLimit();
|
||||||
|
|
||||||
|
$answerRepo = new AnswerRepo();
|
||||||
|
|
||||||
|
$pager = $answerRepo->paginate($params, $sort, $page, $limit);
|
||||||
|
|
||||||
|
return $this->handleAnswers($pager);
|
||||||
|
}
|
||||||
|
|
||||||
protected function handleArticles($pager)
|
protected function handleArticles($pager)
|
||||||
{
|
{
|
||||||
if ($pager->total_items > 0) {
|
if ($pager->total_items > 0) {
|
||||||
@ -49,4 +95,41 @@ class Moderation extends Service
|
|||||||
return $pager;
|
return $pager;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function handleQuestions($pager)
|
||||||
|
{
|
||||||
|
if ($pager->total_items > 0) {
|
||||||
|
|
||||||
|
$builder = new QuestionListBuilder();
|
||||||
|
|
||||||
|
$items = $pager->items->toArray();
|
||||||
|
|
||||||
|
$pipeA = $builder->handleQuestions($items);
|
||||||
|
$pipeB = $builder->handleCategories($pipeA);
|
||||||
|
$pipeC = $builder->handleUsers($pipeB);
|
||||||
|
$pipeD = $builder->objects($pipeC);
|
||||||
|
|
||||||
|
$pager->items = $pipeD;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $pager;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function handleAnswers($pager)
|
||||||
|
{
|
||||||
|
if ($pager->total_items > 0) {
|
||||||
|
|
||||||
|
$builder = new AnswerListBuilder();
|
||||||
|
|
||||||
|
$items = $pager->items->toArray();
|
||||||
|
|
||||||
|
$pipeA = $builder->handleQuestions($items);
|
||||||
|
$pipeB = $builder->handleUsers($pipeA);
|
||||||
|
$pipeC = $builder->objects($pipeB);
|
||||||
|
|
||||||
|
$pager->items = $pipeC;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $pager;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
349
app/Http/Admin/Services/Question.php
Normal file
349
app/Http/Admin/Services/Question.php
Normal file
@ -0,0 +1,349 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Admin\Services;
|
||||||
|
|
||||||
|
use App\Builders\QuestionList as QuestionListBuilder;
|
||||||
|
use App\Caches\Question as QuestionCache;
|
||||||
|
use App\Library\Paginator\Query as PagerQuery;
|
||||||
|
use App\Models\Category as CategoryModel;
|
||||||
|
use App\Models\Question as QuestionModel;
|
||||||
|
use App\Models\Reason as ReasonModel;
|
||||||
|
use App\Models\User as UserModel;
|
||||||
|
use App\Repos\Category as CategoryRepo;
|
||||||
|
use App\Repos\Question as QuestionRepo;
|
||||||
|
use App\Repos\Tag as TagRepo;
|
||||||
|
use App\Repos\User as UserRepo;
|
||||||
|
use App\Services\Logic\Notice\System\QuestionApproved as QuestionApprovedNotice;
|
||||||
|
use App\Services\Logic\Notice\System\QuestionRejected as QuestionRejectedNotice;
|
||||||
|
use App\Services\Logic\Point\History\QuestionPost as QuestionPostPointHistory;
|
||||||
|
use App\Services\Logic\Question\QuestionDataTrait;
|
||||||
|
use App\Services\Sync\QuestionIndex as QuestionIndexSync;
|
||||||
|
use App\Validators\Question as QuestionValidator;
|
||||||
|
|
||||||
|
class Question extends Service
|
||||||
|
{
|
||||||
|
|
||||||
|
use QuestionDataTrait;
|
||||||
|
|
||||||
|
public function getXmTags($id)
|
||||||
|
{
|
||||||
|
$tagRepo = new TagRepo();
|
||||||
|
|
||||||
|
$allTags = $tagRepo->findAll(['published' => 1]);
|
||||||
|
|
||||||
|
if ($allTags->count() == 0) return [];
|
||||||
|
|
||||||
|
$questionTagIds = [];
|
||||||
|
|
||||||
|
if ($id > 0) {
|
||||||
|
$question = $this->findOrFail($id);
|
||||||
|
if (!empty($question->tags)) {
|
||||||
|
$questionTagIds = kg_array_column($question->tags, 'id');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$list = [];
|
||||||
|
|
||||||
|
foreach ($allTags as $tag) {
|
||||||
|
$selected = in_array($tag->id, $questionTagIds);
|
||||||
|
$list[] = [
|
||||||
|
'name' => $tag->name,
|
||||||
|
'value' => $tag->id,
|
||||||
|
'selected' => $selected,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCategories()
|
||||||
|
{
|
||||||
|
$categoryRepo = new CategoryRepo();
|
||||||
|
|
||||||
|
return $categoryRepo->findAll([
|
||||||
|
'type' => CategoryModel::TYPE_ARTICLE,
|
||||||
|
'level' => 1,
|
||||||
|
'published' => 1,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPublishTypes()
|
||||||
|
{
|
||||||
|
return QuestionModel::publishTypes();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getReasons()
|
||||||
|
{
|
||||||
|
return ReasonModel::questionRejectOptions();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQuestions()
|
||||||
|
{
|
||||||
|
$pagerQuery = new PagerQuery();
|
||||||
|
|
||||||
|
$params = $pagerQuery->getParams();
|
||||||
|
|
||||||
|
if (!empty($params['xm_tag_ids'])) {
|
||||||
|
$params['tag_id'] = explode(',', $params['xm_tag_ids']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$params['deleted'] = $params['deleted'] ?? 0;
|
||||||
|
|
||||||
|
$sort = $pagerQuery->getSort();
|
||||||
|
$page = $pagerQuery->getPage();
|
||||||
|
$limit = $pagerQuery->getLimit();
|
||||||
|
|
||||||
|
$questionRepo = new QuestionRepo();
|
||||||
|
|
||||||
|
$pager = $questionRepo->paginate($params, $sort, $page, $limit);
|
||||||
|
|
||||||
|
return $this->handleQuestions($pager);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQuestion($id)
|
||||||
|
{
|
||||||
|
return $this->findOrFail($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function createQuestion()
|
||||||
|
{
|
||||||
|
$post = $this->request->getPost();
|
||||||
|
|
||||||
|
$user = $this->getLoginUser();
|
||||||
|
|
||||||
|
$validator = new QuestionValidator();
|
||||||
|
|
||||||
|
$title = $validator->checkTitle($post['title']);
|
||||||
|
|
||||||
|
$question = new QuestionModel();
|
||||||
|
|
||||||
|
$question->published = QuestionModel::PUBLISH_APPROVED;
|
||||||
|
$question->owner_id = $user->id;
|
||||||
|
$question->title = $title;
|
||||||
|
|
||||||
|
$question->create();
|
||||||
|
|
||||||
|
$this->recountUserQuestions($user);
|
||||||
|
|
||||||
|
$this->eventsManager->fire('Question:afterCreate', $this, $question);
|
||||||
|
|
||||||
|
return $question;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function updateQuestion($id)
|
||||||
|
{
|
||||||
|
$post = $this->request->getPost();
|
||||||
|
|
||||||
|
$question = $this->findOrFail($id);
|
||||||
|
|
||||||
|
$validator = new QuestionValidator();
|
||||||
|
|
||||||
|
$data = [];
|
||||||
|
|
||||||
|
if (isset($post['category_id'])) {
|
||||||
|
$category = $validator->checkCategory($post['category_id']);
|
||||||
|
$data['category_id'] = $category->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($post['title'])) {
|
||||||
|
$data['title'] = $validator->checkTitle($post['title']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($post['content'])) {
|
||||||
|
$data['content'] = $validator->checkContent($post['content']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($post['anonymous'])) {
|
||||||
|
$data['anonymous'] = $validator->checkAnonymousStatus($post['anonymous']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($post['closed'])) {
|
||||||
|
$data['closed'] = $validator->checkCloseStatus($post['closed']);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($post['published'])) {
|
||||||
|
|
||||||
|
$data['published'] = $validator->checkPublishStatus($post['published']);
|
||||||
|
|
||||||
|
$owner = $this->findUser($question->owner_id);
|
||||||
|
|
||||||
|
$this->recountUserQuestions($owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($post['xm_tag_ids'])) {
|
||||||
|
$this->saveTags($question, $post['xm_tag_ids']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$question->update($data);
|
||||||
|
|
||||||
|
$this->rebuildQuestionIndex($question);
|
||||||
|
|
||||||
|
$this->eventsManager->fire('Question:afterUpdate', $this, $question);
|
||||||
|
|
||||||
|
return $question;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function deleteQuestion($id)
|
||||||
|
{
|
||||||
|
$question = $this->findOrFail($id);
|
||||||
|
|
||||||
|
$question->deleted = 1;
|
||||||
|
|
||||||
|
$question->update();
|
||||||
|
|
||||||
|
$userRepo = new UserRepo();
|
||||||
|
|
||||||
|
$owner = $userRepo->findById($question->owner_id);
|
||||||
|
|
||||||
|
$this->recountUserQuestions($owner);
|
||||||
|
|
||||||
|
$this->rebuildQuestionIndex($question);
|
||||||
|
|
||||||
|
$this->eventsManager->fire('Question:afterDelete', $this, $question);
|
||||||
|
|
||||||
|
return $question;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function restoreQuestion($id)
|
||||||
|
{
|
||||||
|
$question = $this->findOrFail($id);
|
||||||
|
|
||||||
|
$question->deleted = 0;
|
||||||
|
|
||||||
|
$question->update();
|
||||||
|
|
||||||
|
$userRepo = new UserRepo();
|
||||||
|
|
||||||
|
$owner = $userRepo->findById($question->owner_id);
|
||||||
|
|
||||||
|
$this->recountUserQuestions($owner);
|
||||||
|
|
||||||
|
$this->rebuildQuestionIndex($question);
|
||||||
|
|
||||||
|
$this->eventsManager->fire('Question:afterRestore', $this, $question);
|
||||||
|
|
||||||
|
return $question;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function reviewQuestion($id)
|
||||||
|
{
|
||||||
|
$type = $this->request->getPost('type', ['trim', 'string']);
|
||||||
|
$reason = $this->request->getPost('reason', ['trim', 'string']);
|
||||||
|
|
||||||
|
$question = $this->findOrFail($id);
|
||||||
|
|
||||||
|
$validator = new QuestionValidator();
|
||||||
|
|
||||||
|
if ($type == 'approve') {
|
||||||
|
$question->published = QuestionModel::PUBLISH_APPROVED;
|
||||||
|
} elseif ($type == 'reject') {
|
||||||
|
$validator->checkRejectReason($reason);
|
||||||
|
$question->published = QuestionModel::PUBLISH_REJECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
$question->update();
|
||||||
|
|
||||||
|
$owner = $this->findUser($question->owner_id);
|
||||||
|
|
||||||
|
$this->recountUserQuestions($owner);
|
||||||
|
|
||||||
|
$sender = $this->getLoginUser();
|
||||||
|
|
||||||
|
if ($type == 'approve') {
|
||||||
|
|
||||||
|
$this->rebuildQuestionIndex($question);
|
||||||
|
|
||||||
|
$this->handlePostPoint($question);
|
||||||
|
|
||||||
|
$notice = new QuestionApprovedNotice();
|
||||||
|
|
||||||
|
$notice->handle($question, $sender);
|
||||||
|
|
||||||
|
$this->eventsManager->fire('Question:afterApprove', $this, $question);
|
||||||
|
|
||||||
|
} elseif ($type == 'reject') {
|
||||||
|
|
||||||
|
$options = ReasonModel::questionRejectOptions();
|
||||||
|
|
||||||
|
if (array_key_exists($reason, $options)) {
|
||||||
|
$reason = $options[$reason];
|
||||||
|
}
|
||||||
|
|
||||||
|
$notice = new QuestionRejectedNotice();
|
||||||
|
|
||||||
|
$notice->handle($question, $sender, $reason);
|
||||||
|
|
||||||
|
$this->eventsManager->fire('Question:afterReject', $this, $question);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $question;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function findOrFail($id)
|
||||||
|
{
|
||||||
|
$validator = new QuestionValidator();
|
||||||
|
|
||||||
|
return $validator->checkQuestion($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function findUser($id)
|
||||||
|
{
|
||||||
|
$userRepo = new UserRepo();
|
||||||
|
|
||||||
|
return $userRepo->findById($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function handleQuestions($pager)
|
||||||
|
{
|
||||||
|
if ($pager->total_items > 0) {
|
||||||
|
|
||||||
|
$builder = new QuestionListBuilder();
|
||||||
|
|
||||||
|
$items = $pager->items->toArray();
|
||||||
|
|
||||||
|
$pipeA = $builder->handleQuestions($items);
|
||||||
|
$pipeB = $builder->handleCategories($pipeA);
|
||||||
|
$pipeC = $builder->handleUsers($pipeB);
|
||||||
|
$pipeD = $builder->objects($pipeC);
|
||||||
|
|
||||||
|
$pager->items = $pipeD;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $pager;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function recountUserQuestions(UserModel $user)
|
||||||
|
{
|
||||||
|
$userRepo = new UserRepo();
|
||||||
|
|
||||||
|
$questionCount = $userRepo->countQuestions($user->id);
|
||||||
|
|
||||||
|
$user->question_count = $questionCount;
|
||||||
|
|
||||||
|
$user->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function rebuildQuestionCache(QuestionModel $question)
|
||||||
|
{
|
||||||
|
$cache = new QuestionCache();
|
||||||
|
|
||||||
|
$cache->rebuild($question->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function rebuildQuestionIndex(QuestionModel $question)
|
||||||
|
{
|
||||||
|
$sync = new QuestionIndexSync();
|
||||||
|
|
||||||
|
$sync->addItem($question->id);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function handlePostPoint(QuestionModel $question)
|
||||||
|
{
|
||||||
|
if ($question->published != QuestionModel::PUBLISH_APPROVED) return;
|
||||||
|
|
||||||
|
$service = new QuestionPostPointHistory();
|
||||||
|
|
||||||
|
$service->handle($question);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -4,6 +4,7 @@ namespace App\Http\Admin\Services;
|
|||||||
|
|
||||||
use App\Builders\ReviewList as ReviewListBuilder;
|
use App\Builders\ReviewList as ReviewListBuilder;
|
||||||
use App\Library\Paginator\Query as PagerQuery;
|
use App\Library\Paginator\Query as PagerQuery;
|
||||||
|
use App\Models\Course as CourseModel;
|
||||||
use App\Repos\Course as CourseRepo;
|
use App\Repos\Course as CourseRepo;
|
||||||
use App\Repos\Review as ReviewRepo;
|
use App\Repos\Review as ReviewRepo;
|
||||||
use App\Services\CourseStat as CourseStatService;
|
use App\Services\CourseStat as CourseStatService;
|
||||||
@ -47,6 +48,8 @@ class Review extends Service
|
|||||||
{
|
{
|
||||||
$review = $this->findOrFail($id);
|
$review = $this->findOrFail($id);
|
||||||
|
|
||||||
|
$course = $this->findCourse($review->course_id);
|
||||||
|
|
||||||
$post = $this->request->getPost();
|
$post = $this->request->getPost();
|
||||||
|
|
||||||
$validator = new ReviewValidator();
|
$validator = new ReviewValidator();
|
||||||
@ -71,11 +74,12 @@ class Review extends Service
|
|||||||
|
|
||||||
if (isset($post['published'])) {
|
if (isset($post['published'])) {
|
||||||
$data['published'] = $validator->checkPublishStatus($post['published']);
|
$data['published'] = $validator->checkPublishStatus($post['published']);
|
||||||
|
$this->recountCourseReviews($course);
|
||||||
}
|
}
|
||||||
|
|
||||||
$review->update($data);
|
$review->update($data);
|
||||||
|
|
||||||
$this->updateCourseRating($review->course_id);
|
$this->updateCourseRating($course);
|
||||||
|
|
||||||
return $review;
|
return $review;
|
||||||
}
|
}
|
||||||
@ -88,7 +92,9 @@ class Review extends Service
|
|||||||
|
|
||||||
$review->update();
|
$review->update();
|
||||||
|
|
||||||
$this->decrCourseReviewCount($review->course_id);
|
$course = $this->findCourse($review->course_id);
|
||||||
|
|
||||||
|
$this->recountCourseReviews($course);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function restoreReview($id)
|
public function restoreReview($id)
|
||||||
@ -99,7 +105,9 @@ class Review extends Service
|
|||||||
|
|
||||||
$review->update();
|
$review->update();
|
||||||
|
|
||||||
$this->incrCourseReviewCount($review->course_id);
|
$course = $this->findCourse($review->course_id);
|
||||||
|
|
||||||
|
$this->recountCourseReviews($course);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function findOrFail($id)
|
protected function findOrFail($id)
|
||||||
@ -109,33 +117,29 @@ class Review extends Service
|
|||||||
return $validator->checkReview($id);
|
return $validator->checkReview($id);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function incrCourseReviewCount($courseId)
|
protected function findCourse($id)
|
||||||
{
|
{
|
||||||
$courseRepo = new CourseRepo();
|
$courseRepo = new CourseRepo();
|
||||||
|
|
||||||
$course = $courseRepo->findById($courseId);
|
return $courseRepo->findById($id);
|
||||||
|
}
|
||||||
|
|
||||||
$course->review_count -= 1;
|
protected function recountCourseReviews(CourseModel $course)
|
||||||
|
{
|
||||||
|
$courseRepo = new CourseRepo();
|
||||||
|
|
||||||
|
$reviewCount = $courseRepo->countReviews($course->id);
|
||||||
|
|
||||||
|
$course->review_count = $reviewCount;
|
||||||
|
|
||||||
$course->update();
|
$course->update();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function decrCourseReviewCount($courseId)
|
protected function updateCourseRating(CourseModel $course)
|
||||||
{
|
|
||||||
$courseRepo = new CourseRepo();
|
|
||||||
|
|
||||||
$course = $courseRepo->findById($courseId);
|
|
||||||
|
|
||||||
$course->review_count += 1;
|
|
||||||
|
|
||||||
$course->update();
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function updateCourseRating($courseId)
|
|
||||||
{
|
{
|
||||||
$service = new CourseStatService();
|
$service = new CourseStatService();
|
||||||
|
|
||||||
$service->updateRating($courseId);
|
$service->updateRating($course->id);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function handleReviews($pager)
|
protected function handleReviews($pager)
|
||||||
|
@ -19,7 +19,7 @@ class Tag extends Service
|
|||||||
|
|
||||||
$params['deleted'] = $params['deleted'] ?? 0;
|
$params['deleted'] = $params['deleted'] ?? 0;
|
||||||
|
|
||||||
$sort = 'priority';
|
$sort = $pagerQuery->getSort();
|
||||||
$page = $pagerQuery->getPage();
|
$page = $pagerQuery->getPage();
|
||||||
$limit = $pagerQuery->getLimit();
|
$limit = $pagerQuery->getLimit();
|
||||||
|
|
||||||
@ -42,7 +42,7 @@ class Tag extends Service
|
|||||||
$tag = new TagModel();
|
$tag = new TagModel();
|
||||||
|
|
||||||
$tag->name = $validator->checkName($post['name']);
|
$tag->name = $validator->checkName($post['name']);
|
||||||
$tag->published = $validator->checkPublishStatus($post['published']);
|
$tag->priority = $validator->checkPriority($post['priority']);
|
||||||
|
|
||||||
$tag->create();
|
$tag->create();
|
||||||
|
|
||||||
@ -68,6 +68,10 @@ class Tag extends Service
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (isset($post['icon'])) {
|
||||||
|
$data['icon'] = $validator->checkIcon($post['icon']);
|
||||||
|
}
|
||||||
|
|
||||||
if (isset($post['priority'])) {
|
if (isset($post['priority'])) {
|
||||||
$data['priority'] = $validator->checkPriority($post['priority']);
|
$data['priority'] = $validator->checkPriority($post['priority']);
|
||||||
}
|
}
|
||||||
|
@ -10,6 +10,7 @@ use App\Models\Account as AccountModel;
|
|||||||
use App\Models\ImUser as ImUserModel;
|
use App\Models\ImUser as ImUserModel;
|
||||||
use App\Models\User as UserModel;
|
use App\Models\User as UserModel;
|
||||||
use App\Repos\Account as AccountRepo;
|
use App\Repos\Account as AccountRepo;
|
||||||
|
use App\Repos\Online as OnlineRepo;
|
||||||
use App\Repos\Role as RoleRepo;
|
use App\Repos\Role as RoleRepo;
|
||||||
use App\Repos\User as UserRepo;
|
use App\Repos\User as UserRepo;
|
||||||
use App\Validators\Account as AccountValidator;
|
use App\Validators\Account as AccountValidator;
|
||||||
@ -30,6 +31,25 @@ class User extends Service
|
|||||||
return $roleRepo->findAll(['deleted' => 0]);
|
return $roleRepo->findAll(['deleted' => 0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getOnlineLogs($id)
|
||||||
|
{
|
||||||
|
$user = $this->findOrFail($id);
|
||||||
|
|
||||||
|
$pageQuery = new PaginateQuery();
|
||||||
|
|
||||||
|
$params = $pageQuery->getParams();
|
||||||
|
|
||||||
|
$params['user_id'] = $user->id;
|
||||||
|
|
||||||
|
$sort = $pageQuery->getSort();
|
||||||
|
$page = $pageQuery->getPage();
|
||||||
|
$limit = $pageQuery->getLimit();
|
||||||
|
|
||||||
|
$onlineRepo = new OnlineRepo();
|
||||||
|
|
||||||
|
return $onlineRepo->paginate($params, $sort, $page, $limit);
|
||||||
|
}
|
||||||
|
|
||||||
public function getUsers()
|
public function getUsers()
|
||||||
{
|
{
|
||||||
$pageQuery = new PaginateQuery();
|
$pageQuery = new PaginateQuery();
|
||||||
|
41
app/Http/Admin/Views/answer/add.volt
Normal file
41
app/Http/Admin/Views/answer/add.volt
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
{% extends 'templates/main.volt' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<form class="layui-form kg-form" method="POST" action="{{ url({'for':'admin.answer.create'}) }}">
|
||||||
|
<fieldset class="layui-elem-field layui-field-title">
|
||||||
|
<legend>回答问题</legend>
|
||||||
|
</fieldset>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<div class="layui-input-block" style="margin:0;">
|
||||||
|
<input class="layui-input" type="text" name="title" value="{{ question.title }}" readonly="readonly">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<div class="layui-input-block" style="margin:0;">
|
||||||
|
<div id="vditor"></div>
|
||||||
|
<textarea name="content" class="layui-hide" id="vditor-textarea"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-input-block kg-center" style="margin:0;">
|
||||||
|
<button class="layui-btn kg-submit" lay-submit="true" lay-filter="go">提交</button>
|
||||||
|
<button type="button" class="kg-back layui-btn layui-btn-primary">返回</button>
|
||||||
|
<input type="hidden" name="referer" value="{{ referer }}">
|
||||||
|
<input type="hidden" name="question_id" value="{{ question.id }}">
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block link_css %}
|
||||||
|
|
||||||
|
{{ css_link('https://cdn.jsdelivr.net/npm/vditor/dist/index.css', false) }}
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block include_js %}
|
||||||
|
|
||||||
|
{{ js_include('https://cdn.jsdelivr.net/npm/vditor/dist/index.min.js', false) }}
|
||||||
|
{{ js_include('admin/js/vditor.js') }}
|
||||||
|
|
||||||
|
{% endblock %}
|
40
app/Http/Admin/Views/answer/edit.volt
Normal file
40
app/Http/Admin/Views/answer/edit.volt
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
{% extends 'templates/main.volt' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<form class="layui-form kg-form" method="POST" action="{{ url({'for':'admin.answer.update','id':answer.id}) }}">
|
||||||
|
<fieldset class="layui-elem-field layui-field-title">
|
||||||
|
<legend>编辑答案</legend>
|
||||||
|
</fieldset>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<div class="layui-input-block" style="margin:0;">
|
||||||
|
<input class="layui-input" type="text" name="title" value="{{ question.title }}" readonly="readonly">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<div class="layui-input-block" style="margin:0;">
|
||||||
|
<div id="vditor"></div>
|
||||||
|
<textarea name="content" class="layui-hide" id="vditor-textarea">{{ answer.content }}</textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-input-block kg-center" style="margin:0;">
|
||||||
|
<button class="layui-btn kg-submit" lay-submit="true" lay-filter="go">提交</button>
|
||||||
|
<button type="button" class="kg-back layui-btn layui-btn-primary">返回</button>
|
||||||
|
<input type="hidden" name="referer" value="{{ referer }}">
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block link_css %}
|
||||||
|
|
||||||
|
{{ css_link('https://cdn.jsdelivr.net/npm/vditor/dist/index.css', false) }}
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block include_js %}
|
||||||
|
|
||||||
|
{{ js_include('https://cdn.jsdelivr.net/npm/vditor/dist/index.min.js', false) }}
|
||||||
|
{{ js_include('admin/js/vditor.js') }}
|
||||||
|
|
||||||
|
{% endblock %}
|
64
app/Http/Admin/Views/answer/list.volt
Normal file
64
app/Http/Admin/Views/answer/list.volt
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
{% extends 'templates/main.volt' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
{{ partial('macros/answer') }}
|
||||||
|
|
||||||
|
<table class="layui-table kg-table layui-form">
|
||||||
|
<colgroup>
|
||||||
|
<col>
|
||||||
|
<col>
|
||||||
|
<col>
|
||||||
|
<col>
|
||||||
|
<col width="10%">
|
||||||
|
</colgroup>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>信息</th>
|
||||||
|
<th>评论</th>
|
||||||
|
<th>点赞</th>
|
||||||
|
<th>状态</th>
|
||||||
|
<th>操作</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for item in pager.items %}
|
||||||
|
{% set question_url = url({'for':'home.question.show','id':item.question.id}) %}
|
||||||
|
{% set owner_url = url({'for':'home.user.show','id':item.owner.id}) %}
|
||||||
|
{% set review_url = url({'for':'admin.answer.review','id':item.id}) %}
|
||||||
|
{% set edit_url = url({'for':'admin.answer.edit','id':item.id}) %}
|
||||||
|
{% set delete_url = url({'for':'admin.answer.delete','id':item.id}) %}
|
||||||
|
{% set restore_url = url({'for':'admin.answer.restore','id':item.id}) %}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<P>问题:<a href="{{ question_url }}" target="_blank">{{ item.question.title }}</a></P>
|
||||||
|
<p>回答:{{ item.summary }}</p>
|
||||||
|
<p>作者:<a href="{{ owner_url }}" target="_blank">{{ item.owner.name }}</a> 创建:{{ date('Y-m-d',item.create_time) }}</p>
|
||||||
|
</td>
|
||||||
|
<td>{{ item.comment_count }}</td>
|
||||||
|
<td>{{ item.like_count }}</td>
|
||||||
|
<td>{{ publish_status(item.published) }}</td>
|
||||||
|
<td class="center">
|
||||||
|
<div class="layui-dropdown">
|
||||||
|
<button class="layui-btn layui-btn-sm">操作 <i class="layui-icon layui-icon-triangle-d"></i></button>
|
||||||
|
<ul>
|
||||||
|
{% if item.published == 1 %}
|
||||||
|
<li><a href="{{ review_url }}">审核回答</a></li>
|
||||||
|
{% endif %}
|
||||||
|
<li><a href="{{ edit_url }}">编辑回答</a></li>
|
||||||
|
{% if item.deleted == 0 %}
|
||||||
|
<a href="javascript:" class="kg-delete" data-url="{{ delete_url }}">删除回答</a>
|
||||||
|
{% else %}
|
||||||
|
<a href="javascript:" class="kg-restore" data-url="{{ restore_url }}">还原回答</a>
|
||||||
|
{% endif %}
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{{ partial('partials/pager') }}
|
||||||
|
|
||||||
|
{% endblock %}
|
@ -2,24 +2,20 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
{% set list_url = url({'for':'admin.article.pending_list'}) %}
|
<fieldset class="layui-elem-field layui-field-title">
|
||||||
|
<legend>审核内容</legend>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
<div class="kg-nav">
|
<div class="kg-mod-preview">
|
||||||
<div class="kg-nav-left">
|
<div class="title">{{ question.title }}</div>
|
||||||
<span class="layui-breadcrumb">
|
<div class="content markdown-body">{{ answer.content }}</div>
|
||||||
<a href="{{ list_url }}">审核列表</a>
|
|
||||||
<a><cite>{{ article.title }}</cite></a>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<form class="layui-form kg-form" method="POST" action="{{ url({'for':'admin.article.review','id':article.id}) }}">
|
<fieldset class="layui-elem-field layui-field-title">
|
||||||
<div class="layui-form-item">
|
<legend>审核意见</legend>
|
||||||
<label class="layui-form-label">内容</label>
|
</fieldset>
|
||||||
<div class="layui-input-block">
|
|
||||||
<div class="content markdown-body">{{ article.content|parse_markdown }}</div>
|
<form class="layui-form kg-form kg-review-form" method="POST" action="{{ url({'for':'admin.answer.review','id':answer.id}) }}">
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<label class="layui-form-label">审核</label>
|
<label class="layui-form-label">审核</label>
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
@ -33,7 +29,7 @@
|
|||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
<select name="reason">
|
<select name="reason">
|
||||||
<option value="">请选择</option>
|
<option value="">请选择</option>
|
||||||
{% for value,name in reject_options %}
|
{% for value,name in reasons %}
|
||||||
<option value="{{ value }}">{{ name }}</option>
|
<option value="{{ value }}">{{ name }}</option>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</select>
|
</select>
|
@ -2,20 +2,20 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
<form class="layui-form kg-form" method="GET" action="{{ url({'for':'admin.review.list'}) }}">
|
<form class="layui-form kg-form" method="GET" action="{{ url({'for':'admin.answer.list'}) }}">
|
||||||
<fieldset class="layui-elem-field layui-field-title">
|
<fieldset class="layui-elem-field layui-field-title">
|
||||||
<legend>搜索评价</legend>
|
<legend>搜索回答</legend>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<label class="layui-form-label">评价编号</label>
|
<label class="layui-form-label">回答编号</label>
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
<input class="layui-input" type="text" name="id" placeholder="评价编号精确匹配">
|
<input class="layui-input" type="text" name="id" placeholder="回答编号精确匹配">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<label class="layui-form-label">课程编号</label>
|
<label class="layui-form-label">问题编号</label>
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
<input class="layui-input" type="text" name="course_id" placeholder="课程编号精确匹配">
|
<input class="layui-input" type="text" name="question_id" placeholder="问题编号精确匹配">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
@ -25,10 +25,18 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<label class="layui-form-label">发布</label>
|
<label class="layui-form-label">发布状态</label>
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
<input type="radio" name="published" value="1" title="是">
|
{% for value,title in publish_types %}
|
||||||
<input type="radio" name="published" value="0" title="否">
|
<input type="radio" name="published" value="{{ value }}" title="{{ title }}">
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label">匿名</label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<input type="radio" name="anonymous" value="1" title="是">
|
||||||
|
<input type="radio" name="anonymous" value="0" title="否">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
@ -6,17 +6,6 @@
|
|||||||
<fieldset class="layui-elem-field layui-field-title">
|
<fieldset class="layui-elem-field layui-field-title">
|
||||||
<legend>添加文章</legend>
|
<legend>添加文章</legend>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<div class="layui-form-item">
|
|
||||||
<label class="layui-form-label">分类</label>
|
|
||||||
<div class="layui-input-block">
|
|
||||||
<select name="category_id" lay-verify="required">
|
|
||||||
<option value="">请选择</option>
|
|
||||||
{% for item in categories %}
|
|
||||||
<option value="{{ item.id }}">{{ item.name }}</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<label class="layui-form-label">标题</label>
|
<label class="layui-form-label">标题</label>
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
|
@ -33,7 +33,6 @@
|
|||||||
|
|
||||||
{{ js_include('https://cdn.jsdelivr.net/npm/vditor/dist/index.min.js', false) }}
|
{{ js_include('https://cdn.jsdelivr.net/npm/vditor/dist/index.min.js', false) }}
|
||||||
{{ js_include('lib/xm-select.js') }}
|
{{ js_include('lib/xm-select.js') }}
|
||||||
{{ js_include('admin/js/cover.upload.js') }}
|
|
||||||
{{ js_include('admin/js/vditor.js') }}
|
{{ js_include('admin/js/vditor.js') }}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
@ -1,33 +1,12 @@
|
|||||||
{% set source_url_display = article.source_type == 1 ? 'display:none' : 'display:block' %}
|
{% set source_url_display = article.source_type == 1 ? 'display:none' : 'display:block' %}
|
||||||
|
|
||||||
<form class="layui-form kg-form" method="POST" action="{{ url({'for':'admin.article.update','id':article.id}) }}">
|
<form class="layui-form kg-form" method="POST" action="{{ url({'for':'admin.article.update','id':article.id}) }}">
|
||||||
<div class="layui-form-item">
|
|
||||||
<label class="layui-form-label">封面</label>
|
|
||||||
<div class="layui-input-inline">
|
|
||||||
<img id="img-cover" class="kg-cover" src="{{ article.cover }}">
|
|
||||||
<input type="hidden" name="cover" value="{{ article.cover }}">
|
|
||||||
</div>
|
|
||||||
<div class="layui-input-inline" style="padding-top:35px;">
|
|
||||||
<button id="change-cover" class="layui-btn layui-btn-sm" type="button">更换</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<label class="layui-form-label">标题</label>
|
<label class="layui-form-label">标题</label>
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
<input class="layui-input" type="text" name="title" value="{{ article.title }}" lay-verify="required">
|
<input class="layui-input" type="text" name="title" value="{{ article.title }}" lay-verify="required">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-form-item">
|
|
||||||
<label class="layui-form-label">分类</label>
|
|
||||||
<div class="layui-input-block">
|
|
||||||
<select name="category_id" lay-verify="required">
|
|
||||||
<option value="">请选择</option>
|
|
||||||
{% for item in categories %}
|
|
||||||
<option value="{{ item.id }}" {% if article.category_id == item.id %}selected="selected"{% endif %}>{{ item.name }}</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<label class="layui-form-label">标签</label>
|
<label class="layui-form-label">标签</label>
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
@ -51,18 +30,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<label class="layui-form-label">发布状态</label>
|
<label class="layui-form-label">关闭评论</label>
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
{% for value,title in publish_types %}
|
<input type="radio" name="closed" value="1" title="是" {% if article.closed == 1 %}checked="checked"{% endif %}>
|
||||||
<input type="radio" name="published" value="{{ value }}" title="{{ title }}" {% if article.published == value %}checked="checked"{% endif %} lay-filter="source_type">
|
<input type="radio" name="closed" value="0" title="否" {% if article.closed == 0 %}checked="checked"{% endif %}>
|
||||||
{% endfor %}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="layui-form-item">
|
|
||||||
<label class="layui-form-label">允许评论</label>
|
|
||||||
<div class="layui-input-block">
|
|
||||||
<input type="radio" name="allow_comment" value="1" title="是" {% if article.allow_comment == 1 %}checked="checked"{% endif %}>
|
|
||||||
<input type="radio" name="allow_comment" value="0" title="否" {% if article.allow_comment == 0 %}checked="checked"{% endif %}>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
|
@ -1,20 +1,12 @@
|
|||||||
<form class="layui-form kg-form" method="POST" action="{{ url({'for':'admin.article.update','id':article.id}) }}">
|
<form class="layui-form kg-form" method="POST" action="{{ url({'for':'admin.article.update','id':article.id}) }}">
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<label class="layui-form-label">详情</label>
|
<div class="layui-input-block" style="margin:0;">
|
||||||
<div class="layui-input-block">
|
|
||||||
<div id="vditor"></div>
|
<div id="vditor"></div>
|
||||||
<textarea name="content" class="layui-hide" id="vditor-textarea">{{ article.content }}</textarea>
|
<textarea name="content" class="layui-hide" id="vditor-textarea">{{ article.content }}</textarea>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<label class="layui-form-label">简介</label>
|
<div class="layui-input-block" style="margin:0;">
|
||||||
<div class="layui-input-block">
|
|
||||||
<textarea name="summary" class="layui-textarea">{{ article.summary }}</textarea>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="layui-form-item">
|
|
||||||
<label class="layui-form-label"></label>
|
|
||||||
<div class="layui-input-block">
|
|
||||||
<button class="kg-submit layui-btn" lay-submit="true" lay-filter="go">提交</button>
|
<button class="kg-submit layui-btn" lay-submit="true" lay-filter="go">提交</button>
|
||||||
<button type="button" class="kg-back layui-btn layui-btn-primary">返回</button>
|
<button type="button" class="kg-back layui-btn layui-btn-primary">返回</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -38,13 +38,13 @@
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>文章</th>
|
<th>文章</th>
|
||||||
<th>状态</th>
|
|
||||||
<th>浏览</th>
|
|
||||||
<th>评论</th>
|
<th>评论</th>
|
||||||
|
<th>浏览</th>
|
||||||
<th>点赞</th>
|
<th>点赞</th>
|
||||||
<th>收藏</th>
|
<th>收藏</th>
|
||||||
|
<th>状态</th>
|
||||||
<th>推荐</th>
|
<th>推荐</th>
|
||||||
<th>评论</th>
|
<th>关闭</th>
|
||||||
<th>操作</th>
|
<th>操作</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
@ -52,6 +52,7 @@
|
|||||||
{% for item in pager.items %}
|
{% for item in pager.items %}
|
||||||
{% set owner_url = url({'for':'home.user.show','id':item.owner.id}) %}
|
{% set owner_url = url({'for':'home.user.show','id':item.owner.id}) %}
|
||||||
{% set preview_url = url({'for':'home.article.show','id':item.id}) %}
|
{% set preview_url = url({'for':'home.article.show','id':item.id}) %}
|
||||||
|
{% set review_url = url({'for':'admin.article.review','id':item.id}) %}
|
||||||
{% set edit_url = url({'for':'admin.article.edit','id':item.id}) %}
|
{% set edit_url = url({'for':'admin.article.edit','id':item.id}) %}
|
||||||
{% set update_url = url({'for':'admin.article.update','id':item.id}) %}
|
{% set update_url = url({'for':'admin.article.update','id':item.id}) %}
|
||||||
{% set delete_url = url({'for':'admin.article.delete','id':item.id}) %}
|
{% set delete_url = url({'for':'admin.article.delete','id':item.id}) %}
|
||||||
@ -74,18 +75,22 @@
|
|||||||
<span>创建:{{ date('Y-m-d',item.create_time) }}</span>
|
<span>创建:{{ date('Y-m-d',item.create_time) }}</span>
|
||||||
</p>
|
</p>
|
||||||
</td>
|
</td>
|
||||||
<td>{{ publish_status(item.published) }}</td>
|
|
||||||
<td>{{ item.view_count }}</td>
|
<td>{{ item.view_count }}</td>
|
||||||
<td>{{ item.comment_count }}</td>
|
<td>{{ item.comment_count }}</td>
|
||||||
<td>{{ item.like_count }}</td>
|
<td>{{ item.like_count }}</td>
|
||||||
<td>{{ item.favorite_count }}</td>
|
<td>{{ item.favorite_count }}</td>
|
||||||
|
<td>{{ publish_status(item.published) }}</td>
|
||||||
<td><input type="checkbox" name="featured" value="1" lay-skin="switch" lay-text="是|否" lay-filter="featured" data-url="{{ update_url }}" {% if item.featured == 1 %}checked="checked"{% endif %}></td>
|
<td><input type="checkbox" name="featured" value="1" lay-skin="switch" lay-text="是|否" lay-filter="featured" data-url="{{ update_url }}" {% if item.featured == 1 %}checked="checked"{% endif %}></td>
|
||||||
<td><input type="checkbox" name="comment" value="1" lay-skin="switch" lay-text="开|关" lay-filter="comment" data-url="{{ update_url }}" {% if item.allow_comment == 1 %}checked="checked"{% endif %}></td>
|
<td><input type="checkbox" name="comment" value="1" lay-skin="switch" lay-text="是|否" lay-filter="closed" data-url="{{ update_url }}" {% if item.closed == 1 %}checked="checked"{% endif %}></td>
|
||||||
<td class="center">
|
<td class="center">
|
||||||
<div class="layui-dropdown">
|
<div class="layui-dropdown">
|
||||||
<button class="layui-btn layui-btn-sm">操作 <i class="layui-icon layui-icon-triangle-d"></i></button>
|
<button class="layui-btn layui-btn-sm">操作 <i class="layui-icon layui-icon-triangle-d"></i></button>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="{{ preview_url }}" target="_blank">预览文章</a></li>
|
{% if item.published == 1 %}
|
||||||
|
<li><a href="{{ review_url }}">审核文章</a></li>
|
||||||
|
{% elseif item.published == 2 %}
|
||||||
|
<li><a href="{{ preview_url }}" target="_blank">预览文章</a></li>
|
||||||
|
{% endif %}
|
||||||
<li><a href="{{ edit_url }}">编辑文章</a></li>
|
<li><a href="{{ edit_url }}">编辑文章</a></li>
|
||||||
{% if item.deleted == 0 %}
|
{% if item.deleted == 0 %}
|
||||||
<li><a href="javascript:" class="kg-delete" data-url="{{ delete_url }}">删除文章</a></li>
|
<li><a href="javascript:" class="kg-delete" data-url="{{ delete_url }}">删除文章</a></li>
|
||||||
@ -142,16 +147,16 @@
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
form.on('switch(comment)', function (data) {
|
form.on('switch(closed)', function (data) {
|
||||||
var checked = $(this).is(':checked');
|
var checked = $(this).is(':checked');
|
||||||
var allowComment = checked ? 1 : 0;
|
var closed = checked ? 1 : 0;
|
||||||
var url = $(this).data('url');
|
var url = $(this).data('url');
|
||||||
var tips = allowComment === 1 ? '确定要开启评论?' : '确定要关闭评论?';
|
var tips = closed === 1 ? '确定要关闭评论?' : '确定要开启评论?';
|
||||||
layer.confirm(tips, function () {
|
layer.confirm(tips, function () {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
type: 'POST',
|
type: 'POST',
|
||||||
url: url,
|
url: url,
|
||||||
data: {allow_comment: allowComment},
|
data: {closed: closed},
|
||||||
success: function (res) {
|
success: function (res) {
|
||||||
layer.msg(res.msg, {icon: 1});
|
layer.msg(res.msg, {icon: 1});
|
||||||
},
|
},
|
||||||
|
85
app/Http/Admin/Views/article/review.volt
Normal file
85
app/Http/Admin/Views/article/review.volt
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
{% extends 'templates/main.volt' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<fieldset class="layui-elem-field layui-field-title">
|
||||||
|
<legend>审核内容</legend>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<div class="kg-mod-preview">
|
||||||
|
<div class="title">{{ article.title }}</div>
|
||||||
|
<div class="content markdown-body">{{ article.content }}</div>
|
||||||
|
{% if article.tags %}
|
||||||
|
<div class="tags">
|
||||||
|
{% for item in article.tags %}
|
||||||
|
<span class="layui-btn layui-btn-xs">{{ item['name'] }}</span>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<fieldset class="layui-elem-field layui-field-title">
|
||||||
|
<legend>审核意见</legend>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<form class="layui-form kg-form kg-review-form" method="POST" action="{{ url({'for':'admin.article.review','id':article.id}) }}">
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label">审核</label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<input type="radio" name="type" value="approve" title="通过" lay-filter="review">
|
||||||
|
<input type="radio" name="type" value="reject" title="拒绝" lay-filter="review">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="reason-block" style="display:none;">
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label">理由</label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<select name="reason">
|
||||||
|
<option value="">请选择</option>
|
||||||
|
{% for value,name in reasons %}
|
||||||
|
<option value="{{ value }}">{{ name }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label"></label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<button id="kg-submit" class="layui-btn" lay-submit="true" lay-filter="go">提交</button>
|
||||||
|
<button type="button" class="kg-back layui-btn layui-btn-primary">返回</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block link_css %}
|
||||||
|
|
||||||
|
{{ css_link('home/css/markdown.css') }}
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block inline_js %}
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
layui.use(['jquery', 'form'], function () {
|
||||||
|
|
||||||
|
var $ = layui.jquery;
|
||||||
|
var form = layui.form;
|
||||||
|
|
||||||
|
form.on('radio(review)', function (data) {
|
||||||
|
var block = $('#reason-block');
|
||||||
|
if (data.value === 'approve') {
|
||||||
|
block.hide();
|
||||||
|
} else {
|
||||||
|
block.show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{% endblock %}
|
@ -24,17 +24,6 @@
|
|||||||
<input class="layui-input" type="text" name="title" placeholder="标题模糊匹配">
|
<input class="layui-input" type="text" name="title" placeholder="标题模糊匹配">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-form-item">
|
|
||||||
<label class="layui-form-label">分类</label>
|
|
||||||
<div class="layui-input-block">
|
|
||||||
<select name="category_id">
|
|
||||||
<option value="">请选择</option>
|
|
||||||
{% for item in categories %}
|
|
||||||
<option value="{{ item.id }}">{{ item.name }}</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<label class="layui-form-label">标签</label>
|
<label class="layui-form-label">标签</label>
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
@ -57,6 +46,20 @@
|
|||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label">允许评论</label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<input type="radio" name="closed" value="0" title="是">
|
||||||
|
<input type="radio" name="closed" value="1" title="否">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label">仅我可见</label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<input type="radio" name="private" value="1" title="是">
|
||||||
|
<input type="radio" name="private" value="0" title="否">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<label class="layui-form-label">推荐</label>
|
<label class="layui-form-label">推荐</label>
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
|
@ -40,20 +40,15 @@
|
|||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for item in pager.items %}
|
{% for item in pager.items %}
|
||||||
{% set list_by_id_url = url({'for':'admin.audit.list'},{'user_id':item.user_id}) %}
|
|
||||||
{% set list_by_ip_url = url({'for':'admin.audit.list'},{'user_ip':item.user_ip}) %}
|
|
||||||
{% set list_by_route_url = url({'for':'admin.audit.list'},{'req_route':item.req_route}) %}
|
|
||||||
{% set list_by_path_url = url({'for':'admin.audit.list'},{'req_path':item.req_path}) %}
|
|
||||||
{% set show_url = url({'for':'admin.audit.show','id':item.id}) %}
|
{% set show_url = url({'for':'admin.audit.show','id':item.id}) %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ item.user_id }}</td>
|
<td>{{ item.user_id }}</td>
|
||||||
<td><a href="{{ list_by_id_url }}">{{ item.user_name }}</a></td>
|
<td>{{ item.user_name }}</td>
|
||||||
<td>
|
<td>
|
||||||
<a href="{{ list_by_ip_url }}">{{ item.user_ip }}</a>
|
<a href="javascript:" class="kg-ip2region" title="查看位置" data-ip="{{ item.user_ip }}">{{ item.user_ip }}</a>
|
||||||
<span class="layui-btn layui-btn-xs kg-ip2region" data-ip="{{ item.user_ip }}">位置</span>
|
|
||||||
</td>
|
</td>
|
||||||
<td><a href="{{ list_by_route_url }}">{{ item.req_route }}</a></td>
|
<td>{{ item.req_route }}</td>
|
||||||
<td><a href="{{ list_by_path_url }}">{{ item.req_path }}</a></td>
|
<td>{{ item.req_path }}</td>
|
||||||
<td>{{ date('Y-m-d H:i:s',item.create_time) }}</td>
|
<td>{{ date('Y-m-d H:i:s',item.create_time) }}</td>
|
||||||
<td class="center">
|
<td class="center">
|
||||||
<button class="kg-view layui-btn layui-btn-sm" data-url="{{ show_url }}">详情</button>
|
<button class="kg-view layui-btn layui-btn-sm" data-url="{{ show_url }}">详情</button>
|
||||||
|
@ -1,98 +0,0 @@
|
|||||||
{% extends 'templates/main.volt' %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
|
|
||||||
<form class="layui-form kg-form" method="POST" action="{{ url({'for':'admin.review.update','id':review.id}) }}">
|
|
||||||
<fieldset class="layui-elem-field layui-field-title">
|
|
||||||
<legend>编辑评价</legend>
|
|
||||||
</fieldset>
|
|
||||||
<div class="layui-form-item">
|
|
||||||
<label class="layui-form-label">内容实用</label>
|
|
||||||
<div class="layui-input-block">
|
|
||||||
<div id="rating1" class="kg-rating"></div>
|
|
||||||
<input type="hidden" name="rating1" value="{{ review.rating1 }}"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="layui-form-item">
|
|
||||||
<label class="layui-form-label">通俗易懂</label>
|
|
||||||
<div class="layui-input-block">
|
|
||||||
<div id="rating2" class="kg-rating"></div>
|
|
||||||
<input type="hidden" name="rating2" value="{{ review.rating2 }}"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="layui-form-item">
|
|
||||||
<label class="layui-form-label">逻辑清晰</label>
|
|
||||||
<div class="layui-input-block">
|
|
||||||
<div id="rating3" class="kg-rating"></div>
|
|
||||||
<input type="hidden" name="rating3" value="{{ review.rating3 }}"/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="layui-form-item">
|
|
||||||
<label class="layui-form-label">课程评价</label>
|
|
||||||
<div class="layui-input-block">
|
|
||||||
<div class="layui-form-mid gray">{{ review.content }}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="layui-form-item">
|
|
||||||
<label class="layui-form-label">发布</label>
|
|
||||||
<div class="layui-input-block">
|
|
||||||
<input type="radio" name="published" value="1" title="是" {% if review.published == 1 %}checked="checked"{% endif %}>
|
|
||||||
<input type="radio" name="published" value="0" title="否" {% if review.published == 0 %}checked="checked"{% endif %}>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="layui-form-item">
|
|
||||||
<label class="layui-form-label"></label>
|
|
||||||
<div class="layui-input-block">
|
|
||||||
<button class="layui-btn" lay-submit="true" lay-filter="go">提交</button>
|
|
||||||
<button type="button" class="kg-back layui-btn layui-btn-primary">返回</button>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block inline_js %}
|
|
||||||
|
|
||||||
<script>
|
|
||||||
|
|
||||||
layui.use(['jquery', 'rate'], function () {
|
|
||||||
|
|
||||||
var $ = layui.jquery;
|
|
||||||
var rate = layui.rate;
|
|
||||||
|
|
||||||
var $rating1 = $('input[name=rating1]');
|
|
||||||
var $rating2 = $('input[name=rating2]');
|
|
||||||
var $rating3 = $('input[name=rating3]');
|
|
||||||
|
|
||||||
rate.render({
|
|
||||||
elem: '#rating1',
|
|
||||||
value: $rating1.val(),
|
|
||||||
readonly: true,
|
|
||||||
choose: function (value) {
|
|
||||||
$rating1.val(value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
rate.render({
|
|
||||||
elem: '#rating2',
|
|
||||||
value: $rating2.val(),
|
|
||||||
readonly: true,
|
|
||||||
choose: function (value) {
|
|
||||||
$rating2.val(value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
rate.render({
|
|
||||||
elem: '#rating3',
|
|
||||||
value: $rating3.val(),
|
|
||||||
readonly: true,
|
|
||||||
choose: function (value) {
|
|
||||||
$rating3.val(value);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{% endblock %}
|
|
@ -4,9 +4,9 @@
|
|||||||
|
|
||||||
{{ partial('macros/common') }}
|
{{ partial('macros/common') }}
|
||||||
|
|
||||||
<table class="layui-table kg-table layui-form">
|
<table class="layui-table kg-table layui-form" lay-size="lg">
|
||||||
<colgroup>
|
<colgroup>
|
||||||
<col>
|
<col width="50%">
|
||||||
<col>
|
<col>
|
||||||
<col>
|
<col>
|
||||||
<col>
|
<col>
|
||||||
@ -15,31 +15,23 @@
|
|||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>评论</th>
|
<th>评论</th>
|
||||||
|
<th>点赞</th>
|
||||||
<th>用户</th>
|
<th>用户</th>
|
||||||
<th>终端</th>
|
<th>时间</th>
|
||||||
<th>发布</th>
|
|
||||||
<th>操作</th>
|
<th>操作</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for item in pager.items %}
|
{% for item in pager.items %}
|
||||||
|
{% set owner_url = url({'for':'home.user.show','id':item.owner.id}) %}
|
||||||
{% set update_url = url({'for':'admin.comment.update','id':item.id}) %}
|
{% set update_url = url({'for':'admin.comment.update','id':item.id}) %}
|
||||||
{% set delete_url = url({'for':'admin.comment.delete','id':item.id}) %}
|
{% set delete_url = url({'for':'admin.comment.delete','id':item.id}) %}
|
||||||
{% set restore_url = url({'for':'admin.comment.restore','id':item.id}) %}
|
{% set restore_url = url({'for':'admin.comment.restore','id':item.id}) %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>{{ item.content }}</td>
|
||||||
<p>内容:<a href="javascript:" title="{{ item.content }}">{{ substr(item.content,0,30) }}</a></p>
|
<td>{{ item.like_count }}</td>
|
||||||
<p>时间:{{ date('Y-m-d H:i',item.create_time) }},点赞:{{ item.like_count }}</p>
|
<td><a href="{{ owner_url }}" target="_blank">{{ item.owner.name }}</a></td>
|
||||||
</td>
|
<td>{{ date('Y-m-d',item.create_time) }}</td>
|
||||||
<td>
|
|
||||||
<p>昵称:{{ item.owner.name }}</p>
|
|
||||||
<p>编号:{{ item.owner.id }}</p>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<p>类型:{{ client_type(item.client_type) }}</p>
|
|
||||||
<p>地址:<a href="javascript:" class="kg-ip2region" data-ip="{{ item.client_ip }}">查看</a></p>
|
|
||||||
</td>
|
|
||||||
<td><input type="checkbox" name="published" value="1" lay-skin="switch" lay-text="是|否" lay-filter="published" data-url="{{ update_url }}" {% if item.published == 1 %}checked="checked"{% endif %}></td>
|
|
||||||
<td class="center">
|
<td class="center">
|
||||||
{% if item.deleted == 0 %}
|
{% if item.deleted == 0 %}
|
||||||
<a href="javascript:" class="layui-badge layui-bg-red kg-delete" data-url="{{ delete_url }}">删除</a>
|
<a href="javascript:" class="layui-badge layui-bg-red kg-delete" data-url="{{ delete_url }}">删除</a>
|
||||||
@ -54,10 +46,4 @@
|
|||||||
|
|
||||||
{{ partial('partials/pager') }}
|
{{ partial('partials/pager') }}
|
||||||
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block include_js %}
|
|
||||||
|
|
||||||
{{ js_include('admin/js/ip2region.js') }}
|
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
11
app/Http/Admin/Views/macros/answer.volt
Normal file
11
app/Http/Admin/Views/macros/answer.volt
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{%- macro publish_status(type) %}
|
||||||
|
{% if type == 1 %}
|
||||||
|
审核中
|
||||||
|
{% elseif type == 2 %}
|
||||||
|
已发布
|
||||||
|
{% elseif type == 3 %}
|
||||||
|
未通过
|
||||||
|
{% else %}
|
||||||
|
未知
|
||||||
|
{% endif %}
|
||||||
|
{%- endmacro %}
|
@ -12,11 +12,11 @@
|
|||||||
|
|
||||||
{%- macro source_info(type,url) %}
|
{%- macro source_info(type,url) %}
|
||||||
{% if type == 1 %}
|
{% if type == 1 %}
|
||||||
原创
|
<span class="layui-badge">原创</span>
|
||||||
{% elseif type == 2 %}
|
{% elseif type == 2 %}
|
||||||
<a href="{{ url }}" target="_blank">转载</a>
|
<a class="layui-badge layui-bg-blue" href="{{ url }}" target="_blank">转载</a>
|
||||||
{% elseif type == 3 %}
|
{% elseif type == 3 %}
|
||||||
翻译
|
<span class="layui-badge layui-bg-gray">翻译</span>
|
||||||
{% else %}
|
{% else %}
|
||||||
N/A
|
N/A
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
18
app/Http/Admin/Views/macros/question.volt
Normal file
18
app/Http/Admin/Views/macros/question.volt
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{%- macro publish_status(type) %}
|
||||||
|
{% if type == 1 %}
|
||||||
|
审核中
|
||||||
|
{% elseif type == 2 %}
|
||||||
|
已发布
|
||||||
|
{% elseif type == 3 %}
|
||||||
|
未通过
|
||||||
|
{% else %}
|
||||||
|
未知
|
||||||
|
{% endif %}
|
||||||
|
{%- endmacro %}
|
||||||
|
|
||||||
|
{%- macro tags_info(items) %}
|
||||||
|
{% for item in items %}
|
||||||
|
{% set comma = loop.last ? '' : ',' %}
|
||||||
|
{{ item.name ~ comma }}
|
||||||
|
{% endfor %}
|
||||||
|
{%- endmacro %}
|
55
app/Http/Admin/Views/moderation/answers.volt
Normal file
55
app/Http/Admin/Views/moderation/answers.volt
Normal file
@ -0,0 +1,55 @@
|
|||||||
|
{% extends 'templates/main.volt' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
{{ partial('macros/answer') }}
|
||||||
|
|
||||||
|
<div class="kg-nav">
|
||||||
|
<div class="kg-nav-left">
|
||||||
|
<span class="layui-breadcrumb">
|
||||||
|
<a><cite>回答审核</cite></a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table class="layui-table kg-table layui-form">
|
||||||
|
<colgroup>
|
||||||
|
<col width="50%">
|
||||||
|
<col>
|
||||||
|
<col>
|
||||||
|
<col width="10%">
|
||||||
|
</colgroup>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>回答</th>
|
||||||
|
<th>作者</th>
|
||||||
|
<th>时间</th>
|
||||||
|
<th>操作</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for item in pager.items %}
|
||||||
|
{% set question_url = url({'for':'home.question.show','id':item.question.id}) %}
|
||||||
|
{% set owner_url = url({'for':'home.user.show','id':item.owner.id}) %}
|
||||||
|
{% set review_url = url({'for':'admin.answer.review','id':item.id}) %}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<P>问题:<a href="{{ question_url }}" target="_blank">{{ item.question.title }}</a></P>
|
||||||
|
<p class="layui-elip">回答:{{ substr(item.summary,0,32) }}</p>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<p>昵称:<a href="{{ owner_url }}" target="_blank">{{ item.owner.name }}</a></p>
|
||||||
|
<p>编号:{{ item.owner.id }}</p>
|
||||||
|
</td>
|
||||||
|
<td>{{ date('Y-m-d H:i',item.create_time) }}</td>
|
||||||
|
<td class="center">
|
||||||
|
<a href="{{ review_url }}" class="layui-btn layui-btn-sm">详情</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{{ partial('partials/pager') }}
|
||||||
|
|
||||||
|
{% endblock %}
|
@ -14,8 +14,6 @@
|
|||||||
|
|
||||||
<table class="layui-table kg-table layui-form">
|
<table class="layui-table kg-table layui-form">
|
||||||
<colgroup>
|
<colgroup>
|
||||||
<col>
|
|
||||||
<col>
|
|
||||||
<col>
|
<col>
|
||||||
<col>
|
<col>
|
||||||
<col>
|
<col>
|
||||||
@ -25,8 +23,6 @@
|
|||||||
<tr>
|
<tr>
|
||||||
<th>文章</th>
|
<th>文章</th>
|
||||||
<th>作者</th>
|
<th>作者</th>
|
||||||
<th>来源</th>
|
|
||||||
<th>评论</th>
|
|
||||||
<th>时间</th>
|
<th>时间</th>
|
||||||
<th>操作</th>
|
<th>操作</th>
|
||||||
</tr>
|
</tr>
|
||||||
@ -34,14 +30,11 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
{% for item in pager.items %}
|
{% for item in pager.items %}
|
||||||
{% set owner_url = url({'for':'home.user.show','id':item.owner.id}) %}
|
{% set owner_url = url({'for':'home.user.show','id':item.owner.id}) %}
|
||||||
{% set show_url = url({'for':'admin.article.show','id':item.id}) %}
|
{% set review_url = url({'for':'admin.article.review','id':item.id}) %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<p>标题:{{ item.title }}({{ item.id }})</p>
|
<p>标题:{{ item.title }} {{ source_info(item.source_type,item.source_url) }}</p>
|
||||||
<p class="meta">
|
<p class="meta">
|
||||||
{% if item.category.id is defined %}
|
|
||||||
<span>分类:{{ item.category.name }}</span>
|
|
||||||
{% endif %}
|
|
||||||
{% if item.tags %}
|
{% if item.tags %}
|
||||||
<span>标签:{{ tags_info(item.tags) }}</span>
|
<span>标签:{{ tags_info(item.tags) }}</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
@ -51,23 +44,9 @@
|
|||||||
<p>昵称:<a href="{{ owner_url }}" target="_blank">{{ item.owner.name }}</a></p>
|
<p>昵称:<a href="{{ owner_url }}" target="_blank">{{ item.owner.name }}</a></p>
|
||||||
<p>编号:{{ item.owner.id }}</p>
|
<p>编号:{{ item.owner.id }}</p>
|
||||||
</td>
|
</td>
|
||||||
<td>{{ source_info(item.source_type,item.source_url) }}</td>
|
<td>{{ date('Y-m-d H:i',item.create_time) }}</td>
|
||||||
<td>
|
|
||||||
{% if item.allow_comment == 1 %}
|
|
||||||
开启
|
|
||||||
{% else %}
|
|
||||||
关闭
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
{% if item.update_time > 0 %}
|
|
||||||
{{ date('Y-m-d H:i:s',item.update_time) }}
|
|
||||||
{% else %}
|
|
||||||
{{ date('Y-m-d H:i:s',item.create_time) }}
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
<td class="center">
|
<td class="center">
|
||||||
<a href="{{ show_url }}" class="layui-btn layui-btn-sm">详情</a>
|
<a href="{{ review_url }}" class="layui-btn layui-btn-sm">详情</a>
|
||||||
</td>
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
58
app/Http/Admin/Views/moderation/questions.volt
Normal file
58
app/Http/Admin/Views/moderation/questions.volt
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
{% extends 'templates/main.volt' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
{{ partial('macros/question') }}
|
||||||
|
|
||||||
|
<div class="kg-nav">
|
||||||
|
<div class="kg-nav-left">
|
||||||
|
<span class="layui-breadcrumb">
|
||||||
|
<a><cite>问题审核</cite></a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table class="layui-table kg-table layui-form">
|
||||||
|
<colgroup>
|
||||||
|
<col>
|
||||||
|
<col>
|
||||||
|
<col>
|
||||||
|
<col width="10%">
|
||||||
|
</colgroup>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>问题</th>
|
||||||
|
<th>作者</th>
|
||||||
|
<th>时间</th>
|
||||||
|
<th>操作</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for item in pager.items %}
|
||||||
|
{% set owner_url = url({'for':'home.user.show','id':item.owner.id}) %}
|
||||||
|
{% set review_url = url({'for':'admin.question.review','id':item.id}) %}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<p>标题:{{ item.title }}</p>
|
||||||
|
<p class="meta">
|
||||||
|
{% if item.tags %}
|
||||||
|
<span>标签:{{ tags_info(item.tags) }}</span>
|
||||||
|
{% endif %}
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<p>昵称:<a href="{{ owner_url }}" target="_blank">{{ item.owner.name }}</a></p>
|
||||||
|
<p>编号:{{ item.owner.id }}</p>
|
||||||
|
</td>
|
||||||
|
<td>{{ date('Y-m-d H:i',item.create_time) }}</td>
|
||||||
|
<td class="center">
|
||||||
|
<a href="{{ review_url }}" class="layui-btn layui-btn-sm">详情</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{{ partial('partials/pager') }}
|
||||||
|
|
||||||
|
{% endblock %}
|
24
app/Http/Admin/Views/question/add.volt
Normal file
24
app/Http/Admin/Views/question/add.volt
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{% extends 'templates/main.volt' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<form class="layui-form kg-form" method="POST" action="{{ url({'for':'admin.question.create'}) }}">
|
||||||
|
<fieldset class="layui-elem-field layui-field-title">
|
||||||
|
<legend>添加问题</legend>
|
||||||
|
</fieldset>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label">标题</label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<input class="layui-input" type="text" name="title" lay-verify="required">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label"></label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<button id="kg-submit" class="layui-btn" lay-submit="true" lay-filter="go">提交</button>
|
||||||
|
<button type="button" class="kg-back layui-btn layui-btn-primary">返回</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% endblock %}
|
61
app/Http/Admin/Views/question/edit.volt
Normal file
61
app/Http/Admin/Views/question/edit.volt
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
{% extends 'templates/main.volt' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<fieldset class="layui-elem-field layui-field-title">
|
||||||
|
<legend>编辑问题</legend>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<div class="layui-tab layui-tab-brief">
|
||||||
|
<ul class="layui-tab-title kg-tab-title">
|
||||||
|
<li class="layui-this">基本信息</li>
|
||||||
|
<li>内容详情</li>
|
||||||
|
</ul>
|
||||||
|
<div class="layui-tab-content">
|
||||||
|
<div class="layui-tab-item layui-show">
|
||||||
|
{{ partial('question/edit_basic') }}
|
||||||
|
</div>
|
||||||
|
<div class="layui-tab-item">
|
||||||
|
{{ partial('question/edit_desc') }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block link_css %}
|
||||||
|
|
||||||
|
{{ css_link('https://cdn.jsdelivr.net/npm/vditor/dist/index.css', false) }}
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block include_js %}
|
||||||
|
|
||||||
|
{{ js_include('https://cdn.jsdelivr.net/npm/vditor/dist/index.min.js', false) }}
|
||||||
|
{{ js_include('lib/xm-select.js') }}
|
||||||
|
{{ js_include('admin/js/vditor.js') }}
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block inline_js %}
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
layui.use(['jquery', 'form'], function () {
|
||||||
|
|
||||||
|
xmSelect.render({
|
||||||
|
el: '#xm-tag-ids',
|
||||||
|
name: 'xm_tag_ids',
|
||||||
|
max: 3,
|
||||||
|
filterable: true,
|
||||||
|
filterMethod: function (val, item, index, prop) {
|
||||||
|
return item.name.toLowerCase().indexOf(val.toLowerCase()) !== -1;
|
||||||
|
},
|
||||||
|
data: {{ xm_tags|json_encode }}
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{% endblock %}
|
35
app/Http/Admin/Views/question/edit_basic.volt
Normal file
35
app/Http/Admin/Views/question/edit_basic.volt
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
<form class="layui-form kg-form" method="POST" action="{{ url({'for':'admin.question.update','id':question.id}) }}">
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label">标题</label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<input class="layui-input" type="text" name="title" value="{{ question.title }}" lay-verify="required">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label">标签</label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<div id="xm-tag-ids"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label">匿名</label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<input type="radio" name="anonymous" value="1" title="是" {% if question.anonymous == 1 %}checked="checked"{% endif %}>
|
||||||
|
<input type="radio" name="anonymous" value="0" title="否" {% if question.anonymous == 0 %}checked="checked"{% endif %}>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label">关闭</label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<input type="radio" name="closed" value="1" title="是" {% if question.closed == 1 %}checked="checked"{% endif %}>
|
||||||
|
<input type="radio" name="closed" value="0" title="否" {% if question.closed == 0 %}checked="checked"{% endif %}>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label"></label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<button class="layui-btn kg-submit" lay-submit="true" lay-filter="go">提交</button>
|
||||||
|
<button type="button" class="kg-back layui-btn layui-btn-primary">返回</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
14
app/Http/Admin/Views/question/edit_desc.volt
Normal file
14
app/Http/Admin/Views/question/edit_desc.volt
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<form class="layui-form kg-form" method="POST" action="{{ url({'for':'admin.question.update','id':question.id}) }}">
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<div class="layui-input-block" style="margin:0;">
|
||||||
|
<div id="vditor"></div>
|
||||||
|
<textarea name="content" class="layui-hide" id="vditor-textarea">{{ question.content }}</textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<div class="layui-input-block" style="margin:0;">
|
||||||
|
<button class="kg-submit layui-btn" lay-submit="true" lay-filter="go">提交</button>
|
||||||
|
<button type="button" class="kg-back layui-btn layui-btn-primary">返回</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
162
app/Http/Admin/Views/question/list.volt
Normal file
162
app/Http/Admin/Views/question/list.volt
Normal file
@ -0,0 +1,162 @@
|
|||||||
|
{% extends 'templates/main.volt' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
{{ partial('macros/question') }}
|
||||||
|
|
||||||
|
{% set add_url = url({'for':'admin.question.add'}) %}
|
||||||
|
{% set search_url = url({'for':'admin.question.search'}) %}
|
||||||
|
|
||||||
|
<div class="kg-nav">
|
||||||
|
<div class="kg-nav-left">
|
||||||
|
<span class="layui-breadcrumb">
|
||||||
|
<a><cite>问题管理</cite></a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div class="kg-nav-right">
|
||||||
|
<a class="layui-btn layui-btn-sm" href="{{ add_url }}">
|
||||||
|
<i class="layui-icon layui-icon-add-1"></i>添加问题
|
||||||
|
</a>
|
||||||
|
<a class="layui-btn layui-btn-sm" href="{{ search_url }}">
|
||||||
|
<i class="layui-icon layui-icon-search"></i>搜索问题
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table class="layui-table kg-table layui-form">
|
||||||
|
<colgroup>
|
||||||
|
<col>
|
||||||
|
<col>
|
||||||
|
<col>
|
||||||
|
<col>
|
||||||
|
<col>
|
||||||
|
<col>
|
||||||
|
<col>
|
||||||
|
<col width="10%">
|
||||||
|
</colgroup>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>问题</th>
|
||||||
|
<th>回答</th>
|
||||||
|
<th>浏览</th>
|
||||||
|
<th>点赞</th>
|
||||||
|
<th>收藏</th>
|
||||||
|
<th>状态</th>
|
||||||
|
<th>关闭</th>
|
||||||
|
<th>操作</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for item in pager.items %}
|
||||||
|
{% set owner_url = url({'for':'home.user.show','id':item.owner.id}) %}
|
||||||
|
{% set preview_url = url({'for':'home.question.show','id':item.id}) %}
|
||||||
|
{% set review_url = url({'for':'admin.question.review','id':item.id}) %}
|
||||||
|
{% set edit_url = url({'for':'admin.question.edit','id':item.id}) %}
|
||||||
|
{% set update_url = url({'for':'admin.question.update','id':item.id}) %}
|
||||||
|
{% set delete_url = url({'for':'admin.question.delete','id':item.id}) %}
|
||||||
|
{% set restore_url = url({'for':'admin.question.restore','id':item.id}) %}
|
||||||
|
{% set answer_add_url = url({'for':'admin.answer.add'},{'question_id':item.id}) %}
|
||||||
|
{% set answer_list_url = url({'for':'admin.answer.list'},{'question_id':item.id}) %}
|
||||||
|
<tr>
|
||||||
|
<td>
|
||||||
|
<p>标题:<a href="{{ edit_url }}">{{ item.title }}</a>({{ item.id }})</p>
|
||||||
|
<p class="meta">
|
||||||
|
{% if item.category.id is defined %}
|
||||||
|
<span>分类:{{ item.category.name }}</span>
|
||||||
|
{% endif %}
|
||||||
|
{% if item.tags %}
|
||||||
|
<span>标签:{{ tags_info(item.tags) }}</span>
|
||||||
|
{% endif %}
|
||||||
|
</p>
|
||||||
|
<p class="meta">
|
||||||
|
<span>作者:<a href="{{ owner_url }}" target="_blank">{{ item.owner.name }}</a></span>
|
||||||
|
<span>创建:{{ date('Y-m-d',item.create_time) }}</span>
|
||||||
|
</p>
|
||||||
|
</td>
|
||||||
|
<td>{{ item.answer_count }}</td>
|
||||||
|
<td>{{ item.view_count }}</td>
|
||||||
|
<td>{{ item.like_count }}</td>
|
||||||
|
<td>{{ item.favorite_count }}</td>
|
||||||
|
<td>{{ publish_status(item.published) }}</td>
|
||||||
|
<td><input type="checkbox" name="closed" value="1" lay-skin="switch" lay-text="是|否" lay-filter="closed" data-url="{{ update_url }}" {% if item.closed == 1 %}checked="checked"{% endif %}></td>
|
||||||
|
<td class="center">
|
||||||
|
<div class="layui-dropdown">
|
||||||
|
<button class="layui-btn layui-btn-sm">操作 <i class="layui-icon layui-icon-triangle-d"></i></button>
|
||||||
|
<ul>
|
||||||
|
{% if item.published == 1 %}
|
||||||
|
<li><a href="{{ review_url }}">审核问题</a></li>
|
||||||
|
{% elseif item.published == 2 %}
|
||||||
|
<li><a href="{{ preview_url }}" target="_blank">预览问题</a></li>
|
||||||
|
{% endif %}
|
||||||
|
<li><a href="{{ answer_add_url }}">回答问题</a></li>
|
||||||
|
<li><a href="{{ edit_url }}">编辑问题</a></li>
|
||||||
|
{% if item.deleted == 0 %}
|
||||||
|
<li><a href="javascript:" class="kg-delete" data-url="{{ delete_url }}">删除问题</a></li>
|
||||||
|
{% else %}
|
||||||
|
<li><a href="javascript:" class="kg-restore" data-url="{{ restore_url }}">还原问题</a></li>
|
||||||
|
{% endif %}
|
||||||
|
<hr>
|
||||||
|
<li><a href="{{ answer_list_url }}">回答管理</a></li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{{ partial('partials/pager') }}
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block inline_js %}
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
layui.define(['jquery', 'form', 'layer'], function () {
|
||||||
|
|
||||||
|
var $ = layui.jquery;
|
||||||
|
var form = layui.form;
|
||||||
|
var layer = layui.layer;
|
||||||
|
|
||||||
|
form.on('switch(closed)', function (data) {
|
||||||
|
var checked = $(this).is(':checked');
|
||||||
|
var closed = checked ? 1 : 0;
|
||||||
|
var url = $(this).data('url');
|
||||||
|
var tips = closed === 1 ? '确定要关闭讨论?' : '确定要开启讨论?';
|
||||||
|
layer.confirm(tips, function () {
|
||||||
|
$.ajax({
|
||||||
|
type: 'POST',
|
||||||
|
url: url,
|
||||||
|
data: {closed: data.value},
|
||||||
|
success: function (res) {
|
||||||
|
layer.msg(res.msg, {icon: 1});
|
||||||
|
},
|
||||||
|
error: function (xhr) {
|
||||||
|
var json = JSON.parse(xhr.responseText);
|
||||||
|
layer.msg(json.msg, {icon: 2});
|
||||||
|
data.elem.checked = !checked;
|
||||||
|
form.render();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}, function () {
|
||||||
|
data.elem.checked = !checked;
|
||||||
|
form.render();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
$('.kg-answer').on('click', function () {
|
||||||
|
var url = $(this).data('url');
|
||||||
|
layer.open({
|
||||||
|
type: 2,
|
||||||
|
title: '回答管理',
|
||||||
|
area: ['1000px', '600px'],
|
||||||
|
content: url
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{% endblock %}
|
85
app/Http/Admin/Views/question/review.volt
Normal file
85
app/Http/Admin/Views/question/review.volt
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
{% extends 'templates/main.volt' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<fieldset class="layui-elem-field layui-field-title">
|
||||||
|
<legend>审核内容</legend>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<div class="kg-mod-preview">
|
||||||
|
<div class="title">{{ question.title }}</div>
|
||||||
|
<div class="content markdown-body">{{ question.content }}</div>
|
||||||
|
{% if question.tags %}
|
||||||
|
<div class="tags">
|
||||||
|
{% for item in question.tags %}
|
||||||
|
<span class="layui-btn layui-btn-xs">{{ item['name'] }}</span>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<fieldset class="layui-elem-field layui-field-title">
|
||||||
|
<legend>审核意见</legend>
|
||||||
|
</fieldset>
|
||||||
|
|
||||||
|
<form class="layui-form kg-form kg-review-form" method="POST" action="{{ url({'for':'admin.question.review','id':question.id}) }}">
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label">审核</label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<input type="radio" name="type" value="approve" title="通过" lay-filter="review">
|
||||||
|
<input type="radio" name="type" value="reject" title="拒绝" lay-filter="review">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div id="reason-block" style="display:none;">
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label">理由</label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<select name="reason">
|
||||||
|
<option value="">请选择</option>
|
||||||
|
{% for value,name in reasons %}
|
||||||
|
<option value="{{ value }}">{{ name }}</option>
|
||||||
|
{% endfor %}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label"></label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<button id="kg-submit" class="layui-btn" lay-submit="true" lay-filter="go">提交</button>
|
||||||
|
<button type="button" class="kg-back layui-btn layui-btn-primary">返回</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block link_css %}
|
||||||
|
|
||||||
|
{{ css_link('home/css/markdown.css') }}
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block inline_js %}
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
layui.use(['jquery', 'form'], function () {
|
||||||
|
|
||||||
|
var $ = layui.jquery;
|
||||||
|
var form = layui.form;
|
||||||
|
|
||||||
|
form.on('radio(review)', function (data) {
|
||||||
|
var block = $('#reason-block');
|
||||||
|
if (data.value === 'approve') {
|
||||||
|
block.hide();
|
||||||
|
} else {
|
||||||
|
block.show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{% endblock %}
|
93
app/Http/Admin/Views/question/search.volt
Normal file
93
app/Http/Admin/Views/question/search.volt
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
{% extends 'templates/main.volt' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<form class="layui-form kg-form" method="GET" action="{{ url({'for':'admin.question.list'}) }}">
|
||||||
|
<fieldset class="layui-elem-field layui-field-title">
|
||||||
|
<legend>搜索问题</legend>
|
||||||
|
</fieldset>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label">问题编号</label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<input class="layui-input" type="text" name="id" placeholder="问题编号精确匹配">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label">作者编号</label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<input class="layui-input" type="text" name="owner_id" placeholder="作者编号精确匹配">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label">标题</label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<input class="layui-input" type="text" name="title" placeholder="标题模糊匹配">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label">标签</label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<div id="xm-tag-ids"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label">发布</label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
{% for value,title in publish_types %}
|
||||||
|
<input type="radio" name="published" value="{{ value }}" title="{{ title }}">
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label">匿名</label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<input type="radio" name="anonymous" value="1" title="是">
|
||||||
|
<input type="radio" name="anonymous" value="0" title="否">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label">关闭</label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<input type="radio" name="closed" value="1" title="是">
|
||||||
|
<input type="radio" name="closed" value="0" title="否">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label">删除</label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<input type="radio" name="deleted" value="1" title="是">
|
||||||
|
<input type="radio" name="deleted" value="0" title="否">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<label class="layui-form-label"></label>
|
||||||
|
<div class="layui-input-block">
|
||||||
|
<button class="layui-btn" lay-submit="true">提交</button>
|
||||||
|
<button type="button" class="kg-back layui-btn layui-btn-primary">返回</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block include_js %}
|
||||||
|
|
||||||
|
{{ js_include('lib/xm-select.js') }}
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block inline_js %}
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
xmSelect.render({
|
||||||
|
el: '#xm-tag-ids',
|
||||||
|
name: 'xm_tag_ids',
|
||||||
|
max: 5,
|
||||||
|
filterable: true,
|
||||||
|
data: {{ xm_tags|json_encode }}
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{% endblock %}
|
@ -121,10 +121,6 @@
|
|||||||
<td>群组头像</td>
|
<td>群组头像</td>
|
||||||
<td>public/static/admin/img/default/group_cover.png</td>
|
<td>public/static/admin/img/default/group_cover.png</td>
|
||||||
</tr>
|
</tr>
|
||||||
<tr>
|
|
||||||
<td>文章封面</td>
|
|
||||||
<td>public/static/admin/img/default/article_cover.png</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
<tr>
|
||||||
<td>课程封面</td>
|
<td>课程封面</td>
|
||||||
<td>public/static/admin/img/default/course_cover.png</td>
|
<td>public/static/admin/img/default/course_cover.png</td>
|
||||||
|
@ -12,12 +12,6 @@
|
|||||||
<input class="layui-input" type="text" name="name" lay-verify="required">
|
<input class="layui-input" type="text" name="name" lay-verify="required">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-form-item">
|
|
||||||
<label class="layui-form-label">排序</label>
|
|
||||||
<div class="layui-input-block">
|
|
||||||
<input class="layui-input" type="text" name="priority" lay-verify="number">
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<label class="layui-form-label"></label>
|
<label class="layui-form-label"></label>
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
|
@ -7,15 +7,19 @@
|
|||||||
<legend>编辑标签</legend>
|
<legend>编辑标签</legend>
|
||||||
</fieldset>
|
</fieldset>
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<label class="layui-form-label">名称</label>
|
<label class="layui-form-label">图标</label>
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-inline" style="width: 80px;">
|
||||||
<input class="layui-input" type="text" name="name" value="{{ tag2.name }}" lay-verify="required">
|
<img id="img-icon" class="kg-icon" src="{{ tag2.icon }}">
|
||||||
|
<input type="hidden" name="icon" value="{{ tag2.icon }}">
|
||||||
|
</div>
|
||||||
|
<div class="layui-input-inline" style="padding-top:15px;">
|
||||||
|
<button id="change-icon" class="layui-btn layui-btn-sm" type="button">更换</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<label class="layui-form-label">排序</label>
|
<label class="layui-form-label">名称</label>
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
<input class="layui-input" type="text" name="priority" value="{{ tag2.priority }}" lay-verify="number">
|
<input class="layui-input" type="text" name="name" value="{{ tag2.name }}" lay-verify="required">
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
@ -34,4 +38,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block include_js %}
|
||||||
|
|
||||||
|
{{ js_include('admin/js/icon.upload.js') }}
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
@ -29,15 +29,17 @@
|
|||||||
<col>
|
<col>
|
||||||
<col>
|
<col>
|
||||||
<col>
|
<col>
|
||||||
|
<col>
|
||||||
<col width="12%">
|
<col width="12%">
|
||||||
</colgroup>
|
</colgroup>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>编号</th>
|
<th>编号</th>
|
||||||
|
<th>图标</th>
|
||||||
<th>名称</th>
|
<th>名称</th>
|
||||||
|
<th>关注数</th>
|
||||||
<th>创建时间</th>
|
<th>创建时间</th>
|
||||||
<th>更新时间</th>
|
<th>更新时间</th>
|
||||||
<th>排序</th>
|
|
||||||
<th>发布</th>
|
<th>发布</th>
|
||||||
<th>操作</th>
|
<th>操作</th>
|
||||||
</tr>
|
</tr>
|
||||||
@ -49,10 +51,11 @@
|
|||||||
{% set delete_url = url({'for':'admin.tag.delete','id':item.id}) %}
|
{% set delete_url = url({'for':'admin.tag.delete','id':item.id}) %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>{{ item.id }}</td>
|
<td>{{ item.id }}</td>
|
||||||
|
<td><img class="kg-icon" src="{{ item.icon }}" alt="{{ item.name }}"></td>
|
||||||
<td><a href="{{ edit_url }}">{{ item.name }}</a></td>
|
<td><a href="{{ edit_url }}">{{ item.name }}</a></td>
|
||||||
<td>{{ date('Y-m-d H:i',item.create_time) }}</td>
|
<td>{{ item.follow_count }}</td>
|
||||||
<td>{{ date('Y-m-d H:i',item.update_time) }}</td>
|
<td>{{ date('Y-m-d',item.create_time) }}</td>
|
||||||
<td class="center"><input class="layui-input kg-priority" type="text" name="priority" title="数值越小排序越靠前" value="{{ item.priority }}" data-url="{{ update_url }}"></td>
|
<td>{{ date('Y-m-d',item.update_time) }}</td>
|
||||||
<td><input type="checkbox" name="published" value="1" lay-skin="switch" lay-text="是|否" lay-filter="published" data-url="{{ update_url }}" {% if item.published == 1 %}checked="checked"{% endif %}>
|
<td><input type="checkbox" name="published" value="1" lay-skin="switch" lay-text="是|否" lay-filter="published" data-url="{{ update_url }}" {% if item.published == 1 %}checked="checked"{% endif %}>
|
||||||
</td>
|
</td>
|
||||||
<td class="center">
|
<td class="center">
|
||||||
|
@ -33,6 +33,7 @@
|
|||||||
<col>
|
<col>
|
||||||
<col>
|
<col>
|
||||||
<col>
|
<col>
|
||||||
|
<col>
|
||||||
<col width="10%">
|
<col width="10%">
|
||||||
</colgroup>
|
</colgroup>
|
||||||
<thead>
|
<thead>
|
||||||
@ -42,7 +43,8 @@
|
|||||||
<th>用户角色</th>
|
<th>用户角色</th>
|
||||||
<th>课程</th>
|
<th>课程</th>
|
||||||
<th>文章</th>
|
<th>文章</th>
|
||||||
<th>收藏</th>
|
<th>提问</th>
|
||||||
|
<th>回答</th>
|
||||||
<th>活跃时间</th>
|
<th>活跃时间</th>
|
||||||
<th>注册时间</th>
|
<th>注册时间</th>
|
||||||
<th>操作</th>
|
<th>操作</th>
|
||||||
@ -51,6 +53,7 @@
|
|||||||
<tbody>
|
<tbody>
|
||||||
{% for item in pager.items %}
|
{% for item in pager.items %}
|
||||||
{% set preview_url = url({'for':'home.user.show','id':item.id}) %}
|
{% set preview_url = url({'for':'home.user.show','id':item.id}) %}
|
||||||
|
{% set online_url = url({'for':'admin.user.online','id':item.id}) %}
|
||||||
{% set edit_url = url({'for':'admin.user.edit','id':item.id}) %}
|
{% set edit_url = url({'for':'admin.user.edit','id':item.id}) %}
|
||||||
<tr>
|
<tr>
|
||||||
<td class="center">
|
<td class="center">
|
||||||
@ -71,15 +74,17 @@
|
|||||||
</td>
|
</td>
|
||||||
<td>{{ item.course_count }}</td>
|
<td>{{ item.course_count }}</td>
|
||||||
<td>{{ item.article_count }}</td>
|
<td>{{ item.article_count }}</td>
|
||||||
<td>{{ item.favorite_count }}</td>
|
<td>{{ item.question_count }}</td>
|
||||||
|
<td>{{ item.answer_count }}</td>
|
||||||
<td>{{ date('Y-m-d',item.active_time) }}</td>
|
<td>{{ date('Y-m-d',item.active_time) }}</td>
|
||||||
<td>{{ date('Y-m-d',item.create_time) }}</td>
|
<td>{{ date('Y-m-d',item.create_time) }}</td>
|
||||||
<td class="center">
|
<td class="center">
|
||||||
<div class="layui-dropdown">
|
<div class="layui-dropdown">
|
||||||
<button class="layui-btn layui-btn-sm">操作 <i class="layui-icon layui-icon-triangle-d"></i></button>
|
<button class="layui-btn layui-btn-sm">操作 <i class="layui-icon layui-icon-triangle-d"></i></button>
|
||||||
<ul>
|
<ul>
|
||||||
<li><a href="{{ preview_url }}" target="_blank">预览</a></li>
|
<li><a href="{{ preview_url }}" target="_blank">用户主页</a></li>
|
||||||
<li><a href="{{ edit_url }}">编辑</a></li>
|
<li><a href="javascript:" class="kg-online" data-url="{{ online_url }}">在线记录</a></li>
|
||||||
|
<li><a href="{{ edit_url }}">编辑用户</a></li>
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
@ -90,4 +95,29 @@
|
|||||||
|
|
||||||
{{ partial('partials/pager') }}
|
{{ partial('partials/pager') }}
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block inline_js %}
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
layui.define(['jquery', 'layer'], function () {
|
||||||
|
|
||||||
|
var $ = layui.jquery;
|
||||||
|
var layer = layui.layer;
|
||||||
|
|
||||||
|
$('.kg-online').on('click', function () {
|
||||||
|
var url = $(this).data('url');
|
||||||
|
layer.open({
|
||||||
|
type: 2,
|
||||||
|
title: '在线记录',
|
||||||
|
area: ['800px', '600px'],
|
||||||
|
content: url
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
{% endblock %}
|
{% endblock %}
|
42
app/Http/Admin/Views/user/online.volt
Normal file
42
app/Http/Admin/Views/user/online.volt
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
{% extends 'templates/main.volt' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
{{ partial('macros/common') }}
|
||||||
|
|
||||||
|
<table class="layui-table kg-table">
|
||||||
|
<colgroup>
|
||||||
|
<col>
|
||||||
|
<col>
|
||||||
|
<col>
|
||||||
|
<col>
|
||||||
|
</colgroup>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>终端类型</th>
|
||||||
|
<th>终端地址</th>
|
||||||
|
<th>活跃时间</th>
|
||||||
|
<th>创建时间</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{% for item in pager.items %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ client_type(item.client_type) }}</td>
|
||||||
|
<td><a href="javascript:" title="查看位置" class="layui-badge layui-bg-gray kg-ip2region">{{ item.client_ip }}</a></td>
|
||||||
|
<td>{{ date('Y-m-d H:i',item.active_time) }}</td>
|
||||||
|
<td>{{ date('Y-m-d H:i',item.create_time) }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
{{ partial('partials/pager') }}
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block include_js %}
|
||||||
|
|
||||||
|
{{ js_include('admin/js/ip2region.js') }}
|
||||||
|
|
||||||
|
{% endblock %}
|
178
app/Http/Home/Controllers/AnswerController.php
Normal file
178
app/Http/Home/Controllers/AnswerController.php
Normal file
@ -0,0 +1,178 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Home\Controllers;
|
||||||
|
|
||||||
|
use App\Http\Home\Services\Answer as AnswerService;
|
||||||
|
use App\Http\Home\Services\Question as QuestionService;
|
||||||
|
use App\Services\Logic\Answer\AnswerAccept as AnswerAcceptService;
|
||||||
|
use App\Services\Logic\Answer\AnswerCreate as AnswerCreateService;
|
||||||
|
use App\Services\Logic\Answer\AnswerDelete as AnswerDeleteService;
|
||||||
|
use App\Services\Logic\Answer\AnswerInfo as AnswerInfoService;
|
||||||
|
use App\Services\Logic\Answer\AnswerLike as AnswerLikeService;
|
||||||
|
use App\Services\Logic\Answer\AnswerUpdate as AnswerUpdateService;
|
||||||
|
use Phalcon\Mvc\View;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @RoutePrefix("/answer")
|
||||||
|
*/
|
||||||
|
class AnswerController extends Controller
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Get("/tips", name="home.answer.tips")
|
||||||
|
*/
|
||||||
|
public function tipsAction()
|
||||||
|
{
|
||||||
|
$this->view->setRenderLevel(View::LEVEL_ACTION_VIEW);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Get("/add", name="home.answer.add")
|
||||||
|
*/
|
||||||
|
public function addAction()
|
||||||
|
{
|
||||||
|
$id = $this->request->getQuery('question_id', 'int', 0);
|
||||||
|
|
||||||
|
$service = new QuestionService();
|
||||||
|
|
||||||
|
$question = $service->getQuestion($id);
|
||||||
|
|
||||||
|
$this->seo->prependTitle('回答问题');
|
||||||
|
|
||||||
|
$this->view->setVar('question', $question);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Get("/{id:[0-9]+}/edit", name="home.answer.edit")
|
||||||
|
*/
|
||||||
|
public function editAction($id)
|
||||||
|
{
|
||||||
|
$service = new AnswerService();
|
||||||
|
|
||||||
|
$answer = $service->getAnswer($id);
|
||||||
|
|
||||||
|
$service = new QuestionService();
|
||||||
|
|
||||||
|
$question = $service->getQuestion($answer->question_id);
|
||||||
|
|
||||||
|
$this->seo->prependTitle('编辑回答');
|
||||||
|
|
||||||
|
$this->view->setVar('question', $question);
|
||||||
|
$this->view->setVar('answer', $answer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Get("/{id:[0-9]+}", name="home.answer.show")
|
||||||
|
*/
|
||||||
|
public function showAction($id)
|
||||||
|
{
|
||||||
|
$service = new AnswerInfoService();
|
||||||
|
|
||||||
|
$answer = $service->handle($id);
|
||||||
|
|
||||||
|
$this->view->setVar('answer', $answer);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Post("/create", name="home.answer.create")
|
||||||
|
*/
|
||||||
|
public function createAction()
|
||||||
|
{
|
||||||
|
$service = new AnswerCreateService();
|
||||||
|
|
||||||
|
$answer = $service->handle();
|
||||||
|
|
||||||
|
$location = $this->url->get([
|
||||||
|
'for' => 'home.question.show',
|
||||||
|
'id' => $answer->question_id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$content = [
|
||||||
|
'location' => $location,
|
||||||
|
'msg' => '创建回答成功',
|
||||||
|
];
|
||||||
|
|
||||||
|
return $this->jsonSuccess($content);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Post("/{id:[0-9]+}/update", name="home.answer.update")
|
||||||
|
*/
|
||||||
|
public function updateAction($id)
|
||||||
|
{
|
||||||
|
$service = new AnswerUpdateService();
|
||||||
|
|
||||||
|
$answer = $service->handle($id);
|
||||||
|
|
||||||
|
$location = $this->url->get([
|
||||||
|
'for' => 'home.question.show',
|
||||||
|
'id' => $answer->question_id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$content = [
|
||||||
|
'location' => $location,
|
||||||
|
'msg' => '更新回答成功',
|
||||||
|
];
|
||||||
|
|
||||||
|
return $this->jsonSuccess($content);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Post("/{id:[0-9]+}/delete", name="home.answer.delete")
|
||||||
|
*/
|
||||||
|
public function deleteAction($id)
|
||||||
|
{
|
||||||
|
$service = new AnswerDeleteService();
|
||||||
|
|
||||||
|
$answer = $service->handle($id);
|
||||||
|
|
||||||
|
$location = $this->url->get([
|
||||||
|
'for' => 'home.question.show',
|
||||||
|
'id' => $answer->question_id,
|
||||||
|
]);
|
||||||
|
|
||||||
|
$content = [
|
||||||
|
'location' => $location,
|
||||||
|
'msg' => '删除回答成功',
|
||||||
|
];
|
||||||
|
|
||||||
|
return $this->jsonSuccess($content);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Post("/{id:[0-9]+}/like", name="home.answer.like")
|
||||||
|
*/
|
||||||
|
public function likeAction($id)
|
||||||
|
{
|
||||||
|
$service = new AnswerLikeService();
|
||||||
|
|
||||||
|
$data = $service->handle($id);
|
||||||
|
|
||||||
|
$msg = $data['action'] == 'do' ? '点赞成功' : '取消点赞成功';
|
||||||
|
|
||||||
|
return $this->jsonSuccess(['data' => $data, 'msg' => $msg]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Post("/{id:[0-9]+}/accept", name="home.answer.accept")
|
||||||
|
*/
|
||||||
|
public function acceptAction($id)
|
||||||
|
{
|
||||||
|
$service = new AnswerAcceptService();
|
||||||
|
|
||||||
|
$data = $service->handle($id);
|
||||||
|
|
||||||
|
$msg = $data['action'] == 'do' ? '采纳成功' : '取消采纳成功';
|
||||||
|
|
||||||
|
return $this->jsonSuccess(['data' => $data, 'msg' => $msg]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Route("/{id:[0-9]+}/report", name="home.answer.report")
|
||||||
|
*/
|
||||||
|
public function reportAction($id)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -4,13 +4,15 @@ namespace App\Http\Home\Controllers;
|
|||||||
|
|
||||||
use App\Http\Home\Services\Article as ArticleService;
|
use App\Http\Home\Services\Article as ArticleService;
|
||||||
use App\Http\Home\Services\ArticleQuery as ArticleQueryService;
|
use App\Http\Home\Services\ArticleQuery as ArticleQueryService;
|
||||||
|
use App\Services\Logic\Article\ArticleCreate as ArticleCreateService;
|
||||||
|
use App\Services\Logic\Article\ArticleDelete as ArticleDeleteService;
|
||||||
use App\Services\Logic\Article\ArticleFavorite as ArticleFavoriteService;
|
use App\Services\Logic\Article\ArticleFavorite as ArticleFavoriteService;
|
||||||
use App\Services\Logic\Article\ArticleInfo as ArticleInfoService;
|
use App\Services\Logic\Article\ArticleInfo as ArticleInfoService;
|
||||||
use App\Services\Logic\Article\ArticleLike as ArticleLikeService;
|
use App\Services\Logic\Article\ArticleLike as ArticleLikeService;
|
||||||
use App\Services\Logic\Article\ArticleList as ArticleListService;
|
use App\Services\Logic\Article\ArticleList as ArticleListService;
|
||||||
use App\Services\Logic\Article\CommentList as ArticleCommentListService;
|
use App\Services\Logic\Article\ArticleUpdate as ArticleUpdateService;
|
||||||
use App\Services\Logic\Article\HotAuthorList as HotAuthorListService;
|
use App\Services\Logic\Article\HotAuthorList as HotAuthorListService;
|
||||||
use App\Services\Logic\Article\RelatedList as ArticleRelatedListService;
|
use App\Services\Logic\Article\RelatedArticleList as RelatedArticleListService;
|
||||||
use Phalcon\Mvc\View;
|
use Phalcon\Mvc\View;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -78,7 +80,7 @@ class ArticleController extends Controller
|
|||||||
$article = $service->getArticleModel();
|
$article = $service->getArticleModel();
|
||||||
$xmTags = $service->getXmTags(0);
|
$xmTags = $service->getXmTags(0);
|
||||||
|
|
||||||
$this->seo->prependTitle('撰写文章');
|
$this->seo->prependTitle('写文章');
|
||||||
|
|
||||||
$this->view->pick('article/edit');
|
$this->view->pick('article/edit');
|
||||||
$this->view->setVar('source_types', $sourceTypes);
|
$this->view->setVar('source_types', $sourceTypes);
|
||||||
@ -123,6 +125,7 @@ class ArticleController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
$this->seo->prependTitle($article['title']);
|
$this->seo->prependTitle($article['title']);
|
||||||
|
$this->seo->setDescription($article['summary']);
|
||||||
|
|
||||||
$this->view->setVar('article', $article);
|
$this->view->setVar('article', $article);
|
||||||
}
|
}
|
||||||
@ -132,7 +135,7 @@ class ArticleController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function relatedAction($id)
|
public function relatedAction($id)
|
||||||
{
|
{
|
||||||
$service = new ArticleRelatedListService();
|
$service = new RelatedArticleListService();
|
||||||
|
|
||||||
$articles = $service->handle($id);
|
$articles = $service->handle($id);
|
||||||
|
|
||||||
@ -140,27 +143,14 @@ class ArticleController extends Controller
|
|||||||
$this->view->setVar('articles', $articles);
|
$this->view->setVar('articles', $articles);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @Get("/{id:[0-9]+}/comments", name="home.article.comments")
|
|
||||||
*/
|
|
||||||
public function commentsAction($id)
|
|
||||||
{
|
|
||||||
$service = new ArticleCommentListService();
|
|
||||||
|
|
||||||
$comments = $service->handle($id);
|
|
||||||
|
|
||||||
$this->view->setRenderLevel(View::LEVEL_ACTION_VIEW);
|
|
||||||
$this->view->setVar('comments', $comments);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Post("/create", name="home.article.create")
|
* @Post("/create", name="home.article.create")
|
||||||
*/
|
*/
|
||||||
public function createAction()
|
public function createAction()
|
||||||
{
|
{
|
||||||
$service = new ArticleService();
|
$service = new ArticleCreateService();
|
||||||
|
|
||||||
$service->createArticle();
|
$service->handle();
|
||||||
|
|
||||||
$location = $this->url->get(['for' => 'home.uc.articles']);
|
$location = $this->url->get(['for' => 'home.uc.articles']);
|
||||||
|
|
||||||
@ -177,9 +167,9 @@ class ArticleController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function updateAction($id)
|
public function updateAction($id)
|
||||||
{
|
{
|
||||||
$service = new ArticleService();
|
$service = new ArticleUpdateService();
|
||||||
|
|
||||||
$service->updateArticle($id);
|
$service->handle($id);
|
||||||
|
|
||||||
$location = $this->url->get(['for' => 'home.uc.articles']);
|
$location = $this->url->get(['for' => 'home.uc.articles']);
|
||||||
|
|
||||||
@ -196,12 +186,14 @@ class ArticleController extends Controller
|
|||||||
*/
|
*/
|
||||||
public function deleteAction($id)
|
public function deleteAction($id)
|
||||||
{
|
{
|
||||||
$service = new ArticleService();
|
$service = new ArticleDeleteService();
|
||||||
|
|
||||||
$service->deleteArticle($id);
|
$service->handle($id);
|
||||||
|
|
||||||
|
$location = $this->url->get(['for' => 'home.uc.articles']);
|
||||||
|
|
||||||
$content = [
|
$content = [
|
||||||
'location' => $this->request->getHTTPReferer(),
|
'location' => $location,
|
||||||
'msg' => '删除文章成功',
|
'msg' => '删除文章成功',
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -236,5 +228,12 @@ class ArticleController extends Controller
|
|||||||
return $this->jsonSuccess(['data' => $data, 'msg' => $msg]);
|
return $this->jsonSuccess(['data' => $data, 'msg' => $msg]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Route("/{id:[0-9]+}/report", name="home.article.report")
|
||||||
|
*/
|
||||||
|
public function reportAction($id)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,6 @@ use App\Models\ChapterLive as LiveModel;
|
|||||||
use App\Models\Course as CourseModel;
|
use App\Models\Course as CourseModel;
|
||||||
use App\Services\Logic\Chapter\ChapterInfo as ChapterInfoService;
|
use App\Services\Logic\Chapter\ChapterInfo as ChapterInfoService;
|
||||||
use App\Services\Logic\Chapter\ChapterLike as ChapterLikeService;
|
use App\Services\Logic\Chapter\ChapterLike as ChapterLikeService;
|
||||||
use App\Services\Logic\Chapter\DanmuList as ChapterDanmuListService;
|
|
||||||
use App\Services\Logic\Chapter\Learning as ChapterLearningService;
|
use App\Services\Logic\Chapter\Learning as ChapterLearningService;
|
||||||
use App\Services\Logic\Chapter\ResourceList as ChapterResourceListService;
|
use App\Services\Logic\Chapter\ResourceList as ChapterResourceListService;
|
||||||
use App\Services\Logic\Course\BasicInfo as CourseInfoService;
|
use App\Services\Logic\Course\BasicInfo as CourseInfoService;
|
||||||
@ -30,18 +29,6 @@ class ChapterController extends Controller
|
|||||||
$this->view->setVar('items', $items);
|
$this->view->setVar('items', $items);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @Get("/{id:[0-9]+}/danmus", name="home.chapter.danmus")
|
|
||||||
*/
|
|
||||||
public function danmusAction($id)
|
|
||||||
{
|
|
||||||
$service = new ChapterDanmuListService();
|
|
||||||
|
|
||||||
$items = $service->handle($id);
|
|
||||||
|
|
||||||
return $this->jsonSuccess(['items' => $items]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Get("/{id:[0-9]+}", name="home.chapter.show")
|
* @Get("/{id:[0-9]+}", name="home.chapter.show")
|
||||||
*/
|
*/
|
||||||
|
@ -63,26 +63,6 @@ class CommentController extends Controller
|
|||||||
$this->view->setVar('comment', $comment);
|
$this->view->setVar('comment', $comment);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @Get("/add", name="home.comment.add")
|
|
||||||
*/
|
|
||||||
public function addAction()
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @Get("/{id:[0-9]+}/reply", name="home.comment.reply")
|
|
||||||
*/
|
|
||||||
public function replyAction($id)
|
|
||||||
{
|
|
||||||
$service = new CommentInfoService();
|
|
||||||
|
|
||||||
$comment = $service->handle($id);
|
|
||||||
|
|
||||||
$this->view->setVar('comment', $comment);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Post("/create", name="home.comment.create")
|
* @Post("/create", name="home.comment.create")
|
||||||
*/
|
*/
|
||||||
@ -100,9 +80,9 @@ class CommentController extends Controller
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Post("/{id:[0-9]+}/reply", name="home.comment.create_reply")
|
* @Post("/{id:[0-9]+}/reply", name="home.comment.reply")
|
||||||
*/
|
*/
|
||||||
public function createReplyAction($id)
|
public function replyAction($id)
|
||||||
{
|
{
|
||||||
$service = new CommentReplyService();
|
$service = new CommentReplyService();
|
||||||
|
|
||||||
@ -141,4 +121,12 @@ class CommentController extends Controller
|
|||||||
return $this->jsonSuccess(['msg' => '删除评论成功']);
|
return $this->jsonSuccess(['msg' => '删除评论成功']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Route("/{id:[0-9]+}/report", name="home.comment.report")
|
||||||
|
*/
|
||||||
|
public function reportAction($id)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
261
app/Http/Home/Controllers/QuestionController.php
Normal file
261
app/Http/Home/Controllers/QuestionController.php
Normal file
@ -0,0 +1,261 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Home\Controllers;
|
||||||
|
|
||||||
|
use App\Http\Home\Services\Question as QuestionService;
|
||||||
|
use App\Http\Home\Services\QuestionQuery as QuestionQueryService;
|
||||||
|
use App\Services\Logic\Question\AnswerList as AnswerListService;
|
||||||
|
use App\Services\Logic\Question\HotQuestionList as HotQuestionListService;
|
||||||
|
use App\Services\Logic\Question\QuestionCreate as QuestionCreateService;
|
||||||
|
use App\Services\Logic\Question\QuestionDelete as QuestionDeleteService;
|
||||||
|
use App\Services\Logic\Question\QuestionFavorite as QuestionFavoriteService;
|
||||||
|
use App\Services\Logic\Question\QuestionInfo as QuestionInfoService;
|
||||||
|
use App\Services\Logic\Question\QuestionLike as QuestionLikeService;
|
||||||
|
use App\Services\Logic\Question\QuestionList as QuestionListService;
|
||||||
|
use App\Services\Logic\Question\QuestionUpdate as QuestionUpdateService;
|
||||||
|
use App\Services\Logic\Question\RelatedQuestionList as RelatedQuestionListService;
|
||||||
|
use App\Services\Logic\Question\TopAnswererList as TopAnswererListService;
|
||||||
|
use Phalcon\Mvc\View;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @RoutePrefix("/question")
|
||||||
|
*/
|
||||||
|
class QuestionController extends Controller
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Get("/hot/questions", name="home.question.hot_questions")
|
||||||
|
*/
|
||||||
|
public function hotQuestionsAction()
|
||||||
|
{
|
||||||
|
$service = new HotQuestionListService();
|
||||||
|
|
||||||
|
$questions = $service->handle();
|
||||||
|
|
||||||
|
$this->view->setRenderLevel(View::LEVEL_ACTION_VIEW);
|
||||||
|
$this->view->pick('question/hot_questions');
|
||||||
|
$this->view->setVar('questions', $questions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Get("/top/answerers", name="home.question.top_answerers")
|
||||||
|
*/
|
||||||
|
public function topAnswerersAction()
|
||||||
|
{
|
||||||
|
$service = new TopAnswererListService();
|
||||||
|
|
||||||
|
$answerers = $service->handle();
|
||||||
|
|
||||||
|
$this->view->setRenderLevel(View::LEVEL_ACTION_VIEW);
|
||||||
|
$this->view->pick('question/top_answerers');
|
||||||
|
$this->view->setVar('answerers', $answerers);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Get("/list", name="home.question.list")
|
||||||
|
*/
|
||||||
|
public function listAction()
|
||||||
|
{
|
||||||
|
$service = new QuestionQueryService();
|
||||||
|
|
||||||
|
$sorts = $service->handleSorts();
|
||||||
|
$params = $service->getParams();
|
||||||
|
|
||||||
|
$this->seo->prependTitle('问答');
|
||||||
|
|
||||||
|
$this->view->setVar('sorts', $sorts);
|
||||||
|
$this->view->setVar('params', $params);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Get("/pager", name="home.question.pager")
|
||||||
|
*/
|
||||||
|
public function pagerAction()
|
||||||
|
{
|
||||||
|
$service = new QuestionListService();
|
||||||
|
|
||||||
|
$pager = $service->handle();
|
||||||
|
|
||||||
|
$pager->target = 'question-list';
|
||||||
|
|
||||||
|
$this->view->setRenderLevel(View::LEVEL_ACTION_VIEW);
|
||||||
|
$this->view->setVar('pager', $pager);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Get("/add", name="home.question.add")
|
||||||
|
*/
|
||||||
|
public function addAction()
|
||||||
|
{
|
||||||
|
$service = new QuestionService();
|
||||||
|
|
||||||
|
$question = $service->getQuestionModel();
|
||||||
|
|
||||||
|
$xmTags = $service->getXmTags(0);
|
||||||
|
|
||||||
|
$this->seo->prependTitle('提问题');
|
||||||
|
|
||||||
|
$this->view->pick('question/edit');
|
||||||
|
$this->view->setVar('question', $question);
|
||||||
|
$this->view->setVar('xm_tags', $xmTags);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Get("/{id:[0-9]+}/edit", name="home.question.edit")
|
||||||
|
*/
|
||||||
|
public function editAction($id)
|
||||||
|
{
|
||||||
|
$service = new QuestionService();
|
||||||
|
|
||||||
|
$question = $service->getQuestion($id);
|
||||||
|
|
||||||
|
$xmTags = $service->getXmTags($id);
|
||||||
|
|
||||||
|
$this->seo->prependTitle('编辑问题');
|
||||||
|
|
||||||
|
$this->view->setVar('question', $question);
|
||||||
|
$this->view->setVar('xm_tags', $xmTags);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Get("/{id:[0-9]+}", name="home.question.show")
|
||||||
|
*/
|
||||||
|
public function showAction($id)
|
||||||
|
{
|
||||||
|
$service = new QuestionInfoService();
|
||||||
|
|
||||||
|
$question = $service->handle($id);
|
||||||
|
|
||||||
|
$this->seo->prependTitle($question['title']);
|
||||||
|
$this->seo->setDescription($question['summary']);
|
||||||
|
|
||||||
|
$this->view->setVar('question', $question);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Get("/{id:[0-9]+}/answers", name="home.question.answers")
|
||||||
|
*/
|
||||||
|
public function answersAction($id)
|
||||||
|
{
|
||||||
|
$service = new QuestionService();
|
||||||
|
|
||||||
|
$question = $service->getQuestion($id);
|
||||||
|
|
||||||
|
$service = new AnswerListService();
|
||||||
|
|
||||||
|
$pager = $service->handle($id);
|
||||||
|
|
||||||
|
$pager->target = 'answer-list';
|
||||||
|
|
||||||
|
$this->view->setRenderLevel(View::LEVEL_ACTION_VIEW);
|
||||||
|
$this->view->setVar('question', $question);
|
||||||
|
$this->view->setVar('pager', $pager);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Get("/{id:[0-9]+}/related", name="home.question.related")
|
||||||
|
*/
|
||||||
|
public function relatedAction($id)
|
||||||
|
{
|
||||||
|
$service = new RelatedQuestionListService();
|
||||||
|
|
||||||
|
$questions = $service->handle($id);
|
||||||
|
|
||||||
|
$this->view->setRenderLevel(View::LEVEL_ACTION_VIEW);
|
||||||
|
$this->view->setVar('questions', $questions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Post("/create", name="home.question.create")
|
||||||
|
*/
|
||||||
|
public function createAction()
|
||||||
|
{
|
||||||
|
$service = new QuestionCreateService();
|
||||||
|
|
||||||
|
$service->handle();
|
||||||
|
|
||||||
|
$location = $this->url->get(['for' => 'home.uc.questions']);
|
||||||
|
|
||||||
|
$content = [
|
||||||
|
'location' => $location,
|
||||||
|
'msg' => '创建问题成功',
|
||||||
|
];
|
||||||
|
|
||||||
|
return $this->jsonSuccess($content);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Post("/{id:[0-9]+}/update", name="home.question.update")
|
||||||
|
*/
|
||||||
|
public function updateAction($id)
|
||||||
|
{
|
||||||
|
$service = new QuestionUpdateService();
|
||||||
|
|
||||||
|
$service->handle($id);
|
||||||
|
|
||||||
|
$location = $this->url->get(['for' => 'home.uc.questions']);
|
||||||
|
|
||||||
|
$content = [
|
||||||
|
'location' => $location,
|
||||||
|
'msg' => '更新问题成功',
|
||||||
|
];
|
||||||
|
|
||||||
|
return $this->jsonSuccess($content);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Post("/{id:[0-9]+}/delete", name="home.question.delete")
|
||||||
|
*/
|
||||||
|
public function deleteAction($id)
|
||||||
|
{
|
||||||
|
$service = new QuestionDeleteService();
|
||||||
|
|
||||||
|
$service->handle($id);
|
||||||
|
|
||||||
|
$location = $this->url->get(['for' => 'home.uc.questions']);
|
||||||
|
|
||||||
|
$content = [
|
||||||
|
'location' => $location,
|
||||||
|
'msg' => '删除问题成功',
|
||||||
|
];
|
||||||
|
|
||||||
|
return $this->jsonSuccess($content);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Post("/{id:[0-9]+}/favorite", name="home.question.favorite")
|
||||||
|
*/
|
||||||
|
public function favoriteAction($id)
|
||||||
|
{
|
||||||
|
$service = new QuestionFavoriteService();
|
||||||
|
|
||||||
|
$data = $service->handle($id);
|
||||||
|
|
||||||
|
$msg = $data['action'] == 'do' ? '收藏成功' : '取消收藏成功';
|
||||||
|
|
||||||
|
return $this->jsonSuccess(['data' => $data, 'msg' => $msg]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Post("/{id:[0-9]+}/like", name="home.question.like")
|
||||||
|
*/
|
||||||
|
public function likeAction($id)
|
||||||
|
{
|
||||||
|
$service = new QuestionLikeService();
|
||||||
|
|
||||||
|
$data = $service->handle($id);
|
||||||
|
|
||||||
|
$msg = $data['action'] == 'do' ? '点赞成功' : '取消点赞成功';
|
||||||
|
|
||||||
|
return $this->jsonSuccess(['data' => $data, 'msg' => $msg]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Route("/{id:[0-9]+}/report", name="home.question.report")
|
||||||
|
*/
|
||||||
|
public function reportAction($id)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -5,6 +5,7 @@ namespace App\Http\Home\Controllers;
|
|||||||
use App\Services\Logic\Search\Article as ArticleSearchService;
|
use App\Services\Logic\Search\Article as ArticleSearchService;
|
||||||
use App\Services\Logic\Search\Course as CourseSearchService;
|
use App\Services\Logic\Search\Course as CourseSearchService;
|
||||||
use App\Services\Logic\Search\Group as GroupSearchService;
|
use App\Services\Logic\Search\Group as GroupSearchService;
|
||||||
|
use App\Services\Logic\Search\Question as QuestionSearchService;
|
||||||
use App\Services\Logic\Search\User as UserSearchService;
|
use App\Services\Logic\Search\User as UserSearchService;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -42,7 +43,7 @@ class SearchController extends Controller
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* @param string $type
|
* @param string $type
|
||||||
* @return ArticleSearchService|CourseSearchService|GroupSearchService|UserSearchService
|
* @return ArticleSearchService|QuestionSearchService|CourseSearchService|GroupSearchService|UserSearchService
|
||||||
*/
|
*/
|
||||||
protected function getSearchService($type)
|
protected function getSearchService($type)
|
||||||
{
|
{
|
||||||
@ -50,6 +51,9 @@ class SearchController extends Controller
|
|||||||
case 'article':
|
case 'article':
|
||||||
$service = new ArticleSearchService();
|
$service = new ArticleSearchService();
|
||||||
break;
|
break;
|
||||||
|
case 'question':
|
||||||
|
$service = new QuestionSearchService();
|
||||||
|
break;
|
||||||
case 'group':
|
case 'group':
|
||||||
$service = new GroupSearchService();
|
$service = new GroupSearchService();
|
||||||
break;
|
break;
|
||||||
|
70
app/Http/Home/Controllers/TagController.php
Normal file
70
app/Http/Home/Controllers/TagController.php
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Home\Controllers;
|
||||||
|
|
||||||
|
use App\Services\Logic\Tag\TagFollow as TagFollowService;
|
||||||
|
use App\Services\Logic\Tag\TagList as TagListService;
|
||||||
|
use App\Services\Logic\Tag\FollowList as FollowListService;
|
||||||
|
use Phalcon\Mvc\View;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @RoutePrefix("/tag")
|
||||||
|
*/
|
||||||
|
class TagController extends Controller
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Get("/list", name="home.tag.list")
|
||||||
|
*/
|
||||||
|
public function listAction()
|
||||||
|
{
|
||||||
|
$this->seo->prependTitle('标签');
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Get("/list/pager", name="home.tag.list_pager")
|
||||||
|
*/
|
||||||
|
public function listPagerAction()
|
||||||
|
{
|
||||||
|
$service = new TagListService();
|
||||||
|
|
||||||
|
$pager = $service->handle();
|
||||||
|
|
||||||
|
$pager->target = 'all-tag-list';
|
||||||
|
|
||||||
|
$this->view->setRenderLevel(View::LEVEL_ACTION_VIEW);
|
||||||
|
$this->view->pick('tag/list_pager');
|
||||||
|
$this->view->setVar('pager', $pager);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Get("/my/pager", name="home.tag.my_pager")
|
||||||
|
*/
|
||||||
|
public function myPagerAction()
|
||||||
|
{
|
||||||
|
$service = new FollowListService();
|
||||||
|
|
||||||
|
$pager = $service->handle();
|
||||||
|
|
||||||
|
$pager->target = 'my-tag-list';
|
||||||
|
|
||||||
|
$this->view->setRenderLevel(View::LEVEL_ACTION_VIEW);
|
||||||
|
$this->view->pick('tag/my_pager');
|
||||||
|
$this->view->setVar('pager', $pager);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Post("/{id:[0-9]+}/follow", name="home.tag.follow")
|
||||||
|
*/
|
||||||
|
public function followAction($id)
|
||||||
|
{
|
||||||
|
$service = new TagFollowService();
|
||||||
|
|
||||||
|
$data = $service->handle($id);
|
||||||
|
|
||||||
|
$msg = $data['action'] == 'do' ? '关注成功' : '取消关注成功';
|
||||||
|
|
||||||
|
return $this->jsonSuccess(['data' => $data, 'msg' => $msg]);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -5,6 +5,7 @@ namespace App\Http\Home\Controllers;
|
|||||||
use App\Repos\WeChatSubscribe as WeChatSubscribeRepo;
|
use App\Repos\WeChatSubscribe as WeChatSubscribeRepo;
|
||||||
use App\Services\Logic\Account\OAuthProvider as OAuthProviderService;
|
use App\Services\Logic\Account\OAuthProvider as OAuthProviderService;
|
||||||
use App\Services\Logic\User\Console\AccountInfo as AccountInfoService;
|
use App\Services\Logic\User\Console\AccountInfo as AccountInfoService;
|
||||||
|
use App\Services\Logic\User\Console\AnswerList as AnswerListService;
|
||||||
use App\Services\Logic\User\Console\ArticleList as ArticleListService;
|
use App\Services\Logic\User\Console\ArticleList as ArticleListService;
|
||||||
use App\Services\Logic\User\Console\ConnectDelete as ConnectDeleteService;
|
use App\Services\Logic\User\Console\ConnectDelete as ConnectDeleteService;
|
||||||
use App\Services\Logic\User\Console\ConnectList as ConnectListService;
|
use App\Services\Logic\User\Console\ConnectList as ConnectListService;
|
||||||
@ -24,6 +25,7 @@ use App\Services\Logic\User\Console\PointHistory as PointHistoryService;
|
|||||||
use App\Services\Logic\User\Console\PointRedeemList as PointRedeemListService;
|
use App\Services\Logic\User\Console\PointRedeemList as PointRedeemListService;
|
||||||
use App\Services\Logic\User\Console\ProfileInfo as ProfileInfoService;
|
use App\Services\Logic\User\Console\ProfileInfo as ProfileInfoService;
|
||||||
use App\Services\Logic\User\Console\ProfileUpdate as ProfileUpdateService;
|
use App\Services\Logic\User\Console\ProfileUpdate as ProfileUpdateService;
|
||||||
|
use App\Services\Logic\User\Console\QuestionList as QuestionListService;
|
||||||
use App\Services\Logic\User\Console\RefundList as RefundListService;
|
use App\Services\Logic\User\Console\RefundList as RefundListService;
|
||||||
use App\Services\Logic\User\Console\ReviewList as ReviewListService;
|
use App\Services\Logic\User\Console\ReviewList as ReviewListService;
|
||||||
use Phalcon\Mvc\Dispatcher;
|
use Phalcon\Mvc\Dispatcher;
|
||||||
@ -154,6 +156,32 @@ class UserConsoleController extends Controller
|
|||||||
$this->view->setVar('pager', $pager);
|
$this->view->setVar('pager', $pager);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Get("/questions", name="home.uc.questions")
|
||||||
|
*/
|
||||||
|
public function questionsAction()
|
||||||
|
{
|
||||||
|
$service = new QuestionListService();
|
||||||
|
|
||||||
|
$pager = $service->handle();
|
||||||
|
|
||||||
|
$this->view->pick('user/console/questions');
|
||||||
|
$this->view->setVar('pager', $pager);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Get("/answers", name="home.uc.answers")
|
||||||
|
*/
|
||||||
|
public function answersAction()
|
||||||
|
{
|
||||||
|
$service = new AnswerListService();
|
||||||
|
|
||||||
|
$pager = $service->handle();
|
||||||
|
|
||||||
|
$this->view->pick('user/console/answers');
|
||||||
|
$this->view->setVar('pager', $pager);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @Get("/favorites", name="home.uc.favorites")
|
* @Get("/favorites", name="home.uc.favorites")
|
||||||
*/
|
*/
|
||||||
|
58
app/Http/Home/Controllers/WidgetController.php
Normal file
58
app/Http/Home/Controllers/WidgetController.php
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Home\Controllers;
|
||||||
|
|
||||||
|
use App\Services\Logic\Question\HotQuestionList as HotQuestionListService;
|
||||||
|
use App\Services\Logic\Question\TopAnswererList as TopAnswererListService;
|
||||||
|
use App\Services\Logic\Tag\FollowList as FollowListService;
|
||||||
|
use Phalcon\Mvc\View;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @RoutePrefix("/widget")
|
||||||
|
*/
|
||||||
|
class WidgetController extends Controller
|
||||||
|
{
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Get("/my/tags", name="home.widget.my_tags")
|
||||||
|
*/
|
||||||
|
public function myTagsAction()
|
||||||
|
{
|
||||||
|
$service = new FollowListService();
|
||||||
|
|
||||||
|
$pager = $service->handle();
|
||||||
|
|
||||||
|
$this->view->setRenderLevel(View::LEVEL_ACTION_VIEW);
|
||||||
|
$this->view->pick('widget/my_tags');
|
||||||
|
$this->view->setVar('tags', $pager->items);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Get("/hot/questions", name="home.widget.hot_questions")
|
||||||
|
*/
|
||||||
|
public function hotQuestionsAction()
|
||||||
|
{
|
||||||
|
$service = new HotQuestionListService();
|
||||||
|
|
||||||
|
$questions = $service->handle();
|
||||||
|
|
||||||
|
$this->view->setRenderLevel(View::LEVEL_ACTION_VIEW);
|
||||||
|
$this->view->pick('widget/hot_questions');
|
||||||
|
$this->view->setVar('questions', $questions);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @Get("/top/answerers", name="home.widget.top_answerers")
|
||||||
|
*/
|
||||||
|
public function topAnswerersAction()
|
||||||
|
{
|
||||||
|
$service = new TopAnswererListService();
|
||||||
|
|
||||||
|
$answerers = $service->handle();
|
||||||
|
|
||||||
|
$this->view->setRenderLevel(View::LEVEL_ACTION_VIEW);
|
||||||
|
$this->view->pick('widget/top_answerers');
|
||||||
|
$this->view->setVar('answerers', $answerers);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
24
app/Http/Home/Services/Answer.php
Normal file
24
app/Http/Home/Services/Answer.php
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Home\Services;
|
||||||
|
|
||||||
|
use App\Models\Answer as AnswerModel;
|
||||||
|
use App\Services\Logic\AnswerTrait;
|
||||||
|
use App\Services\Logic\Service as LogicService;
|
||||||
|
|
||||||
|
class Answer extends LogicService
|
||||||
|
{
|
||||||
|
|
||||||
|
use AnswerTrait;
|
||||||
|
|
||||||
|
public function getAnswerModel()
|
||||||
|
{
|
||||||
|
return new AnswerModel();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getAnswer($id)
|
||||||
|
{
|
||||||
|
return $this->checkAnswer($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -2,143 +2,76 @@
|
|||||||
|
|
||||||
namespace App\Http\Home\Services;
|
namespace App\Http\Home\Services;
|
||||||
|
|
||||||
use App\Http\Admin\Services\Article as ArticleService;
|
|
||||||
use App\Library\Utils\Word as WordUtil;
|
|
||||||
use App\Models\Article as ArticleModel;
|
use App\Models\Article as ArticleModel;
|
||||||
use App\Traits\Client as ClientTrait;
|
use App\Models\Category as CategoryModel;
|
||||||
use App\Validators\Article as ArticleValidator;
|
use App\Repos\Category as CategoryRepo;
|
||||||
|
use App\Repos\Tag as TagRepo;
|
||||||
|
use App\Services\Logic\ArticleTrait;
|
||||||
|
|
||||||
class Article extends ArticleService
|
class Article extends Service
|
||||||
{
|
{
|
||||||
|
|
||||||
use ClientTrait;
|
use ArticleTrait;
|
||||||
|
|
||||||
public function createArticle()
|
public function getArticleModel()
|
||||||
{
|
{
|
||||||
$post = $this->request->getPost();
|
|
||||||
|
|
||||||
$user = $this->getLoginUser();
|
|
||||||
|
|
||||||
$article = new ArticleModel();
|
$article = new ArticleModel();
|
||||||
|
|
||||||
$data = $this->handlePostData($post);
|
$article->afterFetch();
|
||||||
|
|
||||||
$data['client_type'] = $this->getClientType();
|
|
||||||
$data['client_ip'] = $this->getClientIp();
|
|
||||||
$data['owner_id'] = $user->id;
|
|
||||||
|
|
||||||
$article->create($data);
|
|
||||||
|
|
||||||
if (isset($post['xm_tag_ids'])) {
|
|
||||||
$this->saveTags($article, $post['xm_tag_ids']);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->incrUserArticleCount($user);
|
|
||||||
|
|
||||||
$this->eventsManager->fire('Article:afterCreate', $this, $article);
|
|
||||||
|
|
||||||
return $article;
|
return $article;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function updateArticle($id)
|
public function getXmTags($id)
|
||||||
{
|
{
|
||||||
$post = $this->request->getPost();
|
$tagRepo = new TagRepo();
|
||||||
|
|
||||||
$article = $this->findOrFail($id);
|
$allTags = $tagRepo->findAll(['published' => 1], 'priority');
|
||||||
|
|
||||||
$data = $this->handlePostData($post);
|
if ($allTags->count() == 0) return [];
|
||||||
|
|
||||||
$data['client_type'] = $this->getClientType();
|
$articleTagIds = [];
|
||||||
$data['client_ip'] = $this->getClientIp();
|
|
||||||
|
|
||||||
if ($article->published == ArticleModel::PUBLISH_REJECTED) {
|
if ($id > 0) {
|
||||||
$data['published'] = ArticleModel::PUBLISH_PENDING;
|
$article = $this->checkArticle($id);
|
||||||
}
|
if (!empty($article->tags)) {
|
||||||
|
$articleTagIds = kg_array_column($article->tags, 'id');
|
||||||
/**
|
|
||||||
* 当通过审核后,禁止修改部分文章属性
|
|
||||||
*/
|
|
||||||
if ($article->published == ArticleModel::PUBLISH_APPROVED) {
|
|
||||||
unset(
|
|
||||||
$data['title'],
|
|
||||||
$data['content'],
|
|
||||||
$data['cover'],
|
|
||||||
$data['source_type'],
|
|
||||||
$data['source_url'],
|
|
||||||
$data['category_id'],
|
|
||||||
$post['xm_tag_ids'],
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
$article->update($data);
|
|
||||||
|
|
||||||
if (isset($post['xm_tag_ids'])) {
|
|
||||||
$this->saveTags($article, $post['xm_tag_ids']);
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->eventsManager->fire('Article:afterUpdate', $this, $article);
|
|
||||||
|
|
||||||
return $article;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function deleteArticle($id)
|
|
||||||
{
|
|
||||||
$article = $this->findOrFail($id);
|
|
||||||
|
|
||||||
$user = $this->getLoginUser();
|
|
||||||
|
|
||||||
$validator = new ArticleValidator();
|
|
||||||
|
|
||||||
$validator->checkOwner($user->id, $article->owner_id);
|
|
||||||
|
|
||||||
$article->deleted = 1;
|
|
||||||
|
|
||||||
$article->update();
|
|
||||||
|
|
||||||
$this->decrUserArticleCount($user);
|
|
||||||
|
|
||||||
$this->rebuildArticleIndex($article);
|
|
||||||
|
|
||||||
$this->eventsManager->fire('Article:afterDelete', $this, $article);
|
|
||||||
|
|
||||||
return $article;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function handlePostData($post)
|
|
||||||
{
|
|
||||||
$data = [];
|
|
||||||
|
|
||||||
$validator = new ArticleValidator();
|
|
||||||
|
|
||||||
$data['title'] = $validator->checkTitle($post['title']);
|
|
||||||
$data['content'] = $validator->checkContent($post['content']);
|
|
||||||
$data['word_count'] = WordUtil::getWordCount($data['content']);
|
|
||||||
|
|
||||||
if (isset($post['category_id'])) {
|
|
||||||
$category = $validator->checkCategory($post['category_id']);
|
|
||||||
$data['category_id'] = $category->id;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($post['cover'])) {
|
|
||||||
$data['cover'] = $validator->checkCover($post['cover']);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isset($post['source_type'])) {
|
|
||||||
$data['source_type'] = $validator->checkSourceType($post['source_type']);
|
|
||||||
if ($post['source_type'] != ArticleModel::SOURCE_ORIGIN) {
|
|
||||||
$data['source_url'] = $validator->checkSourceUrl($post['source_url']);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($post['allow_comment'])) {
|
$list = [];
|
||||||
$data['allow_comment'] = $validator->checkAllowCommentStatus($post['allow_comment']);
|
|
||||||
|
foreach ($allTags as $tag) {
|
||||||
|
$selected = in_array($tag->id, $articleTagIds);
|
||||||
|
$list[] = [
|
||||||
|
'name' => $tag->name,
|
||||||
|
'value' => $tag->id,
|
||||||
|
'selected' => $selected,
|
||||||
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isset($post['private'])) {
|
return $list;
|
||||||
$data['private'] = $validator->checkPrivateStatus($post['private']);
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return $data;
|
public function getCategories()
|
||||||
|
{
|
||||||
|
$categoryRepo = new CategoryRepo();
|
||||||
|
|
||||||
|
return $categoryRepo->findAll([
|
||||||
|
'type' => CategoryModel::TYPE_ARTICLE,
|
||||||
|
'level' => 1,
|
||||||
|
'published' => 1,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSourceTypes()
|
||||||
|
{
|
||||||
|
return ArticleModel::sourceTypes();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getArticle($id)
|
||||||
|
{
|
||||||
|
return $this->checkArticle($id);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
73
app/Http/Home/Services/Question.php
Normal file
73
app/Http/Home/Services/Question.php
Normal file
@ -0,0 +1,73 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Home\Services;
|
||||||
|
|
||||||
|
use App\Models\Category as CategoryModel;
|
||||||
|
use App\Models\Question as QuestionModel;
|
||||||
|
use App\Repos\Category as CategoryRepo;
|
||||||
|
use App\Repos\Tag as TagRepo;
|
||||||
|
use App\Services\Logic\QuestionTrait;
|
||||||
|
use App\Services\Logic\Service as LogicService;
|
||||||
|
|
||||||
|
class Question extends LogicService
|
||||||
|
{
|
||||||
|
|
||||||
|
use QuestionTrait;
|
||||||
|
|
||||||
|
public function getQuestionModel()
|
||||||
|
{
|
||||||
|
$question = new QuestionModel();
|
||||||
|
|
||||||
|
$question->afterFetch();
|
||||||
|
|
||||||
|
return $question;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCategories()
|
||||||
|
{
|
||||||
|
$categoryRepo = new CategoryRepo();
|
||||||
|
|
||||||
|
return $categoryRepo->findAll([
|
||||||
|
'type' => CategoryModel::TYPE_ARTICLE,
|
||||||
|
'level' => 1,
|
||||||
|
'published' => 1,
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getXmTags($id)
|
||||||
|
{
|
||||||
|
$tagRepo = new TagRepo();
|
||||||
|
|
||||||
|
$allTags = $tagRepo->findAll(['published' => 1], 'priority');
|
||||||
|
|
||||||
|
if ($allTags->count() == 0) return [];
|
||||||
|
|
||||||
|
$questionTagIds = [];
|
||||||
|
|
||||||
|
if ($id > 0) {
|
||||||
|
$question = $this->checkQuestion($id);
|
||||||
|
if (!empty($question->tags)) {
|
||||||
|
$questionTagIds = kg_array_column($question->tags, 'id');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$list = [];
|
||||||
|
|
||||||
|
foreach ($allTags as $tag) {
|
||||||
|
$selected = in_array($tag->id, $questionTagIds);
|
||||||
|
$list[] = [
|
||||||
|
'name' => $tag->name,
|
||||||
|
'value' => $tag->id,
|
||||||
|
'selected' => $selected,
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $list;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getQuestion($id)
|
||||||
|
{
|
||||||
|
return $this->checkQuestion($id);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
64
app/Http/Home/Services/QuestionQuery.php
Normal file
64
app/Http/Home/Services/QuestionQuery.php
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
namespace App\Http\Home\Services;
|
||||||
|
|
||||||
|
use App\Models\Question as QuestionModel;
|
||||||
|
use App\Validators\QuestionQuery as QuestionQueryValidator;
|
||||||
|
|
||||||
|
class QuestionQuery extends Service
|
||||||
|
{
|
||||||
|
|
||||||
|
protected $baseUrl;
|
||||||
|
|
||||||
|
public function __construct()
|
||||||
|
{
|
||||||
|
$this->baseUrl = $this->url->get(['for' => 'home.question.list']);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function handleSorts()
|
||||||
|
{
|
||||||
|
$params = $this->getParams();
|
||||||
|
|
||||||
|
$result = [];
|
||||||
|
|
||||||
|
$sorts = QuestionModel::sortTypes();
|
||||||
|
|
||||||
|
foreach ($sorts as $key => $value) {
|
||||||
|
$params['sort'] = $key;
|
||||||
|
$result[] = [
|
||||||
|
'id' => $key,
|
||||||
|
'name' => $value,
|
||||||
|
'url' => $this->baseUrl . $this->buildParams($params),
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getParams()
|
||||||
|
{
|
||||||
|
$query = $this->request->getQuery();
|
||||||
|
|
||||||
|
$params = [];
|
||||||
|
|
||||||
|
$validator = new QuestionQueryValidator();
|
||||||
|
|
||||||
|
if (isset($query['tag_id'])) {
|
||||||
|
$validator->checkTag($query['tag_id']);
|
||||||
|
$params['tag_id'] = $query['tag_id'];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isset($query['sort'])) {
|
||||||
|
$validator->checkSort($query['sort']);
|
||||||
|
$params['sort'] = $query['sort'];
|
||||||
|
}
|
||||||
|
|
||||||
|
return $params;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function buildParams($params)
|
||||||
|
{
|
||||||
|
return $params ? '?' . http_build_query($params) : '';
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
76
app/Http/Home/Views/answer/add.volt
Normal file
76
app/Http/Home/Views/answer/add.volt
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
{% extends 'templates/main.volt' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div class="breadcrumb">
|
||||||
|
<span class="layui-breadcrumb">
|
||||||
|
<a href="/">首页</a>
|
||||||
|
<a><cite>回答问题</cite></a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="layout-main">
|
||||||
|
<div class="writer-content wrap">
|
||||||
|
<form class="layui-form" method="POST" action="{{ url({'for':'home.answer.create'}) }}">
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<div class="layui-input-block" style="margin:0;">
|
||||||
|
<input class="layui-input" type="text" name="title" value="{{ question.title }}" readonly="readonly">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<div class="layui-input-block" style="margin:0;">
|
||||||
|
<div id="vditor"></div>
|
||||||
|
<textarea name="content" class="layui-hide" id="vditor-textarea"></textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item center">
|
||||||
|
<div class="layui-input-block" style="margin:0;">
|
||||||
|
<button class="layui-btn kg-submit" lay-submit="true" lay-filter="go">发布回答</button>
|
||||||
|
<input type="hidden" name="question_id" value="{{ question.id }}">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div id="tips" data-url="{{ url({'for':'home.answer.tips'}) }}"></div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block link_css %}
|
||||||
|
|
||||||
|
{{ css_link('https://cdn.jsdelivr.net/npm/vditor/dist/index.css', false) }}
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block include_js %}
|
||||||
|
|
||||||
|
{{ js_include('https://cdn.jsdelivr.net/npm/vditor/dist/index.min.js', false) }}
|
||||||
|
{{ js_include('home/js/answer.js') }}
|
||||||
|
{{ js_include('home/js/vditor.js') }}
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block inline_js %}
|
||||||
|
|
||||||
|
<script>
|
||||||
|
|
||||||
|
layui.use(['jquery', 'layer'], function () {
|
||||||
|
|
||||||
|
var $ = layui.jquery;
|
||||||
|
var layer = layui.layer;
|
||||||
|
var $tips = $('#tips');
|
||||||
|
|
||||||
|
if ($tips.length > 0) {
|
||||||
|
layer.open({
|
||||||
|
type: 2,
|
||||||
|
title: '答题指南',
|
||||||
|
content: $tips.data('url'),
|
||||||
|
area: ['600px', '320px'],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{% endblock %}
|
49
app/Http/Home/Views/answer/edit.volt
Normal file
49
app/Http/Home/Views/answer/edit.volt
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
{% extends 'templates/main.volt' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div class="breadcrumb">
|
||||||
|
<span class="layui-breadcrumb">
|
||||||
|
<a href="/">首页</a>
|
||||||
|
<a><cite>编辑回答</cite></a>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="layout-main">
|
||||||
|
<div class="writer-content wrap">
|
||||||
|
<form class="layui-form" method="POST" action="{{ url({'for':'home.answer.update','id':answer.id}) }}">
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<div class="layui-input-block" style="margin:0;">
|
||||||
|
<input class="layui-input" type="text" name="title" value="{{ question.title }}" readonly="readonly">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item">
|
||||||
|
<div class="layui-input-block" style="margin:0;">
|
||||||
|
<div id="vditor"></div>
|
||||||
|
<textarea name="content" class="layui-hide" id="vditor-textarea">{{ answer.content }}</textarea>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="layui-form-item center">
|
||||||
|
<div class="layui-input-block" style="margin:0;">
|
||||||
|
<button class="layui-btn kg-submit" lay-submit="true" lay-filter="go">发布回答</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block link_css %}
|
||||||
|
|
||||||
|
{{ css_link('https://cdn.jsdelivr.net/npm/vditor/dist/index.css', false) }}
|
||||||
|
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block include_js %}
|
||||||
|
|
||||||
|
{{ js_include('https://cdn.jsdelivr.net/npm/vditor/dist/index.min.js', false) }}
|
||||||
|
{{ js_include('home/js/answer.js') }}
|
||||||
|
{{ js_include('home/js/vditor.js') }}
|
||||||
|
|
||||||
|
{% endblock %}
|
20
app/Http/Home/Views/answer/tips.volt
Normal file
20
app/Http/Home/Views/answer/tips.volt
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{% extends 'templates/layer.volt' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
|
||||||
|
<div class="answer-tips">
|
||||||
|
<h3 class="suggest-text">适合作为回答的</h3>
|
||||||
|
<ul class="suggest-list">
|
||||||
|
<li><i class="layui-icon layui-icon-ok-circle"></i> 经过验证的有效解决办法</li>
|
||||||
|
<li><i class="layui-icon layui-icon-ok-circle"></i> 自己的经验指引,对解决问题有帮助</li>
|
||||||
|
<li><i class="layui-icon layui-icon-ok-circle"></i> 遵循 Markdown 语法排版,表达语义正确</li>
|
||||||
|
</ul>
|
||||||
|
<h3 class="not-suggest-text">不该作为回答的</h3>
|
||||||
|
<ul class="not-suggest-list">
|
||||||
|
<li><i class="layui-icon layui-icon-close-fill"></i> 询问内容细节或回复楼层</li>
|
||||||
|
<li><i class="layui-icon layui-icon-close-fill"></i> 与题目无关的内容</li>
|
||||||
|
<li><i class="layui-icon layui-icon-close-fill"></i> “赞” “顶” “同问” 等毫无意义的内容</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{% endblock %}
|
@ -9,7 +9,7 @@
|
|||||||
<div class="footer" id="comment-footer" style="display:none;">
|
<div class="footer" id="comment-footer" style="display:none;">
|
||||||
<div class="toolbar"></div>
|
<div class="toolbar"></div>
|
||||||
<div class="action">
|
<div class="action">
|
||||||
<button class="{{ submit_class }}" lay-submit="true" lay-filter="addComment">发布</button>
|
<button class="{{ submit_class }}" lay-submit="true" lay-filter="add_comment">发布</button>
|
||||||
<button class="layui-btn layui-btn-sm layui-bg-gray" id="btn-cancel-comment" type="button">取消</button>
|
<button class="layui-btn layui-btn-sm layui-bg-gray" id="btn-cancel-comment" type="button">取消</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
{% set title = article.id > 0 ? '编辑文章' : '撰写文章' %}
|
{% set title = article.id > 0 ? '编辑文章' : '写文章' %}
|
||||||
{% set action_url = article.id > 0 ? url({'for':'home.article.update','id':article.id}) : url({'for':'home.article.create'}) %}
|
{% set action_url = article.id > 0 ? url({'for':'home.article.update','id':article.id}) : url({'for':'home.article.create'}) %}
|
||||||
{% set source_url_display = article.source_type == 1 ? 'display:none' : 'display:block' %}
|
{% set source_url_display = article.source_type == 1 ? 'display:none' : 'display:block' %}
|
||||||
|
|
||||||
@ -30,8 +30,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="layui-form-item center">
|
<div class="layui-form-item center">
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
<button class="layui-btn kg-submit" lay-submit="true" lay-filter="go">提交</button>
|
<button class="layui-btn kg-submit" lay-submit="true" lay-filter="go">发布文章</button>
|
||||||
<button class="kg-back layui-btn layui-btn-primary" type="button">返回</button>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@ -41,25 +40,6 @@
|
|||||||
<div class="layui-card-header">基本信息</div>
|
<div class="layui-card-header">基本信息</div>
|
||||||
<div class="layui-card-body">
|
<div class="layui-card-body">
|
||||||
<div class="writer-sidebar">
|
<div class="writer-sidebar">
|
||||||
<div class="layui-form-item cover-wrap">
|
|
||||||
<label class="layui-form-label">封面</label>
|
|
||||||
<div class="layui-input-block">
|
|
||||||
<img id="img-cover" class="cover" src="{{ article.cover }}!cover_270">
|
|
||||||
<input type="hidden" name="cover" value="{{ article.cover }}">
|
|
||||||
</div>
|
|
||||||
<button id="change-cover" class="layui-btn layui-btn-sm btn-change" type="button">更换</button>
|
|
||||||
</div>
|
|
||||||
<div class="layui-form-item">
|
|
||||||
<label class="layui-form-label">分类</label>
|
|
||||||
<div class="layui-input-block">
|
|
||||||
<select name="category_id" lay-verify="required">
|
|
||||||
<option value="">请选择</option>
|
|
||||||
{% for item in categories %}
|
|
||||||
<option value="{{ item.id }}" {% if article.category_id == item.id %}selected="selected"{% endif %}>{{ item.name }}</option>
|
|
||||||
{% endfor %}
|
|
||||||
</select>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<label class="layui-form-label">标签</label>
|
<label class="layui-form-label">标签</label>
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
@ -87,10 +67,10 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
<label class="layui-form-label">允许评论</label>
|
<label class="layui-form-label">关闭评论</label>
|
||||||
<div class="layui-input-block">
|
<div class="layui-input-block">
|
||||||
<input type="radio" name="allow_comment" value="1" title="是" {% if article.allow_comment == 1 %}checked="checked"{% endif %}>
|
<input type="radio" name="closed" value="1" title="是" {% if article.closed == 1 %}checked="checked"{% endif %}>
|
||||||
<input type="radio" name="allow_comment" value="0" title="否" {% if article.allow_comment == 0 %}checked="checked"{% endif %}>
|
<input type="radio" name="closed" value="0" title="否" {% if article.closed == 0 %}checked="checked"{% endif %}>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="layui-form-item">
|
<div class="layui-form-item">
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% if authors %}
|
{% if authors|length > 0 %}
|
||||||
<div class="layui-card">
|
<div class="layui-card">
|
||||||
<div class="layui-card-header">推荐作者</div>
|
<div class="layui-card-header">推荐作者</div>
|
||||||
<div class="layui-card-body">
|
<div class="layui-card-body">
|
||||||
|
@ -2,10 +2,10 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% block content %}
|
||||||
|
|
||||||
{% set category_val = request.get('category_id','int','all') %}
|
|
||||||
{% set sort_val = request.get('sort','trim','latest') %}
|
{% set sort_val = request.get('sort','trim','latest') %}
|
||||||
{% set pager_url = url({'for':'home.article.pager'}, params) %}
|
{% set pager_url = url({'for':'home.article.pager'}, params) %}
|
||||||
{% set hot_author_url = url({'for':'home.article.hot_authors'}) %}
|
{% set hot_authors_url = url({'for':'home.article.hot_authors'}) %}
|
||||||
|
{% set my_tags_url = url({'for':'home.widget.my_tags'},{'type':'article'}) %}
|
||||||
|
|
||||||
<div class="breadcrumb">
|
<div class="breadcrumb">
|
||||||
<span class="layui-breadcrumb">
|
<span class="layui-breadcrumb">
|
||||||
@ -17,30 +17,26 @@
|
|||||||
<div class="layout-main clearfix">
|
<div class="layout-main clearfix">
|
||||||
<div class="layout-content">
|
<div class="layout-content">
|
||||||
<div class="content-wrap wrap">
|
<div class="content-wrap wrap">
|
||||||
<div class="article-sort">
|
<div class="layui-tab layui-tab-brief search-tab">
|
||||||
{% for sort in sorts %}
|
<ul class="layui-tab-title">
|
||||||
{% set class = sort_val == sort.id ? 'layui-btn layui-btn-xs' : 'none' %}
|
{% for sort in sorts %}
|
||||||
<a class="{{ class }}" href="{{ sort.url }}">{{ sort.name }}</a>
|
{% set class = sort_val == sort.id ? 'layui-this' : 'none' %}
|
||||||
{% endfor %}
|
<li class="{{ class }}"><a href="{{ sort.url }}">{{ sort.name }}</a></li>
|
||||||
</div>
|
{% endfor %}
|
||||||
<div class="article-list" id="article-list" data-url="{{ pager_url }}"></div>
|
</ul>
|
||||||
</div>
|
<div class="layui-tab-content">
|
||||||
</div>
|
<div class="layui-tab-item layui-show">
|
||||||
<div class="layout-sidebar">
|
<div id="article-list" data-url="{{ pager_url }}"></div>
|
||||||
<div class="sidebar">
|
</div>
|
||||||
<div class="layui-card">
|
|
||||||
<div class="layui-card-header">文章分类</div>
|
|
||||||
<div class="layui-card-body">
|
|
||||||
<ul class="article-cate-list">
|
|
||||||
{% for category in categories %}
|
|
||||||
{% set class = category_val == category.id ? 'active' : '' %}
|
|
||||||
<li class="{{ class }}"><a href="{{ category.url }}">{{ category.name }}</a></li>
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="sidebar" id="hot-author-list" data-url="{{ hot_author_url }}"></div>
|
</div>
|
||||||
|
<div class="layout-sidebar">
|
||||||
|
{% if auth_user.id > 0 %}
|
||||||
|
<div class="sidebar" id="sidebar-my-tags" data-url="{{ my_tags_url }}"></div>
|
||||||
|
{% endif %}
|
||||||
|
<div class="sidebar" id="sidebar-hot-authors" data-url="{{ hot_authors_url }}"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -1,16 +1,11 @@
|
|||||||
{{ partial('macros/article') }}
|
{{ partial('macros/article') }}
|
||||||
|
|
||||||
{% if pager.total_pages > 0 %}
|
{% if pager.total_pages > 0 %}
|
||||||
<div class="article-list clearfix">
|
<div class="article-list">
|
||||||
{% for item in pager.items %}
|
{% for item in pager.items %}
|
||||||
{% set article_url = url({'for':'home.article.show','id':item.id}) %}
|
{% set article_url = url({'for':'home.article.show','id':item.id}) %}
|
||||||
{% set owner_url = url({'for':'home.user.show','id':item.owner.id}) %}
|
{% set owner_url = url({'for':'home.user.show','id':item.owner.id}) %}
|
||||||
<div class="article-card clearfix">
|
<div class="article-card">
|
||||||
<div class="cover">
|
|
||||||
<a href="{{ article_url }}" target="_blank">
|
|
||||||
<img src="{{ item.cover }}!cover_270" alt="{{ item.title }}">
|
|
||||||
</a>
|
|
||||||
</div>
|
|
||||||
<div class="info">
|
<div class="info">
|
||||||
<div class="title layui-elip">
|
<div class="title layui-elip">
|
||||||
<a href="{{ article_url }}" target="_blank">{{ item.title }}</a>
|
<a href="{{ article_url }}" target="_blank">{{ item.title }}</a>
|
||||||
@ -18,12 +13,19 @@
|
|||||||
<div class="summary">{{ item.summary }}</div>
|
<div class="summary">{{ item.summary }}</div>
|
||||||
<div class="meta">
|
<div class="meta">
|
||||||
<span class="owner"><a href="{{ owner_url }}">{{ item.owner.name }}</a></span>
|
<span class="owner"><a href="{{ owner_url }}">{{ item.owner.name }}</a></span>
|
||||||
<span class="view">{{ item.view_count }} 浏览</span>
|
|
||||||
<span class="comment">{{ item.comment_count }} 评论</span>
|
|
||||||
<span class="like">{{ item.like_count }} 点赞</span>
|
|
||||||
<span class="time">{{ item.create_time|time_ago }}</span>
|
<span class="time">{{ item.create_time|time_ago }}</span>
|
||||||
|
<span class="view">{{ item.view_count }} 浏览</span>
|
||||||
|
<span class="like">{{ item.like_count }} 点赞</span>
|
||||||
|
<span class="comment">{{ item.comment_count }} 评论</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
{% if item.cover %}
|
||||||
|
<div class="cover">
|
||||||
|
<a href="{{ article_url }}" target="_blank">
|
||||||
|
<img src="{{ item.cover }}!cover_270" alt="{{ item.title }}">
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -11,7 +11,7 @@
|
|||||||
<div class="meta">
|
<div class="meta">
|
||||||
<span class="view">{{ article.view_count }} 浏览</span>
|
<span class="view">{{ article.view_count }} 浏览</span>
|
||||||
<span class="like">{{ article.like_count }} 点赞</span>
|
<span class="like">{{ article.like_count }} 点赞</span>
|
||||||
<span class="comment">{{ article.like_count }} 评论</span>
|
<span class="comment">{{ article.comment_count }} 评论</span>
|
||||||
</div>
|
</div>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -5,14 +5,17 @@
|
|||||||
{{ partial('macros/article') }}
|
{{ partial('macros/article') }}
|
||||||
|
|
||||||
{% set article_list_url = url({'for':'home.article.list'}) %}
|
{% set article_list_url = url({'for':'home.article.list'}) %}
|
||||||
{% set related_article_url = url({'for':'home.article.related','id':article.id}) %}
|
{% set article_report_url = url({'for':'home.article.report','id':article.id}) %}
|
||||||
{% set owner_url = url({'for':'home.user.show','id':article.owner.id}) %}
|
{% set article_edit_url = url({'for':'home.article.edit','id':article.id}) %}
|
||||||
|
{% set article_delete_url = url({'for':'home.article.delete','id':article.id}) %}
|
||||||
|
{% set article_owner_url = url({'for':'home.user.show','id':article.owner.id}) %}
|
||||||
|
{% set article_related_url = url({'for':'home.article.related','id':article.id}) %}
|
||||||
|
|
||||||
<div class="breadcrumb">
|
<div class="breadcrumb">
|
||||||
<span class="layui-breadcrumb">
|
<span class="layui-breadcrumb">
|
||||||
<a href="/">首页</a>
|
<a href="/">首页</a>
|
||||||
<a href="{{ article_list_url }}">专栏</a>
|
<a href="{{ article_list_url }}">专栏</a>
|
||||||
<a><cite>文章详情</cite></a>
|
<a><cite>详情</cite></a>
|
||||||
</span>
|
</span>
|
||||||
<span class="share">
|
<span class="share">
|
||||||
<a href="javascript:" title="分享到微信"><i class="layui-icon layui-icon-login-wechat icon-wechat"></i></a>
|
<a href="javascript:" title="分享到微信"><i class="layui-icon layui-icon-login-wechat icon-wechat"></i></a>
|
||||||
@ -29,13 +32,20 @@
|
|||||||
<div class="article-info wrap">
|
<div class="article-info wrap">
|
||||||
<div class="title">{{ article.title }}</div>
|
<div class="title">{{ article.title }}</div>
|
||||||
<div class="meta">
|
<div class="meta">
|
||||||
<span class="source layui-badge layui-bg-green">{{ source_type(article.source_type) }}</span>
|
<div class="left">
|
||||||
<span class="owner">
|
<span class="source layui-badge layui-bg-green">{{ source_type(article.source_type) }}</span>
|
||||||
<a href="{{ owner_url }}">{{ article.owner.name }}</a>
|
<span class="owner"><a href="{{ article_owner_url }}">{{ article.owner.name }}</a></span>
|
||||||
</span>
|
<span class="time">{{ article.create_time|time_ago }}</span>
|
||||||
<span class="view">{{ article.view_count }} 阅读</span>
|
<span class="view">{{ article.view_count }} 阅读</span>
|
||||||
<span class="word">{{ article.word_count }} 字数</span>
|
<span class="comment">{{ article.comment_count }} 评论</span>
|
||||||
<span class="time" title="{{ date('Y-m-d H:i:s',article.create_time) }}">{{ article.create_time|time_ago }}</span>
|
</div>
|
||||||
|
<div class="right">
|
||||||
|
<span class="article-report" data-url="{{ article_report_url }}">举报</span>
|
||||||
|
{% if auth_user.id == article.owner.id %}
|
||||||
|
<span class="article-edit" data-url="{{ article_edit_url }}">编辑</span>
|
||||||
|
<span class="kg-delete" data-url="{{ article_delete_url }}">删除</span>
|
||||||
|
{% endif %}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="content markdown-body">{{ article.content }}</div>
|
<div class="content markdown-body">{{ article.content }}</div>
|
||||||
{% if article.tags %}
|
{% if article.tags %}
|
||||||
@ -55,7 +65,7 @@
|
|||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div id="comment-anchor"></div>
|
<div id="comment-anchor"></div>
|
||||||
{% if article.allow_comment == 1 %}
|
{% if article.closed == 0 %}
|
||||||
<div class="article-comment wrap">
|
<div class="article-comment wrap">
|
||||||
{{ partial('article/comment') }}
|
{{ partial('article/comment') }}
|
||||||
</div>
|
</div>
|
||||||
@ -74,7 +84,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="info">
|
<div class="info">
|
||||||
<div class="name layui-elip">
|
<div class="name layui-elip">
|
||||||
<a href="{{ owner_url }}" title="{{ article.owner.about }}">{{ article.owner.name }}</a>
|
<a href="{{ article_owner_url }}" title="{{ article.owner.about }}">{{ article.owner.name }}</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="title layui-elip">{{ article.owner.title|default('初出江湖') }}</div>
|
<div class="title layui-elip">{{ article.owner.title|default('初出江湖') }}</div>
|
||||||
</div>
|
</div>
|
||||||
@ -82,7 +92,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="sidebar" id="related-article-list" data-url="{{ related_article_url }}"></div>
|
<div class="sidebar" id="sidebar-related" data-url="{{ article_related_url }}"></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="item" id="toolbar-favorite">
|
<div class="item" id="toolbar-favorite">
|
||||||
<div class="icon" title="{{ favorite_title }}" data-url="{{ favorite_url }}">
|
<div class="icon" title="{{ favorite_title }}" data-url="{{ favorite_url }}">
|
||||||
<i class="layui-icon layui-icon-star icon-star {{ favorite_class }}"></i>
|
<i class="layui-icon icon-star {{ favorite_class }}"></i>
|
||||||
</div>
|
</div>
|
||||||
<div class="text" data-count="{{ article.favorite_count }}">{{ article.favorite_count }}</div>
|
<div class="text" data-count="{{ article.favorite_count }}">{{ article.favorite_count }}</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
<div class="footer" id="comment-footer" style="display:none;">
|
<div class="footer" id="comment-footer" style="display:none;">
|
||||||
<div class="toolbar"></div>
|
<div class="toolbar"></div>
|
||||||
<div class="action">
|
<div class="action">
|
||||||
<button class="layui-btn layui-btn-sm" lay-submit="true" lay-filter="addComment">发布</button>
|
<button class="layui-btn layui-btn-sm" lay-submit="true" lay-filter="add_comment">发布</button>
|
||||||
<button class="layui-btn layui-btn-sm layui-bg-gray" id="btn-cancel-comment" type="button">取消</button>
|
<button class="layui-btn layui-btn-sm layui-bg-gray" id="btn-cancel-comment" type="button">取消</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,12 +1,13 @@
|
|||||||
{% if pager.total_pages > 0 %}
|
{% if pager.total_pages > 0 %}
|
||||||
{% for item in pager.items %}
|
{% for item in pager.items %}
|
||||||
{% set owner_url = url({'for':'home.user.show','id':item.owner.id}) %}
|
{% set owner_url = url({'for':'home.user.show','id':item.owner.id}) %}
|
||||||
|
{% set report_url = url({'for':'home.comment.report','id':item.id}) %}
|
||||||
{% set like_url = url({'for':'home.comment.like','id':item.id}) %}
|
{% set like_url = url({'for':'home.comment.like','id':item.id}) %}
|
||||||
{% set delete_url = url({'for':'home.comment.delete','id':item.id}) %}
|
{% set delete_url = url({'for':'home.comment.delete','id':item.id}) %}
|
||||||
{% set reply_create_url = url({'for':'home.comment.create_reply','id':item.id}) %}
|
{% set reply_url = url({'for':'home.comment.reply','id':item.id}) %}
|
||||||
{% set reply_list_url = url({'for':'home.comment.replies','id':item.id},{'limit':5}) %}
|
{% set reply_list_url = url({'for':'home.comment.replies','id':item.id},{'limit':5}) %}
|
||||||
<div class="comment-box" id="comment-{{ item.id }}">
|
<div class="comment-box" id="comment-{{ item.id }}">
|
||||||
<div class="comment-card clearfix">
|
<div class="comment-card">
|
||||||
<div class="avatar">
|
<div class="avatar">
|
||||||
<a href="{{ owner_url }}" title="{{ item.owner.name }}" target="_blank">
|
<a href="{{ owner_url }}" title="{{ item.owner.name }}" target="_blank">
|
||||||
<img src="{{ item.owner.avatar }}!avatar_160" alt="{{ item.owner.name }}">
|
<img src="{{ item.owner.avatar }}!avatar_160" alt="{{ item.owner.name }}">
|
||||||
@ -25,26 +26,26 @@
|
|||||||
<div class="column">
|
<div class="column">
|
||||||
<span class="like-count" data-count="{{ item.like_count }}">{{ item.like_count }}</span>
|
<span class="like-count" data-count="{{ item.like_count }}">{{ item.like_count }}</span>
|
||||||
{% if item.me.liked == 1 %}
|
{% if item.me.liked == 1 %}
|
||||||
<span class="action action-like liked" title="取消点赞" data-url="{{ like_url }}">已赞</span>
|
<span class="action comment-like liked" title="取消点赞" data-url="{{ like_url }}">已赞</span>
|
||||||
{% else %}
|
{% else %}
|
||||||
<span class="action action-like" title="点赞支持" data-url="{{ like_url }}">点赞</span>
|
<span class="action comment-like" title="点赞支持" data-url="{{ like_url }}">点赞</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<span class="reply-count" data-count="{{ item.reply_count }}">{{ item.reply_count }}</span>
|
<span class="reply-count" data-count="{{ item.reply_count }}">{{ item.reply_count }}</span>
|
||||||
<span class="action action-toggle" title="展开回应" data-id="{{ item.id }}" data-url="{{ reply_list_url }}">回应</span>
|
<span class="action comment-toggle" title="展开回应" data-id="{{ item.id }}" data-url="{{ reply_list_url }}">回应</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<span class="action action-reply" data-id="{{ item.id }}">回复</span>
|
<span class="action comment-reply" data-id="{{ item.id }}">回复</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<span class="action action-report" data-id="{{ item.id }}">举报</span>
|
<span class="action comment-report" data-url="{{ report_url }}">举报</span>
|
||||||
</div>
|
</div>
|
||||||
{% if item.owner.id == auth_user.id %}
|
{% if item.owner.id == auth_user.id %}
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<span class="action action-delete" data-id="{{ item.id }}" data-url="{{ delete_url }}">删除</span>
|
<span class="action comment-delete" data-id="{{ item.id }}" data-url="{{ delete_url }}">删除</span>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
@ -52,12 +53,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="comment-form" id="comment-form-{{ item.id }}" style="display:none;">
|
<div class="comment-form" id="comment-form-{{ item.id }}" style="display:none;">
|
||||||
<form class="layui-form" method="post" action="{{ reply_create_url }}">
|
<form class="layui-form" method="post" action="{{ reply_url }}">
|
||||||
<textarea class="layui-textarea" name="content" placeholder="撰写评论..." lay-verify="required"></textarea>
|
<textarea class="layui-textarea" name="content" placeholder="撰写评论..." lay-verify="required"></textarea>
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
<div class="toolbar"></div>
|
<div class="toolbar"></div>
|
||||||
<div class="action">
|
<div class="action">
|
||||||
<button class="layui-btn layui-btn-sm" lay-submit="true" lay-filter="replyComment" data-comment-id="{{ item.id }}" data-parent-id="{{ item.parent_id }}">发布</button>
|
<button class="layui-btn layui-btn-sm" lay-submit="true" lay-filter="reply_comment" data-comment-id="{{ item.id }}" data-parent-id="{{ item.parent_id }}">发布</button>
|
||||||
<button class="layui-btn layui-btn-sm layui-bg-gray btn-cancel-reply" type="button" data-id="{{ item.id }}">取消</button>
|
<button class="layui-btn layui-btn-sm layui-bg-gray btn-cancel-reply" type="button" data-id="{{ item.id }}">取消</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -3,9 +3,9 @@
|
|||||||
{% set owner_url = url({'for':'home.user.show','id':item.owner.id}) %}
|
{% set owner_url = url({'for':'home.user.show','id':item.owner.id}) %}
|
||||||
{% set like_url = url({'for':'home.comment.like','id':item.id}) %}
|
{% set like_url = url({'for':'home.comment.like','id':item.id}) %}
|
||||||
{% set delete_url = url({'for':'home.comment.delete','id':item.id}) %}
|
{% set delete_url = url({'for':'home.comment.delete','id':item.id}) %}
|
||||||
{% set reply_create_url = url({'for':'home.comment.create_reply','id':item.id}) %}
|
{% set reply_url = url({'for':'home.comment.reply','id':item.id}) %}
|
||||||
<div class="comment-box" id="comment-{{ item.id }}">
|
<div class="comment-box" id="comment-{{ item.id }}">
|
||||||
<div class="comment-card clearfix">
|
<div class="comment-card">
|
||||||
<div class="avatar">
|
<div class="avatar">
|
||||||
<a href="{{ owner_url }}" title="{{ item.owner.name }}" target="_blank">
|
<a href="{{ owner_url }}" title="{{ item.owner.name }}" target="_blank">
|
||||||
<img src="{{ item.owner.avatar }}!avatar_160" alt="{{ item.owner.name }}">
|
<img src="{{ item.owner.avatar }}!avatar_160" alt="{{ item.owner.name }}">
|
||||||
@ -29,22 +29,22 @@
|
|||||||
<div class="column">
|
<div class="column">
|
||||||
<span class="like-count" data-count="{{ item.like_count }}">{{ item.like_count }}</span>
|
<span class="like-count" data-count="{{ item.like_count }}">{{ item.like_count }}</span>
|
||||||
{% if item.me.liked == 1 %}
|
{% if item.me.liked == 1 %}
|
||||||
<span class="action action-like liked" title="取消点赞" data-url="{{ like_url }}">已赞</span>
|
<span class="action comment-like liked" title="取消点赞" data-url="{{ like_url }}">已赞</span>
|
||||||
{% else %}
|
{% else %}
|
||||||
<span class="action action-like" title="点赞支持" data-url="{{ like_url }}">点赞</span>
|
<span class="action comment-like" title="点赞支持" data-url="{{ like_url }}">点赞</span>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="right">
|
<div class="right">
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<span class="action action-reply" data-id="{{ item.id }}">回复</span>
|
<span class="action comment-reply" data-id="{{ item.id }}">回复</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<span class="action action-report" data-id="{{ item.id }}">举报</span>
|
<span class="action comment-report" data-id="{{ item.id }}">举报</span>
|
||||||
</div>
|
</div>
|
||||||
{% if item.owner.id == auth_user.id %}
|
{% if item.owner.id == auth_user.id %}
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<span class="action action-delete" data-id="{{ item.id }}" data-parent-id="{{ item.parent_id }}" data-url="{{ delete_url }}">删除</span>
|
<span class="action comment-delete" data-id="{{ item.id }}" data-parent-id="{{ item.parent_id }}" data-url="{{ delete_url }}">删除</span>
|
||||||
</div>
|
</div>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
</div>
|
</div>
|
||||||
@ -52,12 +52,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="comment-form" id="comment-form-{{ item.id }}" style="display:none;">
|
<div class="comment-form" id="comment-form-{{ item.id }}" style="display:none;">
|
||||||
<form class="layui-form" method="post" action="{{ reply_create_url }}">
|
<form class="layui-form" method="post" action="{{ reply_url }}">
|
||||||
<textarea class="layui-textarea" name="content" placeholder="撰写评论..." lay-verify="required"></textarea>
|
<textarea class="layui-textarea" name="content" placeholder="撰写评论..." lay-verify="required"></textarea>
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
<div class="toolbar"></div>
|
<div class="toolbar"></div>
|
||||||
<div class="action">
|
<div class="action">
|
||||||
<button class="layui-btn layui-btn-sm" lay-submit="true" lay-filter="replyComment" data-comment-id="{{ item.id }}" data-parent-id="{{ item.parent_id }}">发布</button>
|
<button class="layui-btn layui-btn-sm" lay-submit="true" lay-filter="reply_comment" data-comment-id="{{ item.id }}" data-parent-id="{{ item.parent_id }}">发布</button>
|
||||||
<button class="layui-btn layui-btn-sm layui-bg-gray btn-cancel-reply" type="button" data-id="{{ item.id }}">取消</button>
|
<button class="layui-btn layui-btn-sm layui-bg-gray btn-cancel-reply" type="button" data-id="{{ item.id }}">取消</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -1,19 +0,0 @@
|
|||||||
{% extends 'templates/layer.volt' %}
|
|
||||||
|
|
||||||
{% block content %}
|
|
||||||
<form class="layui-form" method="post" action="{{ url({'for':'home.comment.create_reply','id':comment.id}) }}">
|
|
||||||
<div class="layui-form-item">
|
|
||||||
<textarea class="layui-textarea" name="content" placeholder="@{{ comment.owner.name }}" lay-verify="required"></textarea>
|
|
||||||
</div>
|
|
||||||
<div class="layui-form-item center">
|
|
||||||
<button class="layui-btn" lay-submit="true" lay-filter="replyComment">提交</button>
|
|
||||||
<button type="reset" class="layui-btn layui-btn-primary">重置</button>
|
|
||||||
</div>
|
|
||||||
</form>
|
|
||||||
{% endblock %}
|
|
||||||
|
|
||||||
{% block include_js %}
|
|
||||||
|
|
||||||
{{ js_include('home/js/comment.js') }}
|
|
||||||
|
|
||||||
{% endblock %}
|
|
@ -5,7 +5,7 @@
|
|||||||
{% set owner_url = url({'for':'home.user.show','id':item.owner.id}) %}
|
{% set owner_url = url({'for':'home.user.show','id':item.owner.id}) %}
|
||||||
{% set consult_url = url({'for':'home.consult.show','id':item.id}) %}
|
{% set consult_url = url({'for':'home.consult.show','id':item.id}) %}
|
||||||
{% set like_url = url({'for':'home.consult.like','id':item.id}) %}
|
{% set like_url = url({'for':'home.consult.like','id':item.id}) %}
|
||||||
<div class="comment-card review-card clearfix">
|
<div class="comment-card consult-card">
|
||||||
<div class="avatar">
|
<div class="avatar">
|
||||||
<a href="{{ owner_url }}" title="{{ item.owner.name }}" target="_blank">
|
<a href="{{ owner_url }}" title="{{ item.owner.name }}" target="_blank">
|
||||||
<img src="{{ item.owner.avatar }}!avatar_160" alt="{{ item.owner.name }}">
|
<img src="{{ item.owner.avatar }}!avatar_160" alt="{{ item.owner.name }}">
|
||||||
@ -17,8 +17,8 @@
|
|||||||
<i class="layui-icon layui-icon-more"></i>
|
<i class="layui-icon layui-icon-more"></i>
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<div class="title layui-elip">{{ item.question }}</div>
|
<div class="question">{{ item.question }}</div>
|
||||||
<div class="content">{{ item.answer }}</div>
|
<div class="answer">{{ item.answer }}</div>
|
||||||
<div class="footer">
|
<div class="footer">
|
||||||
<div class="left">
|
<div class="left">
|
||||||
<div class="column">
|
<div class="column">
|
||||||
|
@ -5,7 +5,7 @@
|
|||||||
{% for item in pager.items %}
|
{% for item in pager.items %}
|
||||||
{% set owner_url = url({'for':'home.user.show','id':item.owner.id}) %}
|
{% set owner_url = url({'for':'home.user.show','id':item.owner.id}) %}
|
||||||
{% set like_url = url({'for':'home.review.like','id':item.id}) %}
|
{% set like_url = url({'for':'home.review.like','id':item.id}) %}
|
||||||
<div class="comment-card review-card clearfix">
|
<div class="comment-card review-card">
|
||||||
<div class="avatar">
|
<div class="avatar">
|
||||||
<a href="{{ owner_url }}" title="{{ item.owner.name }}" target="_blank">
|
<a href="{{ owner_url }}" title="{{ item.owner.name }}" target="_blank">
|
||||||
<img src="{{ item.owner.avatar }}!avatar_160" alt="{{ item.owner.name }}">
|
<img src="{{ item.owner.avatar }}!avatar_160" alt="{{ item.owner.name }}">
|
||||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user