添加任务

This commit is contained in:
kuaifan 2021-06-03 00:34:58 +08:00
parent f8fa57ae78
commit 1c856c34f9
19 changed files with 587 additions and 262 deletions

View File

@ -6,9 +6,11 @@ use App\Models\AbstractModel;
use App\Models\Project;
use App\Models\ProjectColumn;
use App\Models\ProjectLog;
use App\Models\ProjectTask;
use App\Models\ProjectUser;
use App\Models\User;
use App\Module\Base;
use Carbon\Carbon;
use Request;
/**
@ -68,7 +70,7 @@ class ProjectController extends AbstractController
//
$project = Project::with(['projectColumn' => function($query) {
$query->with(['projectTask' => function($taskQuery) {
$taskQuery->where('parent_id', 0);
$taskQuery->with(['taskUser', 'taskTag'])->where('parent_id', 0);
}]);
}, 'projectUser'])
->select($this->projectSelect)
@ -155,4 +157,73 @@ class ProjectController extends AbstractController
return Base::retSuccess('添加成功!');
});
}
/**
* {post}【任务】添加任务
*
* @apiParam {Number} project_id 项目ID
* @apiParam {Number} [column_id] 列表ID留空取第一个
* @apiParam {String} name 任务名称
* @apiParam {String} [content] 任务描述
* @apiParam {Array} [times] 计划时间(格式:开始时间,结束时间2020-01-01 00:00,2020-01-01 23:59
* @apiParam {Number} [owner] 负责人,留空为自己
* @apiParam {Array} [subtasks] 子任务(格式:[{name,owner,times}]
*/
public function task__add()
{
$user = User::authE();
if (Base::isError($user)) {
return $user;
} else {
$user = User::IDE($user['data']);
}
$project_id = Base::getPostInt('project_id');
$column_id = Base::getPostValue('column_id');
$name = Base::getPostValue('name');
$content = Base::getPostValue('content');
$times = Base::getPostValue('times');
$owner = Base::getPostValue('owner');
$subtasks = Base::getPostValue('subtasks');
// 项目
$project = Project::select($this->projectSelect)
->join('project_users', 'projects.id', '=', 'project_users.project_id')
->where('projects.id', $project_id)
->where('project_users.userid', $user->userid)
->first();
if (empty($project)) {
return Base::retError('项目不存在或已被删除!');
}
// 列表
if (is_array($column_id)) {
$column_id = Base::arrayFirst($column_id);
}
if (empty($column_id)) {
$column = $project->projectColumn->first();
} elseif (intval($column_id) > 0) {
$column = $project->projectColumn->where('id', $column_id)->first();
} else {
$column = ProjectColumn::whereProjectId($project->id)->whereName($column_id)->first();
if (empty($column)) {
$column = ProjectColumn::createInstance([
'project_id' => $project->id,
'name' => $column_id,
]);
$column->save();
}
}
if (empty($column)) {
return Base::retError('任务列表不存在或已被删除!');
}
//
return ProjectTask::addTask([
'parent_id' => 0,
'project_id' => $project->id,
'column_id' => $column->id,
'name' => $name,
'content' => $content,
'times' => $times,
'owner' => $owner,
'subtasks' => $subtasks,
]);
}
}

View File

@ -12,6 +12,7 @@ class VerifyCsrfToken extends Middleware
* @var array
*/
protected $except = [
//
// 添加任务
'api/project/task/add/',
];
}

View File

@ -45,7 +45,7 @@ class Project extends AbstractModel
*/
public function projectColumn(): \Illuminate\Database\Eloquent\Relations\HasMany
{
return $this->hasMany(projectColumn::class, 'project_id', 'id')->orderByDesc('id');
return $this->hasMany(projectColumn::class, 'project_id', 'id')->orderBy('id');
}
/**
@ -61,6 +61,6 @@ class Project extends AbstractModel
*/
public function projectUser(): \Illuminate\Database\Eloquent\Relations\HasMany
{
return $this->hasMany(projectUser::class, 'project_id', 'id')->orderByDesc('id');
return $this->hasMany(projectUser::class, 'project_id', 'id')->orderBy('id');
}
}

View File

@ -2,6 +2,9 @@
namespace App\Models;
use App\Module\Base;
use Carbon\Carbon;
/**
* Class ProjectTask
*
@ -20,6 +23,13 @@ namespace App\Models;
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @property \Illuminate\Support\Carbon|null $deleted_at
* @property-read int $file_num
* @property-read int $msg_num
* @property-read bool $overdue
* @property-read int $percent
* @property-read bool $today
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\ProjectTaskUser[] $taskUser
* @property-read int|null $task_user_count
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTask query()
@ -42,4 +52,182 @@ namespace App\Models;
class ProjectTask extends AbstractModel
{
protected $appends = [
'file_num',
'msg_num',
'percent',
'today',
'overdue',
];
/**
* 附件数量
* @return int
*/
public function getFileNumAttribute()
{
if (!isset($this->attributes['file_num'])) {
$this->attributes['file_num'] = ProjectTaskFile::whereTaskId($this->id)->count();
}
return $this->attributes['file_num'];
}
/**
* 消息数量
* @return int
*/
public function getMsgNumAttribute()
{
if (!isset($this->attributes['msg_num'])) {
$this->attributes['msg_num'] = ProjectTaskMsg::whereTaskId($this->id)->count();
}
return $this->attributes['msg_num'];
}
/**
* 进度0-100
* @return int
*/
public function getPercentAttribute()
{
$builder = self::whereParentId($this->id);
$subTaskTotal = $builder->count();
if ($subTaskTotal == 0) {
return $this->complete_at ? 1 : 0;
}
$subTaskComplete = $builder->whereNotNull('complete_at')->count();
if ($subTaskComplete == 0) {
return 0;
}
return intval($subTaskComplete / $subTaskTotal * 100);
}
/**
* 是否今日任务
* @return bool
*/
public function getTodayAttribute()
{
if ($this->end_at) {
$end_at = Carbon::parse($this->end_at);
if ($end_at->toDateString() == Carbon::now()->toDateString()) {
return true;
}
}
return false;
}
/**
* 是否过期
* @return bool
*/
public function getOverdueAttribute()
{
if ($this->end_at) {
if (Carbon::parse($this->end_at)->lt(Carbon::now())) {
return true;
}
}
return false;
}
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function taskUser(): \Illuminate\Database\Eloquent\Relations\HasMany
{
return $this->hasMany(projectTaskUser::class, 'task_id', 'id')->orderByDesc('id');
}
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function taskTag(): \Illuminate\Database\Eloquent\Relations\HasMany
{
return $this->hasMany(projectTaskTag::class, 'task_id', 'id')->orderByDesc('id');
}
/**
* 添加任务
* @param $params
* @return array|bool
*/
public static function addTask($params)
{
$parent_id = intval($params['parent_id']);
$project_id = intval($params['project_id']);
$column_id = intval($params['column_id']);
$name = $params['name'];
$content = $params['content'];
$times = $params['times'];
$owner = $params['owner'];
$subtasks = $params['subtasks'];
//
$retPre = $parent_id ? '子任务' : '任务';
$task = self::createInstance();
$task->parent_id = $parent_id;
$task->project_id = $project_id;
$task->column_id = $column_id;
if ($content) {
$task->desc = Base::getHtml($content);
}
// 标题
if (empty($name)) {
return Base::retError($retPre . '名称不能为空!');
} elseif (mb_strlen($name) > 255) {
return Base::retError($retPre . '名称最多只能设置255个字');
}
$task->name = $name;
// 时间
if ($times) {
list($start, $end) = is_string($times) ? explode(",", $times) : (is_array($times) ? $times : []);
if (Base::isDate($start) && Base::isDate($end)) {
if ($start != $end) {
$task->start_at = Carbon::parse($start);
$task->end_at = Carbon::parse($end);
}
}
}
// 负责人
if (is_array($owner)) {
$owner = Base::arrayFirst($owner);
}
$owner = $owner ?: User::token2userid();
if (!ProjectUser::whereProjectId($project_id)->whereUserid($owner)->exists()) {
return Base::retError($retPre . '负责人填写错误!');
}
// 创建人
$task->userid = User::token2userid();
//
return AbstractModel::transaction(function() use ($subtasks, $content, $owner, $task) {
$task->save();
if ($owner) {
ProjectTaskUser::createInstance([
'project_id' => $task->parent_id,
'task_id' => $task->id,
'userid' => $owner,
'owner' => 1,
])->save();
}
if ($content) {
ProjectTaskContent::createInstance([
'project_id' => $task->parent_id,
'task_id' => $task->id,
'content' => $content,
])->save();
}
if ($task->parent_id == 0 && $subtasks && is_array($subtasks)) {
foreach ($subtasks as $subtask) {
$subtask['parent_id'] = $task->id;
$subtask['project_id'] = $task->project_id;
$subtask['column_id'] = $task->column_id;
$res = self::addTask($subtask);
if (Base::isError($res)) {
return $res;
}
}
}
return Base::retSuccess('添加成功');
});
}
}

View File

@ -25,5 +25,8 @@ namespace App\Models;
*/
class ProjectTaskContent extends AbstractModel
{
protected $hidden = [
'created_at',
'updated_at',
];
}

View File

@ -0,0 +1,41 @@
<?php
namespace App\Models;
/**
* Class ProjectTaskFile
*
* @package App\Models
* @property int $id
* @property int|null $project_id 项目ID
* @property int|null $task_id 任务ID
* @property string|null $name 文件名称
* @property int|null $size 文件大小(B)
* @property string|null $ext 文件格式
* @property string|null $path 文件地址
* @property string|null $thumb 缩略图
* @property int|null $userid 上传用户ID
* @property int|null $download 下载次数
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskFile newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskFile newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskFile query()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskFile whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskFile whereDownload($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskFile whereExt($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskFile whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskFile whereName($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskFile wherePath($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskFile whereProjectId($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskFile whereSize($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskFile whereTaskId($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskFile whereThumb($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskFile whereUpdatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskFile whereUserid($value)
* @mixin \Eloquent
*/
class ProjectTaskFile extends AbstractModel
{
}

View File

@ -0,0 +1,31 @@
<?php
namespace App\Models;
/**
* Class ProjectTaskMsg
*
* @package App\Models
* @property int $id
* @property int|null $project_id 项目ID
* @property int|null $task_id 任务ID
* @property string|null $msg 详细内容(JSON)
* @property int|null $userid 发送用户ID
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskMsg newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskMsg newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskMsg query()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskMsg whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskMsg whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskMsg whereMsg($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskMsg whereProjectId($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskMsg whereTaskId($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskMsg whereUpdatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskMsg whereUserid($value)
* @mixin \Eloquent
*/
class ProjectTaskMsg extends AbstractModel
{
}

View File

@ -0,0 +1,34 @@
<?php
namespace App\Models;
/**
* Class ProjectTaskTag
*
* @package App\Models
* @property int $id
* @property int|null $project_id 项目ID
* @property int|null $task_id 任务ID
* @property string|null $name 标题
* @property string|null $color 颜色
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskTag newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskTag newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskTag query()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskTag whereColor($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskTag whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskTag whereId($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskTag whereName($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskTag whereProjectId($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskTag whereTaskId($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectTaskTag whereUpdatedAt($value)
* @mixin \Eloquent
*/
class ProjectTaskTag extends AbstractModel
{
protected $hidden = [
'created_at',
'updated_at',
];
}

View File

@ -1921,7 +1921,12 @@ class Base
public static function getPostValue($key, $default = null)
{
return self::getContentValue($key, $default);
return self::newTrim(self::getContentValue($key, $default));
}
public static function getPostInt($key, $default = null)
{
return intval(self::getContentValue($key, $default));
}
/**

View File

@ -1,5 +1,5 @@
<template>
<div v-if="ready" class="common-user">
<div v-if="ready" :class="['common-user', maxHiddenClass]">
<Select
v-model="values"
:transfer="transfer"
@ -40,13 +40,15 @@
},
transfer: {
type: Boolean,
default () {
return true;
}
default: true
},
multipleMax: {
type: Number,
}
},
maxHiddenInput: {
type: Boolean,
default: true
},
},
data() {
return {
@ -67,6 +69,17 @@
this.ready = true;
});
},
computed: {
maxHiddenClass() {
const {multipleMax, maxHiddenInput, values} = this;
if (multipleMax && maxHiddenInput) {
if (values.length >= multipleMax) {
return 'hidden-input'
}
}
return '';
}
},
watch: {
value(val) {
this.values = val;

View File

@ -13,7 +13,7 @@
<li>
<UserAvatar :userid="projectDetail.owner_userid" :size="36"/>
</li>
<li class="project-icon" @click="addShow=true">
<li class="project-icon" @click="addOpen(0)">
<Icon type="md-add" />
</li>
<li class="project-icon">
@ -24,241 +24,57 @@
</div>
</Tooltip>
</li>
<li class="project-icon" :class="{'active':$store.state.projectChatShow}" @click="$store.commit('toggleProjectChatShow')">
<li :class="['project-icon', $store.state.projectChatShow ? 'active' : '']" @click="$store.commit('toggleProjectChatShow')">
<Icon type="ios-chatbubbles" />
<Badge :count="999"></Badge>
<Badge :count="projectMsgUnread"></Badge>
</li>
<li class="project-icon">
<Icon type="ios-more" />
</li>
</ul>
<div class="project-switch">
<div class="project-switch-button" @click="$store.commit('toggleProjectListPanel')">
<template v-if="$store.state.projectListPanel">
<div class="project-switch-img active"><img src="../../../../statics/images/project-panel-blue.svg"></div>
<div class="project-switch-img"><img src="../../../../statics/images/project-menu-gray.svg"></div>
</template>
<template v-else>
<div class="project-switch-img"><img src="../../../../statics/images/project-panel-gray.svg"></div>
<div class="project-switch-img active"><img src="../../../../statics/images/project-menu-blue.svg"></div>
</template>
<div :class="['project-switch-button', $store.state.projectListPanel ? 'menu' : '']" @click="$store.commit('toggleProjectListPanel')">
<div><i class="iconfont">&#xe60c;</i></div>
<div><i class="iconfont">&#xe66a;</i></div>
</div>
</div>
</div>
</div>
<div v-if="$store.state.projectListPanel" class="project-column">
<ul>
<li>
<li v-for="column in projectDetail.project_column">
<div class="column-head">
<div class="column-head-title">Next Up</div>
<div class="column-head-num">3</div>
<div class="column-head-title">{{column.name}}</div>
<div :class="['column-head-num', column.project_task.length > 0 ? 'have' : '']">{{column.project_task.length}}</div>
</div>
<ul>
<li>
<li v-for="item in column.project_task">
<div class="task-head">
<div class="task-title">Maxxis Tyres</div>
<div class="task-title">{{item.name}}</div>
<Icon type="ios-more" />
</div>
<div class="task-desc">These project will need a new brand Identity where they will get recognise.</div>
<div class="task-tags">
<Tag color="red">red</Tag>
<Tag color="volcano">volcano</Tag>
<div v-if="item.desc" class="task-desc">{{item.desc}}</div>
<div v-if="item.task_tag.length > 0" class="task-tags">
<Tag v-for="(tag, keyt) in item.task_tag" :key="keyt" :color="tag.color">{{tag.name}}</Tag>
</div>
<div class="task-member">
<div class="task-users">
<ul>
<li class="online">
<Avatar src="https://i.loli.net/2017/08/21/599a521472424.jpg" />
</li>
<li class="online">
<Avatar src="https://i.loli.net/2017/08/21/599a521472424.jpg" />
</li>
<li>
<Avatar src="https://i.loli.net/2017/08/21/599a521472424.jpg" />
<li v-for="(user, keyu) in item.task_user" :key="keyu">
<UserAvatar :userid="user.userid" size="28"/>
</li>
</ul>
<div class="task-icon">5<Icon type="ios-link-outline" /></div>
<div class="task-icon">5<Icon type="ios-chatbubbles-outline" /></div>
<div v-if="item.file_num > 0" class="task-icon">{{item.file_num}}<Icon type="ios-link-outline" /></div>
<div v-if="item.msg_num > 0" class="task-icon">{{item.msg_num}}<Icon type="ios-chatbubbles-outline" /></div>
</div>
<div class="task-progress">
<Progress :percent="25" :stroke-width="6" />
<div class="task-time today"><Icon type="ios-time-outline" />Mar 30</div>
</div>
</li>
<li>
<div class="task-head">
<div class="task-title">Maxxis Tyres</div>
<Icon type="ios-more" />
</div>
<div class="task-desc">These project will need a new brand Identity where they will get recognise.</div>
<div class="task-member">
<ul>
<li class="online">
<Avatar src="https://i.loli.net/2017/08/21/599a521472424.jpg" />
</li>
<li class="online">
<Avatar src="https://i.loli.net/2017/08/21/599a521472424.jpg" />
</li>
<li>
<Avatar src="https://i.loli.net/2017/08/21/599a521472424.jpg" />
</li>
</ul>
<div class="task-icon">5<Icon type="ios-link-outline" /></div>
<div class="task-icon">5<Icon type="ios-chatbubbles-outline" /></div>
</div>
<div class="task-progress">
<Progress :percent="25" :stroke-width="6" />
<div class="task-time"><Icon type="ios-time-outline" />Mar 30</div>
<Progress :percent="item.percent" :stroke-width="6" />
<div v-if="item.end_at" :class="['task-time', item.today ? 'today' : '', item.overdue ? 'overdue' : '']">
<Icon type="ios-time-outline" />{{item.end_at}}
</div>
</div>
</li>
</ul>
<div class="column-add">
<Icon type="md-add" />
</div>
</li>
<li>
<div class="column-head">
<div class="column-head-title">Hi Progress</div>
<div class="column-head-num">3</div>
</div>
<ul>
<li>
<div class="task-head">
<div class="task-title">Maxxis Tyres</div>
<Icon type="ios-more" />
</div>
<div class="task-desc">These project will need a new brand Identity where they will get recognise.</div>
<div class="task-tags">
<Tag color="magenta">magenta</Tag>
<Tag color="red">red</Tag>
<Tag color="volcano">volcano</Tag>
</div>
<div class="task-member">
<ul>
<li class="online">
<Avatar src="https://i.loli.net/2017/08/21/599a521472424.jpg" />
</li>
<li class="online">
<Avatar src="https://i.loli.net/2017/08/21/599a521472424.jpg" />
</li>
<li>
<Avatar src="https://i.loli.net/2017/08/21/599a521472424.jpg" />
</li>
</ul>
<div class="task-icon">5<Icon type="ios-link-outline" /></div>
<div class="task-icon">5<Icon type="ios-chatbubbles-outline" /></div>
</div>
<div class="task-progress">
<Progress :percent="25" :stroke-width="6" />
<div class="task-time"><Icon type="ios-time-outline" />Mar 30</div>
</div>
</li>
<li>
<div class="task-head">
<div class="task-title">Maxxis Tyres</div>
<Icon type="ios-more" />
</div>
<div class="task-desc">These project will need a new brand Identity where they will get recognise.</div>
<div class="task-tags">
<Tag color="magenta">magenta</Tag>
<Tag color="red">red</Tag>
<Tag color="volcano">volcano</Tag>
</div>
<div class="task-member">
<ul>
<li class="online">
<Avatar src="https://i.loli.net/2017/08/21/599a521472424.jpg" />
</li>
<li class="online">
<Avatar src="https://i.loli.net/2017/08/21/599a521472424.jpg" />
</li>
<li>
<Avatar src="https://i.loli.net/2017/08/21/599a521472424.jpg" />
</li>
</ul>
<div class="task-icon">5<Icon type="ios-link-outline" /></div>
<div class="task-icon">5<Icon type="ios-chatbubbles-outline" /></div>
</div>
<div class="task-progress">
<Progress :percent="25" :stroke-width="6" />
<div class="task-time tomorrow"><Icon type="ios-time-outline" />Mar 30</div>
</div>
</li>
</ul>
<div class="column-add">
<Icon type="md-add" />
</div>
</li>
<li>
<div class="column-head">
<div class="column-head-title">Complete</div>
<div class="column-head-num">3</div>
</div>
<ul>
<li>
<div class="task-head">
<div class="task-title">Maxxis Tyres</div>
<Icon type="ios-more" />
</div>
<div class="task-desc">These project will need a new brand Identity where they will get recognise.</div>
<div class="task-member">
<ul>
<li class="online">
<Avatar src="https://i.loli.net/2017/08/21/599a521472424.jpg" />
</li>
<li class="online">
<Avatar src="https://i.loli.net/2017/08/21/599a521472424.jpg" />
</li>
<li>
<Avatar src="https://i.loli.net/2017/08/21/599a521472424.jpg" />
</li>
</ul>
<div class="task-icon">5<Icon type="ios-link-outline" /></div>
<div class="task-icon">5<Icon type="ios-chatbubbles-outline" /></div>
</div>
<div class="task-progress">
<Progress :percent="25" :stroke-width="6" />
<div class="task-time"><Icon type="ios-time-outline" />Mar 30</div>
</div>
</li>
<li>
<div class="task-head">
<div class="task-title">Maxxis Tyres</div>
<Icon type="ios-more" />
</div>
<div class="task-desc">These project will need a new brand Identity where they will get recognise.</div>
<div class="task-tags">
<Tag color="primary">primary</Tag>
<Tag color="success">success</Tag>
<Tag color="error">error</Tag>
<Tag color="warning">warning</Tag>
<Tag color="magenta">magenta</Tag>
<Tag color="red">red</Tag>
<Tag color="volcano">volcano</Tag>
</div>
<div class="task-member">
<ul>
<li class="online">
<Avatar src="https://i.loli.net/2017/08/21/599a521472424.jpg" />
</li>
<li class="online">
<Avatar src="https://i.loli.net/2017/08/21/599a521472424.jpg" />
</li>
<li>
<Avatar src="https://i.loli.net/2017/08/21/599a521472424.jpg" />
</li>
</ul>
<div class="task-icon">5<Icon type="ios-link-outline" /></div>
<div class="task-icon">5<Icon type="ios-chatbubbles-outline" /></div>
</div>
<div class="task-progress">
<Progress :percent="25" :stroke-width="6" />
<div class="task-time"><Icon type="ios-time-outline" />Mar 30</div>
</div>
</li>
</ul>
<div class="column-add">
<Icon type="md-add" />
</div>
<div class="column-add" @click="addOpen(column.id)"><Icon type="md-add" /></div>
</li>
</ul>
</div>
@ -602,7 +418,7 @@
</div>
</div>
<!--新建项目-->
<!--添加任务-->
<Modal
v-model="addShow"
:title="$L('添加任务')"
@ -615,7 +431,7 @@
<TaskAdd v-model="addData"/>
<div slot="footer">
<Button type="default" @click="addShow=false">{{$L('取消')}}</Button>
<Button type="primary" :loading="taskLoad > 0" @click="">{{$L('添加')}}</Button>
<Button type="primary" :loading="taskLoad > 0" @click="onAddTask">{{$L('添加')}}</Button>
</div>
</Modal>
</div>
@ -713,20 +529,47 @@
align-items: center;
background-color: #ffffff;
border-radius: 6px;
.project-switch-img {
position: relative;
&:before {
content: "";
position: absolute;
top: 0;
left: 0;
width: 50%;
height: 100%;
z-index: 0;
color: #2d8cf0;
border-radius: 6px;
border: 1px solid #2d8cf0;
background-color: #e6f7ff;
transition: left 0.2s;
}
> div {
z-index: 1;
width: 32px;
height: 30px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 6px;
> img {
width: 16px;
height: 16px;
cursor: pointer;
color: #515a6e;
> i {
font-size: 17px;
}
&.active {
border: 1px solid #2d8cf0;
background-color: #e6f7ff;
&:first-child {
color: #2d8cf0;
}
}
&.menu {
&:before {
left: 50%;
}
> div:first-child {
color: #515a6e;
}
> div:last-child {
color: #2d8cf0;
}
}
}
@ -772,7 +615,10 @@
line-height: 16px;
border-radius: 3px;
color: #ffffff;
background-color: #1C1D1E;
background-color: #cccccc;
&.have {
background-color: #1C1D1E;
}
}
}
> ul {
@ -815,7 +661,7 @@
}
}
.task-member {
.task-users {
margin-top: 10px;
display: flex;
align-items: center;
@ -832,9 +678,7 @@
&:first-child {
margin-left: 0;
}
.ivu-avatar {
width: 28px;
height: 28px;
.common-avatar {
border: 2px solid #ffffff;
}
}
@ -861,11 +705,12 @@
border-radius: 3px;
display: flex;
align-items: center;
&.today {
&.overdue {
font-weight: 600;
color: #ffffff;
background-color: #ed4014;
}
&.tomorrow {
&.today {
color: #ffffff;
background-color: #ff9900;
}
@ -878,14 +723,24 @@
}
}
.column-add {
cursor: pointer;
border-radius: 6px;
border: 2px dashed #F1f1f1;
padding: 5px 12px;
text-align: center;
margin: 0 10px 18px;
transition: all 0.2s;
.ivu-icon {
color: #cccccc;
font-size: 22px;
transition: all 0.2s;
}
&:hover {
border-color: #e1e1e1;
.ivu-icon {
color: #aaaaaa;
transform: scale(1.1);
}
}
}
}
@ -1018,19 +873,48 @@ export default {
addShow: false,
addData: {
type: 'task',
owner: 1,
column_id: 5,
owner: 0,
column_id: 0,
times: [],
subtasks: [],
},
taskLoad: false,
taskLoad: 0,
}
},
mounted() {
},
computed: {
...mapState(['projectDetail', 'projectLoad']),
...mapState(['userId', 'projectDetail', 'projectLoad', 'projectMsgUnread']),
},
methods: {
addOpen(column_id) {
this.$set(this.addData, 'owner', this.userId);
this.$set(this.addData, 'column_id', column_id);
this.$set(this.addData, 'project_id', this.projectDetail.id);
this.addShow = true;
},
onAddTask() {
this.taskLoad++;
$A.apiAjax({
url: 'project/task/add',
data: this.addData,
method: 'post',
complete: () => {
this.taskLoad--;
},
success: ({ret, data, msg}) => {
if (ret === 1) {
$A.messageSuccess(msg);
this.$store.commit('getProjectDetail', this.addData.project_id);
this.addShow = false;
} else {
$A.modalError(msg);
}
}
});
}
}
}
</script>

View File

@ -33,13 +33,38 @@
:placeholder="$L('选择计划范围')"
format="yyyy-MM-dd HH:mm"
type="datetimerange"
@on-change="taskTimeChange"/>
@on-change="taskTimeChange(value.times)"/>
</FormItem>
<FormItem :label="$L('项目负责人')">
<UserInput v-model="value.owner" :multiple-max="1" :placeholder="$L('选择项目负责人')"/>
<FormItem :label="$L('任务负责人')">
<UserInput v-model="value.owner" :multiple-max="1" :placeholder="$L('选择任务负责人')"/>
</FormItem>
<div class="subtasks">
<Input type="text" :placeholder="$L('+ 输入子任务,回车添加子任务')"></Input>
<div v-if="value.subtasks.length > 0" class="sublist">
<Row>
<Col span="12">{{$L('任务名称')}}</Col>
<Col span="6">{{$L('计划时间')}}</Col>
<Col span="6">{{$L('负责人')}}</Col>
</Row>
<Row v-for="(item, key) in value.subtasks" :key="key">
<Col span="12">
<Input v-model="item.name" @on-clear="value.subtasks.splice(key, 1)" clearable/>
</Col>
<Col span="6">
<DatePicker
v-model="item.times"
:options="timeOptions"
:editable="false"
:placeholder="$L('选择时间')"
format="yyyy-MM-dd HH:mm"
type="datetimerange"
@on-change="taskTimeChange(item.times)"/>
</Col>
<Col span="6">
<UserInput v-model="item.owner" :multiple-max="1" :placeholder="$L('选择负责人')"/>
</Col>
</Row>
</div>
<Input type="text" v-model="subName" class="enter-input" @on-enter="addSubTask" :placeholder="$L('+ 输入子任务,回车添加子任务')"></Input>
</div>
</Form>
</div>
@ -65,6 +90,7 @@ export default {
return {
advanced: false,
columns: [],
subName: '',
taskPlugins: [
'advlist autolink lists link image charmap print preview hr anchor pagebreak imagetools',
@ -101,11 +127,11 @@ export default {
},
computed: {
...mapState(['projectDetail']),
...mapState(['userId', 'projectDetail']),
},
watch: {
projectDetail(detail) {
this.columns = detail.project_column;
this.columns = $A.cloneJSON(detail.project_column);
}
},
methods: {
@ -168,14 +194,24 @@ export default {
});
}
},
taskTimeChange() {
let tempc = $A.date2string(this.value.times, "Y-m-d H:i");
taskTimeChange(times) {
let tempc = $A.date2string(times, "Y-m-d H:i");
if (tempc[0] && tempc[1]) {
if ($A.rightExists(tempc[0], '00:00') && $A.rightExists(tempc[1], '00:00')) {
this.$set(this.value.times, 1, tempc[1].replace("00:00", "23:59"));
this.$set(times, 1, tempc[1].replace("00:00", "23:59"));
}
}
},
addSubTask() {
if (this.subName.trim() !== '') {
this.value.subtasks.push({
name: this.subName.trim(),
times: [],
owner: this.userId
});
this.subName = '';
}
}
}
}
</script>

View File

@ -179,6 +179,7 @@ export default Object.assign(stateCommon, {
project_column: [],
project_user: []
},
projectMsgUnread: 0,
cacheProject: {},
cacheUserBasic: {},

View File

@ -1,8 +1,8 @@
@font-face {
font-family: 'iconfont'; /* Project id 2583385 */
src: url('//at.alicdn.com/t/font_2583385_eusvc29tdm5.woff2?t=1622518656789') format('woff2'),
url('//at.alicdn.com/t/font_2583385_eusvc29tdm5.woff?t=1622518656789') format('woff'),
url('//at.alicdn.com/t/font_2583385_eusvc29tdm5.ttf?t=1622518656789') format('truetype');
src: url('//at.alicdn.com/t/font_2583385_jz78bezyc7o.woff2?t=1622640071039') format('woff2'),
url('//at.alicdn.com/t/font_2583385_jz78bezyc7o.woff?t=1622640071039') format('woff'),
url('//at.alicdn.com/t/font_2583385_jz78bezyc7o.ttf?t=1622640071039') format('truetype');
}
.iconfont {

View File

@ -371,12 +371,25 @@
padding: 12px 16px;
border-radius: 6px;
background-color: #f8f8f8;
.ivu-input {
background: transparent;
border-color: transparent;
&:hover,
&:focus {
box-shadow: none;
.enter-input {
.ivu-input {
background: transparent;
border-color: transparent;
&:hover,
&:focus {
box-shadow: none;
}
}
}
.sublist {
.ivu-row {
margin-bottom: 12px;
> div {
padding-right: 7px;
&:last-child {
padding-right: 0;
}
}
}
}
}
@ -411,6 +424,14 @@
height: 14px;
}
}
&.hidden-input {
.ivu-select-selection {
padding: 0 4px;
.ivu-select-input {
display: none;
}
}
}
}
.common-avatar {

View File

@ -1 +0,0 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1622448571867" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7934" width="128" height="128" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><style type="text/css"></style></defs><path d="M936.19430083 441.30094987c20.7383885 0 37.70616053 7.06990501 50.90331611 21.20971504S1006.89335097 493.14692043 1006.89335097 512s-7.06990501 35.34952507-21.20971503 49.48933509-30.63625552 21.20971504-49.48933511 21.20971504H87.80569917c-18.85307957 0-35.34952507-7.06990501-49.48933511-21.20971504S17.10664903 530.85307957 17.10664903 512s6.59857847-35.34952507 19.79573403-49.48933509S67.06731066 441.30094987 87.80569917 441.30094987h848.38860166zM87.80569917 299.90284958c-18.85307957 0-35.34952507-7.06990501-49.48933511-21.20971504S17.10664903 248.05687901 17.10664903 229.20379943s6.59857847-35.34952507 19.79573403-49.4893351S67.06731066 158.5047493 87.80569917 158.5047493h848.38860166c20.7383885 0 37.70616053 7.06990501 50.90331611 21.20971503S1006.89335097 210.35071986 1006.89335097 229.20379943s-7.06990501 35.34952507-21.20971503 49.48933511-30.63625552 21.20971504-49.48933511 21.20971504H87.80569917z m848.38860166 424.19430084c20.7383885 0 37.70616053 7.06990501 50.90331611 21.20971504S1006.89335097 775.94312099 1006.89335097 794.79620057s-7.06990501 35.34952507-21.20971503 49.4893351-30.63625552 21.20971504-49.48933511 21.20971503H87.80569917c-18.85307957 0-35.34952507-7.06990501-49.48933511-21.20971503S17.10664903 813.64928014 17.10664903 794.79620057s6.59857847-35.34952507 19.79573403-49.48933511S67.06731066 724.09715042 87.80569917 724.09715042h848.38860166z" fill="#2d8cf0" p-id="7935"></path></svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -1 +0,0 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1622448571867" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="7934" width="128" height="128" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><style type="text/css"></style></defs><path d="M936.19430083 441.30094987c20.7383885 0 37.70616053 7.06990501 50.90331611 21.20971504S1006.89335097 493.14692043 1006.89335097 512s-7.06990501 35.34952507-21.20971503 49.48933509-30.63625552 21.20971504-49.48933511 21.20971504H87.80569917c-18.85307957 0-35.34952507-7.06990501-49.48933511-21.20971504S17.10664903 530.85307957 17.10664903 512s6.59857847-35.34952507 19.79573403-49.48933509S67.06731066 441.30094987 87.80569917 441.30094987h848.38860166zM87.80569917 299.90284958c-18.85307957 0-35.34952507-7.06990501-49.48933511-21.20971504S17.10664903 248.05687901 17.10664903 229.20379943s6.59857847-35.34952507 19.79573403-49.4893351S67.06731066 158.5047493 87.80569917 158.5047493h848.38860166c20.7383885 0 37.70616053 7.06990501 50.90331611 21.20971503S1006.89335097 210.35071986 1006.89335097 229.20379943s-7.06990501 35.34952507-21.20971503 49.48933511-30.63625552 21.20971504-49.48933511 21.20971504H87.80569917z m848.38860166 424.19430084c20.7383885 0 37.70616053 7.06990501 50.90331611 21.20971504S1006.89335097 775.94312099 1006.89335097 794.79620057s-7.06990501 35.34952507-21.20971503 49.4893351-30.63625552 21.20971504-49.48933511 21.20971503H87.80569917c-18.85307957 0-35.34952507-7.06990501-49.48933511-21.20971503S17.10664903 813.64928014 17.10664903 794.79620057s6.59857847-35.34952507 19.79573403-49.48933511S67.06731066 724.09715042 87.80569917 724.09715042h848.38860166z" fill="#515a6e" p-id="7935"></path></svg>

Before

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -1 +0,0 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1622448314339" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2852" width="128" height="128" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><style type="text/css"></style></defs><path d="M387.264 473.68h-192c-52.928 0-96-43.072-96-96v-192c0-52.928 43.072-96 96-96h192c52.928 0 96 43.072 96 96v192c0 52.96-43.072 96-96 96z m-192-320c-17.632 0-32 14.368-32 32v192c0 17.632 14.368 32 32 32h192c17.632 0 32-14.368 32-32v-192c0-17.632-14.368-32-32-32h-192z m192 768h-192c-52.928 0-96-43.072-96-96v-192c0-52.928 43.072-96 96-96h192c52.928 0 96 43.072 96 96v192c0 52.96-43.072 96-96 96z m-192-320c-17.632 0-32 14.336-32 32v192c0 17.664 14.368 32 32 32h192c17.632 0 32-14.336 32-32v-192c0-17.664-14.368-32-32-32h-192z m636.864-128h-192c-52.928 0-96-43.072-96-96v-192c0-52.928 43.072-96 96-96h192c52.928 0 96 43.072 96 96v192c0 52.96-43.072 96-96 96z m-192-320c-17.664 0-32 14.368-32 32v192c0 17.632 14.336 32 32 32h192c17.664 0 32-14.368 32-32v-192c0-17.632-14.336-32-32-32h-192z m192 768h-192c-52.928 0-96-43.072-96-96v-192c0-52.928 43.072-96 96-96h192c52.928 0 96 43.072 96 96v192c0 52.96-43.072 96-96 96z m-192-320c-17.664 0-32 14.336-32 32v192c0 17.664 14.336 32 32 32h192c17.664 0 32-14.336 32-32v-192c0-17.664-14.336-32-32-32h-192z" p-id="2853" fill="#2d8cf0"></path></svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -1 +0,0 @@
<?xml version="1.0" standalone="no"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1622448314339" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2852" width="128" height="128" xmlns:xlink="http://www.w3.org/1999/xlink"><defs><style type="text/css"></style></defs><path d="M387.264 473.68h-192c-52.928 0-96-43.072-96-96v-192c0-52.928 43.072-96 96-96h192c52.928 0 96 43.072 96 96v192c0 52.96-43.072 96-96 96z m-192-320c-17.632 0-32 14.368-32 32v192c0 17.632 14.368 32 32 32h192c17.632 0 32-14.368 32-32v-192c0-17.632-14.368-32-32-32h-192z m192 768h-192c-52.928 0-96-43.072-96-96v-192c0-52.928 43.072-96 96-96h192c52.928 0 96 43.072 96 96v192c0 52.96-43.072 96-96 96z m-192-320c-17.632 0-32 14.336-32 32v192c0 17.664 14.368 32 32 32h192c17.632 0 32-14.336 32-32v-192c0-17.664-14.368-32-32-32h-192z m636.864-128h-192c-52.928 0-96-43.072-96-96v-192c0-52.928 43.072-96 96-96h192c52.928 0 96 43.072 96 96v192c0 52.96-43.072 96-96 96z m-192-320c-17.664 0-32 14.368-32 32v192c0 17.632 14.336 32 32 32h192c17.664 0 32-14.368 32-32v-192c0-17.632-14.336-32-32-32h-192z m192 768h-192c-52.928 0-96-43.072-96-96v-192c0-52.928 43.072-96 96-96h192c52.928 0 96 43.072 96 96v192c0 52.96-43.072 96-96 96z m-192-320c-17.664 0-32 14.336-32 32v192c0 17.664 14.336 32 32 32h192c17.664 0 32-14.336 32-32v-192c0-17.664-14.336-32-32-32h-192z" p-id="2853" fill="#515a6e"></path></svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB