mirror of
https://github.com/kuaifan/dootask.git
synced 2026-02-06 05:05:35 +00:00
perf: 添加项目任务标签功能
This commit is contained in:
parent
248b0ce196
commit
8e108e2d38
@ -1995,6 +1995,7 @@ class ProjectController extends AbstractController
|
|||||||
* @apiParam {String} [content] 任务详情(子任务不支持)
|
* @apiParam {String} [content] 任务详情(子任务不支持)
|
||||||
* @apiParam {String} [color] 背景色(子任务不支持)
|
* @apiParam {String} [color] 背景色(子任务不支持)
|
||||||
* @apiParam {Array} [assist] 修改协助人员(子任务不支持)
|
* @apiParam {Array} [assist] 修改协助人员(子任务不支持)
|
||||||
|
* @apiParam {Array} [task_tag] 任务标签(子任务不支持)
|
||||||
* @apiParam {Number} [visibility] 修改可见性
|
* @apiParam {Number} [visibility] 修改可见性
|
||||||
* @apiParam {Array} [visibility_appointor] 修改可见性人员
|
* @apiParam {Array} [visibility_appointor] 修改可见性人员
|
||||||
*
|
*
|
||||||
|
|||||||
@ -944,6 +944,60 @@ class ProjectTask extends AbstractModel
|
|||||||
$this->addLog("修改{任务}详细描述", $logRecord);
|
$this->addLog("修改{任务}详细描述", $logRecord);
|
||||||
$updateMarking['is_update_content'] = true;
|
$updateMarking['is_update_content'] = true;
|
||||||
}
|
}
|
||||||
|
// 标签
|
||||||
|
if (Arr::exists($data, 'task_tag')) {
|
||||||
|
$oldTags = collect($this->taskTag);
|
||||||
|
$newTags = collect($data['task_tag']);
|
||||||
|
|
||||||
|
// 找出需要删除的标签(在旧数据中存在,但在新数据中不存在)
|
||||||
|
$deletedTags = $oldTags->filter(function ($oldTag) use ($newTags) {
|
||||||
|
return !$newTags->contains('name', $oldTag['name']);
|
||||||
|
});
|
||||||
|
if ($deletedTags->isNotEmpty()) {
|
||||||
|
$this->addLog("删除{任务}标签", [
|
||||||
|
'tags' => $deletedTags->values()->all()
|
||||||
|
]);
|
||||||
|
ProjectTaskTag::whereProjectId($this->project_id)
|
||||||
|
->whereTaskId($this->id)
|
||||||
|
->whereIn('name', $deletedTags->pluck('name'))
|
||||||
|
->delete();
|
||||||
|
}
|
||||||
|
|
||||||
|
// 找出需要新增的标签(在新数据中存在,但在旧数据中不存在)
|
||||||
|
$addedTags = $newTags->filter(function ($newTag) use ($oldTags) {
|
||||||
|
return !$oldTags->contains('name', $newTag['name']);
|
||||||
|
});
|
||||||
|
if ($addedTags->isNotEmpty()) {
|
||||||
|
$this->addLog("新增{任务}标签", [
|
||||||
|
'tags' => $addedTags->values()->all()
|
||||||
|
]);
|
||||||
|
$addedTags->each(function ($tag) {
|
||||||
|
ProjectTaskTag::createInstance([
|
||||||
|
'project_id' => $this->project_id,
|
||||||
|
'task_id' => $this->id,
|
||||||
|
'name' => $tag['name'],
|
||||||
|
'color' => $tag['color'],
|
||||||
|
])->save();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// 找出需要更新的标签(标签名相同,但其他属性可能变化)
|
||||||
|
$updatedTags = $newTags->filter(function ($newTag) use ($oldTags) {
|
||||||
|
$oldTag = $oldTags->firstWhere('name', $newTag['name']);
|
||||||
|
return $oldTag && ($oldTag['color'] !== $newTag['color']);
|
||||||
|
});
|
||||||
|
if ($updatedTags->isNotEmpty()) {
|
||||||
|
$this->addLog("更新{任务}标签", [
|
||||||
|
'tags' => $updatedTags->values()->all()
|
||||||
|
]);
|
||||||
|
$updatedTags->each(function ($tag) {
|
||||||
|
ProjectTaskTag::whereProjectId($this->project_id)
|
||||||
|
->whereTaskId($this->id)
|
||||||
|
->whereName($tag['name'])
|
||||||
|
->update(['color' => $tag['color']]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
// 优先级
|
// 优先级
|
||||||
$p = false;
|
$p = false;
|
||||||
$oldPName = $this->p_name;
|
$oldPName = $this->p_name;
|
||||||
|
|||||||
@ -180,9 +180,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<template v-if="!item.complete_at">
|
<template v-if="!item.complete_at">
|
||||||
<div v-if="item.desc" class="task-desc"><pre v-html="item.desc"></pre></div>
|
<div v-if="item.desc" class="task-desc"><pre v-html="item.desc"></pre></div>
|
||||||
<div v-if="item.task_tag.length > 0" class="task-tags">
|
<TaskTag v-if="item.task_tag.length > 0" class="task-tags" :tags="item.task_tag"/>
|
||||||
<Tag v-for="(tag, keyt) in item.task_tag" :key="keyt" :color="tag.color">{{tag.name}}</Tag>
|
|
||||||
</div>
|
|
||||||
<div class="task-users">
|
<div class="task-users">
|
||||||
<ul>
|
<ul>
|
||||||
<li v-for="(user, keyu) in ownerUser(item.task_user)" :key="keyu">
|
<li v-for="(user, keyu) in ownerUser(item.task_user)" :key="keyu">
|
||||||
@ -529,6 +527,7 @@ import ProjectWorkflow from "./ProjectWorkflow";
|
|||||||
import ProjectPermission from "./ProjectPermission";
|
import ProjectPermission from "./ProjectPermission";
|
||||||
import TaskMenu from "./TaskMenu";
|
import TaskMenu from "./TaskMenu";
|
||||||
import TaskDeleted from "./TaskDeleted";
|
import TaskDeleted from "./TaskDeleted";
|
||||||
|
import TaskTag from "./ProjectTaskTag/tags.vue";
|
||||||
import ProjectGantt from "./ProjectGantt";
|
import ProjectGantt from "./ProjectGantt";
|
||||||
import UserSelect from "../../../components/UserSelect.vue";
|
import UserSelect from "../../../components/UserSelect.vue";
|
||||||
import UserAvatarTip from "../../../components/UserAvatar/tip.vue";
|
import UserAvatarTip from "../../../components/UserAvatar/tip.vue";
|
||||||
@ -546,7 +545,16 @@ export default {
|
|||||||
ProjectWorkflow,
|
ProjectWorkflow,
|
||||||
ProjectPermission,
|
ProjectPermission,
|
||||||
DrawerOverlay,
|
DrawerOverlay,
|
||||||
ProjectLog, TaskArchived, TaskRow, Draggable, TaskAddSimple, TaskPriority, TaskDeleted, ProjectGantt},
|
ProjectLog,
|
||||||
|
TaskArchived,
|
||||||
|
TaskRow,
|
||||||
|
Draggable,
|
||||||
|
TaskAddSimple,
|
||||||
|
TaskPriority,
|
||||||
|
TaskDeleted,
|
||||||
|
TaskTag,
|
||||||
|
ProjectGantt,
|
||||||
|
},
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
loading: false,
|
loading: false,
|
||||||
|
|||||||
@ -0,0 +1,269 @@
|
|||||||
|
<template>
|
||||||
|
<div class="task-tag-select" :class="{'no-search': filteredTags.length <= 5}">
|
||||||
|
<!-- Search Box -->
|
||||||
|
<div class="search-box">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
v-model="searchQuery"
|
||||||
|
:placeholder="$L('搜索标签')"
|
||||||
|
class="search-input"
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Tag List -->
|
||||||
|
<div class="tag-list">
|
||||||
|
<template v-if="filteredTags.length">
|
||||||
|
<div
|
||||||
|
v-for="tag in filteredTags"
|
||||||
|
:key="tag.name"
|
||||||
|
class="tag-item"
|
||||||
|
:class="{ 'is-selected': isSelected(tag) }"
|
||||||
|
@click="toggleTag(tag)"
|
||||||
|
>
|
||||||
|
<div class="tag-color" :style="{ backgroundColor: tag.color }"></div>
|
||||||
|
<div class="tag-info">
|
||||||
|
<div class="tag-name">{{ tag.name }}</div>
|
||||||
|
<div class="tag-desc" v-if="tag.desc">{{ tag.desc }}</div>
|
||||||
|
</div>
|
||||||
|
<div class="tag-check" v-if="isSelected(tag)">
|
||||||
|
<i class="el-icon-check"></i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
<div v-else-if="!loading" class="no-data">{{ $L('暂无标签') }}</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Add Button -->
|
||||||
|
<div class="footer-box">
|
||||||
|
<div class="add-button" @click="$emit('add')">
|
||||||
|
<i class="el-icon-plus"></i>
|
||||||
|
<span>{{ $L('添加标签') }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- Loading -->
|
||||||
|
<Spin v-if="loading" fix/>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script>
|
||||||
|
export default {
|
||||||
|
name: "TaskSelect",
|
||||||
|
props: {
|
||||||
|
value: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
dataSources: {
|
||||||
|
type: Array,
|
||||||
|
default: () => []
|
||||||
|
},
|
||||||
|
loading: {
|
||||||
|
type: Boolean,
|
||||||
|
default: false
|
||||||
|
},
|
||||||
|
max: {
|
||||||
|
type: Number,
|
||||||
|
default: 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
data() {
|
||||||
|
return {
|
||||||
|
searchQuery: '',
|
||||||
|
internalDataSources: []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
value: {
|
||||||
|
immediate: true,
|
||||||
|
handler() {
|
||||||
|
this.syncValueToDataSources();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dataSources: {
|
||||||
|
immediate: true,
|
||||||
|
handler(newVal) {
|
||||||
|
this.internalDataSources = [...newVal];
|
||||||
|
this.syncValueToDataSources();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
computed: {
|
||||||
|
filteredTags() {
|
||||||
|
return this.internalDataSources.filter(tag =>
|
||||||
|
tag.name.toLowerCase().includes(this.searchQuery.toLowerCase())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
methods: {
|
||||||
|
isSelected(tag) {
|
||||||
|
return this.value.some(item => item.name === tag.name);
|
||||||
|
},
|
||||||
|
toggleTag(tag) {
|
||||||
|
const isSelected = this.isSelected(tag);
|
||||||
|
let newValue;
|
||||||
|
if (isSelected) {
|
||||||
|
newValue = this.value.filter(item => item.name !== tag.name);
|
||||||
|
} else {
|
||||||
|
if (this.max > 0 && this.value.length >= this.max) {
|
||||||
|
$A.messageWarning(this.$L('最多只能选择 (*) 个标签', this.max));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
newValue = [...this.value, { name: tag.name, color: tag.color }];
|
||||||
|
}
|
||||||
|
this.$emit('input', newValue);
|
||||||
|
},
|
||||||
|
syncValueToDataSources() {
|
||||||
|
if (!this.value || !this.internalDataSources) return;
|
||||||
|
|
||||||
|
const missingTags = this.value.filter(valueTag =>
|
||||||
|
!this.internalDataSources.some(sourceTag => sourceTag.name === valueTag.name)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (missingTags.length) {
|
||||||
|
this.internalDataSources = [
|
||||||
|
...missingTags.map(tag => ({
|
||||||
|
name: tag.name,
|
||||||
|
color: tag.color,
|
||||||
|
desc: ''
|
||||||
|
})),
|
||||||
|
...this.internalDataSources
|
||||||
|
];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss" scoped>
|
||||||
|
.task-tag-select {
|
||||||
|
width: 100%;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
|
||||||
|
&.no-search {
|
||||||
|
.search-box {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
.tag-list {
|
||||||
|
.tag-item {
|
||||||
|
&:first-child {
|
||||||
|
margin-top: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.search-box {
|
||||||
|
padding-bottom: 8px;
|
||||||
|
border-bottom: 1px solid #eee;
|
||||||
|
|
||||||
|
.search-input {
|
||||||
|
width: 100%;
|
||||||
|
height: 34px;
|
||||||
|
padding: 0 12px;
|
||||||
|
border: 1px solid #dcdfe6;
|
||||||
|
border-radius: 4px;
|
||||||
|
outline: none;
|
||||||
|
|
||||||
|
&:focus {
|
||||||
|
border-color: #84C56A;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag-list {
|
||||||
|
flex: 1;
|
||||||
|
overflow-y: auto;
|
||||||
|
max-height: 300px;
|
||||||
|
|
||||||
|
.tag-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: flex-start;
|
||||||
|
padding: 8px 12px;
|
||||||
|
cursor: pointer;
|
||||||
|
border-radius: 6px;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
|
||||||
|
&:first-child {
|
||||||
|
margin-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:last-child {
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background-color: #f5f7fa;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.is-selected {
|
||||||
|
background-color: #ecf5ff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag-color {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-right: 8px;
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag-info {
|
||||||
|
flex: 1;
|
||||||
|
|
||||||
|
.tag-name {
|
||||||
|
line-height: 20px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #303133;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag-desc {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #909399;
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag-check {
|
||||||
|
color: #84C56A;
|
||||||
|
margin-left: 12px;
|
||||||
|
height: 20px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-data {
|
||||||
|
text-align: center;
|
||||||
|
color: #909399;
|
||||||
|
padding: 24px 0;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer-box {
|
||||||
|
border-top: 1px solid #eee;
|
||||||
|
padding-top: 8px;
|
||||||
|
|
||||||
|
.add-button {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
padding: 4px 0 2px;
|
||||||
|
cursor: pointer;
|
||||||
|
color: #84C56A;
|
||||||
|
border-radius: 6px;
|
||||||
|
transition: color 0.2s;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
color: #a2d98d;
|
||||||
|
}
|
||||||
|
|
||||||
|
i {
|
||||||
|
margin-right: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
@ -147,21 +147,30 @@
|
|||||||
@on-history="onHistory"
|
@on-history="onHistory"
|
||||||
@on-blur="updateBlur('content', $event)"/>
|
@on-blur="updateBlur('content', $event)"/>
|
||||||
<Form class="items" label-position="left" label-width="auto" @submit.native.prevent>
|
<Form class="items" label-position="left" label-width="auto" @submit.native.prevent>
|
||||||
<FormItem v-if="taskDetail.p_name">
|
<FormItem v-if="getTag.length > 0 || tagForce">
|
||||||
<div class="item-label" slot="label">
|
<div class="item-label" slot="label">
|
||||||
<i class="taskfont"></i>{{$L('标签')}}
|
<i class="taskfont"></i>{{$L('标签')}}
|
||||||
</div>
|
</div>
|
||||||
<ul class="item-content">
|
<div class="item-content tags">
|
||||||
<li>
|
<EPopover v-model="tagShow" class="tags-select" placement="bottom">
|
||||||
|
<TagSelect
|
||||||
</li>
|
v-model="tagValue"
|
||||||
</ul>
|
:data-sources="tagData"
|
||||||
|
:loading="tagLoad > 0"
|
||||||
|
:max="10"/>
|
||||||
|
<div slot="reference">
|
||||||
|
<TaskTag :tags="getTag">
|
||||||
|
<li v-if="getTag.length === 0" slot="end" class="add-icon"></li>
|
||||||
|
</TaskTag>
|
||||||
|
</div>
|
||||||
|
</EPopover>
|
||||||
|
</div>
|
||||||
</FormItem>
|
</FormItem>
|
||||||
<FormItem v-if="taskDetail.p_name">
|
<FormItem v-if="taskDetail.p_name">
|
||||||
<div class="item-label" slot="label">
|
<div class="item-label" slot="label">
|
||||||
<i class="taskfont"></i>{{$L('优先级')}}
|
<i class="taskfont"></i>{{$L('优先级')}}
|
||||||
</div>
|
</div>
|
||||||
<ul class="item-content">
|
<ul class="item-content priority">
|
||||||
<li>
|
<li>
|
||||||
<EDropdown
|
<EDropdown
|
||||||
ref="priority"
|
ref="priority"
|
||||||
@ -300,7 +309,7 @@
|
|||||||
<div class="file-size">{{$A.bytesToSize(file.size)}}</div>
|
<div class="file-size">{{$A.bytesToSize(file.size)}}</div>
|
||||||
</li>
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
<ul class="item-content">
|
<ul class="item-content file-up">
|
||||||
<li>
|
<li>
|
||||||
<div class="add-button" @click="onUploadClick(true)">
|
<div class="add-button" @click="onUploadClick(true)">
|
||||||
<i class="taskfont"></i>
|
<i class="taskfont"></i>
|
||||||
@ -313,7 +322,7 @@
|
|||||||
<div class="item-label" slot="label">
|
<div class="item-label" slot="label">
|
||||||
<i class="taskfont"></i>{{$L('子任务')}}
|
<i class="taskfont"></i>{{$L('子任务')}}
|
||||||
</div>
|
</div>
|
||||||
<ul class="item-content subtask">
|
<ul v-if="subList.length > 0" class="item-content subtask">
|
||||||
<TaskDetail
|
<TaskDetail
|
||||||
v-for="(task, key) in subList"
|
v-for="(task, key) in subList"
|
||||||
:ref="`subTask_${task.id}`"
|
:ref="`subTask_${task.id}`"
|
||||||
@ -323,7 +332,7 @@
|
|||||||
:main-end-at="taskDetail.end_at"
|
:main-end-at="taskDetail.end_at"
|
||||||
:can-update-blur="canUpdateBlur"/>
|
:can-update-blur="canUpdateBlur"/>
|
||||||
</ul>
|
</ul>
|
||||||
<ul :class="['item-content', subList.length === 0 ? 'nosub' : '']">
|
<ul class="item-content subtask-add">
|
||||||
<li>
|
<li>
|
||||||
<Input
|
<Input
|
||||||
v-if="addsubShow"
|
v-if="addsubShow"
|
||||||
@ -551,6 +560,8 @@ import {Store} from "le5le-store";
|
|||||||
import TaskMenu from "./TaskMenu";
|
import TaskMenu from "./TaskMenu";
|
||||||
import ChatInput from "./ChatInput";
|
import ChatInput from "./ChatInput";
|
||||||
import UserSelect from "../../../components/UserSelect.vue";
|
import UserSelect from "../../../components/UserSelect.vue";
|
||||||
|
import TaskTag from "./ProjectTaskTag/tags.vue";
|
||||||
|
import TagSelect from "./ProjectTaskTag/select.vue";
|
||||||
import TaskExistTips from "./TaskExistTips.vue";
|
import TaskExistTips from "./TaskExistTips.vue";
|
||||||
import TEditorTask from "../../../components/TEditorTask.vue";
|
import TEditorTask from "../../../components/TEditorTask.vue";
|
||||||
import TaskContentHistory from "./TaskContentHistory.vue";
|
import TaskContentHistory from "./TaskContentHistory.vue";
|
||||||
@ -561,6 +572,8 @@ export default {
|
|||||||
TaskContentHistory,
|
TaskContentHistory,
|
||||||
TEditorTask,
|
TEditorTask,
|
||||||
UserSelect,
|
UserSelect,
|
||||||
|
TaskTag,
|
||||||
|
TagSelect,
|
||||||
TaskExistTips,
|
TaskExistTips,
|
||||||
ChatInput,
|
ChatInput,
|
||||||
TaskMenu,
|
TaskMenu,
|
||||||
@ -605,6 +618,13 @@ export default {
|
|||||||
|
|
||||||
receiveShow: false,
|
receiveShow: false,
|
||||||
|
|
||||||
|
tagForce: false,
|
||||||
|
tagShow: false,
|
||||||
|
tagValue: [],
|
||||||
|
tagBakValue: [],
|
||||||
|
tagData: [],
|
||||||
|
tagLoad: 0,
|
||||||
|
|
||||||
assistForce: false,
|
assistForce: false,
|
||||||
assistData: {},
|
assistData: {},
|
||||||
assistLoad: 0,
|
assistLoad: 0,
|
||||||
@ -819,6 +839,14 @@ export default {
|
|||||||
return string
|
return string
|
||||||
},
|
},
|
||||||
|
|
||||||
|
getTag() {
|
||||||
|
const {taskDetail} = this;
|
||||||
|
if (!$A.isArray(taskDetail.task_tag)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
return taskDetail.task_tag;
|
||||||
|
},
|
||||||
|
|
||||||
getOwner() {
|
getOwner() {
|
||||||
const {taskDetail} = this;
|
const {taskDetail} = this;
|
||||||
if (!$A.isArray(taskDetail.task_user)) {
|
if (!$A.isArray(taskDetail.task_user)) {
|
||||||
@ -842,6 +870,13 @@ export default {
|
|||||||
menuList() {
|
menuList() {
|
||||||
const {taskDetail} = this;
|
const {taskDetail} = this;
|
||||||
const list = [];
|
const list = [];
|
||||||
|
if ($A.arrayLength(taskDetail.task_tag) === 0) {
|
||||||
|
list.push({
|
||||||
|
command: 'tag',
|
||||||
|
icon: '',
|
||||||
|
name: '标签',
|
||||||
|
});
|
||||||
|
}
|
||||||
if (!taskDetail.p_name) {
|
if (!taskDetail.p_name) {
|
||||||
list.push({
|
list.push({
|
||||||
command: 'priority',
|
command: 'priority',
|
||||||
@ -934,6 +969,7 @@ export default {
|
|||||||
this.timeOpen = false;
|
this.timeOpen = false;
|
||||||
this.timeForce = false;
|
this.timeForce = false;
|
||||||
this.loopForce = false;
|
this.loopForce = false;
|
||||||
|
this.tagForce = false;
|
||||||
this.assistForce = false;
|
this.assistForce = false;
|
||||||
this.visibleForce = false;
|
this.visibleForce = false;
|
||||||
this.addsubForce = false;
|
this.addsubForce = false;
|
||||||
@ -974,6 +1010,36 @@ export default {
|
|||||||
},
|
},
|
||||||
immediate: true
|
immediate: true
|
||||||
},
|
},
|
||||||
|
tagShow(val) {
|
||||||
|
if (val) {
|
||||||
|
this.tagValue = this.getTag;
|
||||||
|
this.tagBakValue = $A.cloneJSON(this.tagValue);
|
||||||
|
//
|
||||||
|
const isLoad = this.tagValue.length === 0 && this.tagData.length === 0;
|
||||||
|
isLoad && this.tagLoad++;
|
||||||
|
this.$store.dispatch("call", {
|
||||||
|
url: "project/tag/list",
|
||||||
|
data: {
|
||||||
|
project_id: this.taskDetail.project_id
|
||||||
|
}
|
||||||
|
}).then(res => {
|
||||||
|
this.tagData = res.data;
|
||||||
|
}).finally(_ => {
|
||||||
|
isLoad && this.tagLoad--;
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
const isChanged = (() => {
|
||||||
|
if (this.tagValue.length !== this.tagBakValue.length) return true;
|
||||||
|
const sortValue = arr => [...arr].map(({name, color}) => ({name, color})).sort((a, b) => a.name.localeCompare(b.name));
|
||||||
|
const sortedValue = sortValue(this.tagValue);
|
||||||
|
const sortedBakValue = sortValue(this.tagBakValue);
|
||||||
|
return JSON.stringify(sortedValue) !== JSON.stringify(sortedBakValue);
|
||||||
|
})();
|
||||||
|
if (isChanged) {
|
||||||
|
this.updateData('tag', this.tagValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
methods: {
|
methods: {
|
||||||
@ -1158,10 +1224,14 @@ export default {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case 'tag':
|
||||||
|
this.$set(this.taskDetail, 'task_tag', params)
|
||||||
|
action = 'task_tag'
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
//
|
//
|
||||||
|
const dataJson = {task_id: this.taskDetail.id};
|
||||||
let dataJson = {task_id: this.taskDetail.id};
|
|
||||||
($A.isArray(action) ? action : [action]).forEach(key => {
|
($A.isArray(action) ? action : [action]).forEach(key => {
|
||||||
let newData = this.taskDetail[key];
|
let newData = this.taskDetail[key];
|
||||||
let originalData = this.openTask[key];
|
let originalData = this.openTask[key];
|
||||||
@ -1426,6 +1496,13 @@ export default {
|
|||||||
|
|
||||||
dropAdd(command) {
|
dropAdd(command) {
|
||||||
switch (command) {
|
switch (command) {
|
||||||
|
case 'tag':
|
||||||
|
this.tagForce = true;
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.tagShow = true;
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
|
||||||
case 'priority':
|
case 'priority':
|
||||||
this.$set(this.taskDetail, 'p_name', this.$L('未设置'));
|
this.$set(this.taskDetail, 'p_name', this.$L('未设置'));
|
||||||
this.$nextTick(() => {
|
this.$nextTick(() => {
|
||||||
|
|||||||
@ -480,9 +480,6 @@
|
|||||||
}
|
}
|
||||||
.task-tags {
|
.task-tags {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
.ivu-tag {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
.task-users {
|
.task-users {
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user