no message
This commit is contained in:
parent
f3d4d3199f
commit
7405c5bba8
97
app/Http/Controllers/Api/DialogController.php
Executable file
97
app/Http/Controllers/Api/DialogController.php
Executable file
@ -0,0 +1,97 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers\Api;
|
||||
|
||||
use App\Models\User;
|
||||
use App\Models\WebSocketDialog;
|
||||
use App\Models\WebSocketDialogMsg;
|
||||
use App\Models\WebSocketDialogUser;
|
||||
use App\Module\Base;
|
||||
use Request;
|
||||
|
||||
/**
|
||||
* @apiDefine dialog
|
||||
*
|
||||
* 对话
|
||||
*/
|
||||
class DialogController extends AbstractController
|
||||
{
|
||||
/**
|
||||
* 消息列表
|
||||
*
|
||||
* @apiParam {Number} dialog_id 对话ID
|
||||
*
|
||||
* @apiParam {Number} [page] 当前页,默认:1
|
||||
* @apiParam {Number} [pagesize] 每页显示数量,默认:50,最大:100
|
||||
*/
|
||||
public function msg__lists()
|
||||
{
|
||||
$user = User::authE();
|
||||
if (Base::isError($user)) {
|
||||
return $user;
|
||||
} else {
|
||||
$user = User::IDE($user['data']);
|
||||
}
|
||||
//
|
||||
$dialog_id = intval(Request::input('dialog_id'));
|
||||
//
|
||||
if (!WebSocketDialogUser::whereDialogId($dialog_id)->whereUserid($user->userid)->exists()) {
|
||||
return Base::retError('不在成员列表内');
|
||||
}
|
||||
$dialog = WebSocketDialog::whereId($dialog_id)->first();
|
||||
if (empty($dialog)) {
|
||||
return Base::retError('对话不存在或已被删除');
|
||||
}
|
||||
//
|
||||
$list = WebSocketDialogMsg::whereDialogId($dialog_id)->orderByDesc('id')->paginate(Base::getPaginate(100, 50));
|
||||
//
|
||||
return Base::retSuccess('success', $list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送消息
|
||||
*
|
||||
* @apiParam {Number} dialog_id 对话ID
|
||||
* @apiParam {Number} [extra_int] 额外参数(数字)
|
||||
* @apiParam {String} [extra_str] 额外参数(字符)
|
||||
* @apiParam {String} text 消息内容
|
||||
*/
|
||||
public function msg__sendtext()
|
||||
{
|
||||
$user = User::authE();
|
||||
if (Base::isError($user)) {
|
||||
return $user;
|
||||
} else {
|
||||
$user = User::IDE($user['data']);
|
||||
}
|
||||
//
|
||||
$dialog_id = intval(Request::input('dialog_id'));
|
||||
$extra_int = intval(Request::input('extra_int'));
|
||||
$extra_str = trim(Request::input('extra_str'));
|
||||
$text = trim(Request::input('text'));
|
||||
//
|
||||
if (mb_strlen($text) < 1) {
|
||||
return Base::retError('消息内容不能为空');
|
||||
} elseif (mb_strlen($text) > 20000) {
|
||||
return Base::retError('消息内容最大不能超过20000字');
|
||||
}
|
||||
//
|
||||
if (!WebSocketDialogUser::whereDialogId($dialog_id)->whereUserid($user->userid)->exists()) {
|
||||
return Base::retError('不在成员列表内');
|
||||
}
|
||||
$dialog = WebSocketDialog::whereId($dialog_id)->first();
|
||||
if (empty($dialog)) {
|
||||
return Base::retError('对话不存在或已被删除');
|
||||
}
|
||||
//
|
||||
$msg = [
|
||||
'text' => $text
|
||||
];
|
||||
//
|
||||
if ($dialog->type == 'group') {
|
||||
return WebSocketDialogMsg::addGroupMsg($dialog_id, 'text', $msg, $user->userid, $extra_int, $extra_str);
|
||||
} else {
|
||||
return WebSocketDialogMsg::addUserMsg($dialog_id, 'text', $msg, $user->userid, $extra_int, $extra_str);
|
||||
}
|
||||
}
|
||||
}
|
@ -9,9 +9,8 @@ use App\Models\ProjectLog;
|
||||
use App\Models\ProjectTask;
|
||||
use App\Models\ProjectUser;
|
||||
use App\Models\User;
|
||||
use App\Models\WebSocketDialog;
|
||||
use App\Models\WebSocketDialogMsg;
|
||||
use App\Module\Base;
|
||||
use Carbon\Carbon;
|
||||
use Request;
|
||||
|
||||
/**
|
||||
@ -209,6 +208,54 @@ class ProjectController extends AbstractController
|
||||
return Base::retSuccess('修改成功');
|
||||
}
|
||||
|
||||
/**
|
||||
* 修改项目成员
|
||||
*
|
||||
* @apiParam {Number} project_id 项目ID
|
||||
* @apiParam {Number} userid 成员ID或成员ID组
|
||||
*/
|
||||
public function user()
|
||||
{
|
||||
$user = User::authE();
|
||||
if (Base::isError($user)) {
|
||||
return $user;
|
||||
} else {
|
||||
$user = User::IDE($user['data']);
|
||||
}
|
||||
//
|
||||
$project_id = intval(Request::input('project_id'));
|
||||
$userid = Request::input('userid');
|
||||
$userid = is_array($userid) ? $userid : [$userid];
|
||||
//
|
||||
$project = Project::select($this->projectSelect)
|
||||
->join('project_users', 'projects.id', '=', 'project_users.project_id')
|
||||
->where('projects.id', $project_id)
|
||||
->where('project_users.userid', $user->userid)
|
||||
->first();
|
||||
if (empty($project)) {
|
||||
return Base::retError('项目不存在或不在成员列表内');
|
||||
}
|
||||
if (!$project->owner) {
|
||||
return Base::retError('你不是项目负责人');
|
||||
}
|
||||
//
|
||||
return AbstractModel::transaction(function() use ($project, $userid) {
|
||||
$array = [];
|
||||
foreach ($userid as $value) {
|
||||
if ($value > 0 && $project->joinProject($value)) {
|
||||
$array[] = $value;
|
||||
}
|
||||
}
|
||||
$delUser = ProjectUser::whereProjectId($project->id)->whereNotIn('userid', $array)->get();
|
||||
if ($delUser->isNotEmpty()) {
|
||||
foreach ($delUser as $value) {
|
||||
$value->exitProject();
|
||||
}
|
||||
}
|
||||
return Base::retSuccess('修改成功');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 移交项目
|
||||
*
|
||||
@ -256,6 +303,42 @@ class ProjectController extends AbstractController
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 退出项目
|
||||
*
|
||||
* @apiParam {Number} project_id 项目ID
|
||||
*/
|
||||
public function exit()
|
||||
{
|
||||
$user = User::authE();
|
||||
if (Base::isError($user)) {
|
||||
return $user;
|
||||
} else {
|
||||
$user = User::IDE($user['data']);
|
||||
}
|
||||
//
|
||||
$project_id = intval(Request::input('project_id'));
|
||||
//
|
||||
$project = Project::select($this->projectSelect)
|
||||
->join('project_users', 'projects.id', '=', 'project_users.project_id')
|
||||
->where('projects.id', $project_id)
|
||||
->where('project_users.userid', $user->userid)
|
||||
->first();
|
||||
if (empty($project)) {
|
||||
return Base::retError('项目不存在或不在成员列表内');
|
||||
}
|
||||
//
|
||||
if ($project->owner) {
|
||||
return Base::retError('项目负责人无法退出项目');
|
||||
}
|
||||
//
|
||||
$projectUser = ProjectUser::whereProjectId($project->id)->whereUserid($user->userid)->first();
|
||||
if ($projectUser->exitProject()) {
|
||||
return Base::retSuccess('退出成功');
|
||||
}
|
||||
return Base::retError('退出失败');
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除项目
|
||||
*
|
||||
@ -284,12 +367,91 @@ class ProjectController extends AbstractController
|
||||
return Base::retError('你不是项目负责人');
|
||||
}
|
||||
//
|
||||
return AbstractModel::transaction(function() use ($project) {
|
||||
ProjectTask::whereProjectId($project->id)->delete();
|
||||
$project->delete();
|
||||
//
|
||||
if ($project->deleteProject()) {
|
||||
return Base::retSuccess('删除成功');
|
||||
});
|
||||
}
|
||||
return Base::retError('删除失败');
|
||||
}
|
||||
|
||||
/**
|
||||
* 【消息】消息列表
|
||||
*
|
||||
* @apiParam {Number} project_id 项目ID
|
||||
* @apiParam {Number} [task_id] 任务ID
|
||||
*
|
||||
* @apiParam {Number} [page] 当前页,默认:1
|
||||
* @apiParam {Number} [pagesize] 每页显示数量,默认:30,最大:100
|
||||
*/
|
||||
public function msg__lists()
|
||||
{
|
||||
$user = User::authE();
|
||||
if (Base::isError($user)) {
|
||||
return $user;
|
||||
} else {
|
||||
$user = User::IDE($user['data']);
|
||||
}
|
||||
//
|
||||
$project_id = intval(Request::input('project_id'));
|
||||
$task_id = intval(Request::input('task_id'));
|
||||
//
|
||||
$project = Project::select($this->projectSelect)
|
||||
->join('project_users', 'projects.id', '=', 'project_users.project_id')
|
||||
->where('projects.id', $project_id)
|
||||
->where('project_users.userid', $user->userid)
|
||||
->first();
|
||||
if (empty($project)) {
|
||||
return Base::retError('项目不存在或不在成员列表内');
|
||||
}
|
||||
//
|
||||
$builder = WebSocketDialogMsg::whereDialogId($project->dialog_id);
|
||||
if ($task_id > 0) {
|
||||
$builder->whereExtraInt($task_id);
|
||||
}
|
||||
$list = $builder->orderByDesc('id')->paginate(Base::getPaginate(100, 30));
|
||||
//
|
||||
return Base::retSuccess('success', $list);
|
||||
}
|
||||
|
||||
/**
|
||||
* 【消息】发送消息
|
||||
*
|
||||
* @apiParam {Number} project_id 项目ID
|
||||
* @apiParam {Number} [task_id] 任务ID
|
||||
* @apiParam {String} text 消息内容
|
||||
*/
|
||||
public function msg__sendtext()
|
||||
{
|
||||
$user = User::authE();
|
||||
if (Base::isError($user)) {
|
||||
return $user;
|
||||
} else {
|
||||
$user = User::IDE($user['data']);
|
||||
}
|
||||
//
|
||||
$project_id = intval(Request::input('project_id'));
|
||||
$task_id = intval(Request::input('task_id'));
|
||||
$text = trim(Request::input('text'));
|
||||
//
|
||||
if (mb_strlen($text) < 1) {
|
||||
return Base::retError('消息内容不能为空');
|
||||
} elseif (mb_strlen($text) > 20000) {
|
||||
return Base::retError('消息内容最大不能超过20000字');
|
||||
}
|
||||
//
|
||||
$project = Project::select($this->projectSelect)
|
||||
->join('project_users', 'projects.id', '=', 'project_users.project_id')
|
||||
->where('projects.id', $project_id)
|
||||
->where('project_users.userid', $user->userid)
|
||||
->first();
|
||||
if (empty($project)) {
|
||||
return Base::retError('项目不存在或不在成员列表内');
|
||||
}
|
||||
//
|
||||
$msg = [
|
||||
'text' => $text
|
||||
];
|
||||
//
|
||||
return WebSocketDialogMsg::addGroupMsg($project->dialog_id, 'text', $msg, $user->userid, $task_id);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Module\Base;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
|
||||
/**
|
||||
@ -82,4 +83,41 @@ class Project extends AbstractModel
|
||||
{
|
||||
return $this->hasMany(projectUser::class, 'project_id', 'id')->orderBy('id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 加入项目
|
||||
* @param int $userid 加入的会员ID
|
||||
* @return bool
|
||||
*/
|
||||
public function joinProject($userid) {
|
||||
$result = AbstractModel::transaction(function () use ($userid) {
|
||||
ProjectUser::updateInsert([
|
||||
'project_id' => $this->id,
|
||||
'userid' => $userid,
|
||||
]);
|
||||
WebSocketDialogUser::updateInsert([
|
||||
'dialog_id' => $this->dialog_id,
|
||||
'userid' => $userid,
|
||||
]);
|
||||
});
|
||||
return Base::isSuccess($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除项目
|
||||
* @return bool
|
||||
*/
|
||||
public function deleteProject()
|
||||
{
|
||||
$result = AbstractModel::transaction(function () {
|
||||
ProjectTask::whereProjectId($this->id)->delete();
|
||||
WebSocketDialog::whereId($this->dialog_id)->delete();
|
||||
if ($this->delete()) {
|
||||
return Base::retSuccess('success');
|
||||
} else {
|
||||
return Base::retError('error');
|
||||
}
|
||||
});
|
||||
return Base::isSuccess($result);
|
||||
}
|
||||
}
|
||||
|
@ -27,12 +27,14 @@ use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
* @property \Illuminate\Support\Carbon|null $created_at
|
||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||
* @property \Illuminate\Support\Carbon|null $deleted_at
|
||||
* @property-read int $dialog_id
|
||||
* @property-read int $file_num
|
||||
* @property-read int $msg_num
|
||||
* @property-read bool $overdue
|
||||
* @property-read int $percent
|
||||
* @property-read int $sub_num
|
||||
* @property-read bool $today
|
||||
* @property-read \App\Models\Project|null $project
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\ProjectTaskTag[] $taskTag
|
||||
* @property-read int|null $task_tag_count
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\ProjectTaskUser[] $taskUser
|
||||
@ -70,6 +72,7 @@ class ProjectTask extends AbstractModel
|
||||
'file_num',
|
||||
'msg_num',
|
||||
'sub_num',
|
||||
'dialog_id',
|
||||
'percent',
|
||||
'today',
|
||||
'overdue',
|
||||
@ -94,7 +97,7 @@ class ProjectTask extends AbstractModel
|
||||
public function getMsgNumAttribute()
|
||||
{
|
||||
if (!isset($this->attributes['msg_num'])) {
|
||||
$this->attributes['msg_num'] = ProjectTaskMsg::whereTaskId($this->id)->count();
|
||||
$this->attributes['msg_num'] = WebSocketDialogMsg::whereDialogId($this->dialog_id)->whereExtraInt($this->id)->count();
|
||||
}
|
||||
return $this->attributes['msg_num'];
|
||||
}
|
||||
@ -114,6 +117,18 @@ class ProjectTask extends AbstractModel
|
||||
return $this->attributes['sub_num'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 对话ID
|
||||
* @return int
|
||||
*/
|
||||
public function getDialogIdAttribute()
|
||||
{
|
||||
if (!isset($this->attributes['dialog_id'])) {
|
||||
$this->attributes['dialog_id'] = intval(Project::whereId($this->project_id)->value('dialog_id'));
|
||||
}
|
||||
return $this->attributes['dialog_id'];
|
||||
}
|
||||
|
||||
/**
|
||||
* 进度(0-100)
|
||||
* @return int
|
||||
@ -167,6 +182,14 @@ class ProjectTask extends AbstractModel
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasOne
|
||||
*/
|
||||
public function project(): \Illuminate\Database\Eloquent\Relations\HasOne
|
||||
{
|
||||
return $this->hasOne(Project::class, 'id', 'project_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
|
@ -1,31 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
/**
|
||||
* Class ProjectTaskMsg
|
||||
*
|
||||
* @package App\Models
|
||||
* @property int $id
|
||||
* @property int|null $project_id 项目ID
|
||||
* @property int|null $task_id 任务ID
|
||||
* @property string|null $msg 详细内容(JSON)
|
||||
* @property int|null $userid 发送用户ID
|
||||
* @property \Illuminate\Support\Carbon|null $created_at
|
||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskMsg newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskMsg newQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskMsg query()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskMsg whereCreatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskMsg whereId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskMsg whereMsg($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskMsg whereProjectId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskMsg whereTaskId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskMsg whereUpdatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskMsg whereUserid($value)
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class ProjectTaskMsg extends AbstractModel
|
||||
{
|
||||
|
||||
}
|
@ -2,6 +2,8 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Module\Base;
|
||||
|
||||
/**
|
||||
* Class ProjectUser
|
||||
*
|
||||
@ -12,6 +14,7 @@ namespace App\Models;
|
||||
* @property int|null $owner 是否负责人
|
||||
* @property \Illuminate\Support\Carbon|null $created_at
|
||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||
* @property-read \App\Models\Project|null $project
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectUser newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectUser newQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectUser query()
|
||||
@ -26,4 +29,28 @@ namespace App\Models;
|
||||
class ProjectUser extends AbstractModel
|
||||
{
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasOne
|
||||
*/
|
||||
public function project(): \Illuminate\Database\Eloquent\Relations\HasOne
|
||||
{
|
||||
return $this->hasOne(Project::class, 'id', 'project_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* 退出项目
|
||||
* @return bool
|
||||
*/
|
||||
public function exitProject() {
|
||||
$result = AbstractModel::transaction(function () {
|
||||
WebSocketDialogUser::whereDialogId($this->project->dialog_id)->whereUserid($this->userid)->delete();
|
||||
if ($this->delete()) {
|
||||
return Base::retSuccess('success');
|
||||
} else {
|
||||
return Base::retError('error');
|
||||
}
|
||||
});
|
||||
return Base::isSuccess($result);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -26,6 +26,8 @@ use App\Module\Base;
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialog whereType($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialog whereUpdatedAt($value)
|
||||
* @mixin \Eloquent
|
||||
* @property \Illuminate\Support\Carbon|null $deleted_at
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialog whereDeletedAt($value)
|
||||
*/
|
||||
class WebSocketDialog extends AbstractModel
|
||||
{
|
||||
@ -89,7 +91,7 @@ class WebSocketDialog extends AbstractModel
|
||||
* @param int|array $userid 加入的会员ID或会员ID组
|
||||
* @return bool
|
||||
*/
|
||||
public static function quitGroup($dialog_id, $userid)
|
||||
public static function exitGroup($dialog_id, $userid)
|
||||
{
|
||||
if (is_array($userid)) {
|
||||
WebSocketDialogUser::whereDialogId($dialog_id)->whereIn('userid', $userid)->delete();
|
||||
|
@ -13,8 +13,11 @@ use Carbon\Carbon;
|
||||
* @property int $id
|
||||
* @property int|null $dialog_id 对话ID
|
||||
* @property int|null $userid 发送会员ID
|
||||
* @property string|null $msg 详细消息
|
||||
* @property string|null $type 消息类型
|
||||
* @property array|mixed $msg 详细消息
|
||||
* @property int|null $send 是否已送达
|
||||
* @property int|null $extra_int 额外数字参数
|
||||
* @property string|null $extra_str 额外字符参数
|
||||
* @property \Illuminate\Support\Carbon|null $created_at
|
||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg newModelQuery()
|
||||
@ -22,27 +25,53 @@ use Carbon\Carbon;
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg query()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereCreatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereDialogId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereExtraInt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereExtraStr($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereMsg($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereSend($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereType($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereUpdatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogMsg whereUserid($value)
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class WebSocketDialogMsg extends AbstractModel
|
||||
{
|
||||
protected $hidden = [
|
||||
'updated_at',
|
||||
];
|
||||
|
||||
/**
|
||||
* 消息
|
||||
* @param $value
|
||||
* @return array|mixed
|
||||
*/
|
||||
public function getMsgAttribute($value)
|
||||
{
|
||||
if (is_array($value)) {
|
||||
return $value;
|
||||
}
|
||||
return Base::json2array($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* 给会员添加并发送消息
|
||||
* @param int $dialog_id 会话ID(即 聊天室ID)
|
||||
* @param string $type 消息类型
|
||||
* @param array $msg 发送的消息
|
||||
* @param int $sender 发送的会员ID(默认自己,0为系统)
|
||||
* @param int $extra_int
|
||||
* @param string $extra_str
|
||||
* @return array
|
||||
*/
|
||||
public static function addGroupMsg($dialog_id, $msg, $sender = 0)
|
||||
public static function addGroupMsg($dialog_id, $type, $msg, $sender = 0, $extra_int = 0, $extra_str = '')
|
||||
{
|
||||
$dialogMsg = self::createInstance([
|
||||
'userid' => $sender ?: User::token2userid(),
|
||||
'type' => $type,
|
||||
'msg' => $msg,
|
||||
'extra_int' => $extra_int,
|
||||
'extra_str' => $extra_str,
|
||||
]);
|
||||
return AbstractModel::transaction(function () use ($dialog_id, $msg, $dialogMsg) {
|
||||
$dialog = WebSocketDialog::checkGroupDialog($dialogMsg->userid, $dialog_id);
|
||||
@ -60,11 +89,11 @@ class WebSocketDialogMsg extends AbstractModel
|
||||
'userid' => $userids,
|
||||
'msg' => [
|
||||
'type' => 'dialog',
|
||||
'data' => $msg,
|
||||
'data' => $dialogMsg->toArray(),
|
||||
]
|
||||
]);
|
||||
}
|
||||
return Base::retSuccess('发送成功');
|
||||
return Base::retSuccess('发送成功', $dialogMsg);
|
||||
});
|
||||
}
|
||||
|
||||
@ -72,15 +101,21 @@ class WebSocketDialogMsg extends AbstractModel
|
||||
/**
|
||||
* 给会员添加并发送消息
|
||||
* @param int $userid 接收的会员ID
|
||||
* @param string $type 消息类型
|
||||
* @param array $msg 发送的消息
|
||||
* @param int $sender 发送的会员ID(默认自己,0为系统)
|
||||
* @param int $extra_int
|
||||
* @param string $extra_str
|
||||
* @return array
|
||||
*/
|
||||
public static function addUserMsg($userid, $msg, $sender = 0)
|
||||
public static function addUserMsg($userid, $type, $msg, $sender = 0, $extra_int = 0, $extra_str = '')
|
||||
{
|
||||
$dialogMsg = self::createInstance([
|
||||
'userid' => $sender ?: User::token2userid(),
|
||||
'type' => $type,
|
||||
'msg' => $msg,
|
||||
'extra_int' => $extra_int,
|
||||
'extra_str' => $extra_str,
|
||||
]);
|
||||
return AbstractModel::transaction(function () use ($userid, $msg, $dialogMsg) {
|
||||
$dialog = WebSocketDialog::checkUserDialog($dialogMsg->userid, $userid);
|
||||
@ -96,10 +131,10 @@ class WebSocketDialogMsg extends AbstractModel
|
||||
'userid' => $userid,
|
||||
'msg' => [
|
||||
'type' => 'dialog',
|
||||
'data' => $msg,
|
||||
'data' => $dialogMsg->toArray(),
|
||||
]
|
||||
]);
|
||||
return Base::retSuccess('发送成功');
|
||||
return Base::retSuccess('发送成功', $dialogMsg);
|
||||
});
|
||||
}
|
||||
|
||||
|
@ -34,7 +34,7 @@
|
||||
"dependencies": {
|
||||
"echarts": "^5.1.1",
|
||||
"tinymce": "^5.8.1",
|
||||
"view-design-hi": "^4.5.0-10",
|
||||
"view-design-hi": "^4.5.0-11",
|
||||
"vue-clipboard2": "^0.3.1",
|
||||
"vue-emoji-picker": "^1.0.1",
|
||||
"vue-kityminder-gg": "^1.3.6",
|
||||
|
@ -91,6 +91,18 @@ export default {
|
||||
},
|
||||
scrollToBottom(animate) {
|
||||
this.scrollTo(this.$refs.scrollerView.scrollHeight, animate);
|
||||
},
|
||||
getScrollInfo() {
|
||||
let scrollerView = $A(this.$refs.scrollerView);
|
||||
let wInnerH = Math.round(scrollerView.innerHeight());
|
||||
let wScrollY = scrollerView.scrollTop();
|
||||
let bScrollH = this.$refs.scrollerView.scrollHeight;
|
||||
this.scrollY = wScrollY;
|
||||
return {
|
||||
scale: wScrollY / (bScrollH - wInnerH), //已滚动比例
|
||||
scrollY: wScrollY, //滚动的距离
|
||||
scrollE: bScrollH - wInnerH - wScrollY, //与底部距离
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -10,9 +10,11 @@
|
||||
:default-label="value"
|
||||
:default-event-object="true"
|
||||
:multipleMax="multipleMax"
|
||||
:multipleUncancelable="uncancelable"
|
||||
multiple
|
||||
filterable
|
||||
transfer-class-name="common-user-transfer"
|
||||
@on-open-change="openChange"
|
||||
@on-set-default-options="setDefaultOptions">
|
||||
<div v-if="multipleMax" slot="drop-prepend" class="user-drop-prepend">{{$L('最多只能选择' + multipleMax + '个')}}</div>
|
||||
<Option v-for="(item, key) in lists" :value="item.userid" :key="key" :label="item.nickname" :avatar="item.userimg">
|
||||
@ -35,6 +37,12 @@
|
||||
type: [String, Number, Array],
|
||||
default: ''
|
||||
},
|
||||
uncancelable: {
|
||||
type: Array,
|
||||
default: () => {
|
||||
return [];
|
||||
}
|
||||
},
|
||||
placeholder: {
|
||||
default: ''
|
||||
},
|
||||
@ -55,6 +63,7 @@
|
||||
ready: false,
|
||||
initialized: false,
|
||||
loading: false,
|
||||
openLoad: false,
|
||||
values: [],
|
||||
lists: []
|
||||
}
|
||||
@ -89,6 +98,15 @@
|
||||
}
|
||||
},
|
||||
methods: {
|
||||
openChange(show) {
|
||||
if (show && !this.openLoad) {
|
||||
this.openLoad = true;
|
||||
if (this.lists.length == this.values.length) {
|
||||
this.$nextTick(this.searchUser);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
setDefaultOptions(options) {
|
||||
const userids = [];
|
||||
options.forEach(({value, label}) => {
|
||||
@ -126,7 +144,7 @@
|
||||
url: 'users/search',
|
||||
data: {
|
||||
keys: {
|
||||
key: query
|
||||
key: query || ''
|
||||
},
|
||||
take: 30
|
||||
},
|
||||
|
49
resources/assets/js/pages/manage/components/message-view.vue
Normal file
49
resources/assets/js/pages/manage/components/message-view.vue
Normal file
@ -0,0 +1,49 @@
|
||||
<template>
|
||||
<div class="message-view">
|
||||
|
||||
<div v-if="msgData.type == 'text'" class="message-content" v-html="textMsg(msgData.msg.text)"></div>
|
||||
<div v-else class="message-content message-unknown">{{$L("未知的消息类型")}}</div>
|
||||
|
||||
<div v-if="msgData.created_at" class="message-time">{{formatTime(msgData.created_at)}}</div>
|
||||
<div v-else class="message-time"><Loading/></div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
export default {
|
||||
name: "MessageView",
|
||||
props: {
|
||||
msgData: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
formatTime(date) {
|
||||
let time = Math.round(new Date(date).getTime() / 1000),
|
||||
string = '';
|
||||
if ($A.formatDate('Ymd') === $A.formatDate('Ymd', time)) {
|
||||
string = $A.formatDate('H:i', time)
|
||||
} else if ($A.formatDate('Y') === $A.formatDate('Y', time)) {
|
||||
string = $A.formatDate('m-d', time)
|
||||
} else {
|
||||
string = $A.formatDate('Y-m-d', time)
|
||||
}
|
||||
return string || '';
|
||||
},
|
||||
|
||||
textMsg(text) {
|
||||
if (!text) {
|
||||
return ""
|
||||
}
|
||||
text = text.trim().replace(/(\n\x20*){3,}/g, "<br/><br/>");
|
||||
text = text.trim().replace(/\n/g, "<br/>");
|
||||
return text;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
@ -31,10 +31,14 @@
|
||||
<li class="project-icon">
|
||||
<Dropdown @on-click="projectDropdown" transfer>
|
||||
<Icon type="ios-more" />
|
||||
<DropdownMenu slot="list">
|
||||
<DropdownMenu v-if="projectDetail.owner_userid === userId" slot="list">
|
||||
<DropdownItem name="setting">{{$L('项目设置')}}</DropdownItem>
|
||||
<DropdownItem name="transfer">{{$L('移交项目')}}</DropdownItem>
|
||||
<DropdownItem name="delete" style="color:#f40" divided>{{$L('删除项目')}}</DropdownItem>
|
||||
<DropdownItem name="user">{{$L('成员管理')}}</DropdownItem>
|
||||
<DropdownItem name="transfer" divided>{{$L('移交项目')}}</DropdownItem>
|
||||
<DropdownItem name="delete" style="color:#f40">{{$L('删除项目')}}</DropdownItem>
|
||||
</DropdownMenu>
|
||||
<DropdownMenu v-else slot="list">
|
||||
<DropdownItem name="exit">{{$L('退出项目')}}</DropdownItem>
|
||||
</DropdownMenu>
|
||||
</Dropdown>
|
||||
|
||||
@ -293,6 +297,23 @@
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
<!--成员管理-->
|
||||
<Modal
|
||||
v-model="userShow"
|
||||
:title="$L('成员管理')"
|
||||
:mask-closable="false"
|
||||
class-name="simple-modal">
|
||||
<Form ref="addProject" :model="userData" label-width="auto" @submit.native.prevent>
|
||||
<FormItem prop="userids" :label="$L('项目成员')">
|
||||
<UserInput v-if="userShow" v-model="userData.userids" :uncancelable="userData.uncancelable" :multiple-max="100" :placeholder="$L('选择项目成员')"/>
|
||||
</FormItem>
|
||||
</Form>
|
||||
<div slot="footer">
|
||||
<Button type="default" @click="userShow=false">{{$L('取消')}}</Button>
|
||||
<Button type="primary" :loading="userLoad > 0" @click="onUser">{{$L('保存')}}</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
<!--移交项目-->
|
||||
<Modal
|
||||
v-model="transferShow"
|
||||
@ -833,6 +854,10 @@ export default {
|
||||
settingData: {},
|
||||
settingLoad: 0,
|
||||
|
||||
userShow: false,
|
||||
userData: {},
|
||||
userLoad: 0,
|
||||
|
||||
transferShow: false,
|
||||
transferData: {},
|
||||
transferLoad: 0,
|
||||
@ -978,6 +1003,29 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
onUser() {
|
||||
this.userLoad++;
|
||||
$A.apiAjax({
|
||||
url: 'project/user',
|
||||
data: {
|
||||
project_id: this.userData.project_id,
|
||||
userid: this.userData.userids,
|
||||
},
|
||||
complete: () => {
|
||||
this.userLoad--;
|
||||
},
|
||||
success: ({ret, data, msg}) => {
|
||||
if (ret === 1) {
|
||||
$A.messageSuccess(msg);
|
||||
this.$store.commit('getProjectDetail', this.userData.project_id);
|
||||
this.userShow = false;
|
||||
} else {
|
||||
$A.modalError(msg);
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
onTransfer() {
|
||||
this.transferLoad++;
|
||||
$A.apiAjax({
|
||||
@ -1004,7 +1052,7 @@ export default {
|
||||
onDelete() {
|
||||
$A.modalConfirm({
|
||||
title: '删除项目',
|
||||
content: '你确定要删除此项目吗?',
|
||||
content: '你确定要删除项目【' + this.projectDetail.name + '】吗?',
|
||||
loading: true,
|
||||
onOk: () => {
|
||||
$A.apiAjax({
|
||||
@ -1031,6 +1079,36 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
onExit() {
|
||||
$A.modalConfirm({
|
||||
title: '退出项目',
|
||||
content: '你确定要退出项目【' + this.projectDetail.name + '】吗?',
|
||||
loading: true,
|
||||
onOk: () => {
|
||||
$A.apiAjax({
|
||||
url: 'project/exit',
|
||||
data: {
|
||||
project_id: this.projectDetail.id,
|
||||
},
|
||||
error: () => {
|
||||
this.$Modal.remove();
|
||||
$A.modalAlert('网络繁忙,请稍后再试!');
|
||||
},
|
||||
success: ({ret, data, msg}) => {
|
||||
this.$Modal.remove();
|
||||
if (ret === 1) {
|
||||
$A.messageSuccess(msg);
|
||||
this.$store.commit('getProjectList');
|
||||
this.goForward({path: '/manage/dashboard'}, true);
|
||||
}else{
|
||||
$A.modalError(msg, 301);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
projectDropdown(name) {
|
||||
switch (name) {
|
||||
case "setting":
|
||||
@ -1040,6 +1118,13 @@ export default {
|
||||
this.settingShow = true;
|
||||
break;
|
||||
|
||||
case "user":
|
||||
this.$set(this.userData, 'project_id', this.projectDetail.id);
|
||||
this.$set(this.userData, 'userids', this.projectDetail.project_user.map(({userid}) => userid));
|
||||
this.$set(this.userData, 'uncancelable', [this.projectDetail.owner_userid]);
|
||||
this.userShow = true;
|
||||
break;
|
||||
|
||||
case "transfer":
|
||||
this.$set(this.transferData, 'project_id', this.projectDetail.id);
|
||||
this.$set(this.transferData, 'owner_userid', [this.projectDetail.owner_userid]);
|
||||
@ -1049,6 +1134,10 @@ export default {
|
||||
case "delete":
|
||||
this.onDelete();
|
||||
break;
|
||||
|
||||
case "exit":
|
||||
this.onExit();
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -2,85 +2,32 @@
|
||||
<div v-if="$store.state.projectChatShow" class="project-message">
|
||||
<div class="group-member">
|
||||
<div class="member-head">
|
||||
<div class="member-title">Member<span>(25)</span></div>
|
||||
<div class="member-view-all">View All</div>
|
||||
<div class="member-title">{{$L('项目成员')}}<span>({{projectDetail.project_user.length}})</span></div>
|
||||
<div class="member-view-all" @click="memberShowAll=!memberShowAll">{{$L('查看所有')}}</div>
|
||||
</div>
|
||||
<ul class="member-list">
|
||||
<li class="online"><Avatar src="https://i.loli.net/2017/08/21/599a521472424.jpg" /></li>
|
||||
<li class="online"><Avatar src="https://i.loli.net/2017/08/21/599a521472424.jpg" /></li>
|
||||
<li class="online"><Avatar src="https://i.loli.net/2017/08/21/599a521472424.jpg" /></li>
|
||||
<li class="online"><Avatar src="https://i.loli.net/2017/08/21/599a521472424.jpg" /></li>
|
||||
<li><Avatar src="https://i.loli.net/2017/08/21/599a521472424.jpg" /></li>
|
||||
<li><Avatar src="https://i.loli.net/2017/08/21/599a521472424.jpg" /></li>
|
||||
<li><Avatar src="https://i.loli.net/2017/08/21/599a521472424.jpg" /></li>
|
||||
<li><Avatar src="https://i.loli.net/2017/08/21/599a521472424.jpg" /></li>
|
||||
<ul :class="['member-list', memberShowAll ? 'member-all' : '']">
|
||||
<li v-for="item in projectDetail.project_user">
|
||||
<UserAvatar :userid="item.userid" :size="36"/>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="group-title">Group Chat</div>
|
||||
<ScrollerY ref="groupChat" class="group-chat" @on-scroll="groupChatScroll">
|
||||
<ScrollerY ref="groupChat" class="group-chat message-scroller" @on-scroll="groupChatScroll">
|
||||
<div ref="manageList" class="message-list">
|
||||
<ul>
|
||||
<li>
|
||||
<div class="message-avatar online"><Avatar src="https://i.loli.net/2017/08/21/599a521472424.jpg" /></div>
|
||||
<div class="message-item">
|
||||
<div class="message-text">Selamat pagi, Mas!</div>
|
||||
<div class="message-time">08:00 AM</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="self">
|
||||
<div class="message-avatar"><Avatar src="https://i.loli.net/2017/08/21/599a521472424.jpg" /></div>
|
||||
<div class="message-item">
|
||||
<div class="message-text">Selamat pagi, Mas!</div>
|
||||
<div class="message-time">08:00 AM</div>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="message-avatar offline"><Avatar src="https://i.loli.net/2017/08/21/599a521472424.jpg" /></div>
|
||||
<div class="message-item">
|
||||
<div class="message-text">Pagi Mas Piko, Langsung saja Ada apa Gerangan mas?</div>
|
||||
<div class="message-time">08:00 AM</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="self">
|
||||
<div class="message-avatar"><Avatar src="https://i.loli.net/2017/08/21/599a521472424.jpg" /></div>
|
||||
<div class="message-item">
|
||||
<div class="message-text">Pagi Mas Piko, Langsung saja Ada apa Gerangan mas?</div>
|
||||
<div class="message-time">08:00 AM</div>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="message-avatar online"><Avatar src="https://i.loli.net/2017/08/21/599a521472424.jpg" /></div>
|
||||
<div class="message-item">
|
||||
<div class="message-text">Selamat pagi, Mas!</div>
|
||||
<div class="message-time">08:00 AM</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="self">
|
||||
<div class="message-avatar"><Avatar src="https://i.loli.net/2017/08/21/599a521472424.jpg" /></div>
|
||||
<div class="message-item">
|
||||
<div class="message-text">Selamat pagi, Mas!</div>
|
||||
<div class="message-time">08:00 AM</div>
|
||||
</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="message-avatar offline"><Avatar src="https://i.loli.net/2017/08/21/599a521472424.jpg" /></div>
|
||||
<div class="message-item">
|
||||
<div class="message-text">Pagi Mas Piko, Langsung saja Ada apa Gerangan mas?</div>
|
||||
<div class="message-time">08:00 AM</div>
|
||||
</div>
|
||||
</li>
|
||||
<li class="self">
|
||||
<div class="message-avatar"><Avatar src="https://i.loli.net/2017/08/21/599a521472424.jpg" /></div>
|
||||
<div class="message-item">
|
||||
<div class="message-text">Pagi Mas Piko, Langsung saja Ada apa Gerangan mas?</div>
|
||||
<div class="message-time">08:00 AM</div>
|
||||
<li v-if="dialogLoad > 0" class="loading"><Loading/></li>
|
||||
<li v-else-if="dialogList.length === 0" class="nothing">{{$L('暂无消息')}}</li>
|
||||
<li v-for="(item, key) in dialogList" :key="key" :class="{self:item.userid == userId}">
|
||||
<div class="message-avatar">
|
||||
<UserAvatar :userid="item.userid" :size="30"/>
|
||||
</div>
|
||||
<MessageView :msg-data="item"/>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</ScrollerY>
|
||||
<div class="group-footer">
|
||||
<DragInput class="group-input" v-model="groupText" type="textarea" :rows="1" :autosize="{ minRows: 1, maxRows: 3 }" :maxlength="255" @on-keydown="groupKeydown" @on-input-paste="groupPasteDrag" :placeholder="$L('输入消息...')" />
|
||||
<DragInput class="group-input" v-model="msgText" type="textarea" :rows="1" :autosize="{ minRows: 1, maxRows: 3 }" :maxlength="255" @on-keydown="groupKeydown" @on-input-paste="groupPasteDrag" :placeholder="$L('输入消息...')" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
@ -130,6 +77,11 @@
|
||||
}
|
||||
.member-view-all {
|
||||
color: #999;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
&:hover {
|
||||
color: #777;
|
||||
}
|
||||
}
|
||||
}
|
||||
.member-list {
|
||||
@ -141,33 +93,19 @@
|
||||
position: relative;
|
||||
list-style: none;
|
||||
margin-right: 14px;
|
||||
.ivu-avatar {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
}
|
||||
&:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 9px;
|
||||
height: 9px;
|
||||
border-radius: 50%;
|
||||
background-color: #ff0000;
|
||||
border: 1px solid #ffffff;
|
||||
z-index: 1;
|
||||
}
|
||||
&.online {
|
||||
&:before {
|
||||
background-color: #87d068;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
&.member-all {
|
||||
display: block;
|
||||
> li {
|
||||
display: inline-block;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.group-title {
|
||||
padding: 0 32px;
|
||||
margin-top: 28px;
|
||||
margin-top: 20px;
|
||||
font-size: 18px;
|
||||
font-weight: 600;
|
||||
}
|
||||
@ -175,78 +113,6 @@
|
||||
flex: 1;
|
||||
padding: 0 32px;
|
||||
margin-top: 18px;
|
||||
overflow: auto;
|
||||
.message-list {
|
||||
> ul {
|
||||
> li {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-end;
|
||||
list-style: none;
|
||||
margin-bottom: 16px;
|
||||
.message-avatar {
|
||||
position: relative;
|
||||
margin-bottom: 20px;
|
||||
.ivu-avatar {
|
||||
width: 28px;
|
||||
height: 28px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
&.online,
|
||||
&.offline {
|
||||
&:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
transform: scale(0.8);
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 9px;
|
||||
height: 9px;
|
||||
border-radius: 50%;
|
||||
background-color: #ff0000;
|
||||
border: 1px solid #ffffff;
|
||||
z-index: 1;
|
||||
}
|
||||
}
|
||||
&.online {
|
||||
&:before {
|
||||
background-color: #87d068;
|
||||
}
|
||||
}
|
||||
}
|
||||
.message-item {
|
||||
max-width: 70%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
margin: 0 0 0 8px;
|
||||
.message-text {
|
||||
color: #333333;
|
||||
background-color: #F4F5F7;
|
||||
padding: 8px;
|
||||
border-radius: 6px 6px 6px 0;
|
||||
}
|
||||
.message-time {
|
||||
color: #bbbbbb;
|
||||
font-size: 12px;
|
||||
padding-top: 3px;
|
||||
}
|
||||
}
|
||||
&.self {
|
||||
flex-direction: row-reverse;
|
||||
.message-item {
|
||||
align-items: flex-end;
|
||||
margin: 0 8px 0 0;
|
||||
.message-text {
|
||||
color: #ffffff;
|
||||
background-color: #2d8cf0;
|
||||
border-radius: 6px 6px 0 6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.group-footer {
|
||||
padding: 0 28px;
|
||||
@ -259,27 +125,98 @@
|
||||
<script>
|
||||
import DragInput from "../../../components/DragInput";
|
||||
import ScrollerY from "../../../components/ScrollerY";
|
||||
import {mapState} from "vuex";
|
||||
import MessageView from "./message-view";
|
||||
|
||||
export default {
|
||||
name: "ProjectMessage",
|
||||
components: {ScrollerY, DragInput},
|
||||
components: {MessageView, ScrollerY, DragInput},
|
||||
data() {
|
||||
return {
|
||||
groupText: '',
|
||||
autoBottom: true
|
||||
autoBottom: true,
|
||||
memberShowAll: false,
|
||||
|
||||
dialogId: 0,
|
||||
|
||||
msgText: '',
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
this.groupChatGoAuto();
|
||||
this.groupChatGoBottom();
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapState(['userId', 'projectDetail', 'projectMsgUnread', 'dialogLoad', 'dialogList']),
|
||||
},
|
||||
|
||||
watch: {
|
||||
projectDetail(detail) {
|
||||
this.dialogId = detail.dialog_id;
|
||||
},
|
||||
|
||||
dialogId(id) {
|
||||
this.$store.commit('getDialogMsg', id);
|
||||
}
|
||||
},
|
||||
|
||||
methods: {
|
||||
groupKeydown() {
|
||||
|
||||
sendMsg() {
|
||||
let mid = $A.randomString(16);
|
||||
this.dialogList.push({
|
||||
id: mid,
|
||||
userid: this.userId,
|
||||
type: 'text',
|
||||
msg: {
|
||||
text: this.msgText,
|
||||
},
|
||||
groupPasteDrag() {
|
||||
|
||||
});
|
||||
this.groupChatGoBottom(true);
|
||||
//
|
||||
$A.apiAjax({
|
||||
url: 'dialog/msg/sendtext',
|
||||
data: {
|
||||
dialog_id: this.projectDetail.dialog_id,
|
||||
text: this.msgText,
|
||||
},
|
||||
error:() => {
|
||||
this.dialogList = this.dialogList.filter(({id}) => id != mid);
|
||||
},
|
||||
success: ({ret, data, msg}) => {
|
||||
if (ret === 1) {
|
||||
let index = this.dialogList.findIndex(({id}) => id == mid);
|
||||
if (index > -1) this.dialogList.splice(index, 1, data);
|
||||
} else {
|
||||
this.dialogList = this.dialogList.filter(({id}) => id != mid);
|
||||
}
|
||||
}
|
||||
});
|
||||
//
|
||||
this.msgText = '';
|
||||
},
|
||||
|
||||
groupKeydown(e) {
|
||||
if (e.keyCode === 13) {
|
||||
if (e.shiftKey) {
|
||||
return;
|
||||
}
|
||||
e.preventDefault();
|
||||
this.sendMsg();
|
||||
}
|
||||
},
|
||||
|
||||
groupPasteDrag(e, type) {
|
||||
const files = type === 'drag' ? e.dataTransfer.files : e.clipboardData.files;
|
||||
const postFiles = Array.prototype.slice.call(files);
|
||||
if (postFiles.length > 0) {
|
||||
e.preventDefault();
|
||||
postFiles.forEach((file) => {
|
||||
// 上传文件
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
groupChatScroll(res) {
|
||||
if (res.directionreal === 'up') {
|
||||
if (res.scrollE < 10) {
|
||||
@ -289,23 +226,40 @@ export default {
|
||||
this.autoBottom = false;
|
||||
}
|
||||
},
|
||||
|
||||
groupChatGoAuto() {
|
||||
clearTimeout(this.groupChatGoTimeout);
|
||||
this.groupChatGoTimeout = setTimeout(() => {
|
||||
if (this.autoBottom) {
|
||||
this.groupChatGoBottom();
|
||||
this.groupChatGoBottom(true);
|
||||
}
|
||||
this.groupChatGoAuto();
|
||||
}, 1000);
|
||||
},
|
||||
|
||||
groupChatGoBottom(animation = false) {
|
||||
this.$nextTick(() => {
|
||||
if (typeof this.$refs.groupChat !== "undefined") {
|
||||
if (this.$refs.groupChat.getScrollInfo().scrollE > 0) {
|
||||
this.$refs.groupChat.scrollTo(this.$refs.manageList.clientHeight, animation);
|
||||
}
|
||||
this.autoBottom = true;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
formatTime(date) {
|
||||
let time = Math.round(new Date(date).getTime() / 1000),
|
||||
string = '';
|
||||
if ($A.formatDate('Ymd') === $A.formatDate('Ymd', time)) {
|
||||
string = $A.formatDate('H:i', time)
|
||||
} else if ($A.formatDate('Y') === $A.formatDate('Y', time)) {
|
||||
string = $A.formatDate('m-d', time)
|
||||
} else {
|
||||
string = $A.formatDate('Y-m-d', time)
|
||||
}
|
||||
return string || '';
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
66
resources/assets/js/store/mutations.js
vendored
66
resources/assets/js/store/mutations.js
vendored
@ -260,7 +260,22 @@ export default {
|
||||
console.log("[WS] Callerr", err);
|
||||
}
|
||||
}
|
||||
})
|
||||
});
|
||||
if (type === "dialog") {
|
||||
const msgData = msgDetail.data;
|
||||
const dialog_id = msgData.dialog_id;
|
||||
if (dialog_id == state.dialogId) {
|
||||
let index = state.dialogList.findIndex(({id}) => id === msgData.id);
|
||||
if (index === -1) {
|
||||
if (state.dialogList.length >= 200) {
|
||||
state.dialogList.splice(0, 1);
|
||||
}
|
||||
state.dialogList.push(msgData);
|
||||
} else {
|
||||
state.dialogList.splice(index, 1, msgData);
|
||||
}
|
||||
}
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
@ -315,5 +330,54 @@ export default {
|
||||
*/
|
||||
wsClose(state) {
|
||||
state.ws && state.ws.close();
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取对话消息
|
||||
* @param state
|
||||
* @param dialog_id
|
||||
*/
|
||||
getDialogMsg(state, dialog_id) {
|
||||
if (state.method.runNum(dialog_id) === 0) {
|
||||
return;
|
||||
}
|
||||
if (state.method.isArray(state.cacheDialog[dialog_id])) {
|
||||
state.dialogList = state.cacheDialog[dialog_id]
|
||||
} else {
|
||||
state.dialogList = [];
|
||||
}
|
||||
state.dialogId = dialog_id;
|
||||
//
|
||||
if (state.cacheDialog[dialog_id + "::load"]) {
|
||||
return;
|
||||
}
|
||||
state.cacheDialog[dialog_id + "::load"] = true;
|
||||
//
|
||||
state.dialogLoad++;
|
||||
$A.apiAjax({
|
||||
url: 'dialog/msg/lists',
|
||||
data: {
|
||||
dialog_id: dialog_id,
|
||||
},
|
||||
complete: () => {
|
||||
state.dialogLoad--;
|
||||
state.cacheDialog[dialog_id + "::load"] = false;
|
||||
},
|
||||
success: ({ret, data, msg}) => {
|
||||
if (ret === 1) {
|
||||
state.cacheDialog[dialog_id] = data.data.reverse();
|
||||
if (state.dialogId === dialog_id) {
|
||||
state.cacheDialog[dialog_id].forEach((item) => {
|
||||
let index = state.dialogList.findIndex(({id}) => id === item.id);
|
||||
if (index === -1) {
|
||||
state.dialogList.push(item);
|
||||
} else {
|
||||
state.dialogList.splice(index, 1, item);
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
31
resources/assets/js/store/state.js
vendored
31
resources/assets/js/store/state.js
vendored
@ -172,18 +172,27 @@ state.wsCall = {};
|
||||
state.wsTimeout = null;
|
||||
state.wsListener = {};
|
||||
|
||||
export default Object.assign(state, {
|
||||
cacheProject: {},
|
||||
cacheUserBasic: {},
|
||||
|
||||
projectLoad: 0,
|
||||
projectList: [],
|
||||
projectDetail: {
|
||||
// 项目信息
|
||||
state.projectLoad = 0;
|
||||
state.projectList = [];
|
||||
state.projectDetail = {
|
||||
id: 0,
|
||||
project_column: [],
|
||||
project_user: []
|
||||
},
|
||||
projectMsgUnread: 0,
|
||||
};
|
||||
state.projectMsgUnread = 0;
|
||||
|
||||
taskPriority: [],
|
||||
})
|
||||
// 会话消息
|
||||
state.dialogId = 0;
|
||||
state.dialogLoad = 0;
|
||||
state.dialogList = [];
|
||||
|
||||
// 任务优先级
|
||||
state.taskPriority = [];
|
||||
|
||||
// 其他
|
||||
state.cacheProject = {};
|
||||
state.cacheUserBasic = {};
|
||||
state.cacheDialog = {};
|
||||
|
||||
export default state
|
||||
|
76
resources/assets/sass/main.scss
vendored
76
resources/assets/sass/main.scss
vendored
@ -666,3 +666,79 @@ body {
|
||||
}
|
||||
}
|
||||
|
||||
.message-scroller {
|
||||
position: relative;
|
||||
overflow: auto;
|
||||
.message-list {
|
||||
> ul {
|
||||
> li {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: flex-end;
|
||||
list-style: none;
|
||||
margin-bottom: 16px;
|
||||
.message-avatar {
|
||||
position: relative;
|
||||
margin-bottom: 20px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.message-view {
|
||||
max-width: 70%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
margin: 0 0 0 8px;
|
||||
.message-content {
|
||||
color: #333333;
|
||||
background-color: #F4F5F7;
|
||||
padding: 8px;
|
||||
border-radius: 6px 6px 6px 0;
|
||||
}
|
||||
.message-unknown {
|
||||
text-decoration: underline;
|
||||
}
|
||||
.message-time {
|
||||
color: #bbbbbb;
|
||||
font-size: 12px;
|
||||
padding-top: 3px;
|
||||
height: 21px;
|
||||
line-height: 21px;
|
||||
.common-loading {
|
||||
margin: 0 2px;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.loading {
|
||||
padding: 12px 0;
|
||||
justify-content: center;
|
||||
.common-loading {
|
||||
margin: 0;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
}
|
||||
}
|
||||
&.nothing {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
color: #999999;
|
||||
}
|
||||
&.self {
|
||||
flex-direction: row-reverse;
|
||||
.message-view {
|
||||
align-items: flex-end;
|
||||
margin: 0 8px 0 0;
|
||||
.message-content {
|
||||
color: #ffffff;
|
||||
background-color: #2d8cf0;
|
||||
border-radius: 6px 6px 0 6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,6 @@
|
||||
<?php
|
||||
|
||||
use App\Http\Controllers\Api\DialogController;
|
||||
use App\Http\Controllers\Api\ProjectController;
|
||||
use App\Http\Controllers\Api\SystemController;
|
||||
use App\Http\Controllers\Api\UsersController;
|
||||
@ -31,6 +32,9 @@ Route::prefix('api')->middleware(['webapi'])->group(function () {
|
||||
// 系统
|
||||
Route::any('system/{method}', SystemController::class);
|
||||
Route::any('system/{method}/{action}', SystemController::class);
|
||||
// 对话
|
||||
Route::any('dialog/{method}', DialogController::class);
|
||||
Route::any('dialog/{method}/{action}', DialogController::class);
|
||||
});
|
||||
|
||||
/**
|
||||
|
Loading…
x
Reference in New Issue
Block a user