1
0
mirror of https://gitee.com/koogua/course-tencent-cloud.git synced 2025-06-17 15:55:31 +08:00

Merge branch 'koogua/v1.4.0'

This commit is contained in:
koogua 2021-08-03 17:25:00 +08:00
commit 2102ee8e4a
43 changed files with 377 additions and 140 deletions

View File

@ -1,3 +1,21 @@
### [v1.4.0](https://gitee.com/koogua/course-tencent-cloud/releases/v1.4.0)(2021-08-03)
### 更新
- 单页增加自定义别名访问
- 回答增加评论功能
- 顶部导航微聊增加开关控制
- 更新默认的ICP备案链接指向
- 更正部分model定义中字段的类型申明
- 优化章节过多导致页面过长问题
- 优化评论前端部分相关逻辑和交互
- 优化403错误页面使用forward代替redirect
- 优化播放地址中带queryString的扩展名检查
- 修正解除第三登录绑定500错误问题
- 修正教师教授课程未过滤已删除课程问题
- 修正咨询编辑500错误问题
- 修正后台列表中restore_url未定义问题
### [v1.3.9](https://gitee.com/koogua/course-tencent-cloud/releases/v1.3.9)(2021-07-24)
### 更新

View File

@ -44,7 +44,7 @@ class CourseUserList extends Builder
$columns = [
'id', 'title', 'cover',
'market_price', 'vip_price',
'rating', 'model', 'level', 'attrs',
'rating', 'model', 'level', 'attrs', 'published', 'deleted',
'user_count', 'lesson_count', 'review_count', 'favorite_count',
];
@ -55,9 +55,11 @@ class CourseUserList extends Builder
$result = [];
foreach ($courses->toArray() as $course) {
$course['cover'] = $baseUrl . $course['cover'];
$course['attrs'] = json_decode($course['attrs'], true);
$result[$course['id']] = $course;
if ($course['deleted'] == 0) {
$course['cover'] = $baseUrl . $course['cover'];
$course['attrs'] = json_decode($course['attrs'], true);
$result[$course['id']] = $course;
}
}
return $result;

View File

@ -72,6 +72,16 @@ class Page extends Service
$data['title'] = $validator->checkTitle($post['title']);
}
if (isset($post['alias'])) {
$data['alias'] = '';
if (!empty($post['alias'])) {
$data['alias'] = $validator->checkAlias($post['alias']);
if ($data['alias'] != $page->alias) {
$validator->checkIfAliasTaken($data['alias']);
}
}
}
if (isset($post['content'])) {
$data['content'] = $validator->checkContent($post['content']);
}

View File

@ -12,6 +12,12 @@
<input class="layui-input" type="text" name="title" value="{{ page.title }}" lay-verify="required">
</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="alias" value="{{ page.alias }}" placeholder="可以通过 /page/{别名} 访问页面">
</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">内容</label>
<div class="layui-input-block">

View File

@ -2,6 +2,14 @@
{% block content %}
{%- macro alias_tips(alias) %}
{% if alias %}
<a href="javascript:" title="可通过 /page/{{ alias }} 访问页面">{{ alias }}</a>
{% else %}
N/A
{% endif %}
{%- endmacro %}
{% set add_url = url({'for':'admin.page.add'}) %}
<div class="kg-nav">
@ -24,12 +32,14 @@
<col>
<col>
<col>
<col>
<col width="12%">
</colgroup>
<thead>
<tr>
<th>编号</th>
<th>标题</th>
<th>别名</th>
<th>创建时间</th>
<th>更新时间</th>
<th>发布</th>
@ -42,9 +52,11 @@
{% set edit_url = url({'for':'admin.page.edit','id':item.id}) %}
{% set update_url = url({'for':'admin.page.update','id':item.id}) %}
{% set delete_url = url({'for':'admin.page.delete','id':item.id}) %}
{% set restore_url = url({'for':'admin.page.restore','id':item.id}) %}
<tr>
<td>{{ item.id }}</td>
<td><a href="{{ edit_url }}">{{ item.title }}</a></td>
<td>{{ alias_tips(item.alias) }}</td>
<td>{{ date('Y-m-d H:i:s',item.create_time) }}</td>
<td>{{ date('Y-m-d H:i:s',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 %}>

View File

@ -51,6 +51,7 @@
{% set edit_url = url({'for':'admin.tag.edit','id':item.id}) %}
{% set update_url = url({'for':'admin.tag.update','id':item.id}) %}
{% set delete_url = url({'for':'admin.tag.delete','id':item.id}) %}
{% set restore_url = url({'for':'admin.tag.restore','id':item.id}) %}
<tr>
<td>{{ item.id }}</td>
<td><img class="kg-icon" src="{{ item.icon }}" alt="{{ item.name }}"></td>

View File

@ -61,7 +61,7 @@ class AnswerController extends Controller
}
if ($answer['me']['owned'] == 0) {
$this->response->redirect(['for' => 'home.error.403']);
return $this->forbidden();
}
$questionId = $answer['question']['id'];

View File

@ -107,7 +107,7 @@ class ArticleController extends Controller
}
if ($article['me']['owned'] == 0) {
$this->response->redirect(['for' => 'home.error.403']);
return $this->forbidden();
}
$this->seo->prependTitle(['专栏', $article['title']]);

View File

@ -17,7 +17,7 @@ class PageController extends Controller
{
/**
* @Get("/{id:[0-9]+}", name="home.page.show")
* @Get("/{id}", name="home.page.show")
*/
public function showAction($id)
{
@ -29,10 +29,6 @@ class PageController extends Controller
return $this->notFound();
}
if ($page['me']['owned'] == 0) {
$this->response->redirect(['for' => 'home.error.403']);
}
$featuredCourses = $this->getFeaturedCourses();
$this->seo->prependTitle(['单页', $page['title']]);

View File

@ -106,7 +106,7 @@ class QuestionController extends Controller
}
if ($question['me']['owned'] == 0) {
$this->response->redirect(['for' => 'home.error.403']);
return $this->forbidden();
}
$this->seo->prependTitle(['问答', $question['title']]);

View File

@ -26,6 +26,10 @@ class TopicController extends Controller
$topic = $service->handle($id);
if ($topic['deleted'] == 1) {
return $this->notFound();
}
$this->seo->prependTitle(['专题', $topic['title']]);
$this->seo->setDescription($topic['summary']);

View File

@ -10,7 +10,7 @@
<div class="toolbar"></div>
<div class="action">
<button class="{{ submit_class }}" lay-submit="true" lay-filter="add_comment">发布</button>
<button class="layui-btn layui-btn-sm layui-btn-primary" id="btn-cancel-comment" type="button">取消</button>
<button class="layui-btn layui-btn-sm layui-btn-primary" id="comment-cancel" type="button">取消</button>
</div>
</div>
<input type="hidden" name="item_id" value="{{ article.id }}">

View File

@ -9,7 +9,7 @@
<div class="toolbar"></div>
<div class="action">
<button class="layui-btn layui-btn-sm" lay-submit="true" lay-filter="add_comment">发布</button>
<button class="layui-btn layui-btn-sm layui-btn-primary" id="btn-cancel-comment" type="button">取消</button>
<button class="layui-btn layui-btn-sm layui-btn-primary" id="comment-cancel" type="button">取消</button>
</div>
</div>
<input type="hidden" name="item_id" value="{{ chapter.id }}">

View File

@ -20,9 +20,6 @@
<div class="content">{{ item.content }}</div>
<div class="footer">
<div class="left">
<div class="column">
<span class="time">{{ item.create_time|time_ago }}</span>
</div>
<div class="column">
<span class="like-count" data-count="{{ item.like_count }}">{{ item.like_count }}</span>
{% if item.me.liked == 1 %}
@ -35,6 +32,9 @@
<span class="reply-count" data-count="{{ item.reply_count }}">{{ item.reply_count }}</span>
<span class="action comment-toggle" title="展开回应" data-id="{{ item.id }}" data-url="{{ reply_list_url }}">回应</span>
</div>
<div class="column">
<span class="time">{{ item.create_time|time_ago }}</span>
</div>
</div>
<div class="right">
<div class="column">
@ -59,12 +59,12 @@
<div class="toolbar"></div>
<div class="action">
<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-btn-primary btn-cancel-reply" type="button" data-id="{{ item.id }}">取消</button>
<button class="layui-btn layui-btn-sm layui-btn-primary reply-cancel" type="button" data-id="{{ item.id }}">取消</button>
</div>
</div>
</form>
</div>
<div class="reply-list" id="reply-list-{{ item.id }}" style="display:none"></div>
<div class="reply-list" id="reply-list-{{ item.id }}" style="display:none;"></div>
</div>
{% endfor %}
{{ partial('partials/pager_ajax') }}

View File

@ -24,9 +24,6 @@
<div class="content">{{ item.content }}</div>
<div class="footer">
<div class="left">
<div class="column">
<span class="time">{{ item.create_time|time_ago }}</span>
</div>
<div class="column">
<span class="like-count" data-count="{{ item.like_count }}">{{ item.like_count }}</span>
{% if item.me.liked == 1 %}
@ -35,6 +32,9 @@
<span class="action comment-like" title="点赞支持" data-url="{{ like_url }}">点赞</span>
{% endif %}
</div>
<div class="column">
<span class="time">{{ item.create_time|time_ago }}</span>
</div>
</div>
<div class="right">
<div class="column">
@ -59,7 +59,7 @@
<div class="toolbar"></div>
<div class="action">
<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-btn-primary btn-cancel-reply" type="button" data-id="{{ item.id }}">取消</button>
<button class="layui-btn layui-btn-sm layui-btn-primary reply-cancel" type="button" data-id="{{ item.id }}">取消</button>
</div>
</div>
</form>

View File

@ -5,14 +5,22 @@
{% set update_url = url({'for':'home.consult.update','id':consult.id}) %}
<form class="layui-form consult-form" method="post" action="{{ update_url }}">
<div class="layui-form-item mb0">
<label class="layui-form-label">课程</label>
<div class="layui-form-mid">{{ consult.course.title }}</div>
</div>
<div class="layui-form-item">
<label class="layui-form-label">章节</label>
<div class="layui-form-mid">{{ consult.chapter.title }}</div>
</div>
{% if consult.course.id is defined %}
<div class="layui-form-item">
<label class="layui-form-label">课程</label>
<div class="layui-input-block">
<input class="layui-input" type="text" value="{{ consult.course.title }}" disabled="disabled">
</div>
</div>
{% endif %}
{% if consult.chapter.id is defined %}
<div class="layui-form-item">
<label class="layui-form-label">章节</label>
<div class="layui-input-block">
<input class="layui-input" type="text" value="{{ consult.chapter.title }}" disabled="disabled">
</div>
</div>
{% endif %}
<div class="layui-form-item">
<label class="layui-form-label" for="answer">咨询</label>
<div class="layui-input-block">

View File

@ -76,12 +76,15 @@
{% endif %}
{%- endmacro %}
{% set show_all = course.lesson_count < 30 %}
{% if chapters %}
<div class="layui-collapse">
<div class="layui-collapse" lay-accordion="true">
{% for chapter in chapters %}
{% set show_class = (show_all or loop.first) ? 'layui-show' : '' %}
<div class="layui-colla-item">
<h2 class="layui-colla-title">{{ chapter.title }}</h2>
<div class="layui-colla-content layui-show">
<div class="layui-colla-content {{ show_class }}">
<ul class="lesson-list">
{% for lesson in chapter.children %}
{% if lesson.model == 1 %}

View File

@ -13,6 +13,8 @@
{%- macro answer_card(item,auth_user) %}
{% set show_url = full_url({'for':'home.answer.show','id':item.id}) %}
{% set owner_url = url({'for':'home.user.show','id':item.owner.id}) %}
{% set comment_create_url = url({'for':'home.comment.create'}) %}
{% set comment_list_url = url({'for':'home.comment.list'},{'item_id':item.id,'item_type':4}) %}
{% set report_url = url({'for':'home.report.add'},{'item_id':item.id,'item_type':108}) %}
{% set like_url = url({'for':'home.answer.like','id':item.id}) %}
{% set edit_url = url({'for':'home.answer.edit','id':item.id}) %}
@ -31,9 +33,6 @@
<div class="content markdown-body">{{ item.content }}</div>
<div class="footer">
<div class="left">
<div class="column">
<span class="time" title="{{ date('Y-m-d H:i',item.create_time) }}">{{ item.create_time|time_ago }}</span>
</div>
<div class="column">
<span class="like-count" data-count="{{ item.like_count }}">{{ item.like_count }}</span>
{% if item.me.liked == 1 %}
@ -42,8 +41,18 @@
<span class="action answer-like" title="点赞支持" data-url="{{ like_url }}">点赞</span>
{% endif %}
</div>
<div class="column">
<span class="comment-count" data-count="{{ item.comment_count }}">{{ item.comment_count }}</span>
<span class="action answer-comment-toggle" title="展开回应" data-id="{{ item.id }}" data-url="{{ comment_list_url }}">回应</span>
</div>
<div class="column">
<span class="time" title="{{ date('Y-m-d H:i',item.create_time) }}">{{ item.create_time|time_ago }}</span>
</div>
</div>
<div class="right">
<div class="column">
<span class="action answer-comment" data-id="{{ item.id }}">回复</span>
</div>
<div class="column">
<span class="action kg-copy" title="复制链接" data-clipboard-text="{{ show_url }}">链接</span>
</div>
@ -60,5 +69,20 @@
{% endif %}
</div>
</div>
<div class="comment-form" id="answer-comment-form-{{ item.id }}" style="display:none;">
<form class="layui-form" method="post" action="{{ comment_create_url }}">
<textarea class="layui-textarea" name="content" placeholder="撰写评论..." lay-verify="required"></textarea>
<div class="footer">
<div class="toolbar"></div>
<div class="action">
<button class="layui-btn layui-btn-sm" data-answer-id="{{ item.id }}" lay-submit="true" lay-filter="comment_answer">发布</button>
<button class="layui-btn layui-btn-sm layui-btn-primary answer-comment-cancel" type="button" data-id="{{ item.id }}">取消</button>
</div>
</div>
<input type="hidden" name="item_id" value="{{ item.id }}">
<input type="hidden" name="item_type" value="4">
</form>
</div>
<div class="comment-list" id="answer-comment-list-{{ item.id }}" data-url="{{ comment_list_url }}"></div>
</div>
{%- endmacro %}

View File

@ -35,9 +35,11 @@
<li class="layui-nav-item">
<a href="{{ url({'for':'home.vip.index'}) }}" class="nav-vip"><i class="layui-icon layui-icon-diamond"></i> 会员</a>
</li>
<li class="layui-nav-item">
<a href="{{ url({'for':'home.im.index'}) }}" class="nav-im" target="im"><i class="layui-icon layui-icon-chat"></i> 微聊</a>
</li>
{% if im_info.cs.enabled == 1 %}
<li class="layui-nav-item">
<a href="{{ url({'for':'home.im.index'}) }}" class="nav-im" target="im"><i class="layui-icon layui-icon-chat"></i> 微聊</a>
</li>
{% endif %}
{% if auth_user.id > 0 %}
<li class="layui-nav-item">
<a href="javascript:">创建</a>

View File

@ -116,6 +116,7 @@
{{ js_include('home/js/question.show.js') }}
{{ js_include('home/js/question.share.js') }}
{{ js_include('home/js/answer.js') }}
{{ js_include('home/js/comment.js') }}
{{ js_include('home/js/copy.js') }}
{% endblock %}

View File

@ -16,7 +16,7 @@ class AppInfo
protected $link = 'https://koogua.com';
protected $version = '1.3.9';
protected $version = '1.4.0';
public function __get($name)
{

View File

@ -100,7 +100,7 @@ class Answer extends Model
/**
* 终端IP
*
* @var integer
* @var string
*/
public $client_ip = '';

View File

@ -109,7 +109,7 @@ class Article extends Model
/**
* 终端IP
*
* @var integer
* @var string
*/
public $client_ip = '';

View File

@ -87,7 +87,7 @@ class Comment extends Model
/**
* 终端IP
*
* @var integer
* @var string
*/
public $client_ip = '';

View File

@ -71,7 +71,7 @@ class Consult extends Model
/**
* 终端IP
*
* @var integer
* @var string
*/
public $client_ip = '';

View File

@ -27,6 +27,13 @@ class Page extends Model
*/
public $title = '';
/**
* 别名
*
* @var string
*/
public $alias = '';
/**
* 内容
*

View File

@ -158,7 +158,7 @@ class Question extends Model
/**
* 终端IP
*
* @var integer
* @var string
*/
public $client_ip = '';

View File

@ -69,7 +69,7 @@ class Report extends Model
/**
* 终端IP
*
* @var integer
* @var string
*/
public $client_ip = '';

View File

@ -50,7 +50,7 @@ class Review extends Model
/**
* 终端IP
*
* @var integer
* @var string
*/
public $client_ip = '';

View File

@ -80,6 +80,18 @@ class Page extends Repository
]);
}
/**
* @param string $alias
* @return PageModel|Model|bool
*/
public function findByAlias($alias)
{
return PageModel::findFirst([
'conditions' => 'alias = :alias:',
'bind' => ['alias' => $alias],
]);
}
/**
* @param array $ids
* @param array|string $columns

View File

@ -8,7 +8,6 @@
namespace App\Services\Logic\Page;
use App\Models\Page as PageModel;
use App\Models\User as UserModel;
use App\Services\Logic\PageTrait;
use App\Services\Logic\Service as LogicService;
@ -19,19 +18,15 @@ class PageInfo extends LogicService
public function handle($id)
{
$user = $this->getCurrentUser(true);
$page = $this->checkPage($id);
return $this->handlePage($page, $user);
return $this->handlePage($page);
}
protected function handlePage(PageModel $page, UserModel $user)
protected function handlePage(PageModel $page)
{
$page->content = kg_parse_markdown($page->content);
$me = $this->handleMeInfo($page, $user);
return [
'id' => $page->id,
'title' => $page->title,
@ -40,19 +35,7 @@ class PageInfo extends LogicService
'deleted' => $page->deleted,
'create_time' => $page->create_time,
'update_time' => $page->update_time,
'me' => $me,
];
}
protected function handleMeInfo(PageModel $page, UserModel $user)
{
$me = ['owned' => 0];
if ($page->published == 1) {
$me['owned'] = 1;
}
return $me;
}
}

View File

@ -7,6 +7,7 @@
namespace App\Services\Logic\User\Console;
use App\Services\Logic\Service as LogicService;
use App\Validators\Connect as ConnectValidator;
class ConnectDelete extends LogicService

View File

@ -16,6 +16,22 @@ use Phalcon\Mvc\Dispatcher;
trait Response
{
public function forbidden()
{
/**
* @var Dispatcher $dispatcher
*/
$dispatcher = Di::getDefault()->getShared('dispatcher');
$dispatcher->forward([
'module' => 'home',
'controller' => 'error',
'action' => 'show403',
]);
return false;
}
public function notFound()
{
/**

View File

@ -43,7 +43,9 @@ class ChapterVod extends Validator
throw new BadRequestException('chapter_vod.invalid_file_url');
}
$ext = strtolower(pathinfo($url, PATHINFO_EXTENSION));
$path = parse_url($value, PHP_URL_PATH);
$ext = strtolower(pathinfo($path, PATHINFO_EXTENSION));
/**
* 点播只支持mp4,m3u8格式

View File

@ -50,7 +50,7 @@ class Consult extends Validator
$length = kg_strlen($value);
if ($length < 15) {
if ($length < 5) {
throw new BadRequestException('consult.question_too_short');
}
@ -67,7 +67,7 @@ class Consult extends Validator
$length = kg_strlen($value);
if ($length < 15) {
if ($length < 5) {
throw new BadRequestException('consult.answer_too_short');
}

View File

@ -10,6 +10,7 @@ namespace App\Validators;
use App\Caches\MaxPageId as MaxPageIdCache;
use App\Caches\Page as PageCache;
use App\Exceptions\BadRequest as BadRequestException;
use App\Library\Validators\Common as CommonValidator;
use App\Models\Page as PageModel;
use App\Repos\Page as PageRepo;
@ -38,11 +39,13 @@ class Page extends Validator
public function checkPage($id)
{
$this->checkId($id);
$pageRepo = new PageRepo();
$page = $pageRepo->findById($id);
if (CommonValidator::intNumber($id)) {
$page = $pageRepo->findById($id);
} else {
$page = $pageRepo->findByAlias($id);
}
if (!$page) {
throw new BadRequestException('page.not_found');
@ -81,6 +84,29 @@ class Page extends Validator
return $value;
}
public function checkAlias($alias)
{
$value = $this->filter->sanitize($alias, ['trim', 'string']);
$value = str_replace(['/', '?', '#'], '', $value);
$length = kg_strlen($value);
if (CommonValidator::intNumber($value)) {
throw new BadRequestException('page.invalid_alias');
}
if ($length < 2) {
throw new BadRequestException('page.alias_too_short');
}
if ($length > 50) {
throw new BadRequestException('page.alias_too_long');
}
return $value;
}
public function checkContent($content)
{
$value = $this->filter->sanitize($content, ['trim']);
@ -107,4 +133,15 @@ class Page extends Validator
return $status;
}
public function checkIfAliasTaken($alias)
{
$pageRepo = new PageRepo();
$page = $pageRepo->findByAlias($alias);
if ($page) {
throw new BadRequestException('page.alias_taken');
}
}
}

View File

@ -310,9 +310,9 @@ $error['consult.invalid_rating'] = '无效的评分范围1-5';
$error['consult.invalid_private_status'] = '无效的私密状态';
$error['consult.invalid_publish_status'] = '无效的发布状态';
$error['consult.question_duplicated'] = '你已经咨询过类似问题啦';
$error['consult.question_too_short'] = '问题内容太短(少于15个字符';
$error['consult.question_too_short'] = '问题内容太短(少于5个字符';
$error['consult.question_too_long'] = '问题内容太长多于1000个字符';
$error['consult.answer_too_short'] = '回复内容太短(少于15个字符';
$error['consult.answer_too_short'] = '回复内容太短(少于5个字符';
$error['consult.answer_too_long'] = '回复内容太长多于1000个字符';
$error['consult.edit_not_allowed'] = '当前不允许修改操作';
$error['consult.has_liked'] = '你已经点过赞啦';
@ -323,8 +323,11 @@ $error['consult.has_liked'] = '你已经点过赞啦';
$error['page.not_found'] = '单页不存在';
$error['page.title_too_short'] = '标题太短少于2个字符';
$error['page.title_too_long'] = '标题太长多于50个字符';
$error['page.alias_too_short'] = '别名太短少于2个字符';
$error['page.alias_too_long'] = '别名太长多于50个字符';
$error['page.content_too_short'] = '内容太短少于10个字符';
$error['page.content_too_long'] = '内容太长多于30000个字符';
$error['page.invalid_alias'] = '无效的别名(推荐使用英文作为别名)';
$error['page.invalid_publish_status'] = '无效的发布状态';
/**

View File

@ -694,7 +694,7 @@ final class V20210403184518 extends AbstractMigration
[
'section' => 'site',
'item_key' => 'icp_link',
'item_value' => 'http://www.miitbeian.gov.cn',
'item_value' => 'http://beian.miit.gov.cn',
],
[
'section' => 'site',

View File

@ -0,0 +1,32 @@
<?php
/**
* @copyright Copyright (c) 2021 深圳市酷瓜软件有限公司
* @license https://opensource.org/licenses/GPL-2.0
* @link https://www.koogua.com
*/
use Phinx\Migration\AbstractMigration;
final class V20210802021814 extends AbstractMigration
{
public function up()
{
$this->alterPageTable();
}
protected function alterPageTable()
{
$this->table('kg_page')
->addColumn('alias', 'string', [
'null' => false,
'default' => '',
'limit' => 50,
'collation' => 'utf8mb4_general_ci',
'encoding' => 'utf8mb4',
'comment' => '别名',
'after' => 'title',
])->save();
}
}

View File

@ -636,6 +636,42 @@
color: green;
}
.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;
}
.answer-card .comment-list {
margin-top: 20px;
}
#comment-form {
margin-bottom: 30px;
}
@ -668,15 +704,13 @@
}
.comment-card .avatar {
width: 48px;
height: 48px;
margin-right: 15px;
margin-right: 10px;
}
.comment-card .avatar img {
width: 100%;
height: 100%;
border-radius: 60px;
width: 32px;
height: 32px;
border-radius: 100%;
}
.comment-card .info {
@ -684,7 +718,7 @@
}
.comment-card .user {
margin-bottom: 10px;
margin-bottom: 15px;
color: #666;
}
@ -726,60 +760,11 @@
.comment-card .footer .action:hover,
.answer-card .footer .action:hover {
background-color: #393D49;
cursor: pointer;
color: white;
}
.comment-card .footer .right .action,
.answer-card .footer .right .action {
display: none;
}
.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;
}
.reply-list .comment-card .info {
float: left;
width: 640px;
margin-left: 42px;
}
.toolbar-sticky {

View File

@ -1,8 +1,46 @@
layui.use(['jquery', 'helper'], function () {
layui.use(['jquery', 'form', 'helper'], function () {
var $ = layui.jquery;
var form = layui.form;
var helper = layui.helper;
form.on('submit(comment_answer)', function (data) {
var submit = $(this);
var answerId = $(this).data('answer-id');
var $commentForm = $('#answer-comment-form-' + answerId);
var $commentList = $('#answer-comment-list-' + answerId);
var $textarea = $(data.form).find('.layui-textarea');
var $commentCount = $('#answer-' + answerId).find('.comment-count');
var commentCount = $commentCount.data('count');
submit.attr('disabled', 'disabled').addClass('layui-btn-disabled');
$.ajax({
type: 'POST',
url: data.form.action,
data: data.field,
success: function (res) {
$.ajax({
type: 'GET',
url: '/comment/' + res.comment.id + '/info',
success: function (html) {
$commentCount.data('count', commentCount).text(commentCount);
$commentList.prepend(html);
}
});
$commentForm.hide();
$commentList.show();
$textarea.val('');
layer.msg('发表评论成功');
submit.removeAttr('disabled').removeClass('layui-btn-disabled');
},
error: function (xhr) {
var res = JSON.parse(xhr.responseText);
layer.msg(res.msg);
submit.removeAttr('disabled').removeClass('layui-btn-disabled');
}
});
return false;
});
$('body').on('click', '.answer-like', function () {
var $this = $(this);
var $likeCount = $this.prev();
@ -29,4 +67,42 @@ layui.use(['jquery', 'helper'], function () {
window.location.href = $(this).data('url');
});
$('body').on('click', '.answer-comment', function () {
var id = $(this).data('id');
var $block = $('#answer-comment-form-' + id);
var $textarea = $block.find('textarea');
$block.toggle();
$textarea.focus();
});
$('body').on('click', '.answer-comment-cancel', function () {
var id = $(this).data('id');
$('#answer-comment-form-' + id).hide();
});
$('body').on('click', '.answer-comment-toggle', function () {
var $this = $(this);
var id = $this.data('id');
var url = $this.data('url');
var $commentList = $('#answer-comment-list-' + id);
if ($commentList.hasClass('loaded')) {
if ($this.hasClass('expanded')) {
$this.attr('title', '展开回应').removeClass('expanded');
} else {
$this.attr('title', '收起回应').addClass('expanded');
}
$commentList.toggle();
} else {
$.ajax({
type: 'GET',
url: url,
success: function (html) {
$this.attr('title', '收起回应').addClass('expanded');
$commentList.addClass('loaded').show();
$commentList.html(html);
}
});
}
});
});

View File

@ -49,8 +49,6 @@ layui.use(['jquery', 'form', 'layer', 'helper'], function () {
var $textarea = $(data.form).find('.layui-textarea');
var $replyCount = $('#comment-' + blockId).find('.reply-count');
var replyCount = $replyCount.data('count');
var $tbCommentCount = $('#toolbar-comment > .text');
var tbCommentCount = $tbCommentCount.data('count');
submit.attr('disabled', 'disabled').addClass('layui-btn-disabled');
$.ajax({
type: 'POST',
@ -65,12 +63,10 @@ layui.use(['jquery', 'form', 'layer', 'helper'], function () {
}
});
replyCount++;
tbCommentCount++;
$commentForm.hide();
$replyList.show();
$textarea.val('');
$replyCount.data('count', replyCount).text(replyCount);
$tbCommentCount.data('count', tbCommentCount).text(tbCommentCount);
layer.msg('发表回复成功');
submit.removeAttr('disabled').removeClass('layui-btn-disabled');
},
@ -83,7 +79,7 @@ layui.use(['jquery', 'form', 'layer', 'helper'], function () {
return false;
});
$('#btn-cancel-comment').on('click', function () {
$('#comment-cancel').on('click', function () {
$('#comment-footer').hide();
});
@ -91,7 +87,7 @@ layui.use(['jquery', 'form', 'layer', 'helper'], function () {
$('#comment-footer').show();
});
$('body').on('click', '.btn-cancel-reply', function () {
$('body').on('click', '.reply-cancel', function () {
var id = $(this).data('id');
$('#comment-form-' + id).hide();
});

View File

@ -25,7 +25,7 @@ layui.use(['jquery', 'layer'], function () {
type: 2,
title: '编辑咨询',
content: [url, 'no'],
area: ['720px', '400px'],
area: ['720px', '420px'],
cancel: function () {
parent.location.reload();
}