Compare commits

...

22 Commits

Author SHA1 Message Date
kuaifan
9eaa575d1a build 2022-03-04 08:53:42 +08:00
kuaifan
996ed78a0e perf: 优化仪表盘角标数 2022-03-04 08:51:22 +08:00
kuaifan
1759572b2e perf: 优化客户端任务详情按command+s保存 2022-03-04 08:50:22 +08:00
kuaifan
0a9f9eea90 build 2022-03-04 00:00:26 +08:00
kuaifan
223fb540b1 perf: 优化文件重命名,支持按esc取消编辑 2022-03-03 23:57:58 +08:00
kuaifan
c5f1c95f7b perf: 任务详情打开操作菜单时按esc任务窗口隐藏了但是菜单还看见 2022-03-03 23:06:54 +08:00
kuaifan
3bbcbca926 build 2022-03-03 16:21:08 +08:00
kuaifan
63c1deb630 fix: md编辑器出现toc混乱的情况 2022-03-03 16:17:49 +08:00
kuaifan
424e2428fe pref: 上传文件名称过程显示错位的问题 2022-03-03 15:11:50 +08:00
kuaifan
2fdef45156 pref: 退出登录返回登录页而不是注册页 2022-03-03 15:11:34 +08:00
kuaifan
4cd4550a36 pref: 网络异常的情况下需提示网络异常而不是系统出错 2022-03-03 14:53:01 +08:00
kuaifan
16af625aef no msg 2022-03-03 14:35:21 +08:00
kuaifan
8e72794c07 pref: 任务详情当任务倒计时结束时显示"超期未完成"标签 2022-03-03 14:35:12 +08:00
kuaifan
d9cf6d7e1b pref: 优化脚本,支持部分服务器是docker compose命令 2022-03-03 14:22:25 +08:00
kuaifan
f4e4252227 perf: 优化已读回执 2022-03-03 11:41:54 +08:00
kuaifan
7107409b1b no message 2022-03-02 08:46:29 +08:00
kuaifan
6c086fab6f no message 2022-03-02 08:32:38 +08:00
kuaifan
d027d67e08 pref: 倒计时刚到到达0时会显示自定义才继续显示计时,且未显示超时标签 2022-03-02 08:25:38 +08:00
kuaifan
a7d9e635eb pref: 鼠标滑动至仪表盘中的待完成任务卡片时,卡片周围未显示光晕,且未显示为手指样式 2022-03-02 08:19:09 +08:00
kuaifan
e7196efaea pref: 优化任务详细描述显示 2022-03-02 08:17:30 +08:00
kuaifan
0a2a903c74 fix: 修复登录页设置下拉显示不全的情况 2022-03-02 07:59:53 +08:00
kuaifan
8104c26b19 处理任务简介出现"的情况 2022-03-02 07:48:29 +08:00
55 changed files with 280 additions and 142 deletions

View File

@ -70,11 +70,7 @@ class WebSocketDialogMsg extends AbstractModel
public function getPercentageAttribute()
{
if (!isset($this->appendattrs['percentage'])) {
if ($this->read > $this->send || empty($this->send)) {
$this->appendattrs['percentage'] = 100;
} else {
$this->appendattrs['percentage'] = intval($this->read / $this->send * 100);
}
$this->generatePercentage();
}
return $this->appendattrs['percentage'];
}
@ -98,6 +94,22 @@ class WebSocketDialogMsg extends AbstractModel
return $value;
}
/**
* 获取占比
* @param bool $increment 是否新增阅读数
* @return int
*/
public function generatePercentage($increment = false) {
if ($increment) {
$this->increment('read');
}
if ($this->read > $this->send || empty($this->send)) {
return $this->appendattrs['percentage'] = 100;
} else {
return $this->appendattrs['percentage'] = intval($this->read / $this->send * 100);
}
}
/**
* 标记已送达 同时 告诉发送人已送达
* @param $userid
@ -127,13 +139,17 @@ class WebSocketDialogMsg extends AbstractModel
if (!$msgRead->read_at) {
$msgRead->read_at = Carbon::now();
$msgRead->save();
$this->increment('read');
$this->generatePercentage(true);
PushTask::push([
'userid' => $this->userid,
'msg' => [
'type' => 'dialog',
'mode' => 'update',
'data' => $this->toArray(),
'mode' => 'readed',
'data' => [
'id' => $this->id,
'read' => $this->read,
'percentage' => $this->percentage,
],
]
]);
}

View File

@ -342,19 +342,15 @@ class Base
{
if (strtolower($charset) == 'utf-8') {
if (Base::getStrlen($string) <= $length) return $string;
$strcut = str_replace(array('&amp;', '&quot;', '&lt;', '&gt;'), array('&', '"', '<', '>'), $string);
$strcut = Base::utf8Substr($strcut, $length, $start);
$strcut = str_replace(array('&', '"', '<', '>'), array('&amp;', '&quot;', '&lt;', '&gt;'), $strcut);
$strcut = Base::utf8Substr($string, $length, $start);
return $strcut . $dot;
} else {
$length = $length * 2;
if (strlen($string) <= $length) return $string;
$string = str_replace(array('&amp;', '&quot;', '&lt;', '&gt;'), array('&', '"', '<', '>'), $string);
$strcut = '';
for ($i = 0; $i < $length; $i++) {
$strcut .= ord($string[$i]) > 127 ? $string[$i] . $string[++$i] : $string[$i];
}
$strcut = str_replace(array('&', '"', '<', '>'), array('&amp;', '&quot;', '&lt;', '&gt;'), $strcut);
}
return $strcut . $dot;
}

View File

@ -127,9 +127,11 @@ class WebSocketService implements WebSocketHandlerInterface
case 'readMsg':
$ids = is_array($data['id']) ? $data['id'] : [$data['id']];
$userid = $this->getUserid($frame->fd);
$list = WebSocketDialogMsg::whereIn('id', $ids)->get();
$list->transform(function(WebSocketDialogMsg $item) use ($userid) {
$item->readSuccess($userid);
WebSocketDialogMsg::whereIn('id', $ids)->chunkById(20, function($list) use ($userid) {
/** @var WebSocketDialogMsg $item */
foreach ($list as $item) {
$item->readSuccess($userid);
}
});
return;

35
cmd
View File

@ -13,6 +13,7 @@ Error="${Red}[错误]${Font}"
cur_path="$(pwd)"
cur_arg=$@
COMPOSE="docker-compose"
judge() {
if [[ 0 -eq $? ]]; then
@ -57,18 +58,22 @@ check_docker() {
fi
docker-compose version &> /dev/null
if [ $? -ne 0 ]; then
echo -e "${Error} ${RedBG} 未安装 Docker-compose${Font}"
exit 1
docker compose version &> /dev/null
if [ $? -ne 0 ]; then
echo -e "${Error} ${RedBG} 未安装 Docker-compose${Font}"
exit 1
fi
COMPOSE="docker compose"
fi
if [[ -n `docker-compose version | grep "docker-compose" | grep -E "\sv*1"` ]]; then
docker-compose version
if [[ -n `$COMPOSE version | grep -E "\sv*1"` ]]; then
$COMPOSE version
echo -e "${Error} ${RedBG} Docker-compose 版本过低请升级至v2+${Font}"
exit 1
fi
}
check_node() {
npm --version > /dev/null
npm --version &> /dev/null
if [ $? -ne 0 ]; then
echo -e "${Error} ${RedBG} 未安装nodejs${Font}"
exit 1
@ -76,7 +81,7 @@ check_node() {
}
docker_name() {
echo `docker-compose ps | awk '{print $1}' | grep "\-$1\-"`
echo `$COMPOSE ps | awk '{print $1}' | grep "\-$1\-"`
}
run_compile() {
@ -272,7 +277,7 @@ if [ $# -gt 0 ]; then
chmod -R 775 "${cur_path}/docker/mysql/data"
# 启动容器
[[ "$(arg_get port)" -gt 0 ]] && env_set APP_PORT "$(arg_get port)"
docker-compose up php -d
$COMPOSE up php -d
# 安装composer依赖
run_exec php "composer install"
if [ ! -f "${cur_path}/vendor/autoload.php" ]; then
@ -300,7 +305,7 @@ if [ $# -gt 0 ]; then
run_exec php "php artisan migrate --seed"
# 设置初始化密码
res=`run_exec mariadb "sh /etc/mysql/repassword.sh"`
docker-compose up -d
$COMPOSE up -d
supervisorctl_restart php
echo -e "${OK} ${GreenBG} 安装完成 ${Font}"
echo -e "地址: http://${GreenBG}127.0.0.1:$(env_get APP_PORT)${Font}"
@ -314,7 +319,7 @@ if [ $# -gt 0 ]; then
run_exec php "composer update"
run_exec php "php artisan migrate"
supervisorctl_restart php
docker-compose up -d
$COMPOSE up -d
elif [[ "$1" == "uninstall" ]]; then
shift 1
read -rp "确定要卸载(含:删除容器、数据库、日志)吗?(y/n): " uninstall
@ -328,7 +333,7 @@ if [ $# -gt 0 ]; then
exit 2
;;
esac
docker-compose down
$COMPOSE down
rm -rf "./docker/mysql/data"
rm -rf "./docker/log/supervisor"
find "./storage/logs" -name "*.log" | xargs rm -rf
@ -341,7 +346,7 @@ if [ $# -gt 0 ]; then
elif [[ "$1" == "port" ]]; then
shift 1
env_set APP_PORT "$1"
docker-compose up -d
$COMPOSE up -d
echo -e "${OK} ${GreenBG} 修改成功 ${Font}"
echo -e "地址: http://${GreenBG}127.0.0.1:$(env_get APP_PORT)${Font}"
elif [[ "$1" == "repassword" ]]; then
@ -414,11 +419,11 @@ if [ $# -gt 0 ]; then
e="./vendor/bin/phpunit $@" && run_exec php "$e"
elif [[ "$1" == "restart" ]]; then
shift 1
docker-compose stop "$@"
docker-compose start "$@"
$COMPOSE stop "$@"
$COMPOSE start "$@"
else
docker-compose "$@"
$COMPOSE "$@"
fi
else
docker-compose ps
$COMPOSE ps
fi

View File

@ -1,6 +1,6 @@
{
"name": "DooTask",
"version": "0.9.83",
"version": "0.10.5",
"description": "DooTask is task management system.",
"main": "electron.js",
"license": "MIT",

View File

@ -1,6 +1,6 @@
{
"name": "DooTask",
"version": "0.9.83",
"version": "0.10.5",
"description": "DooTask is task management system.",
"scripts": {
"start": "./cmd dev",

2
public/css/app.css vendored

File diff suppressed because one or more lines are too long

2
public/js/app.js vendored

File diff suppressed because one or more lines are too long

2
public/js/build/189.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,5 @@
/*!
* clipboard.js v2.0.10
* clipboard.js v2.0.8
* https://clipboardjs.com/
*
* Licensed MIT © Zeno Rocha

File diff suppressed because one or more lines are too long

View File

@ -257,7 +257,7 @@
/** @license
*
* jsPDF - PDF Document creation from JavaScript
* Version 2.5.1 Built on 2022-01-28T15:37:57.791Z
* Version 2.5.0 Built on 2021-12-21T09:44:51.866Z
* CommitID 00000000
*
* Copyright (c) 2010-2021 James Hall <james@parall.ax>, https://github.com/MrRio/jsPDF

File diff suppressed because one or more lines are too long

2
public/js/build/304.js vendored Normal file

File diff suppressed because one or more lines are too long

View File

@ -1,5 +1,5 @@
/*!
* clipboard.js v2.0.10
* clipboard.js v2.0.8
* https://clipboardjs.com/
*
* Licensed MIT © Zeno Rocha

File diff suppressed because one or more lines are too long

1
public/js/build/422.js vendored Normal file

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -257,7 +257,7 @@
/** @license
*
* jsPDF - PDF Document creation from JavaScript
* Version 2.5.1 Built on 2022-01-28T15:37:57.791Z
* Version 2.5.0 Built on 2021-12-21T09:44:51.866Z
* CommitID 00000000
*
* Copyright (c) 2010-2021 James Hall <james@parall.ax>, https://github.com/MrRio/jsPDF

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -257,7 +257,7 @@
/** @license
*
* jsPDF - PDF Document creation from JavaScript
* Version 2.5.1 Built on 2022-01-28T15:37:57.791Z
* Version 2.5.0 Built on 2021-12-21T09:44:51.866Z
* CommitID 00000000
*
* Copyright (c) 2010-2021 James Hall <james@parall.ax>, https://github.com/MrRio/jsPDF

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1 +1 @@
/*! @license DOMPurify 2.3.6 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.3.6/LICENSE */
/*! @license DOMPurify 2.3.4 | (c) Cure53 and other contributors | Released under the Apache license 2.0 and Mozilla Public License 2.0 | github.com/cure53/DOMPurify/blob/2.3.4/LICENSE */

File diff suppressed because one or more lines are too long

View File

@ -257,7 +257,7 @@
/** @license
*
* jsPDF - PDF Document creation from JavaScript
* Version 2.5.1 Built on 2022-01-28T15:37:57.791Z
* Version 2.5.0 Built on 2021-12-21T09:44:51.866Z
* CommitID 00000000
*
* Copyright (c) 2010-2021 James Hall <james@parall.ax>, https://github.com/MrRio/jsPDF

File diff suppressed because one or more lines are too long

View File

@ -257,7 +257,7 @@
/** @license
*
* jsPDF - PDF Document creation from JavaScript
* Version 2.5.1 Built on 2022-01-28T15:37:57.791Z
* Version 2.5.0 Built on 2021-12-21T09:44:51.866Z
* CommitID 00000000
*
* Copyright (c) 2010-2021 James Hall <james@parall.ax>, https://github.com/MrRio/jsPDF

File diff suppressed because one or more lines are too long

View File

@ -21,6 +21,7 @@ export default {
},
mounted() {
tocObj.reset()
this.init();
this.createEditor();
},

View File

@ -1,8 +1,20 @@
<template>
<div class="quick-edit" :class="[alwaysIcon ? 'quick-always' : '']">
<div v-if="isEdit" v-clickoutside="onClickOut" class="quick-input">
<TagInput v-if="isTag" ref="input" v-model="content" :disabled="isLoad" @on-enter="onEnter" @on-blur="onBlur"/>
<Input v-else ref="input" v-model="content" :disabled="isLoad" @on-enter="onEnter" @on-blur="onBlur"/>
<TagInput
v-if="isTag"
ref="input"
v-model="content"
:disabled="isLoad"
@on-keyup="onKeyup"
@on-blur="onBlur"/>
<Input
v-else
ref="input"
v-model="content"
:disabled="isLoad"
@on-keyup="onKeyup"
@on-blur="onBlur"/>
<div v-if="isLoad" class="quick-loading"><Loading/></div>
</div>
<template v-else>
@ -54,9 +66,6 @@ export default {
},
watch: {
isEdit(val) {
this.$emit("on-edit-change", val);
},
autoEdit(val) {
if (val === true) {
setTimeout(this.onEdit, 0)
@ -65,9 +74,14 @@ export default {
},
methods: {
onEditChange(val) {
this.isEdit = val;
this.$emit("on-edit-change", val);
},
onEdit() {
this.content = this.value;
this.isEdit = true;
this.onEditChange(true);
this.$nextTick(() => {
this.$refs.input.focus({
cursor: 'all'
@ -75,9 +89,18 @@ export default {
})
},
onKeyup(e) {
if (e.keyCode === 13) {
this.onEnter();
} else if (e.keyCode === 27) {
this.isEdit = false;
this.isLoad = false;
}
},
onEnter() {
if (this.content == this.value) {
this.isEdit = false;
this.onEditChange(false);
return;
}
if (this.isLoad) {
@ -86,7 +109,7 @@ export default {
this.isLoad = true;
this.$emit("input", this.content);
this.$emit("on-update", this.content, () => {
this.isEdit = false;
this.onEditChange(false);
this.isLoad = false;
})
},
@ -99,7 +122,7 @@ export default {
},
onBlur() {
if (this.clickOutSide) {
if (this.clickOutSide || !this.isEdit) {
return;
}
this.onEnter();

View File

@ -2,7 +2,7 @@
<div class="teditor-wrapper">
<div class="teditor-box" :class="[!inline && spinShow ? 'teditor-loadstyle' : 'teditor-loadedstyle']">
<template v-if="inline">
<div ref="myTextarea" :id="id" v-html="content"></div>
<div ref="myTextarea" :id="id" v-html="spinShow ? '' : content"></div>
<Icon v-if="spinShow" type="ios-loading" :size="18" class="icon-loading icon-inline"></Icon>
</template>
<template v-else>
@ -181,11 +181,7 @@
newValue = "";
}
if (!this.isTyping) {
if (this.getEditor() !== null) {
this.getEditor().setContent(newValue);
} else{
this.content = newValue;
}
this.setContent(newValue);
}
},
readOnly(value) {
@ -459,6 +455,14 @@
return this.getEditor().getContent();
},
setContent(content) {
if (this.getEditor() === null) {
this.content = content;
} else if (content != this.getEditor().getContent()){
this.getEditor().setContent(content);
}
},
insertImage(src) {
this.insertContent('<img src="' + src + '">');
},

View File

@ -11,7 +11,7 @@
:placeholder="tis || placeholderText"
@keydown.enter="downEnter($event)"
@keydown.delete="delTag(false)"
@keyup="addTag($event, content)"
@keyup="onKeyup"
@focus="onFocus"
@blur="onBlur"
:disabled="disabled"
@ -158,19 +158,22 @@
this.addTag(false, this.content)
this.$emit("on-blur", e)
},
onKeyup(e) {
this.addTag(e, this.content);
//
this.$emit("on-keyup", e)
if (e.keyCode === 13) {
this.$nextTick(() => {
this.$emit("on-enter", e)
})
}
},
addTag(e, content) {
if (e === false || e.keyCode === 13) {
if (content.trim() != '' && this.disSource.indexOf(content.trim()) === -1) {
this.disSource.push(content.trim());
}
this.content = '';
//
if (e.keyCode === 13) {
this.$nextTick(() => {
this.$emit("on-enter", e)
})
}
return;
}
if (this.max > 0 && this.disSource.length >= this.max) {

View File

@ -102,7 +102,7 @@
* @returns {*|string}
*/
formatTime(date) {
let time = Math.round($A.Date(date).getTime() / 1000),
let time = $A.Date(date, true),
string = '';
if ($A.formatDate('Ymd') === $A.formatDate('Ymd', time)) {
string = $A.formatDate('H:i', time)
@ -157,8 +157,10 @@
let time = Math.round(this.Date(date).getTime() / 1000) - nowTime;
if (time < 86400 * 7 && time > 0 ) {
return this.formatSeconds(time);
} else if (time <= 0) {
} else if (time < 0) {
return '-' + this.formatSeconds(time * -1);
} else if (time == 0) {
return 0 + 's';
}
return this.formatTime(date)
},

View File

@ -37,7 +37,7 @@
<i class="taskfont">&#xe689;</i>
</div>
<Dropdown-menu slot="list" class="login-setting-menu">
<Dropdown placement="right-start" @on-click="setTheme">
<Dropdown placement="right" @on-click="setTheme">
<DropdownItem>
<div class="login-setting-item">
{{$L('主题皮肤')}}
@ -48,7 +48,7 @@
<Dropdown-item v-for="(item, key) in themeList" :key="key" :name="item.value" :selected="themeMode === item.value">{{$L(item.name)}}</Dropdown-item>
</DropdownMenu>
</Dropdown>
<Dropdown placement="right-start" @on-click="setLanguage">
<Dropdown placement="right" @on-click="setLanguage">
<DropdownItem divided>
<div class="login-setting-item">
{{currentLanguage}}
@ -114,6 +114,9 @@ export default {
this.subscribe = null;
}
},
activated() {
this.loginType = 'login'
},
deactivated() {
this.loginJump = false;
this.password = "";

View File

@ -103,7 +103,9 @@
<li @click="toggleRoute('dashboard')" :class="classNameRoute('dashboard')">
<i class="taskfont">&#xe6fb;</i>
<div class="menu-title">{{$L('仪表盘')}}</div>
<Badge class="menu-badge" :type="dashboardTask.overdue.length > 0 ? 'error' : 'primary'" :count="dashboardTotal"></Badge>
<Badge v-if="dashboardTask.overdue.length > 0" class="menu-badge" type="error" :count="dashboardTask.overdue.length"/>
<Badge v-else-if="dashboardTask.today.length > 0" class="menu-badge" type="info" :count="dashboardTask.today.length"/>
<Badge v-else-if="dashboardTask.all.length > 0" class="menu-badge" type="primary" :count="dashboardTask.all.length"/>
</li>
<li @click="toggleRoute('calendar')" :class="classNameRoute('calendar')">
<i class="taskfont">&#xe6f5;</i>
@ -112,7 +114,7 @@
<li @click="toggleRoute('messenger')" :class="classNameRoute('messenger')">
<i class="taskfont">&#xe6eb;</i>
<div class="menu-title">{{$L('消息')}}</div>
<Badge class="menu-badge" :count="msgAllUnread"></Badge>
<Badge class="menu-badge" :count="msgAllUnread"/>
</li>
<li @click="toggleRoute('file')" :class="classNameRoute('file')">
<i class="taskfont">&#xe6f3;</i>
@ -256,15 +258,15 @@
<!--任务详情-->
<Modal
:value="taskId > 0"
:mask-closable="false"
:styles="{
width: '90%',
maxWidth: taskData.dialog_id ? '1200px' : '700px'
}"
@on-visible-change="taskVisibleChange"
footer-hide>
:mask-closable="false"
:footer-hide="true"
@on-visible-change="taskVisibleChange">
<div class="page-manage-task-modal" :style="taskStyle">
<TaskDetail :task-id="taskId" :open-task="taskData"/>
<TaskDetail ref="taskDetail" :task-id="taskId" :open-task="taskData"/>
</div>
</Modal>
@ -463,12 +465,8 @@ export default {
return num;
},
dashboardTotal() {
return this.dashboardTask.today.length + this.dashboardTask.overdue.length
},
unreadTotal() {
return this.msgAllUnread + this.dashboardTotal + this.reportUnreadNumber;
return this.msgAllUnread + this.dashboardTask.overdue.length + this.reportUnreadNumber;
},
currentLanguage() {
@ -787,10 +785,13 @@ export default {
},
shortcutEvent(e) {
if (e.keyCode === 75 || e.keyCode === 78) {
if (e.metaKey || e.ctrlKey) {
if (e.metaKey || e.ctrlKey) {
if (e.keyCode === 75 || e.keyCode === 78) {
e.preventDefault();
this.onAddTask(0)
} else if (e.keyCode === 83 && this.taskId > 0) {
e.preventDefault();
this.$refs.taskDetail.checkUpdate(true)
}
}
},

View File

@ -46,6 +46,7 @@
<div class="time" :title="msgData.created_at">{{$A.formatTime(msgData.created_at)}}</div>
<Poptip
v-if="msgData.send > 1 || dialogType == 'group'"
ref="percent"
class="percent"
placement="left-end"
transfer
@ -134,13 +135,13 @@ export default {
}
this.msgData._r = true;
//
this.$nextTick(() => {
setTimeout(() => {
if (!this.$el.offsetParent) {
this.msgData._r = false;
return
}
this.$store.dispatch("dialogMsgRead", this.msgData);
})
}, 50)
},
popperShow() {
@ -151,6 +152,7 @@ export default {
},
}).then(({data}) => {
this.read_list = data;
this.$refs.percent.updatePopper();
}).catch(() => {
this.read_list = [];
});

View File

@ -238,7 +238,7 @@ export default {
const {times} = this.addData;
let temp = $A.date2string(times, "Y-m-d H:i");
if (temp[0] && temp[1]) {
let d = Math.ceil(($A.Date(temp[1]).getTime() - $A.Date(temp[0]).getTime()) / 86400000);
let d = Math.ceil(($A.Date(temp[1], true) - $A.Date(temp[0], true)) / 86400);
if (d > 0) {
return d;
}

View File

@ -3,6 +3,7 @@
<li v-if="ready && taskDetail.parent_id > 0">
<div class="subtask-icon">
<TaskMenu
v-if="taskId > 0"
:ref="`taskMenu_${taskDetail.id}`"
:task="taskDetail"
:load-status="taskDetail.loading === true"
@ -72,6 +73,7 @@
<div v-show="taskDetail.id > 0" class="task-info">
<div class="head">
<TaskMenu
v-if="taskId > 0"
:ref="`taskMenu_${taskDetail.id}`"
:task="taskDetail"
class="icon"
@ -121,6 +123,7 @@
</ETooltip>
<div class="menu">
<TaskMenu
v-if="taskId > 0"
:task="taskDetail"
icon="ios-more"
completed-icon="ios-more"
@ -150,7 +153,6 @@
:option-full="taskOptionFull"
:placeholder="$L('详细描述...')"
@on-blur="updateData('content')"
@editorSave="updateData('content')"
inline/>
</div>
<Form class="items" label-position="left" label-width="auto" @submit.native.prevent>
@ -261,7 +263,7 @@
<div @click="openTime" class="time">{{taskDetail.end_at ? cutTime : '--'}}</div>
<template v-if="!taskDetail.complete_at && taskDetail.end_at">
<Tag v-if="within24Hours(taskDetail.end_at)" color="blue"><i class="taskfont">&#xe71d;</i>{{expiresFormat(taskDetail.end_at)}}</Tag>
<Tag v-if="taskDetail.overdue" color="red">{{$L('超期未完成')}}</Tag>
<Tag v-if="isOverdue(taskDetail)" color="red">{{$L('超期未完成')}}</Tag>
</template>
</div>
</DatePicker>
@ -307,7 +309,13 @@
<i class="taskfont">&#xe6f0;</i>{{$L('子任务')}}
</div>
<ul class="item-content subtask">
<TaskDetail v-for="(task, key) in subList" :key="key" :task-id="task.id" :open-task="task" :main-end-at="taskDetail.end_at"/>
<TaskDetail
v-for="(task, key) in subList"
:ref="`subTask_${task.id}`"
:key="key"
:task-id="task.id"
:open-task="task"
:main-end-at="taskDetail.end_at"/>
</ul>
<ul :class="['item-content', subList.length === 0 ? 'nosub' : '']">
<li>
@ -616,8 +624,8 @@ export default {
cutTime() {
const {taskDetail} = this;
let start_at = Math.round($A.Date(taskDetail.start_at).getTime() / 1000);
let end_at = Math.round($A.Date(taskDetail.end_at).getTime() / 1000);
let start_at = $A.Date(taskDetail.start_at, true);
let end_at = $A.Date(taskDetail.end_at, true);
let string = "";
if ($A.formatDate('Y/m/d', start_at) == $A.formatDate('Y/m/d', end_at)) {
string = $A.formatDate('Y/m/d H:i', start_at) + " ~ " + $A.formatDate('H:i', end_at)
@ -725,29 +733,27 @@ export default {
},
methods: {
initLanguage() {
},
innerHeightListener() {
this.innerHeight = Math.min(1100, window.innerHeight);
},
within24Hours(date) {
return Math.round($A.Date(date).getTime() / 1000) - this.nowTime < 86400
return $A.Date(date, true) - this.nowTime < 86400
},
expiresFormat(date) {
return $A.countDownFormat(date, this.nowTime)
},
isOverdue(taskDetail) {
if (taskDetail.overdue) {
return true;
}
return $A.Date(taskDetail.end_at, true) < this.nowTime;
},
onNameKeydown(e) {
if (e.keyCode === 83) {
if (e.metaKey || e.ctrlKey) {
e.preventDefault();
this.updateData('name');
}
} else if (e.keyCode === 13) {
if (e.keyCode === 13) {
if (!e.shiftKey) {
e.preventDefault();
this.updateData('name');
@ -755,6 +761,40 @@ export default {
}
},
checkUpdate(update) {
let isModify = false;
if (this.openTask.name != this.taskDetail.name) {
isModify = true;
if (update) {
this.updateData('name');
} else if (isModify) {
return true
}
}
if (this.$refs.desc && this.$refs.desc.getContent() != this.taskContent) {
isModify = true;
if (update) {
this.updateData('content');
} else if (isModify) {
return true
}
}
if (this.addsubShow && this.addsubName) {
isModify = true;
if (update) {
this.onAddsub();
} else if (isModify) {
return true
}
}
this.subList.some(({id}) => {
if (this.$refs[`subTask_${id}`][0].checkUpdate(update)) {
isModify = true;
}
})
return isModify;
},
updateData(action, params) {
switch (action) {
case 'priority':

View File

@ -15,7 +15,7 @@
</template>
</div>
</slot>
<EDropdownMenu slot="dropdown" class="task-menu-more-dropdown">
<EDropdownMenu ref="dropdownMenu" slot="dropdown" class="task-menu-more-dropdown">
<li class="task-menu-more-warp" :class="size">
<ul>
<EDropdownItem v-if="!flow" class="load-flow" disabled>
@ -218,7 +218,9 @@ export default {
visibleChange(visible) {
if (visible) {
this.$store.dispatch("getTaskFlow", this.task.id).catch(() => {})
this.$store.dispatch("getTaskFlow", this.task.id)
.then(this.$refs.dropdownMenu.updatePopper)
.catch(this.$refs.dropdownMenu.updatePopper)
}
},

View File

@ -266,7 +266,7 @@ export default {
},
completeAtFormat(date) {
let time = Math.round($A.Date(date).getTime() / 1000);
let time = $A.Date(date, true);
if ($A.formatDate('Y') === $A.formatDate('Y', time)) {
return $A.formatDate('m-d H:i', time)
} else {

View File

@ -87,9 +87,9 @@
<li
v-for="item in fileList"
:class="{
shear: shearIds.includes(item.id),
highlight: selectIds.includes(item.id),
}"
shear: shearIds.includes(item.id),
highlight: selectIds.includes(item.id),
}"
@contextmenu.prevent.stop="handleRightClick($event, item)"
@click="openFile(item)">
<div class="file-check" :class="{'file-checked':selectIds.includes(item.id)}" @click.stop="dropFile(item, 'select')">
@ -121,7 +121,7 @@
size="small"
:disabled="!!item._load"
@on-blur="onBlur(item)"
@on-enter="onEnter(item)"/>
@on-keyup="onKeyup($event, item)"/>
<div v-if="item._load" class="file-load"><Loading/></div>
</div>
<div v-else class="file-name" :title="item.name">{{formatName(item)}}</div>
@ -959,7 +959,6 @@ export default {
break;
case 'rename':
this.$set(item, 'newname', item.name);
this.setEdit(item.id, true)
this.autoBlur(item.id)
break;
@ -1154,9 +1153,21 @@ export default {
},
onBlur(item) {
if (this.files.find(({id, _edit}) => id == item.id && !_edit)) {
return;
}
this.onEnter(item);
},
onKeyup(e, item) {
if (e.keyCode === 13) {
this.onEnter(item);
} else if (e.keyCode === 27) {
this.setLoad(item.id, false)
this.setEdit(item.id, false)
}
},
onEnter(item) {
let isCreate = !/^\d+$/.test(item.id);
if (!item.newname) {
@ -1204,6 +1215,9 @@ export default {
let item = this.$store.state.files.find(({id}) => id == fileId)
if (item) {
this.$set(item, '_edit', is);
if (is) {
this.$set(item, 'newname', item.name);
}
}
},

View File

@ -74,8 +74,12 @@ export default {
}
}
};
params.error = () => {
reject({data: {}, msg: "System error"})
params.error = (xhr, status) => {
if (window.navigator.onLine === false || (status === 0 && xhr.readyState === 4)) {
reject({data: {}, msg: $A.L('网络异常,请稍后再试!')})
} else {
reject({data: {}, msg: "System error"})
}
};
//
if (params.websocket === true || params.ws === true) {
@ -2046,7 +2050,7 @@ export default {
}
});
state.wsReadWaitList = [];
}, 20);
}, 50);
},
/**
@ -2174,6 +2178,12 @@ export default {
// 更新最后消息
dispatch("updateDialogLastMsg", data);
break;
case 'readed':
// 已读回执
if (state.dialogMsgs.find(({id}) => id == data.id)) {
dispatch("saveDialogMsg", data)
}
break;
}
})(msgDetail);
break;

View File

@ -77,6 +77,13 @@
img {
max-width: 100%;
}
pre {
padding: 14px;
margin: 7px 0;
overflow: auto;
background: #f5f2f0;
border-radius: 5px;
}
&[data-mce-placeholder]:not(.mce-visualblocks)::before {
color: #bbbbbb;
}

View File

@ -163,6 +163,13 @@
img {
max-width: 100%;
}
pre {
padding: 14px;
margin: 7px 0;
overflow: auto;
background: #f5f2f0;
border-radius: 5px;
}
&[data-mce-placeholder]:not(.mce-visualblocks)::before {
color: #bbbbbb;
}

View File

@ -108,8 +108,8 @@
padding: 8px;
.load-flow-warp {
width: 20px;
height: 20px;
width: 18px;
height: 18px;
}
}
}

View File

@ -51,8 +51,6 @@
&:last-child {
background-color: #98de6e;
margin-right: 0;
cursor: default;
box-shadow: none;
}
.block-title {
color: rgba(255, 255, 255, 0.6);

View File

@ -485,7 +485,7 @@
.file-upload-list {
display: flex;
width: 380px;
padding: 14px 26px 14px 13px;
padding: 14px 26px 14px 26px;
border-radius: 8px;
border: 1px solid #ebeef5;
position: fixed;
@ -497,8 +497,7 @@
overflow: hidden;
.upload-wrap {
flex: 1;
margin-left: 13px;
margin-right: 8px;
width: 100%;
.title {
font-weight: 700;
font-size: 16px;
@ -516,16 +515,18 @@
.content {
font-size: 14px;
line-height: 21px;
margin: 12px -16px 0 0;
margin: 12px 0 0;
color: #606266;
max-height: 500px;
max-width: 100%;
overflow: auto;
> li {
list-style: none;
padding: 4px 16px 4px 0;
padding: 4px 0;
position: relative;
.file-name {
line-height: 18px;
padding-right: 16px;
}
.file-error {
font-size: 12px;
@ -533,8 +534,9 @@
}
.file-close {
position: absolute;
font-size: 14px;
top: 7px;
right: 0;
right: -1px;
display: none;
cursor: pointer;
}