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:
commit
2102ee8e4a
18
CHANGELOG.md
18
CHANGELOG.md
@ -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)
|
||||
|
||||
### 更新
|
||||
|
@ -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;
|
||||
|
@ -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']);
|
||||
}
|
||||
|
@ -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">
|
||||
|
@ -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 %}>
|
||||
|
@ -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>
|
||||
|
@ -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'];
|
||||
|
@ -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']]);
|
||||
|
@ -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']]);
|
||||
|
@ -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']]);
|
||||
|
@ -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']);
|
||||
|
||||
|
@ -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 }}">
|
||||
|
@ -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 }}">
|
||||
|
@ -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') }}
|
||||
|
@ -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>
|
||||
|
@ -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">
|
||||
|
@ -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 %}
|
||||
|
@ -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 %}
|
@ -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>
|
||||
|
@ -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 %}
|
@ -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)
|
||||
{
|
||||
|
@ -100,7 +100,7 @@ class Answer extends Model
|
||||
/**
|
||||
* 终端IP
|
||||
*
|
||||
* @var integer
|
||||
* @var string
|
||||
*/
|
||||
public $client_ip = '';
|
||||
|
||||
|
@ -109,7 +109,7 @@ class Article extends Model
|
||||
/**
|
||||
* 终端IP
|
||||
*
|
||||
* @var integer
|
||||
* @var string
|
||||
*/
|
||||
public $client_ip = '';
|
||||
|
||||
|
@ -87,7 +87,7 @@ class Comment extends Model
|
||||
/**
|
||||
* 终端IP
|
||||
*
|
||||
* @var integer
|
||||
* @var string
|
||||
*/
|
||||
public $client_ip = '';
|
||||
|
||||
|
@ -71,7 +71,7 @@ class Consult extends Model
|
||||
/**
|
||||
* 终端IP
|
||||
*
|
||||
* @var integer
|
||||
* @var string
|
||||
*/
|
||||
public $client_ip = '';
|
||||
|
||||
|
@ -27,6 +27,13 @@ class Page extends Model
|
||||
*/
|
||||
public $title = '';
|
||||
|
||||
/**
|
||||
* 别名
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $alias = '';
|
||||
|
||||
/**
|
||||
* 内容
|
||||
*
|
||||
|
@ -158,7 +158,7 @@ class Question extends Model
|
||||
/**
|
||||
* 终端IP
|
||||
*
|
||||
* @var integer
|
||||
* @var string
|
||||
*/
|
||||
public $client_ip = '';
|
||||
|
||||
|
@ -69,7 +69,7 @@ class Report extends Model
|
||||
/**
|
||||
* 终端IP
|
||||
*
|
||||
* @var integer
|
||||
* @var string
|
||||
*/
|
||||
public $client_ip = '';
|
||||
|
||||
|
@ -50,7 +50,7 @@ class Review extends Model
|
||||
/**
|
||||
* 终端IP
|
||||
*
|
||||
* @var integer
|
||||
* @var string
|
||||
*/
|
||||
public $client_ip = '';
|
||||
|
||||
|
@ -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
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
{
|
||||
/**
|
||||
|
@ -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格式
|
||||
|
@ -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');
|
||||
}
|
||||
|
||||
|
@ -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');
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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'] = '无效的发布状态';
|
||||
|
||||
/**
|
||||
|
@ -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',
|
||||
|
32
db/migrations/20210802021814.php
Normal file
32
db/migrations/20210802021814.php
Normal 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();
|
||||
}
|
||||
|
||||
}
|
@ -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 {
|
||||
|
@ -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);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
});
|
@ -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();
|
||||
});
|
||||
|
@ -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();
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user