perf: 工作流支持关联任务列表自动移动

This commit is contained in:
kuaifan 2023-08-05 16:38:12 +08:00
parent fcecccc9b8
commit 7e5bbb4bb7
6 changed files with 187 additions and 39 deletions

View File

@ -390,6 +390,7 @@ class Project extends AbstractModel
$userids = Base::arrayRetainInt($item['userids'] ?: [], true);
$usertype = trim($item['usertype']);
$userlimit = intval($item['userlimit']);
$columnid = intval($item['columnid']);
if ($usertype == 'replace' && empty($userids)) {
throw new ApiException("状态[{$item['name']}]设置错误,设置流转模式时必须填写状态负责人");
}
@ -411,6 +412,7 @@ class Project extends AbstractModel
'userids' => $userids,
'usertype' => trim($item['usertype']),
'userlimit' => $userlimit,
'columnid' => $columnid,
], [], $isInsert);
if ($flow) {
$ids[] = $flow->id;
@ -545,7 +547,7 @@ class Project extends AbstractModel
$project->save();
//
if ($flow == 'open') {
$project->addFlow(Base::json2array('[{"id":-10,"name":"待处理","status":"start","turns":[-10,-11,-12,-13,-14],"userids":[],"usertype":"add","userlimit":0},{"id":-11,"name":"进行中","status":"progress","turns":[-10,-11,-12,-13,-14],"userids":[],"usertype":"add","userlimit":0},{"id":-12,"name":"待测试","status":"test","turns":[-10,-11,-12,-13,-14],"userids":[],"usertype":"add","userlimit":0},{"id":-13,"name":"已完成","status":"end","turns":[-10,-11,-12,-13,-14],"userids":[],"usertype":"add","userlimit":0},{"id":-14,"name":"已取消","status":"end","turns":[-10,-11,-12,-13,-14],"userids":[],"usertype":"add","userlimit":0}]'));
$project->addFlow(Base::json2array('[{"id":-10,"name":"待处理","status":"start","turns":[-10,-11,-12,-13,-14],"userids":[],"usertype":"add","userlimit":0,"columnid":0},{"id":-11,"name":"进行中","status":"progress","turns":[-10,-11,-12,-13,-14],"userids":[],"usertype":"add","userlimit":0,"columnid":0},{"id":-12,"name":"待测试","status":"test","turns":[-10,-11,-12,-13,-14],"userids":[],"usertype":"add","userlimit":0,"columnid":0},{"id":-13,"name":"已完成","status":"end","turns":[-10,-11,-12,-13,-14],"userids":[],"usertype":"add","userlimit":0,"columnid":0},{"id":-14,"name":"已取消","status":"end","turns":[-10,-11,-12,-13,-14],"userids":[],"usertype":"add","userlimit":0,"columnid":0}]'));
}
});
//

View File

@ -16,6 +16,7 @@ use App\Module\Base;
* @property array $userids 状态负责人ID
* @property string|null $usertype 流转模式
* @property int|null $userlimit 限制负责人
* @property int|null $columnid 对应的项目列表
* @property int|null $sort 排序
* @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at
@ -23,6 +24,7 @@ use App\Module\Base;
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlowItem newModelQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlowItem newQuery()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlowItem query()
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlowItem whereColumnid($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlowItem whereCreatedAt($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlowItem whereFlowId($value)
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlowItem whereId($value)

View File

@ -618,6 +618,9 @@ class ProjectTask extends AbstractModel
$data['assist'] = array_values(array_unique(array_diff($data['assist'], $data['owner'])));
}
}
if ($newFlowItem->columnid && ProjectColumn::whereProjectId($this->project_id)->whereId($newFlowItem->columnid)->exists()) {
$data['column_id'] = $newFlowItem->columnid;
}
$this->flow_item_id = $newFlowItem->id;
$this->flow_item_name = $newFlowItem->status . "|" . $newFlowItem->name;
$this->addLog("修改{任务}状态", [

View File

@ -0,0 +1,34 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class ProjectFlowItemsAddColumnid extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('project_flow_items', function (Blueprint $table) {
if (!Schema::hasColumn('project_flow_items', 'columnid')) {
$table->bigInteger('columnid')->nullable()->default(0)->after('userlimit')->comment('对应的项目列表');
}
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('project_flow_items', function (Blueprint $table) {
$table->dropColumn("columnid");
});
}
}

View File

@ -80,12 +80,12 @@
<EDropdown
trigger="click"
class="more"
:class="{opacity: item.userids.length > 0}"
:class="{opacity: item.userids.length > 0 || item.columnid > 0}"
@command="onMore($event, item)">
<div class="more-icon">
<EAvatar v-if="item.userids.length > 1" :size="20">{{item.userids.length}}</EAvatar>
<UserAvatar v-else-if="item.userids.length > 0" :userid="item.userids[0]" :size="20" tooltipDisabled/>
<Icon v-else type="ios-more" />
<Badge :dot="item.userids.length > 0 || item.columnid > 0">
<Icon type="ios-more" />
</Badge>
</div>
<EDropdownMenu slot="dropdown" class="taskflow-config-more-dropdown-menu">
<EDropdownItem v-if="item.userids.length > 0" command="user">
@ -95,8 +95,10 @@
</EDropdownItem>
<EDropdownItem command="user">
<div class="item">
<Icon type="md-person" />
{{$L('状态负责人')}}
<Icon type="md-settings" />
<Badge :dot="item.userids.length > 0 || item.columnid > 0">
{{$L('状态设置')}}
</Badge>
</div>
</EDropdownItem>
<EDropdownItem command="name">
@ -144,30 +146,53 @@
<Button type="primary" @click="onCreate">{{$L('创建工作流')}}</Button>
</div>
<!--状态负责人-->
<!--状态设置-->
<Modal
v-model="userShow"
:title="`${$L('状态负责人')} (${userData.name})`"
:styles="{
width: '90%',
maxWidth: '640px'
}"
:title="`${$L('状态设置')} (${settingData.name})`"
:mask-closable="false">
<Form :model="userData" label-width="auto" @submit.native.prevent>
<FormItem prop="userids" :label="$L('状态负责人')">
<UserSelect v-model="userData.userids" :project-id="projectId" :multiple-max="5" :title="$L('选择状态负责人')"/>
</FormItem>
<FormItem prop="usertype" :label="$L('流转模式')">
<RadioGroup v-model="userData.usertype">
<Radio label="add">{{$L('添加模式')}}</Radio>
<Radio label="replace">{{$L('流转模式')}}</Radio>
<Radio label="merge">{{$L('剔除模式')}}</Radio>
</RadioGroup>
<div v-if="userData.usertype=='replace'" class="form-tip">{{$L(`流转到${userData.name}时改变任务负责人为状态负责人原本的任务负责人移至协助人员`)}}</div>
<div v-else-if="userData.usertype=='merge'" class="form-tip">{{$L(`流转到【${userData.name}】时改变任务负责人为状态负责人(并保留操作状态的人员),原本的任务负责人移至协助人员。`)}}</div>
<div v-else class="form-tip">{{$L(`流转到【${userData.name}】时添加状态负责人至任务负责人。`)}}</div>
</FormItem>
<FormItem prop="userlimit" :label="$L('限制负责人')">
<iSwitch v-model="userData.userlimit" :true-value="1" :false-value="0"/>
<div v-if="userData.userlimit===1" class="form-tip">{{$L(`流转到${userData.name}[任务负责人] [项目管理员] 可以修改状态`)}}</div>
<div v-else class="form-tip">{{$L(`流转到【${userData.name}】时,[任务负责人] 和 [项目管理员] 可以修改状态。`)}}</div>
</FormItem>
<Form :model="settingData" label-width="auto" @submit.native.prevent>
<div class="workflow-setting-box">
<h3>{{ $L('状态负责人') }}</h3>
<div class="form-box">
<FormItem prop="userids" :label="$L('状态负责人')">
<UserSelect v-model="settingData.userids" :project-id="projectId" :multiple-max="5" :title="$L('选择状态负责人')"/>
</FormItem>
<FormItem prop="usertype" :label="$L('流转模式')">
<RadioGroup v-model="settingData.usertype">
<Radio label="add">{{$L('添加模式')}}</Radio>
<Radio label="replace">{{$L('流转模式')}}</Radio>
<Radio label="merge">{{$L('剔除模式')}}</Radio>
</RadioGroup>
<div v-if="settingData.usertype=='replace'" class="form-tip">{{$L(`流转到${settingData.name}时改变任务负责人为状态负责人原本的任务负责人移至协助人员`)}}</div>
<div v-else-if="settingData.usertype=='merge'" class="form-tip">{{$L(`流转到【${settingData.name}】时改变任务负责人为状态负责人(并保留操作状态的人员),原本的任务负责人移至协助人员。`)}}</div>
<div v-else class="form-tip">{{$L(`流转到【${settingData.name}】时添加状态负责人至任务负责人。`)}}</div>
</FormItem>
<FormItem prop="userlimit" :label="$L('限制负责人')">
<iSwitch v-model="settingData.userlimit" :true-value="1" :false-value="0"/>
<div v-if="settingData.userlimit===1" class="form-tip">{{$L(`流转到${settingData.name}[任务负责人] [项目管理员] 可以修改状态`)}}</div>
<div v-else class="form-tip">{{$L(`流转到【${settingData.name}】时,[任务负责人] 和 [项目管理员] 可以修改状态。`)}}</div>
</FormItem>
</div>
</div>
<div class="workflow-setting-box">
<h3>{{ $L('关联列表') }}</h3>
<div class="form-box">
<FormItem prop="usertype" :label="$L('关联列表')">
<Select v-model="settingData.columnid" :placeholder="$L('选择关联列表')" transfer>
<Option v-for="(item, index) in columnList" :value="item.id" :key="index">{{ item.name }}</Option>
</Select>
<div class="form-tip">
{{$L(`流转到【${settingData.name}】时自动将任务移动至关联列表。`)}}
<a v-if="settingData.columnid" href="javascript:void(0)" @click="settingData.columnid=0">{{$L('取消关联')}}</a>
</div>
</FormItem>
</div>
</div>
</Form>
<div slot="footer" class="adaption">
<Button type="default" @click="userShow=false">{{$L('取消')}}</Button>
@ -180,6 +205,7 @@
<script>
import Draggable from "vuedraggable";
import UserSelect from "../../../components/UserSelect.vue";
import {mapState} from "vuex";
export default {
name: "ProjectWorkflow",
@ -198,7 +224,7 @@ export default {
openIndex: "",
userShow: false,
userData: {},
settingData: {},
}
},
@ -206,6 +232,26 @@ export default {
},
computed: {
...mapState(['cacheColumns']),
columnList({projectId, cacheColumns}) {
return cacheColumns.filter(({project_id}) => {
return project_id == projectId
}).sort((a, b) => {
if (a.sort != b.sort) {
return a.sort - b.sort;
}
return a.id - b.id;
}).map(item => {
return {
id: item.id,
name: item.name,
}
});
}
},
watch: {
projectId: {
handler(val) {
@ -285,6 +331,7 @@ export default {
"userids": [],
"usertype": 'add',
"userlimit": 0,
"columnid": 0,
},
{
"id": -11,
@ -294,6 +341,7 @@ export default {
"userids": [],
"usertype": 'add',
"userlimit": 0,
"columnid": 0,
},
{
"id": -12,
@ -303,6 +351,7 @@ export default {
"userids": [],
"usertype": 'add',
"userlimit": 0,
"columnid": 0,
},
{
"id": -13,
@ -312,6 +361,7 @@ export default {
"userids": [],
"usertype": 'add',
"userlimit": 0,
"columnid": 0,
},
{
"id": -14,
@ -321,6 +371,7 @@ export default {
"userids": [],
"usertype": 'add',
"userlimit": 0,
"columnid": 0,
}
]
})
@ -367,11 +418,12 @@ export default {
onMore(name, item) {
switch (name) {
case "user":
this.$set(this.userData, 'id', item.id);
this.$set(this.userData, 'name', item.name);
this.$set(this.userData, 'userids', item.userids);
this.$set(this.userData, 'usertype', item.usertype);
this.$set(this.userData, 'userlimit', item.userlimit);
this.$set(this.settingData, 'id', item.id);
this.$set(this.settingData, 'name', item.name);
this.$set(this.settingData, 'userids', item.userids);
this.$set(this.settingData, 'usertype', item.usertype);
this.$set(this.settingData, 'userlimit', item.userlimit);
this.$set(this.settingData, 'columnid', item.columnid);
this.userShow = true;
break;
@ -388,11 +440,12 @@ export default {
onUser() {
this.userShow = false;
this.list.some(data => {
let item = data.project_flow_item.find(item => item.id == this.userData.id)
let item = data.project_flow_item.find(item => item.id == this.settingData.id)
if (item) {
this.$set(item, 'userids', this.userData.userids)
this.$set(item, 'usertype', this.userData.usertype)
this.$set(item, 'userlimit', this.userData.userlimit)
this.$set(item, 'userids', this.settingData.userids)
this.$set(item, 'usertype', this.settingData.usertype)
this.$set(item, 'userlimit', this.settingData.userlimit)
this.$set(item, 'columnid', this.settingData.columnid)
}
})
},
@ -442,6 +495,7 @@ export default {
userids: [],
usertype: 'add',
userlimit: 0,
columnid: 0,
})
data.project_flow_item.some(item => {
item.turns.push(id)

View File

@ -468,13 +468,22 @@
font-size: 18px;
font-weight: normal !important;
opacity: 0.2;
transition: opacity 0.3s;
transition: opacity,transform 0.3s;
&.opacity {
opacity: 1;
}
&:hover {
transform: scale(1.1);
}
.more-icon {
display: flex;
align-items: center;
.ivu-badge-dot {
top: 4px;
right: -6px;
width: 6px;
height: 6px;
}
}
}
}
@ -482,6 +491,47 @@
}
}
}
.workflow-setting-box {
position: relative;
padding: 44px 24px 4px;
margin: 24px 0 12px;
border-radius: 8px;
border: 1px solid #eeeeee;
transition: box-shadow 0.3s;
&:hover {
box-shadow: 0 0 10px #e6ecfa;
}
h3 {
position: absolute;
top: 0;
left: 24px;
padding: 4px 10px;
border-radius: 4px;
display: inline-block;
background-color: #ffffff;
border: 1px solid #eeeeee;
font-size: 15px;
font-weight: 500;
transform: translateY(-50%);
}
.form-box {
overflow: auto;
}
.ivu-form-item {
.ivu-form {
padding: 12px 0 0 0;
.ivu-form-item {
margin-bottom: 8px;
.ivu-form-item-content {
display: flex;
align-items: center;
}
}
}
}
}
.taskflow-config-more-dropdown-menu {
.users {
display: flex;
@ -496,6 +546,9 @@
.item {
display: flex;
align-items: center;
.ivu-badge-dot {
top: 4px;
}
}
.delete {
color: #f00;