no message

This commit is contained in:
kuaifan 2021-06-13 23:10:49 +08:00
parent f4cd7fe1e2
commit 74fa8ff9a1
15 changed files with 835 additions and 252 deletions

View File

@ -247,43 +247,8 @@ class DialogController extends AbstractController
if (Base::isError($data)) { if (Base::isError($data)) {
return Base::retError($data['msg']); return Base::retError($data['msg']);
} else { } else {
$fileData = $data['data']; $msg = $data['data'];
$fileData['thumb'] = $fileData['thumb'] ?: 'images/ext/file.png'; $msg['thumb'] = Base::unFillUrl($msg['thumb']);
switch ($fileData['ext']) {
case "docx":
$fileData['thumb'] = 'images/ext/doc.png';
break;
case "xlsx":
$fileData['thumb'] = 'images/ext/xls.png';
break;
case "pptx":
$fileData['thumb'] = 'images/ext/ppt.png';
break;
case "ai":
case "avi":
case "bmp":
case "cdr":
case "doc":
case "eps":
case "gif":
case "mov":
case "mp3":
case "mp4":
case "pdf":
case "ppt":
case "pr":
case "psd":
case "rar":
case "svg":
case "tif":
case "txt":
case "xls":
case "zip":
$fileData['thumb'] = 'images/ext/' . $fileData['ext'] . '.png';
break;
}
//
$msg = $fileData;
$msg['size'] *= 1024; $msg['size'] *= 1024;
// //
return WebSocketDialogMsg::sendMsg($dialog_id, 'file', $msg, $user->userid, $extra_int, $extra_str); return WebSocketDialogMsg::sendMsg($dialog_id, 'file', $msg, $user->userid, $extra_int, $extra_str);

View File

@ -7,6 +7,7 @@ use App\Models\Project;
use App\Models\ProjectColumn; use App\Models\ProjectColumn;
use App\Models\ProjectLog; use App\Models\ProjectLog;
use App\Models\ProjectTask; use App\Models\ProjectTask;
use App\Models\ProjectTaskFile;
use App\Models\ProjectUser; use App\Models\ProjectUser;
use App\Models\User; use App\Models\User;
use App\Module\Base; use App\Module\Base;
@ -723,7 +724,7 @@ class ProjectController extends AbstractController
* @apiParam {String} name 任务描述 * @apiParam {String} name 任务描述
* @apiParam {String} [content] 任务详情 * @apiParam {String} [content] 任务详情
* @apiParam {Array} [times] 计划时间(格式:开始时间,结束时间2020-01-01 00:00,2020-01-01 23:59 * @apiParam {Array} [times] 计划时间(格式:开始时间,结束时间2020-01-01 00:00,2020-01-01 23:59
* @apiParam {mixed} [owner] 负责人,留空为自己 * @apiParam {Number} [owner] 负责人,留空为自己
* @apiParam {Array} [subtasks] 子任务(格式:[{name,owner,times}] * @apiParam {Array} [subtasks] 子任务(格式:[{name,owner,times}]
* @apiParam {Number} [top] 添加的任务排到列表最前面 * @apiParam {Number} [top] 添加的任务排到列表最前面
*/ */
@ -750,9 +751,6 @@ class ProjectController extends AbstractController
// 列表 // 列表
$column = null; $column = null;
$newColumn = null; $newColumn = null;
if (is_array($column_id)) {
$column_id = Base::arrayFirst($column_id);
}
if ($column_id) { if ($column_id) {
if (intval($column_id) > 0) { if (intval($column_id) > 0) {
$column = $project->projectColumn->find($column_id); $column = $project->projectColumn->find($column_id);
@ -760,6 +758,8 @@ class ProjectController extends AbstractController
if (empty($column)) { if (empty($column)) {
$column = ProjectColumn::whereProjectId($project->id)->whereName($column_id)->first(); $column = ProjectColumn::whereProjectId($project->id)->whereName($column_id)->first();
} }
} else {
$column = ProjectColumn::whereProjectId($project->id)->orderBy('id')->first();
} }
if (empty($column)) { if (empty($column)) {
$column = ProjectColumn::createInstance([ $column = ProjectColumn::createInstance([
@ -798,7 +798,8 @@ class ProjectController extends AbstractController
* @apiParam {String} [color] 任务描述(子任务不支持) * @apiParam {String} [color] 任务描述(子任务不支持)
* @apiParam {String} [content] 任务详情(子任务不支持) * @apiParam {String} [content] 任务详情(子任务不支持)
* @apiParam {Array} [times] 计划时间(格式:开始时间,结束时间2020-01-01 00:00,2020-01-01 23:59 * @apiParam {Array} [times] 计划时间(格式:开始时间,结束时间2020-01-01 00:00,2020-01-01 23:59
* @apiParam {mixed} [owner] 修改负责人 * @apiParam {Number} [owner] 修改负责人
* @apiParam {Array} [assist] 修改协助人员
* *
* @apiParam {String|false} [complete_at] 完成时间2020-01-01 00:00false表示未完成 * @apiParam {String|false} [complete_at] 完成时间2020-01-01 00:00false表示未完成
*/ */
@ -850,6 +851,76 @@ class ProjectController extends AbstractController
return $result; return $result;
} }
/**
* {post} 上传文件
*
* @apiParam {Number} task_id 任务ID
* @apiParam {String} [filename] post-文件名称
* @apiParam {String} [image64] post-base64图片二选一
* @apiParam {File} [files] post-文件对象(二选一)
*/
public function task__upload()
{
$user = User::authE();
if (Base::isError($user)) {
return $user;
} else {
$user = User::IDE($user['data']);
}
//
$task_id = Base::getPostInt('task_id');
// 任务
$task = ProjectTask::whereId($task_id)->first();
if (empty($task)) {
return Base::retError('任务不存在');
}
// 项目
$project = Project::select($this->projectSelect)
->join('project_users', 'projects.id', '=', 'project_users.project_id')
->where('projects.id', $task->project_id)
->where('project_users.userid', $user->userid)
->first();
if (empty($project)) {
return Base::retError('项目不存在或不在成员列表内');
}
//
$path = "uploads/task/" . $task->id . "/";
$image64 = Base::getPostValue('image64');
$fileName = Base::getPostValue('filename');
if ($image64) {
$data = Base::image64save([
"image64" => $image64,
"path" => $path,
"fileName" => $fileName,
]);
} else {
$data = Base::upload([
"file" => Request::file('files'),
"type" => 'file',
"path" => $path,
"fileName" => $fileName,
]);
}
//
if (Base::isError($data)) {
return Base::retError($data['msg']);
} else {
$fileData = $data['data'];
$file = ProjectTaskFile::createInstance([
'project_id' => $task->project_id,
'task_id' => $task->id,
'name' => $fileData['name'],
'size' => $fileData['size'] * 1024,
'ext' => $fileData['ext'],
'path' => $fileData['path'],
'thumb' => Base::unFillUrl($fileData['thumb']),
'userid' => $user->userid,
]);
$file->save();
return Base::retSuccess("上传成功", $file->find($file->id));
}
}
/** /**
* 归档任务 * 归档任务
* *

View File

@ -27,6 +27,9 @@ class VerifyCsrfToken extends Middleware
// 修改任务 // 修改任务
'api/project/task/update/', 'api/project/task/update/',
// 上传任务问题
'api/project/task/upload/',
// 聊天发文件 // 聊天发文件
'api/dialog/msg/sendfile/', 'api/dialog/msg/sendfile/',
]; ];

View File

@ -299,10 +299,7 @@ class ProjectTask extends AbstractModel
} }
} }
// 负责人 // 负责人
if (is_array($owner)) { $owner = intval($owner) ?: User::token2userid();
$owner = Base::arrayFirst($owner);
}
$owner = $owner ?: User::token2userid();
if (!ProjectUser::whereProjectId($project_id)->whereUserid($owner)->exists()) { if (!ProjectUser::whereProjectId($project_id)->whereUserid($owner)->exists()) {
return Base::retError($retPre . '负责人填写错误'); return Base::retError($retPre . '负责人填写错误');
} }
@ -360,9 +357,6 @@ class ProjectTask extends AbstractModel
public function updateTask($data) public function updateTask($data)
{ {
return AbstractModel::transaction(function () use ($data) { return AbstractModel::transaction(function () use ($data) {
$content = $data['content'];
$times = $data['times'];
$owner = $data['owner'];
// 标题 // 标题
if (Arr::exists($data, 'name')) { if (Arr::exists($data, 'name')) {
if (empty($data['name'])) { if (empty($data['name'])) {
@ -373,25 +367,48 @@ class ProjectTask extends AbstractModel
$this->name = $data['name']; $this->name = $data['name'];
} }
// 负责人 // 负责人
if ($owner) { if (Arr::exists($data, 'owner')) {
if (is_array($owner)) { $row = ProjectTaskUser::whereTaskId($this->id)->whereOwner(1)->first();
$owner = Base::arrayFirst($owner); if ($row->userid != $data['owner']) {
} if (!User::find(intval($data['owner']))) {
$ownerUser = ProjectTaskUser::whereTaskId($this->id)->whereOwner(1)->first(); return Base::retError('请选择正确的负责人');
if ($ownerUser->userid != $owner) { }
$ownerUser->owner = 0; $row->owner = 0;
$ownerUser->save(); $row->save();
ProjectTaskUser::updateInsert([ ProjectTaskUser::updateInsert([
'project_id' => $this->parent_id, 'project_id' => $this->parent_id,
'task_id' => $this->id, 'task_id' => $this->id,
'userid' => $owner, 'userid' => $data['owner'],
], [ ], [
'owner' => 1, 'owner' => 1,
]); ]);
} }
} }
// 协助人员
if (Arr::exists($data, 'assist')) {
$array = [];
$assist = is_array($data['assist']) ? $data['assist'] : [$data['assist']];
foreach ($assist as $uid) {
if (intval($uid) == 0) continue;
if (ProjectTaskUser::whereTaskId($this->id)->whereUserid($uid)->whereOwner(1)->exists()) continue;
//
if (!ProjectTaskUser::whereTaskId($this->id)->whereUserid($uid)->where('owner', '!=', 1)->exists()) {
ProjectTaskUser::createInstance([
'project_id' => $this->parent_id,
'task_id' => $this->id,
'userid' => $uid,
'owner' => 0,
])->save();
}
$array[] = $uid;
}
ProjectTaskUser::whereTaskId($this->id)->where('owner', '!=', 1)->whereNotIn('userid', $array)->delete();
}
// 计划时间 // 计划时间
if ($times) { if (Arr::exists($data, 'times')) {
$this->start_at = null;
$this->end_at = null;
$times = $data['times'];
list($start, $end) = is_string($times) ? explode(",", $times) : (is_array($times) ? $times : []); list($start, $end) = is_string($times) ? explode(",", $times) : (is_array($times) ? $times : []);
if (Base::isDate($start) && Base::isDate($end)) { if (Base::isDate($start) && Base::isDate($end)) {
if ($start != $end) { if ($start != $end) {
@ -407,14 +424,14 @@ class ProjectTask extends AbstractModel
$this->color = $data['color']; $this->color = $data['color'];
} }
// 内容 // 内容
if ($content && $this->parent_id === 0) { if (Arr::exists($data, 'content')) {
ProjectTaskContent::updateInsert([ ProjectTaskContent::updateInsert([
'project_id' => $this->parent_id, 'project_id' => $this->parent_id,
'task_id' => $this->id, 'task_id' => $this->id,
], [ ], [
'content' => $content, 'content' => $data['content'],
]); ]);
$this->desc = Base::getHtml($content); $this->desc = Base::getHtml($data['content']);
} }
// 优先级 // 优先级
if (Arr::exists($data, 'p_level')) { if (Arr::exists($data, 'p_level')) {
@ -428,6 +445,8 @@ class ProjectTask extends AbstractModel
} }
} }
$this->save(); $this->save();
if ($this->start_at instanceof \DateTimeInterface) $this->start_at = $this->start_at->format('Y-m-d H:i:s');
if ($this->end_at instanceof \DateTimeInterface) $this->end_at = $this->end_at->format('Y-m-d H:i:s');
return Base::retSuccess('修改成功'); return Base::retSuccess('修改成功');
}); });
} }

View File

@ -2,6 +2,8 @@
namespace App\Models; namespace App\Models;
use App\Module\Base;
/** /**
* Class ProjectTaskFile * Class ProjectTaskFile
* *
@ -37,5 +39,23 @@ namespace App\Models;
*/ */
class ProjectTaskFile extends AbstractModel class ProjectTaskFile extends AbstractModel
{ {
/**
* 地址
* @param $value
* @return string
*/
public function getPathAttribute($value)
{
return Base::fillUrl($value);
}
/**
* 缩略图
* @param $value
* @return string
*/
public function getThumbAttribute($value)
{
return Base::fillUrl($value ?: Base::extIcon($this->ext));
}
} }

View File

@ -79,8 +79,8 @@ class WebSocketDialogMsg extends AbstractModel
$value = Base::json2array($value); $value = Base::json2array($value);
if ($this->type === 'file') { if ($this->type === 'file') {
$value['type'] = in_array($value['ext'], ['jpg', 'jpeg', 'png', 'gif']) ? 'img' : 'file'; $value['type'] = in_array($value['ext'], ['jpg', 'jpeg', 'png', 'gif']) ? 'img' : 'file';
$value['url'] = Base::fillUrl($value['path']); $value['path'] = Base::fillUrl($value['path']);
$value['thumb'] = Base::fillUrl($value['thumb']); $value['thumb'] = Base::fillUrl($value['thumb'] ?: Base::extIcon($value['ext']));
} }
return $value; return $value;
} }

View File

@ -2486,6 +2486,50 @@ class Base
return true; return true;
} }
/**
* 获取后缀名图标相对地址
* @param $ext
* @return string
*/
public static function extIcon($ext)
{
$value = 'images/ext/file.png';
switch ($ext) {
case "docx":
$value = 'images/ext/doc.png';
break;
case "xlsx":
$value = 'images/ext/xls.png';
break;
case "pptx":
$value = 'images/ext/ppt.png';
break;
case "ai":
case "avi":
case "bmp":
case "cdr":
case "doc":
case "eps":
case "gif":
case "mov":
case "mp3":
case "mp4":
case "pdf":
case "ppt":
case "pr":
case "psd":
case "rar":
case "svg":
case "tif":
case "txt":
case "xls":
case "zip":
$value = 'images/ext/' . $ext . '.png';
break;
}
return $value;
}
/** /**
* 排列组合(无重复) * 排列组合(无重复)
* @param $arr * @param $arr

View File

@ -110,7 +110,7 @@
maxWidth: projectOpenTask._dialog || projectOpenTask._msgText ? '1200px' : '640px' maxWidth: projectOpenTask._dialog || projectOpenTask._msgText ? '1200px' : '640px'
}" }"
footer-hide> footer-hide>
<TaskDetail/> <TaskDetail :open-task="projectOpenTask"/>
</Modal> </Modal>
</div> </div>
</template> </template>

View File

@ -7,7 +7,7 @@
<div v-else-if="msgData.type === 'loading'" class="dialog-content loading"><Loading/></div> <div v-else-if="msgData.type === 'loading'" class="dialog-content loading"><Loading/></div>
<!--文件--> <!--文件-->
<div v-else-if="msgData.type === 'file'" :class="['dialog-content', msgData.msg.type]"> <div v-else-if="msgData.type === 'file'" :class="['dialog-content', msgData.msg.type]">
<a :href="msgData.msg.url" target="_blank"> <a :href="msgData.msg.path" target="_blank">
<img v-if="msgData.msg.type === 'img'" class="file-img" :style="imageStyle(msgData.msg)" :src="msgData.msg.thumb"/> <img v-if="msgData.msg.type === 'img'" class="file-img" :style="imageStyle(msgData.msg)" :src="msgData.msg.thumb"/>
<div v-else class="file-box"> <div v-else class="file-box">
<img class="file-thumb" :src="msgData.msg.thumb"/> <img class="file-thumb" :src="msgData.msg.thumb"/>

View File

@ -107,7 +107,6 @@
:add-top="true" :add-top="true"
@on-close="column.addTopShow=false" @on-close="column.addTopShow=false"
@on-priority="addTaskOpen" @on-priority="addTaskOpen"
@on-success="addTaskSuccess"
auto-active/> auto-active/>
</div> </div>
<Draggable <Draggable
@ -195,8 +194,7 @@
<TaskAddSimple <TaskAddSimple
:column-id="column.id" :column-id="column.id"
:project-id="projectDetail.id" :project-id="projectDetail.id"
@on-priority="addTaskOpen" @on-priority="addTaskOpen"/>
@on-success="addTaskSuccess"/>
</div> </div>
</Draggable> </Draggable>
</div> </div>
@ -589,13 +587,8 @@ export default {
onAddTask() { onAddTask() {
this.taskLoad++; this.taskLoad++;
this.$store.dispatch("call", { this.$store.dispatch("taskAdd", this.addData).then(({msg}) => {
url: 'project/task/add',
data: this.addData,
method: 'post',
}).then(({data, msg}) => {
this.taskLoad--; this.taskLoad--;
$A.messageSuccess(msg);
this.addShow = false; this.addShow = false;
this.addData = { this.addData = {
owner: 0, owner: 0,
@ -606,8 +599,7 @@ export default {
p_name: '', p_name: '',
p_color: '', p_color: '',
}; };
this.$store.dispatch('projectOne', data.project_id); $A.messageSuccess(msg);
this.addTaskSuccess(data)
}).catch(({msg}) => { }).catch(({msg}) => {
this.taskLoad--; this.taskLoad--;
$A.modalError(msg); $A.modalError(msg);
@ -630,21 +622,6 @@ export default {
this.addShow = true; this.addShow = true;
}, },
addTaskSuccess(data) {
const {task, in_top, new_column} = data;
if (new_column) {
this.projectDetail.project_column.push(new_column)
}
const column = this.projectDetail.project_column.find(({id}) => id === task.column_id);
if (column) {
if (in_top) {
column.project_task.unshift(task);
} else {
column.project_task.push(task);
}
}
},
addColumnOpen() { addColumnOpen() {
this.addColumnShow = true; this.addColumnShow = true;
this.$nextTick(() => { this.$nextTick(() => {

View File

@ -151,13 +151,8 @@ export default {
return; return;
} }
this.loadIng++; this.loadIng++;
this.$store.dispatch("call", { this.$store.dispatch("taskAdd", this.getData()).then(({msg}) => {
url: 'project/task/add',
data: this.getData(),
method: 'post',
}).then(({data, msg}) => {
this.loadIng--; this.loadIng--;
$A.messageSuccess(msg);
this.active = false; this.active = false;
this.addData = { this.addData = {
owner: 0, owner: 0,
@ -168,8 +163,7 @@ export default {
p_name: '', p_name: '',
p_color: '', p_color: '',
} }
this.$store.dispatch('projectOne', data.project_id); $A.messageSuccess(msg);
this.$emit("on-success", data)
}).catch(({msg}) => { }).catch(({msg}) => {
this.loadIng--; this.loadIng--;
$A.modalError(msg); $A.modalError(msg);

View File

@ -1,5 +1,90 @@
<template> <template>
<div :class="{'task-detail':true, 'open-dialog': taskDetail._dialog || taskDetail._msgText, 'completed': taskDetail.complete_at}"> <!--子任务-->
<li v-if="taskDetail.parent_id > 0">
<div class="subtask-icon">
<div v-if="taskDetail.loading === true" class="loading"><Loading /></div>
<EDropdown
v-else
trigger="click"
placement="bottom"
size="small"
@command="dropTask">
<div>
<Icon v-if="taskDetail.complete_at" class="completed" type="md-checkmark-circle" />
<Icon v-else type="md-radio-button-off" />
</div>
<EDropdownMenu slot="dropdown" class="project-list-more-dropdown-menu">
<EDropdownItem v-if="taskDetail.complete_at" command="uncomplete">
<div class="item red">
<Icon type="md-checkmark-circle-outline" />{{$L('标记未完成')}}
</div>
</EDropdownItem>
<EDropdownItem v-else command="complete">
<div class="item">
<Icon type="md-radio-button-off" />{{$L('完成')}}
</div>
</EDropdownItem>
<EDropdownItem command="delete">
<div class="item">
<Icon type="md-trash" />{{$L('删除')}}
</div>
</EDropdownItem>
</EDropdownMenu>
</EDropdown>
</div>
<div class="subtask-name">
<Input
v-model="taskDetail.name"
type="textarea"
:rows="1"
:autosize="{ minRows: 1, maxRows: 8 }"
:maxlength="255"
@on-blur="updateData('name')"
@on-keydown="onNameKeydown"/>
</div>
<DatePicker
v-model="timeValue"
:open="timeOpen"
:options="timeOptions"
format="yyyy-MM-dd HH:mm"
type="datetimerange"
class="subtask-time"
@on-open-change="timeChange"
@on-clear="timeClear"
@on-ok="timeOk"
transfer>
<div
@click="openTime"
:class="['time', taskDetail.today ? 'today' : '', taskDetail.overdue ? 'overdue' : '']">
{{taskDetail.end_at ? expiresFormat(taskDetail.end_at) : '--'}}
</div>
</DatePicker>
<Poptip
ref="owner"
class="subtask-avatar"
:title="$L('修改负责人')"
:width="240"
placement="bottom"
@on-popper-show="openOwner"
@on-popper-hide="ownerShow=false"
@on-ok="onOwner"
transfer>
<div slot="content">
<UserInput
v-if="ownerShow"
v-model="ownerData.owner_userid"
:multiple-max="1"
:placeholder="$L('选择任务负责人')"/>
<div class="task-detail-avatar-buttons">
<Button size="small" type="primary" @click="$refs.owner.ok()">{{$L('确定')}}</Button>
</div>
</div>
<UserAvatar v-if="getOwner" :userid="getOwner.userid" :size="20" hide-icon-menu/>
<div v-else>--</div>
</Poptip>
</li>
<!--主任务-->
<div v-else v-show="taskDetail.id > 0" :class="{'task-detail':true, 'open-dialog': taskDetail._dialog || taskDetail._msgText, 'completed': taskDetail.complete_at}">
<div class="task-info"> <div class="task-info">
<div class="head"> <div class="head">
<Icon v-if="taskDetail.complete_at" class="icon completed" type="md-checkmark-circle" @click="updateData('uncomplete')"/> <Icon v-if="taskDetail.complete_at" class="icon completed" type="md-checkmark-circle" @click="updateData('uncomplete')"/>
@ -19,7 +104,8 @@
:rows="1" :rows="1"
:autosize="{ minRows: 1, maxRows: 8 }" :autosize="{ minRows: 1, maxRows: 8 }"
:maxlength="255" :maxlength="255"
@on-blur="updateData('name')"/> @on-blur="updateData('name')"
@on-keydown="onNameKeydown"/>
</div> </div>
<div class="desc"> <div class="desc">
<TEditor <TEditor
@ -40,7 +126,7 @@
<li> <li>
<EDropdown <EDropdown
trigger="click" trigger="click"
placement="bottom-start" placement="bottom"
@command="updateData('priority', $event)"> @command="updateData('priority', $event)">
<TaskPriority :backgroundColor="taskDetail.p_color">{{taskDetail.p_name}}</TaskPriority> <TaskPriority :backgroundColor="taskDetail.p_color">{{taskDetail.p_name}}</TaskPriority>
<EDropdownMenu slot="dropdown"> <EDropdownMenu slot="dropdown">
@ -56,28 +142,89 @@
</li> </li>
</ul> </ul>
</FormItem> </FormItem>
<FormItem v-if="getOwner()"> <FormItem>
<div class="item-label" slot="label"> <div class="item-label" slot="label">
<i class="iconfont">&#xe6e4;</i>{{$L('负责人')}} <i class="iconfont">&#xe6e4;</i>{{$L('负责人')}}
</div> </div>
<ul class="item-content user"> <Poptip
<li @click="openTransfer"><UserAvatar :userid="getOwner().userid" :size="28"/></li> ref="owner"
</ul> :title="$L('修改负责人')"
:width="240"
class="item-content user"
placement="bottom"
@on-popper-show="openOwner"
@on-popper-hide="ownerShow=false"
@on-ok="onOwner"
transfer>
<div slot="content">
<UserInput
v-if="ownerShow"
v-model="ownerData.owner_userid"
:multiple-max="1"
:placeholder="$L('选择任务负责人')"/>
<div class="task-detail-avatar-buttons">
<Button size="small" type="primary" @click="$refs.owner.ok()">{{$L('确定')}}</Button>
</div>
</div>
<div v-if="getOwner" class="user-list">
<UserAvatar :userid="getOwner.userid" :size="28" hide-icon-menu/>
</div>
<div v-else>--</div>
</Poptip>
</FormItem> </FormItem>
<FormItem v-if="getAssist.length > 0"> <FormItem v-if="getAssist.length > 0">
<div class="item-label" slot="label"> <div class="item-label" slot="label">
<i class="iconfont">&#xe63f;</i>{{$L('协助人员')}} <i class="iconfont">&#xe63f;</i>{{$L('协助人员')}}
</div> </div>
<ul class="item-content user"> <Poptip
<li v-for="item in getAssist" @click="openAssist"><UserAvatar :userid="item.userid" :size="28"/></li> ref="assist"
</ul> :title="$L('修改协助人员')"
:width="280"
class="item-content user"
placement="bottom"
@on-popper-show="openAssist"
@on-popper-hide="assistShow=false"
@on-ok="onAssist"
transfer>
<div slot="content">
<UserInput
v-if="assistShow"
v-model="assistData.assist_userid"
:multiple-max="10"
:disabled-choice="assistData.disabled"
:placeholder="$L('选择任务协助人员')"/>
<div class="task-detail-avatar-buttons">
<Button size="small" type="primary" @click="$refs.assist.ok()">{{$L('确定')}}</Button>
</div>
</div>
<div class="user-list">
<UserAvatar v-for="item in getAssist" :key="item.userid" :userid="item.userid" :size="28" hide-icon-menu/>
</div>
</Poptip>
</FormItem> </FormItem>
<FormItem v-if="taskDetail.end_at"> <FormItem v-if="taskDetail.end_at">
<div class="item-label" slot="label"> <div class="item-label" slot="label">
<i class="iconfont">&#xe6e8;</i>{{$L('截止时间')}} <i class="iconfont">&#xe6e8;</i>{{$L('截止时间')}}
</div> </div>
<ul class="item-content"> <ul class="item-content">
<li>{{taskDetail.end_at}}</li> <li>
<DatePicker
v-model="timeValue"
:open="timeOpen"
:options="timeOptions"
format="yyyy-MM-dd HH:mm"
type="datetimerange"
@on-open-change="timeChange"
@on-clear="timeClear"
@on-ok="timeOk"
transfer>
<div class="picker-time">
<div @click="openTime" class="time">{{cutTime}}</div>
<Tag v-if="!taskDetail.complete_at && taskDetail.today" color="blue"><Icon type="ios-time-outline"/>{{expiresFormat(taskDetail.end_at)}}</Tag>
<Tag v-if="!taskDetail.complete_at && taskDetail.overdue" color="red">{{$L('超期未完成')}}</Tag>
</div>
</DatePicker>
</li>
</ul> </ul>
</FormItem> </FormItem>
<FormItem v-if="hasFile"> <FormItem v-if="hasFile">
@ -86,12 +233,13 @@
</div> </div>
<ul class="item-content file"> <ul class="item-content file">
<li v-for="file in taskDetail.files"> <li v-for="file in taskDetail.files">
<img class="file-ext" :src="file.thumb"/> <img v-if="file.id" class="file-ext" :src="file.thumb"/>
<Loading v-else class="file-load"/>
<div class="file-name">{{file.name}}</div> <div class="file-name">{{file.name}}</div>
<div class="file-size">{{$A.bytesToSize(file.size)}}</div> <div class="file-size">{{$A.bytesToSize(file.size)}}</div>
</li> </li>
<li> <li>
<div class="add-button"> <div class="add-button" @click="$refs.upload.handleClick()">
<i class="iconfont">&#xe6f2;</i>{{$L('添加附件')}} <i class="iconfont">&#xe6f2;</i>{{$L('添加附件')}}
</div> </div>
</li> </li>
@ -102,27 +250,9 @@
<i class="iconfont">&#xe6f0;</i>{{$L('子任务')}} <i class="iconfont">&#xe6f0;</i>{{$L('子任务')}}
</div> </div>
<ul class="item-content subtask"> <ul class="item-content subtask">
<li v-for="task in taskDetail.sub_task"> <TaskDetail v-for="(task, key) in taskDetail.sub_task" :key="key" :open-task="task"/>
<Icon class="subtask-icon" type="md-radio-button-off" />
<div class="subtask-name">
<Input
v-model="task.name"
type="textarea"
:rows="1"
:autosize="{ minRows: 1, maxRows: 8 }"
:maxlength="255"/>
</div>
<div
v-if="task.end_at"
:class="['subtask-time-avatar', task.today ? 'today' : '', task.overdue ? 'overdue' : '']">{{expiresFormat(task.end_at)}}</div>
<UserAvatar
v-if="getOwner(task)"
class="subtask-avatar"
:userid="getOwner(task).userid"
:size="20"/>
</li>
<li> <li>
<div class="add-button"> <div class="add-button" @click="">
<i class="iconfont">&#xe6f2;</i>{{$L('添加子任务')}} <i class="iconfont">&#xe6f2;</i>{{$L('添加子任务')}}
</div> </div>
</li> </li>
@ -132,7 +262,7 @@
<div class="add"> <div class="add">
<EDropdown <EDropdown
trigger="click" trigger="click"
placement="bottom-start" placement="bottom"
@command=""> @command="">
<div class="add-button"> <div class="add-button">
<i class="iconfont">&#xe6f2;</i> <i class="iconfont">&#xe6f2;</i>
@ -149,6 +279,7 @@
</EDropdown> </EDropdown>
</div> </div>
</div> </div>
<TaskUpload ref="upload" class="upload"/>
</div> </div>
<div class="task-dialog" > <div class="task-dialog" >
<div class="head"> <div class="head">
@ -173,47 +304,6 @@
</div> </div>
</div> </div>
</div> </div>
<!--修改负责人-->
<Modal
v-model="transferShow"
:title="$L('修改负责人')"
:mask-closable="false">
<Form ref="addProject" :model="transferData" label-width="auto" @submit.native.prevent>
<FormItem prop="owner_userid" :label="$L('任务负责人')">
<UserInput
v-if="transferShow"
v-model="transferData.owner_userid"
:multiple-max="1"
:placeholder="$L('选择任务负责人')"/>
</FormItem>
</Form>
<div slot="footer">
<Button type="default" @click="transferShow=false">{{$L('取消')}}</Button>
<Button type="primary" :loading="transferLoad > 0" @click="onTransfer">{{$L('提交')}}</Button>
</div>
</Modal>
<!--修改协助人员-->
<Modal
v-model="assistShow"
:title="$L('修改协助人员')"
:mask-closable="false">
<Form ref="addProject" :model="assistData" label-width="auto" @submit.native.prevent>
<FormItem prop="owner_userid" :label="$L('协助人员')">
<UserInput
v-if="assistShow"
v-model="assistData.assist_userid"
:multiple-max="1"
:disabled-choice="assistData.disabled"
:placeholder="$L('选择任务协助人员')"/>
</FormItem>
</Form>
<div slot="footer">
<Button type="default" @click="assistShow=false">{{$L('取消')}}</Button>
<Button type="primary" :loading="assistLoad > 0" @click="onAssist">{{$L('提交')}}</Button>
</div>
</Modal>
</div> </div>
</template> </template>
@ -222,17 +312,26 @@ import {mapState} from "vuex";
import TEditor from "../../../components/TEditor"; import TEditor from "../../../components/TEditor";
import TaskPriority from "./TaskPriority"; import TaskPriority from "./TaskPriority";
import UserInput from "../../../components/UserInput"; import UserInput from "../../../components/UserInput";
import TaskUpload from "./TaskUpload";
export default { export default {
name: "TaskDetail", name: "TaskDetail",
components: {UserInput, TaskPriority, TEditor}, components: {TaskUpload, UserInput, TaskPriority, TEditor},
props: {
openTask: {
type: Object,
default: () => {
return {};
}
},
},
data() { data() {
return { return {
taskDetail: {}, taskDetail: {},
transferShow: false, ownerShow: false,
transferData: {}, ownerData: {},
transferLoad: 0, ownerLoad: 0,
assistShow: false, assistShow: false,
assistData: {}, assistData: {},
@ -268,6 +367,12 @@ export default {
valid_elements : 'a[href|target=_blank],em,strong/b,div[align],span[style],a,br,img[src|alt|witdh|height],pre[class],code', valid_elements : 'a[href|target=_blank],em,strong/b,div[align],span[style],a,br,img[src|alt|witdh|height],pre[class],code',
toolbar: 'uploadImages | uploadFiles | bold italic underline forecolor backcolor | codesample | preview screenload' toolbar: 'uploadImages | uploadFiles | bold italic underline forecolor backcolor | codesample | preview screenload'
}, },
timeOpen: false,
timeValue: [],
timeOptions: {
shortcuts: []
},
} }
}, },
@ -285,7 +390,7 @@ export default {
}, },
computed: { computed: {
...mapState(['userId', 'projectOpenTask', 'taskPriority']), ...mapState(['userId', 'taskPriority']),
scrollerStyle() { scrollerStyle() {
const {innerHeight, taskDetail} = this; const {innerHeight, taskDetail} = this;
@ -327,6 +432,18 @@ export default {
} }
}, },
cutTime() {
const {nowTime, taskDetail} = this;
let string = "";
let start_at = Math.round(new Date(taskDetail.start_at).getTime() / 1000);
if (start_at > nowTime) {
string = $A.formatDate('Y/m/d H:i', start_at) + " ~ "
}
let end_at = Math.round(new Date(taskDetail.end_at).getTime() / 1000);
string+= $A.formatDate('Y/m/d H:i', end_at);
return string;
},
hasFile() { hasFile() {
const {taskDetail} = this; const {taskDetail} = this;
return $A.isArray(taskDetail.files) && taskDetail.files.length > 0; return $A.isArray(taskDetail.files) && taskDetail.files.length > 0;
@ -338,15 +455,11 @@ export default {
}, },
getOwner() { getOwner() {
return function (task) { const {taskDetail} = this;
if (task === undefined) { if (!$A.isArray(taskDetail.task_user)) {
task = this.taskDetail; return null;
}
if (!$A.isArray(task.task_user)) {
return null;
}
return task.task_user.find(({owner}) => owner === 1);
} }
return taskDetail.task_user.find(({owner}) => owner === 1);
}, },
getAssist() { getAssist() {
@ -407,13 +520,75 @@ export default {
}, },
watch: { watch: {
projectOpenTask(data) { openTask: {
this.taskDetail = $A.cloneJSON(data); handler(data) {
if (data._show) this.$nextTick(this.$refs.input.focus) this.taskDetail = $A.cloneJSON(data);
},
immediate: true,
deep: true
}, },
'openTask._show' (v) {
if (v) {
this.$nextTick(this.$refs.input.focus)
} else {
this.timeOpen = false;
}
}
}, },
methods: { methods: {
initLanguage() {
const lastSecond = (e) => {
return new Date($A.formatDate("Y-m-d 23:59:29", Math.round(e / 1000)))
};
this.timeOptions = {
shortcuts: [{
text: this.$L('今天'),
value() {
return [new Date(), lastSecond(new Date().getTime())];
}
}, {
text: this.$L('明天'),
value() {
let e = new Date();
e.setDate(e.getDate() + 1);
return [new Date(), lastSecond(e.getTime())];
}
}, {
text: this.$L('本周'),
value() {
return [$A.getData('今天', true), lastSecond($A.getData('本周结束2', true))];
}
}, {
text: this.$L('本月'),
value() {
return [$A.getData('今天', true), lastSecond($A.getData('本月结束', true))];
}
}, {
text: this.$L('3天'),
value() {
let e = new Date();
e.setDate(e.getDate() + 3);
return [new Date(), lastSecond(e.getTime())];
}
}, {
text: this.$L('5天'),
value() {
let e = new Date();
e.setDate(e.getDate() + 5);
return [new Date(), lastSecond(e.getTime())];
}
}, {
text: this.$L('7天'),
value() {
let e = new Date();
e.setDate(e.getDate() + 7);
return [new Date(), lastSecond(e.getTime())];
}
}]
};
},
innerHeightListener() { innerHeightListener() {
this.innerHeight = window.innerHeight; this.innerHeight = window.innerHeight;
}, },
@ -454,6 +629,28 @@ export default {
return duration; return duration;
}, },
onNameKeydown(e) {
if (e.keyCode === 13) {
if (e.shiftKey) {
return;
}
e.preventDefault();
this.updateData('name');
}
},
dropTask(command) {
if (command === 'complete') {
this.updateData('complete')
}
else if (command === 'uncomplete') {
this.updateData('uncomplete')
}
else if (command === 'delete') {
this.archivedOrRemoveTask('delete');
}
},
updateData(action, params) { updateData(action, params) {
switch (action) { switch (action) {
case 'complete': case 'complete':
@ -470,46 +667,77 @@ export default {
this.$set(this.taskDetail, 'p_color', params.color) this.$set(this.taskDetail, 'p_color', params.color)
action = ['p_level', 'p_name', 'p_color']; action = ['p_level', 'p_name', 'p_color'];
break; break;
case 'times':
this.$set(this.taskDetail, 'times', [params.start_at, params.end_at])
break;
} }
// //
let dataJson = {task_id: this.taskDetail.id}; let dataJson = {task_id: this.taskDetail.id};
($A.isArray(action) ? action : [action]).forEach(key => { ($A.isArray(action) ? action : [action]).forEach(key => {
let newData = this.taskDetail[key]; let newData = this.taskDetail[key];
let originalData = this.projectOpenTask[key]; let originalData = this.openTask[key];
if ($A.jsonStringify(newData) != $A.jsonStringify(originalData)) { if ($A.jsonStringify(newData) != $A.jsonStringify(originalData)) {
dataJson[key] = newData; dataJson[key] = newData;
} }
}) })
if (Object.keys(dataJson).length <= 1) return; if (Object.keys(dataJson).length <= 1) return;
// //
this.$store.dispatch("taskUpdate", dataJson).then(() => { this.$store.dispatch("taskUpdate", dataJson).then(({msg}) => {
// //
}).catch(() => { $A.messageSuccess(msg);
}).catch(({msg}) => {
// //
$A.modalError(msg);
}) })
}, },
openTransfer() { archivedOrRemoveTask(type) {
this.$set(this.taskDetail, 'owner_userid', [this.getOwner().userid]) let typeTitle = this.taskDetail.parent_id > 0 ? '子任务' : '任务';
this.$set(this.transferData, 'owner_userid', [this.getOwner().userid]); $A.modalConfirm({
this.transferShow = true; title: '删除' + typeTitle,
content: '你确定要删除' + typeTitle + '【' + this.taskDetail.name + '】吗?',
loading: true,
onOk: () => {
if (this.taskDetail.loading === true) {
return;
}
this.$set(this.taskDetail, 'loading', true);
this.$store.dispatch("taskArchivedOrRemove", {
task_id: this.taskDetail.id,
type: type,
}).then(({msg}) => {
this.$Modal.remove();
$A.messageSuccess(msg);
}).catch(({msg}) => {
this.$Modal.remove();
$A.modalError(msg, 301);
});
}
});
}, },
onTransfer() { openOwner() {
if ($A.jsonStringify(this.taskDetail.owner_userid) === $A.jsonStringify(this.transferData.owner_userid)) { this.$set(this.taskDetail, 'owner_userid', [this.getOwner.userid])
this.$set(this.ownerData, 'owner_userid', [this.getOwner.userid]);
this.ownerShow = true;
},
onOwner() {
if ($A.jsonStringify(this.taskDetail.owner_userid) === $A.jsonStringify(this.ownerData.owner_userid)) {
return; return;
} }
this.transferLoad++; this.ownerLoad++;
this.$store.dispatch("taskUpdate", { this.$store.dispatch("taskUpdate", {
task_id: this.taskDetail.id, task_id: this.taskDetail.id,
owner: this.transferData.owner_userid owner: this.ownerData.owner_userid
}).then(() => { }).then(({msg}) => {
this.transferLoad--; this.ownerLoad--;
this.transferShow = false; this.ownerShow = false;
this.$store.dispatch("taskOne", this.taskDetail.id); this.$store.dispatch("taskOne", this.taskDetail.id);
$A.messageSuccess(msg);
}).catch(({msg}) => { }).catch(({msg}) => {
this.transferLoad--; this.ownerLoad--;
this.transferShow = false; this.ownerShow = false;
$A.modalError(msg); $A.modalError(msg);
}) })
}, },
@ -518,7 +746,7 @@ export default {
const list = this.getAssist.map(({userid}) => userid) const list = this.getAssist.map(({userid}) => userid)
this.$set(this.taskDetail, 'assist_userid', list) this.$set(this.taskDetail, 'assist_userid', list)
this.$set(this.assistData, 'assist_userid', list); this.$set(this.assistData, 'assist_userid', list);
this.$set(this.assistData, 'disabled', [this.getOwner().userid]); this.$set(this.assistData, 'disabled', [this.getOwner.userid]);
this.assistShow = true; this.assistShow = true;
}, },
@ -532,16 +760,58 @@ export default {
this.$store.dispatch("taskUpdate", { this.$store.dispatch("taskUpdate", {
task_id: this.taskDetail.id, task_id: this.taskDetail.id,
assist, assist,
}).then(() => { }).then(({msg}) => {
this.assistLoad--; this.assistLoad--;
this.assistShow = false; this.assistShow = false;
this.$store.dispatch("taskOne", this.taskDetail.id); this.$store.dispatch("taskOne", this.taskDetail.id);
$A.messageSuccess(msg);
}).catch(({msg}) => { }).catch(({msg}) => {
this.assistLoad--; this.assistLoad--;
this.assistShow = false; this.assistShow = false;
$A.modalError(msg); $A.modalError(msg);
}) })
} },
openTime() {
this.timeOpen = !this.timeOpen;
if (this.timeOpen) {
this.timeValue = this.taskDetail.end_at ? [this.taskDetail.start_at, this.taskDetail.end_at] : [];
}
},
timeChange(open) {
if (!open) {
this.timeOpen = false;
}
},
timeClear() {
$A.modalConfirm({
content: '你确定要取消任务时间吗?',
cancelText: '不是',
onOk: () => {
this.updateData('times', {
start_at: false,
end_at: false,
});
this.timeOpen = false;
}
});
},
timeOk() {
let times = $A.date2string(this.timeValue, "Y-m-d H:i");
if (times[0] && times[1]) {
if ($A.rightExists(times[0], '00:00') && $A.rightExists(times[1], '00:00')) {
times[1] = times[1].replace("00:00", "23:59");
}
}
this.updateData('times', {
start_at: times[0],
end_at: times[1],
});
this.timeOpen = false;
},
} }
} }
</script> </script>

View File

@ -0,0 +1,107 @@
<template>
<Upload
name="files"
ref="upload"
:action="actionUrl"
:data="params"
multiple
:format="uploadFormat"
:show-upload-list="false"
:max-size="maxSize"
:on-progress="handleProgress"
:on-success="handleSuccess"
:on-format-error="handleFormatError"
:on-exceeded-size="handleMaxSize">
</Upload>
</template>
<script>
import {mapState} from "vuex";
export default {
name: 'TaskUpload',
props: {
maxSize: {
type: Number,
default: 204800
}
},
data() {
return {
uploadFormat: ['jpg', 'jpeg', 'png', 'gif', 'doc', 'docx', 'xls', 'xlsx', 'ppt', 'pptx', 'txt', 'esp', 'pdf', 'rar', 'zip', 'gz', 'ai', 'avi', 'bmp', 'cdr', 'eps', 'mov', 'mp3', 'mp4', 'pr', 'psd', 'svg', 'tif'],
actionUrl: this.$store.state.method.apiUrl('project/task/upload'),
}
},
computed: {
...mapState(['userToken', 'projectOpenTask']),
params() {
return {
task_id: this.projectOpenTask.id,
token: this.userToken,
}
}
},
methods: {
handleProgress(event, file) {
//
if (typeof file.tempId === "undefined") {
file.tempId = $A.randomString(8);
this.projectOpenTask.files.push(file);
}
},
handleSuccess(res, file) {
//
let index = this.projectOpenTask.files.findIndex(({tempId}) => tempId == file.tempId);
if (res.ret === 1) {
if (index > -1) {
this.projectOpenTask.files.splice(index, 1, res.data);
this.$store.dispatch("taskData", {
id: this.projectOpenTask.id,
file_num: this.projectOpenTask.files.length,
});
}
} else {
if (index > -1) {
this.projectOpenTask.files.splice(index, 1);
}
this.$refs.upload.fileList.pop();
$A.modalWarning({
title: '发送失败',
content: '文件 ' + file.name + ' 发送失败,' + res.msg
});
}
},
handleFormatError(file) {
//
$A.modalWarning({
title: '文件格式不正确',
content: '文件 ' + file.name + ' 格式不正确,仅支持发送:' + this.uploadFormat.join(',')
});
},
handleMaxSize(file) {
//
$A.modalWarning({
title: '超出文件大小限制',
content: '文件 ' + file.name + ' 太大,不能发送超过' + $A.bytesToSize(this.maxSize * 1024) + '。'
});
},
handleClick() {
//
this.$refs.upload.handleClick()
},
upload(file) {
//file
this.$refs.upload.upload(file);
},
}
}
</script>

View File

@ -389,6 +389,11 @@ export default {
}); });
if (data.id == state.projectOpenTask.id) { if (data.id == state.projectOpenTask.id) {
state.projectOpenTask = Object.assign({}, state.projectOpenTask, data); state.projectOpenTask = Object.assign({}, state.projectOpenTask, data);
} else if (data.parent_id == state.projectOpenTask.id) {
let index = state.projectOpenTask.sub_task.findIndex(({id}) => id === data.id);
if (index > -1) {
state.projectOpenTask.sub_task.splice(index, 1, Object.assign(state.projectOpenTask.sub_task[index], data))
}
} }
}, },
@ -522,28 +527,77 @@ export default {
}, },
/** /**
* 更新任务 * 添加任务
* @param state
* @param dispatch * @param dispatch
* @param data * @param data
* @returns {Promise<unknown>} * @returns {Promise<unknown>}
*/ */
taskUpdate({dispatch}, data) { taskAdd({state, dispatch}, data) {
return new Promise(function (resolve, reject) { return new Promise(function (resolve, reject) {
const post = state.method.cloneJSON(data);
if (state.method.isArray(post.column_id)) {
post.column_id = post.column_id.find((val) => val)
}
if (state.method.isArray(post.owner)) {
post.owner = post.owner.find((val) => val)
}
//
dispatch("call", {
url: 'project/task/add',
data: post,
method: 'post',
}).then(result => {
const {task, in_top, new_column} = result.data;
if (state.projectDetail.id == task.project_id) {
if (new_column) {
state.projectDetail.project_column.push(new_column);
}
const column = state.projectDetail.project_column.find(({id}) => id === task.column_id);
if (column) {
if (in_top) {
column.project_task.unshift(task);
} else {
column.project_task.push(task);
}
}
}
dispatch('projectOne', task.project_id);
resolve(result)
}).catch(result => {
reject(result)
});
});
},
/**
* 更新任务
* @param state
* @param dispatch
* @param data
* @returns {Promise<unknown>}
*/
taskUpdate({state, dispatch}, data) {
return new Promise(function (resolve, reject) {
const post = state.method.cloneJSON(data);
if (state.method.isArray(post.owner)) {
post.owner = post.owner.find((id) => id)
}
dispatch("call", { dispatch("call", {
url: 'project/task/update', url: 'project/task/update',
data: data, data: post,
method: 'post', method: 'post',
}).then(result => { }).then(result => {
if (result.data.parent_id) { if (result.data.parent_id) {
dispatch('taskOne', result.data.parent_id); dispatch('taskOne', result.data.parent_id);
} }
if (typeof data.complete_at !== "undefined") { if (typeof post.complete_at !== "undefined") {
dispatch('projectOne', result.data.project_id); dispatch('projectOne', result.data.project_id);
} }
dispatch("taskData", result.data); dispatch("taskData", result.data);
resolve(result) resolve(result)
}).catch(result => { }).catch(result => {
dispatch('taskOne', data.task_id); dispatch('taskOne', post.task_id);
reject(result) reject(result)
}); });
}); });
@ -553,7 +607,7 @@ export default {
* 删除或归档任务 * 删除或归档任务
* @param state * @param state
* @param dispatch * @param dispatch
* @param data * @param data {task_id, type}
* @returns {Promise<unknown>} * @returns {Promise<unknown>}
*/ */
taskArchivedOrRemove({state, dispatch}, data) { taskArchivedOrRemove({state, dispatch}, data) {
@ -565,14 +619,23 @@ export default {
task_id, task_id,
}, },
}).then(result => { }).then(result => {
const column = state.projectDetail.project_column.find(({id}) => id === result.data.column_id); const {data} = result;
const column = state.projectDetail.project_column.find(({id}) => id === data.column_id);
if (column) { if (column) {
let index = column.project_task.findIndex(({id}) => id === result.data.id); let index = column.project_task.findIndex(({id}) => id === data.id);
if (index > -1) { if (index > -1) {
column.project_task.splice(index, 1); column.project_task.splice(index, 1);
} }
} }
dispatch('projectDetail', result.data.project_id); if (data.id == state.projectOpenTask.id) {
state.projectOpenTask = Object.assign({}, state.projectOpenTask, {_show: false});
} else if (data.parent_id == state.projectOpenTask.id) {
let index = state.projectOpenTask.sub_task.findIndex(({id}) => id === data.id);
if (index > -1) {
state.projectOpenTask.sub_task.splice(index, 1)
}
}
dispatch('projectDetail', data.project_id);
resolve(result); resolve(result);
}).catch(result => { }).catch(result => {
reject(result) reject(result)

View File

@ -109,18 +109,49 @@
line-height: 26px; line-height: 26px;
.el-dropdown { .el-dropdown {
display: flex; display: flex;
cursor: pointer;
}
.picker-time {
display: flex;
align-items: center;
line-height: 26px;
.time {
cursor: pointer;
}
.ivu-tag {
margin-left: 10px;
padding: 0 4px;
height: 20px;
line-height: 18px;
.ivu-tag-text {
display: flex;
align-items: center;
> i {
padding-right: 2px;
}
}
}
} }
} }
&.user { &.user {
margin-top: 0; margin-top: 1px;
> li { cursor: pointer;
display: inline-block; .user-list {
cursor: pointer; > div {
display: inline-block;
margin-right: 6px;
}
} }
} }
&.file { &.file {
> li { > li {
margin-bottom: 2px; margin-bottom: 2px;
.file-load {
margin: 0;
padding: 2px;
width: 16px;
height: 16px;
}
.file-ext { .file-ext {
width: 16px; width: 16px;
} }
@ -138,17 +169,25 @@
&.subtask { &.subtask {
> li { > li {
align-items: flex-start; align-items: flex-start;
margin-bottom: 2px; margin-bottom: 4px;
.subtask-icon { .subtask-icon {
width: 16px; width: 16px;
height: 26px; height: 26px;
line-height: 26px; line-height: 26px;
font-size: 16px; margin-right: 6px;
color: #cccccc;
margin-right: 8px;
cursor: pointer; cursor: pointer;
&.completed { .loading {
color: #87d068; width: 16px;
height: 16px;
margin: 0;
padding: 2px;
}
.ivu-icon {
font-size: 16px;
color: #cccccc;
&.completed {
color: #87d068;
}
} }
&.sub-icon { &.sub-icon {
font-size: 16px; font-size: 16px;
@ -168,7 +207,7 @@
margin-right: 16px; margin-right: 16px;
display: flex; display: flex;
.ivu-input { .ivu-input {
margin: -1px 0; margin: -2px 0;
padding: 4px 0; padding: 4px 0;
resize: none; resize: none;
border-color: transparent; border-color: transparent;
@ -179,31 +218,28 @@
} }
} }
.subtask-time { .subtask-time {
overflow: hidden; margin-right: 8px;
text-overflow: ellipsis; .time {
white-space: nowrap; overflow: hidden;
margin-right: 6px; text-overflow: ellipsis;
font-size: 13px; white-space: nowrap;
height: 26px; font-size: 13px;
line-height: 26px; height: 26px;
&.overdue { line-height: 26px;
font-weight: 600; cursor: pointer;
color: #ed4014; &.overdue {
} font-weight: 600;
&.today { color: #ed4014;
font-weight: 500; }
color: #ff9900; &.today {
font-weight: 500;
color: #ff9900;
}
} }
} }
.subtask-avatar { .subtask-avatar {
height: 26px; height: 26px;
line-height: 1; cursor: pointer;
.avatar-box {
> em {
right: -2px;
bottom: -1px;
}
}
} }
} }
} }
@ -240,6 +276,9 @@
} }
} }
} }
.upload {
display: none;
}
} }
.task-dialog { .task-dialog {
flex: 1; flex: 1;
@ -388,4 +427,15 @@
} }
} }
.task-detail-avatar-buttons {
margin-top: 12px;
margin-bottom: 4px;
text-align: right;
position: absolute;
top: 5px;
right: 14px;
> button {
font-size: 12px;
transform: scale(0.9);
}
}