2024-01-03 11:23:06 +08:00
|
|
|
|
<template>
|
|
|
|
|
<div class="task-detail" id="task-detail">
|
|
|
|
|
<a-spin class="task-detail-spin" :spinning="loading">
|
|
|
|
|
<div class="task-header" :class="{'disabled': task.deleted}">
|
|
|
|
|
<span class="head-title" v-if="!task.deleted">
|
|
|
|
|
<span v-if="task.parentTask">
|
|
|
|
|
<span class="muted">属于任务:</span>
|
|
|
|
|
<!--<a class="text-default" @click="init(task.pcode)">{{task.parentTask.name}}</a>-->
|
|
|
|
|
<a-breadcrumb separator=">" class="breadcrumb text-default">
|
|
|
|
|
<a-breadcrumb-item v-for="parent in task.parentTasks" :key="parent.code">
|
|
|
|
|
<a class="text-default" @click="init(parent.code)">{{parent.name}}</a>
|
|
|
|
|
</a-breadcrumb-item>
|
|
|
|
|
</a-breadcrumb>
|
|
|
|
|
</span>
|
|
|
|
|
<span v-else>{{task.projectName}} · {{task.stageName}}</span>
|
|
|
|
|
</span>
|
|
|
|
|
<span class="head-title muted" v-else>
|
|
|
|
|
<a-icon type="delete"/>
|
|
|
|
|
<span>任务已在回收站中,不可修改</span>
|
|
|
|
|
</span>
|
|
|
|
|
<span class="header-action text-right">
|
|
|
|
|
<template v-if="!task.deleted">
|
|
|
|
|
<a-tooltip :mouseEnterDelay="0.5">
|
|
|
|
|
<template slot="title">
|
|
|
|
|
<span>复制任务链接</span>
|
|
|
|
|
</template>
|
|
|
|
|
<a class="action-item muted" v-clipboard="taskLink" @success="copyLink"><a-icon
|
|
|
|
|
type="link"/></a>
|
|
|
|
|
</a-tooltip>
|
|
|
|
|
<a-tooltip :mouseEnterDelay="0.5">
|
|
|
|
|
<template slot="title">
|
|
|
|
|
<span>点个赞</span>
|
|
|
|
|
</template>
|
|
|
|
|
<a class="action-item muted" :class="{'active': task.liked}" @click="like(!task.liked)">
|
|
|
|
|
<a-icon type="like"/>
|
|
|
|
|
<span v-show="task.like">{{task.like}}</span>
|
|
|
|
|
</a>
|
|
|
|
|
</a-tooltip>
|
|
|
|
|
<a-dropdown :trigger="['click']" placement="bottomCenter" v-model="visibleTaskMenu">
|
|
|
|
|
<a-tooltip :mouseEnterDelay="0.5">
|
|
|
|
|
<template slot="title">
|
|
|
|
|
<span>打开菜单</span>
|
|
|
|
|
</template>
|
|
|
|
|
<a class="action-item muted"><a-icon type="ellipsis"/></a>
|
|
|
|
|
</a-tooltip>
|
|
|
|
|
<a-menu @click="doTask" class="field-right-menu"
|
|
|
|
|
slot="overlay">
|
|
|
|
|
<a-menu-item key="copy">
|
|
|
|
|
<a-icon type="copy"/>
|
|
|
|
|
<span>复制任务 *</span>
|
|
|
|
|
</a-menu-item>
|
|
|
|
|
<a-menu-item key="move">
|
|
|
|
|
<a-icon type="snippets"/>
|
|
|
|
|
<span>移动任务 *</span>
|
|
|
|
|
</a-menu-item>
|
|
|
|
|
<a-menu-item key="star">
|
|
|
|
|
<a-icon type="star"></a-icon>
|
|
|
|
|
<span v-if="task.stared">取消收藏</span>
|
|
|
|
|
<span v-else>收藏任务</span>
|
|
|
|
|
</a-menu-item>
|
|
|
|
|
<a-menu-item key="recycle">
|
|
|
|
|
<a-icon type="delete"></a-icon>
|
|
|
|
|
<span>移到回收站</span>
|
|
|
|
|
</a-menu-item>
|
|
|
|
|
<a-menu-item key="open">
|
|
|
|
|
<a-icon type="book"/>
|
|
|
|
|
<span>以新标签页打开</span>
|
|
|
|
|
</a-menu-item>
|
|
|
|
|
<a-menu-divider/>
|
|
|
|
|
<a-menu-item key="private">
|
|
|
|
|
<a-icon :type="task.private ? 'lock' : 'unlock'"/>
|
|
|
|
|
<span>隐私模式</span>
|
|
|
|
|
<a class="menu-action text-primary">
|
|
|
|
|
<span v-if="task.private">已开启</span>
|
|
|
|
|
<span v-else>已关闭</span>
|
|
|
|
|
</a>
|
|
|
|
|
<div class="menu-desc muted">
|
|
|
|
|
<span v-if="task.private">仅参与者可见</span>
|
|
|
|
|
<span v-else>所有成员可见</span>
|
|
|
|
|
</div>
|
|
|
|
|
</a-menu-item>
|
|
|
|
|
</a-menu>
|
|
|
|
|
</a-dropdown>
|
|
|
|
|
</template>
|
|
|
|
|
<template v-else>
|
|
|
|
|
<a class="action-item muted" @click="recoveryTask">
|
|
|
|
|
<a-icon type="undo"/> <span>恢复内容</span>
|
|
|
|
|
</a>
|
|
|
|
|
<a class="action-item muted" @click="deleteTask">
|
|
|
|
|
<a-icon type="delete"/> <span>彻底删除</span>
|
|
|
|
|
</a>
|
|
|
|
|
</template>
|
|
|
|
|
<a-tooltip :mouseEnterDelay="0.5">
|
|
|
|
|
<template slot="title">
|
|
|
|
|
<span>关闭面板</span>
|
|
|
|
|
</template>
|
|
|
|
|
<a class="action-item muted" @click="detailClose"><a-icon type="close"/></a>
|
|
|
|
|
</a-tooltip>
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="task-wrap">
|
|
|
|
|
<div class="task-content">
|
|
|
|
|
<div class="content-left">
|
|
|
|
|
<vue-scroll :ops="scrollOps">
|
|
|
|
|
<div class="task-title" :class="{'disabled': task.deleted}">
|
|
|
|
|
<a-input ref="inputTitle" auto-focus @blur="doName" v-model="task.name" size="large"
|
|
|
|
|
v-show="showEditName"/>
|
|
|
|
|
<a-tooltip :mouseEnterDelay="0.5" v-if="!task.deleted">
|
|
|
|
|
<template slot="title">
|
|
|
|
|
<span>点击即可编辑</span>
|
|
|
|
|
</template>
|
|
|
|
|
<div class="title-text" @click="editTitle" v-show="!showEditName">
|
|
|
|
|
{{task.name}}
|
|
|
|
|
</div>
|
|
|
|
|
</a-tooltip>
|
|
|
|
|
<div v-else class="title-text" v-show="!showEditName">
|
|
|
|
|
{{task.name}}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="task-basic-attrs-view muted">
|
|
|
|
|
<div class="field-list" :class="{'disabled': task.deleted}">
|
|
|
|
|
<div class="component-mount">
|
|
|
|
|
<div class="field">
|
|
|
|
|
<div class="field-left">
|
|
|
|
|
<a-icon type="check-square"/>
|
|
|
|
|
<span class="field-name">完成状态</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="field-right">
|
|
|
|
|
<a-dropdown :trigger="['click']"
|
|
|
|
|
:disabled="!!task.deleted || !!task.hasUnDone"
|
|
|
|
|
:class="{'disabled': task.hasUnDone}">
|
|
|
|
|
<a-tooltip placement="top">
|
|
|
|
|
<template slot="title">
|
|
|
|
|
<span v-if="task.hasUnDone" style="font-size: 12px;">子任务尚未全部完成,无法完成父任务</span>
|
|
|
|
|
</template>
|
|
|
|
|
<span>
|
|
|
|
|
<!--<a-icon type="check-square"/>-->
|
|
|
|
|
<a-tag v-if="task.done" color="green">已完成</a-tag>
|
|
|
|
|
<span v-show="!task.done">未完成</span>
|
|
|
|
|
</span>
|
|
|
|
|
</a-tooltip>
|
|
|
|
|
<a-menu class="field-right-menu" slot="overlay"
|
|
|
|
|
:selectable="false"
|
|
|
|
|
@click="taskDone(task.code,!task.done)">
|
|
|
|
|
<a-menu-item key="done">
|
|
|
|
|
<div class="menu-item-content">
|
|
|
|
|
<a-tag color="green">已完成</a-tag>
|
|
|
|
|
<a-icon type="check" class="check muted"
|
|
|
|
|
v-show="task.done"></a-icon>
|
|
|
|
|
</div>
|
|
|
|
|
</a-menu-item>
|
|
|
|
|
<a-menu-item key="undone">
|
|
|
|
|
<div class="menu-item-content">
|
|
|
|
|
<a-tag color="grey">未完成</a-tag>
|
|
|
|
|
<a-icon type="check" class="check muted"
|
|
|
|
|
v-show="!task.done"></a-icon>
|
|
|
|
|
</div>
|
|
|
|
|
</a-menu-item>
|
|
|
|
|
</a-menu>
|
|
|
|
|
</a-dropdown>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="component-mount">
|
|
|
|
|
<div class="field">
|
|
|
|
|
<div class="field-left">
|
|
|
|
|
<a-icon type="deployment-unit"/>
|
|
|
|
|
<span class="field-name">执行状态</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="field-right">
|
|
|
|
|
<a-dropdown :trigger="['click']"
|
|
|
|
|
:disabled="!!task.deleted">
|
|
|
|
|
<span>{{task.statusText}}</span>
|
|
|
|
|
<a-menu class="field-right-menu" slot="overlay"
|
|
|
|
|
:selectable="false"
|
|
|
|
|
@click="taskStatusChange">
|
|
|
|
|
<a-menu-item :key="status.id" v-for="(status, index) in taskStatusList">
|
|
|
|
|
<div class="menu-item-content">
|
|
|
|
|
<span color="green">{{status.name}}</span>
|
|
|
|
|
<a-icon type="check" class="check muted"
|
|
|
|
|
v-show="task.status == status.id"></a-icon>
|
|
|
|
|
</div>
|
|
|
|
|
</a-menu-item>
|
|
|
|
|
</a-menu>
|
|
|
|
|
</a-dropdown>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="component-mount">
|
|
|
|
|
<div class="field">
|
|
|
|
|
<div class="field-left">
|
|
|
|
|
<a-icon type="user"/>
|
|
|
|
|
<span class="field-name">执行者</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="field-right">
|
|
|
|
|
<a-dropdown
|
|
|
|
|
:trigger="['click']"
|
|
|
|
|
v-model="visibleTaskMemberMenu"
|
|
|
|
|
:disabled="!!task.deleted"
|
|
|
|
|
placement="bottomCenter"
|
|
|
|
|
>
|
|
|
|
|
<a-tooltip :mouseEnterDelay="0.5" v-if="!task.deleted">
|
|
|
|
|
<template slot="title">
|
|
|
|
|
<span>点击设置执行者</span>
|
|
|
|
|
</template>
|
|
|
|
|
<div class="field-flex">
|
|
|
|
|
<template v-if="task.executor">
|
|
|
|
|
<a-avatar :src="task.executor.avatar" icon="user"
|
|
|
|
|
size="small"/>
|
|
|
|
|
<a class="muted name">{{task.executor.name}}</a>
|
|
|
|
|
</template>
|
|
|
|
|
<template v-if="!task.executor">
|
|
|
|
|
<a-avatar icon="user" size="small"/>
|
|
|
|
|
<a class="muted name">待认领</a>
|
|
|
|
|
</template>
|
|
|
|
|
</div>
|
|
|
|
|
</a-tooltip>
|
|
|
|
|
<div class="field-flex" v-else>
|
|
|
|
|
<template v-if="task.executor">
|
|
|
|
|
<a-avatar :src="task.executor.avatar" icon="user"
|
|
|
|
|
size="small"/>
|
|
|
|
|
<a class="muted name">{{task.executor.name}}</a>
|
|
|
|
|
</template>
|
|
|
|
|
<template v-if="!task.executor">
|
|
|
|
|
<a-avatar icon="user" size="small"/>
|
|
|
|
|
<a class="muted name">待认领</a>
|
|
|
|
|
</template>
|
|
|
|
|
</div>
|
|
|
|
|
<div slot="overlay">
|
|
|
|
|
<task-member-menu
|
|
|
|
|
v-if="visibleTaskMemberMenu"
|
|
|
|
|
:projectCode="projectCodeCurrent"
|
|
|
|
|
:taskCode="code"
|
|
|
|
|
@close="init(false)"
|
|
|
|
|
@inviteProjectMember="
|
|
|
|
|
showInviteMember = true,
|
|
|
|
|
visibleTaskMemberMenu = false"
|
|
|
|
|
></task-member-menu>
|
|
|
|
|
</div>
|
|
|
|
|
</a-dropdown>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="component-mount">
|
|
|
|
|
<div class="field">
|
|
|
|
|
<div class="field-left">
|
|
|
|
|
<a-icon type="calendar"/>
|
|
|
|
|
<span class="field-name">时间</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="field-right field-date">
|
|
|
|
|
<template v-if="task.openBeginTime">
|
|
|
|
|
<a-dropdown :trigger="['click']" v-model="showBeginTime"
|
|
|
|
|
:disabled="!!task.deleted">
|
|
|
|
|
<a-tooltip :mouseEnterDelay="0.5" v-if="!task.deleted">
|
|
|
|
|
<template slot="title">
|
|
|
|
|
<span>点击设置开始时间</span>
|
|
|
|
|
</template>
|
|
|
|
|
<div class="field-flex">
|
|
|
|
|
<a class="muted name" style="margin: 0">
|
|
|
|
|
<template v-if="!task.setBeginTime">设置开始时间
|
|
|
|
|
</template>
|
|
|
|
|
<template v-else>{{task.begin_time_format}}
|
|
|
|
|
</template>
|
|
|
|
|
</a>
|
|
|
|
|
</div>
|
|
|
|
|
</a-tooltip>
|
|
|
|
|
<div class="field-flex" v-else>
|
|
|
|
|
<a class="muted name" style="margin: 0">
|
|
|
|
|
<template v-if="!task.setBeginTime">设置开始时间</template>
|
|
|
|
|
<template v-else>{{task.begin_time_format}}</template>
|
|
|
|
|
</a>
|
|
|
|
|
</div>
|
|
|
|
|
<div slot="overlay">
|
|
|
|
|
<a-date-picker
|
|
|
|
|
v-model="task.begin_time"
|
|
|
|
|
size="small"
|
|
|
|
|
format="MM月DD日 HH:mm"
|
|
|
|
|
showTime
|
|
|
|
|
allowClear
|
|
|
|
|
:showToday="false"
|
|
|
|
|
:open="showBeginTime"
|
|
|
|
|
@ok="doBeginTime(true)"
|
|
|
|
|
>
|
|
|
|
|
<template slot="renderExtraFooter">
|
|
|
|
|
<a style="position: absolute;" size="small"
|
|
|
|
|
@click="doBeginTime(false)">清除</a>
|
|
|
|
|
</template>
|
|
|
|
|
</a-date-picker>
|
|
|
|
|
</div>
|
|
|
|
|
</a-dropdown>
|
|
|
|
|
<span class="m-l-sm m-r-sm">-</span>
|
|
|
|
|
</template>
|
|
|
|
|
<a-dropdown :trigger="['click']" v-model="showEndTime"
|
|
|
|
|
:disabled="!!task.deleted">
|
|
|
|
|
<a-tooltip :mouseEnterDelay="0.5" v-if="!task.deleted">
|
|
|
|
|
<template slot="title">
|
|
|
|
|
<span>点击设置截止时间</span>
|
|
|
|
|
</template>
|
|
|
|
|
<div class="field-flex">
|
|
|
|
|
<a class="muted name" style="margin: 0">
|
|
|
|
|
<template v-if="!task.setEndTime">设置截止时间</template>
|
|
|
|
|
<template v-else>{{task.end_time_format}}</template>
|
|
|
|
|
</a>
|
|
|
|
|
</div>
|
|
|
|
|
</a-tooltip>
|
|
|
|
|
<div class="field-flex" v-else>
|
|
|
|
|
<a class="muted name" style="margin: 0">
|
|
|
|
|
<template v-if="!task.setEndTime">设置截止时间</template>
|
|
|
|
|
<template v-else>{{task.end_time_format}}</template>
|
|
|
|
|
</a>
|
|
|
|
|
</div>
|
|
|
|
|
<div slot="overlay">
|
|
|
|
|
<a-date-picker
|
|
|
|
|
v-model="task.end_time"
|
|
|
|
|
size="small"
|
|
|
|
|
format="MM月DD日 HH:mm"
|
|
|
|
|
showTime
|
|
|
|
|
allowClear
|
|
|
|
|
:showToday="false"
|
|
|
|
|
:open="showEndTime"
|
|
|
|
|
@ok="doEndTime(true)"
|
|
|
|
|
>
|
|
|
|
|
<template slot="renderExtraFooter">
|
|
|
|
|
<a style="position: absolute;" size="small"
|
|
|
|
|
@click="doEndTime(false)">清除</a>
|
|
|
|
|
</template>
|
|
|
|
|
</a-date-picker>
|
|
|
|
|
</div>
|
|
|
|
|
</a-dropdown>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="component-mount pink-bg">
|
|
|
|
|
<div class="field">
|
|
|
|
|
<div class="field-left">
|
|
|
|
|
<a-icon type="file-text"/>
|
|
|
|
|
<span class="field-name">备注</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="field-right width-block">
|
|
|
|
|
<div class="task-description" :class="{'disabled': task.deleted}"
|
|
|
|
|
v-show="!showTaskDescriptionEdit"
|
|
|
|
|
@click="showTaskDesc">
|
|
|
|
|
<div class="description-txt img-preview-content" v-show="task.description"
|
|
|
|
|
v-html="task.description"></div>
|
|
|
|
|
<span v-show="!task.description">添加备注</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="m-t-sm" v-if="hasMoreDesc">
|
|
|
|
|
<a v-show="!showMoreDesc"
|
|
|
|
|
@click="checkShowMoreDesc(true)">显示更多</a>
|
|
|
|
|
<a v-show="showMoreDesc"
|
|
|
|
|
@click="checkShowMoreDesc(false)">收起备注</a>
|
|
|
|
|
</div>
|
|
|
|
|
<div v-show="showTaskDescriptionEdit">
|
|
|
|
|
<editor ref="vueWangeditor"
|
|
|
|
|
id="editor"
|
|
|
|
|
:uploadImgServer="editorConfig.uploadImgServer"
|
|
|
|
|
:uploadImgHeaders="editorConfig.uploadImgHeaders"
|
|
|
|
|
:menus="editorConfig.menus"
|
|
|
|
|
></editor>
|
|
|
|
|
<div class="action-btn pull-right">
|
|
|
|
|
<a type="text" class="cancel-text muted"
|
|
|
|
|
@click="showTaskDescriptionEdit = false,initContent(false)">
|
|
|
|
|
取消
|
|
|
|
|
</a>
|
|
|
|
|
<a-button type="primary" htmlType='submit'
|
|
|
|
|
class="middle-btn"
|
|
|
|
|
@click="doContent">保存
|
|
|
|
|
</a-button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="component-mount">
|
|
|
|
|
<div class="field">
|
|
|
|
|
<div class="field-left">
|
|
|
|
|
<a-icon type="bulb"/>
|
|
|
|
|
<span class="field-name">优先级</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="field-right">
|
|
|
|
|
<a-dropdown :trigger="['click']" :disabled="!!task.deleted">
|
|
|
|
|
<span>
|
|
|
|
|
<a-tag :color="priColor(task.pri)">{{task.priText}}</a-tag>
|
|
|
|
|
</span>
|
|
|
|
|
<a-menu class="field-right-menu" slot="overlay" @click="doPri"
|
|
|
|
|
:selectable="false">
|
|
|
|
|
<a-menu-item :key="0">
|
|
|
|
|
<div class="menu-item-content">
|
|
|
|
|
<a-tag :color="priColor(0)">普通</a-tag>
|
|
|
|
|
<a-icon type="check" class="check muted"
|
|
|
|
|
v-show="task.pri == 0"></a-icon>
|
|
|
|
|
</div>
|
|
|
|
|
</a-menu-item>
|
|
|
|
|
<a-menu-item :key="1">
|
|
|
|
|
<div class="menu-item-content">
|
|
|
|
|
<a-tag :color="priColor(1)">紧急</a-tag>
|
|
|
|
|
<a-icon type="check" class="check muted"
|
|
|
|
|
v-show="task.pri == 1"></a-icon>
|
|
|
|
|
</div>
|
|
|
|
|
</a-menu-item>
|
|
|
|
|
<a-menu-item :key="2">
|
|
|
|
|
<div class="menu-item-content">
|
|
|
|
|
<a-tag :color="priColor(2)">非常紧急</a-tag>
|
|
|
|
|
<a-icon type="check" class="check muted"
|
|
|
|
|
v-show="task.pri == 2"></a-icon>
|
|
|
|
|
</div>
|
|
|
|
|
</a-menu-item>
|
|
|
|
|
</a-menu>
|
|
|
|
|
</a-dropdown>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="component-mount task-tag">
|
|
|
|
|
<div class="field">
|
|
|
|
|
<div class="field-left">
|
|
|
|
|
<a-icon type="tag"/>
|
|
|
|
|
<span class="field-name">标签</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="field-right">
|
|
|
|
|
<div class="inline-block">
|
|
|
|
|
<a-tag :color="tag.tag.color" v-for="(tag,index) in task.tags"
|
|
|
|
|
:key="index">
|
|
|
|
|
{{tag.tag.name}}
|
|
|
|
|
<a-icon type="close" @click="removeTag(tag.tag,index)"/>
|
|
|
|
|
</a-tag>
|
|
|
|
|
</div>
|
|
|
|
|
<a-dropdown
|
|
|
|
|
:trigger="['click']"
|
|
|
|
|
v-model="visibleTaskTagMenu"
|
|
|
|
|
:disabled="!!task.deleted"
|
|
|
|
|
placement="bottomCenter"
|
|
|
|
|
>
|
|
|
|
|
<a-tooltip :mouseEnterDelay="0.5" v-if="!task.deleted">
|
|
|
|
|
<template slot="title">
|
|
|
|
|
<span>添加标签</span>
|
|
|
|
|
</template>
|
|
|
|
|
<div class="inline-block">
|
|
|
|
|
<a-icon class="member-item invite" type="plus-circle"
|
|
|
|
|
theme="twoTone"/>
|
|
|
|
|
</div>
|
|
|
|
|
</a-tooltip>
|
|
|
|
|
<div class="inline-block">
|
|
|
|
|
<a-icon class="member-item invite" type="plus-circle"
|
|
|
|
|
theme="twoTone"/>
|
|
|
|
|
</div>
|
|
|
|
|
<div slot="overlay">
|
|
|
|
|
<task-tag-menu
|
|
|
|
|
v-if="visibleTaskTagMenu"
|
|
|
|
|
:projectCode="projectCodeCurrent"
|
|
|
|
|
:taskCode="code"
|
|
|
|
|
@change="taskTagChange"
|
|
|
|
|
@update="taskTagUpdate"
|
|
|
|
|
@delete="taskTagDelete"
|
|
|
|
|
></task-tag-menu>
|
|
|
|
|
</div>
|
|
|
|
|
</a-dropdown>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="component-mount">
|
|
|
|
|
<div class="field">
|
|
|
|
|
<div class="field-left">
|
|
|
|
|
<a-icon type="bars"/>
|
|
|
|
|
<span class="field-name">子任务 <span v-show="childTaskList.length"> · {{childTaskDoneNum}}/{{childTaskList.length}}</span></span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="field-right width-block">
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="component-mount">
|
|
|
|
|
<div class="field">
|
|
|
|
|
<div class="block-field width-block">
|
|
|
|
|
<div class="task-child">
|
|
|
|
|
<div class="task-list" v-show="childTaskList.length">
|
|
|
|
|
<div v-for="done in [0,1]" :key="done">
|
|
|
|
|
<div
|
|
|
|
|
v-for="(childTask, index) in childTaskList"
|
|
|
|
|
:key="childTask.code">
|
|
|
|
|
<div class="list-item task"
|
|
|
|
|
v-if="childTask.done == done"
|
|
|
|
|
>
|
|
|
|
|
<a-tooltip placement="top">
|
|
|
|
|
<template slot="title">
|
|
|
|
|
<span v-if="childTask.parentDone"
|
|
|
|
|
style="font-size: 12px;">父任务已完成,无法重做子任务</span>
|
|
|
|
|
<span v-else-if="childTask.hasUnDone"
|
|
|
|
|
style="font-size: 12px;">子任务尚未全部完成,无法完成父任务</span>
|
|
|
|
|
</template>
|
|
|
|
|
<div class="check-box-wrapper task-item"
|
|
|
|
|
@click.stop="()=>{if(task.deleted || childTask.parentDone || childTask.hasUnDone) return false;taskDone(childTask.code,!childTask.done,index,'child')}">
|
|
|
|
|
<a-icon class="check-box"
|
|
|
|
|
:class="{'disabled': task.deleted || childTask.parentDone || childTask.hasUnDone}"
|
|
|
|
|
:type="childTask.done ? 'check-square' : 'border'"
|
|
|
|
|
:style="{fontSize:'16px'}"/>
|
|
|
|
|
</div>
|
|
|
|
|
<!--<a class="task-item check-box"
|
|
|
|
|
:class="{'disabled': task.deleted || childTask.parentDone || childTask.hasUnDone}"
|
|
|
|
|
@click="()=>{if(task.deleted || childTask.parentDone || childTask.hasUnDone) return false;taskDone(childTask.code,!childTask.done,index,'child')}">
|
|
|
|
|
<a-icon type="check"
|
|
|
|
|
v-show="childTask.done"/>
|
|
|
|
|
</a>-->
|
|
|
|
|
</a-tooltip>
|
|
|
|
|
<a-dropdown :trigger="['click']"
|
|
|
|
|
v-model="childTask.visibleChildTaskMemberMenu"
|
|
|
|
|
:disabled="!!task.deleted"
|
|
|
|
|
placement="bottomCenter"
|
|
|
|
|
|
|
|
|
|
>
|
|
|
|
|
<a-tooltip :mouseEnterDelay="0.5">
|
|
|
|
|
<template slot="title">
|
|
|
|
|
<span v-if="childTask.executor">{{childTask.executor.name}}</span>
|
|
|
|
|
<span v-else>待认领</span>
|
|
|
|
|
</template>
|
|
|
|
|
<a-avatar
|
|
|
|
|
v-if="childTask.executor"
|
|
|
|
|
class="task-item"
|
|
|
|
|
:class="{'disabled': task.deleted}"
|
|
|
|
|
|
|
|
|
|
size="small"
|
|
|
|
|
icon="user"
|
|
|
|
|
:src="childTask.executor.avatar"
|
|
|
|
|
></a-avatar>
|
|
|
|
|
<a-avatar
|
|
|
|
|
v-else
|
|
|
|
|
class="task-item"
|
|
|
|
|
:class="{'disabled': task.deleted}"
|
|
|
|
|
|
|
|
|
|
size="small"
|
|
|
|
|
icon="user"
|
|
|
|
|
></a-avatar>
|
|
|
|
|
</a-tooltip>
|
|
|
|
|
<div slot="overlay">
|
|
|
|
|
<task-member-menu
|
|
|
|
|
v-if="childTask.visibleChildTaskMemberMenu"
|
|
|
|
|
:projectCode="projectCodeCurrent"
|
|
|
|
|
:taskCode="childTask.code"
|
|
|
|
|
:isCommit="true"
|
|
|
|
|
@close="childTask.visibleChildTaskMemberMenu = false;getChildTasks();"
|
|
|
|
|
@inviteProjectMember="
|
|
|
|
|
showInviteMember = true,
|
|
|
|
|
childTask.visibleChildTaskMemberMenu = false"
|
|
|
|
|
></task-member-menu>
|
|
|
|
|
</div>
|
|
|
|
|
</a-dropdown>
|
|
|
|
|
<!-- <a-tooltip :mouseEnterDelay="0.5">
|
|
|
|
|
<template slot="title">
|
|
|
|
|
<span v-if="childTask.executor">{{childTask.executor.name}}</span>
|
|
|
|
|
<span v-else>待认领</span>
|
|
|
|
|
</template>
|
|
|
|
|
<a-avatar
|
|
|
|
|
v-if="childTask.executor"
|
|
|
|
|
class="task-item"
|
|
|
|
|
:class="{'disabled': task.deleted}"
|
|
|
|
|
|
|
|
|
|
size="small"
|
|
|
|
|
icon="user"
|
|
|
|
|
:src="childTask.executor.avatar"
|
|
|
|
|
></a-avatar>
|
|
|
|
|
<a-avatar
|
|
|
|
|
v-else
|
|
|
|
|
class="task-item"
|
|
|
|
|
:class="{'disabled': task.deleted}"
|
|
|
|
|
|
|
|
|
|
size="small"
|
|
|
|
|
icon="user"
|
|
|
|
|
></a-avatar>
|
|
|
|
|
</a-tooltip>-->
|
|
|
|
|
<div class="task-item task-title"
|
|
|
|
|
@click.stop="init(childTask.code)">
|
|
|
|
|
<div class="title-text"
|
|
|
|
|
:class="{'done': childTask.done}"
|
|
|
|
|
>
|
|
|
|
|
{{childTask.name}}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<a class="muted" @click.stop="init(childTask.code)">
|
|
|
|
|
<a-icon class="task-item" type="right"/>
|
|
|
|
|
</a>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="task-list" v-show="showChildTask">
|
|
|
|
|
<div class="add-task">
|
|
|
|
|
<div class="list-item task">
|
|
|
|
|
<span class="task-item check-box"></span>
|
|
|
|
|
|
|
|
|
|
<a-dropdown :trigger="['click']"
|
|
|
|
|
v-model="visibleChildTaskMemberMenu"
|
|
|
|
|
:disabled="!!task.deleted"
|
|
|
|
|
placement="bottomCenter"
|
|
|
|
|
|
|
|
|
|
>
|
|
|
|
|
<a-tooltip :mouseEnterDelay="0.5">
|
|
|
|
|
<template slot="title">
|
|
|
|
|
<span v-if="childExecutor">{{childExecutor.name}}</span>
|
|
|
|
|
<span v-else>待认领</span>
|
|
|
|
|
</template>
|
|
|
|
|
<div class="field-flex">
|
|
|
|
|
<template v-if="childExecutor">
|
|
|
|
|
<a-avatar class="task-item"
|
|
|
|
|
:src="childExecutor.avatar"
|
|
|
|
|
icon="user"
|
|
|
|
|
size="small"/>
|
|
|
|
|
</template>
|
|
|
|
|
<template v-if="!childExecutor">
|
|
|
|
|
<a-avatar class="task-item"
|
|
|
|
|
|
|
|
|
|
icon="user"
|
|
|
|
|
size="small"/>
|
|
|
|
|
</template>
|
|
|
|
|
</div>
|
|
|
|
|
</a-tooltip>
|
|
|
|
|
<div slot="overlay">
|
|
|
|
|
<task-member-menu
|
|
|
|
|
v-if="visibleChildTaskMemberMenu"
|
|
|
|
|
:projectCode="projectCodeCurrent"
|
|
|
|
|
:taskCode="childExecutor ? code : ''"
|
|
|
|
|
:isCommit="false"
|
|
|
|
|
@close="updateChildExecutor"
|
|
|
|
|
@inviteProjectMember="
|
|
|
|
|
showInviteChildTaskMember = true,
|
|
|
|
|
visibleChildTaskMemberMenu = false"
|
|
|
|
|
></task-member-menu>
|
|
|
|
|
</div>
|
|
|
|
|
</a-dropdown>
|
|
|
|
|
<div class="task-item task-input">
|
|
|
|
|
<a-input v-model="childTaskName"
|
|
|
|
|
@pressEnter="createTask"/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="action-btn text-right">
|
|
|
|
|
<a type="text" class="cancel-text muted"
|
|
|
|
|
@click="showChildTask = false">取消</a>
|
|
|
|
|
<a-button type="primary" htmlType='submit'
|
|
|
|
|
class="middle-btn"
|
|
|
|
|
@click="createTask">保存
|
|
|
|
|
</a-button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<a-tooltip placement="top">
|
|
|
|
|
<template slot="title">
|
|
|
|
|
<span v-if="task.done" style="font-size: 12px;">父任务已完成,无法添加新的子任务</span>
|
|
|
|
|
</template>
|
|
|
|
|
<a class="add-handler"
|
|
|
|
|
:class="{'disabled': task.done}"
|
|
|
|
|
v-show="!showChildTask"
|
|
|
|
|
@click="()=>{if (task.deleted || task.done) return false; showChildTask = true}">
|
|
|
|
|
<a-icon type="plus" style="margin-right: 6px;"/>
|
|
|
|
|
添加子任务
|
|
|
|
|
</a>
|
|
|
|
|
</a-tooltip>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="component-mount">
|
|
|
|
|
<div class="field">
|
|
|
|
|
<div class="field-left" style="width: 100%">
|
|
|
|
|
<a-icon type="clock-circle"/>
|
|
|
|
|
<span class="field-name">工时
|
|
|
|
|
<span v-if="workTimeList.length"> · 实际工时 {{workTimeTotal}} 小时,工时记录 {{workTimeList.length}} 条,预估工时 {{task.work_time}} 小时 <a
|
|
|
|
|
class="muted m-l-sm" @click="doPlainWorkTime">
|
|
|
|
|
<a-icon class="task-item" type="edit"/>
|
|
|
|
|
</a>
|
|
|
|
|
</span>
|
|
|
|
|
<span v-else>
|
|
|
|
|
<span v-if="task.work_time"> · 预估工时 {{task.work_time}} 小时</span>
|
|
|
|
|
<a-tooltip>
|
|
|
|
|
<template slot="title">
|
|
|
|
|
<span>设置预估工时</span>
|
|
|
|
|
</template>
|
|
|
|
|
<a class="muted m-l-sm" @click="doPlainWorkTime">
|
|
|
|
|
<a-icon class="task-item" type="edit"/>
|
|
|
|
|
</a>
|
|
|
|
|
</a-tooltip>
|
|
|
|
|
</span>
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="component-mount">
|
|
|
|
|
<div class="field">
|
|
|
|
|
<div class="block-field width-block">
|
|
|
|
|
<div class="task-child">
|
|
|
|
|
<div class="task-list" v-show="workTimeList.length">
|
|
|
|
|
<div
|
|
|
|
|
v-for="(workTime, index) in workTimeList"
|
|
|
|
|
:key="workTime.code">
|
|
|
|
|
<div class="list-item task m-l-xs">
|
|
|
|
|
<div class="task-item task-title hover-none">
|
|
|
|
|
<div class="title-text"
|
|
|
|
|
>
|
|
|
|
|
<a-tooltip :mouseEnterDelay="0.5">
|
|
|
|
|
<template slot="title">
|
|
|
|
|
<span v-if="workTime.member">{{workTime.member.name}}</span>
|
|
|
|
|
<span v-else>待认领</span>
|
|
|
|
|
</template>
|
|
|
|
|
<a-avatar
|
|
|
|
|
class="task-item"
|
|
|
|
|
size="small"
|
|
|
|
|
icon="user"
|
|
|
|
|
:src="workTime.member.avatar"
|
|
|
|
|
></a-avatar>
|
|
|
|
|
</a-tooltip>
|
|
|
|
|
{{workTime.member.name}}
|
|
|
|
|
{{moment(workTime.begin_time).format('MM月DD日 HH:mm')}}开始 工时为
|
|
|
|
|
{{workTime.num}} 小时
|
|
|
|
|
<div class="muted"
|
|
|
|
|
v-show="workTime.content"
|
|
|
|
|
style="padding-left: 40px;margin-top: 6px;">
|
|
|
|
|
{{workTime.content}}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<a class="muted" @click="doWorkTime(workTime)">
|
|
|
|
|
<a-icon class="task-item" type="edit"/>
|
|
|
|
|
</a>
|
|
|
|
|
<a class="muted"
|
|
|
|
|
@click="deleteWorkTime(workTime, index)">
|
|
|
|
|
<a-icon class="task-item" type="delete"/>
|
|
|
|
|
</a>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<a-tooltip placement="top">
|
|
|
|
|
<a class="add-handler" @click="doWorkTime(false)">
|
|
|
|
|
<a-icon type="plus" style="margin-right: 6px;"/>
|
|
|
|
|
添加工时
|
|
|
|
|
</a>
|
|
|
|
|
</a-tooltip>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="component-mount">
|
|
|
|
|
<div class="field">
|
|
|
|
|
<div class="field-left">
|
|
|
|
|
<a-icon type="paper-clip"/>
|
|
|
|
|
<span class="field-name">关联文件</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="field-right width-block">
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="component-mount">
|
|
|
|
|
<div class="field">
|
|
|
|
|
<div class="block-field width-block">
|
|
|
|
|
<div class="task-child">
|
|
|
|
|
<a class="add-handler" id="upload-file">
|
|
|
|
|
<a-icon type="plus" style="margin-right: 6px;"/>
|
|
|
|
|
上传文件
|
|
|
|
|
</a>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="component-mount" v-show="taskSourceList.length > 0">
|
|
|
|
|
<div class="field">
|
|
|
|
|
<div class="block-field width-block">
|
|
|
|
|
<div class="file-list">
|
|
|
|
|
<div class="m-xs m-t-none">关联的文件</div>
|
|
|
|
|
<a-list>
|
|
|
|
|
<a-list-item :key="index"
|
|
|
|
|
v-for="(item, index) in taskSourceList">
|
|
|
|
|
<a-list-item-meta>
|
|
|
|
|
<a-avatar size="small" slot="avatar" shape="square"
|
|
|
|
|
icon="link"
|
|
|
|
|
:src="item.sourceDetail.file_url"/>
|
|
|
|
|
<div slot="title">
|
|
|
|
|
<a class="muted" target="_blank"
|
|
|
|
|
:href="item.sourceDetail.file_url">{{ item.title
|
|
|
|
|
}}</a>
|
|
|
|
|
</div>
|
|
|
|
|
<div slot="description">
|
|
|
|
|
<!--{{item.create_time}}-->
|
|
|
|
|
</div>
|
|
|
|
|
</a-list-item-meta>
|
|
|
|
|
<a class="muted" slot="actions">
|
|
|
|
|
<span>{{item.sourceDetail.projectName}}</span>
|
|
|
|
|
</a>
|
|
|
|
|
<a class="muted" slot="actions">
|
|
|
|
|
<a-dropdown :trigger="['click']"
|
|
|
|
|
placement="bottomCenter">
|
|
|
|
|
<!-- <a-tooltip :mouseEnterDelay="0.5">
|
|
|
|
|
<template slot="title">
|
|
|
|
|
<span>更多操作</span>
|
|
|
|
|
</template>-->
|
|
|
|
|
<a class="action-item muted">
|
|
|
|
|
<a-icon type="down"/>
|
|
|
|
|
</a>
|
|
|
|
|
<!--</a-tooltip>-->
|
|
|
|
|
<a-menu v-clipboard="item.sourceDetail.file_url"
|
|
|
|
|
@click="doSource($event,item)"
|
|
|
|
|
class="field-right-menu"
|
|
|
|
|
slot="overlay">
|
|
|
|
|
<a-menu-item key="copy">
|
|
|
|
|
<a-icon type="link"/>
|
|
|
|
|
<span>复制链接</span>
|
|
|
|
|
</a-menu-item>
|
|
|
|
|
<a-menu-item key="unlink">
|
|
|
|
|
<a-icon type="disconnect"/>
|
|
|
|
|
<span>取消关联</span>
|
|
|
|
|
</a-menu-item>
|
|
|
|
|
</a-menu>
|
|
|
|
|
</a-dropdown>
|
|
|
|
|
</a>
|
|
|
|
|
</a-list-item>
|
|
|
|
|
</a-list>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</vue-scroll>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="content-right">
|
|
|
|
|
<div class="header">
|
|
|
|
|
<div class="title">
|
|
|
|
|
参与者 · {{taskMemberList.length}}
|
|
|
|
|
</div>
|
|
|
|
|
<div class="member-list">
|
|
|
|
|
<a-tooltip
|
|
|
|
|
:mouseEnterDelay="0.5"
|
|
|
|
|
v-for="member in taskMemberList"
|
|
|
|
|
:key="member.code"
|
|
|
|
|
>
|
|
|
|
|
<template slot="title">
|
|
|
|
|
<span>{{member.name}} <span v-if="member.is_owner"> · 创建者</span></span>
|
|
|
|
|
</template>
|
|
|
|
|
<a-avatar
|
|
|
|
|
class="member-item"
|
|
|
|
|
icon="user"
|
|
|
|
|
size="small"
|
|
|
|
|
:src="member.avatar"
|
|
|
|
|
@click="routerLink('/members/profile/' + member.membar_account_code + '?key=3')"
|
|
|
|
|
/>
|
|
|
|
|
</a-tooltip>
|
|
|
|
|
<a-dropdown :trigger="['click']" placement="bottomCenter"
|
|
|
|
|
v-model="visibleProjectMemberMenu">
|
|
|
|
|
<a-tooltip :mouseEnterDelay="0.5">
|
|
|
|
|
<template slot="title">
|
|
|
|
|
<span>点击添加参与者</span>
|
|
|
|
|
</template>
|
|
|
|
|
<a-icon class="member-item invite" type="plus-circle" theme="twoTone"
|
|
|
|
|
style="font-size: 24px;"/>
|
|
|
|
|
</a-tooltip>
|
|
|
|
|
<div slot="overlay">
|
|
|
|
|
<project-member-menu
|
|
|
|
|
v-if="visibleProjectMemberMenu"
|
|
|
|
|
:taskCode="code"
|
|
|
|
|
:projectCode="projectCodeCurrent"
|
|
|
|
|
@close="init(false)"
|
|
|
|
|
@inviteProjectMember="
|
|
|
|
|
showInviteMember = true,
|
|
|
|
|
visibleProjectMemberMenu = false"
|
|
|
|
|
></project-member-menu>
|
|
|
|
|
</div>
|
|
|
|
|
</a-dropdown>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="log-wrap">
|
|
|
|
|
<div class="header">
|
|
|
|
|
<a-dropdown :trigger="['click']">
|
|
|
|
|
<a class="text-default">
|
|
|
|
|
所有动态
|
|
|
|
|
<a-icon type="down"/>
|
|
|
|
|
</a>
|
|
|
|
|
<a-menu v-model="taskLogType" class="field-right-menu" slot="overlay"
|
|
|
|
|
selectable>
|
|
|
|
|
<a-menu-item key="all">
|
|
|
|
|
<div class="menu-item-content">
|
|
|
|
|
<span>所有动态</span>
|
|
|
|
|
</div>
|
|
|
|
|
</a-menu-item>
|
|
|
|
|
<a-menu-item key="comment">
|
|
|
|
|
<div class="menu-item-content">
|
|
|
|
|
<span>仅评论</span>
|
|
|
|
|
</div>
|
|
|
|
|
</a-menu-item>
|
|
|
|
|
<a-menu-item key="log">
|
|
|
|
|
<div class="menu-item-content">
|
|
|
|
|
<span>仅动态</span>
|
|
|
|
|
</div>
|
|
|
|
|
</a-menu-item>
|
|
|
|
|
</a-menu>
|
|
|
|
|
</a-dropdown>
|
|
|
|
|
</div>
|
|
|
|
|
<vue-scroll>
|
|
|
|
|
<div class="log-list muted">
|
|
|
|
|
<a class="show-more muted" v-show="checkShowMoreLog"
|
|
|
|
|
@click="getMoreTaskLog">
|
|
|
|
|
<a-icon type="ellipsis"/>
|
|
|
|
|
显示较早的 {{taskLogTotal - taskLogList.length}} 条动态
|
|
|
|
|
</a>
|
|
|
|
|
<div :class="{'log-comment': log.is_comment,'list-item': !log.is_comment}"
|
|
|
|
|
v-for="log in taskLogList" :key="log.code">
|
|
|
|
|
<template v-if="!log.is_comment">
|
|
|
|
|
<a-icon class="log-item" :type="log.icon"/>
|
|
|
|
|
<div class="log-item log-txt">
|
|
|
|
|
<div>{{log.member.name}} <span v-html="log.remark"></span></div>
|
|
|
|
|
<div v-if="log.content" class="log-content img-preview-content"
|
|
|
|
|
v-html="log.content"></div>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
<template v-if="log.is_comment">
|
|
|
|
|
<div class="log-txt text-default" style="width:100%; display: flex;justify-content: space-between">
|
|
|
|
|
<div style="display: flex;align-items: center">
|
|
|
|
|
<a-avatar :size="24" :src="log.member.avatar" class="m-r-sm" style="margin-left: -5px"/>
|
|
|
|
|
<div>{{log.member.name}}</div>
|
|
|
|
|
</div>
|
|
|
|
|
<a-tooltip :mouseEnterDelay="0.5">
|
|
|
|
|
<template slot="title">
|
|
|
|
|
<span>{{log.create_time}}</span>
|
|
|
|
|
</template>
|
|
|
|
|
<span class="muted">{{log.create_time | formatLogTime}}</span>
|
|
|
|
|
</a-tooltip>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="log-txt text-default" style="padding: 0 0 0 30px;">
|
|
|
|
|
<div class="m-t-xs" v-html="checkLink(log.content)" ></div>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
<a-tooltip v-if="!log.is_comment" :mouseEnterDelay="0.5">
|
|
|
|
|
<template slot="title">
|
|
|
|
|
<span>{{log.create_time}}</span>
|
|
|
|
|
</template>
|
|
|
|
|
<span>{{log.create_time | formatLogTime}}</span>
|
|
|
|
|
</a-tooltip>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</vue-scroll>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="footer" id="footer">
|
|
|
|
|
<a-popover trigger="click" placement="top" :visible="showMentions" arrowPointAtCenter :getPopupContainer="getPopup">
|
|
|
|
|
<template slot="content">
|
|
|
|
|
<div class="mentions-content" style="width: 200px;">
|
|
|
|
|
<div class="mentions-wrapper" v-for="member in taskMemberList" :key="member.id" @click="selectMentionMember(member)">
|
|
|
|
|
<a-avatar :src="member.avatar" icon="user" :size="28"/>
|
|
|
|
|
<span class="muted name m-l-xs">{{member.name}}</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
<!-- <span slot="title">Title</span>-->
|
|
|
|
|
<a-textarea @focus="commenting = true" @blur="commenting = false" ref="commentText" v-model="comment" :rows="1" placeholder="@提及任务成员,Ctrl+Enter发表评论"
|
|
|
|
|
style="margin-right: 24px;"/>
|
|
|
|
|
</a-popover>
|
|
|
|
|
<a-button class="middle-btn" type="primary" @click="createComment">评论</a-button>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</a-spin>
|
|
|
|
|
<invite-project-member v-model="showInviteMember" :project-code="projectCodeCurrent"
|
|
|
|
|
v-if="showInviteMember"></invite-project-member>
|
2024-01-05 19:01:49 +08:00
|
|
|
|
<!-- <a-modal
|
2024-01-03 11:23:06 +08:00
|
|
|
|
class="work-time-modal"
|
|
|
|
|
v-model="workTimeDo.modalStatus"
|
|
|
|
|
:title="workTimeDo.modalTitle"
|
|
|
|
|
:bodyStyle="{paddingBottom:'1px'}"
|
|
|
|
|
@ok="workTimeHandleSubmit"
|
|
|
|
|
:confirmLoading="workTimeDo.confirmLoading"
|
|
|
|
|
>
|
|
|
|
|
<a-form
|
|
|
|
|
layout="vertical"
|
|
|
|
|
@submit.prevent="workTimeHandleSubmit"
|
|
|
|
|
:form="workTimeDo.form">
|
|
|
|
|
<div>
|
|
|
|
|
<div style="font-size: 15px;">登记任务</div>
|
|
|
|
|
<div>{{task.name}}</div>
|
|
|
|
|
<a-divider class="m-t-xs m-b-md"></a-divider>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="work-time-stats">
|
|
|
|
|
<div class="work-time-item">
|
|
|
|
|
<div class="muted title">预估工时</div>
|
|
|
|
|
<span class="work-time-num">{{task.work_time}}</span>
|
|
|
|
|
<span>小时</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div class="work-time-item">
|
|
|
|
|
<div class="muted title">剩余工时</div>
|
|
|
|
|
<span class="work-time-num">
|
|
|
|
|
<template v-if="task.work_time - workTimeTotal < 0">0</template>
|
|
|
|
|
<template v-else>{{task.work_time - workTimeTotal}}</template>
|
|
|
|
|
</span>
|
|
|
|
|
<span>小时</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<a-row :gutter="24">
|
|
|
|
|
<a-col :span="12">
|
|
|
|
|
<a-form-item
|
|
|
|
|
label="开始时间"
|
|
|
|
|
>
|
|
|
|
|
<a-date-picker
|
|
|
|
|
showTime
|
|
|
|
|
format="YYYY年MM月DD日 HH:mm"
|
|
|
|
|
allowClear
|
|
|
|
|
placeholder="请选择日期"
|
|
|
|
|
v-decorator="[
|
|
|
|
|
'begin_time',
|
|
|
|
|
{rules: [{ required: true, message: '请选择日期' }], validateTrigger: 'change',initialValue: moment()}
|
|
|
|
|
]"
|
|
|
|
|
>
|
|
|
|
|
</a-date-picker>
|
|
|
|
|
</a-form-item>
|
|
|
|
|
</a-col>
|
|
|
|
|
<a-col :span="12">
|
|
|
|
|
<a-form-item
|
|
|
|
|
label="消耗时间"
|
|
|
|
|
>
|
|
|
|
|
<a-input
|
|
|
|
|
type="text"
|
|
|
|
|
placeholder="请输入数字"
|
|
|
|
|
addonAfter="小时"
|
|
|
|
|
v-decorator="[
|
|
|
|
|
'num',
|
|
|
|
|
{rules: [{ required: true, message: '请输入消耗时间' }], validateTrigger: 'change'}
|
|
|
|
|
]"
|
|
|
|
|
>
|
|
|
|
|
</a-input>
|
|
|
|
|
</a-form-item>
|
|
|
|
|
</a-col>
|
|
|
|
|
<a-col :span="24">
|
|
|
|
|
<a-form-item
|
|
|
|
|
label="工作内容"
|
|
|
|
|
>
|
|
|
|
|
<a-textarea
|
|
|
|
|
:rows="4"
|
|
|
|
|
type="text"
|
|
|
|
|
placeholder="在这期间做了什么"
|
|
|
|
|
v-decorator="[
|
|
|
|
|
'content',
|
|
|
|
|
]"
|
|
|
|
|
>
|
|
|
|
|
</a-textarea>
|
|
|
|
|
</a-form-item>
|
|
|
|
|
</a-col>
|
|
|
|
|
</a-row>
|
|
|
|
|
</a-form>
|
2024-01-05 19:01:49 +08:00
|
|
|
|
</a-modal> -->
|
|
|
|
|
<!-- <a-modal
|
2024-01-03 11:23:06 +08:00
|
|
|
|
v-model="plainWorkTime.modalStatus"
|
|
|
|
|
:title="plainWorkTime.modalTitle"
|
|
|
|
|
:bodyStyle="{paddingBottom:'1px'}"
|
|
|
|
|
@ok="workTimePlainHandleSubmit"
|
|
|
|
|
:confirmLoading="plainWorkTime.confirmLoading"
|
|
|
|
|
>
|
|
|
|
|
<a-form
|
|
|
|
|
layout="vertical"
|
|
|
|
|
@submit.prevent="workTimePlainHandleSubmit"
|
|
|
|
|
:form="plainWorkTime.form">
|
|
|
|
|
<a-form-item
|
|
|
|
|
label=""
|
|
|
|
|
>
|
|
|
|
|
<a-input
|
|
|
|
|
type="text"
|
|
|
|
|
placeholder="请输入数字"
|
|
|
|
|
addonAfter="小时"
|
|
|
|
|
v-decorator="[
|
|
|
|
|
'work_time',
|
|
|
|
|
{rules: [{ required: true, message: '请输入预估工时' }], validateTrigger: 'change'}
|
|
|
|
|
]"
|
|
|
|
|
>
|
|
|
|
|
</a-input>
|
|
|
|
|
</a-form-item>
|
|
|
|
|
</a-form>
|
2024-01-05 19:01:49 +08:00
|
|
|
|
</a-modal> -->
|
2024-01-03 11:23:06 +08:00
|
|
|
|
</div>
|
|
|
|
|
</template>
|
|
|
|
|
|
|
|
|
|
<script>
|
|
|
|
|
import {mapState} from 'vuex'
|
|
|
|
|
import $ from 'jquery'
|
|
|
|
|
import moment from 'moment';
|
|
|
|
|
import {COMMON} from '../../const/common'
|
|
|
|
|
import editor from '@/components/editor'
|
|
|
|
|
import {createComment, del, edit, logs, read, recovery, recycle, star, taskDone} from "@/api/task";
|
|
|
|
|
import {del as delSourceLink} from "@/api/sourceLink";
|
|
|
|
|
import {list as getTaskMembers} from "@/api/taskMember";
|
|
|
|
|
import taskMemberMenu from "@/components/project/taskMemberMenu"
|
|
|
|
|
import taskTagMenu from "@/components/project/taskTagMenu"
|
|
|
|
|
import projectMemberMenu from "@/components/project/projectMemberMenu"
|
|
|
|
|
import inviteProjectMember from '@/components/project/inviteProjectMember'
|
|
|
|
|
import {getStore} from "@/assets/js/storage";
|
|
|
|
|
import {getApiUrl} from "@/assets/js/utils";
|
|
|
|
|
import {save as createTask, list as getTasks, like as doLike} from "@/api/task";
|
|
|
|
|
import {notice} from "@/assets/js/notice";
|
|
|
|
|
import {relativelyTaskTime, relativelyTime} from "@/assets/js/dateTime";
|
|
|
|
|
import {checkResponse} from "../../assets/js/utils";
|
|
|
|
|
import {
|
|
|
|
|
_taskWorkTimeList, delTaskWorkTime,
|
|
|
|
|
editTaskWorkTime,
|
|
|
|
|
saveTaskWorkTime,
|
|
|
|
|
setPrivate,
|
|
|
|
|
setTag,
|
|
|
|
|
taskSources
|
|
|
|
|
} from "../../api/task";
|
|
|
|
|
import ATextarea from "ant-design-vue/es/input/TextArea";
|
|
|
|
|
import {detail} from "../../api/departmentMember";
|
|
|
|
|
|
|
|
|
|
let tokenList = getStore('tokenList', true);
|
|
|
|
|
let authorization = '';
|
|
|
|
|
if (tokenList) {
|
|
|
|
|
let accessToken = tokenList.accessToken;
|
|
|
|
|
let tokenType = tokenList.tokenType;
|
|
|
|
|
authorization = `${tokenType} ${accessToken}`;
|
|
|
|
|
}
|
|
|
|
|
export default {
|
|
|
|
|
name: "task-detail",
|
|
|
|
|
components: {
|
|
|
|
|
ATextarea,
|
|
|
|
|
editor,
|
|
|
|
|
taskMemberMenu,
|
|
|
|
|
taskTagMenu,
|
|
|
|
|
projectMemberMenu,
|
|
|
|
|
inviteProjectMember
|
|
|
|
|
},
|
|
|
|
|
props: {
|
|
|
|
|
taskCode: {
|
|
|
|
|
type: [String],
|
|
|
|
|
default() {
|
|
|
|
|
return ''
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
width: {
|
|
|
|
|
type: [String, Number],
|
|
|
|
|
default() {
|
|
|
|
|
return '1360'
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
data() {
|
|
|
|
|
return {
|
|
|
|
|
moment,
|
|
|
|
|
loading: false,
|
|
|
|
|
code: this.taskCode,
|
|
|
|
|
projectCodeCurrent: '',
|
|
|
|
|
task: {},
|
|
|
|
|
taskStatusList: COMMON.TASK_STATUS,
|
|
|
|
|
taskLogList: [],
|
|
|
|
|
taskLogTotal: 0,
|
|
|
|
|
taskMemberList: [],
|
|
|
|
|
workTimeList: [],
|
|
|
|
|
workTimeTotal: [],
|
|
|
|
|
|
|
|
|
|
/*任务菜单*/
|
|
|
|
|
visibleTaskMenu: false,
|
|
|
|
|
|
|
|
|
|
/*成员菜单*/
|
|
|
|
|
visibleTaskMemberMenu: false,
|
|
|
|
|
/*任务标签*/
|
|
|
|
|
visibleTaskTagMenu: false,
|
|
|
|
|
visibleProjectMemberMenu: false,
|
|
|
|
|
showInviteMember: false,
|
|
|
|
|
|
|
|
|
|
/*任务标题*/
|
|
|
|
|
taskName: '',
|
|
|
|
|
|
|
|
|
|
/*日期*/
|
|
|
|
|
showEditName: false,
|
|
|
|
|
showBeginTime: false,
|
|
|
|
|
showEndTime: false,
|
|
|
|
|
|
|
|
|
|
/*备注*/
|
|
|
|
|
showTaskDescriptionEdit: false,
|
|
|
|
|
showMoreDesc: false,
|
|
|
|
|
hasMoreDesc: false,
|
|
|
|
|
editorConfig: {
|
|
|
|
|
uploadImgServer: getApiUrl('project/index/uploadImg'),
|
|
|
|
|
uploadImgHeaders: {
|
|
|
|
|
Authorization: authorization
|
|
|
|
|
},
|
|
|
|
|
menus: [
|
|
|
|
|
'head', // 标题
|
|
|
|
|
'bold', // 粗体
|
|
|
|
|
'italic', // 斜体
|
|
|
|
|
'justify', // 居中
|
|
|
|
|
'image', // 图片
|
|
|
|
|
'link', // 链接
|
|
|
|
|
'list', // 无序列表
|
|
|
|
|
'quote', // 引用
|
|
|
|
|
'table', // 表格
|
|
|
|
|
'|',
|
|
|
|
|
'fullscreen' // 全屏
|
|
|
|
|
],
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
departmentMemberInfo: null,
|
|
|
|
|
|
|
|
|
|
/*子任务*/
|
|
|
|
|
childTaskList: [],
|
|
|
|
|
showChildTask: false,
|
|
|
|
|
childTaskName: '',
|
|
|
|
|
childExecutor: null,
|
|
|
|
|
visibleChildTaskMemberMenu: false,
|
|
|
|
|
showInviteChildTaskMember: false,
|
|
|
|
|
|
|
|
|
|
/*资源*/
|
|
|
|
|
taskSourceList: [],
|
|
|
|
|
|
|
|
|
|
/*任务动态*/
|
|
|
|
|
taskLogType: ['all'],
|
|
|
|
|
showMoreTaskLog: 0,
|
|
|
|
|
hasMoreTaskLog: false,
|
|
|
|
|
hideShowMore: false,
|
|
|
|
|
|
|
|
|
|
/*评论*/
|
|
|
|
|
comment: '',
|
|
|
|
|
commenting: false,
|
|
|
|
|
|
|
|
|
|
/*工时*/
|
|
|
|
|
workTimeDo: {
|
|
|
|
|
form: this.$form.createForm(this),
|
|
|
|
|
info: null,
|
|
|
|
|
modalTitle: '登记工时记录',
|
|
|
|
|
modalStatus: false,
|
|
|
|
|
confirmLoading: false,
|
|
|
|
|
},
|
|
|
|
|
plainWorkTime: {
|
|
|
|
|
form: this.$form.createForm(this),
|
|
|
|
|
modalTitle: '设置预估工时',
|
|
|
|
|
modalStatus: false,
|
|
|
|
|
confirmLoading: false,
|
|
|
|
|
},
|
|
|
|
|
//显示评论提及
|
|
|
|
|
showMentions: false,
|
|
|
|
|
mentionsList: []
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
computed: {
|
|
|
|
|
...mapState({
|
|
|
|
|
userInfo: state => state.userInfo,
|
|
|
|
|
uploader: state => state.common.uploader,
|
|
|
|
|
socketAction: state => state.socketAction,
|
|
|
|
|
}),
|
|
|
|
|
childTaskDoneNum() {
|
|
|
|
|
const list = this.childTaskList.filter(item => item.done == 1);
|
|
|
|
|
return list.length;
|
|
|
|
|
},
|
|
|
|
|
checkShowMoreLog() {
|
|
|
|
|
if (!this.hideShowMore) {
|
|
|
|
|
if (this.taskLogTotal > 5) {
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
},
|
|
|
|
|
taskLink() {
|
|
|
|
|
return window.location.href;
|
|
|
|
|
},
|
|
|
|
|
scrollOps() {
|
|
|
|
|
return {
|
|
|
|
|
rail: {
|
|
|
|
|
background: '#e5e5e5',
|
|
|
|
|
opacity: 1
|
|
|
|
|
},
|
|
|
|
|
bar: {
|
|
|
|
|
keepShow: true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
watch: {
|
|
|
|
|
$route(to, from) {
|
|
|
|
|
// if (from.name == 'taskdetail') {
|
|
|
|
|
// this.init();
|
|
|
|
|
// }
|
|
|
|
|
},
|
|
|
|
|
showInviteMember(val) {
|
|
|
|
|
if (!val) {
|
|
|
|
|
this.getTaskMembers();
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
taskLogType() {
|
|
|
|
|
this.getTaskLog();
|
|
|
|
|
},
|
|
|
|
|
socketAction(val) {
|
|
|
|
|
if (val.action === 'organization:task') {
|
|
|
|
|
const data = val.data.data;
|
|
|
|
|
if (data.taskCode == this.code) {
|
|
|
|
|
this.init(null, false);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
uploader: {
|
|
|
|
|
handler(newVal, oldVal) {
|
|
|
|
|
//监听是否有上传文件行为
|
|
|
|
|
console.log(newVal);
|
|
|
|
|
console.log(newVal.fileList);
|
|
|
|
|
const files = newVal.fileList;
|
|
|
|
|
const index = files.findIndex(item => item.projectName == this.task.projectName);
|
|
|
|
|
if (index !== -1) {
|
|
|
|
|
this.taskSources();
|
|
|
|
|
this.getTaskLog();
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
deep: true
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
created() {
|
|
|
|
|
this.init();
|
|
|
|
|
},
|
|
|
|
|
mounted() {
|
|
|
|
|
this.$nextTick(()=>{
|
|
|
|
|
this.changeModalHeight();
|
|
|
|
|
})
|
|
|
|
|
window.onresize = () => {
|
|
|
|
|
return (() => {
|
|
|
|
|
this.changeModalHeight();
|
|
|
|
|
})()
|
|
|
|
|
};
|
|
|
|
|
document.onkeydown = (event) => {
|
|
|
|
|
console.log(event);
|
|
|
|
|
var e = event || window.event || arguments.callee.caller.arguments[0];
|
|
|
|
|
if (13 == e.keyCode && e.ctrlKey) {
|
|
|
|
|
//处理的部分
|
|
|
|
|
this.createComment();
|
|
|
|
|
}
|
|
|
|
|
if ('@' == e.key && this.commenting) {
|
|
|
|
|
this.showMentions = true;
|
|
|
|
|
}else{
|
|
|
|
|
this.showMentions = false;
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
this.uploader.assignBrowse(document.getElementById('upload-file'));
|
|
|
|
|
}, 500)
|
|
|
|
|
},
|
|
|
|
|
filters: {
|
|
|
|
|
formatLogTime(value) {
|
|
|
|
|
return relativelyTime(value)
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
methods: {
|
|
|
|
|
init(code = null, loading = true) {
|
|
|
|
|
if (code) {
|
|
|
|
|
this.code = code;//子任务
|
|
|
|
|
}
|
|
|
|
|
if (loading) {
|
|
|
|
|
this.loading = true;
|
|
|
|
|
}
|
|
|
|
|
this.clearMemberMenu();
|
|
|
|
|
this.getTask();
|
|
|
|
|
this.getChildTasks();
|
|
|
|
|
this.getTaskMembers();
|
|
|
|
|
this.getTaskWorkTimeList();
|
|
|
|
|
},
|
|
|
|
|
detailClose() {
|
|
|
|
|
this.$emit('close', this.task);
|
|
|
|
|
// this.$router.push(`/project/space/task/${this.task.project_code}`);
|
|
|
|
|
},
|
|
|
|
|
clearMemberMenu() {
|
|
|
|
|
this.visibleTaskTagMenu = false;
|
|
|
|
|
this.visibleTaskMemberMenu = false;
|
|
|
|
|
this.visibleProjectMemberMenu = false;
|
|
|
|
|
// this.showChildTask = false;
|
|
|
|
|
},
|
|
|
|
|
getTask() {
|
|
|
|
|
this.$store.commit('viewRefresh');
|
|
|
|
|
this.clearMemberMenu();
|
|
|
|
|
read(this.code).then((res) => {
|
|
|
|
|
this.getTaskLog();
|
|
|
|
|
this.taskSources();
|
|
|
|
|
this.taskName = res.data.name;
|
|
|
|
|
this.task = res.data;
|
|
|
|
|
this.projectCodeCurrent = res.data.project_code;
|
|
|
|
|
if (!this.task.end_time) {
|
|
|
|
|
this.task.setEndTime = false;
|
|
|
|
|
this.task.end_time = moment(moment().format('YYYY-MM-DD') + ' 18:00');
|
|
|
|
|
} else {
|
|
|
|
|
this.task.setEndTime = true;
|
|
|
|
|
this.task.end_time = moment(this.task.end_time);
|
|
|
|
|
}
|
|
|
|
|
this.task.end_time_format = relativelyTaskTime(this.task.end_time, true);
|
|
|
|
|
if (!this.task.begin_time) {
|
|
|
|
|
this.task.setBeginTime = false;
|
|
|
|
|
this.task.begin_time = moment(moment().format('YYYY-MM-DD') + ' 18:00');
|
|
|
|
|
} else {
|
|
|
|
|
this.task.setBeginTime = true;
|
|
|
|
|
this.task.begin_time = moment(this.task.begin_time);
|
|
|
|
|
}
|
|
|
|
|
this.task.begin_time_format = relativelyTaskTime(this.task.begin_time, true);
|
|
|
|
|
this.initContent(this.task.description);
|
|
|
|
|
if (this.task.executor && !this.childExecutor) {
|
|
|
|
|
this.childExecutor = this.task.executor;
|
|
|
|
|
}
|
|
|
|
|
this.loading = false;
|
|
|
|
|
this.$store.dispatch('setTempData', {
|
|
|
|
|
projectCode: this.projectCodeCurrent,
|
|
|
|
|
taskCode: this.code,
|
|
|
|
|
projectName: res.data.projectName
|
|
|
|
|
})
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
getTaskLog() {
|
|
|
|
|
logs({
|
|
|
|
|
taskCode: this.code,
|
|
|
|
|
all: this.showMoreTaskLog,
|
|
|
|
|
pageSize: 5,
|
|
|
|
|
comment: this.taskLogType[0] == 'comment' ? 1 : 0
|
|
|
|
|
}).then((res) => {
|
|
|
|
|
this.taskLogList = res.data.list;
|
|
|
|
|
this.taskLogTotal = res.data.total;
|
|
|
|
|
if (res.data.total > 5) {
|
|
|
|
|
this.hasMoreTaskLog = true;
|
|
|
|
|
// this.showMoreTaskLog = 1;
|
|
|
|
|
}
|
|
|
|
|
this.$nextTick(()=>{
|
|
|
|
|
$(".img-preview-content").find('img').click((e) =>{
|
|
|
|
|
window.open($(e.target).attr("src"));
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
taskSources() {
|
|
|
|
|
taskSources({taskCode: this.code,}).then((res) => {
|
|
|
|
|
this.taskSourceList = res.data;
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
getMoreTaskLog() {
|
|
|
|
|
this.showMoreTaskLog = 1;
|
|
|
|
|
this.hideShowMore = true;
|
|
|
|
|
this.getTaskLog();
|
|
|
|
|
},
|
|
|
|
|
getTaskWorkTimeList() {
|
|
|
|
|
_taskWorkTimeList({taskCode: this.code}).then(res => {
|
|
|
|
|
this.workTimeList = res.data;
|
|
|
|
|
let total = 0;
|
|
|
|
|
if (res.data) {
|
|
|
|
|
res.data.forEach(v => {
|
|
|
|
|
total += Number(v.num);
|
|
|
|
|
});
|
|
|
|
|
this.workTimeTotal = total;
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
getTaskMembers() {
|
|
|
|
|
this.clearMemberMenu();
|
|
|
|
|
getTaskMembers({taskCode: this.code, pageSize: 100}).then((res) => {
|
|
|
|
|
this.taskMemberList = res.data.list;
|
|
|
|
|
this.loading = false;
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
doTask(action) {
|
|
|
|
|
let app = this;
|
|
|
|
|
const actionKey = action.key;
|
|
|
|
|
switch (actionKey) {
|
|
|
|
|
case 'recycle':
|
|
|
|
|
this.$confirm({
|
|
|
|
|
title: '移到回收站',
|
|
|
|
|
content: `您确定要把该任务移到回收站吗?`,
|
|
|
|
|
okText: '确定',
|
|
|
|
|
okType: 'danger',
|
|
|
|
|
cancelText: `再想想`,
|
|
|
|
|
onOk() {
|
|
|
|
|
recycle(app.code).then((res) => {
|
|
|
|
|
const result = checkResponse(res);
|
|
|
|
|
if (!result) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
app.task.deleted = 1;
|
|
|
|
|
app.getTaskLog();
|
|
|
|
|
});
|
|
|
|
|
return Promise.resolve();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
break;
|
|
|
|
|
case 'star':
|
|
|
|
|
this.task.stared ? this.task.stared = 0 : this.task.stared = 1;
|
|
|
|
|
star(app.code, this.task.stared);
|
|
|
|
|
return true;
|
|
|
|
|
case 'open':
|
|
|
|
|
var url = window.location.href;
|
|
|
|
|
if (url.indexOf('?') !== -1) {
|
|
|
|
|
url += '&full-screen';
|
|
|
|
|
}else{
|
|
|
|
|
url += '?full-screen';
|
|
|
|
|
}
|
|
|
|
|
window.open(url);
|
|
|
|
|
break;
|
|
|
|
|
case 'private':
|
|
|
|
|
setPrivate(app.code, Number(!this.task.private)).then(res => {
|
|
|
|
|
const result = checkResponse(res);
|
|
|
|
|
if (!result) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
this.task.private = Number(!this.task.private);
|
|
|
|
|
});
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
this.visibleTaskMenu = false;
|
|
|
|
|
},
|
|
|
|
|
editTitle() {
|
|
|
|
|
this.showEditName = true;
|
|
|
|
|
this.$nextTick(() => {
|
|
|
|
|
this.$refs.inputTitle.focus();
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
doSource(action, source) {
|
|
|
|
|
let app = this;
|
|
|
|
|
const actionKey = action.key;
|
|
|
|
|
switch (actionKey) {
|
|
|
|
|
case 'unlink':
|
|
|
|
|
this.$confirm({
|
|
|
|
|
title: '取消关联',
|
|
|
|
|
content: `您确定永久删除这个关联?`,
|
|
|
|
|
okText: '确定',
|
|
|
|
|
okType: 'danger',
|
|
|
|
|
cancelText: `再想想`,
|
|
|
|
|
onOk() {
|
|
|
|
|
delSourceLink(source.code).then((res) => {
|
|
|
|
|
const result = checkResponse(res);
|
|
|
|
|
if (!result) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
app.taskSources();
|
|
|
|
|
app.getTaskLog();
|
|
|
|
|
});
|
|
|
|
|
return Promise.resolve();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
break;
|
|
|
|
|
case 'copy':
|
|
|
|
|
notice({
|
|
|
|
|
title: '复制链接成功',
|
|
|
|
|
msg: '在地址栏粘贴并打开可下载该资源'
|
|
|
|
|
}, 'notice', 'success', 5);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
deleteTask() {
|
|
|
|
|
let app = this;
|
|
|
|
|
this.$confirm({
|
|
|
|
|
title: '彻底删除',
|
|
|
|
|
content: `彻底删除任务后,该任务及其子任务将会被永久被删除。`,
|
|
|
|
|
okText: '删除',
|
|
|
|
|
okType: 'danger',
|
|
|
|
|
cancelText: `再想想`,
|
|
|
|
|
onOk() {
|
|
|
|
|
del(app.code).then((res) => {
|
|
|
|
|
``
|
|
|
|
|
app.detailClose();
|
|
|
|
|
});
|
|
|
|
|
return Promise.resolve();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
recoveryTask() {
|
|
|
|
|
let app = this;
|
|
|
|
|
this.$confirm({
|
|
|
|
|
title: '恢复内容',
|
|
|
|
|
content: `您确定要恢复该任务吗?`,
|
|
|
|
|
okText: '确定',
|
|
|
|
|
okType: 'primary',
|
|
|
|
|
cancelText: `再想想`,
|
|
|
|
|
onOk() {
|
|
|
|
|
recovery(app.code).then((res) => {
|
|
|
|
|
const result = checkResponse(res);
|
|
|
|
|
if (!result) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
app.getTaskLog();
|
|
|
|
|
});
|
|
|
|
|
app.task.deleted = 0;
|
|
|
|
|
return Promise.resolve();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
copyLink() {
|
|
|
|
|
notice({
|
|
|
|
|
title: '复制链接成功',
|
|
|
|
|
msg: '你可以在其他标签页粘贴并快速打开任务页面'
|
|
|
|
|
}, 'notice', 'success', 5);
|
|
|
|
|
},
|
|
|
|
|
like(like) {
|
|
|
|
|
like = Number(like);
|
|
|
|
|
doLike(this.code, like);
|
|
|
|
|
if (like) {
|
|
|
|
|
this.task.like++;
|
|
|
|
|
} else {
|
|
|
|
|
this.task.like--;
|
|
|
|
|
}
|
|
|
|
|
this.task.liked = like;
|
|
|
|
|
},
|
|
|
|
|
taskDone(taskCode, done, index, type = 'self') {
|
|
|
|
|
done ? done = 1 : done = 0;
|
|
|
|
|
taskDone(taskCode, done).then((res) => {
|
|
|
|
|
const result = checkResponse(res);
|
|
|
|
|
if (!result) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
this.getTaskLog();
|
|
|
|
|
if (type == 'self') {
|
|
|
|
|
//自身完成
|
|
|
|
|
this.task.done = done;
|
|
|
|
|
// this.init();
|
|
|
|
|
} else {
|
|
|
|
|
//完成子任务
|
|
|
|
|
this.childTaskList[index].done = done;
|
|
|
|
|
// this.init(this.childTaskList[index].pcode);
|
|
|
|
|
}
|
|
|
|
|
this.getTask();
|
|
|
|
|
this.getChildTasks();
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
doBeginTime(setBeginTime = false, showBeginTime = false) {
|
|
|
|
|
this.task.setBeginTime = setBeginTime;
|
|
|
|
|
this.showBeginTime = showBeginTime;
|
|
|
|
|
let beginTime = '';
|
|
|
|
|
if (setBeginTime) {
|
|
|
|
|
beginTime = moment(this.task.begin_time).format('YYYY-MM-DD HH:mm');
|
|
|
|
|
this.task.begin_time_format = moment(this.task.begin_time).format('MM月DD日 HH:mm');
|
|
|
|
|
} else {
|
|
|
|
|
beginTime = '';
|
|
|
|
|
}
|
|
|
|
|
this.editTask({begin_time: beginTime});
|
|
|
|
|
|
|
|
|
|
},
|
|
|
|
|
doEndTime(setEndTime = false, showEndTime = false) {
|
|
|
|
|
this.task.setEndTime = setEndTime;
|
|
|
|
|
this.showEndTime = showEndTime;
|
|
|
|
|
let endTime = '';
|
|
|
|
|
if (setEndTime) {
|
|
|
|
|
endTime = moment(this.task.end_time).format('YYYY-MM-DD HH:mm');
|
|
|
|
|
this.task.end_time_format = moment(this.task.end_time).format('MM月DD日 HH:mm');
|
|
|
|
|
} else {
|
|
|
|
|
endTime = '';
|
|
|
|
|
}
|
|
|
|
|
this.editTask({end_time: endTime});
|
|
|
|
|
|
|
|
|
|
},
|
|
|
|
|
doPri(item) {
|
|
|
|
|
this.editTask({pri: item.key});
|
|
|
|
|
},
|
|
|
|
|
doName() {
|
|
|
|
|
this.showEditName = false;
|
|
|
|
|
if (!this.task.name.trim() || this.task.name == this.taskName) {
|
|
|
|
|
this.task.name = this.taskName;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
this.editTask({name: this.task.name});
|
|
|
|
|
},
|
|
|
|
|
taskStatusChange(item) {
|
|
|
|
|
this.task.status = item.key;
|
|
|
|
|
this.editTask({status: item.key});
|
|
|
|
|
},
|
|
|
|
|
editTask(data) {
|
|
|
|
|
data.taskCode = this.code;
|
|
|
|
|
edit(data).then((res) => {
|
|
|
|
|
const result = checkResponse(res);
|
|
|
|
|
if (!result) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
this.getTask();
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
priColor(pri) {
|
|
|
|
|
switch (pri) {
|
|
|
|
|
case 1:
|
|
|
|
|
return '#ff9900';
|
|
|
|
|
case 2:
|
|
|
|
|
return '#ed3f14';
|
|
|
|
|
default:
|
|
|
|
|
return 'green';
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
showTaskDesc() {
|
|
|
|
|
if (this.task.deleted) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
this.showTaskDescriptionEdit = true;
|
|
|
|
|
this.initContent(this.task.description)
|
|
|
|
|
},
|
|
|
|
|
initContent(value) {
|
|
|
|
|
if (value) {
|
|
|
|
|
this.$refs.vueWangeditor.setHtml(value)
|
|
|
|
|
} else {
|
|
|
|
|
this.$refs.vueWangeditor.setHtml('')
|
|
|
|
|
}
|
|
|
|
|
this.$nextTick(() => {
|
|
|
|
|
this.checkShowMoreDesc(false, true);
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
doContent() {
|
|
|
|
|
let content = this.$refs.vueWangeditor.getHtml();
|
|
|
|
|
const obj = {
|
|
|
|
|
taskCode: this.code,
|
|
|
|
|
description: content
|
|
|
|
|
};
|
|
|
|
|
edit(obj).then(() => {
|
|
|
|
|
this.task.description = content;
|
|
|
|
|
this.showTaskDescriptionEdit = false;
|
|
|
|
|
this.$nextTick(() => {
|
|
|
|
|
this.checkShowMoreDesc(false, true);
|
|
|
|
|
});
|
|
|
|
|
this.getTaskLog();
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
createComment() {
|
|
|
|
|
let comment = this.comment.trim();
|
|
|
|
|
if (!comment) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
comment += ' ';
|
|
|
|
|
const regx = /(@[^@]+) /g;
|
|
|
|
|
const res = comment.match(regx);
|
|
|
|
|
if (res) {
|
|
|
|
|
res.forEach((v) => {
|
|
|
|
|
let str = v.substring(1, v.length - 1);
|
|
|
|
|
if (this.mentionsList.findIndex(item => item == str) === -1) {
|
|
|
|
|
this.mentionsList.push(str);
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
createComment(this.code, this.comment, JSON.stringify(this.mentionsList)).then(() => {
|
|
|
|
|
this.comment = '';
|
|
|
|
|
this.mentionsList = [];
|
|
|
|
|
this.getTaskLog();
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
checkShowMoreDesc(show = false, init = false) {
|
|
|
|
|
let dom = $(".description-txt");
|
|
|
|
|
if (!init) {
|
|
|
|
|
if (show) {
|
|
|
|
|
this.showMoreDesc = true;
|
|
|
|
|
dom.css("max-height", () => {
|
|
|
|
|
return '100%';
|
|
|
|
|
});
|
|
|
|
|
} else {
|
|
|
|
|
this.showMoreDesc = false;
|
|
|
|
|
dom.css("max-height", () => {
|
|
|
|
|
return '300px';
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (init) {
|
|
|
|
|
const height = dom.height();
|
|
|
|
|
if (height >= 300) {
|
|
|
|
|
this.hasMoreDesc = true;
|
|
|
|
|
return true;
|
|
|
|
|
} else {
|
|
|
|
|
this.hasMoreDesc = false;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
},
|
|
|
|
|
createTask() {
|
|
|
|
|
let obj = {pcode: this.code, name: this.childTaskName};
|
|
|
|
|
if (this.childExecutor) {
|
|
|
|
|
obj.assign_to = this.childExecutor.code;
|
|
|
|
|
}
|
|
|
|
|
createTask(obj).then((res) => {
|
|
|
|
|
const result = checkResponse(res);
|
|
|
|
|
if (!result) {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
this.childTaskName = '';
|
|
|
|
|
this.getChildTasks();
|
|
|
|
|
this.getTaskLog();
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
getChildTasks() {
|
|
|
|
|
getTasks({pcode: this.code, pageSize: 100, deleted: 0}).then((res) => {
|
|
|
|
|
let list = [];
|
|
|
|
|
res.data.list.forEach(v => {
|
|
|
|
|
v.visibleChildTaskMemberMenu = false;
|
|
|
|
|
list.push(v);
|
|
|
|
|
});
|
|
|
|
|
this.childTaskList = list;
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
taskTagChange(tag) {
|
|
|
|
|
const index = this.task.tags.findIndex(item => item.tag_code == tag.code);
|
|
|
|
|
if (index !== -1) {
|
|
|
|
|
this.task.tags.splice(index, 1);
|
|
|
|
|
} else {
|
|
|
|
|
this.task.tags.push({
|
|
|
|
|
tag_code: tag.code,
|
|
|
|
|
tag: tag
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
taskTagUpdate(tag) {
|
|
|
|
|
const index = this.task.tags.findIndex(item => item.tag_code == tag.code);
|
|
|
|
|
if (index !== -1) {
|
|
|
|
|
this.task.tags[index].tag = tag;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
taskTagDelete(tag) {
|
|
|
|
|
const index = this.task.tags.findIndex(item => item.tag_code == tag.code);
|
|
|
|
|
if (index !== -1) {
|
|
|
|
|
this.task.tags.splice(index, 1);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
removeTag(tag, index) {
|
|
|
|
|
setTag({taskCode: this.task.code, tagCode: tag.code}).then(() => {
|
|
|
|
|
this.task.tags.splice(index, 1);
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
doPlainWorkTime() {
|
|
|
|
|
let app = this;
|
|
|
|
|
this.plainWorkTime.modalStatus = true;
|
|
|
|
|
this.$nextTick(function () {
|
|
|
|
|
app.plainWorkTime.form.setFieldsValue({
|
|
|
|
|
work_time: app.task.work_time,
|
|
|
|
|
});
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
doWorkTime(workTime = false) {
|
|
|
|
|
let app = this;
|
|
|
|
|
this.workTimeDo.modalStatus = true;
|
|
|
|
|
if (workTime) {
|
|
|
|
|
this.workTimeDo.info = workTime;
|
|
|
|
|
this.$nextTick(function () {
|
|
|
|
|
app.workTimeDo.form.setFieldsValue({
|
|
|
|
|
num: workTime.num,
|
|
|
|
|
begin_time: moment(workTime.begin_time),
|
|
|
|
|
content: workTime.content,
|
|
|
|
|
});
|
|
|
|
|
})
|
|
|
|
|
} else {
|
|
|
|
|
this.workTimeDo.form.resetFields();
|
|
|
|
|
this.workTimeDo.info = null;
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
deleteWorkTime(workTime, index) {
|
|
|
|
|
let app = this;
|
|
|
|
|
this.$confirm({
|
|
|
|
|
title: '删除工时记录',
|
|
|
|
|
content: `确定要删除工时记录吗?`,
|
|
|
|
|
okText: '确定',
|
|
|
|
|
okType: 'danger',
|
|
|
|
|
cancelText: '再想想',
|
|
|
|
|
onOk() {
|
|
|
|
|
delTaskWorkTime({code: workTime.code}).then((res) => {
|
|
|
|
|
if (checkResponse(res)) {
|
|
|
|
|
app.workTimeList.splice(index, 1);
|
|
|
|
|
app.workTimeTotal -= workTime.num;
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
return Promise.resolve();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
workTimePlainHandleSubmit() {
|
|
|
|
|
let app = this;
|
|
|
|
|
this.plainWorkTime.form.validateFields(
|
|
|
|
|
(err, values) => {
|
|
|
|
|
if (!err) {
|
|
|
|
|
app.editTask({work_time: values.work_time});
|
|
|
|
|
app.plainWorkTime.modalStatus = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
workTimeHandleSubmit() {
|
|
|
|
|
let app = this;
|
|
|
|
|
this.workTimeDo.form.validateFields(
|
|
|
|
|
(err, values) => {
|
|
|
|
|
if (!err) {
|
|
|
|
|
app.workTimeDo.confirmLoading = true;
|
|
|
|
|
let data = {
|
|
|
|
|
beginTime: values.begin_time.format('YYYY-MM-DD HH:mm'),
|
|
|
|
|
num: values.num,
|
|
|
|
|
content: values.content,
|
|
|
|
|
taskCode: app.code,
|
|
|
|
|
};
|
|
|
|
|
if (app.workTimeDo.info) {
|
|
|
|
|
data.code = app.workTimeDo.info.code;
|
|
|
|
|
editTaskWorkTime(data).then(res => {
|
|
|
|
|
app.workTimeDo.confirmLoading = false;
|
|
|
|
|
if (checkResponse(res)) {
|
|
|
|
|
app.workTimeDo.modalStatus = false;
|
|
|
|
|
app.getTaskWorkTimeList();
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
} else {
|
|
|
|
|
saveTaskWorkTime(data).then(res => {
|
|
|
|
|
app.workTimeDo.confirmLoading = false;
|
|
|
|
|
if (checkResponse(res)) {
|
|
|
|
|
app.workTimeDo.modalStatus = false;
|
|
|
|
|
app.getTaskWorkTimeList();
|
|
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
checkLink(text) {
|
|
|
|
|
let reg = /(http:\/\/|https:\/\/)((\w|=|\?|\.|\/|&|-)+)/g;
|
|
|
|
|
if(!reg.exec(text)){
|
|
|
|
|
return text
|
|
|
|
|
}else{
|
|
|
|
|
text = text.replace(reg, "<a target='_blank' href='$1$2'>$1$2</a>");
|
|
|
|
|
}
|
|
|
|
|
return text;
|
|
|
|
|
},
|
|
|
|
|
updateChildExecutor(member) {
|
|
|
|
|
this.visibleChildTaskMemberMenu = false;
|
|
|
|
|
this.childExecutor = member;
|
|
|
|
|
},
|
|
|
|
|
getPopup() {
|
|
|
|
|
return document.getElementById('footer');
|
|
|
|
|
},
|
|
|
|
|
getTaskMemberPopup() {
|
|
|
|
|
return document.getElementById('task-detail');
|
|
|
|
|
},
|
|
|
|
|
departmentMemberDetail(code) {
|
|
|
|
|
detail({code: code,organization: this.$store.state.currentOrganization.code}).then(res=>{
|
|
|
|
|
this.departmentMemberInfo = res.data;
|
|
|
|
|
})
|
|
|
|
|
},
|
|
|
|
|
selectMentionMember(member) {
|
|
|
|
|
this.showMentions = false;
|
|
|
|
|
this.comment += member.name + ' ';
|
|
|
|
|
this.$nextTick(() => {
|
|
|
|
|
this.$refs.commentText.focus();
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
changeModalHeight() {
|
|
|
|
|
const defaultWidth = this.width;
|
|
|
|
|
let width = $(window).width() - 100;
|
|
|
|
|
let height = $(window).height() - 150;
|
|
|
|
|
let logHeight = $(window).height() - 315;
|
|
|
|
|
if (defaultWidth === 'full-screen' || this.$route.query['full-screen'] !== undefined) {
|
|
|
|
|
//全屏显示
|
|
|
|
|
$(".task-detail-modal").css("width", $(window).width());
|
|
|
|
|
$(".task-detail").css("width", $(window).width());
|
|
|
|
|
$(".ant-modal").css("top", 0);
|
|
|
|
|
height += 85;
|
|
|
|
|
logHeight += 85;
|
|
|
|
|
$(".content-left").css("height", height + "px");
|
|
|
|
|
$(".log-wrap").css("height", logHeight + "px");
|
|
|
|
|
} else {
|
|
|
|
|
if (width > defaultWidth) {
|
|
|
|
|
width = defaultWidth;
|
|
|
|
|
}
|
|
|
|
|
$(".task-detail").css("width", width);
|
|
|
|
|
$(".content-left").css("height", height + "px");
|
|
|
|
|
$(".log-wrap").css("height", logHeight + "px");
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
<style lang="less">
|
|
|
|
|
@import "~ant-design-vue/lib/style/themes/default";
|
|
|
|
|
|
|
|
|
|
.field-flex {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: flex-start;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.task-tag {
|
|
|
|
|
.ant-tag {
|
|
|
|
|
margin-bottom: 6px;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.task-detail {
|
|
|
|
|
background: #FFF;
|
|
|
|
|
display: -webkit-box;
|
|
|
|
|
display: -ms-flexbox;
|
|
|
|
|
display: flex;
|
|
|
|
|
-webkit-box-pack: center;
|
|
|
|
|
-ms-flex-pack: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
-webkit-box-flex: 1;
|
|
|
|
|
-ms-flex: 1;
|
|
|
|
|
flex: 1;
|
|
|
|
|
min-height: 1px;
|
|
|
|
|
min-width: 1px;
|
|
|
|
|
margin: auto;
|
|
|
|
|
|
|
|
|
|
.task-detail-spin {
|
|
|
|
|
width: 100%;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.task-header {
|
|
|
|
|
padding: 20px 0;
|
|
|
|
|
border-bottom: 1px solid #e5e5e5;
|
|
|
|
|
display: flex;
|
|
|
|
|
vertical-align: middle;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
.head-title {
|
|
|
|
|
padding: 0 20px 0 20px;
|
|
|
|
|
flex: 1 1;
|
|
|
|
|
|
|
|
|
|
.breadcrumb {
|
|
|
|
|
display: inline;
|
|
|
|
|
|
|
|
|
|
a {
|
|
|
|
|
color: inherit;
|
|
|
|
|
|
|
|
|
|
&:hover {
|
|
|
|
|
color: #40a9ff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.header-action {
|
|
|
|
|
font-size: 16px;
|
|
|
|
|
padding: 0 20px;
|
|
|
|
|
display: flex;
|
|
|
|
|
max-height: 24px;
|
|
|
|
|
|
|
|
|
|
.action-item {
|
|
|
|
|
margin-left: 10px;
|
|
|
|
|
padding: 4px;
|
|
|
|
|
transition: 218ms;
|
|
|
|
|
transition-property: background, color;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
align-items: center;
|
|
|
|
|
display: flex;
|
|
|
|
|
text-align: center;
|
|
|
|
|
justify-content: center;
|
|
|
|
|
min-width: 32px;
|
|
|
|
|
|
|
|
|
|
span {
|
|
|
|
|
margin-left: 6px;
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&.active {
|
|
|
|
|
color: #3da8f5;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&:hover {
|
|
|
|
|
background: #ecf6fe;
|
|
|
|
|
color: #3da8f5;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&.disabled {
|
|
|
|
|
background: #f5f5f5;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.task-wrap {
|
|
|
|
|
.task-content {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: flex-start;
|
|
|
|
|
|
|
|
|
|
.content-left {
|
|
|
|
|
border-right: 1px solid #e5e5e5;
|
|
|
|
|
min-width: 560px;
|
|
|
|
|
//width: 63%;
|
|
|
|
|
flex: 1;
|
|
|
|
|
/*width: 760px;*/
|
|
|
|
|
|
|
|
|
|
.task-title {
|
|
|
|
|
margin: 10px 40px 20px 20px;
|
|
|
|
|
cursor: text;
|
|
|
|
|
|
|
|
|
|
&.disabled {
|
|
|
|
|
cursor: not-allowed;
|
|
|
|
|
|
|
|
|
|
&:hover {
|
|
|
|
|
background: inherit;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.ant-input, .title-text {
|
|
|
|
|
&:hover, &:focus {
|
|
|
|
|
background: inherit;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&:hover {
|
|
|
|
|
background: #f5f5f5;
|
|
|
|
|
}
|
|
|
|
|
&.hover-none{
|
|
|
|
|
background: initial;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.title-text {
|
|
|
|
|
line-height: 24px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.ant-input, .title-text {
|
|
|
|
|
font-size: 20px;
|
|
|
|
|
padding: 8px;
|
|
|
|
|
border: none !important;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
|
|
|
|
|
&:hover, &:focus {
|
|
|
|
|
background: #f5f5f5;
|
|
|
|
|
box-shadow: none !important;
|
|
|
|
|
border-right-width: 0 !important;
|
|
|
|
|
border: none !important;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.task-basic-attrs-view {
|
|
|
|
|
.field-list {
|
|
|
|
|
padding: 0 40px 0 32px;
|
|
|
|
|
|
|
|
|
|
&.disabled {
|
|
|
|
|
.field-right, a {
|
|
|
|
|
cursor: not-allowed !important;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.component-mount {
|
|
|
|
|
.field {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: flex-start;
|
|
|
|
|
margin: 12px 0;
|
|
|
|
|
min-height: 36px;
|
|
|
|
|
|
|
|
|
|
.field-left {
|
|
|
|
|
align-self: flex-start;
|
|
|
|
|
width: 132px;
|
|
|
|
|
height: 36px;
|
|
|
|
|
padding-right: 12px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.field-right {
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
max-width: calc(100% - 132px);
|
|
|
|
|
|
|
|
|
|
.inline-block {
|
|
|
|
|
display: inline-block;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&.field-date {
|
|
|
|
|
display: flex;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&.width-block {
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
|
|
|
|
.w-e-text {
|
|
|
|
|
cursor: text;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.name {
|
|
|
|
|
margin: 0 8px;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.block-field {
|
|
|
|
|
width: 100%;
|
|
|
|
|
border: 1px solid #e5e5e5;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
padding: 2px 0;
|
|
|
|
|
margin-bottom: 12px;
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: flex-start;
|
|
|
|
|
flex-direction: row;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.task-child {
|
|
|
|
|
width: 100%;
|
|
|
|
|
|
|
|
|
|
.task-list {
|
|
|
|
|
padding: 8px 0;
|
|
|
|
|
|
|
|
|
|
.list-item {
|
|
|
|
|
padding: 4px 12px 4px 5px;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
/*justify-content: space-between;*/
|
|
|
|
|
|
|
|
|
|
.task-title, .task-input {
|
|
|
|
|
flex: 1 1;
|
|
|
|
|
margin: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.title-text {
|
|
|
|
|
color: #333;
|
|
|
|
|
line-height: 14px;
|
|
|
|
|
|
|
|
|
|
&.done {
|
|
|
|
|
color: #a6a6a6;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.ant-input, .title-text {
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
|
|
|
|
|
&:hover, &:focus {
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.check-box-wrapper {
|
|
|
|
|
margin-top: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.task-item {
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
margin-right: 12px;
|
|
|
|
|
|
|
|
|
|
&.disabled {
|
|
|
|
|
cursor: not-allowed;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&.check-box {
|
|
|
|
|
.anticon-check {
|
|
|
|
|
visibility: visible;
|
|
|
|
|
top: 14px;
|
|
|
|
|
left: 18px;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.action-btn {
|
|
|
|
|
padding: 2px 12px 2px 16px;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.add-handler {
|
|
|
|
|
/*margin-bottom: 8px;*/
|
|
|
|
|
padding-left: 16px;
|
|
|
|
|
padding-right: 12px;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
height: 36px;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.file-list {
|
|
|
|
|
width: 100%;
|
|
|
|
|
padding: 8px;
|
|
|
|
|
|
|
|
|
|
.ant-list-item {
|
|
|
|
|
padding: 10px 12px;
|
|
|
|
|
border-bottom: none;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
margin-bottom: 2px;
|
|
|
|
|
|
|
|
|
|
&:hover {
|
|
|
|
|
background-color: #f5f5f5;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.ant-list-item-meta-title {
|
|
|
|
|
margin-bottom: 0;
|
|
|
|
|
line-height: 24px;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.field-name {
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
white-space: nowrap;
|
|
|
|
|
padding-left: 8px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.task-description {
|
|
|
|
|
&:hover {
|
|
|
|
|
background: #f5f5f5;
|
|
|
|
|
border-radius: 4px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
&.disabled {
|
|
|
|
|
&:hover {
|
|
|
|
|
background: inherit;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
padding: 8px;
|
|
|
|
|
margin: -8px;
|
|
|
|
|
|
|
|
|
|
.description-txt {
|
|
|
|
|
max-height: 300px;
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
word-break: break-all;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
p {
|
|
|
|
|
margin: 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#editor {
|
|
|
|
|
.w-e-text {
|
|
|
|
|
min-height: 300px;
|
|
|
|
|
overflow-y: auto;
|
|
|
|
|
}
|
|
|
|
|
.w-e-text-container{
|
|
|
|
|
height: inherit !important;
|
|
|
|
|
min-height: 300px;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.content-right {
|
|
|
|
|
//width: 37%;
|
|
|
|
|
width: 410px;
|
|
|
|
|
|
|
|
|
|
.header {
|
|
|
|
|
padding: 15px 20px 20px 20px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.member-list {
|
|
|
|
|
padding-top: 12px;
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: flex-start;
|
|
|
|
|
|
|
|
|
|
.member-item {
|
|
|
|
|
margin-right: 10px;
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
|
|
|
|
|
&.invite {
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.log-wrap {
|
|
|
|
|
border-top: 1px solid #e5e5e5;
|
|
|
|
|
border-bottom: 1px solid #e5e5e5;
|
|
|
|
|
padding-bottom: 60px;
|
|
|
|
|
|
|
|
|
|
.header {
|
|
|
|
|
width: 100%;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.log-list {
|
|
|
|
|
/*font-size: 12px;*/
|
|
|
|
|
padding: 0 20px 0 20px;
|
|
|
|
|
|
|
|
|
|
.show-more {
|
|
|
|
|
display: block;
|
|
|
|
|
margin-bottom: 20px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.log-comment {
|
|
|
|
|
max-width: 405px;
|
|
|
|
|
align-items: end;
|
|
|
|
|
margin-bottom: 15px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.list-item {
|
|
|
|
|
display: flex;
|
|
|
|
|
justify-content: flex-start;
|
|
|
|
|
align-items: baseline;
|
|
|
|
|
vertical-align: middle;
|
|
|
|
|
width: 100%;
|
|
|
|
|
.log-item {
|
|
|
|
|
margin-right: 12px;
|
|
|
|
|
margin-bottom: 24px;
|
|
|
|
|
|
|
|
|
|
.anticon {
|
|
|
|
|
font-size: 14px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.log-content {
|
|
|
|
|
overflow: hidden;
|
|
|
|
|
text-overflow: ellipsis;
|
|
|
|
|
border-left: 5px solid #ccc;
|
|
|
|
|
padding-left: 12px;
|
|
|
|
|
margin-top: 6px;
|
|
|
|
|
word-break: break-all;
|
|
|
|
|
|
|
|
|
|
img{
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.log-txt {
|
|
|
|
|
flex: 1 1;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.footer {
|
|
|
|
|
padding: 20px 20px 0 20px;
|
|
|
|
|
display: flex;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.work-time-modal {
|
|
|
|
|
.work-time-stats {
|
|
|
|
|
display: flex;
|
|
|
|
|
margin-bottom: 18px;
|
|
|
|
|
|
|
|
|
|
.work-time-item {
|
|
|
|
|
margin-right: 36px;
|
|
|
|
|
|
|
|
|
|
.title {
|
|
|
|
|
font-size: 12px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.work-time-num {
|
|
|
|
|
font-size: 22px;
|
|
|
|
|
font-weight: 500;
|
|
|
|
|
margin-right: 5px;
|
|
|
|
|
font-family: dinmedium;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
.footer{
|
|
|
|
|
.ant-popover-inner-content{
|
|
|
|
|
padding: 0;
|
|
|
|
|
.mentions-content {
|
|
|
|
|
width: 200px;
|
|
|
|
|
padding: 12px 0;
|
|
|
|
|
.mentions-wrapper {
|
|
|
|
|
width: 100%;
|
|
|
|
|
display: flex;
|
|
|
|
|
align-items: center;
|
|
|
|
|
padding: 6px 12px;
|
|
|
|
|
&:hover{
|
|
|
|
|
cursor: pointer;
|
|
|
|
|
background: rgba(51, 143, 229, 0.1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</style>
|