feat: 添加工作流
This commit is contained in:
parent
8a2571f514
commit
bbd394272f
@ -89,6 +89,7 @@ git pull
|
||||
./cmd uninstall
|
||||
./cmd install
|
||||
./cmd mysql recovery
|
||||
./cmd artisan migrate
|
||||
```
|
||||
|
||||
## Uninstall
|
||||
|
@ -89,6 +89,7 @@ git pull
|
||||
./cmd uninstall
|
||||
./cmd install
|
||||
./cmd mysql recovery
|
||||
./cmd artisan migrate
|
||||
```
|
||||
|
||||
## 卸载项目
|
||||
|
@ -6,6 +6,8 @@ use App\Exceptions\ApiException;
|
||||
use App\Models\AbstractModel;
|
||||
use App\Models\Project;
|
||||
use App\Models\ProjectColumn;
|
||||
use App\Models\ProjectFlow;
|
||||
use App\Models\ProjectFlowItem;
|
||||
use App\Models\ProjectInvite;
|
||||
use App\Models\ProjectLog;
|
||||
use App\Models\ProjectTask;
|
||||
@ -1406,6 +1408,163 @@ class ProjectController extends AbstractController
|
||||
return Base::retSuccess('删除成功', ['id' => $task->id]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/project/flow/list 29. 工作流列表
|
||||
*
|
||||
* @apiDescription 需要token身份(限:项目负责人)
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup project
|
||||
* @apiName flow__list
|
||||
*
|
||||
* @apiParam {Number} project_id 项目ID
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function flow__list()
|
||||
{
|
||||
User::auth();
|
||||
//
|
||||
$project_id = intval(Request::input('project_id'));
|
||||
//
|
||||
$project = Project::userProject($project_id, true, true);
|
||||
//
|
||||
$list = ProjectFlow::with(['ProjectFlowItem'])->whereProjectId($project->id)->get();
|
||||
return Base::retSuccess('success', $list);
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {post} api/project/flow/save 29. 保存工作流
|
||||
*
|
||||
* @apiDescription 需要token身份(限:项目负责人)
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup project
|
||||
* @apiName flow__save
|
||||
*
|
||||
* @apiParam {Number} project_id 项目ID
|
||||
* @apiParam {Array} flows 工作流数据
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function flow__save()
|
||||
{
|
||||
User::auth();
|
||||
//
|
||||
$project_id = intval(Base::getContentValue('project_id'));
|
||||
$flows = Base::getContentValue('flows');
|
||||
//
|
||||
if (!is_array($flows)) {
|
||||
return Base::retError('参数错误');
|
||||
}
|
||||
//
|
||||
$project = Project::userProject($project_id, true, true);
|
||||
//
|
||||
return AbstractModel::transaction(function() use ($project, $flows) {
|
||||
$projectFlow = ProjectFlow::whereProjectId($project->id)->first();
|
||||
if (empty($projectFlow)) {
|
||||
$projectFlow = ProjectFlow::createInstance([
|
||||
'project_id' => $project->id,
|
||||
'name' => 'Default'
|
||||
]);
|
||||
if (!$projectFlow->save()) {
|
||||
throw new ApiException('工作流创建失败');
|
||||
}
|
||||
}
|
||||
//
|
||||
$ids = [];
|
||||
$idc = [];
|
||||
$hasStart = false;
|
||||
$hasEnd = false;
|
||||
foreach ($flows as $item) {
|
||||
$id = intval($item['id']);
|
||||
$turns = Base::arrayRetainInt($item['turns'] ?: [], true);
|
||||
$userids = Base::arrayRetainInt($item['userids'] ?: [], true);
|
||||
$flow = ProjectFlowItem::updateInsert([
|
||||
'id' => $id,
|
||||
'project_id' => $project->id,
|
||||
'flow_id' => $projectFlow->id,
|
||||
], [
|
||||
'name' => trim($item['name']),
|
||||
'status' => trim($item['status']),
|
||||
'sort' => intval($item['sort']),
|
||||
'turns' => $turns,
|
||||
'userids' => $userids,
|
||||
]);
|
||||
if ($flow) {
|
||||
$ids[] = $flow->id;
|
||||
if ($flow->id != $id) {
|
||||
$idc[$id] = $flow->id;
|
||||
}
|
||||
if ($flow->status == 'start') {
|
||||
$hasStart = true;
|
||||
}
|
||||
if ($flow->status == 'end') {
|
||||
$hasEnd = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$hasStart) {
|
||||
throw new ApiException('至少需要1个开始状态');
|
||||
}
|
||||
if (!$hasEnd) {
|
||||
throw new ApiException('至少需要1个结束状态');
|
||||
}
|
||||
ProjectFlowItem::whereFlowId($projectFlow->id)->whereNotIn('id', $ids)->delete();
|
||||
//
|
||||
$projectFlow = ProjectFlow::with(['projectFlowItem'])->whereProjectId($project->id)->find($projectFlow->id);
|
||||
$itemIds = $projectFlow->projectFlowItem->pluck('id')->toArray();
|
||||
foreach ($projectFlow->projectFlowItem as $item) {
|
||||
$turns = $item->turns;
|
||||
foreach ($idc as $oid => $nid) {
|
||||
if (in_array($oid, $turns)) {
|
||||
$turns = array_diff($turns, [$oid]);
|
||||
$turns[] = $nid;
|
||||
}
|
||||
}
|
||||
if (!in_array($item->id, $turns)) {
|
||||
$turns[] = $item->id;
|
||||
}
|
||||
$turns = array_values(array_filter(array_unique(array_intersect($turns, $itemIds))));
|
||||
sort($turns);
|
||||
$item->turns = $turns;
|
||||
ProjectFlowItem::whereId($item->id)->update([ 'turns' => Base::array2json($turns) ]);
|
||||
}
|
||||
return Base::retSuccess('保存成功', $projectFlow);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/project/flow/delete 29. 删除工作流
|
||||
*
|
||||
* @apiDescription 需要token身份(限:项目负责人)
|
||||
* @apiVersion 1.0.0
|
||||
* @apiGroup project
|
||||
* @apiName flow__delete
|
||||
*
|
||||
* @apiParam {Number} project_id 项目ID
|
||||
*
|
||||
* @apiSuccess {Number} ret 返回状态码(1正确、0错误)
|
||||
* @apiSuccess {String} msg 返回信息(错误描述)
|
||||
* @apiSuccess {Object} data 返回数据
|
||||
*/
|
||||
public function flow__delete()
|
||||
{
|
||||
User::auth();
|
||||
//
|
||||
$project_id = intval(Request::input('project_id'));
|
||||
//
|
||||
$project = Project::userProject($project_id, true, true);
|
||||
//
|
||||
return AbstractModel::transaction(function() use ($project) {
|
||||
ProjectFlow::whereProjectId($project->id)->delete();
|
||||
ProjectFlowItem::whereProjectId($project->id)->delete();
|
||||
return Base::retSuccess('删除成功');
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* @api {get} api/project/log/lists 30. 获取项目、任务日志
|
||||
*
|
||||
|
@ -24,6 +24,9 @@ class VerifyCsrfToken extends Middleware
|
||||
// 添加任务
|
||||
'api/project/task/add/',
|
||||
|
||||
// 保存工作流
|
||||
'api/project/flow/save/',
|
||||
|
||||
// 修改任务
|
||||
'api/project/task/update/',
|
||||
|
||||
|
@ -140,7 +140,11 @@ class AbstractModel extends Model
|
||||
$row = static::where($where)->first();
|
||||
if (empty($row)) {
|
||||
$row = new static;
|
||||
$row->updateInstance(array_merge($where, $insert ?: $update));
|
||||
$array = array_merge($where, $insert ?: $update);
|
||||
if (isset($array[$row->primaryKey])) {
|
||||
unset($array[$row->primaryKey]);
|
||||
}
|
||||
$row->updateInstance($array);
|
||||
} elseif ($update) {
|
||||
$row->updateInstance($update);
|
||||
}
|
||||
|
@ -30,6 +30,8 @@ use Request;
|
||||
* @property-read int|null $project_log_count
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\ProjectUser[] $projectUser
|
||||
* @property-read int|null $project_user_count
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\ProjectFlow[] $projectFlowItem
|
||||
* @property-read int|null $project_flow_item_count
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|Project allData($userid = null)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|Project authData($userid = null, $owner = null)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|Project newModelQuery()
|
||||
@ -99,6 +101,14 @@ 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
|
||||
|
93
app/Models/ProjectFlow.php
Normal file
93
app/Models/ProjectFlow.php
Normal file
@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Module\Base;
|
||||
|
||||
/**
|
||||
* App\Models\ProjectFlow
|
||||
*
|
||||
* @property int $id
|
||||
* @property int|null $project_id 项目ID
|
||||
* @property string|null $name 流程名称
|
||||
* @property \Illuminate\Support\Carbon|null $created_at
|
||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\ProjectFlowItem[] $projectFlowItem
|
||||
* @property-read int|null $project_flow_item_count
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlow newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlow newQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlow query()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlow whereCreatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlow whereId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlow whereName($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlow whereProjectId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlow whereUpdatedAt($value)
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class ProjectFlow extends AbstractModel
|
||||
{
|
||||
/**
|
||||
* @return \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
*/
|
||||
public function projectFlowItem(): \Illuminate\Database\Eloquent\Relations\HasMany
|
||||
{
|
||||
return $this->hasMany(ProjectFlowItem::class, 'flow_id', 'id')->orderBy('sort');
|
||||
}
|
||||
|
||||
public static function addFlow()
|
||||
{
|
||||
AbstractModel::transaction(function() {
|
||||
$projectFlow = ProjectFlow::whereProjectId($project->id)->first();
|
||||
if (empty($projectFlow)) {
|
||||
$projectFlow = ProjectFlow::createInstance([
|
||||
'project_id' => $project->id,
|
||||
'name' => 'Default'
|
||||
]);
|
||||
if (!$projectFlow->save()) {
|
||||
return Base::retError('工作流创建失败');
|
||||
}
|
||||
}
|
||||
//
|
||||
$ids = [];
|
||||
$idc = [];
|
||||
$hasStart = false;
|
||||
$hasEnd = false;
|
||||
foreach ($flows as $item) {
|
||||
$id = intval($item['id']);
|
||||
$turns = Base::arrayRetainInt($item['turns'] ?: [], true);
|
||||
$userids = Base::arrayRetainInt($item['userids'] ?: [], true);
|
||||
$flow = ProjectFlowItem::updateInsert([
|
||||
'id' => $id,
|
||||
'project_id' => $project->id,
|
||||
'flow_id' => $projectFlow->id,
|
||||
], [
|
||||
'name' => trim($item['name']),
|
||||
'status' => trim($item['status']),
|
||||
'sort' => intval($item['sort']),
|
||||
'turns' => $turns,
|
||||
'userids' => $userids,
|
||||
]);
|
||||
if ($flow) {
|
||||
$ids[] = $flow->id;
|
||||
if ($flow->id != $id) {
|
||||
$idc[$id] = $flow->id;
|
||||
}
|
||||
if ($flow->status == 'start') {
|
||||
$hasStart = true;
|
||||
}
|
||||
if ($flow->status == 'end') {
|
||||
$hasEnd = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!$hasStart) {
|
||||
return Base::retError('至少需要1个开始状态');
|
||||
}
|
||||
if (!$hasEnd) {
|
||||
return Base::retError('至少需要1个结束状态');
|
||||
}
|
||||
ProjectFlowItem::whereFlowId($projectFlow->id)->whereNotIn('id', $ids)->delete();
|
||||
});
|
||||
return Base::retSuccess("success");
|
||||
}
|
||||
}
|
60
app/Models/ProjectFlowItem.php
Normal file
60
app/Models/ProjectFlowItem.php
Normal file
@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Module\Base;
|
||||
|
||||
/**
|
||||
* App\Models\ProjectFlowItem
|
||||
*
|
||||
* @property int $id
|
||||
* @property int|null $project_id 项目ID
|
||||
* @property int|null $flow_id 流程ID
|
||||
* @property string|null $name 名称
|
||||
* @property string|null $status 状态
|
||||
* @property array $turns 可流转
|
||||
* @property array $userids 自动负责人ID
|
||||
* @property int|null $sort 排序
|
||||
* @property \Illuminate\Support\Carbon|null $created_at
|
||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||
* @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 whereCreatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlowItem whereFlowId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlowItem whereId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlowItem whereName($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlowItem whereProjectId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlowItem whereSort($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlowItem whereStatus($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlowItem whereTurns($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlowItem whereUpdatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ProjectFlowItem whereUserids($value)
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class ProjectFlowItem extends AbstractModel
|
||||
{
|
||||
/**
|
||||
* @param $value
|
||||
* @return array
|
||||
*/
|
||||
public function getTurnsAttribute($value)
|
||||
{
|
||||
if (is_array($value)) {
|
||||
return $value;
|
||||
}
|
||||
return Base::json2array($value);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $value
|
||||
* @return array
|
||||
*/
|
||||
public function getUseridsAttribute($value)
|
||||
{
|
||||
if (is_array($value)) {
|
||||
return $value;
|
||||
}
|
||||
return Base::json2array($value);
|
||||
}
|
||||
}
|
@ -53,6 +53,8 @@ use Carbon\Carbon;
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|User whereUserid($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|User whereUserimg($value)
|
||||
* @mixin \Eloquent
|
||||
* @property string|null $disable_at 禁用时间
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|User whereDisableAt($value)
|
||||
*/
|
||||
class User extends AbstractModel
|
||||
{
|
||||
|
@ -830,13 +830,16 @@ class Base
|
||||
/**
|
||||
* 数组只保留数字的
|
||||
* @param $array
|
||||
* @param bool $int 是否格式化值
|
||||
* @return array
|
||||
*/
|
||||
public static function arrayRetainInt($array)
|
||||
public static function arrayRetainInt($array, $int = false)
|
||||
{
|
||||
foreach ($array as $k => $v) {
|
||||
if (!is_numeric($v)) {
|
||||
unset($array[$k]);
|
||||
} elseif ($int === true) {
|
||||
$array[$k] = intval($v);
|
||||
}
|
||||
}
|
||||
return array_values($array);
|
||||
|
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateProjectFlowItemsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('project_flow_items', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->bigInteger('project_id')->nullable()->default(0)->comment('项目ID');
|
||||
$table->bigInteger('flow_id')->nullable()->default(0)->comment('流程ID');
|
||||
$table->string('name', 50)->nullable()->default('')->comment('名称');
|
||||
$table->string('status', 20)->nullable()->default('')->comment('状态');
|
||||
$table->string('turns')->nullable()->default('')->comment('可流转');
|
||||
$table->string('userids')->nullable()->default('')->comment('自动负责人ID');
|
||||
$table->integer('sort')->nullable()->comment('排序');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('project_flow_items');
|
||||
}
|
||||
}
|
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class CreateProjectFlowsTable extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::create('project_flows', function (Blueprint $table) {
|
||||
$table->bigIncrements('id');
|
||||
$table->bigInteger('project_id')->nullable()->default(0)->comment('项目ID');
|
||||
$table->string('name', 50)->nullable()->default('')->comment('流程名称');
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::dropIfExists('project_flows');
|
||||
}
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
use Illuminate\Database\Migrations\Migration;
|
||||
use Illuminate\Database\Schema\Blueprint;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
class ProjectTasksAddFlowItemId extends Migration
|
||||
{
|
||||
/**
|
||||
* Run the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function up()
|
||||
{
|
||||
Schema::table('project_tasks', function (Blueprint $table) {
|
||||
if (!Schema::hasColumn('project_tasks', 'flow_item_id')) {
|
||||
$table->bigInteger('flow_item_id')->nullable()->default(0)->after('dialog_id')->comment('工作流状态ID');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Reverse the migrations.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function down()
|
||||
{
|
||||
Schema::table('project_tasks', function (Blueprint $table) {
|
||||
$table->dropColumn("flow_item_id");
|
||||
});
|
||||
}
|
||||
}
|
@ -51,7 +51,8 @@
|
||||
<Icon class="menu-icon" type="ios-more" />
|
||||
<EDropdownMenu v-if="projectData.owner_userid === userId" slot="dropdown">
|
||||
<EDropdownItem command="setting">{{$L('项目设置')}}</EDropdownItem>
|
||||
<EDropdownItem command="user">{{$L('成员管理')}}</EDropdownItem>
|
||||
<EDropdownItem command="workflow">{{$L('工作流设置')}}</EDropdownItem>
|
||||
<EDropdownItem command="user" divided>{{$L('成员管理')}}</EDropdownItem>
|
||||
<EDropdownItem command="invite">{{$L('邀请链接')}}</EDropdownItem>
|
||||
<EDropdownItem command="log" divided>{{$L('项目动态')}}</EDropdownItem>
|
||||
<EDropdownItem command="archived_task">{{$L('已归档任务')}}</EDropdownItem>
|
||||
@ -431,6 +432,14 @@
|
||||
</div>
|
||||
</Modal>
|
||||
|
||||
<!--工作流程设置-->
|
||||
<DrawerOverlay
|
||||
v-model="workflowShow"
|
||||
placement="right"
|
||||
:size="1200">
|
||||
<ProjectWorkflow v-if="workflowShow" :project-id="projectId"/>
|
||||
</DrawerOverlay>
|
||||
|
||||
<!--查看项目动态-->
|
||||
<DrawerOverlay
|
||||
v-model="logShow"
|
||||
@ -465,10 +474,12 @@ import TaskRow from "./TaskRow";
|
||||
import TaskArchived from "./TaskArchived";
|
||||
import ProjectLog from "./ProjectLog";
|
||||
import DrawerOverlay from "../../../components/DrawerOverlay";
|
||||
import ProjectWorkflow from "./ProjectWorkflow";
|
||||
|
||||
export default {
|
||||
name: "ProjectList",
|
||||
components: {
|
||||
ProjectWorkflow,
|
||||
DrawerOverlay,
|
||||
ProjectLog, TaskArchived, TaskRow, Draggable, TaskAddSimple, UserInput, TaskAdd, TaskPriority},
|
||||
data() {
|
||||
@ -510,6 +521,7 @@ export default {
|
||||
transferData: {},
|
||||
transferLoad: 0,
|
||||
|
||||
workflowShow: false,
|
||||
logShow: false,
|
||||
archivedTaskShow: false,
|
||||
|
||||
@ -1201,6 +1213,10 @@ export default {
|
||||
this.inviteGet()
|
||||
break;
|
||||
|
||||
case "workflow":
|
||||
this.workflowShow = true;
|
||||
break;
|
||||
|
||||
case "log":
|
||||
this.logShow = true;
|
||||
break;
|
||||
|
438
resources/assets/js/pages/manage/components/ProjectWorkflow.vue
Normal file
438
resources/assets/js/pages/manage/components/ProjectWorkflow.vue
Normal file
@ -0,0 +1,438 @@
|
||||
<template>
|
||||
<div class="project-workflow">
|
||||
<div class="workflow-title">
|
||||
{{$L('工作流设置')}}
|
||||
<div class="title-icon">
|
||||
<Loading v-if="loadIng > 0"/>
|
||||
<Icon v-else type="ios-refresh" @click="getData"/>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="list.length > 0" class="workflow-content">
|
||||
<Collapse v-model="openIndex" accordion>
|
||||
<Panel v-for="data in list" :key="data.id" :name="'index_' + data.id">
|
||||
<div class="workflow-item">
|
||||
<div class="workflow-name">{{data.name}}</div>
|
||||
<div class="workflow-status">
|
||||
<div v-for="item in data.project_flow_item" :class="item.status">{{item.name}}</div>
|
||||
</div>
|
||||
<div class="workflow-save" @click.stop="">
|
||||
<template v-if="contrast(data.project_flow_item, data.project_flow_bak)">
|
||||
<Button :loading="loadIng > 0" type="primary" @click="onSave(data)">{{$L('保存')}}</Button>
|
||||
<Button v-if="data.id > 0" :disabled="loadIng > 0" type="primary" ghost @click="onReduction(data, $event)">{{$L('还原')}}</Button>
|
||||
</template>
|
||||
<Button :disabled="loadIng > 0" type="error" ghost @click="onDelete(data)">{{$L('删除')}}</Button>
|
||||
</div>
|
||||
</div>
|
||||
<div slot="content" class="taskflow-config">
|
||||
<div class="taskflow-config-table">
|
||||
<div class="taskflow-config-table-left-container">
|
||||
<div class="taskflow-config-table-column-header left-header">{{$L('配置项')}}</div>
|
||||
<div :ref="`overlay_${data.id}`" class="taskflow-config-table-column-body overlay-y">
|
||||
<div class="taskflow-config-table-block">
|
||||
<div class="taskflow-config-table-block-title">{{$L('设置状态为')}}</div>
|
||||
<div class="taskflow-config-table-block-item">
|
||||
<div>
|
||||
<div class="title">{{$L('开始状态')}}</div>
|
||||
<div class="subtitle">{{$L('新建任务默认状态')}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="taskflow-config-table-block-item">
|
||||
<div>
|
||||
<div class="title">{{$L('进行中')}}</div>
|
||||
<div class="subtitle">{{$L('可设置多个状态为进行中')}}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="taskflow-config-table-block-item">
|
||||
<div>
|
||||
<div class="title">{{$L('结束状态')}}</div>
|
||||
<div class="subtitle">{{$L('该状态下任务自动标记完成')}}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="taskflow-config-table-block hr">
|
||||
<div class="taskflow-config-table-block-title">{{$L('可流转到')}}</div>
|
||||
<div v-for="item in data.project_flow_item" class="taskflow-config-table-block-item">
|
||||
<span class="transform-status-name">{{item.name}}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="taskflow-config-table-right-container">
|
||||
<Draggable
|
||||
:list="data.project_flow_item"
|
||||
:animation="150"
|
||||
class="taskflow-config-table-list-wrapper"
|
||||
tag="div"
|
||||
draggable=".column-border"
|
||||
@sort="">
|
||||
<div v-for="item in data.project_flow_item" class="taskflow-config-table-status-column column-border" :class="item.status">
|
||||
<div
|
||||
class="taskflow-config-table-status-item taskflow-config-table-column-header">
|
||||
<div class="status-label-with-menu" :class="item.status">
|
||||
<div class="name">{{$L(item.name)}}</div>
|
||||
<EDropdown
|
||||
trigger="click"
|
||||
class="more"
|
||||
@command="onMore($event, item)">
|
||||
<Icon type="ios-more" />
|
||||
<EDropdownMenu slot="dropdown" class="taskflow-config-more-dropdown-menu">
|
||||
<EDropdownItem v-if="item.userids.length > 0" command="user">
|
||||
<div class="users">
|
||||
<UserAvatar v-for="(uid, ukey) in item.userids" :key="ukey" :userid="uid" :size="28" :borderWitdh="1" :showName="item.userids.length === 1" tooltipDisabled/>
|
||||
</div>
|
||||
</EDropdownItem>
|
||||
<EDropdownItem command="user">
|
||||
<div class="item">
|
||||
<Icon type="md-person" />
|
||||
{{$L('自动添加负责人')}}
|
||||
</div>
|
||||
</EDropdownItem>
|
||||
<EDropdownItem command="name">
|
||||
<div class="item">
|
||||
<Icon type="md-create" />{{$L('修改名称')}}
|
||||
</div>
|
||||
</EDropdownItem>
|
||||
<EDropdownItem command="remove">
|
||||
<div class="item delete">
|
||||
<Icon type="md-trash" />{{$L('删除')}}
|
||||
</div>
|
||||
</EDropdownItem>
|
||||
</EDropdownMenu>
|
||||
</EDropdown>
|
||||
</div>
|
||||
</div>
|
||||
<div :ref="`overlay_${data.id}`" class="taskflow-config-table-column-body overlay-y">
|
||||
<div class="taskflow-config-table-block">
|
||||
<div class="taskflow-config-table-block-title"></div>
|
||||
<RadioGroup v-model="item.status">
|
||||
<Radio label="start"><span></span></Radio>
|
||||
<Radio label="progress"><span></span></Radio>
|
||||
<Radio label="end"><span></span></Radio>
|
||||
</RadioGroup>
|
||||
</div>
|
||||
<div class="taskflow-config-table-block">
|
||||
<div class="taskflow-config-table-block-title"></div>
|
||||
<CheckboxGroup v-model="item.turns" @on-change="onTurns(item)">
|
||||
<Checkbox v-for="v in data.project_flow_item" :key="v.id" :label="v.id" :disabled="v.id==item.id"><span></span></Checkbox>
|
||||
</CheckboxGroup>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="taskflow-config-table-status-column addnew" @click="onAdd(data)">{{$L('添加状态')}}</div>
|
||||
</Draggable>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Panel>
|
||||
</Collapse>
|
||||
</div>
|
||||
<div v-else-if="loadIng == 0" class="workflow-no">
|
||||
{{$L('当前项目还没有创建工作流')}}
|
||||
<Button type="primary" @click="onCreate">{{$L('创建工作流')}}</Button>
|
||||
</div>
|
||||
|
||||
<!--成员管理-->
|
||||
<Modal
|
||||
v-model="userShow"
|
||||
:title="$L('自动负责人')"
|
||||
:mask-closable="false">
|
||||
<Form :model="userData" label-width="auto" @submit.native.prevent>
|
||||
<FormItem prop="userids" :label="userData.name">
|
||||
<UserInput v-if="userShow" v-model="userData.userids" :project-id="projectId" :multiple-max="5" :placeholder="$L('选择成员')"/>
|
||||
<div class="form-tip">{{$L('任务流转到此流程时自动添加负责人')}}</div>
|
||||
</FormItem>
|
||||
</Form>
|
||||
<div slot="footer" class="adaption">
|
||||
<Button type="default" @click="userShow=false">{{$L('取消')}}</Button>
|
||||
<Button type="primary" @click="onUser">{{$L('保存')}}</Button>
|
||||
</div>
|
||||
</Modal>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import Draggable from "vuedraggable";
|
||||
import UserInput from "../../../components/UserInput";
|
||||
|
||||
export default {
|
||||
name: "ProjectWorkflow",
|
||||
components: {UserInput, Draggable},
|
||||
props: {
|
||||
projectId: {
|
||||
type: Number,
|
||||
default: 0
|
||||
},
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
loadIng: 0,
|
||||
|
||||
list: [],
|
||||
openIndex: "",
|
||||
|
||||
userShow: false,
|
||||
userData: {},
|
||||
}
|
||||
},
|
||||
|
||||
mounted() {
|
||||
|
||||
},
|
||||
|
||||
computed: {
|
||||
|
||||
},
|
||||
|
||||
watch: {
|
||||
projectId: {
|
||||
handler(val) {
|
||||
if (val) {
|
||||
this.getData()
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
},
|
||||
},
|
||||
|
||||
methods: {
|
||||
getData() {
|
||||
this.loadIng++;
|
||||
this.$store.dispatch("call", {
|
||||
url: 'project/flow/list',
|
||||
data: {
|
||||
project_id: this.projectId,
|
||||
},
|
||||
}).then(({data}) => {
|
||||
this.loadIng--;
|
||||
this.list = data.map(item => {
|
||||
item.project_flow_bak = JSON.stringify(item.project_flow_item)
|
||||
return item;
|
||||
});
|
||||
this.openIndex = this.list.length === 1 ? ("index_" + this.list[0].id) : ""
|
||||
this.$nextTick(this.syncScroller);
|
||||
}).catch(({msg}) => {
|
||||
this.loadIng--;
|
||||
$A.modalError(msg);
|
||||
});
|
||||
},
|
||||
|
||||
syncScroller() {
|
||||
this.list.some(data => {
|
||||
this.$refs[`overlay_${data.id}`] && this.$refs[`overlay_${data.id}`].some(el => {
|
||||
if (!Object.keys(el.attributes).includes("sync-scroller")) {
|
||||
el.setAttribute("sync-scroller", true);
|
||||
el.addEventListener('scroll', ({target}) => {
|
||||
let top = target.scrollTop;
|
||||
let left = target.scrollLeft;
|
||||
this.$nextTick(() => {
|
||||
this.$refs[`overlay_${data.id}`].some(node => {
|
||||
if (node != el) {
|
||||
node.scrollTo(left, top)
|
||||
}
|
||||
})
|
||||
})
|
||||
});
|
||||
}
|
||||
});
|
||||
})
|
||||
|
||||
},
|
||||
|
||||
contrast(project_flow_item, project_flow_bak) {
|
||||
return JSON.stringify(project_flow_item) != project_flow_bak
|
||||
},
|
||||
|
||||
onCreate() {
|
||||
let id = -1 * $A.randNum(1000, 10000);
|
||||
this.list.push({
|
||||
"id": id,
|
||||
"name": "Default workflow",
|
||||
"project_flow_item": [
|
||||
{
|
||||
"id": -10,
|
||||
"name": "待处理",
|
||||
"status": "start",
|
||||
"turns": [-10, -11, -12, -13],
|
||||
"userids": [],
|
||||
},
|
||||
{
|
||||
"id": -11,
|
||||
"name": "进行中",
|
||||
"status": "progress",
|
||||
"turns": [-10, -11, -12, -13],
|
||||
"userids": [],
|
||||
},
|
||||
{
|
||||
"id": -12,
|
||||
"name": "已完成",
|
||||
"status": "end",
|
||||
"turns": [-10, -11, -12, -13],
|
||||
"userids": [],
|
||||
},
|
||||
{
|
||||
"id": -13,
|
||||
"name": "已取消",
|
||||
"status": "end",
|
||||
"turns": [-10, -11, -12, -13],
|
||||
"userids": [],
|
||||
}
|
||||
]
|
||||
})
|
||||
this.openIndex = "index_" + id;
|
||||
},
|
||||
|
||||
onDelete(data) {
|
||||
$A.modalConfirm({
|
||||
title: '删除工作流',
|
||||
content: '你确定要删除工作流吗?',
|
||||
loading: true,
|
||||
onOk: () => {
|
||||
if (data.id > 0) {
|
||||
this.loadIng++;
|
||||
this.$store.dispatch("call", {
|
||||
url: 'project/flow/delete',
|
||||
data: {
|
||||
project_id: this.projectId,
|
||||
},
|
||||
}).then(({msg}) => {
|
||||
this.loadIng--;
|
||||
$.messageSuccess(msg);
|
||||
this.$Modal.remove();
|
||||
//
|
||||
let index = this.list.findIndex(({id}) => id == data.id)
|
||||
if (index > -1) {
|
||||
this.list.splice(index, 1)
|
||||
}
|
||||
}).catch(({msg}) => {
|
||||
this.loadIng--;
|
||||
$A.modalError(msg, 301);
|
||||
this.$Modal.remove();
|
||||
});
|
||||
} else {
|
||||
let index = this.list.findIndex(({id}) => id == data.id)
|
||||
if (index > -1) {
|
||||
this.list.splice(index, 1)
|
||||
}
|
||||
this.$Modal.remove();
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
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.userShow = true;
|
||||
break;
|
||||
|
||||
case "name":
|
||||
this.onName(item);
|
||||
break;
|
||||
|
||||
case "remove":
|
||||
this.onRemove(item);
|
||||
break;
|
||||
}
|
||||
},
|
||||
|
||||
onUser() {
|
||||
this.userShow = false;
|
||||
this.list.some(data => {
|
||||
let item = data.project_flow_item.find(item => item.id == this.userData.id)
|
||||
if (item) {
|
||||
this.$set(item, 'userids', this.userData.userids)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
onName(item) {
|
||||
$A.modalInput({
|
||||
value: item.name,
|
||||
title: "修改名称",
|
||||
placeholder: "输入流程名称",
|
||||
onOk: (name) => {
|
||||
if (name) {
|
||||
this.$set(item, 'name', name);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
onRemove(item) {
|
||||
this.list.some(data => {
|
||||
let index = data.project_flow_item.findIndex(({id}) => id == item.id)
|
||||
if (index > -1) {
|
||||
data.project_flow_item.splice(index, 1)
|
||||
}
|
||||
})
|
||||
},
|
||||
|
||||
onTurns(item) {
|
||||
this.$set(item, 'turns', item.turns.sort())
|
||||
},
|
||||
|
||||
onAdd(data) {
|
||||
$A.modalInput({
|
||||
title: "添加状态",
|
||||
placeholder: "输入状态名称",
|
||||
onOk: (name) => {
|
||||
if (name) {
|
||||
let id = $A.randNum(100000, 999999) * -1;
|
||||
let turns = data.project_flow_item.map(({id}) => id)
|
||||
data.project_flow_item.push({
|
||||
id,
|
||||
name,
|
||||
status: 'end',
|
||||
turns,
|
||||
userids: [],
|
||||
})
|
||||
data.project_flow_item.some(item => {
|
||||
item.turns.push(id)
|
||||
})
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
onReduction(data) {
|
||||
this.$set(data, 'project_flow_item', JSON.parse(data.project_flow_bak))
|
||||
},
|
||||
|
||||
onSave(formData) {
|
||||
let sort = 0;
|
||||
formData.project_flow_item.some(item => {
|
||||
item.sort = sort++
|
||||
})
|
||||
this.loadIng++;
|
||||
this.$store.dispatch("call", {
|
||||
url: 'project/flow/save',
|
||||
data: {
|
||||
project_id: this.projectId,
|
||||
flows: formData.project_flow_item,
|
||||
},
|
||||
method: 'post',
|
||||
}).then(({data, msg}) => {
|
||||
this.loadIng--;
|
||||
$.messageSuccess(msg)
|
||||
//
|
||||
data.project_flow_bak = JSON.stringify(data.project_flow_item)
|
||||
let index = this.list.findIndex(({id}) => id == formData.id)
|
||||
if (index > -1) {
|
||||
this.list.splice(index, 1, data)
|
||||
} else {
|
||||
this.list.push(data)
|
||||
}
|
||||
this.openIndex = "index_" + data.id;
|
||||
}).catch(({msg}) => {
|
||||
this.loadIng--;
|
||||
$A.modalError(msg);
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
@ -5,6 +5,7 @@
|
||||
@import "project-list";
|
||||
@import "project-log";
|
||||
@import "project-management";
|
||||
@import "project-workflow";
|
||||
@import "task-add";
|
||||
@import "task-add-simple";
|
||||
@import "task-archived";
|
||||
|
431
resources/assets/sass/pages/components/project-workflow.scss
vendored
Normal file
431
resources/assets/sass/pages/components/project-workflow.scss
vendored
Normal file
@ -0,0 +1,431 @@
|
||||
.project-workflow {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.workflow-title {
|
||||
color: #333333;
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
line-height: 1;
|
||||
padding: 20px 20px 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
.title-icon {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
margin-left: 4px;
|
||||
margin-top: 2px;
|
||||
> i {
|
||||
cursor: pointer;
|
||||
}
|
||||
}
|
||||
}
|
||||
.workflow-content {
|
||||
flex: 1;
|
||||
padding: 0 20px;
|
||||
overflow: auto;
|
||||
|
||||
.ivu-collapse-header {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.workflow-item {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
.workflow-name {
|
||||
flex-shrink: 0;
|
||||
margin-right: 24px;
|
||||
}
|
||||
.workflow-status {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
> div {
|
||||
height: 22px;
|
||||
line-height: 22px;
|
||||
margin-right: 8px;
|
||||
padding: 0 8px;
|
||||
border: 1px solid #e8eaec;
|
||||
border-radius: 3px;
|
||||
background: #f7f7f7;
|
||||
font-size: 12px;
|
||||
vertical-align: middle;
|
||||
overflow: hidden;
|
||||
&.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
.workflow-save {
|
||||
margin: 0 8px;
|
||||
flex-shrink: 0;
|
||||
> button {
|
||||
height: 26px;
|
||||
line-height: 24px;
|
||||
padding: 0 13px;
|
||||
font-size: 13px;
|
||||
margin-right: 4px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.workflow-no {
|
||||
flex: 1;
|
||||
padding: 0 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: #999;
|
||||
> button {
|
||||
margin-top: 18px;
|
||||
}
|
||||
}
|
||||
|
||||
.taskflow-config {
|
||||
display: flex;
|
||||
max-height: 580px;
|
||||
|
||||
.taskflow-config-table {
|
||||
display: flex;
|
||||
width: 100%;
|
||||
border-radius: 4px;
|
||||
box-shadow: 1px 0 3px rgba(0, 0, 0, 0.1);
|
||||
|
||||
.taskflow-config-table-left-container {
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
width: 246px;
|
||||
padding-top: 16px;
|
||||
padding-bottom: 16px;
|
||||
overflow-x: scroll;
|
||||
-ms-overflow-style: none;
|
||||
&:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
z-index: 1;
|
||||
border-right: 1px solid #f4f4f5;
|
||||
}
|
||||
}
|
||||
|
||||
.taskflow-config-table-column-header {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 58px;
|
||||
padding: 0 20px;
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
line-height: 58px;
|
||||
|
||||
&.left-header {
|
||||
top: 16px
|
||||
}
|
||||
}
|
||||
|
||||
.taskflow-config-table-column-body {
|
||||
margin-top: 58px;
|
||||
height: calc(100% - 58px);
|
||||
}
|
||||
|
||||
.taskflow-config-table-block {
|
||||
width: 100%;
|
||||
padding: 12px 0;
|
||||
|
||||
|
||||
&.hr {
|
||||
position: relative;
|
||||
&:after {
|
||||
content: "";
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
z-index: 1;
|
||||
border-top: 1px solid #f4f4f5;
|
||||
}
|
||||
}
|
||||
|
||||
.taskflow-config-table-block-title {
|
||||
padding: 0 20px;
|
||||
height: 40px;
|
||||
color: #8c8c8c;
|
||||
line-height: 40px
|
||||
}
|
||||
|
||||
.ivu-radio-group {
|
||||
display: block;
|
||||
text-align: center;
|
||||
.ivu-radio-group-item {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
padding: 0 20px;
|
||||
height: 58px;
|
||||
}
|
||||
}
|
||||
|
||||
.ivu-checkbox-group {
|
||||
display: block;
|
||||
text-align: center;
|
||||
.ivu-checkbox-group-item {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
padding: 0 20px;
|
||||
height: 58px;
|
||||
}
|
||||
}
|
||||
|
||||
.taskflow-config-table-block-item {
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
width: 100%;
|
||||
padding: 0 20px;
|
||||
height: 58px;
|
||||
|
||||
&.with-indicator:before {
|
||||
content: "";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
height: 20px;
|
||||
width: 4px;
|
||||
border-radius: 4px
|
||||
}
|
||||
|
||||
.title {
|
||||
font-weight: 700;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap
|
||||
}
|
||||
|
||||
.subtitle {
|
||||
margin-top: 2px;
|
||||
font-size: 12px;
|
||||
color: #8c8c8c;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap
|
||||
}
|
||||
|
||||
&.center {
|
||||
align-items: center
|
||||
}
|
||||
|
||||
&.radio-item>span {
|
||||
display: none
|
||||
}
|
||||
|
||||
.transform-status-name {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.taskflow-config-table-right-container {
|
||||
flex-grow: 1;
|
||||
flex-direction: row;
|
||||
padding-top: 16px;
|
||||
padding-bottom: 16px;
|
||||
overflow-x: auto;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
.taskflow-config-table-list-wrapper,
|
||||
.taskflow-config-table-right-container {
|
||||
display: flex
|
||||
}
|
||||
|
||||
|
||||
.taskflow-config-table-status-column {
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
width: 210px;
|
||||
height: 100%;
|
||||
margin-bottom: 16px;
|
||||
margin-right: 16px;
|
||||
border-radius: 4px;
|
||||
|
||||
&:first-child {
|
||||
margin-left: 20px
|
||||
}
|
||||
|
||||
&:hover {
|
||||
.status-label-with-menu {
|
||||
.more {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
&.addnew {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border: 1px dashed #d9d9d9;
|
||||
cursor: pointer;
|
||||
color: #8c8c8c;
|
||||
|
||||
&:hover {
|
||||
color: #777777;
|
||||
border-color: #bfbfbf
|
||||
}
|
||||
}
|
||||
|
||||
&.column-border {
|
||||
border-width: 1px;
|
||||
border-style: solid
|
||||
}
|
||||
|
||||
&.start {
|
||||
border-color: #e5e5e5;
|
||||
&:hover {
|
||||
border-color: #bfbfbf
|
||||
}
|
||||
}
|
||||
|
||||
&.progress {
|
||||
border-color: #ccecff;
|
||||
&:hover {
|
||||
border-color: #87d2ff
|
||||
}
|
||||
}
|
||||
|
||||
&.end {
|
||||
border-color: #cafac8;
|
||||
&:hover {
|
||||
border-color: #64d16d
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.taskflow-config-table-status-item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 8px;
|
||||
height: 58px;
|
||||
line-height: 58px;
|
||||
cursor: move
|
||||
}
|
||||
|
||||
.status-label-with-menu {
|
||||
max-width: 100%;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
padding: 0 32px;
|
||||
width: 200px;
|
||||
height: 36px;
|
||||
line-height: 32px;
|
||||
font-size: 14px;
|
||||
font-weight: 400;
|
||||
justify-content: center;
|
||||
|
||||
&:hover {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
&.start {
|
||||
background: rgba(38,38,38,0.05);
|
||||
color: #595959
|
||||
}
|
||||
|
||||
&.progress {
|
||||
background: rgba(27,154,238,0.1);
|
||||
color: #0171c2
|
||||
}
|
||||
|
||||
&.end {
|
||||
background: rgba(21,173,49,0.1);
|
||||
color: #038a24
|
||||
}
|
||||
|
||||
.name {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap
|
||||
}
|
||||
|
||||
.more {
|
||||
cursor: pointer;
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 36px;
|
||||
font-size: 18px;
|
||||
opacity: 0;
|
||||
transition: opacity 0.2s;
|
||||
&.opacity {
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
.taskflow-config-more-dropdown-menu {
|
||||
.users {
|
||||
display: flex;
|
||||
padding: 6px 0;
|
||||
margin: 0 -8px;
|
||||
overflow: auto;
|
||||
.common-avatar {
|
||||
max-width: 100%;
|
||||
margin-right: -5px;
|
||||
}
|
||||
}
|
||||
.item {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.delete {
|
||||
color: #f00;
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user