no message

This commit is contained in:
kuaifan 2021-06-12 13:47:33 +08:00
parent f282ed5cd9
commit 1e6fecb021
12 changed files with 791 additions and 195 deletions

View File

@ -646,6 +646,72 @@ class ProjectController extends AbstractController
return Base::retSuccess('success', $data); return Base::retSuccess('success', $data);
} }
/**
* 获取任务详细描述
*
* @apiParam {Number} task_id 任务ID
*/
public function task__content()
{
$user = User::authE();
if (Base::isError($user)) {
return $user;
} else {
$user = User::IDE($user['data']);
}
//
$task_id = intval(Request::input('task_id'));
// 任务
$task = ProjectTask::whereId($task_id)->first();
if (empty($task)) {
return Base::retError('任务不存在');
}
// 项目
$project = Project::select($this->projectSelect)
->join('project_users', 'projects.id', '=', 'project_users.project_id')
->where('projects.id', $task->project_id)
->where('project_users.userid', $user->userid)
->first();
if (empty($project)) {
return Base::retError('项目不存在或不在成员列表内');
}
//
return Base::retSuccess('success', $task->content);
}
/**
* 获取任务文件列表
*
* @apiParam {Number} task_id 任务ID
*/
public function task__files()
{
$user = User::authE();
if (Base::isError($user)) {
return $user;
} else {
$user = User::IDE($user['data']);
}
//
$task_id = intval(Request::input('task_id'));
// 任务
$task = ProjectTask::whereId($task_id)->first();
if (empty($task)) {
return Base::retError('任务不存在');
}
// 项目
$project = Project::select($this->projectSelect)
->join('project_users', 'projects.id', '=', 'project_users.project_id')
->where('projects.id', $task->project_id)
->where('project_users.userid', $user->userid)
->first();
if (empty($project)) {
return Base::retError('项目不存在或不在成员列表内');
}
//
return Base::retSuccess('success', $task->taskFile);
}
/** /**
* {post} 添加任务 * {post} 添加任务
* *

View File

@ -30,6 +30,7 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @property \Illuminate\Support\Carbon|null $created_at * @property \Illuminate\Support\Carbon|null $created_at
* @property \Illuminate\Support\Carbon|null $updated_at * @property \Illuminate\Support\Carbon|null $updated_at
* @property \Illuminate\Support\Carbon|null $deleted_at * @property \Illuminate\Support\Carbon|null $deleted_at
* @property-read \App\Models\ProjectTaskContent|null $content
* @property-read int $dialog_id * @property-read int $dialog_id
* @property-read int $file_num * @property-read int $file_num
* @property-read int $msg_num * @property-read int $msg_num
@ -39,6 +40,8 @@ use Illuminate\Database\Eloquent\SoftDeletes;
* @property-read int $sub_num * @property-read int $sub_num
* @property-read bool $today * @property-read bool $today
* @property-read \App\Models\Project|null $project * @property-read \App\Models\Project|null $project
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\ProjectTaskFile[] $taskFile
* @property-read int|null $task_file_count
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\ProjectTaskTag[] $taskTag * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\ProjectTaskTag[] $taskTag
* @property-read int|null $task_tag_count * @property-read int|null $task_tag_count
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\ProjectTaskUser[] $taskUser * @property-read \Illuminate\Database\Eloquent\Collection|\App\Models\ProjectTaskUser[] $taskUser
@ -214,6 +217,22 @@ class ProjectTask extends AbstractModel
return $this->hasOne(Project::class, 'id', 'project_id'); return $this->hasOne(Project::class, 'id', 'project_id');
} }
/**
* @return \Illuminate\Database\Eloquent\Relations\HasOne
*/
public function content(): \Illuminate\Database\Eloquent\Relations\HasOne
{
return $this->hasOne(ProjectTaskContent::class, 'task_id', 'id');
}
/**
* @return \Illuminate\Database\Eloquent\Relations\HasMany
*/
public function taskFile(): \Illuminate\Database\Eloquent\Relations\HasMany
{
return $this->hasMany(projectTaskFile::class, 'task_id', 'id')->orderBy('id');
}
/** /**
* @return \Illuminate\Database\Eloquent\Relations\HasMany * @return \Illuminate\Database\Eloquent\Relations\HasMany
*/ */
@ -227,7 +246,7 @@ class ProjectTask extends AbstractModel
*/ */
public function taskTag(): \Illuminate\Database\Eloquent\Relations\HasMany public function taskTag(): \Illuminate\Database\Eloquent\Relations\HasMany
{ {
return $this->hasMany(projectTaskTag::class, 'task_id', 'id')->orderByDesc('id'); return $this->hasMany(projectTaskTag::class, 'task_id', 'id')->orderBy('id');
} }
/** /**

View File

@ -20,7 +20,7 @@
"lodash": "^4.17.19", "lodash": "^4.17.19",
"node-sass": "^4.11.0", "node-sass": "^4.11.0",
"postcss": "^8.1.14", "postcss": "^8.1.14",
"resolve-url-loader": "^3.1.3", "resolve-url-loader": "^4.0.0",
"sass": "^1.34.1", "sass": "^1.34.1",
"sass-loader": "^7.1.0", "sass-loader": "^7.1.0",
"stylus": "^0.54.8", "stylus": "^0.54.8",

View File

@ -103,11 +103,11 @@
<!--任务详情--> <!--任务详情-->
<Modal <Modal
v-model="projectTask._show" v-model="projectOpenTask._show"
:mask-closable="false" :mask-closable="false"
:styles="{ :styles="{
width: '90%', width: '90%',
maxWidth: '640px' maxWidth: projectOpenTask._dialog ? '1200px' : '640px'
}" }"
footer-hide> footer-hide>
<TaskDetail/> <TaskDetail/>
@ -169,7 +169,7 @@ export default {
}, },
computed: { computed: {
...mapState(['userId', 'userInfo', 'dialogMsgUnread', 'projectList', 'projectTask']), ...mapState(['userId', 'userInfo', 'dialogMsgUnread', 'projectList', 'projectOpenTask']),
}, },
watch: { watch: {

View File

@ -123,7 +123,7 @@
v-for="item in panelTask(column.project_task)" v-for="item in panelTask(column.project_task)"
:class="['task-item task-draggable', item.complete_at ? 'complete' : '']" :class="['task-item task-draggable', item.complete_at ? 'complete' : '']"
:style="item.color ? {backgroundColor: item.color} : {}" :style="item.color ? {backgroundColor: item.color} : {}"
@click="$store.commit('openTask', item)"> @click="$store.dispatch('openTask', item.id)">
<div :class="['task-head', item.desc ? 'has-desc' : '']"> <div :class="['task-head', item.desc ? 'has-desc' : '']">
<div class="task-title"><pre>{{item.name}}</pre></div> <div class="task-title"><pre>{{item.name}}</pre></div>
<div class="task-menu" @click.stop=""> <div class="task-menu" @click.stop="">
@ -834,7 +834,7 @@ export default {
this.$set(task, key, data[key]); this.$set(task, key, data[key]);
}); });
if (data.parent_id) { if (data.parent_id) {
this.getTaskOne(data.parent_id); this.$store.dispatch('taskOne', data.parent_id);
} }
if (typeof updata.complete_at !== "undefined") { if (typeof updata.complete_at !== "undefined") {
this.$store.commit('getProjectOne', data.project_id); this.$store.commit('getProjectOne', data.project_id);
@ -848,26 +848,6 @@ export default {
}); });
}, },
getTaskOne(task_id) {
let task = null;
this.projectDetail.project_column.some(({project_task}) => {
task = project_task.find(({id}) => id === task_id);
if (task) return true;
});
if (!task) return;
//
this.$store.dispatch("call", {
url: 'project/task/one',
data: {
task_id: task.id
},
}).then((data, msg) => {
Object.keys(data).forEach(key => {
this.$set(task, key, data[key]);
});
});
},
archivedOrRemoveTask(task, type) { archivedOrRemoveTask(task, type) {
if (task.loading === true) { if (task.loading === true) {
return; return;

View File

@ -143,14 +143,14 @@ export default {
autoresize_bottom_margin: 2, autoresize_bottom_margin: 2,
min_height: 200, min_height: 200,
max_height: 380, max_height: 380,
valid_elements : 'a[href|target=_blank],em,strong/b,div[align],span[style],a,br,img,pre[class],code', valid_elements : 'a[href|target=_blank],em,strong/b,div[align],span[style],a,br,img[src|alt|witdh|height],pre[class],code',
toolbar: 'uploadImages | uploadFiles | bold italic underline forecolor backcolor | codesample | preview screenload' toolbar: 'uploadImages | uploadFiles | bold italic underline forecolor backcolor | codesample | preview screenload'
}, },
taskOptionFull: { taskOptionFull: {
menubar: 'file edit view', menubar: 'file edit view',
forced_root_block : false, forced_root_block : false,
remove_trailing_brs: false, remove_trailing_brs: false,
valid_elements : 'a[href|target=_blank],em,strong/b,div[align],span[style],a,br,img,pre[class],code', valid_elements : 'a[href|target=_blank],em,strong/b,div[align],span[style],a,br,img[src|alt|witdh|height],pre[class],code',
toolbar: 'uploadImages | uploadFiles | bold italic underline forecolor backcolor | codesample | preview screenload' toolbar: 'uploadImages | uploadFiles | bold italic underline forecolor backcolor | codesample | preview screenload'
}, },

View File

@ -1,8 +1,8 @@
<template> <template>
<div class="task-detail"> <div :class="['task-detail', projectOpenTask._dialog || projectOpenTask._msgText ? 'open-dialog' : '']">
<div class="task-info"> <div class="task-info">
<div class="head"> <div class="head">
<Icon class="radio" type="md-radio-button-off"/> <Icon class="icon" type="md-radio-button-off"/>
<div class="nav"> <div class="nav">
<p>项目名称</p> <p>项目名称</p>
<p>列表名称</p> <p>列表名称</p>
@ -10,92 +10,161 @@
</div> </div>
<Icon class="menu" type="ios-more"/> <Icon class="menu" type="ios-more"/>
</div> </div>
<div class="title"> <div class="scroller overlay-y" :style="scrollerStyle">
<Input <div class="title">
v-model="projectTask.name" <Input
type="textarea" v-model="projectOpenTask.name"
:rows="1" type="textarea"
:autosize="{ minRows: 1, maxRows: 8 }" :rows="1"
:maxlength="255"/> :autosize="{ minRows: 1, maxRows: 8 }"
</div> :maxlength="255"/>
<div class="desc"> </div>
<TEditor <div class="desc">
v-model="content" <TEditor
:plugins="taskPlugins" v-model="content"
:options="taskOptions" :plugins="taskPlugins"
:option-full="taskOptionFull" :options="taskOptions"
:placeholder="$L('详细描述...')" :option-full="taskOptionFull"
inline></TEditor> :placeholder="$L('详细描述...')"
</div> inline></TEditor>
<Form class="items" label-position="left" label-width="auto" @submit.native.prevent> </div>
<FormItem> <Form class="items" label-position="left" label-width="auto" @submit.native.prevent>
<div class="item-label" slot="label"> <FormItem>
<i class="iconfont">&#xe6ec;</i> <div class="item-label" slot="label">
{{$L('优先级')}} <i class="iconfont">&#xe6ec;</i>{{$L('优先级')}}
</div> </div>
<ul class="item-content"> <ul class="item-content">
<li>紧急且重要</li> <li>紧急且重要</li>
</ul> </ul>
</FormItem> </FormItem>
<FormItem> <FormItem>
<div class="item-label" slot="label"> <div class="item-label" slot="label">
<i class="iconfont">&#xe6e4;</i> <i class="iconfont">&#xe6e4;</i>{{$L('负责人')}}
{{$L('负责人')}} </div>
</div> <ul class="item-content user">
<ul class="item-content"> <li><UserAvatar :userid="1"/></li>
<li><UserAvatar :userid="1"/></li> </ul>
</ul> </FormItem>
</FormItem> <FormItem>
<FormItem> <div class="item-label" slot="label">
<div class="item-label" slot="label"> <i class="iconfont">&#xe6e8;</i>{{$L('截止时间')}}
<i class="iconfont">&#xe6e8;</i> </div>
{{$L('截止时间')}} <ul class="item-content">
</div> <li>2020/10/11 10:00</li>
<ul class="item-content"> </ul>
<li>2020/10/11 10:00</li> </FormItem>
</ul> <FormItem>
</FormItem> <div class="item-label" slot="label">
<FormItem> <i class="iconfont">&#xe6e6;</i>{{$L('附件')}}
<div class="item-label" slot="label"> </div>
<i class="iconfont">&#xe6e6;</i> <ul class="item-content file">
{{$L('附件')}} <li>
</div> <img class="file-ext" :src="'/images/ext/psd.png'"/>
<ul class="item-content file"> <div class="file-name">附件名称.psd</div>
<li>文件11</li> <div class="file-size">20.5kb</div>
<li>文件22</li> </li>
<li>文件33</li> <li>
<li> <img class="file-ext" :src="'/images/ext/xls.png'"/>
<div class="add-button"> <div class="file-name">附件名称.xls</div>
<i class="iconfont">&#xe6f2;</i>{{$L('添加附件')}} <div class="file-size">20.5kb</div>
</div> </li>
</li> <li>
</ul> <img class="file-ext" :src="'/images/ext/doc.png'"/>
</FormItem> <div class="file-name">附件名称.doc</div>
<FormItem> <div class="file-size">20.5kb</div>
<div class="item-label" slot="label"> </li>
<i class="iconfont">&#xe6f0;</i> <li>
{{$L('子任务')}} <div class="add-button">
</div> <i class="iconfont">&#xe6f2;</i>{{$L('添加附件')}}
<ul class="item-content subtask"> </div>
<li>文件11</li> </li>
<li>文件22</li> </ul>
<li>文件33</li> </FormItem>
<li> <FormItem>
<div class="add-button"> <div class="item-label" slot="label">
<i class="iconfont">&#xe6f2;</i>{{$L('添加子任务')}} <i class="iconfont">&#xe6f0;</i>{{$L('子任务')}}
</div> </div>
</li> <ul class="item-content subtask">
</ul> <li>
</FormItem> <Icon class="subtask-icon" type="md-radio-button-off" />
</Form> <div class="subtask-name">
<div class="add"> <Input
<div class="add-button"> v-model="projectOpenTask.name"
<i class="iconfont">&#xe6f2;</i>{{$L('添加模块')}} type="textarea"
:rows="1"
:autosize="{ minRows: 1, maxRows: 8 }"
:maxlength="255"/>
</div>
<div class="subtask-time today">{{expiresFormat('2021-06-12')}}</div>
<UserAvatar class="subtask-avatar" :userid="1" :size="20"/>
</li>
<li>
<Icon class="subtask-icon" type="md-radio-button-off" />
<div class="subtask-name">
<Input
v-model="projectOpenTask.name"
type="textarea"
:rows="1"
:autosize="{ minRows: 1, maxRows: 8 }"
:maxlength="255"/>
</div>
<div class="subtask-time overdue">{{expiresFormat('2021-06-11')}}</div>
<UserAvatar class="subtask-avatar" :userid="1" :size="20"/>
</li>
<li>
<Icon class="subtask-icon" type="md-radio-button-off" />
<div class="subtask-name">
<Input
v-model="projectOpenTask.name"
type="textarea"
:rows="1"
:autosize="{ minRows: 1, maxRows: 8 }"
:maxlength="255"/>
</div>
<div class="subtask-time">{{expiresFormat('2021-06-12')}}</div>
<UserAvatar class="subtask-avatar" :userid="1" :size="20"/>
</li>
<li>
<div class="add-button">
<i class="iconfont">&#xe6f2;</i>{{$L('添加子任务')}}
</div>
</li>
</ul>
</FormItem>
</Form>
<div class="add">
<EDropdown
trigger="click"
placement="bottom-start"
@command="">
<div class="add-button">
<i class="iconfont">&#xe6f2;</i>{{$L('添加模块')}}
</div>
<EDropdownMenu slot="dropdown">
<EDropdownItem v-for="(item, key) in menuList" :key="key" :command="item.command">
<div class="item">
<i class="iconfont" v-html="item.icon"></i>{{$L(item.name)}}
</div>
</EDropdownItem>
</EDropdownMenu>
</EDropdown>
</div> </div>
</div> </div>
</div> </div>
<div class="task-dialog"> <div class="task-dialog">
<div class="head">
<Icon class="icon" type="ios-chatbubbles-outline" />
<div class="nav">
<p class="active">群聊</p>
<p>动态</p>
</div>
</div>
<div class="no-dialog">
<div class="no-tip">{{$L('暂无消息')}}</div>
<div class="no-input">
<Input class="dialog-input" v-model="projectOpenTask._msgText" type="textarea" :rows="1" :autosize="{ minRows: 1, maxRows: 3 }" :maxlength="255" :placeholder="$L('输入消息...')" />
</div>
</div>
</div> </div>
</div> </div>
</template> </template>
@ -109,8 +178,37 @@ export default {
components: {TEditor}, components: {TEditor},
data() { data() {
return { return {
nowTime: Math.round(new Date().getTime() / 1000),
nowInterval: null,
innerHeight: window.innerHeight,
content: '随着互联网的发展,生活智能化越来越普及,各类智能产品逐渐出现到人们面前,在体验的过程中,其实里面有 很多细节需要深挖和思考。很多产品细节的背后都是为了提升用户操作效率、兼容用户使用场景、满足用户情感 表达,以最终达到对用户体验的提升。作为智能产品的设计师只有充分了解市面上的智能产品,才能设计出更好', content: '随着互联网的发展,生活智能化越来越普及,各类智能产品逐渐出现到人们面前,在体验的过程中,其实里面有 很多细节需要深挖和思考。很多产品细节的背后都是为了提升用户操作效率、兼容用户使用场景、满足用户情感 表达,以最终达到对用户体验的提升。作为智能产品的设计师只有充分了解市面上的智能产品,才能设计出更好',
menuList: [
{
command: 'priority',
icon: '&#xe6ec;',
name: '优先级',
}, {
command: 'owner',
icon: '&#xe6e4;',
name: '负责人',
}, {
command: 'times',
icon: '&#xe6e8;',
name: '截止时间',
}, {
command: 'file',
icon: '&#xe6e6;',
name: '附件',
}, {
command: 'subtask',
icon: '&#xe6f0;',
name: '子任务',
}
],
taskPlugins: [ taskPlugins: [
'advlist autolink lists link image charmap print preview hr anchor pagebreak imagetools', 'advlist autolink lists link image charmap print preview hr anchor pagebreak imagetools',
'searchreplace visualblocks visualchars code', 'searchreplace visualblocks visualchars code',
@ -126,20 +224,102 @@ export default {
autoresize_bottom_margin: 2, autoresize_bottom_margin: 2,
min_height: 200, min_height: 200,
max_height: 380, max_height: 380,
valid_elements : 'a[href|target=_blank],em,strong/b,div[align],span[style],a,br,img,pre[class],code', valid_elements : 'a[href|target=_blank],em,strong/b,div[align],span[style],a,br,img[src|alt|witdh|height],pre[class],code',
toolbar: 'uploadImages | uploadFiles | bold italic underline forecolor backcolor | codesample | preview screenload' toolbar: 'uploadImages | uploadFiles | bold italic underline forecolor backcolor | codesample | preview screenload'
}, },
taskOptionFull: { taskOptionFull: {
menubar: 'file edit view', menubar: 'file edit view',
forced_root_block : false, forced_root_block : false,
remove_trailing_brs: false, remove_trailing_brs: false,
valid_elements : 'a[href|target=_blank],em,strong/b,div[align],span[style],a,br,img,pre[class],code', valid_elements : 'a[href|target=_blank],em,strong/b,div[align],span[style],a,br,img[src|alt|witdh|height],pre[class],code',
toolbar: 'uploadImages | uploadFiles | bold italic underline forecolor backcolor | codesample | preview screenload' toolbar: 'uploadImages | uploadFiles | bold italic underline forecolor backcolor | codesample | preview screenload'
}, },
} }
}, },
computed: {
...mapState(['userId', 'projectTask']), mounted() {
this.nowInterval = setInterval(() => {
this.nowTime = Math.round(new Date().getTime() / 1000);
}, 1000);
window.addEventListener('resize', this.innerHeightListener);
}, },
destroyed() {
clearInterval(this.nowInterval);
window.removeEventListener('resize', this.innerHeightListener);
},
computed: {
...mapState(['userId', 'projectOpenTask']),
scrollerStyle() {
const {innerHeight, projectOpenTask} = this;
if (!innerHeight || !projectOpenTask._dialog) {
return {};
}
return {
maxHeight: (innerHeight - 70 - 66 - 30) + 'px'
}
},
expiresFormat() {
const {nowTime} = this;
return function (date) {
let time = Math.round(new Date(date).getTime() / 1000) - nowTime;
if (time < 86400 * 4 && time > 0 ) {
return this.formatSeconds(time);
} else if (time <= 0) {
return '-' + this.formatSeconds(time * -1);
}
return this.formatTime(date)
}
},
},
watch: {
},
methods: {
innerHeightListener() {
this.innerHeight = window.innerHeight;
},
formatTime(date) {
let time = Math.round(new Date(date).getTime() / 1000),
string = '';
if ($A.formatDate('Ymd') === $A.formatDate('Ymd', time)) {
string = $A.formatDate('H:i', time)
} else if ($A.formatDate('Y') === $A.formatDate('Y', time)) {
string = $A.formatDate('m-d', time)
} else {
string = $A.formatDate('Y-m-d', time)
}
return string || '';
},
formatBit(val) {
val = +val
return val > 9 ? val : '0' + val
},
formatSeconds(second) {
let duration
let days = Math.floor(second / 86400);
let hours = Math.floor((second % 86400) / 3600);
let minutes = Math.floor(((second % 86400) % 3600) / 60);
let seconds = Math.floor(((second % 86400) % 3600) % 60);
if (days > 0) {
if (hours > 0) duration = days + "d," + this.formatBit(hours) + "h";
else if (minutes > 0) duration = days + "d," + this.formatBit(minutes) + "min";
else if (seconds > 0) duration = days + "d," + this.formatBit(seconds) + "s";
else duration = days + "d";
}
else if (hours > 0) duration = this.formatBit(hours) + ":" + this.formatBit(minutes) + ":" + this.formatBit(seconds);
else if (minutes > 0) duration = this.formatBit(minutes) + ":" + this.formatBit(seconds);
else if (seconds > 0) duration = this.formatBit(seconds) + "s";
return duration;
},
}
} }
</script> </script>

View File

@ -50,7 +50,7 @@
</template> </template>
</EDropdownMenu> </EDropdownMenu>
</EDropdown> </EDropdown>
<div class="item-title" @click="$store.commit('openTask', item)">{{item.name}}</div> <div class="item-title" @click="$store.dispatch('openTask', item.id)">{{item.name}}</div>
<div v-if="item.sub_num > 0" class="item-sub-num" @click="getSublist(item)"> <div v-if="item.sub_num > 0" class="item-sub-num" @click="getSublist(item)">
<Icon type="md-git-merge" /> <Icon type="md-git-merge" />
{{item.sub_complete}}/{{item.sub_num}} {{item.sub_complete}}/{{item.sub_num}}
@ -158,12 +158,7 @@ export default {
return; return;
} }
this.$set(task, 'loading', true); this.$set(task, 'loading', true);
this.$store.dispatch("call", { this.$store.dispatch("subTask", task.id).then((data, msg) => {
url: 'project/task/sublist',
data: {
task_id: task.id,
},
}).then((data, msg) => {
this.$set(task, 'loading', false); this.$set(task, 'loading', false);
this.$set(task, 'sub_list', data); this.$set(task, 'sub_list', data);
this.$set(task, 'sub_open', true); this.$set(task, 'sub_open', true);

View File

@ -118,5 +118,142 @@ export default {
} }
$A.ajaxc(params); $A.ajaxc(params);
}) })
} },
/**
* 获取任务信息
* @param state
* @param dispatch
* @param task_id
* @returns {Promise<unknown>}
*/
taskOne({state, dispatch}, task_id) {
return new Promise(function (resolve, reject) {
dispatch("call", {
url: 'project/task/one',
data: {
task_id,
},
}).then((data, msg) => {
state.projectDetail.project_column.some(({project_task}) => {
let index = project_task.findIndex(({id}) => id === task_id);
if (index > -1) {
project_task.splice(index, 1, Object.assign(project_task[index], data))
return true;
}
});
if (task_id == state.projectOpenTask.id) {
state.projectOpenTask = Object.assign({}, state.projectOpenTask, data);
}
resolve(data, msg)
}).catch((data, msg) => {
reject(data, msg)
});
});
},
/**
* 获取任务详细描述
* @param state
* @param dispatch
* @param task_id
* @returns {Promise<unknown>}
*/
taskContent({state, dispatch}, task_id) {
return new Promise(function (resolve, reject) {
dispatch("call", {
url: 'project/task/content',
data: {
task_id,
},
}).then((data, msg) => {
state.projectTaskContent[task_id] = data;
if (task_id == state.projectOpenTask.id) {
state.projectOpenTask = Object.assign({}, state.projectOpenTask, {content: data || {}});
}
resolve(data, msg)
}).catch((data, msg) => {
reject(data, msg)
});
});
},
/**
* 获取任务文件
* @param state
* @param dispatch
* @param task_id
* @returns {Promise<unknown>}
*/
taskFiles({state, dispatch}, task_id) {
return new Promise(function (resolve, reject) {
dispatch("call", {
url: 'project/task/files',
data: {
task_id,
},
}).then((data, msg) => {
state.projectTaskFiles[task_id] = data;
if (task_id == state.projectOpenTask.id) {
state.projectOpenTask = Object.assign({}, state.projectOpenTask, {files: data});
}
resolve(data, msg)
}).catch((data, msg) => {
reject(data, msg)
});
});
},
/**
* 获取子任务
* @param state
* @param dispatch
* @param task_id
* @returns {Promise<unknown>}
*/
subTask({state, dispatch}, task_id) {
return new Promise(function (resolve, reject) {
dispatch("call", {
url: 'project/task/sublist',
data: {
task_id,
},
}).then((data, msg) => {
state.projectSubTask[task_id] = data;
if (task_id == state.projectOpenTask.id) {
state.projectOpenTask = Object.assign({}, state.projectOpenTask, {sub_task: data});
}
resolve(data, msg)
}).catch((data, msg) => {
reject(data, msg)
});
});
},
/**
* 打开任务详情页
* @param state
* @param dispatch
* @param task_id
*/
openTask({state, dispatch}, task_id) {
let data = {id: task_id};
state.projectDetail.project_column.some(({project_task}) => {
const task = project_task.find(({id}) => id === task_id);
if (task) {
data = Object.assign(data, task);
return true
}
});
//
data.content = state.projectTaskContent[task_id] || {}
data.files = state.projectTaskFiles[task_id] || []
data.subtask = state.projectSubTask[task_id] || []
state.projectOpenTask = Object.assign({}, data, {_show: true});
//
dispatch("taskOne", task_id);
dispatch("taskContent", task_id);
dispatch("taskFiles", task_id);
dispatch("subTask", task_id);
},
} }

View File

@ -181,15 +181,6 @@ export default {
} }
}, },
/**
* 打开任务详情页
* @param state
* @param task
*/
openTask(state, task) {
state.projectTask = Object.assign({_show:true}, task);
},
/** /**
* 获取用户基本信息 * 获取用户基本信息
* @param state * @param state

View File

@ -244,13 +244,16 @@ state.wsListener = {};
state.wsReadTimeout = null; state.wsReadTimeout = null;
state.wsReadWaitList = []; state.wsReadWaitList = [];
// 项目信息 // 项目任务
state.projectLoad = 0; state.projectLoad = 0;
state.projectList = state.cacheProjectList; state.projectList = state.cacheProjectList;
state.projectDetail = {id: 0, project_column: [], project_user: []}; state.projectDetail = {id: 0, project_column: [], project_user: []};
state.projectTask = {_show: false, id: 0, task_user: [], task_tag: []}; state.projectOpenTask = {_show: false, id: 0, task_user: [], task_tag: []};
state.projectTaskContent = {};
state.projectTaskFiles = {};
state.projectSubTask = {};
// 会话消息 // 会话聊天
state.dialogId = 0; state.dialogId = 0;
state.dialogList = []; state.dialogList = [];
state.dialogDetail = {}; state.dialogDetail = {};

View File

@ -1,6 +1,7 @@
.task-detail { .task-detail {
display: flex; display: flex;
padding-bottom: 36px; flex-direction: column;
margin: 0 -10px 30px;
.task-info { .task-info {
flex: 1; flex: 1;
display: flex; display: flex;
@ -16,14 +17,15 @@
content: ""; content: "";
position: absolute; position: absolute;
left: 36px; left: 36px;
right: -12px; right: 0;
bottom: 0; bottom: 0;
height: 1px; height: 1px;
background-color: #f4f5f5; background-color: #f4f5f5;
} }
.radio { .icon {
width: 18px; width: 18px;
font-size: 16px; font-size: 18px;
cursor: pointer;
} }
.nav { .nav {
flex: 1; flex: 1;
@ -48,76 +50,299 @@
} }
.menu { .menu {
font-size: 22px; font-size: 22px;
margin: 0 18px; margin: 0 32px;
cursor: pointer;
} }
} }
.title { .scroller {
margin: 12px 36px 0; margin-left: 36px;
.ivu-input { padding-right: 36px;
font-weight: 500; overflow-x: hidden;
font-size: 24px; overflow-y: auto;
padding: 4px 0; .title {
resize: none; margin-top: 18px;
border-color: transparent; .ivu-input {
&:focus { font-weight: 500;
box-shadow: none font-size: 24px;
padding: 4px 0;
line-height: 1.4;
resize: none;
border-color: transparent;
&:focus {
box-shadow: none
}
} }
} }
} .desc {
.desc { margin-top: 10px;
margin: 12px 36px 0; div[contenteditable="true"] {
div[contenteditable="true"] { outline: none
outline: none }
} .mce-content-body[data-mce-placeholder]:not(.mce-visualblocks)::before {
} color: #bbbbbb;
.items {
margin: 12px 36px 0;
.ivu-form-item {
margin-bottom: 10px;
}
.item-label {
display: flex;
align-items: center;
color: #aaaaaa;
.iconfont {
margin-right: 3px;
} }
} }
.item-content { .items {
margin-top: 5px; margin-top: 12px;
margin-left: 12px; .ivu-form-item {
> li { margin-bottom: 10px;
}
.item-label {
display: flex; display: flex;
align-items: center; align-items: center;
list-style: none; color: #bbbbbb;
line-height: 26px; .iconfont {
margin-right: 4px;
}
} }
&.file { .item-content {
margin-top: 5px;
} margin-left: 12px;
&.subtask { > li {
display: flex;
align-items: center;
list-style: none;
line-height: 26px;
}
&.user {
margin-top: 2px;
> li {
}
}
&.file {
> li {
margin-bottom: 2px;
.file-ext {
width: 16px;
}
.file-name {
padding-left: 8px;
}
.file-size {
padding-left: 10px;
height: 24px;
font-size: 12px;
color: #bbbbbb;
}
}
}
&.subtask {
> li {
align-items: flex-start;
margin-bottom: 2px;
.subtask-icon {
width: 16px;
height: 26px;
line-height: 26px;
font-size: 16px;
color: #cccccc;
margin-right: 8px;
cursor: pointer;
&.completed {
color: #87d068;
}
&.sub-icon {
font-size: 16px;
width: 16px;
height: 16px;
margin-left: -20px;
margin-right: 4px;
color: #cfcfcf;
transition: transform 0.2s;
&.active {
transform: rotate(90deg);
}
}
}
.subtask-name {
flex: 1;
margin-right: 16px;
display: flex;
.ivu-input {
margin: -1px 0;
padding: 4px 0;
resize: none;
border-color: transparent;
line-height: 20px;
&:focus {
box-shadow: none
}
}
}
.subtask-time {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
margin-right: 6px;
font-size: 13px;
height: 26px;
line-height: 26px;
&.overdue {
font-weight: 600;
color: #ed4014;
}
&.today {
font-weight: 500;
color: #ff9900;
}
}
.subtask-avatar {
height: 26px;
line-height: 1;
.avatar-box {
&:before {
transform: scale(0.7);
right: -2px;
bottom: -1px;
}
}
}
}
}
} }
} }
} .add {
.add { margin-top: 12px;
margin: 12px 36px 0;
}
.add-button {
cursor: pointer;
color: #aaaaaa;
display: flex;
align-items: center;
> i {
font-size: 14px;
padding-right: 8px;
} }
&:hover { .add-button {
color: #999999; cursor: pointer;
color: #bbbbbb;
display: flex;
align-items: center;
margin-top: 6px;
> i {
font-size: 14px;
padding-right: 8px;
}
&:hover {
color: #999999;
}
} }
} }
} }
.task-dialog { .task-dialog {
display: none; flex: 1;
display: flex;
flex-direction: column;
margin-top: 32px;
position: relative;
.head {
display: flex;
align-items: center;
height: 40px;
padding-bottom: 10px;
color: #888888;
position: relative;
&:before {
content: "";
position: absolute;
left: 36px;
right: 0;
bottom: 0;
height: 1px;
background-color: #f4f5f5;
}
.icon {
width: 18px;
font-size: 18px;
}
.nav {
flex: 1;
display: flex;
align-items: center;
padding-left: 18px;
font-weight: 500;
color: #666666;
> p {
display: flex;
align-items: center;
margin-right: 28px;
&.active {
margin-top: -2px;
font-size: 18px;
font-weight: 600;
color: #555555;
}
}
}
}
.no-dialog {
flex: 1;
display: flex;
flex-direction: column;
.no-tip {
flex: 1;
display: none;
margin-left: 36px;
}
.no-input {
margin: 32px 0 0 36px;
background-color: #F4F5F7;
padding: 10px 12px;
border-radius: 10px;
.ivu-input {
border: 0;
resize: none;
background-color: transparent;
&:focus {
box-shadow: none;
}
}
}
}
}
&.open-dialog {
flex-direction: row;
.task-info {
overflow: auto;
.head {
.menu {
margin-right: 0;
}
}
}
.task-dialog {
margin: 0 0 0 18px;
min-width: 320px;
max-width: 450px;
&:before {
content: "";
position: absolute;
top: 0;
left: 0;
bottom: 0;
width: 1px;
background-color: #f4f5f5;
}
.head {
&:before {
left: 18px;
}
.icon {
&:before {
display: none;
}
}
.nav {
padding-left: 0;
}
}
.no-dialog {
.no-tip {
display: flex;
align-items: center;
justify-content: center;
margin-left: 18px;
color: #999999;
}
.no-input {
margin: 0 0 0 18px;
}
}
}
} }
} }