Merge commit '4912f9746115e3fe24bc1621076d08d73c20b552' into pro

# Conflicts:
#	public/js/emoticon.all.js
This commit is contained in:
Pang 2023-08-11 08:19:04 +08:00
commit 952836e1f1
23 changed files with 680 additions and 77 deletions

View File

@ -1,3 +1,5 @@
TIMEZONE=PRC
APP_NAME=DooTask
APP_ENV=local
APP_KEY=

View File

@ -1,3 +1,5 @@
TIMEZONE=PRC
APP_NAME=Laravel
APP_ENV=local
APP_KEY=

View File

@ -29,7 +29,7 @@ class ApproveController extends AbstractController
private $flow_url = '';
public function __construct()
{
$this->flow_url = env('FLOW_URL') ?: 'http://dootask-approve-'.env('APP_ID');
$this->flow_url = env('FLOW_URL') ?: 'http://approve';
}
/**
@ -74,6 +74,7 @@ class ApproveController extends AbstractController
$ret = Ihttp::ihttp_post($this->flow_url.'/api/v1/workflow/procdef/findAll', json_encode($data));
$procdef = json_decode($ret['ret'] == 1 ? $ret['data'] : '{}', true);
if (!$procdef || $procdef['status'] != 200 || $ret['ret'] == 0) {
info($ret);
return Base::retError($procdef['message'] ?? '查询失败');
}
return Base::retSuccess('success', Base::arrayKeyToUnderline($procdef['data']));

View File

@ -80,7 +80,7 @@ class DialogController extends AbstractController
return Base::retError('请输入搜索关键词');
}
// 搜索会话
$dialogs = WebSocketDialog::select(['web_socket_dialogs.*', 'u.top_at', 'u.mark_unread', 'u.silence', 'u.updated_at as user_at'])
$dialogs = WebSocketDialog::select(['web_socket_dialogs.*', 'u.top_at', 'u.mark_unread', 'u.silence', 'u.color', 'u.updated_at as user_at'])
->join('web_socket_dialog_users as u', 'web_socket_dialogs.id', '=', 'u.dialog_id')
->where('web_socket_dialogs.name', 'LIKE', "%{$key}%")
->where('u.userid', $user->userid)
@ -117,7 +117,7 @@ class DialogController extends AbstractController
}
// 搜索消息会话
if (count($list) < 20) {
$msgs = WebSocketDialog::select(['web_socket_dialogs.*', 'u.top_at', 'u.mark_unread', 'u.silence', 'u.updated_at as user_at', 'm.id as search_msg_id'])
$msgs = WebSocketDialog::select(['web_socket_dialogs.*', 'u.top_at', 'u.mark_unread', 'u.silence', 'u.color', 'u.updated_at as user_at', 'm.id as search_msg_id'])
->join('web_socket_dialog_users as u', 'web_socket_dialogs.id', '=', 'u.dialog_id')
->join('web_socket_dialog_msgs as m', 'web_socket_dialogs.id', '=', 'm.dialog_id')
->where('u.userid', $user->userid)
@ -154,7 +154,7 @@ class DialogController extends AbstractController
//
$dialog_id = intval(Request::input('dialog_id'));
//
$item = WebSocketDialog::select(['web_socket_dialogs.*', 'u.top_at', 'u.mark_unread', 'u.silence', 'u.updated_at as user_at'])
$item = WebSocketDialog::select(['web_socket_dialogs.*', 'u.top_at', 'u.mark_unread', 'u.silence', 'u.color', 'u.updated_at as user_at'])
->join('web_socket_dialog_users as u', 'web_socket_dialogs.id', '=', 'u.dialog_id')
->where('web_socket_dialogs.id', $dialog_id)
->where('u.userid', $user->userid)
@ -1542,6 +1542,47 @@ class DialogController extends AbstractController
]);
}
/**
* @api {get} api/dialog/msg/color 30. 设置颜色
*
* @apiDescription 需要token身份
* @apiVersion 1.0.0
* @apiGroup dialog
* @apiName msg__color
*
* @apiParam {Number} dialog_id 会话ID
* @apiParam {String} color 颜色
*
* @apiSuccess {Number} ret 返回状态码1正确、0错误
* @apiSuccess {String} msg 返回信息(错误描述)
* @apiSuccess {Object} data 返回数据
*/
public function msg__color()
{
$user = User::auth();
$dialogId = intval(Request::input('dialog_id'));
$color = Request::input('color','');
$dialogUser = WebSocketDialogUser::whereUserid($user->userid)->whereDialogId($dialogId)->first();
if (!$dialogUser) {
return Base::retError("会话不存在");
}
//
$dialogData = WebSocketDialog::find($dialogId);
if (empty($dialogData)) {
return Base::retError("会话不存在");
}
//
$dialogUser->color = $color;
$dialogUser->save();
//
$data = [
'id' => $dialogId,
'color' => $color
];
return Base::retSuccess("success", $data);
}
/**
* @api {get} api/dialog/group/add 33. 新增群组
*

View File

@ -995,10 +995,10 @@ class ProjectController extends AbstractController
->groupBy('task_id');
}, 'task_files', 'task_files.task_id', '=', 'project_tasks.id');
$builder->leftJoinSub(function ($query) {
$query->select('id', DB::raw('count(*) as msg_num'))
->from('web_socket_dialogs')
->groupBy('id');
}, 'socket_dialogs', 'socket_dialogs.id', '=', 'project_tasks.id');
$query->select('dialog_id', DB::raw('count(*) as msg_num'))
->from('web_socket_dialog_msgs')
->groupBy('dialog_id');
}, 'socket_dialog_msgs', 'socket_dialog_msgs.dialog_id', '=', 'project_tasks.dialog_id');
$builder->leftJoinSub(function ($query) {
$query->select('parent_id', DB::raw('count(*) as sub_num, sum(CASE WHEN complete_at IS NOT NULL THEN 1 ELSE 0 END) sub_complete') )
->from('project_tasks')
@ -1007,7 +1007,7 @@ class ProjectController extends AbstractController
// 给前缀“_”是为了不触发获取器
$prefix = DB::getTablePrefix();
$builder->selectRaw("{$prefix}task_files.file_num as _file_num");
$builder->selectRaw("{$prefix}socket_dialogs.msg_num as _msg_num");
$builder->selectRaw("{$prefix}socket_dialog_msgs.msg_num as _msg_num");
$builder->selectRaw("{$prefix}sub_task.sub_num as _sub_num");
$builder->selectRaw("{$prefix}sub_task.sub_complete as _sub_complete");
$builder->selectRaw("

View File

@ -76,7 +76,7 @@ class WebSocketDialog extends AbstractModel
*/
public function getDialogList($userid, $updated = "", $deleted = "")
{
$builder = WebSocketDialog::select(['web_socket_dialogs.*', 'u.top_at', 'u.mark_unread', 'u.silence', 'u.updated_at as user_at'])
$builder = WebSocketDialog::select(['web_socket_dialogs.*', 'u.top_at', 'u.mark_unread', 'u.silence', 'u.color', 'u.updated_at as user_at'])
->join('web_socket_dialog_users as u', 'web_socket_dialogs.id', '=', 'u.dialog_id')
->where('u.userid', $userid);
if ($updated) {

View File

@ -849,6 +849,8 @@ class WebSocketDialogMsg extends AbstractModel
$dialogMsg->updateInstance($updateData);
$dialogMsg->key = $dialogMsg->generateMsgKey();
$dialogMsg->save();
//
$dialogMsg->msgJoinGroup($dialog, $dialogMsg);
//
$dialog->pushMsg('update', array_merge($updateData, [
'id' => $dialogMsg->id
@ -896,4 +898,41 @@ class WebSocketDialogMsg extends AbstractModel
return Base::retSuccess('发送成功', $dialogMsg);
}
}
/**
* 将被@的人加入群
* @param $dialogMsg 发送的消息
* @param $dialog 对话
* @return array
*/
public static function msgJoinGroup($dialog, $dialogMsg)
{
$updateds = [];
$silences = [];
foreach ($dialog->dialogUser as $dialogUser) {
$updateds[$dialogUser->userid] = $dialogUser->updated_at;
$silences[$dialogUser->userid] = $dialogUser->silence;
}
$userids = array_keys($silences);
// 提及会员
$mentions = [];
if ($dialogMsg->type === 'text') {
preg_match_all("/<span class=\"mention user\" data-id=\"(\d+)\">/", $dialogMsg->msg['text'], $matchs);
if ($matchs) {
$mentions = array_values(array_filter(array_unique($matchs[1])));
}
}
// 将会话以外的成员加入会话内
$diffids = array_values(array_diff($mentions, $userids));
if ($diffids) {
// 仅(群聊)且(是群主或没有群主)才可以@成员以外的人
if ($dialog->type === 'group' && in_array($dialog->owner_id, [0, $dialogMsg->userid])) {
$dialog->joinGroup($diffids, $dialogMsg->userid);
$dialog->pushMsg("groupJoin", null, $diffids);
$userids = array_values(array_unique(array_merge($mentions, $userids)));
}
}
return compact('updateds', 'silences', 'userids', 'mentions');
}
}

View File

@ -91,33 +91,13 @@ class WebSocketDialogMsgTask extends AbstractTask
if (empty($dialog)) {
return;
}
$updateds = [];
$silences = [];
foreach ($dialog->dialogUser as $dialogUser) {
$updateds[$dialogUser->userid] = $dialogUser->updated_at;
$silences[$dialogUser->userid] = $dialogUser->silence;
}
$userids = array_keys($silences);
// 提及会员
$mentions = [];
if ($msg->type === 'text') {
preg_match_all("/<span class=\"mention user\" data-id=\"(\d+)\">/", $msg->msg['text'], $matchs);
if ($matchs) {
$mentions = array_values(array_filter(array_unique($matchs[1])));
}
}
// 将会话以外的成员加入会话内
$diffids = array_values(array_diff($mentions, $userids));
if ($diffids) {
// 仅(群聊)且(是群主或没有群主)才可以@成员以外的人
if ($dialog->type === 'group' && in_array($dialog->owner_id, [0, $msg->userid])) {
$dialog->joinGroup($diffids, $msg->userid);
$dialog->pushMsg("groupJoin", null, $diffids);
$userids = array_values(array_unique(array_merge($mentions, $userids)));
}
}
$msgJoinGroupResult = $msg->msgJoinGroup($dialog, $msg);
$updateds = $msgJoinGroupResult['updateds'];
$silences = $msgJoinGroupResult['silences'];
$userids = $msgJoinGroupResult['userids'];
$mentions = $msgJoinGroupResult['mentions'];
// 推送目标①:会话成员/群成员
$array = [];

View File

@ -67,7 +67,7 @@ return [
|
*/
'timezone' => 'PRC',
'timezone' => env('TIMEZONE', 'PRC'),
/*
|--------------------------------------------------------------------------

View File

@ -0,0 +1,36 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\Schema;
class AddWebSocketDialogUsersColor extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
//
Schema::table('web_socket_dialog_users', function (Blueprint $table) {
if (!Schema::hasColumn('web_socket_dialog_users', 'color')) {
$table->string('color', 20)->nullable()->default('')->after('important')->comment('颜色');
}
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
//
Schema::table('web_socket_dialog_users', function (Blueprint $table) {
$table->dropColumn("color");
});
}
}

View File

@ -0,0 +1,42 @@
<?php
use Illuminate\Database\Migrations\Migration;
use Illuminate\Support\Facades\Schema;
class repairProjectTasksIsAllVisible extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
// 修复子任务可见性字段为null的数据
if (Schema::hasTable('project_tasks')) {
$prefix = DB::getConfig('prefix');
DB::update("
UPDATE {$prefix}project_tasks
SET is_all_visible = 1
WHERE is_all_visible is null AND parent_id = 0;
");
DB::update("
UPDATE {$prefix}project_tasks t1
JOIN {$prefix}project_tasks t2 ON t1.parent_id = t2.id
SET t1.is_all_visible = t2.is_all_visible
WHERE t1.is_all_visible is null AND t1.parent_id > 0;
");
}
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
}
}

View File

@ -149,8 +149,9 @@ services:
approve:
container_name: "dootask-approve-${APP_ID}"
image: "hitosea2020/go-approve:0.1.3"
image: "hitosea2020/go-approve:0.1.4"
environment:
TZ: "${TIMEZONE:-PRC}"
MYSQL_HOST: "${DB_HOST}"
MYSQL_PORT: "${DB_PORT}"
MYSQL_DBNAME: "${DB_DATABASE}"

View File

@ -5,40 +5,40 @@
"path": "18",
"icon": "icon.png",
"list": [
{"name": "爱你", "key": "爱你 爱你哟 爱老虎油 爱你啦 亲亲 亲一个 啵啵 比心 笔芯 爱心", "path": "1.gif"},
{"name": "搬砖", "key": "搬砖 工作 努力", "path": "2.gif"},
{"name": "不愧是精英", "key": "不愧是精英 精英 大佬 厉害 真棒 你真棒 牛逼 6 666 不愧是你 还得是你", "path": "3.gif"},
{"name": "不想理你", "key": "生气 不理 不开心 哼 不想理你", "path": "4.gif"},
{"name": "不想面对", "key": "不想面对 瑟瑟发抖 体重 担忧 不想理你 又胖了", "path": "5.gif"},
{"name": "沉迷工作", "key": "工作 认真 做事 干活 电脑 沉迷工作", "path": "6.gif"},
{"name": "打卡了么", "key": "打卡 上班 报 记得打卡", "path": "7.gif"},
{"name": "发生了啥", "key": "冒泡 浮出水面 发生了啥 八卦 露头 我看看 怎么个事 怎么了", "path": "8.gif"},
{"name": "给你个眼神", "key": "眼神 真的 质疑 疑问 不确定 给你个眼神", "path": "9.gif"},
{"name": "恭喜发财", "key": "恭喜发财 新年好 新年 拜年", "path": "10.gif"},
{"name": "跪谢", "key": "谢谢 感动 跪 多谢 THANKS THANK YOU 谢了", "path": "11.gif"},
{"name": "Hi", "key": "Hi 打招呼 欢迎 招呼 你好 哈喽 哈啰 hello 大家好", "path": "12.gif"},
{"name": "爱你", "key": "爱你哟 爱老虎油 爱你啦 亲亲 亲一个 啵啵", "path": "1.gif"},
{"name": "搬砖", "key": "工作 努力", "path": "2.gif"},
{"name": "不愧是精英", "key": "精英 大佬 厉害 真棒 你真棒 牛逼", "path": "3.gif"},
{"name": "不想理你", "key": "生气 不理 不开心", "path": "4.gif"},
{"name": "不想面对", "key": "瑟瑟发抖 体重", "path": "5.gif"},
{"name": "沉迷工作", "key": "工作 认真 做事 干活 电脑", "path": "6.gif"},
{"name": "打卡了么", "key": "打卡 上班 报", "path": "7.gif"},
{"name": "发生了啥", "key": "冒泡 浮出水面 八卦 露头", "path": "8.gif"},
{"name": "给你个眼神", "key": "眼神 真的 质疑 疑问 不确定", "path": "9.gif"},
{"name": "恭喜发财", "key": "新年好 新年 拜年", "path": "10.gif"},
{"name": "跪谢", "key": "谢谢 感动 跪", "path": "11.gif"},
{"name": "Hi", "key": "打招呼 欢迎 招呼 你好 好", "path": "12.gif"},
{"name": "欢迎欢迎", "key": "欢迎 新人 兴奋 开心 庆祝 喝彩 气氛组", "path": "13.gif"},
{"name": "加油", "key": "加油 喝彩 气氛组", "path": "14.gif"},
{"name": "静静看着你", "key": "静静 看着 不说话 沉默 静静看着你 无语 看你表演", "path": "15.gif"},
{"name": "开会啦", "key": "开会 会 通知 敲锣 开会啦 开会了", "path": "16.gif"},
{"name": "可以吗", "key": "可以吗 害羞 不好意思 脸红 可以 可以不", "path": "17.gif"},
{"name": "迷之自信", "key": "自信 迷之自信 自我欣赏 自恋 我真帅", "path": "18.gif"},
{"name": "明白", "key": "明白 收到 好的 收 OK", "path": "19.gif"},
{"name": "摸摸", "key": "摸摸 摸头 你真乖 乖 棒", "path": "20.gif"},
{"name": "你真棒", "key": "大拇指 你真棒 太棒了 太棒啦 棒棒棒 棒", "path": "21.gif"},
{"name": "佩服", "key": "佩服 五体投地 崇拜 仰慕 牛逼 厉害", "path": "22.gif"},
{"name": "撒花", "key": "撒花 欢迎 花 开心 耶 真好 真棒", "path": "23.gif"},
{"name": "生日快乐", "key": "生日快乐 吃蛋糕 蛋糕 生日", "path": "24.gif"},
{"name": "收到", "key": "明白 收到 好的 收 已阅 已读", "path": "25.gif"},
{"name": "加油", "key": "喝彩 气氛组", "path": "14.gif"},
{"name": "静静看着你", "key": "静静 看着 不说话 沉默", "path": "15.gif"},
{"name": "开会啦", "key": "开会 会 通知 敲锣", "path": "16.gif"},
{"name": "可以吗", "key": "害羞 不好意思", "path": "17.gif"},
{"name": "迷之自信", "key": "自信 自我欣赏", "path": "18.gif"},
{"name": "明白", "key": "收到 好的 收", "path": "19.gif"},
{"name": "摸摸", "key": "摸头 你真乖 乖 棒", "path": "20.gif"},
{"name": "你真棒", "key": "大拇指 太棒了 太棒啦 棒棒棒 棒", "path": "21.gif"},
{"name": "佩服", "key": "五体投地 崇拜 仰慕 牛逼 厉害", "path": "22.gif"},
{"name": "撒花", "key": "欢迎 花 开心", "path": "23.gif"},
{"name": "生日快乐", "key": "吃蛋糕 蛋糕", "path": "24.gif"},
{"name": "收到", "key": "明白 好的 收 已阅 已读", "path": "25.gif"},
{"name": "送你花花", "key": "送花 送你花 花 兴奋 开心 庆祝 喝彩 气氛组", "path": "26.gif"},
{"name": "在线吃瓜", "key": "吃瓜 八卦 看戏 看热闹 在线吃瓜 凑热闹 吃瓜群众 围观", "path": "27.gif"},
{"name": "我错了", "key": "认错 错了 哭 难过 悲伤 我错了 对不起 道歉", "path": "28.gif"},
{"name": "我太难了", "key": "我太难了 太难了 难过 不开心 委屈", "path": "29.gif"},
{"name": "笑而不语", "key": "笑 不说话 静静 不发表 沉默", "path": "30.gif"},
{"name": "行行好吧", "key": "行行好 乞丐 乞求 请求 求助 给点吧", "path": "31.gif"},
{"name": "幸福", "key": "幸福 满足 洗澡 泡澡 开心", "path": "32.gif"},
{"name": "赞", "key": "大拇指 点赞 厉害 你真棒 太棒了 太棒啦 棒棒棒 棒 6 666 牛", "path": "33.gif"},
{"name": "走人了", "key": "下班 闪人 走人 溜人 走了 关机", "path": "34.gif"}
{"name": "在线吃瓜", "key": "吃瓜 八卦", "path": "27.gif"},
{"name": "我错了", "key": "认错 错了 哭 难过", "path": "28.gif"},
{"name": "我太难了", "key": "太难了 难过 不开心 委屈", "path": "29.gif"},
{"name": "笑而不语", "key": "笑 不说话 静静 不发表", "path": "30.gif"},
{"name": "行行好吧", "key": "行行好 乞丐 乞求 请求 求助", "path": "31.gif"},
{"name": "幸福", "key": "满足 洗澡 泡澡", "path": "32.gif"},
{"name": "赞", "key": "大拇指 点赞 厉害 你真棒 太棒了 太棒啦 棒棒棒 棒", "path": "33.gif"},
{"name": "走人了", "key": "下班 闪人 走人 溜人", "path": "34.gif"}
]
},
{

View File

@ -0,0 +1,39 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Cookie Policy</title>
<meta name="google" value="notranslate">
<meta name="description" content="DooTask是一款轻量级的开源在线项目任务管理工具提供各类文档协作工具、在线思维导图、在线流程图、项目管理、任务分发、即时IM文件管理等工具。助力团队高效推进项目让工作更简单。">
<meta name="keywords" content="中国 DooTask 开源在线项目 任务管理工具 任务管理 轻量级 海豚有海 团队协作">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="shortcut icon" href="../img/favicon.ico">
<link rel="stylesheet" href="../css/privacy.css">
</head>
<body>
<style>
body {
font-family: Arial, sans-serif;
margin: 20px;
}
</style>
<div id="markdown-content"></div>
<script src="https://unpkg.com/markdown-it/dist/markdown-it.min.js"></script>
<script>
// 创建 markdown-it 实例
const md = new window.markdownit();
// 获取 Markdown 文件内容并渲染为 HTML
fetch('./cookie.md')
.then(response => response.text())
.then(markdownText => {
const htmlText = md.render(markdownText);
document.getElementById('markdown-content').innerHTML = htmlText;
})
.catch(error => {
console.error('Error:', error);
});
</script>
</body>
</html>

80
public/site/en/cookie.md Normal file
View File

@ -0,0 +1,80 @@
# Cookies Policy
Last updated: August 10, 2023
This Cookies Policy explains what Cookies are and how We use them. You should read this policy so You can understand what type of cookies We use, or the information We collect using Cookies and how that information is used. This Cookies Policy has been created with the help of the [Cookies Policy Generator](https://www.termsfeed.com/cookies-policy-generator/).
Cookies do not typically contain any information that personally identifies a user, but personal information that we store about You may be linked to the information stored in and obtained from Cookies. For further information on how We use, store and keep your personal data secure, see our Privacy Policy.
We do not store sensitive personal information, such as mailing addresses, account passwords, etc. in the Cookies We use.
# Interpretation and Definitions
## Interpretation
The words of which the initial letter is capitalized have meanings defined under the following conditions. The following definitions shall have the same meaning regardless of whether they appear in singular or in plural.
## Definitions
For the purposes of this Cookies Policy:
- __Company__ (referred to as either "the Company", "We", "Us" or "Our" in this Cookies Policy) refers to Guangxi Hitosea Information Technology Co, No. 1411, 14/F, Building 1, Wuxiang Hangyang City, Liangqing District, Nanning City, Guangxi Zhuang Autonomous Region, China.
- __Cookies__ means small files that are placed on Your computer, mobile device or any other device by a website, containing details of your browsing history on that website among its many uses.
- __Website__ refers to DooTask, accessible from [ https://www.dootask.com/]( https://www.dootask.com/)
- __You__ means the individual accessing or using the Website, or a company, or any legal entity on behalf of which such individual is accessing or using the Website, as applicable.
# The use of the Cookies
## Type of Cookies We Use
Cookies can be "Persistent" or "Session" Cookies. Persistent Cookies remain on your personal computer or mobile device when You go offline, while Session Cookies are deleted as soon as You close your web browser.
We use both session and persistent Cookies for the purposes set out below:
- __Necessary / Essential Cookies__
Type: Session Cookies
Administered by: Us
Purpose: These Cookies are essential to provide You with services available through the Website and to enable You to use some of its features. They help to authenticate users and prevent fraudulent use of user accounts. Without these Cookies, the services that You have asked for cannot be provided, and We only use these Cookies to provide You with those services.
- __Functionality Cookies__
Type: Persistent Cookies
Administered by: Us
Purpose: These Cookies allow us to remember choices You make when You use the Website, such as remembering your login details or language preference. The purpose of these Cookies is to provide You with a more personal experience and to avoid You having to re-enter your preferences every time You use the Website.
## Your Choices Regarding Cookies
If You prefer to avoid the use of Cookies on the Website, first You must disable the use of Cookies in your browser and then delete the Cookies saved in your browser associated with this website. You may use this option for preventing the use of Cookies at any time.
If You do not accept Our Cookies, You may experience some inconvenience in your use of the Website and some features may not function properly.
If You'd like to delete Cookies or instruct your web browser to delete or refuse Cookies, please visit the help pages of your web browser.
- For the Chrome web browser, please visit this page from Google: [https://support.google.com/accounts/answer/32050](https://support.google.com/accounts/answer/32050)
- For the Internet Explorer web browser, please visit this page from Microsoft: [http://support.microsoft.com/kb/278835](http://support.microsoft.com/kb/278835)
- For the Firefox web browser, please visit this page from Mozilla: [https://support.mozilla.org/en-US/kb/delete-cookies-remove-info-websites-stored](https://support.mozilla.org/en-US/kb/delete-cookies-remove-info-websites-stored)
- For the Safari web browser, please visit this page from Apple: [https://support.apple.com/guide/safari/manage-cookies-and-website-data-sfri11471/mac](https://support.apple.com/guide/safari/manage-cookies-and-website-data-sfri11471/mac)
For any other web browser, please visit your web browser's official web pages.
## More Information about Cookies
You can learn more about cookies here: [All About Cookies by TermsFeed](https://www.termsfeed.com/blog/cookies/).
## Contact Us
If you have any questions about this Cookies Policy, You can contact us:
- By email: service@hitosea.com

View File

@ -14,6 +14,16 @@
</head>
<body>
<div id="layout">
<!-- 同意cookie弹框 -->
<div id="cookieConsent">
<div>
We use first party cookies to improve your browsing experience on our website, to analyze our website traffic and to understand where our visitors are coming from. If you choose to opt-out, only strictly necessary cookies will be used.<a href="../en/cookie.html" id="cookie_a" target="_blank">Our cookie policy</a>
</div>
<div id="buttonBox">
<button id="rejectButton" class="btn btn-primary" >I decline</button>
<button id="agreeButton" class="btn btn-primary" >Allow cookies</button>
</div>
</div>
<!-- 页头区域 -->
<header class="head">
<div class="nav">
@ -526,6 +536,83 @@
</body>
<script src="../js/common.js"></script>
<script>
// cookie弹框
document.addEventListener('DOMContentLoaded', function() {
let cookieConsent = document.getElementById('cookieConsent');
let cookieA = document.getElementById('cookie_a');
let buttonBox = document.getElementById('buttonBox');
let rejectButton = document.getElementById('rejectButton');
let agreeButton = document.getElementById('agreeButton');
// 检查localStorage中的同意状态
let hasConsented = sessionStorage.getItem('cookieConsent');
if (!hasConsented) {
cookieConsent.style.display = 'block';
cookieConsent.style.maxWidth = '24em';
cookieConsent.style.height = 'auto';
cookieConsent.style.flexDirection = 'column';
cookieConsent.style.backgroundColor = 'rgb(249,250,251)';
cookieConsent.style.color = 'rgb(30,30,30)';
cookieConsent.style.padding = '1.2em';
cookieConsent.style.borderRadius = '5px';
cookieConsent.style.bottom = '1em';
cookieConsent.style.right = '1em';
cookieConsent.style.position = 'fixed';
cookieConsent.style.overflow = 'hidden'
cookieConsent.style.boxSizing = 'border-box'
cookieConsent.style.fontFamily = 'Helvetica,Calibri,Arial,sans-serif'
cookieConsent.style.zIndex = '9999';
cookieConsent.style.textAlign = 'left';
cookieConsent.style.fontSize = '16px';
cookieConsent.style.lineHeight = '1.5em';
cookieA.style.padding = '0.2em'
cookieA.style.display = 'inline-block';
cookieA.style.color = 'rgb(30,30,30)';
cookieA.style.textDecoration = 'underline';
buttonBox.style.textAlign = 'right';
buttonBox.style.flex = '1 0 auto';
buttonBox.style.alignItems = 'center';
buttonBox.style.alignContent = 'space-between'
buttonBox.style.flexDirection = 'row';
buttonBox.style.display = 'flex';
buttonBox.style.fontSize = '16px';
buttonBox.style.margin = '1em 0 0';
agreeButton.style.position = 'static'
agreeButton.style.display = 'block'
agreeButton.style.padding = '0.7em 0.4em'
agreeButton.style.fontSize = '.9em';
agreeButton.style.fontWeight = '700';
agreeButton.style.textAlign = 'center';
agreeButton.style.width = '100%';
agreeButton.style.borderRadius = '5px';
agreeButton.style.marginLeft = '0.5em';
rejectButton.style.backgroundColor = 'transparent';
rejectButton.style.borderColor = 'transparent';
rejectButton.style.color = 'rgb(30,30,30)';
rejectButton.style.display = 'block'
rejectButton.style.padding = '0.7em 0.4em'
rejectButton.style.fontSize = '.9em';
rejectButton.style.fontWeight = '700';
}
agreeButton.addEventListener('click', handleAgreeButtonClick);
rejectButton.addEventListener('click', handleRejectButtonClick);
function hideCookieConsent() {
cookieConsent.style.display = 'none';
}
function handleAgreeButtonClick() {
// 存储同意状态到localStorage
sessionStorage.setItem('cookieConsent', 'true');
hideCookieConsent();
}
function handleRejectButtonClick() {
// 存储拒绝状态到localStorage
sessionStorage.setItem('cookieConsent', 'false');
hideCookieConsent();
}
});
/* 功能卡片切换函数 */
const tabs = document.querySelector('.card-ul');
const funImg = document.querySelector('#home_pic2');

View File

@ -0,0 +1,39 @@
<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<title>Cookie Policy</title>
<meta name="google" value="notranslate">
<meta name="description" content="DooTask是一款轻量级的开源在线项目任务管理工具提供各类文档协作工具、在线思维导图、在线流程图、项目管理、任务分发、即时IM文件管理等工具。助力团队高效推进项目让工作更简单。">
<meta name="keywords" content="中国 DooTask 开源在线项目 任务管理工具 任务管理 轻量级 海豚有海 团队协作">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="shortcut icon" href="../img/favicon.ico">
<link rel="stylesheet" href="../css/privacy.css">
</head>
<body>
<style>
body {
font-family: Arial, sans-serif;
margin: 20px;
}
</style>
<div id="markdown-content"></div>
<script src="https://unpkg.com/markdown-it/dist/markdown-it.min.js"></script>
<script>
// 创建 markdown-it 实例
const md = new window.markdownit();
// 获取 Markdown 文件内容并渲染为 HTML
fetch('./cookie.md')
.then(response => response.text())
.then(markdownText => {
const htmlText = md.render(markdownText);
document.getElementById('markdown-content').innerHTML = htmlText;
})
.catch(error => {
console.error('Error:', error);
});
</script>
</body>
</html>

80
public/site/zh/cookie.md Normal file
View File

@ -0,0 +1,80 @@
# Cookie 政策
最后更新时间2023 年 8 月 10 日
本 Cookie 政策解释了什么是 Cookie 以及我们如何使用它们。您应该阅读本政策,以便您了解我们使用什么类型的 Cookie或者我们使用 Cookie 收集的信息以及如何使用该信息。本 Cookie 政策是在[Cookie 政策生成器](https://www.termsfeed.com/cookies-policy-generator/)的帮助下创建的。
Cookie 通常不包含任何可识别用户个人身份的信息,但我们存储的有关您的个人信息可能与 Cookie 中存储和获取的信息相关联。有关我们如何使用、存储和保证您的个人数据安全的更多信息,请参阅我们的隐私政策。
我们不会在我们使用的 Cookie 中存储敏感的个人信息,例如邮寄地址、帐户密码等。
# 解释和定义
## 解释
首字母大写的单词具有在以下条件下定义的含义。下列定义无论以单数还是复数形式出现,均具有相同的含义。
## 定义
就本 Cookie 政策而言:
- __公司__ 在本Cookies政策中称为"公司"、“我们”、“我们”或“我们的”)是指广西海豚有海信息科技有限公司,地址:中国广西壮族自治区南宁市良庆区五象航洋城 1 号楼 14 层 1411 号。
- __Cookies__ 是指网站放置在您的计算机、移动设备或任何其他设备上的小文件,其中包含您在该网站上的浏览历史记录以及其多种用途的详细信息。
- __网站__ 指的是 DooTask可从 [ https://www.dootask.com/]( https://www.dootask.com/)访问。
- __您__ 是指访问或使用本网站的个人,或代表该个人访问或使用本网站的公司或任何法律实体(如果适用)。
# Cookie 的使用
## 我们使用的 Cookie 类型
Cookie 可以是“持久”Cookie 或“会话”Cookie。 当您离线时,持久 Cookie 会保留在您的个人计算机或移动设备上,而会话 Cookie 会在您关闭网络浏览器后立即删除。
我们出于以下目的使用会话 Cookie 和持久 Cookie
- __必要/必需的 Cookie__
类型:会话 Cookie
管理机构:我们
目的:这些 Cookie 对于向您提供通过网站提供的服务以及使您能够使用网站的某些功能至关重要。它们有助于验证用户身份并防止欺诈性使用用户帐户。如果没有这些 Cookie则无法提供您所请求的服务我们仅使用这些 Cookie 来为您提供这些服务。
- __功能性 Cookie__
类型:持久性 Cookie
管理机构:我们
目的:这些 Cookie 使我们能够记住您在使用本网站时所做的选择,例如记住您的登录详细信息或语言偏好。这些 Cookie 的目的是为您提供更加个性化的体验,并避免您每次使用网站时都必须重新输入您的偏好设置。
## 您对 Cookie 的选择
如果您希望避免在网站上使用 Cookie首先您必须在浏览器中禁用 Cookie然后删除浏览器中保存的与本网站关联的 Cookie。您可以随时使用此选项来阻止使用 Cookie。
如果您不接受我们的 Cookie您在使用本网站时可能会遇到一些不便并且某些功能可能无法正常运行。
如果您想删除 Cookie 或指示您的网络浏览器删除或拒绝 Cookie请访问您的网络浏览器的帮助页面。
- 对于 Chrome 网络浏览器,请访问 Google 的此页面:[https://support.google.com/accounts/answer/32050](https://support.google.com/accounts/answer/32050)
- 对于 Internet Explorer Web 浏览器,请访问 Microsoft 的此页面:[http://support.microsoft.com/kb/278835](http://support.microsoft.com/kb/278835)
- 对于 Firefox 网络浏览器,请从 Mozilla 访问此页面:[https://support.mozilla.org/en-US/kb/delete-cookies-remove-info-websites-stored](https://support.mozilla.org/en-US/kb/delete-cookies-remove-info-websites-stored)
- 对于 Safari 网络浏览器,请访问 Apple 的此页面:[https://support.apple.com/guide/safari/manage-cookies-and-website-data-sfri11471/mac](https://support.apple.com/guide/safari/manage-cookies-and-website-data-sfri11471/mac)
对于任何其他网络浏览器,请访问您的网络浏览器的官方网页。
## 有关 Cookie 的更多信息
您可以在此处了解有关 Cookie 的更多信息:[TermsFeed 提供的有关 Cookie 的所有信息](https://www.termsfeed.com/blog/cookies/)。
## 联系我们
如果您对此 Cookie 政策有任何疑问,您可以联系我们:
- 通过电子邮件service@hitosea.com

View File

@ -14,6 +14,16 @@
</head>
<body>
<div id="layout">
<!-- 同意cookie弹框 -->
<div id="cookieConsent">
<div>
我们使用第一方 cookie 来改善您在我们网站上的浏览体验、分析我们的网站流量并了解我们的访问者来自哪里。如果您选择退出,则只会使用绝对必要的 cookie。<a href="../zh/cookie.html" id="cookie_a" target="_blank">我们的 cookie 政策</a>
</div>
<div id="buttonBox">
<button id="rejectButton" class="btn btn-primary" >我拒绝</button>
<button id="agreeButton" class="btn btn-primary" >允许cookies</button>
</div>
</div>
<!-- 页头区域 -->
<header class="head">
<div class="nav">
@ -525,6 +535,83 @@
</body>
<script src="../js/common.js"></script>
<script>
// cookie弹框
document.addEventListener('DOMContentLoaded', function() {
let cookieConsent = document.getElementById('cookieConsent');
let cookieA = document.getElementById('cookie_a');
let buttonBox = document.getElementById('buttonBox');
let rejectButton = document.getElementById('rejectButton');
let agreeButton = document.getElementById('agreeButton');
// 检查localStorage中的同意状态
let hasConsented = sessionStorage.getItem('cookieConsent');
if (!hasConsented) {
cookieConsent.style.display = 'block';
cookieConsent.style.maxWidth = '24em';
cookieConsent.style.height = 'auto';
cookieConsent.style.flexDirection = 'column';
cookieConsent.style.backgroundColor = 'rgb(249,250,251)';
cookieConsent.style.color = 'rgb(30,30,30)';
cookieConsent.style.padding = '1.2em';
cookieConsent.style.borderRadius = '5px';
cookieConsent.style.bottom = '1em';
cookieConsent.style.right = '1em';
cookieConsent.style.position = 'fixed';
cookieConsent.style.overflow = 'hidden'
cookieConsent.style.boxSizing = 'border-box'
cookieConsent.style.fontFamily = 'Helvetica,Calibri,Arial,sans-serif'
cookieConsent.style.zIndex = '9999';
cookieConsent.style.textAlign = 'left';
cookieConsent.style.fontSize = '16px';
cookieConsent.style.lineHeight = '1.5em';
cookieA.style.padding = '0.2em'
cookieA.style.display = 'inline-block';
cookieA.style.color = 'rgb(30,30,30)';
cookieA.style.textDecoration = 'underline';
buttonBox.style.textAlign = 'right';
buttonBox.style.flex = '1 0 auto';
buttonBox.style.alignItems = 'center';
buttonBox.style.alignContent = 'space-between'
buttonBox.style.flexDirection = 'row';
buttonBox.style.display = 'flex';
buttonBox.style.fontSize = '16px';
buttonBox.style.margin = '1em 0 0';
agreeButton.style.position = 'static'
agreeButton.style.display = 'block'
agreeButton.style.padding = '0.7em 0.4em'
agreeButton.style.fontSize = '.9em';
agreeButton.style.fontWeight = '700';
agreeButton.style.textAlign = 'center';
agreeButton.style.width = '100%';
agreeButton.style.borderRadius = '5px';
agreeButton.style.marginLeft = '0.5em';
rejectButton.style.backgroundColor = 'transparent';
rejectButton.style.borderColor = 'transparent';
rejectButton.style.color = 'rgb(30,30,30)';
rejectButton.style.display = 'block'
rejectButton.style.padding = '0.7em 0.4em'
rejectButton.style.fontSize = '.9em';
rejectButton.style.fontWeight = '700';
}
agreeButton.addEventListener('click', handleAgreeButtonClick);
rejectButton.addEventListener('click', handleRejectButtonClick);
function hideCookieConsent() {
cookieConsent.style.display = 'none';
}
function handleAgreeButtonClick() {
// 存储同意状态到localStorage
sessionStorage.setItem('cookieConsent', 'true');
hideCookieConsent();
}
function handleRejectButtonClick() {
// 存储拒绝状态到localStorage
sessionStorage.setItem('cookieConsent', 'false');
hideCookieConsent();
}
});
/* 功能卡片切换函数 */
const tabs = document.querySelector('.card-ul');
const funImg = document.querySelector('#home_pic2');

View File

@ -20,11 +20,17 @@
</div>
<div class="approve-details-text">
<h4>{{$L('开始时间')}}</h4>
<p>{{datas.var?.start_time}}</p>
<div>
<span>{{datas.var?.start_time}}</span>
<span>&nbsp;&nbsp;&nbsp;({{getWeekday(datas.var?.start_time)}})</span>
</div>
</div>
<div class="approve-details-text">
<h4>{{$L('结束时间')}}</h4>
<p>{{datas.var?.end_time}}</p>
<div>
<span>{{datas.var?.end_time}}</span>
<span>&nbsp;&nbsp;&nbsp;({{getWeekday(datas.var?.end_time)}})</span>
</div>
</div>
<div class="approve-details-text">
<h4>{{ $L('时长') }}{{getTimeDifference(datas.var?.start_time,datas.var?.end_time)['unit']}}</h4>
@ -273,6 +279,10 @@ export default {
return type == 2 ? `${days+1}${this.$L('天')}` : `${days+1} ${this.$L('天')}`;
}
},
//
getWeekday(dateString) {
return ['周日', '周一', '周二', '周三', '周四', '周五', '周六'][new Date(dateString).getDay()];
},
//
getTimeDifference(startTime,endTime) {
const currentTime = new Date((endTime + '').replace(/-/g,"/"));

View File

@ -1317,6 +1317,20 @@ export default {
this.mentionMode = "user-mention";
const atCallback = (list) => {
this.getMoreUser(searchTerm, list.map(item => item.id)).then(moreUser => {
// -> 5
let cacheDialogs = this.cacheDialogs.filter((h, index) => h.type == "user" && h.bot == 0 && h.last_at)
cacheDialogs.sort((a, b) => a.last_at > b.last_at ? -1 : (a.last_at < b.last_at ? 1 : 0));
cacheDialogs = cacheDialogs.filter((h, index) => index < 5)
moreUser.forEach(user => {
user.last_at = "1990-01-01 00:00:00";
cacheDialogs.forEach(dialog => {
if (dialog.dialog_user?.userid == user.id) {
user.last_at = dialog.last_at;
}
})
})
moreUser.sort((a, b) => a.last_at > b.last_at ? -1 : (a.last_at < b.last_at ? 1 : 0));
//
this.userList = list
this.userCache = [];
if (moreUser.length > 0) {

View File

@ -175,10 +175,13 @@ export default {
},
scrollTo(type) {
$A.scrollToView(this.$refs[`type_${type}`][0], {
behavior: 'smooth',
inline: 'end',
});
let refs = this.$refs[`type_${type}`]
if (refs) {
$A.scrollToView(refs[0], {
behavior: 'smooth',
inline: 'end',
});
}
},
openTask(task) {

View File

@ -63,7 +63,8 @@
dialog_id: dialog.id,
search_msg_id: dialog.search_msg_id
})"
v-longpress="handleLongpress">
v-longpress="handleLongpress"
:style="{'background-color':dialog.color}">
<template v-if="dialog.type=='group'">
<EAvatar v-if="dialog.avatar" class="img-avatar" :src="dialog.avatar" :size="42"></EAvatar>
<i v-else-if="dialog.group_type=='department'" class="taskfont icon-avatar department">&#xe75c;</i>
@ -157,6 +158,11 @@
<DropdownItem @click.native="handleSilenceClick" :disabled="silenceDisabled(operateItem)">
{{ $L(operateItem.silence ? '允许消息通知' : '消息免打扰') }}
</DropdownItem>
<DropdownItem @click.native="handleColorClick(c.color)" v-for="(c, k) in taskColorList" :key="'c_' + k" :divided="k==0" v-if="k<6" >
<div class="item">
<i class="taskfont" :style="{color:c.color||'#f9f9f9'}" v-html="c.color == operateItem.color ? '&#xe61d;' : '&#xe61c;'"></i>{{$L(c.name)}}
</div>
</DropdownItem>
</DropdownMenu>
</Dropdown>
</div>
@ -267,7 +273,7 @@ export default {
},
computed: {
...mapState(['cacheDialogs', 'loadDialogs', 'dialogId', 'messengerSearchKey', 'appNotificationPermission']),
...mapState(['cacheDialogs', 'loadDialogs', 'dialogId', 'messengerSearchKey', 'appNotificationPermission', 'taskColorList']),
routeName() {
return this.$route.name
@ -917,6 +923,20 @@ export default {
});
},
handleColorClick(color) {
this.$store.dispatch("call", {
url: 'dialog/msg/color',
data: {
dialog_id: this.operateItem.id,
color: color
},
}).then(({data}) => {
this.$store.dispatch("saveDialog", data);
}).catch(({msg}) => {
$A.modalError(msg);
});
},
updateDialogs(timeout) {
this.__updateDialogs && clearTimeout(this.__updateDialogs)
if (timeout > -1) {