Merge branch 'develop' of ssh://git.gezi.vip:6006/gx/dootask

# Conflicts:
#	electron/package.json
#	package.json
#	public/css/app.css
#	public/js/app.js
#	public/js/build/189.js.LICENSE.txt
#	public/js/build/206.js
#	public/js/build/400.js.LICENSE.txt
#	public/js/build/486.js
#	public/js/build/496.js.LICENSE.txt
#	public/js/build/528.js
#	public/js/build/535.js
#	public/js/build/535.js.LICENSE.txt
#	public/js/build/578.js
#	public/js/build/78.js.LICENSE.txt
#	public/js/build/817.js
#	public/js/build/856.js
#	public/js/build/862.js
#	public/js/build/889.js
#	public/js/build/91.js.LICENSE.txt
#	public/js/build/954.js
#	public/js/build/956.js
#	resources/assets/js/pages/manage/components/TaskDetail.vue
#	resources/assets/js/pages/manage/dashboard.vue
#	resources/assets/sass/pages/common.scss
#	resources/assets/sass/pages/page-dashboard.scss
This commit is contained in:
kuaifan 2022-03-10 11:27:00 +08:00
commit 30c149be31
108 changed files with 3805 additions and 127 deletions

View File

@ -155,6 +155,12 @@ class DialogController extends AbstractController
$user->task_dialog_id = $dialog->id;
$user->save();
}
//去掉标记未读
$isMarkDialogUser = WebSocketDialogUser::whereDialogId($dialog->id)->whereUserid($user->userid)->whereIsMarkUnread(1)->first();
if ($isMarkDialogUser) {
$isMarkDialogUser->is_mark_unread = 0;
$isMarkDialogUser->save();
}
//
$data = $list->toArray();
if ($list->currentPage() === 1) {
@ -490,4 +496,48 @@ class DialogController extends AbstractController
'top_at' => $dialogUser->top_at?->toDateTimeString(),
]);
}
/**
* @api {get} api/dialog/msg/mark 13. 消息标记操作
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
* @apiGroup dialog
* @apiName msg__mark
*
* @apiParam {Number} dialog_id 消息ID
* @apiParam {String} type 类型
* - read
* - unread
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据
*/
public function msg__mark()
{
$user = User::auth();
$dialogId = intval(Request::input('dialog_id'));
$type = Request::input('type');
$dialogUser = WebSocketDialogUser::whereUserid($user->userid)->whereDialogId($dialogId)->first();
if (!$dialogUser) {
return Base::retError("会话不存在");
}
if ($type == 'read') {
WebSocketDialogMsgRead::whereUserid($user->userid)
->whereReadAt(null)
->whereDialogId($dialogId)
->update(
[
'read_at' => Carbon::now()
]);
$dialogUser->is_mark_unread = 0;
$dialogUser->save();
} elseif ($type == 'unread') {
$dialogUser->is_mark_unread = 1;
$dialogUser->save();
}
return Base::retSuccess("success");
}
}

View File

@ -13,6 +13,7 @@ use App\Models\ProjectInvite;
use App\Models\ProjectLog;
use App\Models\ProjectTask;
use App\Models\ProjectTaskFile;
use App\Models\ProjectTaskFlowChange;
use App\Models\ProjectUser;
use App\Models\User;
use App\Models\WebSocketDialog;
@ -915,6 +916,7 @@ class ProjectController extends AbstractController
$time_before = Request::input('time_before');
$complete = Request::input('complete', 'all');
$archived = Request::input('archived', 'no');
$deleted = Request::input('deleted');
$keys = Request::input('keys');
$sorts = Request::input('sorts');
$keys = is_array($keys) ? $keys : [];
@ -953,7 +955,7 @@ class ProjectController extends AbstractController
$builder->whereNotNull('project_tasks.end_at')->where('project_tasks.end_at', '<', Carbon::parse($time_before));
} elseif (is_array($time)) {
if (Base::isDateOrTime($time[0]) && Base::isDateOrTime($time[1])) {
$builder->betweenTime(Carbon::parse($time[0])->startOfDay(), Carbon::parse($time[1])->endOfDay());
$builder->betweenTime(Carbon::parse($time[0])->startOfDay(), Carbon::parse($time[1])->endOfDay(),'taskTime');
}
}
//
@ -969,6 +971,10 @@ class ProjectController extends AbstractController
$builder->whereNull('project_tasks.archived_at');
}
//
if ($deleted == 'yes') {
$builder->onlyTrashed();
}
//
foreach ($sorts as $column => $direction) {
if (!in_array($column, ['complete_at', 'archived_at', 'end_at'])) continue;
if (!in_array($direction, ['asc', 'desc'])) continue;
@ -1013,56 +1019,111 @@ class ProjectController extends AbstractController
if (Carbon::parse($time[1])->timestamp - Carbon::parse($time[0])->timestamp > 90 * 86400) {
return Base::retError('时间范围限制最大90天');
}
//
$type = Request::input('type','taskTime');
$headings = [];
$headings[] = '任务ID';
$headings[] = '父级任务ID';
$headings[] = '所属项目';
$headings[] = '任务标题';
$headings[] = '负责人';
$headings[] = '创建人';
$headings[] = '是否完成';
$headings[] = '完成时间';
$headings[] = '是否归档';
$headings[] = '归档时间';
$headings[] = '任务开始时间';
$headings[] = '任务结束时间';
$headings[] = '结束剩余';
$headings[] = '所属项目';
$headings[] = '父级任务ID';
$headings[] = '完成时间';
$headings[] = '归档时间';
$headings[] = '任务计划用时';
$headings[] = '实际完成用时';
$headings[] = '超时时间';
$headings[] = '开发用时';
$headings[] = '验收/测试用时';
$headings[] = '负责人';
$headings[] = '创建人';
$datas = [];
//
$builder = ProjectTask::select(['project_tasks.*', 'project_task_users.userid as ownerid'])
->join('project_task_users', 'project_tasks.id', '=', 'project_task_users.task_id')
->where('project_task_users.owner', 1)
->whereIn('project_task_users.userid', $userid)
->betweenTime(Carbon::parse($time[0])->startOfDay(), Carbon::parse($time[1])->endOfDay());
->betweenTime(Carbon::parse($time[0])->startOfDay(), Carbon::parse($time[1])->endOfDay(),$type);
$builder->orderByDesc('project_tasks.id')->chunk(100, function($tasks) use (&$datas) {
/** @var ProjectTask $task */
foreach ($tasks as $task) {
$flowChanges = ProjectTaskFlowChange::whereTaskId($task->id)->get();
$developTime = 0;//开发时间
$testTime = 0;//验收/测试时间
foreach ($flowChanges as $change) {
if (strpos($change->before_flow_item_name, 'end') === false) {
$upOne = ProjectTaskFlowChange::where('id', '<', $change->id)->whereTaskId($task->id)->orderByDesc('id')->first();
if ($upOne) {
if (strpos($change->before_flow_item_name, 'progress') !== false && strpos($change->before_flow_item_name, '进行') !== false) {
$devCtime = Carbon::parse($change->created_at)->timestamp;
$oCtime = Carbon::parse($upOne->created_at)->timestamp;
$minusNum = $devCtime - $oCtime;
$developTime += $minusNum;
}
if (strpos($change->before_flow_item_name, 'test') !== false || strpos($change->before_flow_item_name, '测试') !== false || strpos($change->before_flow_item_name, '验收') !== false) {
$testCtime = Carbon::parse($change->created_at)->timestamp;
$tTime = Carbon::parse($upOne->created_at)->timestamp;
$tMinusNum = $testCtime - $tTime;
$testTime += $tMinusNum;
}
}
}
}
if (!$task->complete_at) {
$lastChange = ProjectTaskFlowChange::whereTaskId($task->id)->orderByDesc('id')->first();
$nowTime = time();
$unFinishTime = $nowTime - Carbon::parse($lastChange->created_at)->timestamp;
if (strpos($lastChange->after_flow_item_name, 'progress') !== false || strpos($lastChange->after_flow_item_name, '进行') !== false) {
$developTime += $unFinishTime;
} elseif (strpos($lastChange->after_flow_item_name, 'test') !== false || strpos($lastChange->after_flow_item_name, '测试') !== false || strpos($lastChange->after_flow_item_name, '验收') !== false) {
$testTime += $unFinishTime;
}
}
$firstChange = ProjectTaskFlowChange::whereTaskId($task->id)->orderBy('id')->first();
if (strpos($firstChange->after_flow_item_name, 'end') !== false) {
$firstDevTime = Carbon::parse($firstChange->created_at)->timestamp - Carbon::parse($task->created_at)->timestamp;
$developTime += $firstDevTime;
}
if (count($flowChanges) === 0 && $task->start_at) {
$lastTime = $task->complete_at ? Carbon::parse($task->complete_at)->timestamp : time();
$developTime = $lastTime - Carbon::parse($task->start_at)->timestamp;
}
$totalTime = $developTime + $testTime; //任务总用时
if ($task->complete_at) {
$a = Carbon::parse($task->complete_at)->timestamp;
$b = Carbon::parse($task->end_at)->timestamp;
if ($b > $a) {
$endSurplus = Base::timeDiff($a, $b);
} else {
$endSurplus = "-" . Base::timeDiff($b, $a);
if ($task->start_at) {
$b = Carbon::parse($task->start_at)->timestamp;
$totalTime = $a - $b;
}
} else {
$endSurplus = '-';
}
$planTime = '-';//任务计划用时
$overTime = '-';//超时时间
if ($task->end_at) {
$startTime = Carbon::parse($task->start_at)->timestamp;
$endTime = Carbon::parse($task->end_at)->timestamp;
$planTotalTime = $endTime - $startTime;
$residueTime = $planTotalTime - $totalTime;
if ($residueTime < 0) {
$overTime = Base::timeFormat(abs($residueTime));
}
$planTime = Base::timeDiff($startTime, $endTime);
}
$actualTime = $task->complete_at ? $totalTime : 0;//实际完成用时
$datas[] = [
$task->id,
$task->parent_id ?: '-',
Base::filterEmoji($task->project?->name) ?: '-',
Base::filterEmoji($task->name),
Base::filterEmoji(User::userid2nickname($task->ownerid)) . " (ID: {$task->ownerid})",
Base::filterEmoji(User::userid2nickname($task->userid)) . " (ID: {$task->userid})",
$task->complete_at ? '已完成' : '-',
$task->complete_at ?: '-',
$task->archived_at ? '已归档' : '-',
$task->archived_at ?: '-',
$task->start_at ?: '-',
$task->end_at ?: '-',
$endSurplus,
Base::filterEmoji($task->project?->name) ?: '-',
$task->parent_id ?: '-',
$task->complete_at ?: '-',
$task->archived_at ?: '-',
$planTime ?: '-',
$actualTime ? Base::timeFormat($actualTime) : '-',
$overTime,
$developTime > 0? Base::timeFormat($developTime) : '-',
$testTime > 0 ? Base::timeFormat($testTime) : '-',
Base::filterEmoji(User::userid2nickname($task->ownerid)) . " (ID: {$task->ownerid})",
Base::filterEmoji(User::userid2nickname($task->userid)) . " (ID: {$task->userid})",
];
}
});
@ -1471,6 +1532,9 @@ class ProjectController extends AbstractController
$task_id = intval($data['task_id']);
//
$task = ProjectTask::userTask($task_id, true, 2);
if ($task->deleted_at) {
throw new ApiException('任务已删除');
}
// 更新任务
$updateMarking = [];
$task->updateTask($data, $updateMarking);
@ -1590,8 +1654,21 @@ class ProjectController extends AbstractController
User::auth();
//
$task_id = intval(Request::input('task_id'));
$type = Request::input('type');
//
$task = ProjectTask::userTask($task_id, null, true);
if($type == 'recovery'){
$task->deleted_at = null;
$task->deleted_userid = 0;
$task->save();
return Base::retSuccess('操作成功', ['id' => $task->id]);
}
if($type == 'completely_delete'){
$task->forceDelete();
return Base::retSuccess('彻底删除成功', ['id' => $task->id]);
}
$task->deleted_userid = User::userid();
$task->save();
//
$task->deleteTask();
return Base::retSuccess('删除成功', ['id' => $task->id]);

View File

@ -41,7 +41,7 @@ class SystemController extends AbstractController
User::auth('admin');
$all = Request::input();
foreach ($all AS $key => $value) {
if (!in_array($key, ['reg', 'reg_invite', 'login_code', 'password_policy', 'project_invite', 'chat_nickname', 'auto_archived', 'archived_day'])) {
if (!in_array($key, ['reg', 'reg_invite', 'login_code', 'password_policy', 'project_invite', 'chat_nickname', 'auto_archived', 'archived_day', 'start_home', 'home_footer'])) {
unset($all[$key]);
}
}
@ -72,6 +72,7 @@ class SystemController extends AbstractController
$setting['chat_nickname'] = $setting['chat_nickname'] ?: 'optional';
$setting['auto_archived'] = $setting['auto_archived'] ?: 'close';
$setting['archived_day'] = floatval($setting['archived_day']) ?: 7;
$setting['start_home'] = $setting['start_home'] ?: 'close';
//
return Base::retSuccess('success', $setting ?: json_decode('{}'));
}
@ -478,4 +479,79 @@ class SystemController extends AbstractController
//
return $data;
}
/**
* @api {get} api/system/get/starthome 13. 启动首页设置信息
*
* @apiDescription 用于判断注册是否需要启动首页
* @apiVersion 1.0.0
* @apiGroup system
* @apiName get__starthome
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据
*/
public function get__starthome()
{
return Base::retSuccess('success', [
'need_start' => Base::settingFind('system', 'start_home') == 'open',
'home_footer' => Base::settingFind('system', 'home_footer')
]);
}
/**
* @api {get} api/system/emailSetting 14. 获取邮箱设置、保存邮箱设置
*
* @apiVersion 1.0.0
* @apiGroup system
* @apiName emailSetting
*
* @apiParam {String} type
* - get: 获取(默认)
* - all: 获取所有(需要管理员权限)
* - save: 保存设置参数smtp_server port account password reg_verify notice task_remind_hours task_remind_hours2
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据
*/
public function emailSetting()
{
$type = trim(Request::input('type'));
if ($type == 'save') {
if (env("SYSTEM_SETTING") == 'disabled') {
return Base::retError('当前环境禁止修改');
}
User::auth('admin');
$all = Request::input();
foreach ($all as $key => $value) {
if (!in_array($key, ['smtp_server', 'port', 'account', 'password', 'reg_verify', 'notice', 'task_remind_hours', 'task_remind_hours2'])) {
unset($all[$key]);
}
}
$setting = Base::setting('emailSetting', Base::newTrim($all));
} else {
$setting = Base::setting('emailSetting');
}
//
if ($type == 'all' || $type == 'save') {
User::auth('admin');
$setting['reg_invite'] = $setting['reg_invite'] ?: Base::generatePassword(8);
} else {
if (isset($setting['reg_invite'])) unset($setting['reg_invite']);
}
//
$setting['smtp_server'] = $setting['smtp_server'] ?: '';
$setting['port'] = $setting['port'] ?: '';
$setting['account'] = $setting['account'] ?: '';
$setting['password'] = $setting['password'] ?: '';
$setting['reg_verify'] = $setting['reg_verify'] ?: 'close';
$setting['notice'] = $setting['notice'] ?: 'open';
$setting['task_remind_hours'] = floatval($setting['task_remind_hours']) ?: 0;
$setting['task_remind_hours2'] = floatval($setting['task_remind_hours2']) ?: 0;
//
return Base::retSuccess('success', $setting ?: json_decode('{}'));
}
}

View File

@ -3,12 +3,14 @@
namespace App\Http\Controllers\Api;
use App\Models\User;
use App\Models\UserEmailVerification;
use App\Module\Base;
use Arr;
use Cache;
use Captcha;
use Carbon\Carbon;
use Request;
use Validator;
/**
* @apiDefine users
@ -42,6 +44,7 @@ class UsersController extends AbstractController
$type = trim(Request::input('type'));
$email = trim(Request::input('email'));
$password = trim(Request::input('password'));
$isRegVerify = Base::settingFind('emailSetting', 'reg_verify') === 'open' ? true : false;
if ($type == 'reg') {
$setting = Base::setting('system');
if ($setting['reg'] == 'close') {
@ -53,6 +56,10 @@ class UsersController extends AbstractController
}
}
$user = User::reg($email, $password);
if ($isRegVerify) {
UserEmailVerification::userEmailSend($user);
return Base::retError('注册成功,请验证邮箱后登录', ['code' => 1000]);
}
} else {
$needCode = !Base::isError(User::needCode($email));
if ($needCode) {
@ -68,21 +75,25 @@ class UsersController extends AbstractController
$retError = function ($msg) use ($email) {
Cache::forever("code::" . $email, "need");
$needCode = !Base::isError(User::needCode($email));
$needData = [ 'code' => $needCode ? 'need' : 'no' ];
$needData = ['code' => $needCode ? 'need' : 'no'];
return Base::retError($msg, $needData);
};
$user = User::whereEmail($email)->first();
if (empty($user)) {
return $retError('账号或密码错误');
return $retError('账号不存在,请确认账号是否输入正确');
}
if ($user->password != Base::md52($password, $user->encrypt)) {
return $retError('账号或密码错误');
return $retError('密码错误,请输入正确密码');
}
//
if (in_array('disable', $user->identity)) {
return $retError('帐号已停用...');
}
Cache::forget("code::" . $email);
if ($isRegVerify && $user->is_email_verity === 0) {
UserEmailVerification::userEmailSend($user);
return $retError('请验证邮箱后再登录');
}
}
//
$array = [
@ -559,4 +570,53 @@ class UsersController extends AbstractController
//
return Base::retSuccess('修改成功', $userInfo);
}
/**
* @api {get} api/users/email/verification 13. 邮箱验证
*
* @apiDescription 不需要token身份
* @apiVersion 1.0.0
* @apiGroup users
* @apiName email__verification
*
* @apiParam {String} code 验证参数
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据(同"获取我的信息"接口)
*/
public function email__verification()
{
$data = Request::input();
// 表单验证
$validator = Validator::make($data, [
"code" => ["required"],
], [
"code.required" => "required字段非法",
]);
if ($validator->fails())
return Base::retError($validator->errors()->first());
$res = UserEmailVerification::where('code', $data['code'])->first();
if (empty($res)) {
return Base::retError('无效连接,请重新注册');
}
// 如果已经校验过
if (intval($res->status) === 1)
return Base::retError('链接已经使用过',['code' => 2]);
$oldTime = strtotime($res->created_at);
$time = time();
//24个小时失效
if (abs($time - $oldTime) > 86400) {
return Base::retError("链接已失效,请重新登录/注册");
}
UserEmailVerification::where('code', $data['code'])
->update([
'status' => 1
]);
User::where('userid', $res->userid)->update([
'is_email_verity' => 1
]);
return Base::retSuccess('绑定邮箱成功');
}
}

View File

@ -5,6 +5,7 @@ namespace App\Http\Controllers;
use App\Module\Base;
use App\Tasks\AutoArchivedTask;
use App\Tasks\DeleteTmpTask;
use App\Tasks\OverdueRemindEmailTask;
use Hhxsv5\LaravelS\Swoole\Task\Task;
use Redirect;
@ -61,6 +62,8 @@ class IndexController extends InvokeController
Task::deliver(new DeleteTmpTask('tmp', 24));
// 自动归档任务
Task::deliver(new AutoArchivedTask());
// 任务到期邮件提醒
Task::deliver(new OverdueRemindEmailTask());
return "success";
}

View File

@ -376,6 +376,7 @@ class Project extends AbstractModel
$idc = [];
$hasStart = false;
$hasEnd = false;
$testNum = 0;
$upTaskList = [];
foreach ($flows as $item) {
$id = intval($item['id']);
@ -416,6 +417,9 @@ class Project extends AbstractModel
if ($flow->status == 'end') {
$hasEnd = true;
}
if ($flow->status == 'test') {
$testNum++;
}
if (!$isInsert) {
$upTaskList[$flow->id] = $flow->status . "|" . $flow->name;
}
@ -424,6 +428,9 @@ class Project extends AbstractModel
if (!$hasStart) {
throw new ApiException('至少需要1个开始状态');
}
if($testNum > 1){
throw new ApiException('验收/测试状态只能有1个');
}
if (!$hasEnd) {
throw new ApiException('至少需要1个结束状态');
}

View File

@ -7,10 +7,12 @@ use App\Module\Base;
use App\Tasks\PushTask;
use Arr;
use Carbon\Carbon;
use Config;
use DB;
use Exception;
use Hhxsv5\LaravelS\Swoole\Task\Task;
use Illuminate\Database\Eloquent\SoftDeletes;
use Mail;
use Request;
/**
@ -40,6 +42,7 @@ use Request;
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property \Illuminate\Support\Carbon|null $deleted_at
* @property int|null $deleted_userid 删除会员
* @property-read \App\Models\ProjectTaskContent|null $content
* @property-read int $file_num
* @property-read int $msg_num
@ -58,7 +61,7 @@ use Request;
* @property-read int|null $task_user_count
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask allData($userid = null)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask authData($userid = null, $owner = null)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask betweenTime($start, $end)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask betweenTime($start, $end, $type)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask newQuery()
* @method static \Illuminate\Database\Query\Builder|ProjectTask onlyTrashed()
@ -71,6 +74,7 @@ use Request;
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask whereCompleteAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask whereDeletedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask whereDeletedUserid($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask whereDesc($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask whereDialogId($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask whereEndAt($value)
@ -312,18 +316,29 @@ class ProjectTask extends AbstractModel
* @param $query
* @param $start
* @param $end
* @param $type
* @return mixed
*/
public function scopeBetweenTime($query, $start, $end)
public function scopeBetweenTime($query, $start, $end, $type)
{
$query->where(function ($q1) use ($start, $end) {
$q1->where(function ($q2) use ($start) {
$q2->where('project_tasks.start_at', '<=', $start)->where('project_tasks.end_at', '>=', $start);
})->orWhere(function ($q2) use ($end) {
$q2->where('project_tasks.start_at', '<=', $end)->where('project_tasks.end_at', '>=', $end);
})->orWhere(function ($q2) use ($start, $end) {
$q2->where('project_tasks.start_at', '>', $start)->where('project_tasks.end_at', '<', $end);
});
$query->where(function ($q1) use ($start, $end, $type) {
if ($type === 'taskTime') {
$q1->where(function ($q2) use ($start) {
$q2->where('project_tasks.start_at', '<=', $start)->where('project_tasks.end_at', '>=', $start);
})->orWhere(function ($q2) use ($end) {
$q2->where('project_tasks.start_at', '<=', $end)->where('project_tasks.end_at', '>=', $end);
})->orWhere(function ($q2) use ($start, $end) {
$q2->where('project_tasks.start_at', '>', $start)->where('project_tasks.end_at', '<', $end);
});
} else {
$q1->where(function ($q2) use ($start) {
$q2->where('project_tasks.created_at', '>=', $start);
})->orWhere(function ($q2) use ($end) {
$q2->where('project_tasks.created_at', '<=', $end);
})->orWhere(function ($q2) use ($start, $end) {
$q2->where('project_tasks.created_at', '>', $start)->where('project_tasks.created_at', '<', $end);
});
}
});
return $query;
}
@ -742,6 +757,9 @@ class ProjectTask extends AbstractModel
$this->addLog("修改{任务}时间", [
'change' => [$oldStringAt, $newStringAt]
]);
//修改计划时间需要重置任务邮件提醒日志
ProjectTaskMailLog::whereTaskId($this->id)->delete();
}
// 以下紧顶级任务可修改
if ($this->parent_id === 0) {
@ -1179,6 +1197,7 @@ class ProjectTask extends AbstractModel
public static function userTask($task_id, $archived = true, $permission = 0, $with = [])
{
$task = self::with($with)->allData()->where("project_tasks.id", intval($task_id))->first();
$task = $task ?: ProjectTask::where("project_tasks.id", intval($task_id))->onlyTrashed()->first();
//
if (empty($task)) {
throw new ApiException('任务不存在', [ 'task_id' => $task_id ], -4002);
@ -1211,4 +1230,58 @@ class ProjectTask extends AbstractModel
//
return $task;
}
/**
* 预超期任务提醒
* @param $task
*/
public static function overdueRemindEmail($task)
{
$ownerIds = ProjectTaskUser::whereTaskId($task['id'])->whereOwner(1)->pluck('userid')->toArray();
$users = User::whereIn('userid', $ownerIds)->get();
if (!$users) {
throw new ApiException("ProjectTask::overdueRemindEmail--没有负责人");
}
$type = $task['end_at'] < Carbon::now() ? 2 : 1;
$setting = Base::setting('emailSetting');
$hours = floatval($setting['task_remind_hours']);
$hours2 = floatval($setting['task_remind_hours2']);
$time = $type === 1 ? $hours : $hours2;
Config::set("mail.mailers.smtp.host", Base::settingFind('emailSetting', 'smtp_server') ?: Config::get("mail.mailers.smtp.host"));
Config::set("mail.mailers.smtp.port", Base::settingFind('emailSetting', 'port') ?: Config::get("mail.mailers.smtp.port"));
Config::set("mail.mailers.smtp.username", Base::settingFind('emailSetting', 'account') ?: Config::get("mail.mailers.smtp.username"));
Config::set("mail.mailers.smtp.password", Base::settingFind('emailSetting', 'password') ?: Config::get("mail.mailers.smtp.password"));
foreach ($users as $user) {
/** @var User $user */
if (ProjectTaskMailLog::whereTaskId($task['id'])->whereUserid($user->userid)->whereType($type)->whereIsSend(1)->exists()) {
return;
}
$email = $user->email;
$isSend = 1;
try {
$emailContent = [
'name' => $task['name'],
'time' => $time,
'type' => $type
];
Mail::send('taskOverdueRemind', $emailContent, function ($m) use ($email) {
$m->from(Config::get("mail.mailers.smtp.username"), env('APP_NAME'));
$m->to($email);
$m->subject("任务提醒");
});
} catch (\Exception $e) {
$isSend = 0;
\Log::error($email . '--邮箱发送报错:', [$e->getMessage()]);
}
$logData = [
'userid' => $user->userid,
'task_id' => $task['id'],
'email' => $email,
'type' => $type,
'is_send' => $isSend,
];
$emailLog = ProjectTaskMailLog::whereTaskId($task['id'])->whereUserid($user->userid)->whereType($type)->first();
ProjectTaskMailLog::createInstance($logData, $emailLog->id ?? null)->save();
}
}
}

View File

@ -0,0 +1,43 @@
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\SoftDeletes;
/**
* App\Models\ProjectTaskMailLog
*
* @property int $id
* @property int|null $userid 用户id
* @property int|null $task_id 任务id
* @property string|null $email 电子邮箱
* @property int|null $type 提醒类型1第一次任务提醒2第二次任务超期提醒
* @property int|null $is_send 邮件发送是否成功0否1是
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property \Illuminate\Support\Carbon|null $deleted_at
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskMailLog newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskMailLog newQuery()
* @method static \Illuminate\Database\Query\Builder|ProjectTaskMailLog onlyTrashed()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskMailLog query()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskMailLog whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskMailLog whereDeletedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskMailLog whereEmail($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskMailLog whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskMailLog whereIsSend($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskMailLog whereTaskId($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskMailLog whereType($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskMailLog whereUpdatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskMailLog whereUserid($value)
* @method static \Illuminate\Database\Query\Builder|ProjectTaskMailLog withTrashed()
* @method static \Illuminate\Database\Query\Builder|ProjectTaskMailLog withoutTrashed()
* @mixin \Eloquent
*/
class ProjectTaskMailLog extends AbstractModel
{
use SoftDeletes;
}

View File

@ -29,6 +29,7 @@ use Carbon\Carbon;
* @property int|null $task_dialog_id 最后打开的任务会话ID
* @property string|null $created_ip 注册IP
* @property string|null $disable_at 禁用时间
* @property int $is_email_verity 邮箱是否已验证
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Database\Factories\UserFactory factory(...$parameters)
@ -43,6 +44,7 @@ use Carbon\Carbon;
* @method static \Illuminate\Database\Eloquent\Builder|User whereEmail($value)
* @method static \Illuminate\Database\Eloquent\Builder|User whereEncrypt($value)
* @method static \Illuminate\Database\Eloquent\Builder|User whereIdentity($value)
* @method static \Illuminate\Database\Eloquent\Builder|User whereIsEmailVerity($value)
* @method static \Illuminate\Database\Eloquent\Builder|User whereLastAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|User whereLastIp($value)
* @method static \Illuminate\Database\Eloquent\Builder|User whereLineAt($value)
@ -185,6 +187,12 @@ class User extends AbstractModel
throw new ApiException('请输入正确的邮箱地址');
}
if (User::email2userid($email) > 0) {
$isRegVerify = Base::settingFind('emailSetting', 'reg_verify') === 'open' ? true : false;
$user = self::whereUserid(User::email2userid($email))->first();
if ($isRegVerify && $user->is_email_verity === 0) {
UserEmailVerification::userEmailSend($user);
throw new ApiException('您的账号已注册过,请验证邮箱');
}
throw new ApiException('邮箱地址已存在');
}
//密码

View File

@ -0,0 +1,79 @@
<?php
namespace App\Models;
use App\Exceptions\ApiException;
use App\Module\Base;
use Carbon\Carbon;
use Config;
use Exception;
use Mail;
/**
* App\Models\UserEmailVerification
*
* @property int $id
* @property int $userid 用户id
* @property string $code 验证参数
* @property string $email 电子邮箱
* @property string $status 0-未验证1-已验证
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder|WebSocket newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|WebSocket newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|WebSocket query()
* @method static \Illuminate\Database\Eloquent\Builder|WebSocket whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocket whereCode($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocket whereEmail($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocket whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocket whereStatus($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocket whereUpdatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocket whereUserid($value)
* @mixin \Eloquent
*/
class UserEmailVerification extends AbstractModel
{
/**
* 发验证邮箱
* @param User $user
*/
public static function userEmailSend(User $user)
{
$res = self::where('userid', $user->userid)->where('created_at', '>', Carbon::now()->subMinutes(1440))->first();
if ($res) return;
//删除
self::where('userid', $user->userid)->delete();
$info['created_at'] = date("Y-m-d H:i:s");
$info['userid'] = $user->userid;
$info['email'] = $user->email;
$info['code'] = md5(uniqid(md5(microtime(true)), true)) . md5($user->userid . md5('lddsgagsgkdiid' . microtime(true)));
$url = Base::fillUrl('single/valid/email') . '?code=' . $info['code'];
$info['status'] = 0;
$userEmailVerification = self::createInstance($info);
$userEmailVerification->save();
try {
// 15秒后超时
Config::set("mail.mailers.smtp.host", Base::settingFind('emailSetting', 'smtp_server') ?: Config::get("mail.mailers.smtp.host"));
Config::set("mail.mailers.smtp.port", Base::settingFind('emailSetting', 'port') ?: Config::get("mail.mailers.smtp.port"));
Config::set("mail.mailers.smtp.username", Base::settingFind('emailSetting', 'account') ?: Config::get("mail.mailers.smtp.username"));
Config::set("mail.mailers.smtp.password", Base::settingFind('emailSetting', 'password') ?: Config::get("mail.mailers.smtp.password"));
Mail::send('email', ['url' => $url], function ($m) use ($user) {
$m->from(Base::settingFind('emailSetting', 'account') ?: Config::get("mail.mailers.smtp.username"), env('APP_NAME'));
$m->to($user->email);
$m->subject("绑定邮箱验证");
});
} catch (Exception $exception) {
// 一般是请求超时
if (strpos($exception->getMessage(), "Timed Out") !== false) {
throw new ApiException("language.TimedOut");
} elseif ($exception->getCode() == 550) {
throw new ApiException('邮件内容被拒绝,请检查邮箱是否开启接收功能');
} else {
throw new ApiException($exception->getMessage());
}
}
}
}

View File

@ -105,13 +105,16 @@ class WebSocketDialog extends AbstractModel
$last_msg = WebSocketDialogMsg::whereDialogId($dialog->id)->orderByDesc('id')->first();
$dialog->last_msg = $last_msg;
// 未读信息
$dialog->unread = WebSocketDialogMsgRead::whereDialogId($dialog->id)->whereUserid($userid)->whereReadAt(null)->count();
$unread = WebSocketDialogMsgRead::whereDialogId($dialog->id)->whereUserid($userid)->whereReadAt(null)->count();
$isMarkUnread = WebSocketDialogUser::whereDialogId($dialog->id)->whereUserid($userid)->whereIsMarkUnread(1)->exists();
$dialog->unread = $unread > 0 ? $unread : ($isMarkUnread ? 1 : 0);
// 对话人数
$builder = WebSocketDialogUser::whereDialogId($dialog->id);
$dialog->people = $builder->count();
// 对方信息
$dialog->dialog_user = null;
$dialog->group_info = null;
$dialog->top_at = $dialog->top_at ?? WebSocketDialogUser::whereDialogId($dialog->id)->whereUserid($userid)->value('top_at');
switch ($dialog->type) {
case "user":
$dialog_user = $builder->where('userid', '!=', $userid)->first();

View File

@ -9,6 +9,7 @@ namespace App\Models;
* @property int|null $dialog_id 对话ID
* @property int|null $userid 会员ID
* @property string|null $top_at 置顶时间
* @property int $is_mark_unread 是否标记为未读0否1是
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser newModelQuery()
@ -17,6 +18,7 @@ namespace App\Models;
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereDialogId($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereIsMarkUnread($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereTopAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereUpdatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|WebSocketDialogUser whereUserid($value)

View File

@ -1712,24 +1712,46 @@ class Base
*/
public static function timeDiff($s, $e)
{
$d = $e - $s;
if ($d > 86400) {
$day = floor($d / 86400);
$hour = ceil(($d - ($day * 86400)) / 3600);
if ($hour > 0) {
return $day . '天' . $hour . '小时';
} else {
return $day . '天';
}
} elseif ($d > 3600) {
return ceil($d / 3600) . '小时';
} elseif ($d > 60) {
return ceil($d / 60) . '分钟';
} elseif ($d > 1) {
return '1分钟内';
} else {
return '0秒';
$time = $e - $s;
$days = 0;
if ($time >= 86400) { // 如果大于1天
$days = (int)($time / 86400);
$time = $time % 86400; // 计算天后剩余的毫秒数
}
$hours = 0;
if ($time >= 3600) { // 如果大于1小时
$hours = (int)($time / 3600);
$time = $time % 3600; // 计算小时后剩余的毫秒数
}
$minutes = ceil($time / 60); // 剩下的毫秒数都算作分
$daysStr = $days > 0 ? $days . '天' : '';
$hoursStr = ($hours > 0 || ($days > 0 && $minutes > 0)) ? $hours . '时' : '';
$minuteStr = ($minutes > 0) ? $minutes . '分' : '';
return $daysStr . $hoursStr . $minuteStr;
}
/**
* 时间秒数格式化
* @param int $time 时间秒数
* @return string
*/
public static function timeFormat($time)
{
$days = 0;
if ($time >= 86400) { // 如果大于1天
$days = (int)($time / 86400);
$time = $time % 86400; // 计算天后剩余的毫秒数
}
$hours = 0;
if ($time >= 3600) { // 如果大于1小时
$hours = (int)($time / 3600);
$time = $time % 3600; // 计算小时后剩余的毫秒数
}
$minutes = ceil($time / 60); // 剩下的毫秒数都算作分
$daysStr = $days > 0 ? $days . '天' : '';
$hoursStr = ($hours > 0 || ($days > 0 && $minutes > 0)) ? $hours . '时' : '';
$minuteStr = ($minutes > 0) ? $minutes . '分' : '';
return $daysStr . $hoursStr . $minuteStr;
}
/**
@ -2978,4 +3000,112 @@ class Base
},
$str);
}
//去重复
public static function assoc_unique($array, $keyid,$desc=true) {
if(empty($array)){
return false;
}
$array = array_values($array);
//倒叙排列数
if($desc)
{
$array =(new Self)->array_rsort($array,true);
}
//提取需要判断的项目变成一维数组
$a = (new Self)->array_tq($array,$keyid);
//去除一维数组重复值
$a = array_unique($a);
//提取二维数组项目值
foreach($array[0] AS $key=>$value)
{
$akey[] = $key;
}
//重新拼接二维数组
foreach($akey AS $key=>$value)
{
$b = (new Self)->array_tq($array,$value);
foreach($a AS $key2=>$value2)
{
$c[$key2][$value] = $b[$key2];
}
}
if($desc)
{
$c = (new Self)->array_rsort($c,true);
}
return $c;
}
//提取二维数组项目
public static function array_tq($array,$aval="")
{
foreach($array AS $key=>$value)
{
$result[] = $value[$aval];
}
return $result;
}
public static function array_rsort($arr,$isvalues=false)
{
if(is_array($arr)){
$flag = false;
//一维数组
if(count($arr) == count($arr,1)){
$flag = true;
$i = 0;
//转换成二维数组
foreach($arr AS $key=>$value){
$a[$i]["okey"] = $key;
$a[$i]["value"] = $value;
$i++;
}
$arr = $a;
}
//多维数组
else
{
//添加临时key值
foreach($arr AS $key=>$value){
$value["okey"] = $key;
$array[] = $value;
}
$arr = $array;
}
//倒叙并还原key值
$count = count($arr)-1;
for($i=0;$i<count($arr);$i++){
$b[$arr[$count]["okey"]] = $arr[$count];
$count--;
}
//重构一维数组
if($flag == true){
foreach($b AS $key=>$value){
if($isvalues){
$c[] = $value["value"];
}else{
$c[$value["okey"]] = $value["value"];
}
}
}
//多维数组去除临时key值
else
{
foreach($b AS $key=>$value) {
unset($value["okey"]);
if($isvalues){
$c[] = $value;
}else{
$c[$key] = $value;
}
}
}
return $c;
}
}
}

View File

@ -0,0 +1,53 @@
<?php
namespace App\Tasks;
use App\Models\ProjectTask;
use App\Module\Base;
use Carbon\Carbon;
class OverdueRemindEmailTask extends AbstractTask
{
public function __construct()
{
//
}
public function start()
{
$setting = Base::setting('emailSetting');
if ($setting['notice'] === 'open') {
$hours = floatval($setting['task_remind_hours']);
$hours2 = floatval($setting['task_remind_hours2']);
$taskLists1 = [];
$taskLists2 = [];
if ($hours > 0) {
$taskLists1 = ProjectTask::whereNull('complete_at')
->where('end_at', '>=', Carbon::now()->addMinutes($hours * 60 - 3)->rawFormat('Y-m-d H:i:s'))
->where('end_at', '<=', Carbon::now()->addMinutes($hours * 60 + 3)->rawFormat('Y-m-d H:i:s'))
->whereNull('archived_at')
->take(100)
->get()
->toArray();
}
if ($hours2 > 0) {
$taskLists2 = ProjectTask::whereNull('complete_at')
->where('end_at', '>=', Carbon::now()->subMinutes($hours2 * 60 + 3)->rawFormat('Y-m-d H:i:s'))
->where('end_at', '<=', Carbon::now()->subMinutes($hours2 * 60 - 3)->rawFormat('Y-m-d H:i:s'))
->whereNull('archived_at')
->take(100)
->get()
->toArray();
}
$taskLists = array_merge($taskLists1, $taskLists2);
$taskLists = Base::assoc_unique($taskLists, 'id');
foreach ($taskLists as $task) {
ProjectTask::overdueRemindEmail($task);
}
}
}
}

View File

@ -36,15 +36,14 @@ return [
'mailers' => [
'smtp' => [
'transport' => 'smtp',
'host' => env('MAIL_HOST', 'smtp.mailgun.org'),
'port' => env('MAIL_PORT', 587),
'encryption' => env('MAIL_ENCRYPTION', 'tls'),
'username' => env('MAIL_USERNAME'),
'password' => env('MAIL_PASSWORD'),
'timeout' => null,
'auth_mode' => null,
'host' => 'smtp.qq.com',
'port' => 465,
'encryption' => 'ssl',
'username' => '302645122@qq.com',
'password' => 'ijncezxbmhrvbhab',
'timeout' => 15,
],
'ses' => [
'transport' => 'ses',
],
@ -70,6 +69,7 @@ return [
'array' => [
'transport' => 'array',
],
],
/*

View File

@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddIsEmailVerityToUsers extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->boolean('is_email_verity')->default(0)->nullable(false)->after('disable_at')->comment('邮箱是否已验证');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn("is_email_verity");
});
}
}

View File

@ -0,0 +1,35 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateUserEmailVerificationsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('user_email_verifications', function (Blueprint $table) {
$table->id();
$table->integer('userid')->nullable()->default(0)->comment('用户id');
$table->string('code')->nullable()->default('')->comment('验证参数');
$table->string('email')->nullable()->default('')->comment('电子邮箱');
$table->integer('status')->nullable()->default(0)->comment('0-未验证1-已验证');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('user_email_verifications');
}
}

View File

@ -0,0 +1,36 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class CreateProjectTaskMailLogsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('project_task_mail_logs', function (Blueprint $table) {
$table->id();
$table->integer('userid')->nullable()->default(0)->comment('用户id');
$table->integer('task_id')->nullable()->default(0)->comment('任务id');
$table->string('email')->nullable()->default('')->comment('电子邮箱');
$table->tinyInteger('type')->nullable()->default(0)->comment('提醒类型1第一次任务提醒2第二次任务超期提醒');
$table->tinyInteger('is_send')->nullable()->default(0)->comment('邮件发送是否成功0否1是');
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('project_task_mail_logs');
}
}

View File

@ -0,0 +1,32 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddWebSocketDialogUsersAddIsMarkUnread extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('web_socket_dialog_users', function (Blueprint $table) {
$table->boolean('is_mark_unread')->default(0)->nullable(false)->after('top_at')->comment('是否标记为未读0否1是');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('web_socket_dialog_users', function (Blueprint $table) {
$table->dropColumn("is_mark_unread");
});
}
}

View File

@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddProjectTaskMailLogsAddDeletes extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('project_task_mail_logs', function (Blueprint $table) {
if (!Schema::hasColumn('project_task_mail_logs', 'deleted_at')) {
$table->softDeletes();
}
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('project_task_mail_logs', function (Blueprint $table) {
$table->dropSoftDeletes();
});
}
}

View File

@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddProjectTasksDeletedUserid extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('project_tasks', function (Blueprint $table) {
if (!Schema::hasColumn('project_tasks', 'deleted_userid')) {
$table->bigInteger('deleted_userid')->nullable()->default(0)->after('deleted_at')->comment('删除会员');
}
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('project_tasks', function (Blueprint $table) {
$table->dropColumn("deleted_userid");
});
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 284 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 348 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 624 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 592 B

BIN
public/images/index/pic.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 530 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 414 B

BIN
public/images/indexlogo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

BIN
public/images/language.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 592 B

13
public/images/mark.svg Normal file
View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 25.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
viewBox="0 0 24 24" style="enable-background:new 0 0 24 24;" xml:space="preserve">
<style type="text/css">
.st0{fill:#8BCF70;}
</style>0
<path class="st0" d="M20.7,8.1c-1.5-1.5-4.4-4.4-4.4-4.4c-1.2-1.2-2.5-1.4-3.4-0.7c-0.5,0.4-0.7,0.9-0.8,1.4c-0.1,0.5-0.4,1-0.8,1.3
l-0.1,0.1c-2,1.5-4.3,2.8-6.8,3.2C3.9,9.2,3.3,9.5,3,10c-0.6,0.9-0.4,2.1,0.3,2.9l3.4,3.4c0,0,0,0,0,0l-3.1,3.1
c-0.3,0.3-0.3,0.8,0,1.1c0.1,0.1,0.3,0.2,0.5,0.2c0.2,0,0.4-0.1,0.5-0.2l3.1-3.1c0,0,0,0,0,0l2.9,2.9c1.3,1.3,2.5,1.5,3.4,0.9
c0.4-0.3,0.7-0.7,0.9-1.2c0.6-2.3,1.4-4.6,2.8-6.5l0.5-0.7c0.3-0.4,0.8-0.8,1.3-0.8c0.5-0.1,1.1-0.3,1.4-0.8
C21.7,10.2,21.5,8.9,20.7,8.1z"/>
</svg>

After

Width:  |  Height:  |  Size: 909 B

2
public/js/app.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,5 @@
/*!
* html2canvas 1.4.1 <https://html2canvas.hertzen.com>
* html2canvas 1.4.0 <https://html2canvas.hertzen.com>
* Copyright (c) 2022 Niklas von Hertzen <https://hertzen.com>
* Released under MIT License
*/

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
public/js/build/39.js vendored Normal file
View File

@ -0,0 +1 @@
"use strict";(self.webpackChunkDooTask=self.webpackChunkDooTask||[]).push([[39],{55983:(t,e,s)=>{s.d(e,{Z:()=>o});var i=s(1519),a=s.n(i)()((function(t){return t[1]}));a.push([t.id,".valid-wrap{align-items:center;display:flex;height:100vh;justify-content:center;width:100vw}.valid-box{background-color:#fff;border-radius:10px;padding:5px 15px 20px;width:500px}.valid-box .valid-title{border-bottom:1px solid #e8eaec;line-height:1;padding:14px 16px}.valid-box .validation-text{color:#333;font-size:14px;padding:10px}",""]);const o=a},5039:(t,e,s)=>{s.r(e),s.d(e,{default:()=>c});const i={name:"validEmail",components:{},data:function(){return{success:!1,error:!1,errorText:this.$L("链接已过期,已重新发送")}},mounted:function(){this.verificationEmail()},watch:{},methods:{verificationEmail:function(){var t=this;this.$store.dispatch("call",{url:"users/email/verification",data:{code:this.$route.query.code}}).then((function(e){e.data;t.success=!0,t.error=!1})).catch((function(e){var s=e.data,i=e.msg;2===s.code?t.goForward({path:"/"},!0):(t.success=!1,t.error=!0,t.errorText=t.$L(i))}))},userLogout:function(){this.$store.dispatch("logout",!1)}}};var a=s(93379),o=s.n(a),r=s(55983),n={insert:"head",singleton:!1};o()(r.Z,n);r.Z.locals;const c=(0,s(51900).Z)(i,(function(){var t=this,e=t.$createElement,s=t._self._c||e;return s("div",{staticClass:"valid-wrap"},[s("div",{staticClass:"valid-box"},[s("div",{staticClass:"valid-title"},[t._v(t._s(t.$L("验证邮箱")))]),t._v(" "),t.success||t.error?t._e():s("Spin",{attrs:{size:"large"}}),t._v(" "),t.success?s("div",{staticClass:"validation-text"},[s("p",[t._v(t._s(t.$L("您的邮箱已通过验证")))]),t._v(" "),s("p",[t._v(t._s(t.$L("今后您可以通过此邮箱重置您的账号密码")))])]):t._e(),t._v(" "),t.error?s("div",{staticClass:"validation-text"},[s("div",[t._v(t._s(t.errorText))])]):t._e(),t._v(" "),t.success?s("div",{attrs:{slot:"footer"},slot:"footer"},[s("Button",{attrs:{type:"primary",long:""},on:{click:t.userLogout}},[t._v(t._s(t.$L("返回首页")))])],1):t._e()],1)])}),[],!1,null,null,null).exports}}]);

1
public/js/build/396.js vendored Normal file

File diff suppressed because one or more lines are too long

2
public/js/build/482.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

2
public/js/build/496.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,13 @@
/*!
* clipboard.js v2.0.8
* https://clipboardjs.com/
*
* Licensed MIT © Zeno Rocha
*/
/**!
* Sortable 1.10.2
* @author RubaXa <trash@rubaxa.org>
* @author owenm <owen23355@gmail.com>
* @license MIT
*/

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
public/js/build/569.js vendored Normal file

File diff suppressed because one or more lines are too long

1
public/js/build/605.js vendored Normal file

File diff suppressed because one or more lines are too long

1
public/js/build/662.js vendored Normal file

File diff suppressed because one or more lines are too long

2
public/js/build/78.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,799 @@
/*!
* TOAST UI Calendar
* @version 1.15.1-5 | Sun Jan 09 2022
* @author NHN FE Development Lab <dl_javascript@nhn.com>
* @license MIT
*/
/*!
* TOAST UI Date Picker
* @version 4.3.1
* @author NHN. FE Development Lab <dl_javascript@nhn.com>
* @license MIT
*/
/*!
* TOAST UI Time Picker
* @version 2.1.4
* @author NHN FE Development Lab <dl_javascript@nhn.com>
* @license MIT
*/
/*!
* tui-code-snippet.js
* @version 1.5.2
* @author NHN. FE Development Lab <dl_javascript@nhn.com>
* @license MIT
*/
/*! ../..//common/vlayout */
/*! ../../common/array */
/*! ../../common/autoScroll */
/*! ../../common/collection */
/*! ../../common/common */
/*! ../../common/common.js */
/*! ../../common/datetime */
/*! ../../common/domevent */
/*! ../../common/domutil */
/*! ../../common/floatingLayer */
/*! ../../common/point */
/*! ../../common/reqAnimFrame */
/*! ../../common/timezone */
/*! ../../config */
/*! ../../model/schedule */
/*! ../../model/viewModel/scheduleViewModel */
/*! ../../view/template/week/timeMoveGuide.hbs */
/*! ../../view/view */
/*! ../common/array */
/*! ../common/collection */
/*! ../common/common */
/*! ../common/datetime */
/*! ../common/dirty */
/*! ../common/domevent */
/*! ../common/domutil */
/*! ../common/dw */
/*! ../common/model */
/*! ../common/point */
/*! ../common/reqAnimFrame */
/*! ../common/sanitizer */
/*! ../common/timezone */
/*! ../common/vlayout */
/*! ../config */
/*! ../controller/base */
/*! ../controller/viewMixin/core */
/*! ../controller/viewMixin/month */
/*! ../controller/viewMixin/week */
/*! ../exception */
/*! ../handler/daygrid/click */
/*! ../handler/daygrid/creation */
/*! ../handler/daygrid/move */
/*! ../handler/daygrid/resize */
/*! ../handler/drag */
/*! ../handler/month/click */
/*! ../handler/month/creation */
/*! ../handler/month/move */
/*! ../handler/month/resize */
/*! ../handler/time/click */
/*! ../handler/time/clickDayname */
/*! ../handler/time/creation */
/*! ../handler/time/move */
/*! ../handler/time/resize */
/*! ../logger */
/*! ../model/schedule */
/*! ../model/viewModel/scheduleViewModel */
/*! ../template/month/month.hbs */
/*! ../template/month/more.hbs */
/*! ../template/month/weekdayInMonth.hbs */
/*! ../template/month/weekdayInMonthSchedule.hbs */
/*! ../template/popup/scheduleCreationPopup.hbs */
/*! ../template/popup/scheduleDetailPopup.hbs */
/*! ../template/week/dayGrid.hbs */
/*! ../template/week/dayGridSchedule.hbs */
/*! ../template/week/daynames.hbs */
/*! ../template/week/time.hbs */
/*! ../template/week/timeGrid.hbs */
/*! ../template/week/timeGridCurrentTime.hbs */
/*! ../template/week/timezoneSticky.hbs */
/*! ../theme/theme */
/*! ../utils */
/*! ../view */
/*! ../view/layout */
/*! ../view/month/month */
/*! ../view/month/more */
/*! ../view/popup/scheduleCreationPopup */
/*! ../view/popup/scheduleDetailPopup */
/*! ../view/view */
/*! ../view/week/dayGrid */
/*! ../view/week/dayname */
/*! ../view/week/timeGrid */
/*! ../view/week/week */
/*! ../weekday */
/*! ./../../../../../webpack/buildin/global.js */
/*! ./../../../../node_modules/webpack/buildin/global.js */
/*! ./../../../../webpack/buildin/global.js */
/*! ./../../../node_modules/webpack/buildin/global.js */
/*! ./../node_modules/webpack/buildin/global.js */
/*! ./base */
/*! ./collection */
/*! ./common */
/*! ./controller */
/*! ./core */
/*! ./create-new-lookup-object */
/*! ./creationGuide */
/*! ./css/main.styl */
/*! ./dayGridSchedule */
/*! ./decorators */
/*! ./decorators/inline */
/*! ./dist/cjs/handlebars.runtime */
/*! ./domevent */
/*! ./domutil */
/*! ./exception */
/*! ./guide */
/*! ./guide.hbs */
/*! ./handlebars/base */
/*! ./handlebars/exception */
/*! ./handlebars/no-conflict */
/*! ./handlebars/runtime */
/*! ./handlebars/safe-string */
/*! ./handlebars/utils */
/*! ./helpers */
/*! ./helpers/block-helper-missing */
/*! ./helpers/each */
/*! ./helpers/helper-missing */
/*! ./helpers/if */
/*! ./helpers/log */
/*! ./helpers/lookup */
/*! ./helpers/with */
/*! ./internal/proto-access */
/*! ./internal/wrapHelper */
/*! ./intlUtil */
/*! ./js/factory/calendar */
/*! ./js/view/template/helper */
/*! ./logger */
/*! ./monthView */
/*! ./move */
/*! ./moveGuide */
/*! ./moveGuide.hbs */
/*! ./node_modules/handlebars/runtime.js */
/*! ./resizeGuide */
/*! ./standard */
/*! ./themeConfig */
/*! ./time */
/*! ./timezone */
/*! ./utils */
/*! ./view */
/*! ./vpanel */
/*! ./weekView */
/*! ./weekdayInMonth */
/*! @license DOMPurify 2.3.4 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.3.4/LICENSE */
/*! dompurify */
/*! handlebars-template-loader/runtime */
/*! handlebars/runtime */
/*! no static exports found */
/*! tui-code-snippet */
/*! tui-date-picker */
/*!**********************!*\
!*** ./src/index.js ***!
\**********************/
/*!**************************!*\
!*** ./src/js/config.js ***!
\**************************/
/*!***************************!*\
!*** ./src/css/main.styl ***!
\***************************/
/*!*****************************!*\
!*** ./src/js/common/dw.js ***!
\*****************************/
/*!*****************************!*\
!*** ./src/js/view/view.js ***!
\*****************************/
/*!*******************************!*\
!*** ./src/js/theme/theme.js ***!
\*******************************/
/*!*******************************!*\
!*** ./src/js/view/layout.js ***!
\*******************************/
/*!********************************!*\
!*** ./src/js/common/array.js ***!
\********************************/
/*!********************************!*\
!*** ./src/js/common/dirty.js ***!
\********************************/
/*!********************************!*\
!*** ./src/js/common/model.js ***!
\********************************/
/*!********************************!*\
!*** ./src/js/common/point.js ***!
\********************************/
/*!********************************!*\
!*** ./src/js/handler/drag.js ***!
\********************************/
/*!********************************!*\
!*** ./src/js/view/weekday.js ***!
\********************************/
/*!*********************************!*\
!*** ./src/js/common/common.js ***!
\*********************************/
/*!*********************************!*\
!*** ./src/js/common/vpanel.js ***!
\*********************************/
/*!**********************************!*\
!*** ./src/js/common/domutil.js ***!
\**********************************/
/*!**********************************!*\
!*** ./src/js/common/vlayout.js ***!
\**********************************/
/*!**********************************!*\
!*** ./src/js/model/schedule.js ***!
\**********************************/
/*!**********************************!*\
!*** ./src/js/theme/standard.js ***!
\**********************************/
/*!**********************************!*\
!*** ./src/js/view/week/time.js ***!
\**********************************/
/*!**********************************!*\
!*** ./src/js/view/week/week.js ***!
\**********************************/
/*!***********************************!*\
!*** (webpack)/buildin/global.js ***!
\***********************************/
/*!***********************************!*\
!*** ./src/js/common/datetime.js ***!
\***********************************/
/*!***********************************!*\
!*** ./src/js/common/domevent.js ***!
\***********************************/
/*!***********************************!*\
!*** ./src/js/common/intlUtil.js ***!
\***********************************/
/*!***********************************!*\
!*** ./src/js/common/timezone.js ***!
\***********************************/
/*!***********************************!*\
!*** ./src/js/controller/base.js ***!
\***********************************/
/*!***********************************!*\
!*** ./src/js/view/month/more.js ***!
\***********************************/
/*!************************************!*\
!*** ./src/js/common/sanitizer.js ***!
\************************************/
/*!************************************!*\
!*** ./src/js/factory/calendar.js ***!
\************************************/
/*!************************************!*\
!*** ./src/js/factory/weekView.js ***!
\************************************/
/*!************************************!*\
!*** ./src/js/view/month/month.js ***!
\************************************/
/*!*************************************!*\
!*** ./src/js/common/autoScroll.js ***!
\*************************************/
/*!*************************************!*\
!*** ./src/js/common/collection.js ***!
\*************************************/
/*!*************************************!*\
!*** ./src/js/factory/monthView.js ***!
\*************************************/
/*!*************************************!*\
!*** ./src/js/handler/time/core.js ***!
\*************************************/
/*!*************************************!*\
!*** ./src/js/handler/time/move.js ***!
\*************************************/
/*!*************************************!*\
!*** ./src/js/theme/themeConfig.js ***!
\*************************************/
/*!*************************************!*\
!*** ./src/js/view/week/dayGrid.js ***!
\*************************************/
/*!*************************************!*\
!*** ./src/js/view/week/dayname.js ***!
\*************************************/
/*!**************************************!*\
!*** ./src/js/factory/controller.js ***!
\**************************************/
/*!**************************************!*\
!*** ./src/js/handler/month/core.js ***!
\**************************************/
/*!**************************************!*\
!*** ./src/js/handler/month/move.js ***!
\**************************************/
/*!**************************************!*\
!*** ./src/js/handler/time/click.js ***!
\**************************************/
/*!**************************************!*\
!*** ./src/js/view/week/timeGrid.js ***!
\**************************************/
/*!***************************************!*\
!*** ./src/js/common/reqAnimFrame.js ***!
\***************************************/
/*!***************************************!*\
!*** ./src/js/handler/month/click.js ***!
\***************************************/
/*!***************************************!*\
!*** ./src/js/handler/month/guide.js ***!
\***************************************/
/*!***************************************!*\
!*** ./src/js/handler/time/resize.js ***!
\***************************************/
/*!****************************************!*\
!*** ./src/js/common/floatingLayer.js ***!
\****************************************/
/*!****************************************!*\
!*** ./src/js/handler/daygrid/core.js ***!
\****************************************/
/*!****************************************!*\
!*** ./src/js/handler/daygrid/move.js ***!
\****************************************/
/*!****************************************!*\
!*** ./src/js/handler/month/guide.hbs ***!
\****************************************/
/*!****************************************!*\
!*** ./src/js/handler/month/resize.js ***!
\****************************************/
/*!****************************************!*\
!*** ./src/js/view/template/helper.js ***!
\****************************************/
/*!*****************************************!*\
!*** ./src/js/handler/daygrid/click.js ***!
\*****************************************/
/*!*****************************************!*\
!*** ./src/js/handler/time/creation.js ***!
\*****************************************/
/*!******************************************!*\
!*** ./src/js/handler/daygrid/resize.js ***!
\******************************************/
/*!******************************************!*\
!*** ./src/js/handler/month/creation.js ***!
\******************************************/
/*!******************************************!*\
!*** ./src/js/handler/time/moveGuide.js ***!
\******************************************/
/*!*******************************************!*\
!*** ./src/js/handler/month/moveGuide.js ***!
\*******************************************/
/*!********************************************!*\
!*** ./node_modules/handlebars/runtime.js ***!
\********************************************/
/*!********************************************!*\
!*** ./src/js/handler/daygrid/creation.js ***!
\********************************************/
/*!********************************************!*\
!*** ./src/js/handler/month/moveGuide.hbs ***!
\********************************************/
/*!********************************************!*\
!*** ./src/js/handler/time/resizeGuide.js ***!
\********************************************/
/*!********************************************!*\
!*** ./src/js/view/template/week/time.hbs ***!
\********************************************/
/*!*********************************************!*\
!*** ./src/js/controller/viewMixin/core.js ***!
\*********************************************/
/*!*********************************************!*\
!*** ./src/js/controller/viewMixin/week.js ***!
\*********************************************/
/*!*********************************************!*\
!*** ./src/js/handler/daygrid/moveGuide.js ***!
\*********************************************/
/*!*********************************************!*\
!*** ./src/js/handler/month/resizeGuide.js ***!
\*********************************************/
/*!*********************************************!*\
!*** ./src/js/handler/time/clickDayname.js ***!
\*********************************************/
/*!*********************************************!*\
!*** ./src/js/view/month/weekdayInMonth.js ***!
\*********************************************/
/*!*********************************************!*\
!*** ./src/js/view/template/month/more.hbs ***!
\*********************************************/
/*!*********************************************!*\
!*** ./src/js/view/week/dayGridSchedule.js ***!
\*********************************************/
/*!**********************************************!*\
!*** ./src/js/controller/viewMixin/month.js ***!
\**********************************************/
/*!**********************************************!*\
!*** ./src/js/handler/time/creationGuide.js ***!
\**********************************************/
/*!**********************************************!*\
!*** ./src/js/view/template/month/month.hbs ***!
\**********************************************/
/*!***********************************************!*\
!*** ./node_modules/dompurify/dist/purify.js ***!
\***********************************************/
/*!***********************************************!*\
!*** ./src/js/handler/daygrid/resizeGuide.js ***!
\***********************************************/
/*!***********************************************!*\
!*** ./src/js/handler/month/creationGuide.js ***!
\***********************************************/
/*!***********************************************!*\
!*** ./src/js/view/template/week/dayGrid.hbs ***!
\***********************************************/
/*!************************************************!*\
!*** ./src/js/view/template/week/daynames.hbs ***!
\************************************************/
/*!************************************************!*\
!*** ./src/js/view/template/week/timeGrid.hbs ***!
\************************************************/
/*!*************************************************!*\
!*** ./src/js/handler/daygrid/creationGuide.js ***!
\*************************************************/
/*!**************************************************!*\
!*** ./src/js/view/popup/scheduleDetailPopup.js ***!
\**************************************************/
/*!****************************************************!*\
!*** ./src/js/view/popup/scheduleCreationPopup.js ***!
\****************************************************/
/*!*****************************************************!*\
!*** ./src/js/model/viewModel/scheduleViewModel.js ***!
\*****************************************************/
/*!*****************************************************!*\
!*** ./src/js/view/template/week/timeMoveGuide.hbs ***!
\*****************************************************/
/*!******************************************************!*\
!*** ./src/js/view/template/week/timezoneSticky.hbs ***!
\******************************************************/
/*!*******************************************************!*\
!*** ./src/js/view/template/month/weekdayInMonth.hbs ***!
\*******************************************************/
/*!*******************************************************!*\
!*** ./src/js/view/template/week/dayGridSchedule.hbs ***!
\*******************************************************/
/*!***********************************************************!*\
!*** ./src/js/view/template/week/timeGridCurrentTime.hbs ***!
\***********************************************************/
/*!************************************************************!*\
!*** ./src/js/view/template/popup/scheduleDetailPopup.hbs ***!
\************************************************************/
/*!*************************************************************!*\
!*** ./node_modules/handlebars/dist/cjs/handlebars/base.js ***!
\*************************************************************/
/*!**************************************************************!*\
!*** ./node_modules/handlebars/dist/cjs/handlebars/utils.js ***!
\**************************************************************/
/*!**************************************************************!*\
!*** ./src/js/view/template/popup/scheduleCreationPopup.hbs ***!
\**************************************************************/
/*!***************************************************************!*\
!*** ./node_modules/handlebars/dist/cjs/handlebars/logger.js ***!
\***************************************************************/
/*!***************************************************************!*\
!*** ./src/js/view/template/month/weekdayInMonthSchedule.hbs ***!
\***************************************************************/
/*!****************************************************************!*\
!*** ./node_modules/handlebars/dist/cjs/handlebars.runtime.js ***!
\****************************************************************/
/*!****************************************************************!*\
!*** ./node_modules/handlebars/dist/cjs/handlebars/helpers.js ***!
\****************************************************************/
/*!****************************************************************!*\
!*** ./node_modules/handlebars/dist/cjs/handlebars/runtime.js ***!
\****************************************************************/
/*!******************************************************************!*\
!*** ./node_modules/handlebars-template-loader/runtime/index.js ***!
\******************************************************************/
/*!******************************************************************!*\
!*** ./node_modules/handlebars/dist/cjs/handlebars/exception.js ***!
\******************************************************************/
/*!*******************************************************************!*\
!*** ./node_modules/handlebars/dist/cjs/handlebars/decorators.js ***!
\*******************************************************************/
/*!*******************************************************************!*\
!*** ./node_modules/handlebars/dist/cjs/handlebars/helpers/if.js ***!
\*******************************************************************/
/*!********************************************************************!*\
!*** ./node_modules/handlebars/dist/cjs/handlebars/helpers/log.js ***!
\********************************************************************/
/*!********************************************************************!*\
!*** ./node_modules/handlebars/dist/cjs/handlebars/no-conflict.js ***!
\********************************************************************/
/*!********************************************************************!*\
!*** ./node_modules/handlebars/dist/cjs/handlebars/safe-string.js ***!
\********************************************************************/
/*!*********************************************************************!*\
!*** ./node_modules/handlebars/dist/cjs/handlebars/helpers/each.js ***!
\*********************************************************************/
/*!*********************************************************************!*\
!*** ./node_modules/handlebars/dist/cjs/handlebars/helpers/with.js ***!
\*********************************************************************/
/*!***********************************************************************!*\
!*** ./node_modules/handlebars/dist/cjs/handlebars/helpers/lookup.js ***!
\***********************************************************************/
/*!**************************************************************************!*\
!*** ./node_modules/handlebars/dist/cjs/handlebars/decorators/inline.js ***!
\**************************************************************************/
/*!****************************************************************************!*\
!*** ./node_modules/handlebars/dist/cjs/handlebars/internal/wrapHelper.js ***!
\****************************************************************************/
/*!******************************************************************************!*\
!*** ./node_modules/handlebars/dist/cjs/handlebars/internal/proto-access.js ***!
\******************************************************************************/
/*!*******************************************************************************!*\
!*** ./node_modules/handlebars/dist/cjs/handlebars/helpers/helper-missing.js ***!
\*******************************************************************************/
/*!*************************************************************************************!*\
!*** ./node_modules/handlebars/dist/cjs/handlebars/helpers/block-helper-missing.js ***!
\*************************************************************************************/
/*!******************************************************************************************!*\
!*** ./node_modules/handlebars/dist/cjs/handlebars/internal/create-new-lookup-object.js ***!
\******************************************************************************************/
/*!******************************************************************************************************************************!*\
!*** external {"commonjs":"tui-code-snippet","commonjs2":"tui-code-snippet","amd":"tui-code-snippet","root":["tui","util"]} ***!
\******************************************************************************************************************************/
/*!*********************************************************************************************************************************!*\
!*** external {"commonjs":"tui-date-picker","commonjs2":"tui-date-picker","amd":"tui-date-picker","root":["tui","DatePicker"]} ***!
\*********************************************************************************************************************************/
//! moment.js
//! moment.js locale configuration

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
"use strict";(self.webpackChunkDooTask=self.webpackChunkDooTask||[]).push([[855],{45855:(t,e,s)=>{s.r(e),s.d(e,{default:()=>a});const n={data:function(){return{}},mounted:function(){this.$store.state.userId>0?this.goForward({path:"/manage/dashboard"},!0):this.goForward({path:"/login"},!0)},deactivated:function(){this.$destroy()}};const a=(0,s(51900).Z)(n,(function(){var t=this.$createElement;return(this._self._c||t)("div")}),[],!1,null,null,null).exports}}]);

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

1
public/js/build/907.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -448,5 +448,58 @@
{"CN": "置顶该聊天","EN": "Top the chat","TC": "置頂該聊天","KM": "បញ្ចូលការជជែក","TH": "ด้านบนการแชท","KO": "채팅 상단","JA": "トップチャット"},
{"CN": "请输入正确的邀请码","EN": "Please enter the correct invitation code","TC": "請輸入正確的邀請碼","KM": "សូមបញ្ចូលលេខកូដអញ្ជើញត្រឹមត្រូវ","TH": "โปรดป้อนรหัสเชิญที่ถูกต้อง","KO": "올바른 초대 코드를 입력하십시오","JA": "正しい招待状コードを入力してください"},
{"CN": "置顶","EN": "Topping","TC": "置頂","KM": "ការបញ្ចូល","TH": "โรยหน้า","KO": "토핑","JA": "トッピング"},
{"CN": "立即登录","EN": "Log in immediately","TC": "立即登錄","KM": "ចូលភ្លាមៗ","TH": "เข้าสู่ระบบทันที","KO": "즉시 로그인하십시오","JA": "すぐにログインします"},
{"CN": "轻量级任务管理工具","EN": "Lightweight task management tool","TC": "輕量級任務管理工具","KM": "ឧបករណ៍គ្រប់គ្រងភារកិច្ចស្រាល","TH": "เครื่องมือการจัดการงานที่มีน้ำหนักเบา","KO": "경량 작업 관리 도구","JA": "軽量タスク管理ツール"},
{"CN": "DooTask是一款轻量级的开源在线项目任务管理工具提供各类文档协作工具、在线思维导图、在线流程图、项目管理、任务分发、即时IM文件管理等工具。","EN": "DOOTASK is a lightweight open source online project task management tool, providing various document collaboration tools, online-related map, online flow chart, project management, task distribution, instant IM, file management and other tools.","TC": "DooTask是一款輕量級的開源在線項目任務管理工具提供各類文檔協作工具、在線思維導圖、在線流程圖、項目管理、任務分發、即時IM文件管理等工具。","KM": "Takestask គឺជាឧបករណ៍នៃការគ្រប់គ្រងភារកិច្ចរបស់គំរោងតាមអ៊ិនធរណេតស្រាល ៗ ដែលផ្តល់នូវឧបករណ៍សហប្រតិបត្តិការឯកសារផ្សេងៗតារាងផែនទីតាមអ៊ីនធឺណិត, គំនូសតាងនៃការគ្រប់គ្រងលើអ៊ីនធឺណិត, ការគ្រប់គ្រងគម្រោង, ការ, ការងារ, ការគ្រប់គ្រងឯកសារ, ការគ្រប់គ្រងឯកសារនិងឧបករណ៍ផ្សេងទៀត។","TH": "DOOTASK เป็นเครื่องมือการจัดการงานโครงการออนไลน์โอเพ่นซอร์สที่มีน้ำหนักเบาให้เครื่องมือการทำงานร่วมกันเอกสารต่างๆแผนที่ที่เกี่ยวข้องกับออนไลน์แผนภูมิการไหลออนไลน์การจัดการโครงการการกระจายงาน IM ทันทีการจัดการไฟล์และเครื่องมืออื่น ๆ","KO": "DootAck는 다양한 문서 공동 작업 도구, 온라인 관련지도, 온라인 흐름도, 프로젝트 관리, 작업 배포, 인스턴트 IM, 파일 관리 및 기타 도구를 제공하는 가벼운 오픈 오픈 온라인 프로젝트 작업 관리 도구입니다.","JA": "DoTaskは、軽量なオープンソースのオンラインプロジェクトタスク管理ツールで、さまざまなドキュメントコラボレーションツール、オンライン関連マップ、オンラインフローチャート、プロジェクト管理、タスク配信、インスタントIM、ファイル管理などのツールを提供します。"},
{"CN": "高效便捷的团队沟通工具","EN": "Efficient and convenient team communication tool","TC": "高效便捷的團隊溝通工具","KM": "ឧបករណ៍ទំនាក់ទំនងក្រុមប្រកបដោយប្រសិទ្ធភាពនិងងាយស្រួល","TH": "เครื่องมือสื่อสารทีมที่มีประสิทธิภาพและสะดวกสบาย","KO": "효율적이고 편리한 팀 커뮤니케이션 도구","JA": "効率的で便利なチームコミュニケーションツール"},
{"CN": "针对项目和任务建立群组,工作问题可及时沟通,促进团队快速协作,提高团队工作效率。","EN": "To establish groups in projects and tasks, work issues can communicate in time, promoting team rapid collaboration, and improve teamwork efficiency.","TC": "針對項目和任務建立群組,工作問題可及時溝通,促進團隊快速協作,提高團隊工作效率。","KM": "ដើម្បីបង្កើតក្រុមក្នុងគម្រោងនិងភារកិច្ចបញ្ហាការងារអាចប្រាស្រ័យទាក់ទងបានទាន់ពេលវេលាលើកកម្ពស់ការសហការគ្នារបស់ក្រុមនិងធ្វើឱ្យប្រសើរឡើងនូវប្រសិទ្ធភាពក្នុងក្រុមជម្រើសជាតិ។","TH": "ในการสร้างกลุ่มในโครงการและภารกิจปัญหาการทำงานสามารถสื่อสารได้ในเวลาการส่งเสริมการทำงานร่วมกันอย่างรวดเร็วของทีมและปรับปรุงประสิทธิภาพการทำงานเป็นทีม","KO": "프로젝트 및 작업의 그룹을 수립하기 위해 작업 문제는 시간에 통신하고 팀의 신속한 협력을 촉진하고 팀워크 효율성을 향상시킬 수 있습니다.","JA": "プロジェクトやタスクでグループを確立するために、仕事の問題は時間内に通信し、チームの急速なコラボレーションを促進し、チームワークの効率を向上させることができます。"},
{"CN": "强大易用的协同创作云文档","EN": "Powerful easy-to-use collaborative creation cloud documentation","TC": "強大易用的協同創作雲文檔","KM": "ឯកសារ Cloud ការបង្កើតរួមគ្នាដែលមានអនុភាពដែលមានអនុភាព","TH": "เอกสารคลาวด์สร้างความร่วมมือที่ใช้งานง่ายที่ใช้งานง่าย","KO": "강력한 사용하기 쉬운 공동 작성 클라우드 문서","JA": "強力な使いやすい共同作成クラウド文書"},
{"CN": "汇集文档、电子表格、思维笔记等多种在线工具,汇聚企业知识资源于一处,支持多人实时协同编辑,让团队协作更便捷。","EN": "Collect a variety of online tools such as documentation, spreadsheets, thinking notes, gather in corporate knowledge resources, support multi-person real-time collaborative editing, making teamwork more convenient.","TC": "匯集文檔、電子表格、思維筆記等多種在線工具,匯聚企業知識資源於一處,支持多人實時協同編輯,讓團隊協作更便捷。","KM": "ប្រមូលប្រភេទឧបករណ៍តាមអ៊ិនធរណេតជាច្រើនប្រភេទបទប្បទានលក្ខណៈការគិតកំណត់ត្រាការគិតដែលប្រមូលផ្តុំនៅក្នុងធនធានចំណេះដឹងរបស់ក្រុមហ៊ុនគាំទ្រការកែប្រែសហការច្រើនរបស់អ្នកធ្វើការធ្វើឱ្យការងារជាក្រុមកាន់តែងាយស្រួល។","TH": "รวบรวมเครื่องมือออนไลน์ที่หลากหลายเช่นเอกสารสเปรดชีตบันทึกการคิดรวบรวมในทรัพยากรความรู้ขององค์กรสนับสนุนการแก้ไขการทำงานร่วมกันแบบเรียลไทม์หลายคนทำให้การทำงานเป็นทีมสะดวกยิ่งขึ้น","KO": "문서, 스프레드 시트, 사고 메모와 같은 다양한 온라인 도구를 수집하고 기업 지식 리소스를 모으고 다중 사람 실시간 협업 편집을 지원하여 팀워크를보다 편리하게 만듭니다.","JA": "ドキュメンテーション、スプレッドシート、思考ノートなどのさまざまなオンラインツールを収集し、企業のナレッジリソースに集まり、マルチパーソンリアルタイムの共同編集をサポートして、チームワークをより便利です。"},
{"CN": "便捷易用的项目管理模板","EN": "Conveniently used project management template","TC": "便捷易用的項目管理模板","KM": "គំរូនៃការគ្រប់គ្រងគម្រោងដែលប្រើយ៉ាងងាយស្រួល","TH": "ใช้แม่แบบการจัดการโครงการที่ใช้งานได้สะดวก","KO": "편리하게 사용되는 프로젝트 관리 템플릿","JA": "便利なプロジェクト管理テンプレートを使用しました"},
{"CN": "模版满足多种团队协作场景,同时支持自定义模版,满足团队个性化场景管理需求,可直观的查看项目的进展情况,团队协作更方便。","EN": "The template meets a variety of teamwork scenes, and supports custom template, meets the management needs of team personalized scenes, and visually view the progress of the project, and teamwork is more convenient.","TC": "模版滿足多種團隊協作場景,同時支持自定義模版,滿足團隊個性化場景管理需求,可直觀的查看項目的進展情況,團隊協作更方便。","KM": "ទំព័រគំរូនេះត្រូវនឹងឈុតឆាកការងារជាក្រុមជាច្រើននិងគាំទ្រគំរូផ្ទាល់ខ្លួនត្រូវឆ្លើយតបទៅនឹងតម្រូវការរបស់ការគ្រប់គ្រងនៃឈុតឆាកផ្ទាល់ខ្លួនរបស់ក្រុមនិងមើលវឌ្ឍនភាពនៃគម្រោងនេះនិងការងារជាក្រុមកាន់តែងាយស្រួល។","TH": "เทมเพลตตรงกับฉากการทำงานเป็นทีมที่หลากหลายและรองรับเทมเพลตที่กำหนดเองตรงตามความต้องการด้านการจัดการของฉากส่วนบุคคลของทีมและมองเห็นความคืบหน้าของโครงการและการทำงานเป็นทีมสะดวกกว่า","KO": "템플릿은 다양한 팀웍 장면을 충족하며 사용자 정의 템플릿을 지원하고 팀 개인화 된 장면의 관리 요구 사항을 충족시키고 시각적으로 프로젝트의 진행 상황을 볼 수 있으며 팀웍이 더 편리합니다.","JA": "テンプレートはさまざまなチームワークシーンを満たし、カスタムテンプレートをサポートし、チームパーソナライズされたシーンの管理ニーズを満たし、プロジェクトの進行状況を視覚的に表示し、チームワークはより便利です。"},
{"CN": "清晰直观的任务日历","EN": "Clear and intuitive task calendar","TC": "清晰直觀的任務日曆","KM": "ប្រតិទិនភារកិច្ចច្បាស់លាស់និងវិចារណញាណ","TH": "ปฏิทินงานที่ชัดเจนและใช้งานง่าย","KO": "명확하고 직관적 인 작업 일정","JA": "クリアで直感的なタスクカレンダー"},
{"CN": "通过灵活的任务日历,轻松安排每一天的日程,把任务拆解到每天,让工作目标更清晰,时间分配更合理。","EN": "Through the flexible task calendar, easily arrange the daily schedule, disassemble the task to every day, let the work goals clearer, time allocation is more reasonable.","TC": "通過靈活的任務日曆,輕鬆安排每一天的日程,把任務拆解到每天,讓工作目標更清晰,時間分配更合理。","KM": "តាមរយៈប្រតិទិនភារកិច្ចដែលអាចបត់បែនបានរៀបចំឱ្យមានកាលវិភាគប្រចាំថ្ងៃដោយងាយផ្តាច់ភារកិច្ចឡើងវិញជារៀងរាល់ថ្ងៃឱ្យគោលដៅការងារកាន់តែច្បាស់ការបែងចែកពេលវេលាគឺសមហេតុផលជាង។","TH": "ผ่านปฏิทินงานที่ยืดหยุ่นจัดเรียงตารางประจำวันได้อย่างง่ายดายถอดชิ้นส่วนงานทุกวันปล่อยให้เป้าหมายการทำงานชัดเจนขึ้นการจัดสรรเวลานั้นสมเหตุสมผลมากขึ้น","KO": "유연한 작업 일정을 통해 일일 일정을 쉽게 준비하고, 매일 작업을 분해하고, 작업 목표를 더 명확하게, 시간 할당이 더 많습니다.","JA": "柔軟なタスクカレンダーを通して、毎日のスケジュールを簡単に調整し、タスクを毎日分解し、作業目標をより明確にして、時間割り当てがより合理的なものです。"},
{"CN": "开启您的 Dootask 团队协作","EN": "Open your DOOTASK team collaboration","TC": "開啟您的 Dootask 團隊協作","KM": "បើកកិច្ចសហប្រតិបត្តិការក្រុមរបស់អ្នក","TH": "เปิดการทำงานร่วมกันของทีม DOOTASK ของคุณ","KO": "Dootask 팀 공동 작업을 엽니 다","JA": "あなたのdootaskチームコラボレーションを開く"},
{"CN": "联系我们","EN": "Contact us","TC": "聯繫我們","KM": "ទាក់ទង​មក​ពួក​យើង","TH": "ติดต่อเรา","KO": "문의하기","JA": "お問い合わせ"},
{"CN": "桂公网安备 31011002000058号 桂ICP备12020087号-3","EN": "Gui Gong Net Anni 31011002000058 Gui ICP No. 12020087 -3","TC": "桂公網安備 31011002000058號 桂ICP備12020087號-3","KM": "Gui Gong Net Net Ann 31011002000058 GUI ICP លេខ 12020087 -3","TH": "Gui Gong Net Anni 31011002000058 GUI ICP No. 12020087 -3","KO": "Gui Gong Net Anni 31011002000058 GUI ICP No. 12020087 -3","JA": "GUI GONG NET ANNI 31011002000058 GUI ICP No. 12020087 -3"},
{"CN": "主题皮肤","EN": "Topic skin","TC": "主題皮膚","KM": "ស្បែកប្រធានបទ","TH": "ผิวหนัง","KO": "주제 피부","JA": "トピックスキン"},
{"CN": "注册账号","EN": "Register an account","TC": "註冊賬號","KM": "ចុះឈ្មោះគណនី","TH": "ลงทะเบียนบัญชี","KO": "계정을 등록하십시오","JA": "アカウントを登録"},
{"CN": "账号不存在,请确认账号是否输入正确","EN": "If the account does not exist, please confirm if the account is entered correct.","TC": "賬號不存在,請確認賬號是否輸入正確","KM": "ប្រសិនបើគណនីមិនមានសូមបញ្ជាក់ប្រសិនបើគណនីត្រូវបានបញ្ចូលត្រឹមត្រូវ។","TH": "หากบัญชีไม่มีอยู่โปรดยืนยันว่าบัญชีถูกป้อนที่ถูกต้องหรือไม่","KO": "계정이 존재하지 않으면 확인하면서 확인하십시오.","JA": "アカウントが存在しない場合は、アカウントが正しいかどうかを確認してください。"},
{"CN": "密码错误,请输入正确密码","EN": "Password error, please enter the correct password","TC": "密碼錯誤,請輸入正確密碼","KM": "កំហុសពាក្យសម្ងាត់សូមបញ្ចូលពាក្យសម្ងាត់ត្រឹមត្រូវ","TH": "ข้อผิดพลาดรหัสผ่านโปรดป้อนรหัสผ่านที่ถูกต้อง","KO": "암호 오류가 올바른 암호를 입력하십시오","JA": "パスワードエラー、正しいパスワードを入力してください"},
{"CN": "任务优先级","EN": "Task priority","TC": "任務優先級","KM": "អាទិភាពកិច្ចការ","TH": "ลำดับความสำคัญของงาน","KO": "작업 우선 순위","JA": "タスクの優先順位"},
{"CN": "邮件设置","EN": "E-Mail settings","TC": "郵件設置","KM": "ការកំណត់អ៊ីម៉ែល","TH": "การตั้งค่าอีเมล","KO": "전자 메일 설정","JA": "電子メール設定"},
{"CN": "邮箱服务器设置","EN": "Mailbox server settings","TC": "郵箱服務器設置","KM": "ការកំណត់ម៉ាស៊ីនមេប្រអប់សំបុត្រ","TH": "การตั้งค่าเซิร์ฟเวอร์กล่องจดหมาย","KO": "사서함 서버 설정","JA": "メールボックスサーバーの設定"},
{"CN": "SMTP服务器","EN": "SMTP server","TC": "SMTP服務器","KM": "ម៉ាស៊ីនមេ SMTP","TH": "เซิร์ฟเวอร์ SMTP","KO": "SMTP 서버","JA": "SMTPサーバー"},
{"CN": "端口","EN": "Port","TC": "端口","KM": "កមបង់ផេ","TH": "ท่า","KO": "포트","JA": "港"},
{"CN": "账号","EN": "Account","TC": "賬號","KM": "កននេយ្យ","TH": "บัญชีผู้ใช้","KO": "계정","JA": "アカウント"},
{"CN": "密码","EN": "Password","TC": "密碼","KM": "ហក្យសមងាត់","TH": "รหัสผ่าน","KO": "비밀번호","JA": "パスワード"},
{"CN": "邮件通知设置","EN": "Mail notification setting","TC": "郵件通知設置","KM": "ការកំណត់ការជូនដំណឹងតាមសំបុត្រ","TH": "การตั้งค่าการแจ้งเตือนทางไปรษณีย์","KO": "메일 알림 설정","JA": "メール通知の設定"},
{"CN": "开启注册验证","EN": "Open registration verification","TC": "開啟註冊驗證","KM": "ការផ្ទៀងផ្ទាត់ការចុះឈ្មោះបើកចំហ","TH": "เปิดการตรวจสอบการลงทะเบียน","KO": "오픈 등록 확인","JA": "オープン登録検証"},
{"CN": "开启后账号需验证通过才可登录","EN": "After the account is required to verify it, you can log in.","TC": "開啟後賬號需驗證通過才可登錄","KM": "បន្ទាប់ពីគណនីតម្រូវឱ្យផ្ទៀងផ្ទាត់វាអ្នកអាចចូលបាន។","TH": "หลังจากบัญชีที่จำเป็นต้องตรวจสอบแล้วคุณสามารถเข้าสู่ระบบได้","KO": "계정을 확인하려면 계정이 필요하면 로그인 할 수 있습니다.","JA": "確認が必要な場合は、ログインできます。"},
{"CN": "开启通知","EN": "Open notification","TC": "開啟通知","KM": "ការជូនដំណឹងបើក","TH": "เปิดการแจ้งเตือน","KO": "오픈 알림","JA": "オープン通知"},
{"CN": "任务提醒:","EN": "Task reminder:","TC": "任務提醒:","KM": "ការរំលឹកភារកិច្ច:","TH": "เตือนงาน:","KO": "작업 알림 :","JA": "タスクリマインダー:"},
{"CN": "到期前","EN": "Before the expiration","TC": "到期前","KM": "មុនពេលផុតកំណត់","TH": "ก่อนที่จะหมดอายุ","KO": "원정대 전에","JA": "満了前に"},
{"CN": "第二次任务提醒:","EN": "Second task reminder:","TC": "第二次任務提醒:","KM": "ការរំលឹកភារកិច្ចលើកទីពីរ:","TH": "เตือนงานที่สอง:","KO": "두 번째 작업 알림 :","JA": "2番目のタスクの通知"},
{"CN": "到期后","EN": "After the expiration","TC": "到期後","KM": "បន្ទាប់ពីការផុតកំណត់","TH": "หลังจากหมดอายุ","KO": "원정대 후에","JA": "満了後"},
{"CN": "小时","EN": "Hour","TC": "小時","KM": "ម៉ោង","TH": "ชั่วโมง","KO": "시","JA": "時間"},
{"CN": "自动:密码输入错误后必须添加验证码。","EN": "Auto: The verification code must be added after password input error.","TC": "自動:密碼輸入錯誤後必須添加驗證碼。","KM": "ស្វ័យប្រវត្តិ: លេខកូដផ្ទៀងផ្ទាត់ត្រូវតែបន្ថែមកំហុសក្នុងកំហុសពាក្យសម្ងាត់។","TH": "อัตโนมัติ: ต้องเพิ่มรหัสยืนยันหลังจากข้อผิดพลาดในการป้อนรหัสผ่าน","KO": "AUTO : 암호 입력 오류가 발생한 후 확인 코드를 추가해야합니다.","JA": "AUTOパスワード入力エラー後に検証コードを追加する必要があります。"},
{"CN": "密码策略","EN": "Password policy","TC": "密碼策略","KM": "គោលការណ៍ពាក្យសម្ងាត់","TH": "นโยบายรหัสผ่าน","KO": "암호 정책","JA": "パスワードポリシー"},
{"CN": "简单","EN": "Simple","TC": "簡單","KM": "តេឯកឯង","TH": "เรียบง่าย","KO": "단순한","JA": "単純"},
{"CN": "复杂","EN": "Complex","TC": "複雜","KM": "អបផាក","TH": "ซับซ้อน","KO": "복잡한","JA": "繁雑"},
{"CN": "简单大于或等于6个字符。","EN": "Simple: greater than or equal to 6 characters.","TC": "簡單大於或等於6個字符。","KM": "សាមញ្ញ: ធំជាងឬស្មើ 6 តួអក្សរ។","TH": "ง่าย: มากกว่าหรือเท่ากับ 6 ตัวอักษร","KO": "단순 : 6 자 이상 이상.","JA": "単純6文字以上。"},
{"CN": "复杂大于或等于6个字符包含数字、字母大小写或者特殊字符。","EN": "Complex: greater than or equal to 6 characters, including numbers, letters, or special characters.","TC": "複雜大於或等於6個字符包含數字、字母大小寫或者特殊字符。","KM": "ស្មុគស្មាញ: ធំជាងឬស្មើ 6 តួអក្សររួមទាំងលេខអក្សរឬតួអក្សរពិសេស។","TH": "คอมเพล็กซ์: มากกว่าหรือเท่ากับ 6 ตัวอักษรรวมถึงตัวเลขตัวอักษรหรืออักขระพิเศษ","KO": "복잡한 : 숫자, 문자 또는 특수 문자를 포함하여 6 자 이상의 6 자 이상.","JA": "複合数字、文字、または特殊文字を含む6文字以上。"},
{"CN": "邀请项目","EN": "Invitation project","TC": "邀請項目","KM": "គម្រោងលិខិតអញ្ជើញ","TH": "โครงการเชิญ","KO": "초대 프로젝트","JA": "招待状プロジェクト"},
{"CN": "开启:项目管理员可生成链接邀请成员加入项目。","EN": "Open: Project administrator can generate a link invitation member to join the project.","TC": "開啟:項目管理員可生成鏈接邀請成員加入項目。","KM": "Open: អ្នកគ្រប់គ្រងគម្រោងអាចបង្កើតនាយកដ្ឋានលិខិតអញ្ជើញភ្ជាប់ដើម្បីចូលរួមគម្រោងនេះ។","TH": "เปิด: ผู้ดูแลโครงการสามารถสร้างสมาชิกคำเชิญลิงก์เพื่อเข้าร่วมโครงการ","KO": "열기 : 프로젝트 관리자는 프로젝트에 가입하기 위해 링크 초대 구성원을 생성 할 수 있습니다.","JA": "OpenProject Administratorは、プロジェクトに参加するためのリンク招待メンバーを生成できます。"},
{"CN": "聊天昵称","EN": "Chat nickname","TC": "聊天暱稱","KM": "ឈ្មោះហៅក្រៅជជែក","TH": "ชื่อเล่นแชท","KO": "채팅 닉네임","JA": "チャットニックネーム"},
{"CN": "可选","EN": "Optional","TC": "可選","KM": "តាមចមរើស","TH": "ไม่จำเป็น","KO": "선택 과목","JA": "任意の"},
{"CN": "自动归档任务","EN": "Automatic archiving task","TC": "自動歸檔任務","KM": "ភារកិច្ចបណ្ណសារដោយស្វ័យប្រវត្តិ","TH": "งานเก็บถาวรอัตโนมัติ","KO": "자동 아카이빙 작업","JA": "自動アーカイブタスク"},
{"CN": "是否启动首页","EN": "Whether to start the home page","TC": "是否啟動首頁","KM": "ថាតើត្រូវចាប់ផ្តើមគេហទំព័រ","TH": "ไม่ว่าจะเริ่มต้นโฮมเพจ","KO": "홈 페이지를 시작할지 여부","JA": "ホームページを起動するかどうか"},
{"CN": "首页底部:首页底部网站备案号等信息","EN": "Home Bottom: Home Bottom Website Record No.","TC": "首頁底部:首頁底部網站備案號等信息","KM": "បាតផ្ទះ: គេហទំព័រកំណត់ត្រាគេហទំព័រលេខទំព័រដើម","TH": "หน้าแรกด้านล่าง: บันทึกเว็บไซต์ด้านล่างของหน้าแรก","KO": "홈 바닥 : 홈 하단 웹 사이트 레코드 번호","JA": "ホームボトム:ホームボトムウェブサイト記録番号"},
{"CN": "首页底部","EN": "Home bottom","TC": "首頁底部","KM": "បាតផ្ទះ","TH": "ก้นบ้าน","KO": "홈 바닥","JA": "ホームボトム"},
{"CN": "必填:发送聊天内容前必须设置昵称。","EN": "Required: You must set a nickname before sending chat content.","TC": "必填:發送聊天內容前必須設置暱稱。","KM": "ទាមទារ: អ្នកត្រូវតែដាក់ឈ្មោះហៅក្រៅមុនពេលផ្ញើមាតិកាជជែក។","TH": "ต้องการ: คุณต้องตั้งชื่อเล่นก่อนส่งเนื้อหาแชท","KO": "필수 : 채팅 콘텐츠를 보내기 전에 닉네임을 설정해야합니다.","JA": "必須:チャットコンテンツを送信する前にニックネームを設定する必要があります。"},
{"CN": "允许:开放注册功能。","EN": "Allow: Open registration function.","TC": "允許:開放註冊功能。","KM": "អនុញ្ញាត: បើកមុខងារចុះឈ្មោះ។","TH": "อนุญาตให้: เปิดฟังก์ชั่นการลงทะเบียน","KO": "허용 : 등록 기능을 엽니 다.","JA": "許可:オープン登録機能"},
{"CN": "邀请码:注册时需填写下方邀请码。","EN": "Invitation code: You need to fill in the following invitation code when registering.","TC": "邀請碼:註冊時需填寫下方邀請碼。","KM": "លេខកូដលិខិតអញ្ជើញ: អ្នកត្រូវបំពេញលេខកូដលិខិតអញ្ជើញខាងក្រោមនៅពេលចុះឈ្មោះ។","TH": "รหัสเชิญ: คุณต้องกรอกรหัสคำเชิญต่อไปนี้เมื่อลงทะเบียน","KO": "초대 코드 : 등록 할 때 다음 초대 코드를 입력해야합니다.","JA": "招待コード:登録時に次の招待コードを入力する必要があります。"},
{"CN": "添加模板","EN": "Add template","TC": "添加模板","KM": "បន្ថែមគំរូ","TH": "เพิ่มเทมเพลต","KO": "템플릿을 추가하십시오","JA": "テンプレートを追加します"},
{"CN": "版本","EN": "Version","TC": "版本","KM": "កមបុរក្នា","TH": "รุ่น","KO": "버전","JA": "バージョン"},
]
})(window)

View File

@ -79,6 +79,7 @@ export default {
this.addLanguageData(require("./language.js").default);
this.addLanguageData(window.languageData);
//
languageListenerObjects.push((lang) => {
this.languageType = lang;
});

View File

@ -1,21 +1,267 @@
<template>
<div></div>
<div v-if="needStartHome">
<div class="page-warp">
<div class="page-header">
<div class="header-nav">
<div class="header-nav-box">
<div class="logo"></div>
</div>
<div class="header-nav-box">
<div class="header-right-one">
<Dropdown trigger="click" @on-click="setLanguage">
<Icon
class="header-right-one-language"
type="md-globe"
/>
<a
href="javascript:void(0)"
class="header-right-one-dropdown"
v-if="screenWidth>441"
>
{{ currentLanguage }}
<Icon type="ios-arrow-down"></Icon>
</a>
<DropdownMenu slot="list">
<Dropdown-item
v-for="(item, key) in languageList"
:key="key"
:name="key"
:selected="getLanguage() === key"
>{{ item }}
</Dropdown-item>
</DropdownMenu>
</Dropdown>
</div>
<div class="header-right-two" @click="register">
{{ $L("注册账号") }}
</div>
<div class="header-right-three" @click="login">
{{ $L("登录") }}
</div>
</div>
</div>
<div class="header-content">
<div class="header-title header-title-one">Dootask</div>
<div class="header-title">
{{ $L("轻量级任务管理工具") }}
</div>
<div class="header-tips">
{{
$L("DooTask是一款轻量级的开源在线项目任务管理工具提供各类文档协作工具、在线思维导图、在线流程图、项目管理、任务分发、即时IM文件管理等工具。")
}}
</div>
<div class="login-buttom" @click="login">
{{ $L("登录") }}
</div>
</div>
<div class="header-bg"></div>
<div class="header-pic"></div>
</div>
<div class="page-main">
<div class="main-box-one">
<div class="box-one-square"></div>
<div class="box-pic" v-if="screenWidth<1920">
<img class="box-img" src="images/index/box-pic1.png" />
</div>
<div class="box-one-pic1" v-else>
</div>
<div class="box-one-tips">
<div class="box-square"></div>
<div class="box-title">
{{ $L("高效便捷的团队沟通工具") }}
</div>
<div class="box-tips">
{{
$L("针对项目和任务建立群组,工作问题可及时沟通,促进团队快速协作,提高团队工作效率。")
}}
</div>
</div>
</div>
<div class="main-box-two" v-if="screenWidth>=1920">
<div class="box-two-tips">
<div class="box-square"></div>
<div class="box-title">
{{ $L("强大易用的协同创作云文档") }}
</div>
<div class="box-tips">
{{
$L("汇集文档、电子表格、思维笔记等多种在线工具,汇聚企业知识资源于一处,支持多人实时协同编辑,让团队协作更便捷。")
}}
</div>
</div>
<div class="box-two-square"></div>
<div class="box-two-pic2"></div>
</div>
<div class="main-box-two" v-else>
<div class="box-two-tips">
<div class="box-pic">
<img class="box-img" src="images/index/box-pic2.png" />
</div>
<div class="box-square"></div>
<div class="box-title">
{{ $L("强大易用的协同创作云文档") }}
</div>
<div class="box-tips">
{{
$L("汇集文档、电子表格、思维笔记等多种在线工具,汇聚企业知识资源于一处,支持多人实时协同编辑,让团队协作更便捷。")
}}
</div>
</div>
</div>
<div class="main-box-one">
<div class="box-one-square"></div>
<div class="box-pic" v-if="screenWidth<1920" >
<img class="box-img" src="images/index/box-pic3.png" />
</div>
<div class="box-one-pic3" v-else >
</div>
<div class="box-one-tips">
<div class="box-square"></div>
<div class="box-title">
{{ $L("便捷易用的项目管理模板") }}
</div>
<div class="box-tips">
{{
$L("模版满足多种团队协作场景,同时支持自定义模版,满足团队个性化场景管理需求,可直观的查看项目的进展情况,团队协作更方便。")
}}
</div>
</div>
</div>
<div class="main-box-two" v-if="screenWidth>=1920">
<div class="box-two-tips">
<div class="box-square"></div>
<div class="box-title">
{{ $L("清晰直观的任务日历") }}
</div>
<div class="box-tips">
{{
$L("通过灵活的任务日历,轻松安排每一天的日程,把任务拆解到每天,让工作目标更清晰,时间分配更合理。")
}}
</div>
</div>
<div class="box-two-square"></div>
<div class="box-two-pic4"></div>
</div>
<div class="main-box-two" v-else>
<div class="box-two-tips">
<div class="box-pic">
<img class="box-img" src="images/index/box-pic4.png" />
</div>
<div class="box-square"></div>
<div class="box-title">
{{ $L("清晰直观的任务日历") }}
</div>
<div class="box-tips">
{{
$L("通过灵活的任务日历,轻松安排每一天的日程,把任务拆解到每天,让工作目标更清晰,时间分配更合理。")
}}
</div>
</div>
</div>
</div>
<div class="page-footer">
<div class="footer-service">
<div class="footer-bg-box">
<div class="box-title">
{{ $L("开启您的 Dootask 团队协作") }}
</div>
<div class="buttom-box">
<div class="login-btn" @click="login">
{{ $L("立即登录") }}
</div>
<div class="contact-btn">{{ $L("联系我们") }}</div>
</div>
</div>
</div>
<div class="footer-opyright" v-html="this.homeFooter"></div>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {}
return {
screenWidth: document.body.clientWidth,
needStartHome: false,
homeFooter: '',
};
},
watch: {
screenWidth(nVal) {
// console.log(nVal)
},
},
computed: {
currentLanguage() {
return this.languageList[this.languageType] || "Language";
},
},
mounted() {
if (this.$store.state.userId > 0) {
this.goForward({path: '/manage/dashboard'}, true);
} else {
this.goForward({path: '/login'}, true);
this.getNeedStartHome();
const that = this;
window.onresize=()=>{
window.screenWidth=document.body.clientWidth
that.screenWidth=window.screenWidth
}
},
methods: {
login() {
this.goForward(
{
path: `/login`,
},
false
);
},
register() {
this.goForward(
{
path: `/login`,
query: {
type: "reg",
},
},
false
);
},
getNeedStartHome() {
this.$store
.dispatch("call", {
url: "system/get/starthome",
})
.then(({data}) => {
this.homeFooter = data.home_footer;
if (this.$store.state.userId > 0) {
this.goForward({path: '/manage/dashboard'}, true);
} else {
this.needStartHome = !!data.need_start;
if (this.needStartHome === false) {
this.goForward({path: '/login'}, true);
}
}
})
.catch(() => {
this.needStartHome = false;
});
},
},
deactivated() {
this.$destroy()
}
}
// this.$destroy()
},
};
</script>

View File

@ -322,7 +322,11 @@ export default {
});
}).catch(({data, msg}) => {
this.loadIng--;
$A.modalError(msg);
if (data.code === 1000) {
$A.modalSuccess(msg);
} else {
$A.modalError(msg);
}
if (data.code === 'need') {
this.reCode();
this.codeNeed = true;

View File

@ -42,6 +42,30 @@
<DropdownItem name="exportTask">{{$L('导出任务统计')}}</DropdownItem>
</DropdownMenu>
</Dropdown>
<!--最近打开的任务-->
<Dropdown
v-else-if="item.path === 'taskBrowse'"
placement="right-start">
<DropdownItem divided>
<div class="manage-menu-flex">
{{$L(item.name)}}
<Icon type="ios-arrow-forward"></Icon>
</div>
</DropdownItem>
<DropdownMenu slot="list" v-if="taskBrowseLists.length > 0">
<DropdownItem
v-for="(item, key) in taskBrowseLists"
class="task-title"
:key="key"
@click.native="openTask(item)"
:name="item.name">{{ item.name }}</DropdownItem>
</DropdownMenu>
<DropdownMenu v-else slot="list">
<DropdownItem style="color: darkgrey;">
{{ $L('暂无打开记录') }}
</DropdownItem>
</DropdownMenu>
</Dropdown>
<!-- 主题皮肤 -->
<Dropdown
v-else-if="item.path === 'theme'"
@ -131,6 +155,7 @@
<div class="project-h1">
<em @click.stop="toggleOpenMenu(item.id)"></em>
<div class="title">{{item.name}}</div>
<img v-if="item.top_at" src="/images/mark.svg" class="icon-top">
<div v-if="item.task_my_num - item.task_my_complete > 0" class="num">{{item.task_my_num - item.task_my_complete}}</div>
</div>
<div class="project-h2">
@ -248,6 +273,12 @@
style="width:100%"
:placeholder="$L('请选择时间')"/>
</FormItem>
<FormItem prop="type" :label="$L('导出时间类型')">
<RadioGroup v-model="exportData.type">
<Radio label="taskTime">{{$L('任务时间')}}</Radio>
<Radio label="CreatedTime">{{$L('创建时间')}}</Radio>
</RadioGroup>
</FormItem>
</Form>
<div slot="footer" class="adaption">
<Button type="default" @click="exportTaskShow=false">{{$L('取消')}}</Button>
@ -363,6 +394,7 @@ export default {
exportData: {
userid: [],
time: [],
type:'taskTime',
},
dialogMsgSubscribe: null,
@ -450,7 +482,8 @@ export default {
'wsMsg',
'clientNewVersion'
'clientNewVersion',
'cacheTaskBrowse'
]),
...mapGetters(['taskData', 'dashboardTask']),
@ -489,6 +522,8 @@ export default {
{path: 'team', name: '团队管理', divided: true},
{path: 'taskBrowse', name: '最近打开的任务', divided: true},
{path: 'theme', name: '主题皮肤', divided: true},
{path: 'language', name: this.currentLanguage, divided: true},
@ -506,6 +541,8 @@ export default {
{path: 'workReport', name: '工作报告', divided: true},
{path: 'archivedProject', name: '已归档的项目'},
{path: 'taskBrowse', name: '最近打开的任务', divided: true},
{path: 'theme', name: '主题皮肤', divided: true},
{path: 'language', name: this.currentLanguage, divided: true},
@ -550,7 +587,16 @@ export default {
'overlay-y': true,
'overlay-none': this.topOperateVisible === true,
}
}
},
taskBrowseLists() {
const {cacheTaskBrowse} = this;
return $A.cloneJSON(cacheTaskBrowse).sort((a, b) => {
if (a.view_time || b.view_time) {
return b.view_time - a.view_time;
}
});
},
},
watch: {
@ -569,6 +615,55 @@ export default {
}
},
msgAllUnread() {
if (this.$Electron) {
this.$Electron.ipcRenderer.send('setDockBadge', this.msgAllUnread + this.dashboardTotal);
}
},
dashboardTotal() {
if (this.$Electron) {
this.$Electron.ipcRenderer.send('setDockBadge', this.msgAllUnread + this.dashboardTotal);
}
},
dialogMsgPush(data) {
if (this.natificationHidden && this.natificationReady) {
const {id, dialog_id, type, msg} = data;
let body = '';
switch (type) {
case 'text':
body = msg.text;
break;
case 'file':
body = '[' + this.$L(msg.type == 'img' ? '图片信息' : '文件信息') + ']'
break;
default:
return;
}
this._notificationId = id;
this.notificationClass.replaceOptions({
icon: $A.originUrl('images/logo.png'),
body: body,
data: data,
tag: "dialog",
requireInteraction: true
});
let dialog = this.dialogs.find((item) => item.id == dialog_id);
if (dialog) {
this.notificationClass.replaceTitle(dialog.name);
this.notificationClass.userAgreed();
} else {
this.$store.dispatch("getDialogOne", dialog_id).then(({data}) => {
if (this._notificationId === id) {
this.notificationClass.replaceTitle(data.name);
this.notificationClass.userAgreed();
}
})
}
}
},
projectKeyValue(val) {
if (val == '') {
return;
@ -723,7 +818,6 @@ export default {
return {
"active": this.curPath == '/manage/' + path,
"open-menu": openMenu === true,
"top": item.top_at,
"operate": item.id == this.topOperateItem.id && this.topOperateVisible
};
},
@ -982,6 +1076,10 @@ export default {
}
document.addEventListener(visibilityChangeEvent, visibilityChangeListener);
},
openTask(task) {
this.$store.dispatch("openTask", task)
},
}
}
</script>

View File

@ -361,7 +361,7 @@ export default {
content: '你确定要删除任务【' + data.name + '】吗?',
loading: true,
onOk: () => {
this.$store.dispatch("removeTask", data.id).then(({msg}) => {
this.$store.dispatch("removeTask", {task_id: data.id}).then(({msg}) => {
$A.messageSuccess(msg);
this.$Modal.remove();
}).catch(({msg}) => {

View File

@ -1,5 +1,5 @@
<template>
<div ref="tuiCalendar" class="calendar-wrapper"></div>
<div ref="tuiCalendar" className="calendar-wrapper"></div>
</template>
<script>
import Calendar from 'tui-calendar-hi';
@ -22,7 +22,7 @@ export default {
let notHave = false;
value.forEach(schedule => {
notHave = [ 'start', 'category' ].some(prop => !schedule.hasOwnProperty(prop));
notHave = ['start', 'category'].some(prop => !schedule.hasOwnProperty(prop));
});
return !notHave;

View File

@ -13,6 +13,7 @@
</template>
<h2>{{dialogData.name}}</h2>
<em v-if="peopleNum > 0">({{peopleNum}})</em>
<label v-if="dialogData.top_at" class="top-text">{{$L('置顶')}}</label>
</div>
<template v-if="dialogData.type === 'group'">
<div v-if="dialogData.group_type === 'project'" class="sub-title pointer" @click="openProject">

View File

@ -55,6 +55,7 @@
<EDropdownItem command="invite">{{$L('邀请链接')}}</EDropdownItem>
<EDropdownItem command="log" divided>{{$L('项目动态')}}</EDropdownItem>
<EDropdownItem command="archived_task">{{$L('已归档任务')}}</EDropdownItem>
<EDropdownItem command="deleted_task">{{$L('已删除任务')}}</EDropdownItem>
<EDropdownItem command="transfer" divided>{{$L('移交项目')}}</EDropdownItem>
<EDropdownItem command="archived">{{$L('归档项目')}}</EDropdownItem>
<EDropdownItem command="delete" style="color:#f40">{{$L('删除项目')}}</EDropdownItem>
@ -62,6 +63,7 @@
<EDropdownMenu v-else slot="dropdown">
<EDropdownItem command="log">{{$L('项目动态')}}</EDropdownItem>
<EDropdownItem command="archived_task">{{$L('已归档任务')}}</EDropdownItem>
<EDropdownItem command="deleted_task">{{$L('已删除任务')}}</EDropdownItem>
<EDropdownItem command="exit" divided style="color:#f40">{{$L('退出项目')}}</EDropdownItem>
</EDropdownMenu>
</EDropdown>
@ -431,6 +433,14 @@
:size="900">
<TaskArchived v-if="archivedTaskShow" :project-id="projectId"/>
</DrawerOverlay>
<!--查看已删除任务-->
<DrawerOverlay
v-model="deletedTaskShow"
placement="right"
:size="900">
<TaskDeleted v-if="deletedTaskShow" :project-id="projectId"/>
</DrawerOverlay>
</div>
</template>
@ -452,6 +462,7 @@ import ProjectLog from "./ProjectLog";
import DrawerOverlay from "../../../components/DrawerOverlay";
import ProjectWorkflow from "./ProjectWorkflow";
import TaskMenu from "./TaskMenu";
import TaskDeleted from "./TaskDeleted";
export default {
name: "ProjectList",
@ -459,7 +470,7 @@ export default {
TaskMenu,
ProjectWorkflow,
DrawerOverlay,
ProjectLog, TaskArchived, TaskRow, Draggable, TaskAddSimple, UserInput, TaskAdd, TaskPriority},
ProjectLog, TaskArchived, TaskRow, Draggable, TaskAddSimple, UserInput, TaskAdd, TaskPriority, TaskDeleted },
data() {
return {
loading: false,
@ -500,6 +511,7 @@ export default {
workflowShow: false,
logShow: false,
archivedTaskShow: false,
deletedTaskShow: false,
projectDialogSubscribe: null,
@ -1180,6 +1192,10 @@ export default {
this.archivedTaskShow = true;
break;
case "deleted_task":
this.deletedTaskShow = true;
break;
case "transfer":
this.$set(this.transferData, 'owner_userid', [this.projectData.owner_userid]);
this.transferShow = true;

View File

@ -42,6 +42,12 @@
<div class="subtitle">{{$L('可设置多个状态为进行中')}}</div>
</div>
</div>
<div class="taskflow-config-table-block-item">
<div>
<div class="title">{{$L('验收/测试')}}</div>
<div class="subtitle">{{$L('只能设置单个状态为验收/测试')}}</div>
</div>
</div>
<div class="taskflow-config-table-block-item">
<div>
<div class="title">{{$L('结束状态')}}</div>
@ -113,6 +119,7 @@
<RadioGroup v-model="item.status">
<Radio label="start"><span></span></Radio>
<Radio label="progress"><span></span></Radio>
<Radio label="test"><span></span></Radio>
<Radio label="end"><span></span></Radio>
</RadioGroup>
</div>
@ -295,6 +302,15 @@ export default {
},
{
"id": -12,
"name": "待测试",
"status": "test",
"turns": [-10, -11, -12, -13],
"userids": [],
"usertype": 'add',
"userlimit": 0,
},
{
"id": -13,
"name": "已完成",
"status": "end",
"turns": [-10, -11, -12, -13],
@ -303,7 +319,7 @@ export default {
"userlimit": 0,
},
{
"id": -13,
"id": -14,
"name": "已取消",
"status": "end",
"turns": [-10, -11, -12, -13],

View File

@ -300,7 +300,7 @@ export default {
delete(row) {
this.list = this.list.filter(({id}) => id != row.id);
this.loadIng++;
this.$store.dispatch("removeTask", row.id).then(({msg}) => {
this.$store.dispatch("removeTask", {task_id: row.id}).then(({msg}) => {
$A.messageSuccess(msg);
this.loadIng--;
this.getLists();

View File

@ -0,0 +1,104 @@
<template>
<ul class="dashboard-list overlay-y">
<li
v-for="(item, index) in list"
:key="index"
:class="{complete: item.complete_at}"
:style="item.color ? {backgroundColor: item.color} : {}"
@click="openTask(item)">
<em
v-if="item.p_name"
class="priority-color"
:style="{backgroundColor:item.p_color}"></em>
<TaskMenu :ref="`taskMenu_${item.id}`" :task="item">
<div slot="icon" class="drop-icon" @click.stop="">
<i class="taskfont" v-html="item.complete_at ? '&#xe627;' : '&#xe625;'"></i>
</div>
</TaskMenu>
<div class="item-title">
<!--工作流状态-->
<span v-if="item.flow_item_name" :class="item.flow_item_status"
@click.stop="openMenu(item)">{{ item.flow_item_name }}</span>
<!--是否子任务-->
<span v-if="item.sub_top === true">{{ $L('子任务') }}</span>
<!--有多少个子任务-->
<span v-if="item.sub_my && item.sub_my.length > 0">+{{ item.sub_my.length }}</span>
<!--任务描述-->
{{ item.name }}
</div>
<div v-if="item.desc" class="item-icon">
<i class="taskfont">&#xe71a;</i>
</div>
<div v-if="item.sub_num > 0" class="item-icon">
<i class="taskfont">&#xe71f;</i>
<em>{{ item.sub_complete }}/{{ item.sub_num }}</em>
</div>
<ETooltip v-if="item.end_at" :content="item.end_at" placement="right">
<div :class="['item-icon', item.today ? 'today' : '', item.overdue ? 'overdue' : '']">
<i class="taskfont">&#xe71d;</i>
<em>{{ expiresFormat(item.end_at) }}</em>
</div>
</ETooltip>
</li>
</ul>
</template>
<script>
import TaskMenu from "../components/TaskMenu";
import {mapGetters, mapState} from "vuex";
export default {
components: {TaskMenu},
name: "TaskDashboard",
props: {
list: {
default: {}
},
title: {
default: null
},
},
data() {
return {
nowTime: $A.Time(),
nowInterval: null,
}
},
mounted() {
this.nowInterval = setInterval(() => {
this.nowTime = $A.Time();
}, 1000)
},
destroyed() {
clearInterval(this.nowInterval)
},
activated() {
this.$store.dispatch("getTaskForDashboard");
},
deactivated() {
this.$store.dispatch("forgetTaskCompleteTemp", true);
},
methods: {
openTask(task) {
this.$store.dispatch("openTask", task)
},
openMenu(task) {
const el = this.$refs[`taskMenu_${task.id}`];
if (el) {
el[0].handleClick()
}
},
expiresFormat(date) {
return $A.countDownFormat(date, this.nowTime)
},
}
}
</script>
<style scoped>
</style>

View File

@ -0,0 +1,286 @@
<template>
<div class="task-archived">
<div class="archived-title">
{{$L('删除的任务')}}
<div class="title-icon">
<Loading v-if="loadIng > 0"/>
</div>
</div>
<div class="search-container lr">
<ul>
<li>
<div class="search-label">
{{$L("任务名")}}
</div>
<div class="search-content">
<Input v-model="keys.name" clearable/>
</div>
</li>
<li class="search-button">
<Tooltip
theme="light"
placement="right"
transfer-class-name="search-button-clear"
transfer>
<Button :loading="loadIng > 0" type="primary" icon="ios-search" @click="getLists">{{$L('搜索')}}</Button>
<div slot="content">
<Button :loading="loadIng > 0" type="text" @click="getLists">{{$L('刷新')}}</Button>
</div>
</Tooltip>
</li>
</ul>
</div>
<Table :columns="columns" :data="list" :loading="loadIng > 0" :no-data-text="$L(noText)"></Table>
<Page
class="page-container"
:total="total"
:current="page"
:pageSize="pageSize"
:disabled="loadIng > 0"
:simple="windowMax768"
showTotal
@on-change="setPage"
@on-page-size-change="setPageSize"/>
</div>
</template>
<script>
import {mapState} from "vuex";
export default {
name: "TaskDeleted",
props: {
projectId: {
type: Number,
default: 0
},
},
data() {
return {
loadIng: 0,
keys: {},
columns: [],
list: [],
page: 1,
pageSize: 20,
total: 0,
noText: ''
}
},
mounted() {
},
computed: {
...mapState(['cacheTasks', 'windowMax768'])
},
watch: {
projectId: {
handler() {
this.getLists();
},
immediate: true
}
},
methods: {
initLanguage() {
this.columns = [
{
title: this.$L('ID'),
minWidth: 50,
maxWidth: 70,
key: 'id',
},
{
title: this.$L('任务名称'),
key: 'name',
minWidth: 200,
render: (h, {row}) => {
return h('AutoTip', {
on: {
'on-click': () => {
this.$store.dispatch("openTask", row);
}
}
}, row.name);
}
},
{
title: this.$L('创建时间'),
key: 'created_at',
width: 168,
},
{
title: this.$L('删除时间'),
key: 'deleted_at',
width: 168,
},
{
title: this.$L('删除人员'),
key: 'deleted_userid',
minWidth: 100,
render: (h, {row}) => {
if (!row.deleted_userid) {
return h('span', '-');
}
return h('UserAvatar', {
props: {
userid: row.deleted_userid,
size: 24,
showName: true
}
});
}
},
{
title: this.$L('操作'),
align: 'center',
width: 100,
render: (h, params) => {
const vNodes = [
h('span', {
style: {
fontSize: '13px',
cursor: 'pointer',
color: '#8bcf70',
},
on: {
'click': () => {
this.$store.dispatch("openTask", params.row);
}
},
}, this.$L('查看')),
h('Poptip', {
props: {
title: this.$L('你确定要还原删除吗?'),
confirm: true,
transfer: true,
placement: 'left',
},
style: {
marginLeft: '6px',
fontSize: '13px',
cursor: 'pointer',
color: '#8bcf70',
},
on: {
'on-ok': () => {
this.recovery(params.row);
}
},
}, this.$L('还原')),
h('Poptip', {
props: {
title: this.$L('你确定要彻底删除任务吗?'),
confirm: true,
transfer: true,
placement: 'left',
},
style: {
marginLeft: '6px',
fontSize: '13px',
cursor: 'pointer',
color: '#f00',
},
on: {
'on-ok': () => {
this.delete(params.row);
}
},
}, this.$L('彻底删除'))
];
return h('TableAction', {
props: {
column: params.column
}
}, vNodes);
}
}
]
},
refresh() {
this.keys = {};
this.getLists()
},
getLists() {
if (!this.projectId) {
return;
}
this.loadIng++;
this.$store.dispatch("call", {
url: 'project/task/lists',
data: {
keys: this.keys,
project_id: this.projectId,
parent_id: -1,
deleted: 'yes',
sorts: {
deleted_at: 'desc'
},
page: Math.max(this.page, 1),
pagesize: Math.max($A.runNum(this.pageSize), 20),
},
}).then(({data}) => {
this.loadIng--;
this.page = data.current_page;
this.total = data.total;
this.list = data.data;
this.noText = '没有相关的数据';
}).catch(() => {
this.loadIng--;
this.noText = '数据加载失败';
})
},
setPage(page) {
this.page = page;
this.getLists();
},
setPageSize(pageSize) {
this.page = 1;
this.pageSize = pageSize;
this.getLists();
},
recovery(row) {
this.list = this.list.filter(({id}) => id != row.id);
this.loadIng++;
this.$store.dispatch("removeTask", {
task_id: row.id,
type: 'recovery'
}).then(({msg}) => {
$A.messageSuccess(msg);
this.loadIng--;
this.getLists();
this.$store.dispatch("openTask", row);
}).catch(({msg}) => {
$A.modalError(msg);
this.loadIng--;
this.getLists();
})
},
delete(row) {
this.list = this.list.filter(({id}) => id != row.id);
this.loadIng++;
this.$store.dispatch("removeTask", {
task_id: row.id,
type: 'completely_delete'
}).then(({msg}) => {
$A.messageSuccess(msg);
this.loadIng--;
this.getLists();
}).catch(({msg}) => {
$A.modalError(msg);
this.loadIng--;
this.getLists();
});
}
}
}
</script>

View File

@ -74,6 +74,7 @@
<div v-show="taskDetail.id > 0" class="task-info">
<div class="head">
<TaskMenu
v-if="taskId > 0 && !taskDetail.deleted_at"
:ref="`taskMenu_${taskDetail.id}`"
:disabled="taskId === 0"
:task="taskDetail"
@ -87,6 +88,9 @@
<div v-if="taskDetail.archived_at" class="flow">
<span class="archived" @click.stop="openMenu(taskDetail)">{{$L('已归档')}}</span>
</div>
<div v-if="taskDetail.deleted_at" class="flow">
<span class="archived">{{$L('已删除')}}</span>
</div>
<div class="nav">
<p v-if="projectName"><span>{{projectName}}</span></p>
<p v-if="columnName"><span>{{columnName}}</span></p>
@ -117,14 +121,18 @@
<Button :loading="ownerLoad > 0" size="small" type="primary" @click="onOwner(true)">确定</Button>
</div>
</div>
<Button slot="reference" :loading="ownerLoad > 0" class="pick" type="primary">{{$L('我要领取任务')}}</Button>
<Button v-if="!taskDetail.deleted_at" slot="reference" :loading="ownerLoad > 0" class="pick" type="primary">{{$L('我要领取任务')}}</Button>
</EPopover>
<ETooltip v-if="$Electron" :content="$L('新窗口打开')">
<i class="taskfont open" @click="openNewWin">&#xe776;</i>
</ETooltip>
<div class="menu">
<TaskMenu
<<<<<<< HEAD
:disabled="taskId === 0"
=======
v-if="taskId > 0 && !taskDetail.deleted_at"
>>>>>>> 00e666e423e8eddc31391cea724548b066a62f41
:task="taskDetail"
icon="ios-more"
completed-icon="ios-more"
@ -340,7 +348,7 @@
</ul>
</FormItem>
</Form>
<div v-if="menuList.length > 0" class="add">
<div v-if="menuList.length > 0 && !taskDetail.deleted_at" class="add">
<EDropdown
trigger="click"
placement="bottom"
@ -383,21 +391,21 @@
<div class="head">
<Icon class="icon" type="ios-chatbubbles-outline" />
<div class="nav">
<p :class="{active:navActive=='dialog'}" @click="navActive='dialog'">{{$L('聊天')}}</p>
<p :class="{active:navActive=='log'}" @click="navActive='log'">{{$L('动态')}}</p>
<p :class="{active:navActive=='dialog'}" @click="navActive='dialog'" v-if="!taskDetail.deleted_at">{{$L('聊天')}}</p>
<p :class="{active:navActive=='log' || taskDetail.deleted_at}" @click="navActive='log'">{{$L('动态')}}</p>
<div v-if="navActive=='log'" class="refresh">
<Loading v-if="logLoadIng"/>
<Icon v-else type="ios-refresh" @click="getLogLists"></Icon>
</div>
</div>
</div>
<ProjectLog v-if="navActive=='log' && taskId > 0" ref="log" :task-id="taskDetail.id" :show-load="false" @on-load-change="logLoadChange"/>
<ProjectLog v-if="(navActive=='log' || taskDetail.deleted_at) && taskId > 0" ref="log" :task-id="taskDetail.id" :show-load="false" @on-load-change="logLoadChange"/>
<div v-else class="no-dialog"
@drop.prevent="taskPasteDrag($event, 'drag')"
@dragover.prevent="taskDragOver(true, $event)"
@dragleave.prevent="taskDragOver(false, $event)">
<div class="no-tip">{{$L('暂无消息')}}</div>
<div class="no-input">
<div class="no-input" v-if="!taskDetail.deleted_at">
<DragInput
class="dialog-input"
v-model="msgText"

View File

@ -287,7 +287,7 @@ export default {
archivedOrRemoveTask(type) {
let typeDispatch = 'removeTask';
let typeName = '删除';
let typeData = this.task.id;
let typeData = {task_id: this.task.id};
let typeTask = this.task.parent_id > 0 ? '子任务' : '任务';
if (type == 'archived') {
typeDispatch = 'archivedTask'

View File

@ -85,6 +85,12 @@
<DropdownItem @click.native="handleTopClick">
{{ $L(topOperateItem.top_at ? '取消置顶' : '置顶该聊天') }}
</DropdownItem>
<DropdownItem @click.native="updateRead('read')" v-if="topOperateItem.unread > 0">
{{ $L('标记已读') }}
</DropdownItem>
<DropdownItem @click.native="updateRead('unread')" v-else>
{{ $L('标记未读') }}
</DropdownItem>
</DropdownMenu>
</Dropdown>
</div>
@ -482,6 +488,22 @@ export default {
}).catch(({msg}) => {
$A.modalError(msg);
});
},
updateRead(type) {
this.$store.dispatch("call", {
url: 'dialog/msg/mark',
data: {
dialog_id: this.topOperateItem.id,
type: type
},
}).then(() => {
this.$store.dispatch("getDialogs");
this.$Modal.remove();
}).catch(({msg}) => {
$A.modalError(msg, 301);
this.$Modal.remove();
});
}
}
}

View File

@ -0,0 +1,136 @@
<template>
<div class="setting-item submit">
<Form ref="formData" :model="formData" :rules="ruleData" label-width="auto" @submit.native.prevent>
<h3 style="margin-bottom: 15px;">{{ $L('邮箱服务器设置') }}</h3>
<FormItem :label="$L('SMTP服务器')" prop="smtp_server">
<Input v-model="formData.smtp_server"/>
</FormItem>
<FormItem :label="$L('端口')" prop="port">
<Input :maxlength="20" v-model="formData.port"/>
</FormItem>
<FormItem :label="$L('账号')" prop="account">
<Input :maxlength="20" v-model="formData.account"/>
</FormItem>
<FormItem :label="$L('密码')" prop="password">
<Input :maxlength="20" v-model="formData.password"/>
</FormItem>
<h3 style="margin-bottom: 15px;">{{ $L('邮件通知设置') }}</h3>
<FormItem :label="$L('开启注册验证')" prop="reg_verify">
<RadioGroup v-model="formData.reg_verify">
<Radio label="open">{{ $L('开启') }}</Radio>
<Radio label="close">{{ $L('关闭') }}</Radio>
</RadioGroup>
<div v-if="formData.reg_verify == 'open'" class="form-tip">{{$L('开启后账号需验证通过才可登录')}}</div>
</FormItem>
<FormItem :label="$L('开启通知')" prop="notice">
<RadioGroup v-model="formData.notice">
<Radio label="open">{{ $L('开启') }}</Radio>
<Radio label="close">{{ $L('关闭') }}</Radio>
</RadioGroup>
</FormItem>
<template v-if="formData.notice == 'open'">
<FormItem :label="$L('任务提醒:')" prop="task_remind_hours">
<label>{{ $L('到期前') }}</label>
<InputNumber v-model="formData.task_remind_hours" :min="0.5" :step="0.5" @on-change="hoursChange"/>
<label>{{ $L('小时') }}</label>
</FormItem>
<FormItem :label="$L('第二次任务提醒:')" prop="task_remind_hours2">
<label>{{ $L('到期后') }}</label>
<InputNumber v-model="formData.task_remind_hours2" :min="0.5" :step="0.5" @on-change="hours2Change"/>
<label>{{ $L('小时') }}</label>
</FormItem>
</template>
</Form>
<div class="setting-footer">
<Button :loading="loadIng > 0" type="primary" @click="submitForm">{{ $L('提交') }}</Button>
<Button :loading="loadIng > 0" @click="resetForm" style="margin-left: 8px">{{ $L('重置') }}</Button>
</div>
</div>
</template>
<script>
export default {
name: "SystemEmailSetting",
data() {
return {
loadIng: 0,
formData: {
smtp_server: '',
port: '',
account: '',
password: '',
reg_verify: 'colse',
notice: 'open',
task_remind_hours: 0,
task_remind_hours2: 0,
},
ruleData: {},
}
},
mounted() {
this.systemSetting();
},
methods: {
submitForm() {
this.$refs.formData.validate((valid) => {
if (valid) {
this.systemSetting(true);
}
})
},
resetForm() {
this.formData = $A.cloneJSON(this.formDatum_bak);
},
formArchived(value) {
this.formData = {...this.formData, auto_archived: value};
},
systemSetting(save) {
this.loadIng++;
this.$store.dispatch("call", {
url: 'system/emailSetting?type=' + (save ? 'save' : 'all'),
data: this.formData,
}).then(({data}) => {
if (save) {
$A.messageSuccess('修改成功');
}
this.loadIng--;
this.formData = data;
this.formDatum_bak = $A.cloneJSON(this.formData);
}).catch(({msg}) => {
if (save) {
$A.modalError(msg);
}
this.loadIng--;
});
},
hoursChange(e) {
let newNum = e * 10;
if (newNum % 5 !== 0) {
setTimeout(() => {
this.formData.task_remind_hours = 1;
})
$A.messageError('任务提醒只能是0.5的倍数');
}
},
hours2Change(e) {
let newNum = e * 10;
if (newNum % 5 !== 0) {
setTimeout(() => {
this.formData.task_remind_hours2 = 1;
})
$A.messageError('第二次任务提醒只能是0.5的倍数');
}
}
}
}
</script>
<style scoped>
</style>

View File

@ -10,6 +10,9 @@
<TabPane :label="$L('项目模板')" name="columnTemplate">
<SystemColumnTemplate/>
</TabPane>
<TabPane :label="$L('邮件设置')" name="emailSetting">
<SystemEmailSetting/>
</TabPane>
</Tabs>
</div>
</template>
@ -18,8 +21,10 @@
import SystemSetting from "./setting";
import SystemTaskPriority from "./taskPriority";
import SystemColumnTemplate from "./columnTemplate";
import SystemEmailSetting from "./EmailSetting";
export default {
components: {SystemColumnTemplate, SystemTaskPriority, SystemSetting},
components: {SystemColumnTemplate, SystemTaskPriority, SystemSetting, SystemEmailSetting},
data() {
return {
tabAction: 'setting',

View File

@ -59,6 +59,18 @@
<div slot="content">{{$L('任务完成 % 天后自动归档。', formDatum.archived_day)}}</div>
</ETooltip>
</FormItem>
<FormItem :label="$L('是否启动首页')" prop="startHome">
<RadioGroup v-model="formDatum.start_home">
<Radio label="open">{{$L('开启')}}</Radio>
<Radio label="close">{{$L('关闭')}}</Radio>
</RadioGroup>
<template v-if="formDatum.start_home == 'open'">
<div class="form-tip">{{ $L('首页底部:首页底部网站备案号等信息') }}</div>
<Input v-model="formDatum.home_footer" style="width:100%;margin-top:6px">
<span slot="prepend">{{ $L('首页底部') }}</span>
</Input>
</template>
</FormItem>
</Form>
<div class="setting-footer">
<Button :loading="loadIng > 0" type="primary" @click="submitForm">{{$L('提交')}}</Button>

View File

@ -0,0 +1,90 @@
<template>
<div class="valid-wrap">
<div class="valid-box">
<div class="valid-title">{{$L('验证邮箱')}}</div>
<Spin size="large" v-if="!success && !error"></Spin>
<div class="validation-text" v-if="success">
<p>{{$L('您的邮箱已通过验证')}}</p>
<p>{{$L('今后您可以通过此邮箱重置您的账号密码')}}</p>
</div>
<div class="validation-text" v-if="error">
<div>{{errorText}}</div>
</div>
<div slot="footer" v-if="success">
<Button type="primary" @click="userLogout" long>{{$L('返回首页')}}</Button>
</div>
</div>
</div>
</template>
<script>
export default {
name: "validEmail",
components: {},
data() {
return {
success: false,
error: false,
errorText: this.$L('链接已过期,已重新发送'),
}
},
mounted() {
this.verificationEmail();
},
watch: {},
methods: {
verificationEmail() {
this.$store
.dispatch("call", {
url: "users/email/verification",
data: {
code: this.$route.query.code
}
})
.then(({data}) => {
this.success = true;
this.error = false;
}).catch(({data, msg}) => {
if (data.code === 2) {
this.goForward({path: '/'}, true);
} else {
this.success = false;
this.error = true;
this.errorText = this.$L(msg);
}
});
},
userLogout() {
this.$store.dispatch("logout", false)
}
},
}
</script>
<style lang="scss">
.valid-wrap {
height: 100vh;
width: 100vw;
display: flex;
align-items: center;
justify-content: center;
}
.valid-box {
width: 500px;
background-color: #fff;
padding: 5px 15px 20px 15px;
border-radius: 10px;
.valid-title {
border-bottom: 1px solid #e8eaec;
padding: 14px 16px;
line-height: 1;
}
.validation-text{
padding: 10px;
color: #333;
font-size: 14px;
}
}
</style>

View File

@ -90,6 +90,12 @@ export default [
path: '/single/task/:id',
component: () => import('./pages/single/task.vue'),
},
{
name: 'valid-email',
path: '/single/valid/email',
meta: {title: '验证绑定邮箱'},
component: () => import('./pages/single/validEmail.vue')
},
{
name: 'login',
path: '/login',

View File

@ -207,6 +207,7 @@ export default {
dispatch("getProjects").catch(() => {});
dispatch("getDialogs").catch(() => {});
dispatch("getTaskForDashboard");
dispatch("getTaskBrowse");
},
/**
@ -424,19 +425,29 @@ export default {
try {
const cacheLoginEmail = $A.getStorageString("cacheLoginEmail");
const cacheThemeMode = $A.getStorageString("cacheThemeMode");
let userId = state.userId > 0 ? state.userId : userInfo.userid;
const cacheTaskBrowse = $A.getStorageArray("cacheTaskBrowse" + userId)
//
window.localStorage.clear();
// window.localStorage.clear();
//
state.cacheUserBasic = [];
state.cacheDialogs = [];
state.cacheProjects = [];
state.cacheColumns = [];
state.cacheTasks = [];
state.cacheLoading = {};
state.cacheDrawerIndex = 0;
state.cacheDrawerOverlay = [];
state.cacheUserActive = {};
state.cacheUserWait = [];
//
$A.setStorage("cacheProjectParameter", state.cacheProjectParameter);
$A.setStorage("cacheServerUrl", state.cacheServerUrl);
$A.setStorage("cacheLoginEmail", cacheLoginEmail);
$A.setStorage("cacheThemeMode", cacheThemeMode);
$A.setStorage("cacheTaskBrowse" + userId, cacheTaskBrowse);
dispatch("saveUserInfo", $A.isJson(userInfo) ? userInfo : state.userInfo);
//
resolve()
@ -1224,29 +1235,27 @@ export default {
* 删除任务
* @param state
* @param dispatch
* @param task_id
* @param data
* @returns {Promise<unknown>}
*/
removeTask({state, dispatch}, task_id) {
removeTask({state, dispatch}, data) {
return new Promise(function (resolve, reject) {
if ($A.runNum(task_id) === 0) {
if ($A.runNum(data.task_id) === 0) {
reject({msg: 'Parameter error'});
return;
}
dispatch("taskLoadStart", task_id)
dispatch("taskLoadStart", data.task_id)
dispatch("call", {
url: 'project/task/remove',
data: {
task_id: task_id,
},
data,
}).then(result => {
dispatch("forgetTask", task_id)
dispatch("taskLoadEnd", task_id)
dispatch("forgetTask", data.task_id)
dispatch("taskLoadEnd", data.task_id)
resolve(result)
}).catch(e => {
console.warn(e);
dispatch("getTaskOne", task_id).catch(() => {})
dispatch("taskLoadEnd", task_id)
dispatch("getTaskOne", data.task_id).catch(() => {})
dispatch("taskLoadEnd", data.task_id)
reject(e)
});
});
@ -1402,7 +1411,21 @@ export default {
}).then(() => {
dispatch("getTaskContent", task_id);
dispatch("getTaskFiles", task_id);
dispatch("getTaskForParent", task_id).catch(() => {})
dispatch("getTaskForParent", task_id).catch(() => {});
task.view_time = new Date().getTime();
let index = state.cacheTaskBrowse.findIndex(({id}) => id == task.id)
if (index > -1) {
state.cacheTaskBrowse.splice(index, 1,task)
} else {
state.cacheTaskBrowse.push(task)
}
if (state.cacheTaskBrowse.length > 10) {
let deleteNum = state.cacheTaskBrowse.length - 10
state.cacheTaskBrowse.splice(0, parseInt(deleteNum))
}
setTimeout(() => {
$A.setStorage("cacheTaskBrowse" + state.userId, state.cacheTaskBrowse);
})
}).catch(({msg}) => {
$A.modalWarning({
content: msg,
@ -2383,4 +2406,8 @@ export default {
websocketClose({state}) {
state.ws && state.ws.close();
},
getTaskBrowse({state}) {
state.cacheTaskBrowse = $A.getStorageArray("cacheTaskBrowse" + state.userId);
}
}

View File

@ -1,3 +1,4 @@
export default {
}

View File

@ -29,6 +29,7 @@ const stateData = {
cacheColumns: $A.getStorageArray("cacheColumns"),
cacheTasks: $A.getStorageArray("cacheTasks"),
cacheProjectParameter: $A.getStorageArray("cacheProjectParameter"),
cacheTaskBrowse: $A.getStorageArray("cacheTaskBrowse" + $A.getStorageJson("userInfo").userid),
// ServerUrl
cacheServerUrl: $A.getStorageString("cacheServerUrl"),

View File

@ -5,7 +5,6 @@
@import "taskfont";
@import "loading";
@import "scrollbar";
@import "components/_";
@import "pages/_";

View File

@ -7,5 +7,5 @@
@import "page-messenger";
@import "page-project";
@import "page-setting";
@import "page-index";
@import "components/_";

View File

@ -82,6 +82,17 @@
font-weight: 500;
padding-left: 6px;
}
.top-text{
width: 40px;
height: 24px;
border-radius:4px;
margin-left: 10px;
background-color: #8BCF70;
color:#FFFFFF;
text-align: center;
margin-top: 4px;
padding-top: 1px
}
}
.sub-title {

View File

@ -174,6 +174,11 @@
border-color: rgba(27, 154, 238, 0.1);
color: #0171c2;
}
&.test {
background-color: rgba(27, 154, 238, 0.1);
border-color: rgba(27, 154, 238, 0.1);
color: #0171c2;
}
&.end {
background-color: rgba(21, 173, 49, 0.1);
border-color: rgba(21, 173, 49, 0.1);
@ -423,6 +428,11 @@
border-color: rgba(27, 154, 238, 0.1);
color: #0171c2;
}
&.test {
background-color: rgba(27, 154, 238, 0.1);
border-color: rgba(27, 154, 238, 0.1);
color: #0171c2;
}
&.end {
background-color: rgba(21, 173, 49, 0.1);
border-color: rgba(21, 173, 49, 0.1);
@ -786,6 +796,11 @@
border-color: rgba(27, 154, 238, 0.1);
color: #0171c2;
}
&.test {
background-color: rgba(27, 154, 238, 0.1);
border-color: rgba(27, 154, 238, 0.1);
color: #0171c2;
}
&.end {
background-color: rgba(21, 173, 49, 0.1);
border-color: rgba(21, 173, 49, 0.1);
@ -978,7 +993,9 @@
&.progress {
color: #0171c2 !important;
}
&.test {
color: #0171c2 !important;
}
&.end {
color: #038a24 !important;
}
@ -994,7 +1011,9 @@
&.progress {
background-color: rgba(27, 154, 238, 0.1);
}
&.test {
background-color: rgba(27, 154, 238, 0.1);
}
&.end {
background-color: rgba(21, 173, 49, 0.1);
}

View File

@ -70,6 +70,11 @@
border-color: rgba(27, 154, 238, 0.1);
color: #0171c2;
}
&.test {
background-color: rgba(27, 154, 238, 0.1);
border-color: rgba(27, 154, 238, 0.1);
color: #0171c2;
}
&.end {
background-color: rgba(21, 173, 49, 0.1);
border-color: rgba(21, 173, 49, 0.1);
@ -329,6 +334,13 @@
}
}
&.test {
border-color: #ccecff;
&:hover {
border-color: #87d2ff
}
}
&.end {
border-color: #cafac8;
&:hover {
@ -377,6 +389,11 @@
color: #0171c2
}
&.test {
background: rgba(27,154,238,0.1);
color: #0171c2
}
&.end {
background: rgba(21,173,49,0.1);
color: #038a24

View File

@ -71,6 +71,11 @@
border-color: rgba(27, 154, 238, 0.1);
color: #0171c2;
}
&.test {
background-color: rgba(27, 154, 238, 0.1);
border-color: rgba(27, 154, 238, 0.1);
color: #0171c2;
}
&.end {
background-color: rgba(21, 173, 49, 0.1);
border-color: rgba(21, 173, 49, 0.1);
@ -354,6 +359,11 @@
border-color: rgba(27, 154, 238, 0.1);
color: #0171c2;
}
&.test {
background-color: rgba(27, 154, 238, 0.1);
border-color: rgba(27, 154, 238, 0.1);
color: #0171c2;
}
&.end {
background-color: rgba(21, 173, 49, 0.1);
border-color: rgba(21, 173, 49, 0.1);

View File

@ -93,6 +93,12 @@
color: #0171c2;
}
&.test {
background-color: rgba(27, 154, 238, 0.1);
border-color: rgba(27, 154, 238, 0.1);
color: #0171c2;
}
&.end {
background-color: rgba(21, 173, 49, 0.1);
border-color: rgba(21, 173, 49, 0.1);

View File

@ -182,6 +182,11 @@
border-color: rgba(27, 154, 238, 0.1);
color: #0171c2;
}
&.test {
background-color: rgba(27, 154, 238, 0.1);
border-color: rgba(27, 154, 238, 0.1);
color: #0171c2;
}
&.end {
background-color: rgba(21, 173, 49, 0.1);
border-color: rgba(21, 173, 49, 0.1);

View File

@ -0,0 +1,667 @@
@media (max-width: 1919px) {
.page-warp {
width: 100%;
margin: 0 auto;
.page-header {
width: 100%;
height: 720px;
background: #8bcf70;
border-radius: 0px 0px 300px 300px;
position: relative;
.header-nav {
width: 100%;
height: 72px;
display: flex;
justify-content: space-between;
.header-nav-box {
display: flex;
justify-items: center;
align-items: center;
color: #ffffff;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
position: relative;
.logo {
width: 65px;
height: 36px;
background: url("../images/index/indexlogo.png")
no-repeat center center;
background-size: contain;
margin: 0 20px 0 20px;
}
.header-right-one {
display: flex;
align-items: center;
justify-content: end;
font-size: 12px;
min-width: 75px;
.header-right-one-language {
margin-right: 4px;
}
.header-right-one-dropdown {
color: #ffffff;
}
}
.header-right-two {
font-size: 12px;
margin: 0 20px 0 20px;
cursor: pointer;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
@media(max-width:414px){
.header-right-three {
max-width: 50px;
font-size: 12px;
height: 36px;
background: #ffa25a;
border-radius: 4px;
text-align: center;
line-height: 36px;
cursor: pointer;
margin-right: 10px;
padding: 0 5px 0 5px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
@media(min-width:414px){
.header-right-three {
font-size: 12px;
height: 36px;
background: #ffa25a;
border-radius: 4px;
text-align: center;
line-height: 36px;
cursor: pointer;
margin-right: 10px;
padding: 0 5px 0 5px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
@media(min-width:660px){
.logo {
width: 143px;
height: 36px;
background: url("../images/index/indexlogo.png")
no-repeat center center;
background-size: contain;
margin: 0 20px 0 20px;
}
.header-right-one {
display: flex;
align-items: center;
font-size: 16px;
min-width: 75px;
.header-right-one-language {
margin-right: 4px;
}
.header-right-one-dropdown {
color: #ffffff;
}
}
.header-right-two {
font-size: 16px;
margin: 0 40px 0 40px;
cursor: pointer;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
.header-right-three {
font-size: 16px;
height: 36px;
background: #ffa25a;
border-radius: 4px;
text-align: center;
line-height: 36px;
cursor: pointer;
margin-right: 10px;
padding: 0 10px 0 10px;
overflow: hidden;
white-space: nowrap;
text-overflow: ellipsis;
}
}
}
}
.header-content {
width: 100%;
height: 396px;
margin: 0 auto;
.header-title {
font-size: 24px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #ffffff;
line-height: 67px;
text-align: center;
}
.header-title-one {
margin-top: 40px;
}
.header-tips {
width: 80%;
font-size: 12px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #ffffff;
line-height: 36px;
text-align: center;
margin-top: 22px;
margin: 0 auto;
}
.login-buttom {
width: 150px;
height: 48px;
background: #ffa25a;
border-radius: 8px;
font-size: 18px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #ffffff;
line-height: 48px;
text-align: center;
margin: 0 auto;
margin-top: 34px;
cursor: pointer;
}
}
// .header-bg {
// width: 100%;
// height: 400px;
// background: url("../images/index/decoration.png") no-repeat center
// center;
// background-size: contain;
// position: absolute;
// bottom: 0;
// }
.header-pic {
width: 100%;
height: 340px;
background: url("../images/index/pic.png") no-repeat center center;
background-size: contain;
margin-top: 10px;
}
}
.page-main {
width: 62.5%;
margin: 0 auto;
margin-top: 200px;
.box-img{
height: auto;
max-width: 100%;
max-height: 100%;
vertical-align: bottom;
bottom: 0;
-o-object-fit: fill;
object-fit: fill;
}
.box-pic {
-webkit-box-shadow: 0px 0px 10px #a9a4a4;
-moz-box-shadow: 0px 0px 10px #a9a4a4;
box-shadow: 0px 0px 10px #a9a4a4;
border-radius: 10px;
padding: 10px;
}
.box-square {
width: 24px;
height: 24px;
background: url("../images/index/square.png") no-repeat
center center;
background-size: contain;
margin-top: 20px;
}
.box-title {
font-size: 16px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #333333;
line-height: 45px;
margin: 6px 0 18px 0;
}
.box-tips {
font-size: 12px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #828282;
line-height: 28px;
}
.main-box-one {
position: relative;
margin-bottom: 70px;
.box-one-tips {
width: 100%;
}
}
.main-box-two {
width: 100%;
position: relative;
margin-bottom: 70px;
.box-two-square {
}
.box-two-tips {
width: 100%;
}
}
}
.page-footer {
.footer-service {
width: 100%;
height: 188px;
background-color: #ffa25a;
position: relative;
.footer-bg-box {
overflow: hidden;
width: 100%;
height: 188px;
// background: url("../images/index/footer-bg.png") no-repeat
// center center;
// background-size: 100% 100%;
// background-size: contain;
.box-title {
height: 45px;
font-size: 16px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #ffffff;
line-height: 45px;
text-align: center;
margin: 33px 0 22px 0;
}
.buttom-box {
display: flex;
justify-content: center;
.login-btn {
width: 150px;
height: 48px;
background: #ffffff;
border-radius: 8px;
font-size: 14px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #ffa25a;
line-height: 48px;
text-align: center;
margin-right: 20px;
cursor: pointer;
}
.contact-btn {
width: 150px;
height: 48px;
border-radius: 8px;
border: 1px solid #ffffff;
font-size: 14px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #ffffff;
line-height: 48px;
text-align: center;
cursor: pointer;
}
}
}
}
.footer-opyright {
width: 100%;
height: 60px;
background: #ffffff;
text-align: center;
font-size: 12px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #828282;
line-height: 60px;
}
}
}
}
@media (min-width: 1920px) {
.page-warp {
width: 1920px;
margin: 0 auto;
.page-header {
width: 100%;
height: 852px;
background: #8bcf70;
border-radius: 0px 0px 300px 300px;
position: relative;
.header-nav {
width: 100%;
height: 72px;
display: flex;
.header-nav-box {
width: 50%;
display: flex;
align-items: center;
justify-content: center;
color: #ffffff;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
position: relative;
.logo {
width: 143px;
height: 36px;
background: url("../images/index/indexlogo.png")
no-repeat center center;
background-size: contain;
}
.header-right-one {
display: flex;
.header-right-one-language {
margin-right: 8px;
font-size: 26px;
}
.header-right-one-dropdown {
color: #ffffff;
font-size: 16px;
}
}
.header-right-two {
font-size: 16px;
margin: 0 30px 0 30px;
cursor: pointer;
}
.header-right-three {
font-size: 16px;
min-width: 100px;
height: 36px;
background: #ffa25a;
border-radius: 4px;
text-align: center;
line-height: 36px;
cursor: pointer;
padding: 0 10px 0 10px;
}
}
}
.header-content {
width: 950px;
margin: 0 auto;
.header-title {
font-size: 48px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #ffffff;
line-height: 67px;
text-align: center;
}
.header-title-one {
margin-top: 40px;
}
.header-tips {
width: 950px;
font-size: 24px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #ffffff;
line-height: 36px;
text-align: center;
margin-top: 22px;
}
.login-buttom {
width: 150px;
height: 48px;
background: #ffa25a;
border-radius: 8px;
font-size: 18px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #ffffff;
line-height: 48px;
text-align: center;
margin: 0 auto;
margin-top: 34px;
cursor: pointer;
}
}
.header-bg {
width: 100%;
height: 405px;
background: url("../images/index/decoration.png") no-repeat
center center;
background-size: contain;
position: absolute;
bottom: -40px;
}
.header-pic {
width: 920px;
height: 528px;
background: url("../images/index/pic.png") no-repeat center
center;
background-size: contain;
position: absolute;
left: 50%;
margin-left: -471px;
bottom: -236px;
}
}
.page-main {
width: 1200px;
margin: 0 auto;
margin-top: 388px;
.main-box-one {
height: 388px;
position: relative;
margin-bottom: 180px;
.box-one-square {
width: 200px;
height: 200px;
background: #8bcf70;
border-radius: 30px;
opacity: 0.15;
float: left;
}
.box-one-pic1 {
width: 600px;
height: 338px;
background: url("../images/index/box-pic1.png") no-repeat
center center;
background-size: contain;
position: absolute;
margin: 50px 0 0 50px;
-webkit-box-shadow: 0px 0px 10px #a9a4a4;
-moz-box-shadow: 0px 0px 10px #a9a4a4;
box-shadow: 0px 0px 10px #a9a4a4;
border-radius: 10px;
}
.box-one-pic3 {
width: 600px;
height: 338px;
background: url("../images/index/box-pic3.png") no-repeat
center center;
background-size: contain;
position: absolute;
margin: 50px 0 0 50px;
-webkit-box-shadow: 0px 0px 10px #a9a4a4;
-moz-box-shadow: 0px 0px 10px #a9a4a4;
box-shadow: 0px 0px 10px #a9a4a4;
border-radius: 10px;
}
.box-one-tips {
width: 460px;
position: absolute;
right: 0;
}
.box-square {
width: 38px;
height: 38px;
background: url("../images/index/square.png") no-repeat
center center;
background-size: contain;
margin-top: 119px;
}
.box-title {
font-size: 32px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #333333;
line-height: 45px;
margin: 6px 0 18px 0;
}
.box-tips {
font-size: 18px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #828282;
line-height: 28px;
}
}
.main-box-two {
height: 388px;
position: relative;
margin-bottom: 180px;
.box-two-square {
width: 200px;
height: 200px;
background: #8bcf70;
border-radius: 30px;
opacity: 0.15;
float: right;
}
.box-two-pic2 {
width: 600px;
height: 338px;
background: url("../images/index/box-pic2.png") no-repeat
center center;
background-size: contain;
position: absolute;
right: 50px;
top: 50px;
-webkit-box-shadow: 0px 0px 10px #a9a4a4;
-moz-box-shadow: 0px 0px 10px #a9a4a4;
box-shadow: 0px 0px 10px #a9a4a4;
border-radius: 10px;
}
.box-two-pic4 {
width: 600px;
height: 338px;
background: url("../images/index/box-pic4.png") no-repeat
center center;
background-size: contain;
position: absolute;
right: 50px;
top: 50px;
-webkit-box-shadow: 0px 0px 10px #a9a4a4;
-moz-box-shadow: 0px 0px 10px #a9a4a4;
box-shadow: 0px 0px 10px #a9a4a4;
border-radius: 10px;
}
.box-two-tips {
width: 460px;
position: absolute;
left: 0;
}
.box-square {
width: 38px;
height: 38px;
background: url("../images/index/square.png") no-repeat
center center;
background-size: contain;
margin-top: 119px;
}
.box-title {
font-size: 32px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #333333;
line-height: 45px;
margin: 6px 0 18px 0;
}
.box-tips {
font-size: 18px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #828282;
line-height: 28px;
}
}
}
.page-footer {
.footer-service {
width: 100%;
height: 188px;
background-color: #ffa25a;
position: relative;
.footer-bg-box {
overflow: hidden;
width: 100%;
height: 188px;
background: url("../images/index/footer-bg.png") no-repeat
center center;
background-size: contain;
.box-title {
height: 45px;
font-size: 32px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #ffffff;
line-height: 45px;
text-align: center;
margin: 33px 0 22px 0;
}
.buttom-box {
display: flex;
justify-content: center;
.login-btn {
width: 150px;
height: 48px;
background: #ffffff;
border-radius: 8px;
font-size: 18px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #ffa25a;
line-height: 48px;
text-align: center;
margin-right: 20px;
cursor: pointer;
}
.contact-btn {
width: 150px;
height: 48px;
border-radius: 8px;
border: 1px solid #ffffff;
font-size: 18px;
font-family: PingFangSC-Medium, PingFang SC;
font-weight: 500;
color: #ffffff;
line-height: 48px;
text-align: center;
cursor: pointer;
}
}
}
}
.footer-opyright {
width: 100%;
height: 60px;
background: #ffffff;
text-align: center;
font-size: 14px;
font-family: PingFangSC-Regular, PingFang SC;
font-weight: 400;
color: #828282;
line-height: 60px;
}
}
}
}

View File

@ -95,6 +95,11 @@
color: #666666;
}
}
.task-title {
overflow: hidden;
text-overflow: ellipsis;
}
}
> ul {
flex: 1;
@ -196,6 +201,11 @@
font-size: 12px;
padding-left: 16px;
}
.icon-top {
width: 14px;
height: 14px;
}
}
.project-h2 {
display: none;

Binary file not shown.

After

Width:  |  Height:  |  Size: 281 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 284 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 348 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 624 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 10 KiB

Some files were not shown because too many files have changed in this diff Show More