1
0
mirror of https://gitee.com/koogua/course-tencent-cloud.git synced 2025-07-03 07:26:49 +08:00

简单化直播聊天

This commit is contained in:
xiaochong0302 2020-07-03 19:52:53 +08:00
parent 5bd716169c
commit 51e88b53c0
26 changed files with 404 additions and 378 deletions

View File

@ -28,16 +28,12 @@ class CourseController extends Controller
*/
public function listAction()
{
$_REQUEST['limit'] = 12;
$service = new CourseListService();
$pager = $service->handle();
$pager->items = kg_array_object($pager->items);
$service = new CourseQueryService();
$params = $service->getQueryParams();
$pagerUrl = $this->url->get(['for' => 'web.course.pager'], $params);
$topCategories = $service->handleTopCategories();
$subCategories = $service->handleSubCategories();
@ -45,11 +41,27 @@ class CourseController extends Controller
$levels = $service->handleLevels();
$sorts = $service->handleSorts();
$this->view->setVar('pager_url', $pagerUrl);
$this->view->setVar('top_categories', $topCategories);
$this->view->setVar('sub_categories', $subCategories);
$this->view->setVar('models', $models);
$this->view->setVar('levels', $levels);
$this->view->setVar('sorts', $sorts);
}
/**
* @Get("/pager", name="web.course.pager")
*/
public function pagerAction()
{
$service = new CourseListService();
$pager = $service->handle();
$pager->items = kg_array_object($pager->items);
$pager->target = 'course-list';
$this->view->setRenderLevel(View::LEVEL_ACTION_VIEW);
$this->view->pick('course/list_pager');
$this->view->setVar('pager', $pager);
}

View File

@ -9,19 +9,11 @@ use Phalcon\Mvc\View;
/**
* @RoutePrefix("/live")
*/
class LiveController extends \Phalcon\Mvc\Controller
class LiveController extends Controller
{
use ResponseTrait;
/**
* @Get("/{id:[0-9]+}/members", name="web.live.members")
*/
public function membersAction($id)
{
return $this->jsonSuccess();
}
/**
* @Get("/{id:[0-9]+}/stats", name="web.live.stats")
*/
@ -48,14 +40,6 @@ class LiveController extends \Phalcon\Mvc\Controller
return $this->jsonSuccess();
}
/**
* @Post("/{id:[0-9]+}/unbind", name="web.live.unbind")
*/
public function unbindAction($id)
{
}
/**
* @Post("/{id:[0-9]+}/message", name="web.live.message")
*/

View File

@ -210,7 +210,7 @@ class CourseQuery extends Service
return $result;
}
protected function getQueryParams()
public function getQueryParams()
{
$query = $this->request->getQuery();

View File

@ -15,6 +15,7 @@ use App\Repos\ImGroup as ImGroupRepo;
use App\Repos\ImGroupMessage as ImGroupMessageRepo;
use App\Repos\ImSystemMessage as ImSystemMessageRepo;
use App\Repos\ImUser as ImUserRepo;
use App\Repos\User as UserRepo;
use App\Validators\ImFriendUser as ImFriendUserValidator;
use App\Validators\ImGroup as ImGroupValidator;
use App\Validators\ImGroupUser as ImGroupUserValidator;
@ -24,7 +25,7 @@ use GatewayClient\Gateway;
/**
* 警告:
* user对象有更新操作会导致afterFetch()中的设置失效,
* 对象有更新操作会导致afterFetch()中的设置失效,
* 有相关依赖的要重新调用一次afterFetch()
*/
class Im extends Service
@ -34,20 +35,20 @@ class Im extends Service
public function init()
{
$user = $this->getLoginUser();
$loginUser = $this->getLoginUser();
$imUser = $this->getImUser($user->id);
$user = $this->getImUser($loginUser->id);
$mine = [
'id' => $imUser->id,
'username' => $imUser->name,
'avatar' => $imUser->avatar,
'sign' => $imUser->sign,
'status' => $imUser->status,
'id' => $user->id,
'username' => $user->name,
'avatar' => $user->avatar,
'sign' => $user->sign,
'status' => $user->status,
];
$friend = $this->handleFriendList($imUser);
$group = $this->handleGroupList($imUser);
$friend = $this->handleFriendList($user);
$group = $this->handleGroupList($user);
return [
'mine' => $mine,
@ -68,7 +69,7 @@ class Im extends Service
$page = $pagerQuery->getPage();
$limit = $pagerQuery->getLimit();
$userRepo = new ImUserRepo();
$userRepo = new UserRepo();
$pager = $userRepo->paginate($params, $sort, $page, $limit);
@ -293,9 +294,9 @@ class Im extends Service
public function bindUser()
{
$user = $this->getLoginUser();
$loginUser = $this->getLoginUser();
$imUser = $this->getImUser($user->id);
$user = $this->getImUser($loginUser->id);
$clientId = $this->request->getPost('client_id');
@ -313,7 +314,7 @@ class Im extends Service
}
}
$this->pushOnlineTips($imUser);
$this->pushOnlineTips($user);
}
public function sendMessage()
@ -434,9 +435,9 @@ class Im extends Service
public function updateStatus()
{
$user = $this->getLoginUser();
$loginUser = $this->getLoginUser();
$imUser = $this->getImUser($user->id);
$user = $this->getImUser($loginUser->id);
$status = $this->request->getPost('status');
@ -444,16 +445,16 @@ class Im extends Service
$validator->checkSign($status);
$imUser->update(['status' => $status]);
$user->update(['status' => $status]);
$this->pushOnlineTips($imUser);
$this->pushOnlineTips($user);
}
public function updateSignature()
{
$user = $this->getLoginUser();
$loginUser = $this->getLoginUser();
$imUser = $this->getImUser($user->id);
$user = $this->getImUser($loginUser->id);
$sign = $this->request->getPost('sign');
@ -461,16 +462,16 @@ class Im extends Service
$sign = $validator->checkSign($sign);
$imUser->update(['sign' => $sign]);
$user->update(['sign' => $sign]);
return $imUser;
return $user;
}
public function updateSkin()
{
$user = $this->getLoginUser();
$loginUser = $this->getLoginUser();
$imUser = $this->getImUser($user->id);
$user = $this->getImUser($loginUser->id);
$skin = $this->request->getPost('skin');
@ -478,9 +479,9 @@ class Im extends Service
$skin = $validator->checkSkin($skin);
$imUser->update(['skin' => $skin]);
$user->update(['skin' => $skin]);
return $imUser;
return $user;
}
protected function pushOnlineTips(ImUserModel $user)
@ -503,9 +504,9 @@ class Im extends Service
$this->persistent->online_push_time = time();
$imUserRepo = new ImUserRepo();
$userRepo = new ImUserRepo();
$friendUsers = $imUserRepo->findImFriendUsers($user->id);
$friendUsers = $userRepo->findImFriendUsers($user->id);
if ($friendUsers->count() == 0) {
return;

View File

@ -5,10 +5,9 @@ namespace App\Http\Web\Services;
use App\Models\ImFriendGroup as ImFriendGroupModel;
use App\Models\ImFriendUser as ImFriendUserModel;
use App\Models\ImSystemMessage as ImSystemMessageModel;
use App\Models\User as UserModel;
use App\Models\ImUser as ImUserModel;
use App\Repos\ImFriendUser as ImFriendUserRepo;
use App\Repos\ImSystemMessage as ImSystemMessageRepo;
use App\Repos\User as UserRepo;
use App\Repos\ImUser as ImUserRepo;
use App\Validators\ImFriendUser as ImFriendUserValidator;
use App\Validators\ImMessage as ImMessageValidator;
use GatewayClient\Gateway;
@ -18,9 +17,11 @@ Trait ImFriendTrait
public function applyFriend()
{
$post = $this->request->getPost();
$loginUser = $this->getLoginUser();
$user = $this->getLoginUser();
$user = $this->getImUser($loginUser->id);
$post = $this->request->getPost();
$validator = new ImFriendUserValidator();
@ -37,7 +38,9 @@ Trait ImFriendTrait
public function acceptFriend()
{
$user = $this->getLoginUser();
$loginUser = $this->getLoginUser();
$user = $this->getImUser($loginUser->id);
$messageId = $this->request->getPost('message_id');
$groupId = $this->request->getPost('group_id');
@ -54,9 +57,7 @@ Trait ImFriendTrait
return;
}
$userRepo = new UserRepo();
$sender = $userRepo->findById($message->sender_id);
$sender = $this->getImUser($message->sender_id);
$friendUserRepo = new ImFriendUserRepo();
@ -85,7 +86,9 @@ Trait ImFriendTrait
}
$itemInfo = $message->item_info;
$itemInfo['status'] = ImSystemMessageModel::REQUEST_ACCEPTED;
$message->update(['item_info' => $itemInfo]);
$this->handleAcceptFriendNotice($user, $sender, $message);
@ -93,7 +96,9 @@ Trait ImFriendTrait
public function refuseFriend()
{
$user = $this->getLoginUser();
$loginUser = $this->getLoginUser();
$user = $this->getImUser($loginUser->id);
$messageId = $this->request->getPost('message_id');
@ -106,19 +111,19 @@ Trait ImFriendTrait
}
$itemInfo = $message->item_info;
$itemInfo['status'] = ImSystemMessageModel::REQUEST_REFUSED;
$message->update(['item_info' => $itemInfo]);
$userRepo = new UserRepo();
$sender = $userRepo->findById($message->sender_id);
$sender = $this->getImUser($message->sender_id);
$this->handleRefuseFriendNotice($user, $sender);
}
protected function handleApplyFriendNotice(UserModel $sender, UserModel $receiver, ImFriendGroupModel $group, $remark)
protected function handleApplyFriendNotice(ImUserModel $sender, ImUserModel $receiver, ImFriendGroupModel $group, $remark)
{
$userRepo = new UserRepo();
$userRepo = new ImUserRepo();
$itemType = ImSystemMessageModel::TYPE_FRIEND_REQUEST;
@ -163,7 +168,7 @@ Trait ImFriendTrait
}
}
protected function handleAcceptFriendNotice(UserModel $sender, UserModel $receiver, ImSystemMessageModel $applyMessage)
protected function handleAcceptFriendNotice(ImUserModel $sender, ImUserModel $receiver, ImSystemMessageModel $applyMessage)
{
$sysMsgModel = new ImSystemMessageModel();
@ -189,9 +194,12 @@ Trait ImFriendTrait
/**
* 上层操作更新了item_info类型发生了变化故重新获取
*/
$messageRepo = new ImSystemMessageRepo();
$message = $messageRepo->findById($applyMessage->id);
$itemInfo = $message->item_info;
$applyMessage->afterFetch();
/**
* @var array $itemInfo
*/
$itemInfo = $applyMessage->item_info;
$content = kg_json_encode([
'type' => 'friend_accepted',
@ -210,7 +218,7 @@ Trait ImFriendTrait
}
}
protected function handleRefuseFriendNotice(UserModel $sender, UserModel $receiver)
protected function handleRefuseFriendNotice(ImUserModel $sender, ImUserModel $receiver)
{
$sysMsgModel = new ImSystemMessageModel();

View File

@ -5,10 +5,10 @@ namespace App\Http\Web\Services;
use App\Models\ImGroup as ImGroupModel;
use App\Models\ImGroupUser as ImGroupUserModel;
use App\Models\ImSystemMessage as ImSystemMessageModel;
use App\Models\User as UserModel;
use App\Models\ImUser as ImUserModel;
use App\Repos\ImGroup as ImGroupRepo;
use App\Repos\ImGroupUser as ImGroupUserRepo;
use App\Repos\User as UserRepo;
use App\Repos\ImUser as ImUserRepo;
use App\Validators\ImGroup as ImGroupValidator;
use App\Validators\ImGroupUser as ImGroupUserValidator;
use App\Validators\ImMessage as ImMessageValidator;
@ -19,9 +19,11 @@ Trait ImGroupTrait
public function applyGroup()
{
$post = $this->request->getPost();
$loginUser = $this->getLoginUser();
$user = $this->getLoginUser();
$user = $this->getImUser($loginUser->id);
$post = $this->request->getPost();
$validator = new ImGroupUserValidator();
@ -36,7 +38,9 @@ Trait ImGroupTrait
public function acceptGroup()
{
$user = $this->getLoginUser();
$loginUser = $this->getLoginUser();
$user = $this->getImUser($loginUser->id);
$messageId = $this->request->getPost('message_id');
@ -56,9 +60,7 @@ Trait ImGroupTrait
$validator->checkOwner($user->id, $group->user_id);
$userRepo = new UserRepo();
$applicant = $userRepo->findById($message->sender_id);
$applicant = $this->getImUser($message->sender_id);
$groupUserRepo = new ImGroupUserRepo();
@ -73,7 +75,9 @@ Trait ImGroupTrait
}
$itemInfo = $message->item_info;
$itemInfo['status'] = ImSystemMessageModel::REQUEST_ACCEPTED;
$message->update(['item_info' => $itemInfo]);
$this->handleAcceptGroupNotice($user, $applicant, $group);
@ -83,7 +87,9 @@ Trait ImGroupTrait
public function refuseGroup()
{
$user = $this->getLoginUser();
$loginUser = $this->getLoginUser();
$user = $this->getImUser($loginUser->id);
$messageId = $this->request->getPost('message_id');
@ -104,19 +110,19 @@ Trait ImGroupTrait
$validator->checkOwner($user->id, $group->user_id);
$itemInfo = $message->item_info;
$itemInfo['status'] = ImSystemMessageModel::REQUEST_REFUSED;
$message->update(['item_info' => $itemInfo]);
$userRepo = new UserRepo();
$sender = $userRepo->findById($message->sender_id);
$sender = $this->getImUser($message->sender_id);
$this->handleRefuseGroupNotice($user, $sender);
}
protected function handleApplyGroupNotice(UserModel $sender, ImGroupModel $group, $remark)
protected function handleApplyGroupNotice(ImUserModel $sender, ImGroupModel $group, $remark)
{
$userRepo = new UserRepo();
$userRepo = new ImUserRepo();
$receiver = $userRepo->findById($group->user_id);
@ -163,7 +169,7 @@ Trait ImGroupTrait
}
}
protected function handleAcceptGroupNotice(UserModel $sender, UserModel $receiver, ImGroupModel $group)
protected function handleAcceptGroupNotice(ImUserModel $sender, ImUserModel $receiver, ImGroupModel $group)
{
$sysMsgModel = new ImSystemMessageModel();
@ -199,7 +205,7 @@ Trait ImGroupTrait
}
}
protected function handleRefuseGroupNotice(UserModel $sender, UserModel $receiver)
protected function handleRefuseGroupNotice(ImUserModel $sender, ImUserModel $receiver)
{
$sysMsgModel = new ImSystemMessageModel();
@ -224,7 +230,7 @@ Trait ImGroupTrait
}
}
protected function handleNewGroupUserNotice(UserModel $newUser, ImGroupModel $group)
protected function handleNewGroupUserNotice(ImUserModel $newUser, ImGroupModel $group)
{
$groupRepo = new ImGroupRepo();

View File

@ -15,7 +15,7 @@ class Live extends Service
{
$chapter = $this->checkChapterCache($id);
Gateway::$registerAddress = '127.0.0.1:1238';
Gateway::$registerAddress = $this->getRegisterAddress();
$groupName = $this->getGroupName($chapter->id);
@ -44,7 +44,7 @@ class Live extends Service
$groupName = $this->getGroupName($chapter->id);
Gateway::$registerAddress = '127.0.0.1:1238';
Gateway::$registerAddress = $this->getRegisterAddress();
if ($user->id > 0) {
Gateway::bindUid($clientId, $user->id);
@ -59,36 +59,21 @@ class Live extends Service
$user = $this->getLoginUser();
$from = $this->request->getPost('from');
$to = $this->request->getPost('to');
Gateway::$registerAddress = '127.0.0.1:1238';
Gateway::$registerAddress = $this->getRegisterAddress();
$groupName = $this->getGroupName($chapter->id);
$excludeClientId = null;
if ($user->id == $from['id']) {
$excludeClientId = Gateway::getClientIdByUid($user->id);
}
$content = [
'username' => $from['username'],
'avatar' => $from['avatar'],
'content' => $from['content'],
'fromid' => $from['id'],
'id' => $to['id'],
'type' => $to['type'],
'timestamp' => 1000 * time(),
'mine' => false,
];
$excludeClientId = Gateway::getClientIdByUid($user->id);
$message = json_encode([
'type' => 'show_message',
'content' => $content,
'user' => [
'id' => $user->id,
'name' => $user->name,
'avatar' => $user->avatar,
],
]);
Gateway::sendToGroup($groupName, $message, $excludeClientId);
}
@ -105,13 +90,10 @@ class Live extends Service
$result = [];
foreach ($users->toArray() as $key => $user) {
$user['avatar'] = $baseUrl . $user['avatar'];
$result[] = [
'id' => $user['id'],
'name' => $user['name'],
'title' => $user['title'],
'vip' => $user['vip'],
'avatar' => $user['avatar'],
];
@ -122,7 +104,14 @@ class Live extends Service
protected function getGroupName($groupId)
{
return "chapter_{$groupId}";
return "live_{$groupId}";
}
protected function getRegisterAddress()
{
$config = $this->getDI()->get('config');
return $config->websocket->register_address;
}
}

View File

@ -2,11 +2,14 @@
{% block content %}
<div class="layui-breadcrumb breadcrumb">
<a href="/">首页</a>
<a href="{{ url({'for':'web.course.list'}) }}">全部课程</a>
<a href="{{ url({'for':'web.course.show','id':chapter.course.id}) }}">{{ chapter.course.title }}</a>
<a><cite>{{ chapter.title }}</cite></a>
{% set course_url = url({'for':'web.course.show','id':chapter.course.id}) %}
{% set learning_url = url({'for':'web.chapter.learning','id':chapter.id}) %}
{% set stats_url = url({'for':'web.live.stats','id':chapter.id}) %}
<div class="breadcrumb">
<span class="layui-breadcrumb">
<span><i class="layui-icon layui-icon-return"></i> <a href="{{ course_url }}">返回课程主页</a></span>
</span>
</div>
<div class="layout-main">
@ -16,7 +19,24 @@
</div>
</div>
<div class="layout-sidebar">
<div id="sidebar-live-stats" data-url="{{ url({'for':'web.live.stats','id':chapter.id}) }}"></div>
<div class="layui-tab layui-tab-brief user-tab">
<ul class="layui-tab-title">
<li class="layui-this">讨论</li>
<li>成员</li>
</ul>
<div class="layui-tab-content">
<div class="layui-tab-item layui-show">
<div class="live-msg-list"></div>
<div class="live-msg-form">
<form class="layui-form" method="post" action="{{ url({'for':'web.live.message'}) }}">
<input class="layui-input" type="text" name="content" placeholder="请输入内容..." lay-verify="required">
<button class="layui-hide" type="submit" lay-submit="true" lay-filter="chat">发送</button>
</form>
</div>
</div>
<div class="layui-tab-item" id="tab-stats" data-url="{{ stats_url }}"></div>
</div>
</div>
</div>
</div>
@ -26,11 +46,8 @@
<input type="hidden" name="user.avatar" value="{{ auth_user.avatar }}">
<input type="hidden" name="chapter.id" value="{{ chapter.id }}">
<input type="hidden" name="chapter.plan_id" value="{{ chapter.me.plan_id }}">
<input type="hidden" name="chapter.learning_url" value="{{ learning_url }}">
<input type="hidden" name="chapter.play_urls" value='{{ chapter.play_urls|json_encode }}'>
<input type="hidden" name="chapter.learning_url" value="{{ url({'for':'web.learning','id':chapter.id}) }}">
<input type="hidden" name="im.members_url" value="{{ url({'for':'web.live.members','id':chapter.id}) }}">
<input type="hidden" name="im.bind_user_url" value="{{ url({'for':'web.live.bind','id':chapter.id}) }}">
<input type="hidden" name="im.send_msg_url" value="{{ url({'for':'web.live.message','id':chapter.id}) }}">
</div>
{% endblock %}
@ -39,25 +56,7 @@
<script src="//imgcache.qq.com/open/qcloud/video/vcplayer/TcPlayer-2.3.2.js"></script>
{{ js_include('lib/layui/layui.js') }}
{{ js_include('web/js/live.player.js') }}
{{ js_include('web/js/live.im.js') }}
{% endblock %}
{% block inline_js %}
<script>
refreshLiveStats();
setInterval('refreshLiveStats()', 60000);
function refreshLiveStats() {
var $liveStats = $('#sidebar-live-stats');
helper.ajaxLoadHtml($liveStats.data('url'), $liveStats.attr('id'));
}
</script>
{% endblock %}

View File

@ -3,6 +3,7 @@
{% block content %}
{% set course_url = url({'for':'web.course.show','id':chapter.course.id}) %}
{% set learning_url = url({'for':'web.chapter.learning','id':chapter.id}) %}
<div class="breadcrumb">
<span class="layui-breadcrumb">
@ -20,99 +21,22 @@
</div>
</div>
<div class="layui-hide">
<input type="hidden" name="user.id" value="{{ auth_user.id }}">
<input type="hidden" name="user.name" value="{{ auth_user.name }}">
<input type="hidden" name="user.avatar" value="{{ auth_user.avatar }}">
<input type="hidden" name="chapter.id" value="{{ chapter.id }}">
<input type="hidden" name="chapter.plan_id" value="{{ chapter.me.plan_id }}">
<input type="hidden" name="chapter.learning_url" value="{{ learning_url }}">
<input type="hidden" name="chapter.play_urls" value='{{ chapter.play_urls|json_encode }}'>
</div>
{% endblock %}
{% block include_js %}
<script src="//imgcache.qq.com/open/qcloud/video/vcplayer/TcPlayer-2.3.2.js"></script>
{% endblock %}
{% block inline_js %}
<script>
var interval = null;
var intervalTime = 5000;
var position = 0;
var chapterId = '{{ chapter.id }}';
var courseId = '{{ chapter.course.id }}';
var planId = '{{ chapter.me.plan_id }}';
var userId = '{{ auth_user.id }}';
var requestId = getRequestId();
var playUrls = JSON.parse('{{ chapter.play_urls|json_encode }}');
var options = {
autoplay: false,
width: 760,
height: 450
};
if (playUrls.od) {
options.m3u8 = playUrls.od.url;
}
if (playUrls.hd) {
options.m3u8_hd = playUrls.hd.url;
}
if (playUrls.sd) {
options.m3u8_sd = playUrls.sd.url;
}
if (userId !== '0' && planId !== '0') {
options.listener = function (msg) {
if (msg.type === 'play') {
start();
} else if (msg.type === 'pause') {
stop();
} else if (msg.type === 'end') {
stop();
}
}
}
var player = new TcPlayer('player', options);
if (position > 0) {
player.currentTime(position);
}
function start() {
if (interval != null) {
clearInterval(interval);
interval = null;
}
interval = setInterval(learning, intervalTime);
}
function stop() {
clearInterval(interval);
interval = null;
}
function learning() {
$.ajax({
type: 'POST',
url: '/learning',
data: {
request_id: requestId,
chapter_id: chapterId,
course_id: courseId,
user_id: userId,
plan_id: planId,
interval: intervalTime,
position: player.currentTime(),
}
});
}
function getRequestId() {
var id = Date.now().toString(36);
id += Math.random().toString(36).substr(3);
return id;
}
</script>
{{ js_include('web/js/vod.player.js') }}
{% endblock %}

View File

@ -2,25 +2,14 @@
{% block content %}
{{ partial('partials/macro_course') }}
{{ partial('course/list_filter') }}
{% if pager.total_pages > 0 %}
<div class="course-list clearfix">
<div class="layui-row layui-col-space20">
{% for item in pager.items %}
<div class="layui-col-md3">
{{ course_card(item) }}
</div>
{% endfor %}
</div>
</div>
{{ partial('partials/pager') }}
{% else %}
<div class="search-empty container">
<div class="icon"><i class="layui-icon layui-icon-face-surprised"></i></div>
<div class="text">没有检索到相关课程哦</div>
</div>
{% endif %}
<div id="course-list" data-url="{{ pager_url }}"></div>
{% endblock %}
{% block include_js %}
{{ js_include('web/js/course.list.js') }}
{% endblock %}

View File

@ -1,3 +1,7 @@
<div class="filter-toggle">
<span class="layui-icon layui-icon-up"></span>
</div>
<div class="course-filter container">
<div class="filter-group">
<div class="title">方向</div>
@ -41,15 +45,12 @@
{% endfor %}
</div>
</div>
<div class="filter-group">
<div class="title">排序</div>
<div class="content">
{% set sort_val = request.get('sort','trim','score') %}
{% for sort in sorts %}
{% set class = sort_val == sort.id ? 'layui-btn layui-btn-xs' : 'none' %}
<a class="{{ class }}" href="{{ sort.url }}">{{ sort.name }}</a>
{% endfor %}
</div>
</div>
</div>
<div class="course-sort container">
{% set sort_val = request.get('sort','trim','score') %}
{% for sort in sorts %}
{% set class = sort_val == sort.id ? 'layui-btn layui-btn-xs' : 'none' %}
<a class="{{ class }}" href="{{ sort.url }}">{{ sort.name }}</a>
{% endfor %}
</div>

View File

@ -0,0 +1,14 @@
{{ partial('partials/macro_course') }}
{% if pager.total_pages > 0 %}
<div class="course-list clearfix">
<div class="layui-row layui-col-space20">
{% for item in pager.items %}
<div class="layui-col-md3">
{{ course_card(item) }}
</div>
{% endfor %}
</div>
</div>
{{ partial('partials/pager_ajax') }}
{% endif %}

View File

@ -1,10 +1,12 @@
{{ partial('partials/macro_course') }}
<div class="layui-card">
<div class="layui-card-header">推荐课程</div>
<div class="layui-card-body">
{% for course in courses %}
{{ sidebar_course_card(course) }}
{% endfor %}
{% if courses %}
<div class="layui-card">
<div class="layui-card-header">推荐课程</div>
<div class="layui-card-body">
{% for course in courses %}
{{ sidebar_course_card(course) }}
{% endfor %}
</div>
</div>
</div>
{% endif %}

View File

@ -1,10 +1,12 @@
{{ partial('partials/macro_course') }}
<div class="layui-card">
<div class="layui-card-header">相关课程</div>
<div class="layui-card-body">
{% for course in courses %}
{{ sidebar_course_card(course) }}
{% endfor %}
{% if courses %}
<div class="layui-card">
<div class="layui-card-header">相关课程</div>
<div class="layui-card-body">
{% for course in courses %}
{{ sidebar_course_card(course) }}
{% endfor %}
</div>
</div>
</div>
{% endif %}

View File

@ -6,7 +6,6 @@
<div class="breadcrumb">
<span class="layui-breadcrumb">
<a href="/">首页</a>
<a href="{{ url({'for':'web.course.list'}) }}">全部课程</a>
{% for path in course.category_paths %}
<a href="{{ path.url }}">{{ path.name }}</a>

View File

@ -1,20 +1,22 @@
<div class="layui-card">
<div class="layui-card-header">授课教师</div>
<div class="layui-card-body">
{% for teacher in teachers %}
{% set teacher_url = url({'for':'web.user.show','id':teacher.id}) %}
<div class="sidebar-teacher-card clearfix" title="{{ teacher.about|e }}">
<div class="avatar">
<img src="{{ teacher.avatar }}" alt="{{ teacher.name }}">
</div>
<div class="info">
<div class="name layui-elip">
<a href="{{ teacher_url }}">{{ teacher.name }}</a>
{% if teachers %}
<div class="layui-card">
<div class="layui-card-header">授课教师</div>
<div class="layui-card-body">
{% for teacher in teachers %}
{% set teacher_url = url({'for':'web.user.show','id':teacher.id}) %}
<div class="sidebar-teacher-card clearfix" title="{{ teacher.about|e }}">
<div class="avatar">
<img src="{{ teacher.avatar }}" alt="{{ teacher.name }}">
</div>
<div class="info">
<div class="name layui-elip">
<a href="{{ teacher_url }}">{{ teacher.name }}</a>
</div>
{% set title = teacher.title ? teacher.title : '小小教书匠' %}
<div class="title layui-elip">{{ title }}</div>
</div>
{% set title = teacher.title ? teacher.title : '小小教书匠' %}
<div class="title layui-elip">{{ title }}</div>
</div>
</div>
{% endfor %}
{% endfor %}
</div>
</div>
</div>
{% endif %}

View File

@ -1,8 +1,10 @@
<div class="layui-card">
<div class="layui-card-header">热门专题</div>
<div class="layui-card-body">
{% for topic in topics %}
<a class="layui-badge-rim topic-badge" href="{{ url({'for':'web.topic.show','id':topic.id}) }}">{{ topic.title }}</a>
{% endfor %}
{% if topics %}
<div class="layui-card">
<div class="layui-card-header">热门专题</div>
<div class="layui-card-body">
{% for topic in topics %}
<a class="layui-badge-rim topic-badge" href="{{ url({'for':'web.topic.show','id':topic.id}) }}">{{ topic.title }}</a>
{% endfor %}
</div>
</div>
</div>
{% endif %}

View File

@ -13,7 +13,6 @@
<body class="layer">
{% block content %}{% endblock %}
{{ partial('partials/js_global_vars') }}
{{ js_include('lib/layui/layui.js') }}
{{ js_include('web/js/common.js') }}

View File

@ -355,7 +355,7 @@ class Course extends Model
{
return [
'score' => '综合',
'rating' => '',
'rating' => '评',
'latest' => '最新',
'popular' => '最热',
'free' => '免费',

View File

@ -11,7 +11,7 @@ class ImFriendUser extends Validator
public function checkFriend($id)
{
$validator = new User();
$validator = new ImUser();
return $validator->checkUser($id);
}

View File

@ -16,5 +16,11 @@ layui.define(['jquery', 'element'], function (exports) {
});
};
helper.getRequestId = function () {
var id = Date.now().toString(36);
id += Math.random().toString(36).substr(3);
return id;
};
exports(MOD_NAME, helper);
});

View File

@ -64,6 +64,11 @@
text-align: center;
}
.loading {
padding: 30px;
text-align: center;
}
.layout-content {
float: left;
width: 800px;
@ -330,6 +335,13 @@
color: green;
}
.filter-toggle {
text-align: center;
margin-top: -10px;
margin-bottom: 10px;
cursor: pointer;
}
.course-filter {
margin-bottom: 20px;
}
@ -354,6 +366,15 @@
margin-right: 15px;
}
.course-sort {
padding: 10px 20px;
margin-bottom: 20px;
}
.course-sort a {
margin-right: 20px;
}
.course-list {
margin-bottom: 20px;
}

View File

@ -0,0 +1,23 @@
layui.use(['jquery', 'helper'], function () {
var $ = layui.jquery;
var helper = layui.helper;
var $courseList = $('#course-list');
helper.ajaxLoadHtml($courseList.data('url'), $courseList.attr('id'));
var $filter = $('.course-filter');
$('.filter-toggle').on('click', function () {
var $icon = $(this).find('.layui-icon');
if ($icon.hasClass('layui-icon-up')) {
$icon.removeClass('layui-icon-up').addClass('layui-icon-down');
$filter.hide();
} else {
$icon.removeClass('layui-icon-down').addClass('layui-icon-up');
$filter.show();
}
});
});

View File

@ -1,51 +1,10 @@
layui.use(['jquery', 'layim'], function () {
layui.use(['jquery', 'helper'], function () {
var $ = layui.jquery;
var layim = layui.layim;
var socket = new WebSocket('ws://127.0.0.1:8282');
var helper = layui.helper;
var socket = new WebSocket(window.koogua.socketUrl);
var membersUrl = $('input[name="im.members_url"]').val();
var bindUserUrl = $('input[name="im.bind_user_url"]').val();
var sendMsgUrl = $('input[name="im.send_msg_url"]').val();
var group = {
id: $('input[name="chapter.id"]').val(),
avatar: 'http://tp1.sinaimg.cn/5619439268/180/40030060651/1',
name: '直播讨论'
};
var user = {
id: $('input[name="user.id"]').val(),
name: $('input[name="user.name"]').val(),
avatar: $('input[name="user.avatar"]').val(),
status: 'online',
sign: ''
};
layim.config({
brief: true,
init: {
mine: {
'username': user.name,
'avatar': user.avatar,
'id': user.id,
'status': user.status,
'sign': user.sign
}
},
members: {url: membersUrl}
}).chat({
type: 'group',
name: group.name,
avatar: group.avatar,
id: group.id
});
layim.setChatMin();
layim.on('sendMessage', function (res) {
sendMessage(res.mine, res.to);
});
var $chatMsgList = $('#chat-msg-list');
socket.onopen = function () {
console.log('socket connect success');
@ -71,36 +30,44 @@ layui.use(['jquery', 'layim'], function () {
}
};
showOrHidePoster();
form.on('submit(chat)', function (data) {
$.ajax({
type: 'POST',
url: data.form.action,
data: data.field,
success: function (res) {
showMessage(res);
}
});
return false;
});
refreshLiveStats();
setInterval('refreshLiveStats()', 60000);
function bindUser(clientId) {
$.ajax({
type: 'POST',
url: bindUserUrl,
url: '/live/bind',
data: {client_id: clientId}
});
}
function sendMessage(from, to) {
$.ajax({
type: 'POST',
dataType: 'json',
url: sendMsgUrl,
data: {from: from, to: to}
});
function showMessage(res) {
var html = '<div class="chat">';
html += '<span class="user">' + res.user.name + '</span>';
if (res.user.vip === 1) {
html += '<span class="layui-badge">VIP</span>';
}
html += '<span class="content">' + res.content + '</span>';
html += '</div>';
$chatMsgList.append(html);
}
function showMessage(message) {
if (message.fromid !== user.id) {
layim.getMessage(message);
}
}
function showOrHidePoster() {
if (user.id === '0') {
var html = '<div class="chat-login-tips">登录用户才可以参与讨论哦</div>';
$('.layim-chat-footer').hide().after(html);
}
function refreshLiveStats() {
var $liveStats = $('#live-stats');
helper.ajaxLoadHtml($liveStats.data('url'), $liveStats.attr('id'));
}
});

View File

@ -1,4 +1,7 @@
layui.use(['jquery'], function () {
layui.use(['jquery', 'helper'], function () {
var $ = layui.jquery;
var helper = layui.helper;
var interval = null;
var intervalTime = 5000;
@ -8,7 +11,7 @@ layui.use(['jquery'], function () {
var userId = $('input[name="user.id"]').val();
var learningUrl = $('input[name="chapter.learning_url"]').val();
var playUrls = JSON.parse($('input[name="chapter.play_urls"]').val());
var requestId = getRequestId();
var requestId = helper.getRequestId();
var options = {
live: true,
@ -99,10 +102,4 @@ layui.use(['jquery'], function () {
});
}
function getRequestId() {
var id = Date.now().toString(36);
id += Math.random().toString(36).substr(3);
return id;
}
});

View File

@ -0,0 +1,79 @@
layui.use(['jquery', 'helper'], function () {
var $ = layui.jquery;
var helper = layui.helper;
var interval = null;
var intervalTime = 5000;
var position = 0;
var chapterId = $('input[name="chapter.id"]').val();
var planId = $('input[name="chapter.plan_id"]').val();
var userId = $('input[name="user.id"]').val();
var learningUrl = $('input[name="chapter.learning_url"]').val();
var playUrls = JSON.parse($('input[name="chapter.play_urls"]').val());
var requestId = helper.getRequestId();
var options = {
autoplay: false,
width: 760,
height: 450
};
if (playUrls.od) {
options.m3u8 = playUrls.od.url;
}
if (playUrls.hd) {
options.m3u8_hd = playUrls.hd.url;
}
if (playUrls.sd) {
options.m3u8_sd = playUrls.sd.url;
}
if (userId !== '0' && planId !== '0') {
options.listener = function (msg) {
if (msg.type === 'play') {
start();
} else if (msg.type === 'pause') {
stop();
} else if (msg.type === 'end') {
stop();
}
}
}
var player = new TcPlayer('player', options);
if (position > 0) {
player.currentTime(position);
}
function start() {
if (interval != null) {
clearInterval(interval);
interval = null;
}
interval = setInterval(learning, intervalTime);
}
function stop() {
clearInterval(interval);
interval = null;
}
function learning() {
$.ajax({
type: 'POST',
url: learningUrl,
data: {
request_id: requestId,
chapter_id: chapterId,
plan_id: planId,
interval: intervalTime,
position: player.currentTime(),
}
});
}
});