初步完成工作流
This commit is contained in:
parent
1fe4e80f82
commit
b895eec69c
@ -170,19 +170,6 @@ class ProjectController extends AbstractController
|
||||
}
|
||||
],
|
||||
|
||||
"project_flow_item": [ // 工作流
|
||||
{
|
||||
"id": 9,
|
||||
"project_id": 2,
|
||||
"flow_id": 3,
|
||||
"name": "待处理",
|
||||
"status": "start",
|
||||
"turns": [9,10,11,12],
|
||||
"userids": [],
|
||||
"sort": 0
|
||||
}
|
||||
],
|
||||
|
||||
"task_num": 9,
|
||||
"task_complete": 0,
|
||||
"task_percent": 0,
|
||||
@ -200,7 +187,6 @@ class ProjectController extends AbstractController
|
||||
$project = Project::userProject($project_id);
|
||||
$data = array_merge($project->toArray(), $project->getTaskStatistics($user->userid), [
|
||||
'project_user' => $project->projectUser,
|
||||
'project_flow_item' => $project->projectFlowItem
|
||||
]);
|
||||
//
|
||||
return Base::retSuccess('success', $data);
|
||||
@ -1216,6 +1202,7 @@ class ProjectController extends AbstractController
|
||||
* @apiParam {String} [p_name] 优先级相关(子任务不支持)
|
||||
* @apiParam {String} [p_color] 优先级相关(子任务不支持)
|
||||
*
|
||||
* @apiParam {Number} [flow_item_id] 任务状态,工作流状态ID
|
||||
* @apiParam {String|false} [complete_at] 完成时间(如:2020-01-01 00:00,false表示未完成)
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
@ -1442,26 +1429,73 @@ class ProjectController extends AbstractController
|
||||
//
|
||||
$task_id = intval(Request::input('task_id'));
|
||||
//
|
||||
$projectTask = ProjectTask::select(['id', 'flow_item_id', 'flow_item_name', 'project_id'])->find($task_id);
|
||||
$projectTask = ProjectTask::select(['id', 'project_id', 'complete_at', 'flow_item_id', 'flow_item_name'])->find($task_id);
|
||||
if (empty($projectTask)) {
|
||||
return Base::retError('任务不存在', [ 'task_id' => $task_id ], -4002);
|
||||
}
|
||||
//
|
||||
$projectFlowItem = [];
|
||||
$turnBuilder = ProjectFlowItem::select(['id', 'name']);
|
||||
if ($projectTask->flow_item_id) {
|
||||
$projectFlowItem = ProjectFlowItem::select(['id', 'name', 'turns'])->find($projectTask->flow_item_id);
|
||||
$projectFlowItem = $projectTask->flow_item_id ? ProjectFlowItem::with(['projectFlow'])->find($projectTask->flow_item_id) : null;
|
||||
if ($projectFlowItem?->projectFlow) {
|
||||
$projectFlow = $projectFlowItem->projectFlow;
|
||||
} else {
|
||||
$projectFlow = ProjectFlow::whereProjectId($projectTask->project_id)->first();
|
||||
}
|
||||
if (empty($projectFlow)) {
|
||||
return Base::retSuccess('success', [
|
||||
'task_id' => $projectTask->id,
|
||||
'flow_item_id' => 0,
|
||||
'flow_item_name' => '',
|
||||
'turns' => [],
|
||||
]);
|
||||
}
|
||||
//
|
||||
$turns = ProjectFlowItem::select(['id', 'name', 'status', 'turns'])->whereFlowId($projectFlow->id)->orderBy('sort')->get();
|
||||
if (empty($projectFlowItem)) {
|
||||
$data = [
|
||||
'id' => 0,
|
||||
'name' => '',
|
||||
'turns' => $turnBuilder->whereProjectId($projectTask->project_id)->get()
|
||||
'task_id' => $projectTask->id,
|
||||
'flow_item_id' => 0,
|
||||
'flow_item_name' => '',
|
||||
'turns' => $turns,
|
||||
];
|
||||
} else {
|
||||
$data = $projectFlowItem->toArray();
|
||||
$data['turns'] = $turnBuilder->whereIn('id', $data['turns'])->get();
|
||||
$assign = null;
|
||||
if ($projectTask->complete_at) {
|
||||
// 赋一个结束状态
|
||||
foreach ($turns as $turn) {
|
||||
if ($turn->status == 'end' || preg_match("/complete|done|完成/i", $turn->name)) {
|
||||
$assign = $turn;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (empty($data['flow_item_id'])) {
|
||||
foreach ($turns as $turn) {
|
||||
if ($turn->status == 'end') {
|
||||
$assign = $turn;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 赋一个开始状态
|
||||
foreach ($turns as $turn) {
|
||||
if ($turn->status == 'start') {
|
||||
$assign = $turn;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ($assign) {
|
||||
$data['flow_item_id'] = $assign->id;
|
||||
$data['flow_item_name'] = $assign->name;
|
||||
}
|
||||
} else {
|
||||
$data = [
|
||||
'task_id' => $projectTask->id,
|
||||
'flow_item_id' => $projectFlowItem->id,
|
||||
'flow_item_name' => $projectFlowItem->name,
|
||||
'turns' => $turns,
|
||||
];
|
||||
}
|
||||
//
|
||||
return Base::retSuccess('success', $data);
|
||||
}
|
||||
|
||||
|
@ -26,8 +26,6 @@ use Request;
|
||||
* @property-read int $owner_userid
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\ProjectColumn[] $projectColumn
|
||||
* @property-read int|null $project_column_count
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\ProjectFlowItem[] $projectFlowItem
|
||||
* @property-read int|null $project_flow_item_count
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\ProjectLog[] $projectLog
|
||||
* @property-read int|null $project_log_count
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\ProjectUser[] $projectUser
|
||||
@ -101,14 +99,6 @@ class Project extends AbstractModel
|
||||
return $this->hasMany(ProjectUser::class, 'project_id', 'id')->orderBy('id');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function projectFlowItem(): \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
{
|
||||
return $this->hasMany(projectFlowItem::class, 'project_id', 'id')->orderBy('sort');
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询所有项目(与正常查询多返回owner字段)
|
||||
* @param self $query
|
||||
|
@ -17,6 +17,7 @@ use App\Module\Base;
|
||||
* @property int|null $sort 排序
|
||||
* @property \Illuminate\Support\Carbon|null $created_at
|
||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||
* @property-read \App\Models\ProjectFlow|null $projectFlow
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlowItem newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlowItem newQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlowItem query()
|
||||
@ -63,6 +64,14 @@ class ProjectFlowItem extends AbstractModel
|
||||
return Base::json2array($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasOne
|
||||
*/
|
||||
public function projectFlow(): \Illuminate\Database\Eloquent\Relations\HasOne
|
||||
{
|
||||
return $this->hasOne(ProjectFlow::class, 'id', 'flow_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool|null
|
||||
*/
|
||||
|
@ -473,6 +473,45 @@ class ProjectTask extends AbstractModel
|
||||
public function updateTask($data, &$updateProject = false, &$updateContent = false, &$updateSubTask = false)
|
||||
{
|
||||
AbstractModel::transaction(function () use ($data, &$updateProject, &$updateContent, &$updateSubTask) {
|
||||
// 工作流
|
||||
if (Arr::exists($data, 'flow_item_id')) {
|
||||
if ($this->flow_item_id == $data['flow_item_id']) {
|
||||
throw new ApiException('任务状态未发生改变');
|
||||
}
|
||||
$currentFlowItem = null;
|
||||
$newFlowItem = ProjectFlowItem::whereProjectId($this->project_id)->find(intval($data['flow_item_id']));
|
||||
if (empty($newFlowItem) || empty($newFlowItem->projectFlow)) {
|
||||
throw new ApiException('任务状态不存在');
|
||||
}
|
||||
if ($this->flow_item_id) {
|
||||
// 判断符合流转
|
||||
$currentFlowItem = ProjectFlowItem::find($this->flow_item_id);
|
||||
if ($currentFlowItem && !in_array($currentFlowItem->id, $newFlowItem->turns)) {
|
||||
throw new ApiException("当前状态[{$currentFlowItem->name}]不可流转到[{$newFlowItem->name}]");
|
||||
}
|
||||
}
|
||||
if ($newFlowItem->status == 'end') {
|
||||
// 判断自动完成
|
||||
if (!$this->complete_at) {
|
||||
$data['complete_at'] = date("Y-m-d H:i");
|
||||
}
|
||||
} else {
|
||||
// 判断自动打开
|
||||
if ($this->complete_at) {
|
||||
$data['complete_at'] = false;
|
||||
}
|
||||
}
|
||||
if ($newFlowItem->userids) {
|
||||
// 判断自动添加负责人
|
||||
if (!Arr::exists($data, 'owner')) {
|
||||
$data['owner'] = $this->taskUser->pluck('userid')->toArray();
|
||||
}
|
||||
$data['owner'] = array_values(array_unique(array_merge($data['owner'], $newFlowItem->userids)));
|
||||
}
|
||||
$this->flow_item_id = $newFlowItem->id;
|
||||
$this->flow_item_name = $newFlowItem->status . "|" . $newFlowItem->name;
|
||||
$this->addLog("修改{任务}状态:{$currentFlowItem?->name} => {$newFlowItem->name}");
|
||||
}
|
||||
// 状态
|
||||
if (Arr::exists($data, 'complete_at')) {
|
||||
if (Base::isDate($data['complete_at'])) {
|
||||
|
@ -151,7 +151,7 @@
|
||||
:mask-closable="false"
|
||||
:styles="{
|
||||
width: '90%',
|
||||
maxWidth: taskData.dialog_id ? '1200px' : '640px'
|
||||
maxWidth: taskData.dialog_id ? '1200px' : '700px'
|
||||
}"
|
||||
@on-visible-change="taskVisibleChange"
|
||||
footer-hide>
|
||||
|
@ -125,6 +125,9 @@ export default {
|
||||
if (data.sub_top === true) {
|
||||
task.title = `[${this.$L('子任务')}] ${task.title}`
|
||||
}
|
||||
if (data.flow_item_name) {
|
||||
task.title = `[${data.flow_item_name}] ${task.title}`
|
||||
}
|
||||
if (data.overdue) {
|
||||
task.title = `[${this.$L('超期')}] ${task.title}`
|
||||
task.color = "#f56c6c"
|
||||
|
@ -151,9 +151,14 @@
|
||||
:style="item.color ? {backgroundColor: item.color} : {}"
|
||||
@click="openTask(item)">
|
||||
<div :class="['task-head', item.desc ? 'has-desc' : '']">
|
||||
<div class="task-title"><pre>{{item.name}}</pre></div>
|
||||
<div class="task-title">
|
||||
<!--工作流状态-->
|
||||
<span v-if="item.flow_item_name" :class="item.flow_item_status" @click.stop="openMenu(item)">{{item.flow_item_name}}</span>
|
||||
<!--任务描述-->
|
||||
<pre>{{item.name}}</pre>
|
||||
</div>
|
||||
<div class="task-menu" @click.stop="">
|
||||
<TaskMenu :task="item" icon="ios-more"/>
|
||||
<TaskMenu :ref="`taskMenu_${item.id}`" :task="item" icon="ios-more"/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="item.desc" class="task-desc"><pre v-html="item.desc"></pre></div>
|
||||
@ -1108,6 +1113,13 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
openMenu(task) {
|
||||
const el = this.$refs[`taskMenu_${task.id}`];
|
||||
if (el) {
|
||||
el[0].show()
|
||||
}
|
||||
},
|
||||
|
||||
taskIsHidden(task) {
|
||||
const {name, desc, complete_at} = task;
|
||||
const {searchText} = this;
|
||||
|
@ -2,7 +2,10 @@
|
||||
<!--子任务-->
|
||||
<li v-if="ready && taskDetail.parent_id > 0">
|
||||
<div class="subtask-icon">
|
||||
<TaskMenu :task="taskDetail" :load-status="taskDetail.loading === true"/>
|
||||
<TaskMenu :ref="`taskMenu_${taskDetail.id}`" :task="taskDetail" :load-status="taskDetail.loading === true"/>
|
||||
</div>
|
||||
<div v-if="taskDetail.flow_item_name" class="subtask-flow">
|
||||
<span :class="taskDetail.flow_item_status" @click.stop="openMenu(taskDetail)">{{taskDetail.flow_item_name}}</span>
|
||||
</div>
|
||||
<div class="subtask-name">
|
||||
<Input
|
||||
@ -61,7 +64,10 @@
|
||||
<div v-else-if="ready" v-show="taskDetail.id > 0" :class="{'task-detail':true, 'open-dialog': hasOpenDialog, 'completed': taskDetail.complete_at}">
|
||||
<div class="task-info">
|
||||
<div class="head">
|
||||
<TaskMenu :task="taskDetail" class="icon" size="medium" :color-show="false" quick-completed/>
|
||||
<TaskMenu :ref="`taskMenu_${taskDetail.id}`" :task="taskDetail" class="icon" size="medium" :color-show="false"/>
|
||||
<div v-if="taskDetail.flow_item_name" class="flow">
|
||||
<span :class="taskDetail.flow_item_status" @click.stop="openMenu(taskDetail)">{{taskDetail.flow_item_name}}</span>
|
||||
</div>
|
||||
<div class="nav">
|
||||
<p v-if="projectName"><span>{{projectName}}</span></p>
|
||||
<p v-if="columnName"><span>{{columnName}}</span></p>
|
||||
@ -1096,6 +1102,13 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
openMenu(task) {
|
||||
const el = this.$refs[`taskMenu_${task.id}`];
|
||||
if (el) {
|
||||
el.show()
|
||||
}
|
||||
},
|
||||
|
||||
openNewWin() {
|
||||
let config = {
|
||||
parent: null,
|
||||
|
@ -4,7 +4,8 @@
|
||||
trigger="click"
|
||||
:size="size"
|
||||
placement="bottom"
|
||||
@command="dropTask">
|
||||
@command="dropTask"
|
||||
@visible-change="visibleChange">
|
||||
<slot name="icon">
|
||||
<div class="task-menu-icon">
|
||||
<div v-if="loadIng" class="loading"><Loading/></div>
|
||||
@ -15,6 +16,23 @@
|
||||
</div>
|
||||
</slot>
|
||||
<EDropdownMenu slot="dropdown" class="task-menu-more-dropdown">
|
||||
<li class="task-menu-more-warp" :class="size">
|
||||
<ul>
|
||||
<EDropdownItem v-if="!flow" class="load-flow" disabled>
|
||||
<div class="load-flow-warp">
|
||||
<Loading/>
|
||||
</div>
|
||||
</EDropdownItem>
|
||||
<template v-else-if="turns.length > 0">
|
||||
<EDropdownItem v-for="item in turns" :key="item.id" :command="`turn::${item.id}`">
|
||||
<div class="item flow">
|
||||
<Icon v-if="item.id == task.flow_item_id && flow.auto_assign !== true" class="check" type="md-checkmark-circle-outline" />
|
||||
<Icon v-else type="md-radio-button-off" />
|
||||
<div class="flow-name" :class="item.status">{{item.name}}</div>
|
||||
</div>
|
||||
</EDropdownItem>
|
||||
</template>
|
||||
<template v-else>
|
||||
<EDropdownItem v-if="task.complete_at" command="uncomplete">
|
||||
<div class="item red">
|
||||
<Icon type="md-checkmark-circle-outline" />{{$L('标记未完成')}}
|
||||
@ -25,7 +43,10 @@
|
||||
<Icon type="md-radio-button-off" />{{$L('完成')}}
|
||||
</div>
|
||||
</EDropdownItem>
|
||||
<EDropdownItem v-if="task.parent_id === 0" command="archived">
|
||||
</template>
|
||||
|
||||
<template v-if="task.parent_id === 0">
|
||||
<EDropdownItem :divided="turns.length > 0" command="archived">
|
||||
<div class="item">
|
||||
<Icon type="ios-filing" />{{$L('归档')}}
|
||||
</div>
|
||||
@ -35,13 +56,21 @@
|
||||
<Icon type="md-trash" />{{$L('删除')}}
|
||||
</div>
|
||||
</EDropdownItem>
|
||||
<template v-if="task.parent_id === 0 && colorShow">
|
||||
<EDropdownItem v-for="(c, k) in $store.state.taskColorList" :key="k" :divided="k==0" :command="c">
|
||||
<template v-if="colorShow">
|
||||
<EDropdownItem v-for="(c, k) in taskColorList" :key="k" :divided="k==0" :command="c">
|
||||
<div class="item">
|
||||
<i class="taskfont" :style="{color:c.color||'#f9f9f9'}" v-html="c.color == task.color ? '' : ''"></i>{{$L(c.name)}}
|
||||
</div>
|
||||
</EDropdownItem>
|
||||
</template>
|
||||
</template>
|
||||
<EDropdownItem v-else command="remove" :divided="turns.length > 0">
|
||||
<div class="item">
|
||||
<Icon type="md-trash" />{{$L('删除')}}
|
||||
</div>
|
||||
</EDropdownItem>
|
||||
</ul>
|
||||
</li>
|
||||
</EDropdownMenu>
|
||||
</EDropdown>
|
||||
</template>
|
||||
@ -66,10 +95,6 @@ export default {
|
||||
type: Boolean,
|
||||
default: true
|
||||
},
|
||||
quickCompleted: { // 如果没有任务流是快速完成
|
||||
type: Boolean,
|
||||
default: false
|
||||
},
|
||||
size: {
|
||||
type: String,
|
||||
default: 'small'
|
||||
@ -89,7 +114,7 @@ export default {
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
...mapState(['taskLoading']),
|
||||
...mapState(['taskColorList', 'taskLoading', 'taskFlows', 'taskFlowItems']),
|
||||
|
||||
loadIng() {
|
||||
if (this.loadStatus) {
|
||||
@ -97,7 +122,22 @@ export default {
|
||||
}
|
||||
const load = this.taskLoading.find(({id}) => id == this.task.id);
|
||||
return load && load.num > 0
|
||||
},
|
||||
|
||||
flow() {
|
||||
return this.taskFlows.find(({task_id}) => task_id == this.task.id);
|
||||
},
|
||||
|
||||
turns() {
|
||||
if (!this.flow) {
|
||||
return [];
|
||||
}
|
||||
let item = this.taskFlowItems.find(({id}) => id == this.flow.flow_item_id);
|
||||
if (!item) {
|
||||
return [];
|
||||
}
|
||||
return this.taskFlowItems.filter(({id}) => item.turns.includes(id))
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
show() {
|
||||
@ -118,13 +158,20 @@ export default {
|
||||
}
|
||||
return;
|
||||
}
|
||||
if ($A.leftExists(command, 'turn::')) {
|
||||
// 修改工作流状态
|
||||
let flow_item_id = $A.leftDelete(command, 'turn::');
|
||||
if (flow_item_id == this.task.flow_item_id) return;
|
||||
this.updateTask({
|
||||
flow_item_id
|
||||
})
|
||||
return;
|
||||
}
|
||||
switch (command) {
|
||||
case 'complete':
|
||||
if (this.task.complete_at) return;
|
||||
this.updateTask({
|
||||
complete_at: $A.formatDate("Y-m-d H:i:s")
|
||||
}).then(() => {
|
||||
// 已完成
|
||||
})
|
||||
break;
|
||||
|
||||
@ -132,8 +179,6 @@ export default {
|
||||
if (!this.task.complete_at) return;
|
||||
this.updateTask({
|
||||
complete_at: false
|
||||
}).then(() => {
|
||||
// 已未完成
|
||||
})
|
||||
break;
|
||||
|
||||
@ -144,6 +189,12 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
visibleChange(visible) {
|
||||
if (visible) {
|
||||
this.$store.dispatch("getTaskFlow", this.task.id);
|
||||
}
|
||||
},
|
||||
|
||||
updateTask(updata) {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (this.loadIng) {
|
||||
|
@ -11,8 +11,13 @@
|
||||
@click="getSublist(item)"/>
|
||||
<TaskMenu :ref="`taskMenu_${item.id}`" :task="item"/>
|
||||
<div class="item-title" @click="openTask(item)">
|
||||
<!--工作流状态-->
|
||||
<span v-if="item.flow_item_name" :class="item.flow_item_status" @click.stop="openMenu(item)">{{item.flow_item_name}}</span>
|
||||
<!--是否子任务-->
|
||||
<span v-if="item.sub_top === true">{{$L('子任务')}}</span>
|
||||
<!--有多少个子任务-->
|
||||
<span v-if="item.sub_my && item.sub_my.length > 0">+{{item.sub_my.length}}</span>
|
||||
<!--任务描述-->
|
||||
{{item.name}}
|
||||
</div>
|
||||
<div class="item-icons" @click="openTask(item)">
|
||||
@ -240,6 +245,13 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
openMenu(task) {
|
||||
const el = this.$refs[`taskMenu_${task.id}`];
|
||||
if (el) {
|
||||
el[0].show()
|
||||
}
|
||||
},
|
||||
|
||||
ownerUser(list) {
|
||||
return list.filter(({owner}) => owner == 1).sort((a, b) => {
|
||||
return a.id - b.id;
|
||||
|
@ -40,14 +40,19 @@
|
||||
v-if="item.p_name"
|
||||
class="priority-color"
|
||||
:style="{backgroundColor:item.p_color}"></em>
|
||||
<TaskMenu :task="item">
|
||||
<TaskMenu :ref="`taskMenu_${item.id}`" :task="item">
|
||||
<div slot="icon" class="drop-icon" @click.stop="">
|
||||
<i class="taskfont" v-html="item.complete_at ? '' : ''"></i>
|
||||
</div>
|
||||
</TaskMenu>
|
||||
<div class="item-title">
|
||||
<!--工作流状态-->
|
||||
<span v-if="item.flow_item_name" :class="item.flow_item_status" @click.stop="openMenu(item)">{{item.flow_item_name}}</span>
|
||||
<!--是否子任务-->
|
||||
<span v-if="item.sub_top === true">{{$L('子任务')}}</span>
|
||||
<!--有多少个子任务-->
|
||||
<span v-if="item.sub_my && item.sub_my.length > 0">+{{item.sub_my.length}}</span>
|
||||
<!--任务描述-->
|
||||
{{item.name}}
|
||||
</div>
|
||||
<div v-if="item.desc" class="item-icon">
|
||||
@ -140,6 +145,13 @@ export default {
|
||||
this.$store.dispatch("openTask", task)
|
||||
},
|
||||
|
||||
openMenu(task) {
|
||||
const el = this.$refs[`taskMenu_${task.id}`];
|
||||
if (el) {
|
||||
el[0].show()
|
||||
}
|
||||
},
|
||||
|
||||
expiresFormat(date) {
|
||||
return $A.countDownFormat(date, this.nowTime)
|
||||
},
|
||||
|
68
resources/assets/js/store/actions.js
vendored
68
resources/assets/js/store/actions.js
vendored
@ -524,9 +524,6 @@ export default {
|
||||
if (typeof data.project_user === "undefined") {
|
||||
data.project_user = []
|
||||
}
|
||||
if (typeof data.project_flow_item === "undefined") {
|
||||
data.project_flow_item = []
|
||||
}
|
||||
state.cacheProjects.push(data);
|
||||
}
|
||||
setTimeout(() => {
|
||||
@ -857,6 +854,9 @@ export default {
|
||||
});
|
||||
} else if ($A.isJson(data)) {
|
||||
data._time = $A.Time();
|
||||
if (data.flow_item_name && data.flow_item_name.indexOf("|") !== -1) {
|
||||
[data.flow_item_status, data.flow_item_name] = data.flow_item_name.split("|")
|
||||
}
|
||||
let index = state.cacheTasks.findIndex(({id}) => id == data.id);
|
||||
if (index > -1) {
|
||||
state.cacheTasks.splice(index, 1, Object.assign({}, state.cacheTasks[index], data));
|
||||
@ -1135,7 +1135,7 @@ export default {
|
||||
reject({msg: 'Parameter error'});
|
||||
return;
|
||||
}
|
||||
dispatch("taskLoadAdd", task_id)
|
||||
dispatch("taskLoadStart", task_id)
|
||||
dispatch("call", {
|
||||
url: 'project/task/remove',
|
||||
data: {
|
||||
@ -1143,12 +1143,12 @@ export default {
|
||||
},
|
||||
}).then(result => {
|
||||
dispatch("forgetTask", task_id)
|
||||
dispatch("taskLoadSub", task_id)
|
||||
dispatch("taskLoadEnd", task_id)
|
||||
resolve(result)
|
||||
}).catch(e => {
|
||||
console.error(e);
|
||||
dispatch("getTaskOne", task_id);
|
||||
dispatch("taskLoadSub", task_id)
|
||||
dispatch("taskLoadEnd", task_id)
|
||||
reject(e)
|
||||
});
|
||||
});
|
||||
@ -1167,7 +1167,7 @@ export default {
|
||||
reject({msg: 'Parameter error'});
|
||||
return;
|
||||
}
|
||||
dispatch("taskLoadAdd", task_id)
|
||||
dispatch("taskLoadStart", task_id)
|
||||
dispatch("call", {
|
||||
url: 'project/task/archived',
|
||||
data: {
|
||||
@ -1175,12 +1175,12 @@ export default {
|
||||
},
|
||||
}).then(result => {
|
||||
dispatch("forgetTask", task_id)
|
||||
dispatch("taskLoadSub", task_id)
|
||||
dispatch("taskLoadEnd", task_id)
|
||||
resolve(result)
|
||||
}).catch(e => {
|
||||
console.error(e);
|
||||
dispatch("getTaskOne", task_id)
|
||||
dispatch("taskLoadSub", task_id)
|
||||
dispatch("taskLoadEnd", task_id)
|
||||
reject(e)
|
||||
});
|
||||
});
|
||||
@ -1378,18 +1378,18 @@ export default {
|
||||
return new Promise(function (resolve, reject) {
|
||||
const post = $A.cloneJSON($A.date2string(data));
|
||||
//
|
||||
dispatch("taskLoadAdd", post.task_id)
|
||||
dispatch("taskLoadStart", post.task_id)
|
||||
dispatch("call", {
|
||||
url: 'project/task/update',
|
||||
data: post,
|
||||
method: 'post',
|
||||
}).then(result => {
|
||||
dispatch("taskLoadSub", post.task_id)
|
||||
dispatch("taskLoadEnd", post.task_id)
|
||||
dispatch("saveTask", result.data)
|
||||
resolve(result)
|
||||
}).catch(e => {
|
||||
console.error(e);
|
||||
dispatch("taskLoadSub", post.task_id)
|
||||
dispatch("taskLoadEnd", post.task_id)
|
||||
dispatch("getTaskOne", post.task_id);
|
||||
reject(e)
|
||||
});
|
||||
@ -1401,7 +1401,7 @@ export default {
|
||||
* @param state
|
||||
* @param task_id
|
||||
*/
|
||||
taskLoadAdd({state}, task_id) {
|
||||
taskLoadStart({state}, task_id) {
|
||||
setTimeout(() => {
|
||||
const load = state.taskLoading.find(({id}) => id == task_id)
|
||||
if (!load) {
|
||||
@ -1420,7 +1420,7 @@ export default {
|
||||
* @param state
|
||||
* @param task_id
|
||||
*/
|
||||
taskLoadSub({state}, task_id) {
|
||||
taskLoadEnd({state}, task_id) {
|
||||
const load = state.taskLoading.find(({id}) => id == task_id)
|
||||
if (!load) {
|
||||
state.taskLoading.push({
|
||||
@ -1432,6 +1432,46 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取任务流程信息
|
||||
* @param state
|
||||
* @param dispatch
|
||||
* @param task_id
|
||||
* @returns {Promise<unknown>}
|
||||
*/
|
||||
getTaskFlow({state, dispatch}, task_id) {
|
||||
return new Promise(function (resolve, reject) {
|
||||
dispatch("call", {
|
||||
url: 'project/task/flow',
|
||||
data: {
|
||||
task_id: task_id
|
||||
},
|
||||
}).then(result => {
|
||||
let {data} = result
|
||||
data.turns.some(item => {
|
||||
let index = state.taskFlowItems.findIndex(({id}) => id == item.id);
|
||||
if (index > -1) {
|
||||
state.taskFlowItems.splice(index, 1, item);
|
||||
} else {
|
||||
state.taskFlowItems.push(item);
|
||||
}
|
||||
})
|
||||
//
|
||||
delete data.turns;
|
||||
let index = state.taskFlows.findIndex(({task_id}) => task_id == data.task_id);
|
||||
if (index > -1) {
|
||||
state.taskFlows.splice(index, 1, data);
|
||||
} else {
|
||||
state.taskFlows.push(data);
|
||||
}
|
||||
resolve(result)
|
||||
}).catch(e => {
|
||||
console.error(e);
|
||||
reject(e);
|
||||
});
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取任务优先级预设数据
|
||||
* @param state
|
||||
|
4
resources/assets/js/store/getters.js
vendored
4
resources/assets/js/store/getters.js
vendored
@ -84,6 +84,10 @@ export default {
|
||||
end_at: task.end_at,
|
||||
complete_at: task.complete_at,
|
||||
|
||||
flow_item_id: task.flow_item_id,
|
||||
flow_item_name: task.flow_item_name,
|
||||
flow_item_status: task.flow_item_status,
|
||||
|
||||
sub_top: true,
|
||||
sub_my: [],
|
||||
});
|
||||
|
6
resources/assets/js/store/state.js
vendored
6
resources/assets/js/store/state.js
vendored
@ -67,8 +67,14 @@ state.taskId = 0;
|
||||
state.taskContents = [];
|
||||
state.taskFiles = [];
|
||||
state.taskLogs = [];
|
||||
|
||||
// 任务等待状态
|
||||
state.taskLoading = [];
|
||||
|
||||
// 任务流程信息
|
||||
state.taskFlows = [];
|
||||
state.taskFlowItems = [];
|
||||
|
||||
// 任务优先级
|
||||
state.taskPriority = [];
|
||||
|
||||
|
6
resources/assets/sass/element.scss
vendored
6
resources/assets/sass/element.scss
vendored
@ -31,12 +31,6 @@ $--dropdown-menuItem-hover-color: #606266;
|
||||
color: #f00;
|
||||
}
|
||||
}
|
||||
&.hover-del {
|
||||
color: #f00;
|
||||
> i {
|
||||
color: #f00;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -352,7 +352,35 @@
|
||||
.task-title {
|
||||
flex: 1;
|
||||
padding-top: 1px;
|
||||
> span {
|
||||
float: left;
|
||||
font-size: 12px;
|
||||
height: 20px;
|
||||
line-height: 18px;
|
||||
padding: 0 3px;
|
||||
border-radius: 3px;
|
||||
color: #8bcf70;
|
||||
border: 1px solid #8bcf70;
|
||||
margin-right: 4px;
|
||||
text-align: center;
|
||||
&.start {
|
||||
background-color: rgba(38, 38, 38, 0.05);
|
||||
border-color: rgba(38, 38, 38, 0.05);
|
||||
color: #595959;
|
||||
}
|
||||
&.progress {
|
||||
background-color: rgba(27, 154, 238, 0.1);
|
||||
border-color: rgba(27, 154, 238, 0.1);
|
||||
color: #0171c2;
|
||||
}
|
||||
&.end {
|
||||
background-color: rgba(21, 173, 49, 0.1);
|
||||
border-color: rgba(21, 173, 49, 0.1);
|
||||
color: #038a24;
|
||||
}
|
||||
}
|
||||
> pre {
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
line-height: 1.5;
|
||||
@ -682,16 +710,32 @@
|
||||
> span {
|
||||
font-size: 12px;
|
||||
height: 18px;
|
||||
min-width: 20px;
|
||||
line-height: 16px;
|
||||
padding: 0 3px;
|
||||
padding: 0 2px;
|
||||
border-radius: 3px;
|
||||
color: #8bcf70;
|
||||
background-color: rgba(139, 207, 112, 0);
|
||||
border: 1px solid #8bcf70;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
margin-top: 3px;
|
||||
margin-right: 3px;
|
||||
text-align: center;
|
||||
&.start {
|
||||
background-color: rgba(38, 38, 38, 0.05);
|
||||
border-color: rgba(38, 38, 38, 0.05);
|
||||
color: #595959;
|
||||
}
|
||||
&.progress {
|
||||
background-color: rgba(27, 154, 238, 0.1);
|
||||
border-color: rgba(27, 154, 238, 0.1);
|
||||
color: #0171c2;
|
||||
}
|
||||
&.end {
|
||||
background-color: rgba(21, 173, 49, 0.1);
|
||||
border-color: rgba(21, 173, 49, 0.1);
|
||||
color: #038a24;
|
||||
}
|
||||
}
|
||||
}
|
||||
.item-icons {
|
||||
|
@ -45,6 +45,37 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
.flow {
|
||||
margin-left: 18px;
|
||||
margin-right: -3px;
|
||||
> span {
|
||||
font-size: 14px;
|
||||
height: 26px;
|
||||
line-height: 24px;
|
||||
padding: 0 8px;
|
||||
border-radius: 4px;
|
||||
color: #8bcf70;
|
||||
border: 1px solid #8bcf70;
|
||||
display: inline-block;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
&.start {
|
||||
background-color: rgba(38, 38, 38, 0.05);
|
||||
border-color: rgba(38, 38, 38, 0.05);
|
||||
color: #595959;
|
||||
}
|
||||
&.progress {
|
||||
background-color: rgba(27, 154, 238, 0.1);
|
||||
border-color: rgba(27, 154, 238, 0.1);
|
||||
color: #0171c2;
|
||||
}
|
||||
&.end {
|
||||
background-color: rgba(21, 173, 49, 0.1);
|
||||
border-color: rgba(21, 173, 49, 0.1);
|
||||
color: #038a24;
|
||||
}
|
||||
}
|
||||
}
|
||||
.nav {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
@ -54,6 +85,9 @@
|
||||
width: 0;
|
||||
height: 40px;
|
||||
overflow: auto;
|
||||
&::-webkit-scrollbar {
|
||||
display: none
|
||||
}
|
||||
> p {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
@ -263,11 +297,12 @@
|
||||
.subtask-time {
|
||||
.clock {
|
||||
transform: translateX(0);
|
||||
opacity: 0.8;
|
||||
opacity: 0.7;
|
||||
}
|
||||
}
|
||||
}
|
||||
.subtask-icon {
|
||||
padding-top: 1px;
|
||||
width: 16px;
|
||||
height: 26px;
|
||||
line-height: 26px;
|
||||
@ -276,9 +311,39 @@
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
.subtask-flow {
|
||||
> span {
|
||||
font-size: 12px;
|
||||
height: 18px;
|
||||
min-width: 20px;
|
||||
line-height: 16px;
|
||||
padding: 0 2px;
|
||||
border-radius: 3px;
|
||||
color: #8bcf70;
|
||||
border: 1px solid #8bcf70;
|
||||
display: inline-block;
|
||||
margin-right: 3px;
|
||||
text-align: center;
|
||||
&.start {
|
||||
background-color: rgba(38, 38, 38, 0.05);
|
||||
border-color: rgba(38, 38, 38, 0.05);
|
||||
color: #595959;
|
||||
}
|
||||
&.progress {
|
||||
background-color: rgba(27, 154, 238, 0.1);
|
||||
border-color: rgba(27, 154, 238, 0.1);
|
||||
color: #0171c2;
|
||||
}
|
||||
&.end {
|
||||
background-color: rgba(21, 173, 49, 0.1);
|
||||
border-color: rgba(21, 173, 49, 0.1);
|
||||
color: #038a24;
|
||||
}
|
||||
}
|
||||
}
|
||||
.subtask-name {
|
||||
flex: 1;
|
||||
margin-right: 16px;
|
||||
margin-right: 8px;
|
||||
display: flex;
|
||||
.ivu-input {
|
||||
margin: -2px 0;
|
||||
|
@ -27,12 +27,25 @@
|
||||
}
|
||||
|
||||
.task-menu-more-dropdown {
|
||||
> li {
|
||||
&.task-menu-more-warp {
|
||||
list-style: none;
|
||||
|
||||
> ul {
|
||||
max-height: 320px;
|
||||
overflow: auto;
|
||||
|
||||
&::-webkit-scrollbar {
|
||||
display: none
|
||||
}
|
||||
|
||||
> li {
|
||||
.item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
> i {
|
||||
flex-shrink: 0;
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
line-height: 18px;
|
||||
@ -46,5 +59,90 @@
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.flow {
|
||||
padding: 4px 0;
|
||||
|
||||
> i {
|
||||
margin-right: 3px;
|
||||
|
||||
&.check {
|
||||
color: $primary-color;
|
||||
}
|
||||
}
|
||||
|
||||
.flow-name {
|
||||
border-radius: 4px;
|
||||
white-space: nowrap;
|
||||
padding: 0 5px;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
font-size: 12px;
|
||||
background: #f4f4f4;
|
||||
color: #595959;
|
||||
|
||||
&.start {
|
||||
background-color: rgba(38, 38, 38, 0.05);
|
||||
border-color: rgba(38, 38, 38, 0.05);
|
||||
color: #595959;
|
||||
}
|
||||
|
||||
&.progress {
|
||||
background-color: rgba(27, 154, 238, 0.1);
|
||||
border-color: rgba(27, 154, 238, 0.1);
|
||||
color: #0171c2;
|
||||
}
|
||||
|
||||
&.end {
|
||||
background-color: rgba(21, 173, 49, 0.1);
|
||||
border-color: rgba(21, 173, 49, 0.1);
|
||||
color: #038a24;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.load-flow {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
padding: 8px;
|
||||
|
||||
.load-flow-warp {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.medium {
|
||||
> ul {
|
||||
> li {
|
||||
.flow {
|
||||
.flow-name {
|
||||
height: 24px;
|
||||
line-height: 24px;
|
||||
padding: 0 7px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.large {
|
||||
> ul {
|
||||
> li {
|
||||
.flow {
|
||||
.flow-name {
|
||||
font-size: 13px;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
padding: 0 8px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
24
resources/assets/sass/pages/page-dashboard.scss
vendored
24
resources/assets/sass/pages/page-dashboard.scss
vendored
@ -136,16 +136,32 @@
|
||||
> span {
|
||||
font-size: 12px;
|
||||
height: 18px;
|
||||
min-width: 20px;
|
||||
line-height: 16px;
|
||||
padding: 0 3px;
|
||||
padding: 0 2px;
|
||||
border-radius: 3px;
|
||||
color: #8bcf70;
|
||||
background-color: rgba(139, 207, 112, 0);
|
||||
border: 1px solid #8bcf70;
|
||||
display: inline-block;
|
||||
vertical-align: top;
|
||||
margin-top: 2px;
|
||||
margin-right: 2px;
|
||||
margin-top: 3px;
|
||||
margin-right: 3px;
|
||||
text-align: center;
|
||||
&.start {
|
||||
background-color: rgba(38, 38, 38, 0.05);
|
||||
border-color: rgba(38, 38, 38, 0.05);
|
||||
color: #595959;
|
||||
}
|
||||
&.progress {
|
||||
background-color: rgba(27, 154, 238, 0.1);
|
||||
border-color: rgba(27, 154, 238, 0.1);
|
||||
color: #0171c2;
|
||||
}
|
||||
&.end {
|
||||
background-color: rgba(21, 173, 49, 0.1);
|
||||
border-color: rgba(21, 173, 49, 0.1);
|
||||
color: #038a24;
|
||||
}
|
||||
}
|
||||
}
|
||||
.item-icon {
|
||||
|
Loading…
x
Reference in New Issue
Block a user