1
0
mirror of https://gitee.com/koogua/course-tencent-cloud.git synced 2025-06-23 03:50:56 +08:00

阶段提交

This commit is contained in:
koogua 2021-05-13 17:50:39 +08:00
parent c7a84e60df
commit 1a15ad58c9
46 changed files with 373 additions and 236 deletions

View File

@ -2,13 +2,15 @@
### 更新
- 前台增加问答功能
- 增加问答功能
- 增加标签关注功能
- 优化标签功能
- 优化文章功能以及全文搜索
- 优化课程评价,咨询,文章等相关统计
- 后台增加提问和回答审核功能
- 后台增加查看用户在线记录 修正后台编辑角色权限错误
- 优化前台界面
- 后台增加提问和回答审核功能
- 后台增加查看用户在线记录
- 修正后台编辑角色权限错误
### [v1.3.3](https://gitee.com/koogua/course-tencent-cloud/releases/v1.3.3)(2021-04-30)

View File

@ -8,7 +8,7 @@ use App\Repos\User as UserRepo;
use Phalcon\Mvc\Model\Resultset;
use Phalcon\Mvc\Model\ResultsetInterface;
class ArticleHotAuthorList extends Cache
class TopAuthorList extends Cache
{
protected $lifetime = 1 * 86400;
@ -20,7 +20,7 @@ class ArticleHotAuthorList extends Cache
public function getKey($id = null)
{
return 'article_hot_author_list';
return 'top_author_list';
}
public function getContent($id = null)

View File

@ -24,7 +24,7 @@ class RevokeVipTask extends Task
}
/**
* 查找待解锁用户
* 查找待撤销会员
*
* @param int $limit
* @return ResultsetInterface|Resultset|UserModel[]

View File

@ -2,6 +2,7 @@
namespace App\Console\Tasks;
use App\Caches\NavTreeList as NavTreeListCache;
use App\Caches\Setting as SettingCache;
use App\Models\Setting as SettingModel;
@ -14,6 +15,7 @@ class UpgradeTask extends Task
$this->resetAnnotationAction();
$this->resetMetadataAction();
$this->resetVoltAction();
$this->resetNavAction();
}
/**
@ -103,6 +105,22 @@ class UpgradeTask extends Task
echo '------ end reset volt ------' . PHP_EOL;
}
/**
* 重置导航
*
* @command: php console.php upgrade reset_nav
*/
public function resetNavAction()
{
echo '------ start reset navigation ------' . PHP_EOL;
$cache = new NavTreeListCache();
$cache->delete();
echo '------ end reset navigation ------' . PHP_EOL;
}
protected function handlePhKeys($keys)
{
return array_map(function ($key) {

View File

@ -171,8 +171,6 @@ class AnswerController extends Controller
{
$answerService = new AnswerService();
$answer = $answerService->getAnswer($id);
if ($this->request->isPost()) {
$answerService->reviewAnswer($id);
@ -188,13 +186,9 @@ class AnswerController extends Controller
}
$reasons = $answerService->getReasons();
$questionService = new QuestionService();
$question = $questionService->getQuestion($answer->question_id);
$answer = $answerService->getAnswerInfo($id);
$this->view->setVar('reasons', $reasons);
$this->view->setVar('question', $question);
$this->view->setVar('answer', $answer);
}

View File

@ -190,7 +190,7 @@ class ArticleController extends Controller
}
$reasons = $articleService->getReasons();
$article = $articleService->getArticle($id);
$article = $articleService->getArticleInfo($id);
$this->view->setVar('reasons', $reasons);
$this->view->setVar('article', $article);

View File

@ -186,7 +186,7 @@ class QuestionController extends Controller
}
$reasons = $questionService->getReasons();
$question = $questionService->getQuestion($id);
$question = $questionService->getQuestionInfo($id);
$this->view->setVar('reasons', $reasons);
$this->view->setVar('question', $question);

View File

@ -11,6 +11,7 @@ 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\Answer\AnswerInfo as AnswerInfoService;
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;
@ -54,6 +55,13 @@ class Answer extends Service
return $this->findOrFail($id);
}
public function getAnswerInfo($id)
{
$service = new AnswerInfoService();
return $service->handle($id);
}
public function createAnswer()
{
$post = $this->request->getPost();
@ -78,6 +86,8 @@ class Answer extends Service
$this->handleAnswerPostPoint($answer);
$this->handleQuestionAnsweredNotice($answer);
$this->eventsManager->fire('Answer:afterCreate', $this, $answer);
return $answer;
}
@ -110,6 +120,8 @@ class Answer extends Service
$answer->update($data);
$this->eventsManager->fire('Answer:afterUpdate', $this, $answer);
return $answer;
}
@ -129,6 +141,8 @@ class Answer extends Service
$this->recountUserAnswers($owner);
$this->eventsManager->fire('Answer:afterDelete', $this, $answer);
return $answer;
}
@ -148,6 +162,8 @@ class Answer extends Service
$this->recountUserAnswers($owner);
$this->eventsManager->fire('Answer:afterRestore', $this, $answer);
return $answer;
}

View File

@ -15,6 +15,7 @@ use App\Repos\Category as CategoryRepo;
use App\Repos\Tag as TagRepo;
use App\Repos\User as UserRepo;
use App\Services\Logic\Article\ArticleDataTrait;
use App\Services\Logic\Article\ArticleInfo as ArticleInfoService;
use App\Services\Logic\Notice\System\ArticleApproved as ArticleApprovedNotice;
use App\Services\Logic\Notice\System\ArticleRejected as ArticleRejectedNotice;
use App\Services\Logic\Point\History\ArticlePost as ArticlePostPointHistory;
@ -111,6 +112,13 @@ class Article extends Service
return $this->findOrFail($id);
}
public function getArticleInfo($id)
{
$service = new ArticleInfoService();
return $service->handle($id);
}
public function createArticle()
{
$post = $this->request->getPost();

View File

@ -243,6 +243,43 @@ class AuthNode extends Service
],
],
],
[
'id' => '1-8',
'title' => '标签管理',
'type' => 'menu',
'children' => [
[
'id' => '1-8-1',
'title' => '标签列表',
'type' => 'menu',
'route' => 'admin.tag.list',
],
[
'id' => '1-8-2',
'title' => '搜索标签',
'type' => 'menu',
'route' => 'admin.tag.search',
],
[
'id' => '1-8-3',
'title' => '添加标签',
'type' => 'menu',
'route' => 'admin.tag.add',
],
[
'id' => '1-8-4',
'title' => '编辑标签',
'type' => 'button',
'route' => 'admin.tag.edit',
],
[
'id' => '1-8-5',
'title' => '删除标签',
'type' => 'button',
'route' => 'admin.tag.delete',
],
],
],
[
'id' => '1-7',
'title' => '文章管理',
@ -390,43 +427,6 @@ class AuthNode extends Service
],
],
],
[
'id' => '1-8',
'title' => '标签管理',
'type' => 'menu',
'children' => [
[
'id' => '1-8-1',
'title' => '标签列表',
'type' => 'menu',
'route' => 'admin.tag.list',
],
[
'id' => '1-8-2',
'title' => '搜索标签',
'type' => 'menu',
'route' => 'admin.tag.search',
],
[
'id' => '1-8-3',
'title' => '添加标签',
'type' => 'menu',
'route' => 'admin.tag.add',
],
[
'id' => '1-8-4',
'title' => '编辑标签',
'type' => 'button',
'route' => 'admin.tag.edit',
],
[
'id' => '1-8-5',
'title' => '删除标签',
'type' => 'button',
'route' => 'admin.tag.delete',
],
],
],
[
'id' => '1-9',
'title' => '评论管理',
@ -475,19 +475,19 @@ class AuthNode extends Service
'children' => [
[
'id' => '2-10-1',
'title' => '文章列表',
'title' => '文章审核',
'type' => 'menu',
'route' => 'admin.mod.articles',
],
[
'id' => '2-10-2',
'title' => '问题列表',
'title' => '问题审核',
'type' => 'menu',
'route' => 'admin.mod.questions',
],
[
'id' => '2-10-3',
'title' => '回答列表',
'title' => '回答审核',
'type' => 'menu',
'route' => 'admin.mod.answers',
],

View File

@ -17,6 +17,7 @@ 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\Logic\Question\QuestionInfo as QuestionInfoService;
use App\Services\Sync\QuestionIndex as QuestionIndexSync;
use App\Validators\Question as QuestionValidator;
@ -105,6 +106,13 @@ class Question extends Service
return $this->findOrFail($id);
}
public function getQuestionInfo($id)
{
$service = new QuestionInfoService();
return $service->handle($id);
}
public function createQuestion()
{
$post = $this->request->getPost();

View File

@ -2,12 +2,18 @@
{% block content %}
{% set owner_url = url({'for':'home.user.show','id':answer.owner.id}) %}
<fieldset class="layui-elem-field layui-field-title">
<legend>审核内容</legend>
</fieldset>
<div class="kg-mod-preview">
<div class="title">{{ question.title }}</div>
<div class="title">{{ answer.question.title }}</div>
<div class="meta">
<span><a href="{{ owner_url }}" target="_blank">{{ answer.owner.name }}</a></span>
<span>{{ date('Y-m-d H:i',answer.create_time) }}</span>
</div>
<div class="content markdown-body">{{ answer.content }}</div>
</div>

View File

@ -70,7 +70,7 @@
{% endif %}
</p>
<p class="meta">
<span>来源:{{ source_info(item.source_type,item.source_url) }}</span>
<span>来源:{{ source_type(item.source_type) }}</span>
<span>作者:<a href="{{ owner_url }}" target="_blank">{{ item.owner.name }}</a></span>
<span>创建:{{ date('Y-m-d',item.create_time) }}</span>
</p>

View File

@ -2,20 +2,34 @@
{% block content %}
{{ partial('macros/article') }}
{% set owner_url = url({'for':'home.user.show','id':article.owner.id}) %}
<fieldset class="layui-elem-field layui-field-title">
<legend>审核内容</legend>
</fieldset>
<div class="kg-mod-preview">
<div class="title">{{ article.title }}</div>
<div class="meta">
<span class="layui-badge layui-bg-green">{{ source_type(article.source_type) }}</span>
<span><a href="{{ owner_url }}" target="_blank">{{ article.owner.name }}</a></span>
<span>{{ date('Y-m-d H:i',article.create_time) }}</span>
</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>
<span class="layui-btn layui-btn-xs">{{ item.name }}</span>
{% endfor %}
</div>
{% endif %}
{% if article.source_url %}
<div class="source-tips kg-center">
<a href="{{ article.source_url }}" target="_blank">查看原文</a>
</div>
{% endif %}
</div>
<fieldset class="layui-elem-field layui-field-title">

View File

@ -10,13 +10,13 @@
{% endif %}
{%- endmacro %}
{%- macro source_info(type,url) %}
{%- macro source_type(type) %}
{% if type == 1 %}
<span class="layui-badge">原创</span>
原创
{% elseif type == 2 %}
<a class="layui-badge layui-bg-blue" href="{{ url }}" target="_blank">转载</a>
转载
{% elseif type == 3 %}
<span class="layui-badge layui-bg-gray">翻译</span>
翻译
{% else %}
N/A
{% endif %}

View File

@ -33,8 +33,9 @@
{% set review_url = url({'for':'admin.article.review','id':item.id}) %}
<tr>
<td>
<p>标题:{{ item.title }} {{ source_info(item.source_type,item.source_url) }}</p>
<p>标题:{{ item.title }}</p>
<p class="meta">
<span>来源:{{ source_type(item.source_type) }}</span>
{% if item.tags %}
<span>标签:{{ tags_info(item.tags) }}</span>
{% endif %}

View File

@ -2,17 +2,23 @@
{% block content %}
{% set owner_url = url({'for':'home.user.show','id':question.owner.id}) %}
<fieldset class="layui-elem-field layui-field-title">
<legend>审核内容</legend>
</fieldset>
<div class="kg-mod-preview">
<div class="title">{{ question.title }}</div>
<div class="meta">
<span><a href="{{ owner_url }}" target="_blank">{{ question.owner.name }}</a></span>
<span>{{ date('Y-m-d H:i',question.create_time) }}</span>
</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>
<span class="layui-btn layui-btn-xs">{{ item.name }}</span>
{% endfor %}
</div>
{% endif %}

View File

@ -57,8 +57,11 @@ class AnswerController extends Controller
$this->seo->prependTitle('编辑回答');
$referer = $this->request->getHTTPReferer();
$this->view->setVar('question', $question);
$this->view->setVar('answer', $answer);
$this->view->setVar('referer', $referer);
}
/**
@ -102,12 +105,13 @@ class AnswerController extends Controller
{
$service = new AnswerUpdateService();
$answer = $service->handle($id);
$service->handle($id);
$location = $this->url->get([
'for' => 'home.question.show',
'id' => $answer->question_id,
]);
$location = $this->request->getPost('referer');
if (empty($location)) {
$location = $this->url->get(['for' => 'home.uc.answers']);
}
$content = [
'location' => $location,
@ -124,12 +128,9 @@ class AnswerController extends Controller
{
$service = new AnswerDeleteService();
$answer = $service->handle($id);
$service->handle($id);
$location = $this->url->get([
'for' => 'home.question.show',
'id' => $answer->question_id,
]);
$location = $this->request->getHTTPReferer();
$content = [
'location' => $location,

View File

@ -11,7 +11,6 @@ use App\Services\Logic\Article\ArticleInfo as ArticleInfoService;
use App\Services\Logic\Article\ArticleLike as ArticleLikeService;
use App\Services\Logic\Article\ArticleList as ArticleListService;
use App\Services\Logic\Article\ArticleUpdate as ArticleUpdateService;
use App\Services\Logic\Article\HotAuthorList as HotAuthorListService;
use App\Services\Logic\Article\RelatedArticleList as RelatedArticleListService;
use Phalcon\Mvc\View;
@ -28,13 +27,11 @@ class ArticleController extends Controller
{
$service = new ArticleQueryService();
$categories = $service->handleCategories();
$sorts = $service->handleSorts();
$params = $service->getParams();
$this->seo->prependTitle('专栏');
$this->view->setVar('categories', $categories);
$this->view->setVar('sorts', $sorts);
$this->view->setVar('params', $params);
}
@ -54,20 +51,6 @@ class ArticleController extends Controller
$this->view->setVar('pager', $pager);
}
/**
* @Get("/hot/authors", name="home.article.hot_authors")
*/
public function hotAuthorsAction()
{
$service = new HotAuthorListService();
$authors = $service->handle();
$this->view->setRenderLevel(View::LEVEL_ACTION_VIEW);
$this->view->pick('article/hot_authors');
$this->view->setVar('authors', $authors);
}
/**
* @Get("/add", name="home.article.add")
*/
@ -76,7 +59,6 @@ class ArticleController extends Controller
$service = new ArticleService();
$sourceTypes = $service->getSourceTypes();
$categories = $service->getCategories();
$article = $service->getArticleModel();
$xmTags = $service->getXmTags(0);
@ -84,7 +66,6 @@ class ArticleController extends Controller
$this->view->pick('article/edit');
$this->view->setVar('source_types', $sourceTypes);
$this->view->setVar('categories', $categories);
$this->view->setVar('article', $article);
$this->view->setVar('xm_tags', $xmTags);
}
@ -97,14 +78,15 @@ class ArticleController extends Controller
$service = new ArticleService();
$sourceTypes = $service->getSourceTypes();
$categories = $service->getCategories();
$article = $service->getArticle($id);
$xmTags = $service->getXmTags($id);
$this->seo->prependTitle('编辑文章');
$referer = $this->request->getHTTPReferer();
$this->view->setVar('source_types', $sourceTypes);
$this->view->setVar('categories', $categories);
$this->view->setVar('referer', $referer);
$this->view->setVar('article', $article);
$this->view->setVar('xm_tags', $xmTags);
}
@ -150,9 +132,12 @@ class ArticleController extends Controller
{
$service = new ArticleCreateService();
$service->handle();
$article = $service->handle();
$location = $this->url->get(['for' => 'home.uc.articles']);
$location = $this->url->get([
'for' => 'home.article.show',
'id' => $article->id,
]);
$content = [
'location' => $location,
@ -171,7 +156,11 @@ class ArticleController extends Controller
$service->handle($id);
$location = $this->request->getPost('referer');
if (empty($location)) {
$location = $this->url->get(['for' => 'home.uc.articles']);
}
$content = [
'location' => $location,
@ -190,7 +179,7 @@ class ArticleController extends Controller
$service->handle($id);
$location = $this->url->get(['for' => 'home.uc.articles']);
$location = $this->request->getHTTPReferer();
$content = [
'location' => $location,

View File

@ -5,7 +5,6 @@ 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;
@ -14,7 +13,6 @@ 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;
/**
@ -23,34 +21,6 @@ use Phalcon\Mvc\View;
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")
*/
@ -113,6 +83,9 @@ class QuestionController extends Controller
$this->seo->prependTitle('编辑问题');
$referer = $this->request->getHTTPReferer();
$this->view->setVar('referer', $referer);
$this->view->setVar('question', $question);
$this->view->setVar('xm_tags', $xmTags);
}
@ -172,9 +145,12 @@ class QuestionController extends Controller
{
$service = new QuestionCreateService();
$service->handle();
$question = $service->handle();
$location = $this->url->get(['for' => 'home.uc.questions']);
$location = $this->url->get([
'for' => 'home.question.show',
'id' => $question->id,
]);
$content = [
'location' => $location,
@ -193,7 +169,11 @@ class QuestionController extends Controller
$service->handle($id);
$location = $this->request->getPost('referer');
if (empty($location)) {
$location = $this->url->get(['for' => 'home.uc.questions']);
}
$content = [
'location' => $location,
@ -212,7 +192,7 @@ class QuestionController extends Controller
$service->handle($id);
$location = $this->url->get(['for' => 'home.uc.questions']);
$location = $this->request->getHTTPReferer();
$content = [
'location' => $location,

View File

@ -2,6 +2,7 @@
namespace App\Http\Home\Controllers;
use App\Services\Logic\Article\TopAuthorList as TopAuthorListService;
use App\Services\Logic\Question\HotQuestionList as HotQuestionListService;
use App\Services\Logic\Question\TopAnswererList as TopAnswererListService;
use App\Services\Logic\Tag\FollowList as FollowListService;
@ -41,6 +42,20 @@ class WidgetController extends Controller
$this->view->setVar('questions', $questions);
}
/**
* @Get("/top/authors", name="home.widget.top_authors")
*/
public function topAuthorsAction()
{
$service = new TopAuthorListService();
$authors = $service->handle();
$this->view->setRenderLevel(View::LEVEL_ACTION_VIEW);
$this->view->pick('widget/top_authors');
$this->view->setVar('authors', $authors);
}
/**
* @Get("/top/answerers", name="home.widget.top_answerers")
*/

View File

@ -26,6 +26,7 @@
<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="referer" value="{{ referer }}">
</div>
</div>
</form>

View File

@ -31,6 +31,7 @@
<div class="layui-form-item center">
<div class="layui-input-block">
<button class="layui-btn kg-submit" lay-submit="true" lay-filter="go">发布文章</button>
<input type="hidden" name="referer" value="{{ referer }}">
</div>
</div>
</div>

View File

@ -4,7 +4,7 @@
{% set sort_val = request.get('sort','trim','latest') %}
{% set pager_url = url({'for':'home.article.pager'}, params) %}
{% set hot_authors_url = url({'for':'home.article.hot_authors'}) %}
{% set top_authors_url = url({'for':'home.widget.top_authors'}) %}
{% set my_tags_url = url({'for':'home.widget.my_tags'},{'type':'article'}) %}
<div class="breadcrumb">
@ -36,7 +36,7 @@
{% 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 class="sidebar" id="sidebar-top-authors" data-url="{{ top_authors_url }}"></div>
</div>
</div>

View File

@ -33,7 +33,10 @@
<div class="title">{{ article.title }}</div>
<div class="meta">
<div class="left">
<span class="source layui-badge layui-bg-green">{{ source_type(article.source_type) }}</span>
{% if article.published == 1 %}
<span class="review layui-badge">审核中</span>
{% endif %}
<span class="source layui-badge layui-bg-blue">{{ source_type(article.source_type) }}</span>
<span class="owner"><a href="{{ article_owner_url }}">{{ article.owner.name }}</a></span>
<span class="time">{{ article.create_time|time_ago }}</span>
<span class="view">{{ article.view_count }} 阅读</span>

View File

@ -1,17 +0,0 @@
<div class="layui-card">
<div class="layui-card-header">答题指南</div>
<div class="layui-card-body">
<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>
</div>

View File

@ -5,15 +5,14 @@
{% set report_url = url({'for':'home.answer.report','id':item.id}) %}
{% set edit_url = url({'for':'home.answer.edit','id':item.id}) %}
{% set delete_url = url({'for':'home.answer.delete','id':item.id}) %}
<div class="comment-card answer-card" id="answer-{{ item.id }}">
<div class="avatar">
<div class="answer-card" id="answer-{{ item.id }}">
<div class="header">
<span class="avatar">
<a href="{{ owner_url }}" title="{{ item.owner.name }}" target="_blank">
<img src="{{ item.owner.avatar }}!avatar_160" alt="{{ item.owner.name }}">
</a>
</div>
<div class="info">
<div class="user">
<a href="{{ owner_url }}" target="_blank">{{ item.owner.name }}</a>
</span>
<span class="name">{{ item.owner.name }}</span>
</div>
<div class="content markdown-body">{{ item.content }}</div>
<div class="footer">
@ -45,7 +44,6 @@
</div>
</div>
</div>
</div>
{% endfor %}
{{ partial('partials/pager_ajax') }}
{% endif %}

View File

@ -30,6 +30,7 @@
<div class="layui-form-item center">
<div class="layui-input-block">
<button class="layui-btn kg-submit" lay-submit="true" lay-filter="go">发布问题</button>
<input type="hidden" name="referer" value="{{ referer }}">
</div>
</div>
</div>

View File

@ -4,8 +4,8 @@
{% set sort_val = request.get('sort','trim','latest') %}
{% set pager_url = url({'for':'home.question.pager'}, params) %}
{% set hot_questions_url = url({'for':'home.question.hot_questions'}) %}
{% set top_answerers_url = url({'for':'home.question.top_answerers'}) %}
{% set hot_questions_url = url({'for':'home.widget.hot_questions'}) %}
{% set top_answerers_url = url({'for':'home.widget.top_answerers'}) %}
{% set my_tags_url = url({'for':'home.widget.my_tags'},{'type':'question'}) %}
<div class="breadcrumb">

View File

@ -35,6 +35,9 @@
<div class="title">{{ question.title }}</div>
<div class="meta">
<div class="left">
{% if question.published == 1 %}
<span class="review layui-badge">审核中</span>
{% endif %}
<span class="owner"><a href="{{ question_owner_url }}">{{ question.owner.name }}</a></span>
<span class="time">{{ question.create_time|time_ago }}</span>
<span class="view">{{ question.view_count }} 阅读</span>
@ -59,11 +62,11 @@
{% endif %}
</div>
<div id="answer-anchor"></div>
<div class="answer-wrap wrap center">
{% if question.me.answered == 0 %}
<div class="answer-wrap wrap">
<button class="layui-btn layui-btn-fluid btn-answer" data-url="{{ answer_add_url }}">回答问题</button>
{% endif %}
</div>
{% endif %}
{% if question.answer_count > 0 %}
<div class="answer-wrap wrap">
<div class="layui-tab layui-tab-brief search-tab">

View File

@ -25,17 +25,13 @@
<col>
<col>
<col>
<col>
<col>
<col>
<col width="15%">
</colgroup>
<thead>
<tr>
<th>文章</th>
<th>浏览</th>
<th>点赞</th>
<th>评论</th>
<th>收藏</th>
<th>浏览</th>
<th>操作</th>
</tr>
</thead>
@ -48,18 +44,15 @@
<td>
<p>
标题:<a href="{{ show_url }}" target="_blank">{{ item.title }}</a>
<span>{{ item.create_time|time_ago }}</span>
</p>
<p class="meta">
来源:<span class="layui-badge layui-bg-gray">{{ source_type(item.source_type) }}</span>
分类<span class="layui-badge layui-bg-gray">{{ item.category.name }}</span>
时间<span class="layui-badge layui-bg-gray">{{ item.create_time|time_ago }}</span>
状态:<span class="layui-badge layui-bg-gray">{{ publish_status(item.published) }}</span>
</p>
</td>
<td>{{ item.view_count }}</td>
<td>{{ item.like_count }}</td>
<td>{{ item.comment_count }}</td>
<td>{{ item.favorite_count }}</td>
<td>{{ item.view_count }}</td>
<td class="center">
<a href="{{ edit_url }}" class="layui-btn layui-btn-xs">编辑</a>
<a href="javascript:" class="layui-btn layui-btn-xs layui-bg-red kg-delete" data-url="{{ delete_url }}">删除</a>

View File

@ -4,15 +4,13 @@
<col>
<col>
<col>
<col>
<col width="12%">
</colgroup>
<thead>
<tr>
<th>文章</th>
<th>浏览</th>
<th>点赞</th>
<th>评论</th>
<th>浏览</th>
<th>操作</th>
</tr>
</thead>
@ -22,9 +20,8 @@
{% set favorite_url = url({'for':'home.article.favorite','id':item.id}) %}
<tr>
<td><a href="{{ article_url }}" target="_blank">{{ item.title }}</a></td>
<td>{{ item.view_count }}</td>
<td>{{ item.like_count }}</td>
<td>{{ item.comment_count }}</td>
<td>{{ item.view_count }}</td>
<td class="center">
<button class="layui-btn layui-btn-sm layui-bg-red kg-delete" data-tips="确定要取消收藏吗?" data-url="{{ favorite_url }}">取消</button>
</td>

View File

@ -4,15 +4,13 @@
<col>
<col>
<col>
<col>
<col width="12%">
</colgroup>
<thead>
<tr>
<th>问题</th>
<th>浏览</th>
<th>点赞</th>
<th>回答</th>
<th>浏览</th>
<th>操作</th>
</tr>
</thead>
@ -22,9 +20,8 @@
{% set favorite_url = url({'for':'home.question.favorite','id':item.id}) %}
<tr>
<td><a href="{{ question_url }}" target="_blank">{{ item.title }}</a></td>
<td>{{ item.view_count }}</td>
<td>{{ item.like_count }}</td>
<td>{{ item.answer_count }}</td>
<td>{{ item.view_count }}</td>
<td class="center">
<button class="layui-btn layui-btn-sm layui-bg-red kg-delete" data-tips="确定要取消收藏吗?" data-url="{{ favorite_url }}">取消</button>
</td>

View File

@ -29,7 +29,7 @@
</div>
<div class="layui-card">
<div class="layui-card-header">创作中心</div>
<div class="layui-card-header">内容中心</div>
<div class="layui-card-body">
<ul class="my-menu">
<li><a href="{{ url({'for':'home.uc.articles'}) }}">我的文章</a></li>

View File

@ -26,16 +26,12 @@
<col>
<col>
<col>
<col>
<col>
</colgroup>
<thead>
<tr>
<th>问题</th>
<th>回答</th>
<th>浏览</th>
<th>点赞</th>
<th>收藏</th>
<th>操作</th>
</tr>
</thead>
@ -54,8 +50,6 @@
</td>
<td>{{ item.answer_count }}</td>
<td>{{ item.view_count }}</td>
<td>{{ item.like_count }}</td>
<td>{{ item.favorite_count }}</td>
<td class="center">
<a href="{{ edit_url }}" class="layui-btn layui-btn-xs">编辑</a>
<a href="javascript:" class="layui-btn layui-btn-xs layui-bg-red kg-delete" data-url="{{ delete_url }}">删除</a>

View File

@ -29,6 +29,7 @@ class AnswerInfo extends LogicService
'content' => $answer->content,
'anonymous' => $answer->anonymous,
'accepted' => $answer->accepted,
'published' => $answer->published,
'like_count' => $answer->like_count,
'create_time' => $answer->create_time,
'update_time' => $answer->update_time,

View File

@ -52,6 +52,7 @@ class ArticleInfo extends LogicService
'me' => $me,
'private' => $article->private,
'closed' => $article->closed,
'published' => $article->published,
'source_type' => $article->source_type,
'source_url' => $article->source_url,
'word_count' => $article->word_count,

View File

@ -2,15 +2,15 @@
namespace App\Services\Logic\Article;
use App\Caches\ArticleHotAuthorList as ArticleHotAuthorListCache;
use App\Caches\TopAuthorList as TopAuthorListCache;
use App\Services\Logic\Service as LogicService;
class HotAuthorList extends LogicService
class TopAuthorList extends LogicService
{
public function handle()
{
$cache = new ArticleHotAuthorListCache();
$cache = new TopAuthorListCache();
$result = $cache->get();

View File

@ -52,6 +52,7 @@ class QuestionInfo extends LogicService
'anonymous' => $question->anonymous,
'solved' => $question->solved,
'closed' => $question->closed,
'published' => $question->published,
'view_count' => $question->view_count,
'like_count' => $question->like_count,
'answer_count' => $question->answer_count,

View File

@ -14,6 +14,7 @@ final class V20210430023157 extends AbstractMigration
$this->createQuestionLikeTable();
$this->createAnswerTable();
$this->createAnswerLikeTable();
$this->createTagFollowTable();
$this->createReportTable();
$this->modifyUserTable();
$this->modifyArticleTable();
@ -34,6 +35,7 @@ final class V20210430023157 extends AbstractMigration
$this->table('kg_question_like')->drop()->save();
$this->table('kg_answer')->drop()->save();
$this->table('kg_answer_like')->drop()->save();
$this->table('kg_tag_follow')->drop()->save();
$this->table('kg_report')->drop()->save();
$this->table('kg_user')
@ -731,6 +733,59 @@ final class V20210430023157 extends AbstractMigration
->create();
}
protected function createTagFollowTable()
{
$this->table('kg_tag_follow', [
'id' => false,
'primary_key' => ['id'],
'engine' => 'InnoDB',
'encoding' => 'utf8mb4',
'collation' => 'utf8mb4_general_ci',
'comment' => '',
'row_format' => 'COMPACT',
])
->addColumn('id', 'integer', [
'null' => false,
'limit' => MysqlAdapter::INT_REGULAR,
'signed' => false,
'identity' => 'enable',
'comment' => '主键编号',
])
->addColumn('tag_id', 'integer', [
'null' => false,
'default' => '0',
'limit' => MysqlAdapter::INT_REGULAR,
'signed' => false,
'comment' => '标签编号',
'after' => 'id',
])
->addColumn('user_id', 'integer', [
'null' => false,
'default' => '0',
'limit' => MysqlAdapter::INT_REGULAR,
'signed' => false,
'comment' => '标签编号',
'after' => 'tag_id',
])
->addColumn('create_time', 'integer', [
'null' => false,
'default' => '0',
'limit' => MysqlAdapter::INT_REGULAR,
'signed' => false,
'comment' => '创建时间',
'after' => 'user_id',
])
->addIndex(['user_id'], [
'name' => 'user_id',
'unique' => false,
])
->addIndex(['tag_id'], [
'name' => 'tag_id',
'unique' => false,
])
->create();
}
protected function createReportTable()
{
$this->table('kg_report', [
@ -874,6 +929,12 @@ final class V20210430023157 extends AbstractMigration
{
$this->table('kg_article')
->renameColumn('allow_comment', 'closed')
->addColumn('score', 'float', [
'null' => false,
'default' => '0.00',
'comment' => '综合得分',
'after' => 'content',
])
->addColumn('report_count', 'integer', [
'null' => false,
'default' => '0',

View File

@ -360,6 +360,10 @@ img.kg-qrcode {
margin-bottom: 20px;
}
.kg-mod-preview .source-tips {
margin-bottom: 20px;
}
.kg-review-form {
width: 50%;
}

View File

@ -717,7 +717,8 @@
color: #666;
}
.comment-card .footer {
.comment-card .footer,
.answer-card .footer {
display: flex;
justify-content: space-between;
line-height: 1.2em;
@ -725,32 +726,71 @@
}
.comment-card .footer .left,
.comment-card .footer .right {
.comment-card .footer .right,
.answer-card .footer .left,
.answer-card .footer .right {
display: flex;
}
.comment-card .footer .left .column {
.comment-card .footer .left .column,
.answer-card .footer .left .column {
margin-right: 10px;
}
.comment-card .footer .right .column {
.comment-card .footer .right .column,
.answer-card .footer .right .column {
margin-left: 10px;
}
.comment-card .footer .action:hover {
.comment-card .footer .action:hover,
.answer-card .footer .action:hover {
background-color: #393D49;
cursor: pointer;
color: white;
}
.comment-card .footer .right .action {
.comment-card .footer .right .action,
.answer-card .footer .right .action {
display: none;
}
.comment-card .info:hover .right .action {
.comment-card .info:hover .right .action,
.answer-card:hover .right .action {
display: inline;
}
.answer-card {
padding-bottom: 20px;
margin-bottom: 20px;
border-bottom: 1px solid #f6f6f6;
}
.answer-card:last-child {
padding-bottom: 0;
margin-bottom: 0;
border-bottom: none;
}
.answer-card .header {
line-height: 32px;
margin-bottom: 20px;
}
.answer-card .header span {
margin-right: 10px;
}
.answer-card .avatar img {
width: 32px;
height: 32px;
border-radius: 100%;
}
.answer-card .content {
margin-bottom: 20px;
overflow-wrap: break-word;
}
.reply-list {
margin-left: 48px;
}

View File

@ -4,7 +4,7 @@ layui.use(['jquery', 'helper'], function () {
var helper = layui.helper;
var $articleList = $('#article-list');
var $sidebarHotAuthors = $('#sidebar-hot-authors');
var $sidebarTopAuthors = $('#sidebar-top-authors');
var $sidebarMyTags = $('#sidebar-my-tags');
if ($articleList.length > 0) {
@ -15,8 +15,8 @@ layui.use(['jquery', 'helper'], function () {
helper.ajaxLoadHtml($sidebarMyTags.data('url'), $sidebarMyTags.attr('id'));
}
if ($sidebarHotAuthors.length > 0) {
helper.ajaxLoadHtml($sidebarHotAuthors.data('url'), $sidebarHotAuthors.attr('id'));
if ($sidebarTopAuthors.length > 0) {
helper.ajaxLoadHtml($sidebarTopAuthors.data('url'), $sidebarTopAuthors.attr('id'));
}
});