diff --git a/app/Http/Controllers/Api/ProjectController.php b/app/Http/Controllers/Api/ProjectController.php index 6cca4fef..3a61680a 100755 --- a/app/Http/Controllers/Api/ProjectController.php +++ b/app/Http/Controllers/Api/ProjectController.php @@ -7,10 +7,13 @@ use App\Models\Project; use App\Models\ProjectColumn; use App\Models\ProjectLog; use App\Models\ProjectTask; +use App\Models\ProjectTaskContent; +use App\Models\ProjectTaskUser; use App\Models\ProjectUser; use App\Models\User; use App\Models\WebSocketDialogMsg; use App\Module\Base; +use Carbon\Carbon; use Request; /** @@ -261,6 +264,9 @@ class ProjectController extends AbstractController 'column_id' => $item['id'], 'sort' => $index ]); + ProjectTask::whereParentId($task_id)->whereProjectId($project->id)->update([ + 'column_id' => $item['id'], + ]); $index++; } } @@ -515,12 +521,10 @@ class ProjectController extends AbstractController } /** - * 添加、修改 任务列表 + * 添加任务列表 * * @apiParam {Number} project_id 项目ID - * @apiParam {Number} [column_id] 列表ID(留空为添加列表) * @apiParam {String} name 列表名称 - * @apiParam {String} color 颜色 */ public function column__add() { @@ -532,12 +536,7 @@ class ProjectController extends AbstractController } // $project_id = intval(Request::input('project_id')); - $column_id = intval(Request::input('column_id')); $name = trim(Request::input('name')); - $color = trim(Request::input('color')); - if (empty($name)) { - return Base::retError('列表名称不能为空'); - } // 项目 $project = Project::select($this->projectSelect) ->join('project_users', 'projects.id', '=', 'project_users.project_id') @@ -548,28 +547,59 @@ class ProjectController extends AbstractController return Base::retError('项目不存在或不在成员列表内'); } // - if ($column_id > 0) { - $column = ProjectColumn::whereId($column_id)->whereProjectId($project_id)->first(); - if (empty($column)) { - return Base::retError('列表不存在'); - } - $column->name = $name; - $column->color = $color; - $column->save(); - return Base::retSuccess('修改成功', $column); - } else { - $column = ProjectColumn::createInstance([ - 'project_id' => $project->id, - 'name' => $name, - 'color' => $color, - ]); - $column->sort = intval(ProjectColumn::whereProjectId($project->id)->orderByDesc('sort')->value('sort')) + 1; - $column->save(); - // - $data = $column->toArray(); - $data['project_task'] = []; - return Base::retSuccess('添加成功', $data); + if (empty($name)) { + return Base::retError('列表名称不能为空'); } + $column = ProjectColumn::createInstance([ + 'project_id' => $project->id, + 'name' => $name, + ]); + $column->sort = intval(ProjectColumn::whereProjectId($project->id)->orderByDesc('sort')->value('sort')) + 1; + $column->save(); + // + $data = $column->toArray(); + $data['project_task'] = []; + return Base::retSuccess('添加成功', $data); + } + + /** + * 修改任务列表 + * + * @apiParam {Number} column_id 列表ID + * @apiParam {String} [name] 列表名称 + * @apiParam {String} [color] 颜色 + */ + public function column__update() + { + $user = User::authE(); + if (Base::isError($user)) { + return $user; + } else { + $user = User::IDE($user['data']); + } + // + $column_id = intval(Request::input('column_id')); + $name = trim(Request::input('name')); + $color = trim(Request::input('color')); + // 列表 + $column = ProjectColumn::whereId($column_id)->first(); + if (empty($column)) { + return Base::retError('列表不存在'); + } + // 项目 + $project = Project::select($this->projectSelect) + ->join('project_users', 'projects.id', '=', 'project_users.project_id') + ->where('projects.id', $column->project_id) + ->where('project_users.userid', $user->userid) + ->first(); + if (empty($project)) { + return Base::retError('项目不存在或不在成员列表内'); + } + // + if ($name) $column->name = $name; + if ($color) $column->color = $color; + $column->save(); + return Base::retSuccess('修改成功', $column); } /** @@ -690,4 +720,57 @@ class ProjectController extends AbstractController } return $result; } + + /** + * {post} 修改任务、子任务 + * + * @apiParam {Number} task_id 任务ID + * @apiParam {String} [name] 任务描述 + * @apiParam {String} [color] 任务描述(子任务不支持) + * @apiParam {String} [content] 任务详情(子任务不支持) + * @apiParam {Array} [times] 计划时间(格式:开始时间,结束时间;如:2020-01-01 00:00,2020-01-01 23:59) + * @apiParam {Number} [owner] 修改负责人 + */ + public function task__update() + { + $user = User::authE(); + if (Base::isError($user)) { + return $user; + } else { + $user = User::IDE($user['data']); + } + // + $task_id = Base::getPostInt('task_id'); + $name = Base::getPostValue('name'); + $color = Base::getPostValue('color'); + $content = Base::getPostValue('content'); + $times = Base::getPostValue('times'); + $owner = Base::getPostValue('owner'); + // 任务 + $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('项目不存在或不在成员列表内'); + } + // + $result = $task->updateTask([ + 'name' => $name, + 'color' => $color, + 'content' => $content, + 'times' => $times, + 'owner' => $owner, + ]); + if (Base::isSuccess($result)) { + $result['data'] = ProjectTask::with(['taskUser', 'taskTag'])->whereId($task->id)->first(); + } + return $result; + } } diff --git a/app/Http/Middleware/VerifyCsrfToken.php b/app/Http/Middleware/VerifyCsrfToken.php index 5e0bb090..638a74c3 100644 --- a/app/Http/Middleware/VerifyCsrfToken.php +++ b/app/Http/Middleware/VerifyCsrfToken.php @@ -24,6 +24,9 @@ class VerifyCsrfToken extends Middleware // 添加任务 'api/project/task/add/', + // 修改任务 + 'api/project/task/update/', + // 聊天发文件 'api/dialog/msg/sendfile/', ]; diff --git a/app/Models/ProjectTask.php b/app/Models/ProjectTask.php index 965c0cd9..92c5361c 100644 --- a/app/Models/ProjectTask.php +++ b/app/Models/ProjectTask.php @@ -309,4 +309,66 @@ class ProjectTask extends AbstractModel ]); }); } + + /** + * 修改任务 + * @param $params + * @return array|bool + */ + public function updateTask($params) + { + return AbstractModel::transaction(function () use ($params) { + $name = $params['name']; + $color = $params['color']; + $content = $params['content']; + $times = $params['times']; + $owner = $params['owner']; + // 子任务禁止修改项 + if ($this->parent_id > 0) { + $color = null; + $content = null; + } + // 名称、背景色 + if ($name) $this->name = $name; + if ($color) $this->color = $color; + if ($content) { + ProjectTaskContent::updateInsert([ + 'project_id' => $this->parent_id, + 'task_id' => $this->id, + ], [ + 'content' => $content, + ]); + } + // 计划时间 + if ($times) { + list($start, $end) = is_string($times) ? explode(",", $times) : (is_array($times) ? $times : []); + if (Base::isDate($start) && Base::isDate($end)) { + if ($start != $end) { + $this->start_at = Carbon::parse($start); + $this->end_at = Carbon::parse($end); + } + } + } + // 负责人 + 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(); + ProjectTaskUser::updateInsert([ + 'project_id' => $this->parent_id, + 'task_id' => $this->id, + 'userid' => $owner, + ], [ + 'owner' => 1, + ]); + } + } + $this->save(); + return Base::retSuccess('修改成功'); + }); + } } diff --git a/resources/assets/js/pages/manage/components/ProjectList.vue b/resources/assets/js/pages/manage/components/ProjectList.vue index 3efde5b5..a05526bf 100644 --- a/resources/assets/js/pages/manage/components/ProjectList.vue +++ b/resources/assets/js/pages/manage/components/ProjectList.vue @@ -31,18 +31,18 @@
  • - + - - {{$L('项目设置')}} - {{$L('成员管理')}} - {{$L('移交项目')}} - {{$L('删除项目')}} - - - {{$L('退出项目')}} - - + + {{$L('项目设置')}} + {{$L('成员管理')}} + {{$L('移交项目')}} + {{$L('删除项目')}} + + + {{$L('退出项目')}} + +
  • @@ -66,16 +66,20 @@
  • + :style="column.color ? {backgroundColor: column.color} : null">
    {{column.name}} ({{column.project_task.length}})
    - +
    + - - + +
    {{$L('修改')}}
    @@ -86,7 +90,7 @@
    {{$L('颜色')}} - +
    {{$L(c.name)}}
    @@ -116,15 +120,35 @@ group="task" @sort="sortUpdate" @remove="sortUpdate"> -
    +
    -
    {{item.name}}
    - +
    + - - {{$L('完成')}} - {{$L('删除')}} + + +
    + {{$L('完成')}} +
    +
    + +
    + {{$L('删除')}} +
    +
    + {{$L('颜色')}} + +
    + {{$L(c.name)}} +
    +
    @@ -461,7 +485,7 @@ export default { transferData: {}, transferLoad: 0, - colorList: [ + columnList: [ {name: '默认', color: ''}, {name: '灰色', color: '#6C6F71'}, {name: '棕色', color: '#695C56'}, @@ -472,6 +496,16 @@ export default { {name: '紫色', color: '#6B5C8D'}, {name: '粉色', color: '#8E5373'}, {name: '红色', color: '#9D6058'}, + ], + + taskList: [ + {name: '默认', color: ''}, + {name: '黄色', color: '#FCF4A7'}, + {name: '蓝色', color: '#BCF2FD'}, + {name: '绿色', color: '#C3FDAA'}, + {name: '粉色', color: '#F6C9C8'}, + {name: '紫色', color: '#BAC9FB'}, + {name: '灰色', color: '#EEEEEE'}, ] } }, @@ -740,43 +774,89 @@ export default { }, dropColumn(column, command) { - if (command === 'modify') { - this.modifyColumn(column); + if (command === 'title') { + this.titleColumn(column); } else if (command === 'delete') { this.removeColumn(column); } else if (command.name) { - this.saveColumn(column, column.name, command.color) + this.updateColumn(column, { + color: command.color + }); } }, - modifyColumn(column) { + titleColumn(column) { $A.modalInput({ value: column.name, title: "修改列表", placeholder: "输入列表名称", onOk: (value) => { if (value) { - this.saveColumn(column, value, column.color); + this.updateColumn(column, { + name: value + }); } return true; } }); }, + updateColumn(column, updata) { + if (column.loading === true) { + return; + } + this.$set(column, 'loading', true); + // + const backup = $A.cloneJSON(column); + Object.keys(updata).forEach(key => { + this.$set(column, key, updata[key]); + }); + // + $A.apiAjax({ + url: 'project/column/update', + data: Object.assign(updata, { + column_id: column.id, + }), + complete: () => { + this.$set(column, 'loading', false); + }, + error: () => { + Object.keys(updata).forEach(key => { + this.$set(column, key, backup[key]); + }); + }, + success: ({ret, data, msg}) => { + if (ret !== 1) { + Object.keys(updata).forEach(key => { + this.$set(column, key, backup[key]); + }); + } + } + }); + }, + removeColumn(column) { $A.modalConfirm({ title: '删除列表', content: '你确定要删除列表【' + column.name + '】及列表内的任务吗?', loading: true, onOk: () => { + if (column.loading === true) { + return; + } + this.$set(column, 'loading', true); + // $A.apiAjax({ url: 'project/column/delete', data: { project_id: this.projectDetail.id, column_id: column.id, }, + complete: () => { + this.$set(column, 'loading', false); + }, error: () => { this.$Modal.remove(); $A.modalAlert('网络繁忙,请稍后再试!'); @@ -785,6 +865,10 @@ export default { this.$Modal.remove(); if (ret === 1) { $A.messageSuccess(msg); + let index = this.projectDetail.project_column.findIndex(({id}) => id === column.id); + if (index > -1) { + this.projectDetail.project_column.splice(index, 1); + } this.$store.commit('getProjectDetail', this.projectDetail.id); }else{ $A.modalError(msg, 301); @@ -795,43 +879,54 @@ export default { }); }, - saveColumn(column, name, color) { - let bakName = column.name; - let bakColor = column.color; - this.$set(column, 'name', name); - this.$set(column, 'color', color); + dropTask(task, command) { + if (command === 'complete') { + // 完成 + } + else if (command === 'delete') { + // 删除 + } + else if (command.name) { + this.updateTask(task, { + color: command.color + }) + } + }, + + updateTask(task, updata) { + if (task.loading === true) { + return; + } + this.$set(task, 'loading', true); // + const backup = $A.cloneJSON(task); + Object.keys(updata).forEach(key => { + this.$set(task, key, updata[key]); + }); $A.apiAjax({ - url: 'project/column/add', - data: { - project_id: this.projectDetail.id, - column_id: column.id, - name: name, - color: color, + url: 'project/task/update', + data: Object.assign(updata, { + task_id: task.id, + }), + method: 'post', + complete: () => { + this.$set(task, 'loading', false); }, error: () => { - this.$set(column, 'name', bakName); - this.$set(column, 'color', bakColor); + Object.keys(updata).forEach(key => { + this.$set(task, key, backup[key]); + }); }, success: ({ret, data, msg}) => { if (ret !== 1) { - this.$set(column, 'name', data.name); - this.$set(column, 'color', data.color); + Object.keys(updata).forEach(key => { + this.$set(task, key, backup[key]); + }); } } }); }, - dropTask(task, command) { - switch (command) { - case 'complete': - break; - - case 'delete': - break; - } - }, - onSetting() { this.settingLoad++; $A.apiAjax({ diff --git a/resources/assets/js/store/mutations.js b/resources/assets/js/store/mutations.js index 7d74b137..6c24b432 100644 --- a/resources/assets/js/store/mutations.js +++ b/resources/assets/js/store/mutations.js @@ -114,9 +114,7 @@ export default { return; } if (state.method.isJson(state.cacheProject[project_id])) { - setTimeout(() => { - state.projectDetail = state.cacheProject[project_id]; - }); + state.projectDetail = state.cacheProject[project_id]; } state.projectDetail.id = project_id; // @@ -309,14 +307,12 @@ export default { // state.dialogMsgList = []; if (state.method.isJson(state.cacheDialogMsg[dialog_id])) { - setTimeout(() => { - let length = state.cacheDialogMsg[dialog_id].data.length; - if (length > 50) { - state.cacheDialogMsg[dialog_id].data.splice(0, length - 50); - } - state.dialogDetail = state.cacheDialogMsg[dialog_id].dialog - state.dialogMsgList = state.cacheDialogMsg[dialog_id].data - }); + let length = state.cacheDialogMsg[dialog_id].data.length; + if (length > 50) { + state.cacheDialogMsg[dialog_id].data.splice(0, length - 50); + } + state.dialogDetail = state.cacheDialogMsg[dialog_id].dialog + state.dialogMsgList = state.cacheDialogMsg[dialog_id].data } state.dialogId = dialog_id; // diff --git a/resources/assets/sass/element.scss b/resources/assets/sass/element.scss index 9e1aaeff..b73ee90a 100644 --- a/resources/assets/sass/element.scss +++ b/resources/assets/sass/element.scss @@ -13,3 +13,6 @@ $--tooltip-font-size: 14px; min-width: 100px; line-height: 34px; } +.el-dropdown-menu__item--divided:before { + height: 5px; +} diff --git a/resources/assets/sass/project-list.scss b/resources/assets/sass/project-list.scss index 786a154a..9536518a 100644 --- a/resources/assets/sass/project-list.scss +++ b/resources/assets/sass/project-list.scss @@ -229,11 +229,24 @@ display: flex; align-items: center; margin-left: 16px; + .loading, .ivu-icon { + width: 24px; + height: 24px; + display: flex; + align-items: center; + justify-content: center; padding-right: 8px; + .common-loading { + width: 16px; + height: 16px; + margin: 0; + } + } + .ivu-icon { + cursor: pointer; font-size: 16px; color: #aaaaaa; - cursor: pointer; font-weight: 600; transition: color 0.3s; &:hover { @@ -296,14 +309,6 @@ .task-head { display: flex; align-items: flex-start; - .task-choose { - margin-right: 5px; - font-size: 22px; - color: #aaaaaa; - height: 22px; - line-height: 22px; - cursor: pointer; - } .task-title { flex: 1; padding-top: 1px; @@ -315,10 +320,23 @@ word-wrap: break-word; } } + .loading, + .ivu-icon { + width: 22px; + height: 22px; + display: flex; + align-items: center; + justify-content: center; + margin-left: 8px; + .common-loading { + width: 16px; + height: 16px; + margin: 0; + } + } .ivu-icon { font-size: 22px; color: #666666; - margin-left: 8px; cursor: pointer; &:hover { color: #555555; @@ -361,7 +379,7 @@ margin-left: 0; } .common-avatar { - border: 2px solid #ffffff; + padding: 2px; } } } @@ -567,9 +585,8 @@ } } } -.project-list-column-more-content { +.project-list-more-dropdown-menu { > li { - min-width: 130px; .item { display: flex; align-items: center; @@ -584,4 +601,9 @@ } } } + &.column-more { + > li { + min-width: 130px; + } + } }