初步完成工作流

This commit is contained in:
kuaifan 2022-01-09 17:52:46 +08:00
parent 1fe4e80f82
commit b895eec69c
19 changed files with 562 additions and 120 deletions

View File

@ -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:00false表示未完成
*
* @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);
}

View File

@ -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

View File

@ -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
*/

View File

@ -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'])) {

View File

@ -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>

View File

@ -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"

View File

@ -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;

View File

@ -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,

View File

@ -4,10 +4,11 @@
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>
<div v-if="loadIng" class="loading"><Loading/></div>
<template v-else>
<Icon v-if="task.complete_at" class="completed" :type="completedIcon" />
<Icon v-else :type="icon" class="uncomplete"/>
@ -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 ? '&#xe61d;' : '&#xe61c;'"></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) {

View File

@ -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;

View File

@ -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 ? '&#xe627;' : '&#xe625;'"></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)
},

View File

@ -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

View File

@ -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: [],
});

View File

@ -67,8 +67,14 @@ state.taskId = 0;
state.taskContents = [];
state.taskFiles = [];
state.taskLogs = [];
// 任务等待状态
state.taskLoading = [];
// 任务流程信息
state.taskFlows = [];
state.taskFlowItems = [];
// 任务优先级
state.taskPriority = [];

View File

@ -31,12 +31,6 @@ $--dropdown-menuItem-hover-color: #606266;
color: #f00;
}
}
&.hover-del {
color: #f00;
> i {
color: #f00;
}
}
}
}
}

View File

@ -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 {

View File

@ -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;

View File

@ -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;
}
}
}
}
}
}
}
}

View File

@ -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 {