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
108 changed files with 3805 additions and 127 deletions
@@ -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");
}
}
+105 -28
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]);
+77 -1
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('{}'));
}
}
+63 -3
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('绑定邮箱成功');
}
}
+3
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";
}
+7
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个结束状态');
}
+83 -10
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();
}
}
}
+43
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;
}
+8
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('邮箱地址已存在');
}
//密码
+79
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());
}
}
}
}
+4 -1
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();
+2
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)
+147 -17
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;
}
}
}
+53
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);
}
}
}
}
+8 -8
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',
],
],
/*
@@ -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");
});
}
}
@@ -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');
}
}
@@ -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');
}
}
@@ -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");
});
}
}
@@ -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();
});
}
}
@@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 530 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 414 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 592 B

+13
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

+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
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
*/
-1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1
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
View File
File diff suppressed because one or more lines are too long
+2
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+2
View File
File diff suppressed because one or more lines are too long
+13
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
*/
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1
View File
File diff suppressed because one or more lines are too long
+1
View File
File diff suppressed because one or more lines are too long
+1
View File
File diff suppressed because one or more lines are too long
+2
View File
File diff suppressed because one or more lines are too long
+799
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
+1 -1
View File
File diff suppressed because one or more lines are too long
-1
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}}]);
+1 -2
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+1
View File
File diff suppressed because one or more lines are too long
+1 -1
View File
File diff suppressed because one or more lines are too long
+53
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": "Open:Project 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)
+1
View File
@@ -79,6 +79,7 @@ export default {
this.addLanguageData(require("./language.js").default);
this.addLanguageData(window.languageData);
//
languageListenerObjects.push((lang) => {
this.languageType = lang;
});
+255 -9
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>
+5 -1
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;
+101 -3
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>
@@ -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}) => {
@@ -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;
@@ -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">
@@ -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;
@@ -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],
@@ -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();
@@ -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>
@@ -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>
@@ -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"
@@ -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'
@@ -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();
});
}
}
}
@@ -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>
@@ -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',
@@ -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>
@@ -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>
+6
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',
+40 -13
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);
}
}
+1
View File
@@ -1,3 +1,4 @@
export default {
}
+1
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"),
-1
View File
@@ -5,7 +5,6 @@
@import "taskfont";
@import "loading";
@import "scrollbar";
@import "components/_";
@import "pages/_";
+1 -1
View File
@@ -7,5 +7,5 @@
@import "page-messenger";
@import "page-project";
@import "page-setting";
@import "page-index";
@import "components/_";
@@ -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 {
+21 -2
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);
}
@@ -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
+10
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);
+6
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);
+5
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);
+667
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;
}
}
}
}
+10
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