合并优化

This commit is contained in:
kuaifan 2022-03-10 17:44:57 +08:00
parent 48b7f4924e
commit 43c51d48d9
77 changed files with 505 additions and 1552 deletions

View File

@ -497,7 +497,6 @@ class DialogController extends AbstractController
]);
}
/**
* @api {get} api/dialog/msg/mark 13. 消息标记操作
*
@ -530,23 +529,7 @@ class DialogController extends AbstractController
->whereReadAt(null)
->whereDialogId($dialogId)
->chunkById(100, function ($list) {
/** @var WebSocketDialogMsgRead $item */
$dialogMsg = [];
foreach ($list as $item) {
$item->read_at = Carbon::now();
$item->save();
if (isset($dialogMsg[$item->msg_id])) {
$dialogMsg[$item->msg_id]['re']++;
} else {
$dialogMsg[$item->msg_id] = [
'ob' => $item->webSocketDialogMsg,
're' => 1
];
}
}
foreach ($dialogMsg as $item) {
$item['ob']?->generatePercentage($item['re']);
}
WebSocketDialogMsgRead::onlyMarkRead($list);
});
$dialogUser->is_mark_unread = 0;
$dialogUser->save();
@ -560,6 +543,9 @@ class DialogController extends AbstractController
default:
return Base::retError("参数错误");
}
return Base::retSuccess("success");
return Base::retSuccess("success", [
'id' => $dialogId,
'is_mark_unread' => $dialogUser->is_mark_unread,
]);
}
}

View File

@ -894,6 +894,9 @@ class ProjectController extends AbstractController
* - all所有
* - yes已归档
* - no未归档默认
* @apiParam {String} [deleted] 是否读取已删除
* - yes
* - no默认
* @apiParam {Object} sorts 排序方式
* - sorts.complete_at 完成时间asc|desc
* - sorts.archived_at 归档时间asc|desc
@ -916,7 +919,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');
$deleted = Request::input('deleted', 'no');
$keys = Request::input('keys');
$sorts = Request::input('sorts');
$keys = is_array($keys) ? $keys : [];
@ -955,7 +958,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(),'taskTime');
$builder->betweenTime(Carbon::parse($time[0])->startOfDay(), Carbon::parse($time[1])->endOfDay());
}
}
//
@ -996,6 +999,9 @@ class ProjectController extends AbstractController
*
* @apiParam {Array} [userid] 指定会员,如:[1, 2]
* @apiParam {Array} [time] 指定时间范围,如:['2020-12-12', '2020-12-30']
* @apiParam {String} [type]
* - createdTime 任务创建时间
* - taskTime 任务计划时间(默认)
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
@ -1007,6 +1013,7 @@ class ProjectController extends AbstractController
//
$userid = Base::arrayRetainInt(Request::input('userid'), true);
$time = Request::input('time');
$type = Request::input('type','taskTime');
if (empty($userid) || empty($time)) {
return Base::retError('参数错误');
}
@ -1019,7 +1026,6 @@ 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';
@ -1042,7 +1048,7 @@ class ProjectController extends AbstractController
->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(),$type);
->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) {
@ -1050,16 +1056,16 @@ class ProjectController extends AbstractController
$developTime = 0;//开发时间
$testTime = 0;//验收/测试时间
foreach ($flowChanges as $change) {
if (strpos($change->before_flow_item_name, 'end') === false) {
if (!str_contains($change->before_flow_item_name, 'end')) {
$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) {
if (str_contains($change->before_flow_item_name, 'progress') && str_contains($change->before_flow_item_name, '进行')) {
$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) {
if (str_contains($change->before_flow_item_name, 'test') || str_contains($change->before_flow_item_name, '测试') || strpos($change->before_flow_item_name, '验收') !== false) {
$testCtime = Carbon::parse($change->created_at)->timestamp;
$tTime = Carbon::parse($upOne->created_at)->timestamp;
$tMinusNum = $testCtime - $tTime;
@ -1072,14 +1078,14 @@ class ProjectController extends AbstractController
$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) {
if (str_contains($lastChange->after_flow_item_name, 'progress') || str_contains($lastChange->after_flow_item_name, '进行')) {
$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) {
} elseif (str_contains($lastChange->after_flow_item_name, 'test') || str_contains($lastChange->after_flow_item_name, '测试') || 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) {
if (str_contains($firstChange->after_flow_item_name, 'end')) {
$firstDevTime = Carbon::parse($firstChange->created_at)->timestamp - Carbon::parse($task->created_at)->timestamp;
$developTime += $firstDevTime;
}
@ -1532,9 +1538,6 @@ 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);
@ -1644,6 +1647,9 @@ class ProjectController extends AbstractController
* @apiName task__remove
*
* @apiParam {Number} task_id 任务ID
* @apiParam {String} type
* - recovery: 还原
* - delete: 删除(默认)
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
@ -1654,24 +1660,16 @@ class ProjectController extends AbstractController
User::auth();
//
$task_id = intval(Request::input('task_id'));
$type = Request::input('type');
$type = Request::input('type', 'delete');
//
$task = ProjectTask::userTask($task_id, null, true);
if($type == 'recovery'){
$task->deleted_at = null;
$task->deleted_userid = 0;
$task->save();
$task = ProjectTask::userTask($task_id, null, true, [], $type === 'recovery');
if ($type == 'recovery') {
$task->recoveryTask();
return Base::retSuccess('操作成功', ['id' => $task->id]);
} else {
$task->deleteTask();
return Base::retSuccess('删除成功', ['id' => $task->id]);
}
if($type == 'completely_delete'){
$task->forceDelete();
return Base::retSuccess('彻底删除成功', ['id' => $task->id]);
}
$task->deleted_userid = User::userid();
$task->save();
//
$task->deleteTask();
return Base::retSuccess('删除成功', ['id' => $task->id]);
}
/**

View File

@ -25,7 +25,7 @@ class SystemController extends AbstractController
* @apiParam {String} type
* - get: 获取(默认)
* - all: 获取所有(需要管理员权限)
* - save: 保存设置参数reg、reg_invite、login_code、password_policy、project_invite、chat_nickname、auto_archived、archived_day
* - save: 保存设置参数reg、reg_invite、login_code、password_policy、project_invite、chat_nickname、auto_archived、archived_day、start_home、home_footer
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
@ -77,6 +77,59 @@ class SystemController extends AbstractController
return Base::retSuccess('success', $setting ?: json_decode('{}'));
}
/**
* @api {get} api/system/setting/email 14. 获取邮箱设置、保存邮箱设置
*
* @apiVersion 1.0.0
* @apiGroup system
* @apiName setting__email
*
* @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 setting__email()
{
$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('{}'));
}
/**
* @api {get} api/system/demo 02. 获取演示账号
*
@ -501,57 +554,4 @@ class SystemController extends AbstractController
]);
}
/**
* @api {get} api/system/emailSetting 14. 获取邮箱设置、保存邮箱设置
*
* @apiVersion 1.0.0
* @apiGroup system
* @apiName emailSetting
*
* @apiParam {String} type
* - get: 获取(默认)
* - all: 获取所有(需要管理员权限)
* - save: 保存设置参数smtp_server port account password reg_verify notice task_remind_hours task_remind_hours2
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据
*/
public function emailSetting()
{
$type = trim(Request::input('type'));
if ($type == 'save') {
if (env("SYSTEM_SETTING") == 'disabled') {
return Base::retError('当前环境禁止修改');
}
User::auth('admin');
$all = Request::input();
foreach ($all as $key => $value) {
if (!in_array($key, ['smtp_server', 'port', 'account', 'password', 'reg_verify', 'notice', 'task_remind_hours', 'task_remind_hours2'])) {
unset($all[$key]);
}
}
$setting = Base::setting('emailSetting', Base::newTrim($all));
} else {
$setting = Base::setting('emailSetting');
}
//
if ($type == 'all' || $type == 'save') {
User::auth('admin');
$setting['reg_invite'] = $setting['reg_invite'] ?: Base::generatePassword(8);
} else {
if (isset($setting['reg_invite'])) unset($setting['reg_invite']);
}
//
$setting['smtp_server'] = $setting['smtp_server'] ?: '';
$setting['port'] = $setting['port'] ?: '';
$setting['account'] = $setting['account'] ?: '';
$setting['password'] = $setting['password'] ?: '';
$setting['reg_verify'] = $setting['reg_verify'] ?: 'close';
$setting['notice'] = $setting['notice'] ?: 'open';
$setting['task_remind_hours'] = floatval($setting['task_remind_hours']) ?: 0;
$setting['task_remind_hours2'] = floatval($setting['task_remind_hours2']) ?: 0;
//
return Base::retSuccess('success', $setting ?: json_decode('{}'));
}
}

View File

@ -44,7 +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;
$isRegVerify = Base::settingFind('emailSetting', 'reg_verify') === 'open';
if ($type == 'reg') {
$setting = Base::setting('system');
if ($setting['reg'] == 'close') {
@ -58,7 +58,7 @@ class UsersController extends AbstractController
$user = User::reg($email, $password);
if ($isRegVerify) {
UserEmailVerification::userEmailSend($user);
return Base::retError('注册成功,请验证邮箱后登录', ['code' => 1000]);
return Base::retError('注册成功,请验证邮箱后登录', ['code' => 'email']);
}
} else {
$needCode = !Base::isError(User::needCode($email));
@ -80,10 +80,10 @@ class UsersController extends AbstractController
};
$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)) {
@ -589,14 +589,11 @@ class UsersController extends AbstractController
{
$data = Request::input();
// 表单验证
$validator = Validator::make($data, [
"code" => ["required"],
], [
"code.required" => "required字段非法",
Base::validator($data, [
'code.required' => '验证码不能为空',
]);
if ($validator->fails())
return Base::retError($validator->errors()->first());
$res = UserEmailVerification::where('code', $data['code'])->first();
//
$res = UserEmailVerification::whereCode($data['code'])->first();
if (empty($res)) {
return Base::retError('无效连接,请重新注册');
}
@ -604,17 +601,17 @@ class UsersController extends AbstractController
if (intval($res->status) === 1)
return Base::retError('链接已经使用过',['code' => 2]);
$oldTime = strtotime($res->created_at);
$time = time();
$oldTime = Carbon::parse($res->created_at)->timestamp;
$time = Base::Time();
//24个小时失效
if (abs($time - $oldTime) > 86400) {
return Base::retError("链接已失效,请重新登录/注册");
}
UserEmailVerification::where('code', $data['code'])
UserEmailVerification::whereCode($data['code'])
->update([
'status' => 1
]);
User::where('userid', $res->userid)->update([
User::whereUserid($res->userid)->update([
'is_email_verity' => 1
]);
return Base::retSuccess('绑定邮箱成功');

View File

@ -428,7 +428,7 @@ class Project extends AbstractModel
if (!$hasStart) {
throw new ApiException('至少需要1个开始状态');
}
if($testNum > 1){
if ($testNum > 1) {
throw new ApiException('验收/测试状态只能有1个');
}
if (!$hasEnd) {

View File

@ -61,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, $type)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask betweenTime($start, $end, $type = 'taskTime')
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask newQuery()
* @method static \Illuminate\Database\Query\Builder|ProjectTask onlyTrashed()
@ -319,25 +319,29 @@ class ProjectTask extends AbstractModel
* @param $type
* @return mixed
*/
public function scopeBetweenTime($query, $start, $end, $type)
public function scopeBetweenTime($query, $start, $end, $type = 'taskTime')
{
$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);
});
switch ($type) {
case 'createdTime':
$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);
});
break;
default:
$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);
});
break;
}
});
return $query;
@ -1087,6 +1091,8 @@ class ProjectTask extends AbstractModel
$dialog?->deleteDialog();
}
self::whereParentId($this->id)->delete();
$this->deleted_userid = User::userid();
$this->save();
$this->addLog("删除{任务}");
$this->delete();
});
@ -1096,6 +1102,28 @@ class ProjectTask extends AbstractModel
return true;
}
/**
* 还原任务
* @param bool $pushMsg 是否推送
* @return bool
*/
public function recoveryTask($pushMsg = true)
{
AbstractModel::transaction(function () {
if ($this->dialog_id) {
$dialog = WebSocketDialog::withTrashed()->find($this->dialog_id);
$dialog?->recoveryDialog();
}
self::whereParentId($this->id)->withTrashed()->restore();
$this->addLog("还原{任务}");
$this->restore();
});
if ($pushMsg) {
$this->pushMsg('restore');
}
return true;
}
/**
* 添加任务日志
* @param string $detail
@ -1192,12 +1220,16 @@ class ProjectTask extends AbstractModel
* @param bool $archived true:仅限未归档, false:仅限已归档, null:不限制
* @param int|bool $permission 0|false:不限制, 1|true:限制项目负责人、任务负责人、协助人员及任务创建者, 2:已有负责人才限制true (子任务时如果是主任务负责人也可以)
* @param array $with
* @param bool $getTrashed
* @return self
*/
public static function userTask($task_id, $archived = true, $permission = 0, $with = [])
public static function userTask($task_id, $archived = true, $permission = 0, $with = [], $getTrashed = false)
{
$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();
$builder = self::with($with)->allData()->where("project_tasks.id", intval($task_id));
if ($getTrashed) {
$builder->withTrashed();
}
$task = $builder->first();
//
if (empty($task)) {
throw new ApiException('任务不存在', [ 'task_id' => $task_id ], -4002);
@ -1247,10 +1279,7 @@ class ProjectTask extends AbstractModel
$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"));
UserEmailVerification::initMailConfig();
foreach ($users as $user) {
/** @var User $user */
if (ProjectTaskMailLog::whereTaskId($task['id'])->whereUserid($user->userid)->whereType($type)->whereIsSend(1)->exists()) {

View File

@ -187,7 +187,7 @@ class User extends AbstractModel
throw new ApiException('请输入正确的邮箱地址');
}
if (User::email2userid($email) > 0) {
$isRegVerify = Base::settingFind('emailSetting', 'reg_verify') === 'open' ? true : false;
$isRegVerify = Base::settingFind('emailSetting', 'reg_verify') === 'open';
$user = self::whereUserid(User::email2userid($email))->first();
if ($isRegVerify && $user->is_email_verity === 0) {
UserEmailVerification::userEmailSend($user);

View File

@ -1,9 +1,7 @@
<?php
namespace App\Models;
use App\Exceptions\ApiException;
use App\Module\Base;
use Carbon\Carbon;
@ -15,22 +13,22 @@ use Mail;
* App\Models\UserEmailVerification
*
* @property int $id
* @property int $userid 用户id
* @property string $code 验证参数
* @property string $email 电子邮箱
* @property string $status 0-未验证1-已验证
* @property int|null $userid 用户id
* @property string|null $code 验证参数
* @property string|null $email 电子邮箱
* @property int|null $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)
* @method static \Illuminate\Database\Eloquent\Builder|UserEmailVerification newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|UserEmailVerification newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|UserEmailVerification query()
* @method static \Illuminate\Database\Eloquent\Builder|UserEmailVerification whereCode($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserEmailVerification whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserEmailVerification whereEmail($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserEmailVerification whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserEmailVerification whereStatus($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserEmailVerification whereUpdatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|UserEmailVerification whereUserid($value)
* @mixin \Eloquent
*/
class UserEmailVerification extends AbstractModel
@ -50,24 +48,21 @@ class UserEmailVerification extends AbstractModel
$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();
$url = Base::fillUrl('single/valid/email') . '?code=' . $info['code'];
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"));
self::initMailConfig();
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->from(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) {
if (str_contains($exception->getMessage(), "Timed Out")) {
throw new ApiException("language.TimedOut");
} elseif ($exception->getCode() == 550) {
throw new ApiException('邮件内容被拒绝,请检查邮箱是否开启接收功能');
@ -76,4 +71,18 @@ class UserEmailVerification extends AbstractModel
}
}
}
/**
* 初始化邮箱配置
* @return void
*/
public static function initMailConfig()
{
$config = Base::setting('emailSetting');
Config::set("mail.mailers.smtp.host", $config['smtp_server'] ?: Config::get("mail.mailers.smtp.host"));
Config::set("mail.mailers.smtp.port", $config['port'] ?: Config::get("mail.mailers.smtp.port"));
Config::set("mail.mailers.smtp.username", $config['account'] ?: Config::get("mail.mailers.smtp.username"));
Config::set("mail.mailers.smtp.password", $config['password'] ?: Config::get("mail.mailers.smtp.password"));
Config::set("mail.mailers.smtp.encryption", 'ssl');
}
}

View File

@ -54,14 +54,26 @@ class WebSocketDialog extends AbstractModel
public function deleteDialog()
{
AbstractModel::transaction(function () {
WebSocketDialogMsgRead::whereDialogId($this->id)->whereNull('read_at')->update([
'read_at' => Carbon::now()
]);
WebSocketDialogMsgRead::whereDialogId($this->id)
->whereNull('read_at')
->chunkById(100, function ($list) {
WebSocketDialogMsgRead::onlyMarkRead($list);
});
$this->delete();
});
return true;
}
/**
* 还原会话
* @return bool
*/
public function recoveryDialog()
{
$this->restore();
return true;
}
/**
* 获取对话(同时检验对话身份)
* @param $dialog_id

View File

@ -2,6 +2,8 @@
namespace App\Models;
use Carbon\Carbon;
/**
* App\Models\WebSocketDialogMsgRead
*
@ -38,4 +40,30 @@ class WebSocketDialogMsgRead extends AbstractModel
{
return $this->hasOne(WebSocketDialogMsg::class, 'id', 'msg_id');
}
/**
* 仅标记成阅读
* @param $list
* @return void
*/
public static function onlyMarkRead($list)
{
$dialogMsg = [];
/** @var WebSocketDialogMsgRead $item */
foreach ($list as $item) {
$item->read_at = Carbon::now();
$item->save();
if (isset($dialogMsg[$item->msg_id])) {
$dialogMsg[$item->msg_id]['readNum']++;
} else {
$dialogMsg[$item->msg_id] = [
'dialogMsg' => $item->webSocketDialogMsg,
'readNum' => 1
];
}
}
foreach ($dialogMsg as $item) {
$item['dialogMsg']?->generatePercentage($item['readNum']);
}
}
}

View File

@ -12,6 +12,7 @@ use Illuminate\Support\Facades\Config;
use Redirect;
use Request;
use Storage;
use Validator;
class Base
{
@ -3000,112 +3001,27 @@ 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"];
}
/**
* 统一验证器
* @param $data
* @param $messages
*/
public static function validator($data, $messages) {
$rules = [];
foreach ($messages as $key => $item) {
$keys = explode(".", $key);
if (isset($keys[1])) {
if (isset($rules[$keys[0]])) {
$rules[$keys[0]] = $rules[$keys[0]] . '|' . $keys[1];
} else {
$rules[$keys[0]] = $keys[1];
}
}
//多维数组去除临时key值
else
{
foreach($b AS $key=>$value) {
unset($value["okey"]);
if($isvalues){
$c[] = $value;
}else{
$c[$key] = $value;
}
}
}
return $c;
}
$validator = Validator::make($data, $rules, $messages);
if ($validator->fails()) {
throw new ApiException($validator->errors()->first());
}
}
}

View File

@ -40,12 +40,14 @@ class OverdueRemindEmailTask extends AbstractTask
->take(100)
->get()
->toArray();
}
$taskLists = array_merge($taskLists1, $taskLists2);
$taskLists = Base::assoc_unique($taskLists, 'id');
$ids = [];
foreach ($taskLists as $task) {
ProjectTask::overdueRemindEmail($task);
if (!in_array($task->id, $ids)) {
$ids[] = $task->id;
ProjectTask::overdueRemindEmail($task);
}
}
}
}

View File

@ -36,14 +36,15 @@ return [
'mailers' => [
'smtp' => [
'transport' => 'smtp',
'host' => 'smtp.qq.com',
'port' => 465,
'encryption' => 'ssl',
'username' => '302645122@qq.com',
'password' => 'ijncezxbmhrvbhab',
'timeout' => 15,
'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,
],
'ses' => [
'transport' => 'ses',
],
@ -69,7 +70,6 @@ return [
'array' => [
'transport' => 'array',
],
],
/*

View File

@ -14,7 +14,9 @@ class AddIsEmailVerityToUsers extends Migration
public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->boolean('is_email_verity')->default(0)->nullable(false)->after('disable_at')->comment('邮箱是否已验证');
if (!Schema::hasColumn('users', 'is_email_verity')) {
$table->boolean('is_email_verity')->default(0)->nullable(false)->after('disable_at')->comment('邮箱是否已验证');
}
});
}

View File

@ -14,7 +14,9 @@ class AddWebSocketDialogUsersAddIsMarkUnread extends Migration
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是');
if (!Schema::hasColumn('web_socket_dialog_users', 'is_mark_unread')) {
$table->boolean('is_mark_unread')->default(0)->nullable(false)->after('top_at')->comment('是否标记为未读0否1是');
}
});
}

View File

@ -1,6 +1,6 @@
{
"name": "DooTask",
"version": "0.10.30",
"version": "0.11.51",
"description": "DooTask is task management system.",
"main": "electron.js",
"license": "MIT",

View File

@ -1,6 +1,6 @@
{
"name": "DooTask",
"version": "0.10.30",
"version": "0.11.51",
"description": "DooTask is task management system.",
"scripts": {
"start": "./cmd dev",

2
public/css/app.css vendored

File diff suppressed because one or more lines are too long

View File

@ -1,11 +1,12 @@
<?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">
<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
<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

Before

Width:  |  Height:  |  Size: 909 B

After

Width:  |  Height:  |  Size: 921 B

2
public/js/app.js vendored

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

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

File diff suppressed because one or more lines are too long

View File

@ -1 +0,0 @@
"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}}]);

File diff suppressed because one or more lines are too long

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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

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

@ -0,0 +1 @@
"use strict";(self.webpackChunkDooTask=self.webpackChunkDooTask||[]).push([[596],{55983:(t,s,e)=>{e.d(s,{Z:()=>r});var i=e(1519),o=e.n(i)()((function(t){return t[1]}));o.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 r=o},58596:(t,s,e)=>{e.r(s),e.d(s,{default:()=>c});const i={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(){t.success=!0,t.error=!1})).catch((function(s){var e=s.data,i=s.msg;2===e.code?t.goForward({path:"/"},!0):(t.success=!1,t.error=!0,t.errorText=t.$L(i))}))},userLogout:function(){this.$store.dispatch("logout",!1)}}};var o=e(93379),r=e.n(o),a=e(55983),n={insert:"head",singleton:!1};r()(a.Z,n);a.Z.locals;const c=(0,e(51900).Z)(i,(function(){var t=this,s=t.$createElement,e=t._self._c||s;return e("div",{staticClass:"valid-wrap"},[e("div",{staticClass:"valid-box"},[e("div",{staticClass:"valid-title"},[t._v(t._s(t.$L("验证邮箱")))]),t._v(" "),t.success||t.error?t._e():e("Spin",{attrs:{size:"large"}}),t._v(" "),t.success?e("div",{staticClass:"validation-text"},[e("p",[t._v(t._s(t.$L("您的邮箱已通过验证")))]),t._v(" "),e("p",[t._v(t._s(t.$L("今后您可以通过此邮箱重置您的账号密码")))])]):t._e(),t._v(" "),t.error?e("div",{staticClass:"validation-text"},[e("div",[t._v(t._s(t.errorText))])]):t._e(),t._v(" "),t.success?e("div",{attrs:{slot:"footer"},slot:"footer"},[e("Button",{attrs:{type:"primary",long:""},on:{click:t.userLogout}},[t._v(t._s(t.$L("返回首页")))])],1):t._e()],1)])}),[],!1,null,null,null).exports}}]);

File diff suppressed because one or more lines are too long

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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

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

File diff suppressed because one or more lines are too long

View File

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

View File

@ -1,5 +1,5 @@
<template>
<div v-if="needStartHome">
<div v-if="needStartHome" class="page-index">
<div class="page-warp">
<div class="page-header">
<div class="header-nav">
@ -11,13 +11,11 @@
<Dropdown trigger="click" @on-click="setLanguage">
<Icon
class="header-right-one-language"
type="md-globe"
/>
type="md-globe"/>
<a
href="javascript:void(0)"
class="header-right-one-dropdown"
v-if="screenWidth>441"
>
v-if="windowWidth>441">
{{ currentLanguage }}
<Icon type="ios-arrow-down"></Icon>
</a>
@ -27,7 +25,7 @@
:key="key"
:name="key"
:selected="getLanguage() === key"
>{{ item }}
>{{ item }}
</Dropdown-item>
</DropdownMenu>
</Dropdown>
@ -61,8 +59,8 @@
<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 class="box-pic" v-if="windowWidth<1920">
<img class="box-img" :src="$A.originUrl('images/index/box-pic1.png')"/>
</div>
<div class="box-one-pic1" v-else>
@ -79,7 +77,7 @@
</div>
</div>
</div>
<div class="main-box-two" v-if="screenWidth>=1920">
<div class="main-box-two" v-if="windowWidth>=1920">
<div class="box-two-tips">
<div class="box-square"></div>
<div class="box-title">
@ -97,7 +95,7 @@
<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" />
<img class="box-img" :src="$A.originUrl('images/index/box-pic2.png')"/>
</div>
<div class="box-square"></div>
<div class="box-title">
@ -108,19 +106,14 @@
$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 class="box-pic" v-if="windowWidth<1920">
<img class="box-img" :src="$A.originUrl('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">
@ -133,7 +126,7 @@
</div>
</div>
</div>
<div class="main-box-two" v-if="screenWidth>=1920">
<div class="main-box-two" v-if="windowWidth>=1920">
<div class="box-two-tips">
<div class="box-square"></div>
<div class="box-title">
@ -151,7 +144,7 @@
<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" />
<img class="box-img" :src="$A.originUrl('images/index/box-pic4.png')"/>
</div>
<div class="box-square"></div>
<div class="box-title">
@ -162,12 +155,8 @@
$L("通过灵活的任务日历,轻松安排每一天的日程,把任务拆解到每天,让工作目标更清晰,时间分配更合理。")
}}
</div>
</div>
</div>
</div>
<div class="page-footer">
<div class="footer-service">
@ -190,32 +179,24 @@
</template>
<script>
import {mapState} from "vuex";
export default {
data() {
return {
screenWidth: document.body.clientWidth,
needStartHome: false,
homeFooter: '',
};
},
watch: {
screenWidth(nVal) {
// console.log(nVal)
},
},
computed: {
...mapState(['userId', 'windowWidth']),
currentLanguage() {
return this.languageList[this.languageType] || "Language";
},
},
mounted() {
this.getNeedStartHome();
const that = this;
window.onresize=()=>{
window.screenWidth=document.body.clientWidth
that.screenWidth=window.screenWidth
}
},
methods: {
@ -240,28 +221,22 @@ export default {
);
},
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);
}
this.$store.dispatch("call", {
url: "system/get/starthome",
}).then(({data}) => {
this.homeFooter = data.home_footer;
if (this.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;
});
}
}).catch(() => {
this.needStartHome = false;
});
},
},
deactivated() {
// this.$destroy()
},
};
</script>

View File

@ -322,8 +322,8 @@ export default {
});
}).catch(({data, msg}) => {
this.loadIng--;
if (data.code === 1000) {
$A.modalSuccess(msg);
if (data.code === 'email') {
$A.modalWarning(msg);
} else {
$A.modalError(msg);
}

View File

@ -20,9 +20,32 @@
</div>
<DropdownMenu slot="list">
<template v-for="item in menu">
<!--最近打开的任务-->
<Dropdown
v-if="item.path === 'taskBrowse'"
placement="right-start">
<DropdownItem>
<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"
v-if="item.id > 0 && key < 10"
:key="key"
class="task-title"
@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-if="item.path === 'team'"
v-else-if="item.path === 'team'"
placement="right-start">
<DropdownItem divided>
<div class="manage-menu-flex">
@ -42,30 +65,6 @@
<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'"
@ -155,7 +154,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.top_at" class="icon-top"></div>
<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">
@ -276,7 +275,7 @@
<FormItem prop="type" :label="$L('导出时间类型')">
<RadioGroup v-model="exportData.type">
<Radio label="taskTime">{{$L('任务时间')}}</Radio>
<Radio label="CreatedTime">{{$L('创建时间')}}</Radio>
<Radio label="createdTime">{{$L('创建时间')}}</Radio>
</RadioGroup>
</FormItem>
</Form>
@ -469,6 +468,7 @@ export default {
'userId',
'userInfo',
'userIsAdmin',
'cacheTasks',
'cacheDialogs',
'cacheProjects',
'projectTotal',
@ -511,7 +511,9 @@ export default {
const {userIsAdmin} = this;
if (userIsAdmin) {
return [
{path: 'personal', name: '个人设置'},
{path: 'taskBrowse', name: '最近打开的任务'},
{path: 'personal', name: '个人设置', divided: true},
{path: 'password', name: '密码设置'},
{path: 'clearCache', name: '清除缓存'},
@ -523,8 +525,6 @@ export default {
{path: 'team', name: '团队管理', divided: true},
{path: 'taskBrowse', name: '最近打开的任务', divided: true},
{path: 'theme', name: '主题皮肤', divided: true},
{path: 'language', name: this.currentLanguage, divided: true},
@ -533,7 +533,9 @@ export default {
]
} else {
return [
{path: 'personal', name: '个人设置'},
{path: 'taskBrowse', name: '最近打开的任务'},
{path: 'personal', name: '个人设置', divided: true},
{path: 'password', name: '密码设置'},
{path: 'clearCache', name: '清除缓存'},
@ -542,8 +544,6 @@ 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},
@ -591,11 +591,9 @@ export default {
},
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;
}
const {cacheTasks, cacheTaskBrowse, userId} = this;
return cacheTaskBrowse.filter(({userid}) => userid === userId).map(({id}) => {
return cacheTasks.find(task => task.id === id) || {}
});
},
},
@ -616,55 +614,6 @@ 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;

View File

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

View File

@ -286,7 +286,7 @@ export default {
"id": -10,
"name": "待处理",
"status": "start",
"turns": [-10, -11, -12, -13],
"turns": [-10, -11, -12, -13, -14],
"userids": [],
"usertype": 'add',
"userlimit": 0,
@ -295,7 +295,7 @@ export default {
"id": -11,
"name": "进行中",
"status": "progress",
"turns": [-10, -11, -12, -13],
"turns": [-10, -11, -12, -13, -14],
"userids": [],
"usertype": 'add',
"userlimit": 0,
@ -304,7 +304,7 @@ export default {
"id": -12,
"name": "待测试",
"status": "test",
"turns": [-10, -11, -12, -13],
"turns": [-10, -11, -12, -13, -14],
"userids": [],
"usertype": 'add',
"userlimit": 0,
@ -313,7 +313,7 @@ export default {
"id": -13,
"name": "已完成",
"status": "end",
"turns": [-10, -11, -12, -13],
"turns": [-10, -11, -12, -13, -14],
"userids": [],
"usertype": 'add',
"userlimit": 0,
@ -322,7 +322,7 @@ export default {
"id": -14,
"name": "已取消",
"status": "end",
"turns": [-10, -11, -12, -13],
"turns": [-10, -11, -12, -13, -14],
"userids": [],
"usertype": 'add',
"userlimit": 0,

View File

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

View File

@ -1,6 +1,6 @@
<template>
<div class="task-archived">
<div class="archived-title">
<div class="task-deleted">
<div class="deleted-title">
{{$L('删除的任务')}}
<div class="title-icon">
<Loading v-if="loadIng > 0"/>
@ -30,17 +30,26 @@
</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 class="table-page-box">
<Table
:columns="columns"
:data="list"
:loading="loadIng > 0"
:no-data-text="$L(noText)"
stripe/>
<Page
:total="total"
:current="page"
:page-size="pageSize"
:disabled="loadIng > 0"
:simple="windowMax768"
:page-size-opts="[10,20,30,50,100]"
show-elevator
show-sizer
show-total
@on-change="setPage"
@on-page-size-change="setPageSize"/>
</div>
</div>
</template>
@ -98,13 +107,7 @@ export default {
key: 'name',
minWidth: 200,
render: (h, {row}) => {
return h('AutoTip', {
on: {
'on-click': () => {
this.$store.dispatch("openTask", row);
}
}
}, row.name);
return h('AutoTip', row.name);
}
},
{
@ -140,18 +143,6 @@ export default {
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('你确定要还原删除吗?'),
@ -160,7 +151,6 @@ export default {
placement: 'left',
},
style: {
marginLeft: '6px',
fontSize: '13px',
cursor: 'pointer',
color: '#8bcf70',
@ -171,25 +161,6 @@ export default {
}
},
}, 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: {
@ -263,23 +234,6 @@ export default {
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();
});
}
}
}

View File

@ -74,7 +74,6 @@
<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"
@ -88,9 +87,6 @@
<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>
@ -121,18 +117,14 @@
<Button :loading="ownerLoad > 0" size="small" type="primary" @click="onOwner(true)">确定</Button>
</div>
</div>
<Button v-if="!taskDetail.deleted_at" slot="reference" :loading="ownerLoad > 0" class="pick" type="primary">{{$L('我要领取任务')}}</Button>
<Button 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"
@ -348,7 +340,7 @@
</ul>
</FormItem>
</Form>
<div v-if="menuList.length > 0 && !taskDetail.deleted_at" class="add">
<div v-if="menuList.length > 0" class="add">
<EDropdown
trigger="click"
placement="bottom"
@ -391,21 +383,21 @@
<div class="head">
<Icon class="icon" type="ios-chatbubbles-outline" />
<div class="nav">
<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>
<p :class="{active:navActive=='dialog'}" @click="navActive='dialog'">{{$L('聊天')}}</p>
<p :class="{active:navActive=='log'}" @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' || taskDetail.deleted_at) && taskId > 0" ref="log" :task-id="taskDetail.id" :show-load="false" @on-load-change="logLoadChange"/>
<ProjectLog v-if="navActive=='log' && 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" v-if="!taskDetail.deleted_at">
<div class="no-input">
<DragInput
class="dialog-input"
v-model="msgText"

View File

@ -498,12 +498,10 @@ export default {
dialog_id: this.topOperateItem.id,
type: type
},
}).then(() => {
this.$store.dispatch("getDialogs");
this.$Modal.remove();
}).then(({data}) => {
this.$store.dispatch("saveDialog", data);
}).catch(({msg}) => {
$A.modalError(msg, 301);
this.$Modal.remove();
$A.modalError(msg);
});
}
}

View File

@ -93,7 +93,7 @@ export default {
systemSetting(save) {
this.loadIng++;
this.$store.dispatch("call", {
url: 'system/emailSetting?type=' + (save ? 'save' : 'all'),
url: 'system/setting/email?type=' + (save ? 'save' : 'all'),
data: this.formData,
}).then(({data}) => {
if (save) {

View File

@ -18,10 +18,7 @@
</template>
<script>
export default {
name: "validEmail",
components: {},
data() {
return {
success: false,
@ -35,23 +32,21 @@ export default {
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}) => {
this.$store.dispatch("call", {
url: "users/email/verification",
data: {
code: this.$route.query.code
}
}).then(() => {
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);
this.errorText = this.$L(msg);
}
});
},

View File

@ -207,7 +207,6 @@ export default {
dispatch("getProjects").catch(() => {});
dispatch("getDialogs").catch(() => {});
dispatch("getTaskForDashboard");
dispatch("getTaskBrowse");
},
/**
@ -425,29 +424,20 @@ 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);
$A.setStorage("cacheTaskBrowse", state.cacheTaskBrowse);
dispatch("saveUserInfo", $A.isJson(userInfo) ? userInfo : state.userInfo);
//
resolve()
@ -1412,20 +1402,7 @@ export default {
dispatch("getTaskContent", task_id);
dispatch("getTaskFiles", task_id);
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);
})
dispatch("saveTaskBrowse", task_id);
}).catch(({msg}) => {
$A.modalWarning({
content: msg,
@ -1781,6 +1758,28 @@ export default {
}
},
/**
* 保存任务浏览记录
* @param state
* @param task_id
*/
saveTaskBrowse({state}, task_id) {
let index = state.cacheTaskBrowse.findIndex(({id}) => id == task_id)
if (index > -1) {
state.cacheTaskBrowse.splice(index, 1)
}
state.cacheTaskBrowse.unshift({
id: task_id,
userid: state.userId
})
if (state.cacheTaskBrowse.length > 200) {
state.cacheTaskBrowse.splice(200);
}
setTimeout(() => {
$A.setStorage("cacheTaskBrowse", state.cacheTaskBrowse);
})
},
/** *****************************************************************************************/
/** ************************************** 会话 **********************************************/
/** *****************************************************************************************/
@ -2294,6 +2293,7 @@ export default {
const {action, data} = msg;
switch (action) {
case 'add':
case 'restore':
dispatch("addTaskSuccess", data)
break;
case 'update':
@ -2406,9 +2406,5 @@ export default {
*/
websocketClose({state}) {
state.ws && state.ws.close();
},
getTaskBrowse({state}) {
state.cacheTaskBrowse = $A.getStorageArray("cacheTaskBrowse" + state.userId);
}
}

View File

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

View File

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

View File

@ -9,6 +9,7 @@
@import "task-add";
@import "task-add-simple";
@import "task-archived";
@import "task-deleted";
@import "task-detail";
@import "task-menu";
@import "task-priority";

View File

@ -77,20 +77,22 @@
}
> em {
flex-shrink: 0;
font-style: normal;
font-size: 17px;
font-weight: 500;
padding-left: 6px;
}
.top-text{
.top-text {
flex-shrink: 0;
width: 40px;
height: 24px;
border-radius:4px;
border-radius: 4px;
margin-left: 10px;
background-color: #8BCF70;
color:#FFFFFF;
color: #FFFFFF;
text-align: center;
margin-top: 4px;
padding-top: 1px
}
}

View File

@ -0,0 +1,38 @@
.task-deleted {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
padding: 20px;
display: flex;
flex-direction: column;
.deleted-title {
color: #333333;
font-size: 20px;
font-weight: 500;
line-height: 1;
margin-bottom: 24px;
display: flex;
align-items: center;
.title-icon {
display: flex;
align-items: center;
width: 14px;
height: 14px;
margin-left: 4px;
margin-top: 2px;
> i {
cursor: pointer;
}
}
}
.table-page-box {
flex: 1;
height: 0;
}
}

View File

@ -1,3 +1,11 @@
.page-index {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
overflow: auto;
}
@media (max-width: 1919px) {
.page-warp {
width: 100%;
@ -51,7 +59,7 @@
text-overflow: ellipsis;
}
@media(max-width:414px){
.header-right-three {
max-width: 50px;
font-size: 12px;
@ -69,7 +77,7 @@
}
}
@media(min-width:414px){
.header-right-three {
font-size: 12px;
height: 36px;
@ -130,7 +138,7 @@
}
}
}
}
.header-content {
@ -239,11 +247,11 @@
.main-box-one {
position: relative;
margin-bottom: 70px;
.box-one-tips {
width: 100%;
}
}
.main-box-two {
width: 100%;
@ -251,11 +259,11 @@
margin-bottom: 70px;
.box-two-square {
}
.box-two-tips {
width: 100%;
}
}
}
.page-footer {

View File

@ -205,6 +205,8 @@
.icon-top {
width: 14px;
height: 14px;
background: url("data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4KPCEtLSBHZW5lcmF0b3I6IEFkb2JlIElsbHVzdHJhdG9yIDI1LjAuMCwgU1ZHIEV4cG9ydCBQbHVnLUluIC4gU1ZHIFZlcnNpb246IDYuMDAgQnVpbGQgMCkgIC0tPgo8c3ZnIHZlcnNpb249IjEuMSIgaWQ9IuWbvuWxgl8xIiB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHhtbG5zOnhsaW5rPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5L3hsaW5rIiB4PSIwcHgiCiAgICAgeT0iMHB4IgogICAgIHZpZXdCb3g9IjAgMCAyNCAyNCIgc3R5bGU9ImVuYWJsZS1iYWNrZ3JvdW5kOm5ldyAwIDAgMjQgMjQ7IiB4bWw6c3BhY2U9InByZXNlcnZlIj4KPHN0eWxlIHR5cGU9InRleHQvY3NzIj4KCS5zdDB7ZmlsbDojOEJDRjcwO30KPC9zdHlsZT4wCiAgICA8cGF0aCBjbGFzcz0ic3QwIiBkPSJNMjAuNyw4LjFjLTEuNS0xLjUtNC40LTQuNC00LjQtNC40Yy0xLjItMS4yLTIuNS0xLjQtMy40LTAuN2MtMC41LDAuNC0wLjcsMC45LTAuOCwxLjRjLTAuMSwwLjUtMC40LDEtMC44LDEuMwoJbC0wLjEsMC4xYy0yLDEuNS00LjMsMi44LTYuOCwzLjJDMy45LDkuMiwzLjMsOS41LDMsMTBjLTAuNiwwLjktMC40LDIuMSwwLjMsMi45bDMuNCwzLjRjMCwwLDAsMCwwLDBsLTMuMSwzLjEKCWMtMC4zLDAuMy0wLjMsMC44LDAsMS4xYzAuMSwwLjEsMC4zLDAuMiwwLjUsMC4yYzAuMiwwLDAuNC0wLjEsMC41LTAuMmwzLjEtMy4xYzAsMCwwLDAsMCwwbDIuOSwyLjljMS4zLDEuMywyLjUsMS41LDMuNCwwLjkKCWMwLjQtMC4zLDAuNy0wLjcsMC45LTEuMmMwLjYtMi4zLDEuNC00LjYsMi44LTYuNWwwLjUtMC43YzAuMy0wLjQsMC44LTAuOCwxLjMtMC44YzAuNS0wLjEsMS4xLTAuMywxLjQtMC44CglDMjEuNywxMC4yLDIxLjUsOC45LDIwLjcsOC4xeiIvPgo8L3N2Zz4K") no-repeat center center;
background-size: contain;
}
}
.project-h2 {

View File

@ -1,13 +0,0 @@
<?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>

Before

Width:  |  Height:  |  Size: 909 B