perf: 添加项目任务标签功能

This commit is contained in:
kuaifan 2024-12-08 23:24:56 +08:00
parent bca0410a08
commit 24c5200a90
5 changed files with 72 additions and 33 deletions

View File

@ -1239,6 +1239,7 @@ class ProjectController extends AbstractController
$headings[] = Doo::translate('父级任务ID'); $headings[] = Doo::translate('父级任务ID');
$headings[] = Doo::translate('所属项目'); $headings[] = Doo::translate('所属项目');
$headings[] = Doo::translate('任务标题'); $headings[] = Doo::translate('任务标题');
$headings[] = Doo::translate('任务标签');
$headings[] = Doo::translate('任务开始时间'); $headings[] = Doo::translate('任务开始时间');
$headings[] = Doo::translate('任务结束时间'); $headings[] = Doo::translate('任务结束时间');
$headings[] = Doo::translate('完成时间'); $headings[] = Doo::translate('完成时间');
@ -1259,7 +1260,7 @@ class ProjectController extends AbstractController
'style' => 'font-weight: bold;padding-bottom: 4px;', 'style' => 'font-weight: bold;padding-bottom: 4px;',
]; ];
// //
$builder = ProjectTask::select(['project_tasks.*', 'project_task_users.userid as ownerid']) $builder = ProjectTask::with(['taskTag'])->select(['project_tasks.*', 'project_task_users.userid as ownerid'])
->join('project_task_users', 'project_tasks.id', '=', 'project_task_users.task_id') ->join('project_task_users', 'project_tasks.id', '=', 'project_task_users.task_id')
->where('project_task_users.owner', 1) ->where('project_task_users.owner', 1)
->whereIn('project_task_users.userid', $userid) ->whereIn('project_task_users.userid', $userid)
@ -1341,6 +1342,9 @@ class ProjectController extends AbstractController
$task->parent_id ?: '-', $task->parent_id ?: '-',
Base::filterEmoji($task->project?->name) ?: '-', Base::filterEmoji($task->project?->name) ?: '-',
Base::filterEmoji($task->name), Base::filterEmoji($task->name),
$task->taskTag->map(function ($tag) {
return Base::filterEmoji($tag->name);
})->join(', ') ?: '-',
$task->start_at ?: '-', $task->start_at ?: '-',
$task->end_at ?: '-', $task->end_at ?: '-',
$task->complete_at ?: '-', $task->complete_at ?: '-',
@ -1463,6 +1467,7 @@ class ProjectController extends AbstractController
$headings[] = Doo::translate('父级任务ID'); $headings[] = Doo::translate('父级任务ID');
$headings[] = Doo::translate('所属项目'); $headings[] = Doo::translate('所属项目');
$headings[] = Doo::translate('任务标题'); $headings[] = Doo::translate('任务标题');
$headings[] = Doo::translate('任务标签');
$headings[] = Doo::translate('任务开始时间'); $headings[] = Doo::translate('任务开始时间');
$headings[] = Doo::translate('任务结束时间'); $headings[] = Doo::translate('任务结束时间');
$headings[] = Doo::translate('任务计划用时'); $headings[] = Doo::translate('任务计划用时');
@ -1471,7 +1476,8 @@ class ProjectController extends AbstractController
$headings[] = Doo::translate('创建人'); $headings[] = Doo::translate('创建人');
$data = []; $data = [];
// //
ProjectTask::whereNull('complete_at') ProjectTask::with(['taskTag'])
->whereNull('complete_at')
->whereNotNull('end_at') ->whereNotNull('end_at')
->where('end_at', '<=', Carbon::now()) ->where('end_at', '<=', Carbon::now())
->orderBy('end_at') ->orderBy('end_at')
@ -1502,11 +1508,14 @@ class ProjectController extends AbstractController
$task->parent_id ?: '-', $task->parent_id ?: '-',
Base::filterEmoji($task->project?->name) ?: '-', Base::filterEmoji($task->project?->name) ?: '-',
Base::filterEmoji($task->name), Base::filterEmoji($task->name),
$task->taskTag->map(function ($tag) {
return Base::filterEmoji($tag->name);
})->join(', ') ?: '-',
$task->start_at ?: '-', $task->start_at ?: '-',
$task->end_at ?: '-', $task->end_at ?: '-',
$planTime, $planTime,
$overTime, $overTime,
implode("", $ownerNames), implode(', ', $ownerNames),
Base::filterEmoji(User::userid2nickname($task->userid)) . " (ID: {$task->userid})", Base::filterEmoji(User::userid2nickname($task->userid)) . " (ID: {$task->userid})",
]; ];
} }

View File

@ -58,6 +58,12 @@ import Tags from "./tags.vue";
export default { export default {
name: "TaskTagAdd", name: "TaskTagAdd",
components: {Tags}, components: {Tags},
props: {
projectId: {
type: [Number, String],
required: true
}
},
data() { data() {
return { return {
loadIng: 0, loadIng: 0,
@ -89,11 +95,25 @@ export default {
} }
}, },
methods: { methods: {
onOpen(tag) { onOpen(tag = null) {
if (tag === null) {
tag = this.getEmptyTag()
}
this.editingTag = { ...tag } this.editingTag = { ...tag }
this.showEditModal = true this.showEditModal = true
}, },
//
getEmptyTag() {
return {
id: null,
project_id: this.projectId,
name: '',
desc: '',
color: ''
}
},
// //
async handleSave() { async handleSave() {
if (!this.editingTag.name) { if (!this.editingTag.name) {
@ -118,7 +138,7 @@ export default {
const results = await Promise.all(savePromises) const results = await Promise.all(savePromises)
$A.messageSuccess(results.length === 1 ? results[0].msg : '全部保存成功') $A.messageSuccess(results.length === 1 ? results[0].msg : '全部保存成功')
this.showEditModal = false this.showEditModal = false
this.$emit('on-save') this.$emit('on-save', results)
} catch (error) { } catch (error) {
$A.messageError(error.msg || '保存失败') $A.messageError(error.msg || '保存失败')
} }

View File

@ -7,7 +7,7 @@
<Loading v-if="loadIng > 0"/> <Loading v-if="loadIng > 0"/>
</div> </div>
<div class="actions"> <div class="actions">
<Button type="primary" icon="md-add" @click="handleAdd"> <Button type="primary" icon="md-add" @click="handleAdd(null)">
{{$L('新建标签')}} {{$L('新建标签')}}
</Button> </Button>
</div> </div>
@ -16,7 +16,7 @@
<div class="content"> <div class="content">
<div v-if="!tags.length" class="empty"> <div v-if="!tags.length" class="empty">
<div class="empty-text">{{$L('当前项目暂无任务标签')}}</div> <div class="empty-text">{{$L('当前项目暂无任务标签')}}</div>
<Button type="primary" icon="md-add" @click="handleAdd">{{$L('新建标签')}}</Button> <Button type="primary" icon="md-add" @click="handleAdd(null)">{{$L('新建标签')}}</Button>
</div> </div>
<div v-else class="template-list"> <div v-else class="template-list">
<div v-for="item in tags" :key="item.id" class="tag-item"> <div v-for="item in tags" :key="item.id" class="tag-item">
@ -27,7 +27,7 @@
<div v-if="item.desc" class="tag-desc">{{ item.desc }}</div> <div v-if="item.desc" class="tag-desc">{{ item.desc }}</div>
</div> </div>
<div class="tag-actions"> <div class="tag-actions">
<Button @click="handleEdit(item)" type="primary"> <Button @click="handleAdd(item)" type="primary">
{{$L('编辑')}} {{$L('编辑')}}
</Button> </Button>
<Button @click="handleDelete(item)" type="error"> <Button @click="handleDelete(item)" type="error">
@ -38,8 +38,8 @@
</div> </div>
</div> </div>
<!-- 编辑标签弹窗 --> <!-- 标签添加/编辑 -->
<TaskTagAdd ref="addTag" @on-save="loadTags"/> <TaskTagAdd ref="addTag" :project-id="projectId" @on-save="loadTags"/>
</div> </div>
</template> </template>
@ -73,17 +73,6 @@ export default {
this.loadTags() this.loadTags()
}, },
methods: { methods: {
//
getEmptyTag() {
return {
id: null,
project_id: this.projectId,
name: '',
desc: '',
color: ''
}
},
// //
async loadTags() { async loadTags() {
this.loadIng++ this.loadIng++
@ -103,13 +92,8 @@ export default {
} }
}, },
// //
handleAdd() { handleAdd(tag) {
this.$refs.addTag.onOpen(this.getEmptyTag())
},
//
handleEdit(tag) {
this.$refs.addTag.onOpen(tag) this.$refs.addTag.onOpen(tag)
}, },

View File

@ -48,7 +48,7 @@
<script> <script>
export default { export default {
name: "TaskSelect", name: "TaskTagSelect",
props: { props: {
value: { value: {
type: Array, type: Array,

View File

@ -153,11 +153,12 @@
</div> </div>
<div class="item-content tags"> <div class="item-content tags">
<EPopover v-model="tagShow" class="tags-select" placement="bottom"> <EPopover v-model="tagShow" class="tags-select" placement="bottom">
<TagSelect <TaskTagSelect
v-model="tagValue" v-model="tagValue"
:data-sources="tagData" :data-sources="tagData"
:loading="tagLoad > 0" :loading="tagLoad > 0"
:max="10"/> :max="10"
@add="onTagAdd"/>
<div slot="reference"> <div slot="reference">
<TaskTag :tags="getTag"> <TaskTag :tags="getTag">
<li v-if="getTag.length === 0" slot="end" class="add-icon"></li> <li v-if="getTag.length === 0" slot="end" class="add-icon"></li>
@ -547,6 +548,8 @@
<Button @click="historyShow=false">{{$L('关闭')}}</Button> <Button @click="historyShow=false">{{$L('关闭')}}</Button>
</div> </div>
</Modal> </Modal>
<!--标签添加-->
<TaskTagAdd ref="addTag" :project-id="taskDetail.project_id" @on-save="onTagAddSave"/>
</div> </div>
</template> </template>
@ -561,19 +564,21 @@ 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 TaskTag from "./ProjectTaskTag/tags.vue";
import TagSelect from "./ProjectTaskTag/select.vue"; import TaskTagSelect 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";
import TaskTagAdd from "./ProjectTaskTag/add.vue";
export default { export default {
name: "TaskDetail", name: "TaskDetail",
components: { components: {
TaskTagAdd,
TaskContentHistory, TaskContentHistory,
TEditorTask, TEditorTask,
UserSelect, UserSelect,
TaskTag, TaskTag,
TagSelect, TaskTagSelect,
TaskExistTips, TaskExistTips,
ChatInput, ChatInput,
TaskMenu, TaskMenu,
@ -1988,6 +1993,27 @@ export default {
onTaskQuick(time, type) { onTaskQuick(time, type) {
this.$set(this.delayTaskForm, 'time', Math.round(time * 100) / 100) this.$set(this.delayTaskForm, 'time', Math.round(time * 100) / 100)
this.$set(this.delayTaskForm, 'type', type) this.$set(this.delayTaskForm, 'type', type)
},
onTagAdd() {
//
this.tagValue = this.getTag;
this.tagBakValue = $A.cloneJSON(this.tagValue);
//
this.tagShow = false
this.$refs.addTag.onOpen(null)
},
onTagAddSave(result) {
const current = this.tagValue;
const addData = result.filter(({data}) => data && data.id > 0).map(({data}) => data);
// 使
const mergedTags = [
...addData,
...current.filter(tag => !addData.some(newTag => newTag.name === tag.name))
];
//
this.updateData('tag', mergedTags);
} }
} }
} }