no message
This commit is contained in:
parent
f4cd7fe1e2
commit
74fa8ff9a1
@ -247,43 +247,8 @@ class DialogController extends AbstractController
|
||||
if (Base::isError($data)) {
|
||||
return Base::retError($data['msg']);
|
||||
} else {
|
||||
$fileData = $data['data'];
|
||||
$fileData['thumb'] = $fileData['thumb'] ?: 'images/ext/file.png';
|
||||
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 = $data['data'];
|
||||
$msg['thumb'] = Base::unFillUrl($msg['thumb']);
|
||||
$msg['size'] *= 1024;
|
||||
//
|
||||
return WebSocketDialogMsg::sendMsg($dialog_id, 'file', $msg, $user->userid, $extra_int, $extra_str);
|
||||
|
@ -7,6 +7,7 @@ use App\Models\Project;
|
||||
use App\Models\ProjectColumn;
|
||||
use App\Models\ProjectLog;
|
||||
use App\Models\ProjectTask;
|
||||
use App\Models\ProjectTaskFile;
|
||||
use App\Models\ProjectUser;
|
||||
use App\Models\User;
|
||||
use App\Module\Base;
|
||||
@ -723,7 +724,7 @@ class ProjectController extends AbstractController
|
||||
* @apiParam {String} name 任务描述
|
||||
* @apiParam {String} [content] 任务详情
|
||||
* @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 {Number} [top] 添加的任务排到列表最前面
|
||||
*/
|
||||
@ -750,9 +751,6 @@ class ProjectController extends AbstractController
|
||||
// 列表
|
||||
$column = null;
|
||||
$newColumn = null;
|
||||
if (is_array($column_id)) {
|
||||
$column_id = Base::arrayFirst($column_id);
|
||||
}
|
||||
if ($column_id) {
|
||||
if (intval($column_id) > 0) {
|
||||
$column = $project->projectColumn->find($column_id);
|
||||
@ -760,6 +758,8 @@ class ProjectController extends AbstractController
|
||||
if (empty($column)) {
|
||||
$column = ProjectColumn::whereProjectId($project->id)->whereName($column_id)->first();
|
||||
}
|
||||
} else {
|
||||
$column = ProjectColumn::whereProjectId($project->id)->orderBy('id')->first();
|
||||
}
|
||||
if (empty($column)) {
|
||||
$column = ProjectColumn::createInstance([
|
||||
@ -798,7 +798,8 @@ class ProjectController extends AbstractController
|
||||
* @apiParam {String} [color] 任务描述(子任务不支持)
|
||||
* @apiParam {String} [content] 任务详情(子任务不支持)
|
||||
* @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:00,false表示未完成)
|
||||
*/
|
||||
@ -850,6 +851,76 @@ class ProjectController extends AbstractController
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 归档任务
|
||||
*
|
||||
|
@ -27,6 +27,9 @@ class VerifyCsrfToken extends Middleware
|
||||
// 修改任务
|
||||
'api/project/task/update/',
|
||||
|
||||
// 上传任务问题
|
||||
'api/project/task/upload/',
|
||||
|
||||
// 聊天发文件
|
||||
'api/dialog/msg/sendfile/',
|
||||
];
|
||||
|
@ -299,10 +299,7 @@ class ProjectTask extends AbstractModel
|
||||
}
|
||||
}
|
||||
// 负责人
|
||||
if (is_array($owner)) {
|
||||
$owner = Base::arrayFirst($owner);
|
||||
}
|
||||
$owner = $owner ?: User::token2userid();
|
||||
$owner = intval($owner) ?: User::token2userid();
|
||||
if (!ProjectUser::whereProjectId($project_id)->whereUserid($owner)->exists()) {
|
||||
return Base::retError($retPre . '负责人填写错误');
|
||||
}
|
||||
@ -360,9 +357,6 @@ class ProjectTask extends AbstractModel
|
||||
public function updateTask($data)
|
||||
{
|
||||
return AbstractModel::transaction(function () use ($data) {
|
||||
$content = $data['content'];
|
||||
$times = $data['times'];
|
||||
$owner = $data['owner'];
|
||||
// 标题
|
||||
if (Arr::exists($data, 'name')) {
|
||||
if (empty($data['name'])) {
|
||||
@ -373,25 +367,48 @@ class ProjectTask extends AbstractModel
|
||||
$this->name = $data['name'];
|
||||
}
|
||||
// 负责人
|
||||
if ($owner) {
|
||||
if (is_array($owner)) {
|
||||
$owner = Base::arrayFirst($owner);
|
||||
}
|
||||
$ownerUser = ProjectTaskUser::whereTaskId($this->id)->whereOwner(1)->first();
|
||||
if ($ownerUser->userid != $owner) {
|
||||
$ownerUser->owner = 0;
|
||||
$ownerUser->save();
|
||||
if (Arr::exists($data, 'owner')) {
|
||||
$row = ProjectTaskUser::whereTaskId($this->id)->whereOwner(1)->first();
|
||||
if ($row->userid != $data['owner']) {
|
||||
if (!User::find(intval($data['owner']))) {
|
||||
return Base::retError('请选择正确的负责人');
|
||||
}
|
||||
$row->owner = 0;
|
||||
$row->save();
|
||||
ProjectTaskUser::updateInsert([
|
||||
'project_id' => $this->parent_id,
|
||||
'task_id' => $this->id,
|
||||
'userid' => $owner,
|
||||
'userid' => $data['owner'],
|
||||
], [
|
||||
'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 : []);
|
||||
if (Base::isDate($start) && Base::isDate($end)) {
|
||||
if ($start != $end) {
|
||||
@ -407,14 +424,14 @@ class ProjectTask extends AbstractModel
|
||||
$this->color = $data['color'];
|
||||
}
|
||||
// 内容
|
||||
if ($content && $this->parent_id === 0) {
|
||||
if (Arr::exists($data, 'content')) {
|
||||
ProjectTaskContent::updateInsert([
|
||||
'project_id' => $this->parent_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')) {
|
||||
@ -428,6 +445,8 @@ class ProjectTask extends AbstractModel
|
||||
}
|
||||
}
|
||||
$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('修改成功');
|
||||
});
|
||||
}
|
||||
|
@ -2,6 +2,8 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Module\Base;
|
||||
|
||||
/**
|
||||
* Class ProjectTaskFile
|
||||
*
|
||||
@ -37,5 +39,23 @@ namespace App\Models;
|
||||
*/
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
@ -79,8 +79,8 @@ class WebSocketDialogMsg extends AbstractModel
|
||||
$value = Base::json2array($value);
|
||||
if ($this->type === 'file') {
|
||||
$value['type'] = in_array($value['ext'], ['jpg', 'jpeg', 'png', 'gif']) ? 'img' : 'file';
|
||||
$value['url'] = Base::fillUrl($value['path']);
|
||||
$value['thumb'] = Base::fillUrl($value['thumb']);
|
||||
$value['path'] = Base::fillUrl($value['path']);
|
||||
$value['thumb'] = Base::fillUrl($value['thumb'] ?: Base::extIcon($value['ext']));
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
@ -2486,6 +2486,50 @@ class Base
|
||||
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
|
||||
|
@ -110,7 +110,7 @@
|
||||
maxWidth: projectOpenTask._dialog || projectOpenTask._msgText ? '1200px' : '640px'
|
||||
}"
|
||||
footer-hide>
|
||||
<TaskDetail/>
|
||||
<TaskDetail :open-task="projectOpenTask"/>
|
||||
</Modal>
|
||||
</div>
|
||||
</template>
|
||||
|
@ -7,7 +7,7 @@
|
||||
<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]">
|
||||
<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"/>
|
||||
<div v-else class="file-box">
|
||||
<img class="file-thumb" :src="msgData.msg.thumb"/>
|
||||
|
@ -107,7 +107,6 @@
|
||||
:add-top="true"
|
||||
@on-close="column.addTopShow=false"
|
||||
@on-priority="addTaskOpen"
|
||||
@on-success="addTaskSuccess"
|
||||
auto-active/>
|
||||
</div>
|
||||
<Draggable
|
||||
@ -195,8 +194,7 @@
|
||||
<TaskAddSimple
|
||||
:column-id="column.id"
|
||||
:project-id="projectDetail.id"
|
||||
@on-priority="addTaskOpen"
|
||||
@on-success="addTaskSuccess"/>
|
||||
@on-priority="addTaskOpen"/>
|
||||
</div>
|
||||
</Draggable>
|
||||
</div>
|
||||
@ -589,13 +587,8 @@ export default {
|
||||
|
||||
onAddTask() {
|
||||
this.taskLoad++;
|
||||
this.$store.dispatch("call", {
|
||||
url: 'project/task/add',
|
||||
data: this.addData,
|
||||
method: 'post',
|
||||
}).then(({data, msg}) => {
|
||||
this.$store.dispatch("taskAdd", this.addData).then(({msg}) => {
|
||||
this.taskLoad--;
|
||||
$A.messageSuccess(msg);
|
||||
this.addShow = false;
|
||||
this.addData = {
|
||||
owner: 0,
|
||||
@ -606,8 +599,7 @@ export default {
|
||||
p_name: '',
|
||||
p_color: '',
|
||||
};
|
||||
this.$store.dispatch('projectOne', data.project_id);
|
||||
this.addTaskSuccess(data)
|
||||
$A.messageSuccess(msg);
|
||||
}).catch(({msg}) => {
|
||||
this.taskLoad--;
|
||||
$A.modalError(msg);
|
||||
@ -630,21 +622,6 @@ export default {
|
||||
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() {
|
||||
this.addColumnShow = true;
|
||||
this.$nextTick(() => {
|
||||
|
@ -151,13 +151,8 @@ export default {
|
||||
return;
|
||||
}
|
||||
this.loadIng++;
|
||||
this.$store.dispatch("call", {
|
||||
url: 'project/task/add',
|
||||
data: this.getData(),
|
||||
method: 'post',
|
||||
}).then(({data, msg}) => {
|
||||
this.$store.dispatch("taskAdd", this.getData()).then(({msg}) => {
|
||||
this.loadIng--;
|
||||
$A.messageSuccess(msg);
|
||||
this.active = false;
|
||||
this.addData = {
|
||||
owner: 0,
|
||||
@ -168,8 +163,7 @@ export default {
|
||||
p_name: '',
|
||||
p_color: '',
|
||||
}
|
||||
this.$store.dispatch('projectOne', data.project_id);
|
||||
this.$emit("on-success", data)
|
||||
$A.messageSuccess(msg);
|
||||
}).catch(({msg}) => {
|
||||
this.loadIng--;
|
||||
$A.modalError(msg);
|
||||
|
@ -1,5 +1,90 @@
|
||||
<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="head">
|
||||
<Icon v-if="taskDetail.complete_at" class="icon completed" type="md-checkmark-circle" @click="updateData('uncomplete')"/>
|
||||
@ -19,7 +104,8 @@
|
||||
:rows="1"
|
||||
:autosize="{ minRows: 1, maxRows: 8 }"
|
||||
:maxlength="255"
|
||||
@on-blur="updateData('name')"/>
|
||||
@on-blur="updateData('name')"
|
||||
@on-keydown="onNameKeydown"/>
|
||||
</div>
|
||||
<div class="desc">
|
||||
<TEditor
|
||||
@ -40,7 +126,7 @@
|
||||
<li>
|
||||
<EDropdown
|
||||
trigger="click"
|
||||
placement="bottom-start"
|
||||
placement="bottom"
|
||||
@command="updateData('priority', $event)">
|
||||
<TaskPriority :backgroundColor="taskDetail.p_color">{{taskDetail.p_name}}</TaskPriority>
|
||||
<EDropdownMenu slot="dropdown">
|
||||
@ -56,28 +142,89 @@
|
||||
</li>
|
||||
</ul>
|
||||
</FormItem>
|
||||
<FormItem v-if="getOwner()">
|
||||
<FormItem>
|
||||
<div class="item-label" slot="label">
|
||||
<i class="iconfont"></i>{{$L('负责人')}}
|
||||
</div>
|
||||
<ul class="item-content user">
|
||||
<li @click="openTransfer"><UserAvatar :userid="getOwner().userid" :size="28"/></li>
|
||||
</ul>
|
||||
<Poptip
|
||||
ref="owner"
|
||||
: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 v-if="getAssist.length > 0">
|
||||
<div class="item-label" slot="label">
|
||||
<i class="iconfont"></i>{{$L('协助人员')}}
|
||||
</div>
|
||||
<ul class="item-content user">
|
||||
<li v-for="item in getAssist" @click="openAssist"><UserAvatar :userid="item.userid" :size="28"/></li>
|
||||
</ul>
|
||||
<Poptip
|
||||
ref="assist"
|
||||
: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 v-if="taskDetail.end_at">
|
||||
<div class="item-label" slot="label">
|
||||
<i class="iconfont"></i>{{$L('截止时间')}}
|
||||
</div>
|
||||
<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>
|
||||
</FormItem>
|
||||
<FormItem v-if="hasFile">
|
||||
@ -86,12 +233,13 @@
|
||||
</div>
|
||||
<ul class="item-content file">
|
||||
<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-size">{{$A.bytesToSize(file.size)}}</div>
|
||||
</li>
|
||||
<li>
|
||||
<div class="add-button">
|
||||
<div class="add-button" @click="$refs.upload.handleClick()">
|
||||
<i class="iconfont"></i>{{$L('添加附件')}}
|
||||
</div>
|
||||
</li>
|
||||
@ -102,27 +250,9 @@
|
||||
<i class="iconfont"></i>{{$L('子任务')}}
|
||||
</div>
|
||||
<ul class="item-content subtask">
|
||||
<li v-for="task in taskDetail.sub_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>
|
||||
<TaskDetail v-for="(task, key) in taskDetail.sub_task" :key="key" :open-task="task"/>
|
||||
<li>
|
||||
<div class="add-button">
|
||||
<div class="add-button" @click="">
|
||||
<i class="iconfont"></i>{{$L('添加子任务')}}
|
||||
</div>
|
||||
</li>
|
||||
@ -132,7 +262,7 @@
|
||||
<div class="add">
|
||||
<EDropdown
|
||||
trigger="click"
|
||||
placement="bottom-start"
|
||||
placement="bottom"
|
||||
@command="">
|
||||
<div class="add-button">
|
||||
<i class="iconfont"></i>
|
||||
@ -149,6 +279,7 @@
|
||||
</EDropdown>
|
||||
</div>
|
||||
</div>
|
||||
<TaskUpload ref="upload" class="upload"/>
|
||||
</div>
|
||||
<div class="task-dialog" >
|
||||
<div class="head">
|
||||
@ -173,47 +304,6 @@
|
||||
</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>
|
||||
</template>
|
||||
|
||||
@ -222,17 +312,26 @@ import {mapState} from "vuex";
|
||||
import TEditor from "../../../components/TEditor";
|
||||
import TaskPriority from "./TaskPriority";
|
||||
import UserInput from "../../../components/UserInput";
|
||||
import TaskUpload from "./TaskUpload";
|
||||
|
||||
export default {
|
||||
name: "TaskDetail",
|
||||
components: {UserInput, TaskPriority, TEditor},
|
||||
components: {TaskUpload, UserInput, TaskPriority, TEditor},
|
||||
props: {
|
||||
openTask: {
|
||||
type: Object,
|
||||
default: () => {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
taskDetail: {},
|
||||
|
||||
transferShow: false,
|
||||
transferData: {},
|
||||
transferLoad: 0,
|
||||
ownerShow: false,
|
||||
ownerData: {},
|
||||
ownerLoad: 0,
|
||||
|
||||
assistShow: false,
|
||||
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',
|
||||
toolbar: 'uploadImages | uploadFiles | bold italic underline forecolor backcolor | codesample | preview screenload'
|
||||
},
|
||||
|
||||
timeOpen: false,
|
||||
timeValue: [],
|
||||
timeOptions: {
|
||||
shortcuts: []
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
@ -285,7 +390,7 @@ export default {
|
||||
},
|
||||
|
||||
computed: {
|
||||
...mapState(['userId', 'projectOpenTask', 'taskPriority']),
|
||||
...mapState(['userId', 'taskPriority']),
|
||||
|
||||
scrollerStyle() {
|
||||
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() {
|
||||
const {taskDetail} = this;
|
||||
return $A.isArray(taskDetail.files) && taskDetail.files.length > 0;
|
||||
@ -338,15 +455,11 @@ export default {
|
||||
},
|
||||
|
||||
getOwner() {
|
||||
return function (task) {
|
||||
if (task === undefined) {
|
||||
task = this.taskDetail;
|
||||
}
|
||||
if (!$A.isArray(task.task_user)) {
|
||||
return null;
|
||||
}
|
||||
return task.task_user.find(({owner}) => owner === 1);
|
||||
const {taskDetail} = this;
|
||||
if (!$A.isArray(taskDetail.task_user)) {
|
||||
return null;
|
||||
}
|
||||
return taskDetail.task_user.find(({owner}) => owner === 1);
|
||||
},
|
||||
|
||||
getAssist() {
|
||||
@ -407,13 +520,75 @@ export default {
|
||||
},
|
||||
|
||||
watch: {
|
||||
projectOpenTask(data) {
|
||||
this.taskDetail = $A.cloneJSON(data);
|
||||
if (data._show) this.$nextTick(this.$refs.input.focus)
|
||||
openTask: {
|
||||
handler(data) {
|
||||
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: {
|
||||
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() {
|
||||
this.innerHeight = window.innerHeight;
|
||||
},
|
||||
@ -454,6 +629,28 @@ export default {
|
||||
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) {
|
||||
switch (action) {
|
||||
case 'complete':
|
||||
@ -470,46 +667,77 @@ export default {
|
||||
this.$set(this.taskDetail, 'p_color', params.color)
|
||||
action = ['p_level', 'p_name', 'p_color'];
|
||||
break;
|
||||
case 'times':
|
||||
this.$set(this.taskDetail, 'times', [params.start_at, params.end_at])
|
||||
break;
|
||||
}
|
||||
//
|
||||
let dataJson = {task_id: this.taskDetail.id};
|
||||
($A.isArray(action) ? action : [action]).forEach(key => {
|
||||
let newData = this.taskDetail[key];
|
||||
let originalData = this.projectOpenTask[key];
|
||||
let originalData = this.openTask[key];
|
||||
if ($A.jsonStringify(newData) != $A.jsonStringify(originalData)) {
|
||||
dataJson[key] = newData;
|
||||
}
|
||||
})
|
||||
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() {
|
||||
this.$set(this.taskDetail, 'owner_userid', [this.getOwner().userid])
|
||||
this.$set(this.transferData, 'owner_userid', [this.getOwner().userid]);
|
||||
this.transferShow = true;
|
||||
archivedOrRemoveTask(type) {
|
||||
let typeTitle = this.taskDetail.parent_id > 0 ? '子任务' : '任务';
|
||||
$A.modalConfirm({
|
||||
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() {
|
||||
if ($A.jsonStringify(this.taskDetail.owner_userid) === $A.jsonStringify(this.transferData.owner_userid)) {
|
||||
openOwner() {
|
||||
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;
|
||||
}
|
||||
this.transferLoad++;
|
||||
this.ownerLoad++;
|
||||
this.$store.dispatch("taskUpdate", {
|
||||
task_id: this.taskDetail.id,
|
||||
owner: this.transferData.owner_userid
|
||||
}).then(() => {
|
||||
this.transferLoad--;
|
||||
this.transferShow = false;
|
||||
owner: this.ownerData.owner_userid
|
||||
}).then(({msg}) => {
|
||||
this.ownerLoad--;
|
||||
this.ownerShow = false;
|
||||
this.$store.dispatch("taskOne", this.taskDetail.id);
|
||||
$A.messageSuccess(msg);
|
||||
}).catch(({msg}) => {
|
||||
this.transferLoad--;
|
||||
this.transferShow = false;
|
||||
this.ownerLoad--;
|
||||
this.ownerShow = false;
|
||||
$A.modalError(msg);
|
||||
})
|
||||
},
|
||||
@ -518,7 +746,7 @@ export default {
|
||||
const list = this.getAssist.map(({userid}) => userid)
|
||||
this.$set(this.taskDetail, '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;
|
||||
},
|
||||
|
||||
@ -532,16 +760,58 @@ export default {
|
||||
this.$store.dispatch("taskUpdate", {
|
||||
task_id: this.taskDetail.id,
|
||||
assist,
|
||||
}).then(() => {
|
||||
}).then(({msg}) => {
|
||||
this.assistLoad--;
|
||||
this.assistShow = false;
|
||||
this.$store.dispatch("taskOne", this.taskDetail.id);
|
||||
$A.messageSuccess(msg);
|
||||
}).catch(({msg}) => {
|
||||
this.assistLoad--;
|
||||
this.assistShow = false;
|
||||
$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>
|
||||
|
107
resources/assets/js/pages/manage/components/TaskUpload.vue
Normal file
107
resources/assets/js/pages/manage/components/TaskUpload.vue
Normal 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>
|
81
resources/assets/js/store/actions.js
vendored
81
resources/assets/js/store/actions.js
vendored
@ -389,6 +389,11 @@ export default {
|
||||
});
|
||||
if (data.id == state.projectOpenTask.id) {
|
||||
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 data
|
||||
* @returns {Promise<unknown>}
|
||||
*/
|
||||
taskUpdate({dispatch}, data) {
|
||||
taskAdd({state, dispatch}, data) {
|
||||
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", {
|
||||
url: 'project/task/update',
|
||||
data: data,
|
||||
data: post,
|
||||
method: 'post',
|
||||
}).then(result => {
|
||||
if (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("taskData", result.data);
|
||||
resolve(result)
|
||||
}).catch(result => {
|
||||
dispatch('taskOne', data.task_id);
|
||||
dispatch('taskOne', post.task_id);
|
||||
reject(result)
|
||||
});
|
||||
});
|
||||
@ -553,7 +607,7 @@ export default {
|
||||
* 删除或归档任务
|
||||
* @param state
|
||||
* @param dispatch
|
||||
* @param data
|
||||
* @param data {task_id, type}
|
||||
* @returns {Promise<unknown>}
|
||||
*/
|
||||
taskArchivedOrRemove({state, dispatch}, data) {
|
||||
@ -565,14 +619,23 @@ export default {
|
||||
task_id,
|
||||
},
|
||||
}).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) {
|
||||
let index = column.project_task.findIndex(({id}) => id === result.data.id);
|
||||
let index = column.project_task.findIndex(({id}) => id === data.id);
|
||||
if (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);
|
||||
}).catch(result => {
|
||||
reject(result)
|
||||
|
@ -109,18 +109,49 @@
|
||||
line-height: 26px;
|
||||
.el-dropdown {
|
||||
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 {
|
||||
margin-top: 0;
|
||||
> li {
|
||||
display: inline-block;
|
||||
cursor: pointer;
|
||||
margin-top: 1px;
|
||||
cursor: pointer;
|
||||
.user-list {
|
||||
> div {
|
||||
display: inline-block;
|
||||
margin-right: 6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
&.file {
|
||||
> li {
|
||||
margin-bottom: 2px;
|
||||
.file-load {
|
||||
margin: 0;
|
||||
padding: 2px;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
.file-ext {
|
||||
width: 16px;
|
||||
}
|
||||
@ -138,17 +169,25 @@
|
||||
&.subtask {
|
||||
> li {
|
||||
align-items: flex-start;
|
||||
margin-bottom: 2px;
|
||||
margin-bottom: 4px;
|
||||
.subtask-icon {
|
||||
width: 16px;
|
||||
height: 26px;
|
||||
line-height: 26px;
|
||||
font-size: 16px;
|
||||
color: #cccccc;
|
||||
margin-right: 8px;
|
||||
margin-right: 6px;
|
||||
cursor: pointer;
|
||||
&.completed {
|
||||
color: #87d068;
|
||||
.loading {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
margin: 0;
|
||||
padding: 2px;
|
||||
}
|
||||
.ivu-icon {
|
||||
font-size: 16px;
|
||||
color: #cccccc;
|
||||
&.completed {
|
||||
color: #87d068;
|
||||
}
|
||||
}
|
||||
&.sub-icon {
|
||||
font-size: 16px;
|
||||
@ -168,7 +207,7 @@
|
||||
margin-right: 16px;
|
||||
display: flex;
|
||||
.ivu-input {
|
||||
margin: -1px 0;
|
||||
margin: -2px 0;
|
||||
padding: 4px 0;
|
||||
resize: none;
|
||||
border-color: transparent;
|
||||
@ -179,31 +218,28 @@
|
||||
}
|
||||
}
|
||||
.subtask-time {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
margin-right: 6px;
|
||||
font-size: 13px;
|
||||
height: 26px;
|
||||
line-height: 26px;
|
||||
&.overdue {
|
||||
font-weight: 600;
|
||||
color: #ed4014;
|
||||
}
|
||||
&.today {
|
||||
font-weight: 500;
|
||||
color: #ff9900;
|
||||
margin-right: 8px;
|
||||
.time {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
font-size: 13px;
|
||||
height: 26px;
|
||||
line-height: 26px;
|
||||
cursor: pointer;
|
||||
&.overdue {
|
||||
font-weight: 600;
|
||||
color: #ed4014;
|
||||
}
|
||||
&.today {
|
||||
font-weight: 500;
|
||||
color: #ff9900;
|
||||
}
|
||||
}
|
||||
}
|
||||
.subtask-avatar {
|
||||
height: 26px;
|
||||
line-height: 1;
|
||||
.avatar-box {
|
||||
> em {
|
||||
right: -2px;
|
||||
bottom: -1px;
|
||||
}
|
||||
}
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -240,6 +276,9 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
.upload {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
.task-dialog {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user